Update Refund journeys

1. Updated the refund journeys so that they use the payment Gateway Factory.
2. Improved request data going in and out of the session.
3. Show error message when Stripe SCA journey fails.
4. Update migration.
This commit is contained in:
Jeremy Quinton 2019-09-09 16:42:01 +02:00
parent 8bcf106ffe
commit 0c1f4c8619
9 changed files with 172 additions and 51 deletions

View File

@ -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;
}

View File

@ -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");

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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',

View File

@ -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',

View File

@ -70,6 +70,12 @@
</div>
@endif
@if($order->payment_intent)
<div class="col-sm-6 col-xs-6">
<b>@lang("Order.payment_intent")</b><br> {{$order->payment_intent}}
</div>
@endif
@if ($order->is_business)
<div class="col-sm-6 col-xs-6">
<b>@lang("Public_ViewEvent.business_name")</b><br />

View File

@ -4,6 +4,13 @@
@lang("Public_ViewEvent.payment_information")
</h1>
</div>
@if($payment_failed)
<div class="row">
<div class="col-md-8 alert-danger" style="text-align: left; padding: 10px">
@lang("Order.payment_failed")
</div>
</div>
@endif
<div class="row">
<div class="col-md-12" style="text-align: center">
@lang("Public_ViewEvent.below_order_details_header")