diff --git a/app/Http/Controllers/EventCheckoutController.php b/app/Http/Controllers/EventCheckoutController.php index 7e08f05a..f6031828 100644 --- a/app/Http/Controllers/EventCheckoutController.php +++ b/app/Http/Controllers/EventCheckoutController.php @@ -181,22 +181,17 @@ class EventCheckoutController extends Controller ]); } - if (config('attendize.enable_dummy_payment_gateway') == TRUE) { - $activeAccountPaymentGateway = new AccountPaymentGateway(); - $activeAccountPaymentGateway->fill(['payment_gateway_id' => config('attendize.payment_gateway_dummy')]); - $paymentGateway = $activeAccountPaymentGateway; - } else { - $activeAccountPaymentGateway = $event->account->getGateway($event->account->payment_gateway_id); - //if no payment gateway configured and no offline pay, don't go to the next step and show user error - if (empty($activeAccountPaymentGateway) && !$event->enable_offline_payments) { - return response()->json([ - 'status' => 'error', - 'message' => 'No payment gateway configured', - ]); - } - $paymentGateway = $activeAccountPaymentGateway ? $activeAccountPaymentGateway->payment_gateway : false; + $activeAccountPaymentGateway = $event->account->getGateway($event->account->payment_gateway_id); + //if no payment gateway configured and no offline pay, don't go to the next step and show user error + if (empty($activeAccountPaymentGateway) && !$event->enable_offline_payments) { + return response()->json([ + 'status' => 'error', + 'message' => 'No payment gateway configured', + ]); } + $paymentGateway = $activeAccountPaymentGateway ? $activeAccountPaymentGateway->payment_gateway : false; + /* * The 'ticket_order_{event_id}' session stores everything we need to complete the transaction. */ @@ -280,8 +275,6 @@ class EventCheckoutController extends Controller public function postValidateOrder(Request $request, $event_id) { - session()->push('ticket_order_' . $event_id . '.request_data', $request->all()); - //If there's no session kill the request and redirect back to the event homepage. if (!session()->get('ticket_order_' . $event_id)) { return response()->json([ @@ -293,6 +286,13 @@ class EventCheckoutController extends Controller ]); } + $request_data = session()->get('ticket_order_' . $event_id . ".request_data"); + $request_data = (!empty($request_data[0])) ? array_merge($request_data[0], $request->all()) + : $request->all(); + + session()->remove('ticket_order_' . $event_id . '.request_data'); + session()->push('ticket_order_' . $event_id . '.request_data', $request_data); + $event = Event::findOrFail($event_id); $order = new Order(); $ticket_order = session()->get('ticket_order_' . $event_id); @@ -354,6 +354,8 @@ class EventCheckoutController extends Controller $orderService = new OrderService($order_session['order_total'], $order_session['total_booking_fee'], $event); $orderService->calculateFinalCosts(); + $payment_failed = $request->get('is_payment_failed') ? 1 : 0; + $secondsToExpire = Carbon::now()->diffInSeconds($order_session['expires']); $viewData = ['event' => $event, @@ -363,7 +365,8 @@ class EventCheckoutController extends Controller 'order_requires_payment' => (ceil($order_session['order_total']) == 0) ? false : true, 'account_payment_gateway' => $account_payment_gateway, 'payment_gateway' => $payment_gateway, - 'secondsToExpire' => $secondsToExpire + 'secondsToExpire' => $secondsToExpire, + 'payment_failed' => $payment_failed ]; return view('Public.ViewEvent.EventPagePayment', $viewData); @@ -380,10 +383,14 @@ class EventCheckoutController extends Controller */ public function postCreateOrder(Request $request, $event_id) { - //Add the request data to a session in case payment is required off-site - session()->push('ticket_order_' . $event_id . '.request_data', $request->except(['cardnumber', 'cvc'])); + $request_data = $ticket_order = session()->get('ticket_order_' . $event_id . ".request_data"); + $request_data = array_merge($request_data[0], $request->except(['cardnumber', 'cvc'])); + + session()->remove('ticket_order_' . $event_id . '.request_data'); + session()->push('ticket_order_' . $event_id . '.request_data', $request_data); $ticket_order = session()->get('ticket_order_' . $event_id); + $event = Event::findOrFail($event_id); $order_requires_payment = $ticket_order['order_requires_payment']; @@ -416,7 +423,6 @@ class EventCheckoutController extends Controller $response = $gateway->startTransaction($order_total, $order_email, $event); - if ($response->isSuccessful()) { session()->push('ticket_order_' . $event_id . '.transaction_id', @@ -426,12 +432,11 @@ class EventCheckoutController extends Controller } elseif ($response->isRedirect()) { - Log::error("reference : " . $response->getTransactionReference()); + $additionalData = ($gateway->storeAdditionalData()) ? $gateway->getAdditionalData($response) : array(); - //As we're going off-site for payment we need to store some data in a session so it's available - //when we return + session()->push('ticket_order_' . $event_id . '.transaction_data', + $gateway->getTransactionData() + $additionalData); - session()->push('ticket_order_' . $event_id . '.transaction_data', $gateway->getTransactionData()); Log::info("Redirect url: " . $response->getRedirectUrl()); @@ -496,7 +501,7 @@ class EventCheckoutController extends Controller return $this->completeOrder($event_id, false); } else { session()->flash('message', $response->getMessage()); - return response()->redirectToRoute('showEventCheckout', [ + return response()->redirectToRoute('showEventPayment', [ 'event_id' => $event_id, 'is_payment_failed' => 1, ]); @@ -533,6 +538,10 @@ class EventCheckoutController extends Controller $order->transaction_id = $ticket_order['transaction_id'][0]; } + if (isset($ticket_order['transaction_data'][0]['payment_intent'])) { + $order->payment_intent = $ticket_order['transaction_data'][0]['payment_intent']; + } + if ($ticket_order['order_requires_payment'] && !isset($request_data['pay_offline'])) { $order->payment_gateway_id = $ticket_order['payment_gateway']->id; } diff --git a/app/Http/Controllers/EventOrdersController.php b/app/Http/Controllers/EventOrdersController.php index 37a882c9..c5be49fc 100644 --- a/app/Http/Controllers/EventOrdersController.php +++ b/app/Http/Controllers/EventOrdersController.php @@ -8,7 +8,9 @@ use App\Models\Attendee; use App\Models\Event; use App\Models\EventStats; use App\Models\Order; +use App\Models\PaymentGateway; use App\Services\Order as OrderService; +use App\Services\PaymentGateway\Factory as PaymentGatewayFactory; use DB; use Excel; use Illuminate\Http\Request; @@ -196,11 +198,10 @@ class EventOrdersController extends MyBaseController /** - * Cancels an order - * * @param Request $request * @param $order_id - * @return mixed + * @return \Illuminate\Http\JsonResponse + * @throws \Exception */ public function postCancelOrder(Request $request, $order_id) { @@ -244,24 +245,23 @@ class EventOrdersController extends MyBaseController $order->event->currency))]); } if (!$error_message) { - try { - $gateway = Omnipay::create($order->payment_gateway->name); - $gateway->initialize($order->account->getGateway($order->payment_gateway->id)->config); + try { + + $payment_gateway_config = $order->account->getGateway($order->payment_gateway->id)->config + [ + 'testMode' => config('attendize.enable_test_payments')]; + + $payment_gateway_factory = new PaymentGatewayFactory(); + $gateway = $payment_gateway_factory->create($order->payment_gateway->name, $payment_gateway_config); if ($refund_type === 'full') { /* Full refund */ $refund_amount = $order->organiser_amount - $order->amount_refunded; } - $request = $gateway->refund([ - 'transactionReference' => $order->transaction_id, - 'amount' => $refund_amount, - 'refundApplicationFee' => floatval($order->booking_fee) > 0 ? true : false, - ]); + $refund_application_fee = floatval($order->booking_fee) > 0 ? true : false; + $response = $gateway->refundTransaction($order, $refund_amount, $refund_application_fee); - $response = $request->send(); - - if ($response->isSuccessful()) { + if ($response['successful']) { /* Update the event sales volume*/ $order->event->decrement('sales_volume', $refund_amount); $order->amount_refunded = round(($order->amount_refunded + $refund_amount), 2); @@ -274,10 +274,11 @@ class EventOrdersController extends MyBaseController $order->order_status_id = config('attendize.order_partially_refunded'); } } else { - $error_message = $response->getMessage(); + $error_message = $response['error_message']; } $order->save(); + } catch (\Exeption $e) { Log::error($e); $error_message = trans("Controllers.refund_exception"); diff --git a/app/Services/PaymentGateway/Dummy.php b/app/Services/PaymentGateway/Dummy.php index 09635bce..78f324f8 100644 --- a/app/Services/PaymentGateway/Dummy.php +++ b/app/Services/PaymentGateway/Dummy.php @@ -49,4 +49,31 @@ class Dummy public function extractRequestParameters($request) {} public function completeTransaction($transactionId) {} + + public function getAdditionalData() {} + + public function storeAdditionalData() { + return false; + } + + public function refundTransaction($order, $refund_amount, $refund_application_fee) { + + $request = $this->gateway->refund([ + 'transactionReference' => $order->transaction_id, + 'amount' => $refund_amount, + 'refundApplicationFee' => $refund_application_fee + ]); + + $response = $request->send(); + + if ($response->isSuccessful()) { + $refundResponse['successful'] = true; + } else { + $refundResponse['successful'] = false; + $refundResponse['error_message'] = $response->getMessage(); + } + + return $refundResponse; + } + } \ No newline at end of file diff --git a/app/Services/PaymentGateway/Stripe.php b/app/Services/PaymentGateway/Stripe.php index d386d53a..15b2ea4c 100644 --- a/app/Services/PaymentGateway/Stripe.php +++ b/app/Services/PaymentGateway/Stripe.php @@ -42,7 +42,8 @@ class Stripe return $response; } - public function getTransactionData() { + public function getTransactionData() + { return $this->transaction_data; } @@ -55,6 +56,37 @@ class Stripe } } - public function completeTransaction($transactionId) {} + public function completeTransaction($transactionId) + { + } + public function getAdditionalData() + { + } + + public function storeAdditionalData() + { + return false; + } + + public function refundTransaction($order, $refund_amount, $refund_application_fee) + { + + $request = $this->gateway->refund([ + 'transactionReference' => $order->transaction_id, + 'amount' => $refund_amount, + 'refundApplicationFee' => $refund_application_fee + ]); + + $response = $request->send(); + + if ($response->isSuccessful()) { + $refundResponse['successful'] = true; + } else { + $refundResponse['successful'] = false; + $refundResponse['error_message'] = $response->getMessage(); + } + + return $refundResponse; + } } \ No newline at end of file diff --git a/app/Services/PaymentGateway/StripeSCA.php b/app/Services/PaymentGateway/StripeSCA.php index f6a7e64e..090ec9fe 100644 --- a/app/Services/PaymentGateway/StripeSCA.php +++ b/app/Services/PaymentGateway/StripeSCA.php @@ -2,7 +2,6 @@ namespace Services\PaymentGateway; - use Illuminate\Support\Facades\Log; class StripeSCA @@ -14,7 +13,7 @@ class StripeSCA private $gateway; - private $extra_params = ['paymentMethod','payment_intent']; + private $extra_params = ['paymentMethod', 'payment_intent']; public function __construct($gateway) { @@ -45,14 +44,14 @@ class StripeSCA public function startTransaction($order_total, $order_email, $event) { - $this->createTransactionData($order_total, $order_email, $event); $response = $this->gateway->authorize($this->transaction_data)->send(); return $response; } - public function getTransactionData() { + public function getTransactionData() + { return $this->transaction_data; } @@ -65,11 +64,12 @@ class StripeSCA } } - public function completeTransaction($transactionId = '') { + public function completeTransaction($transactionId = '') + { - $intentData = array( + $intentData = [ 'paymentIntentReference' => $this->options['payment_intent'], - ); + ]; $paymentIntent = $this->gateway->fetchPaymentIntent($intentData); $response = $paymentIntent->send(); @@ -77,8 +77,41 @@ class StripeSCA $response = $this->gateway->confirm($intentData)->send(); } - return $response; } - + + public function getAdditionalData($response) + { + + $additionalData['payment_intent'] = $response->getPaymentIntentReference(); + return $additionalData; + } + + public function storeAdditionalData() + { + return true; + } + + public function refundTransaction($order, $refund_amount, $refund_application_fee) + { + + $request = $this->gateway->cancel([ + 'transactionReference' => $order->transaction_id, + 'amount' => $refund_amount, + 'refundApplicationFee' => $refund_application_fee, + 'paymentIntentReference' => $order->payment_intent + ]); + + $response = $request->send(); + + if ($response->isCancelled()) { + $refundResponse['successful'] = true; + } else { + $refundResponse['successful'] = false; + $refundResponse['error_message'] = $response->getMessage(); + } + + return $refundResponse; + } + } \ No newline at end of file diff --git a/database/migrations/2019_09_04_075835_add_default_gateways.php b/database/migrations/2019_09_04_075835_add_default_gateways.php index 2d7f6daf..be455ae1 100644 --- a/database/migrations/2019_09_04_075835_add_default_gateways.php +++ b/database/migrations/2019_09_04_075835_add_default_gateways.php @@ -22,6 +22,10 @@ class AddDefaultGateways extends Migration $table->string('checkout_blade_template', 150)->default(''); }); + Schema::table('orders', function($table) { + $table->string('payment_intent', 150)->default(''); + }); + DB::table('payment_gateways') ->where('provider_name', 'Stripe') ->update(['admin_blade_template' => 'ManageAccount.Partials.Stripe', diff --git a/resources/lang/en/Order.php b/resources/lang/en/Order.php index 2577ddbc..39cc28bc 100644 --- a/resources/lang/en/Order.php +++ b/resources/lang/en/Order.php @@ -31,6 +31,8 @@ return array ( 'order_ref' => 'Reference', 'organiser_booking_fees' => 'Organiser Booking Fees', 'payment_gateway' => 'Payment Gateway', + 'payment_intent' => 'Payment Intent', + 'payment_failed' => 'Payment failed please try enter your payment details again.', 'price' => 'Price', 'purchase_date' => 'Purchase Date', 'quantity' => 'Quantity', diff --git a/resources/views/ManageEvent/Modals/ManageOrder.blade.php b/resources/views/ManageEvent/Modals/ManageOrder.blade.php index 8309d5d8..e4212741 100644 --- a/resources/views/ManageEvent/Modals/ManageOrder.blade.php +++ b/resources/views/ManageEvent/Modals/ManageOrder.blade.php @@ -70,6 +70,12 @@ @endif + @if($order->payment_intent) +
+ @lang("Order.payment_intent")
{{$order->payment_intent}} +
+ @endif + @if ($order->is_business)
@lang("Public_ViewEvent.business_name")
diff --git a/resources/views/Public/ViewEvent/Partials/EventPaymentSection.blade.php b/resources/views/Public/ViewEvent/Partials/EventPaymentSection.blade.php index a63b9b3a..417e11ae 100644 --- a/resources/views/Public/ViewEvent/Partials/EventPaymentSection.blade.php +++ b/resources/views/Public/ViewEvent/Partials/EventPaymentSection.blade.php @@ -4,6 +4,13 @@ @lang("Public_ViewEvent.payment_information")
+ @if($payment_failed) +
+
+ @lang("Order.payment_failed") +
+
+ @endif
@lang("Public_ViewEvent.below_order_details_header")