Payment responses and saving card details

This commit is contained in:
David Bomba 2019-09-25 14:03:28 +10:00
parent 89330e6e34
commit 5090c963d3
6 changed files with 131 additions and 26 deletions

View File

@ -124,13 +124,20 @@ class PaymentController extends Controller
'amount_with_fee' => $amount + $gateway->calcGatewayFee($amount), 'amount_with_fee' => $amount + $gateway->calcGatewayFee($amount),
'token' => auth()->user()->client->gateway_token($gateway->id, $payment_method_id), 'token' => auth()->user()->client->gateway_token($gateway->id, $payment_method_id),
'payment_method_id' => $payment_method_id, 'payment_method_id' => $payment_method_id,
'hashed_ids' => explode(",",request()->input('hashed_ids')),
]; ];
return $gateway->driver(auth()->user()->client)->processPayment($data); return $gateway->driver(auth()->user()->client)->processPaymentView($data);
} }
public function response(Request $request)
{
$gateway = CompanyGateway::find($request->input('company_gateway_id'));
return $gateway->driver(auth()->user()->client)->processPaymentResponse($request);
}
} }

View File

@ -82,6 +82,10 @@ class BasePaymentDriver
]; ];
} }
public function getCompanyGatewayId()
{
return $this->company_gateway->id;
}
/** /**
* Returns whether refunds are possible with the gateway * Returns whether refunds are possible with the gateway
* @return boolean TRUE|FALSE * @return boolean TRUE|FALSE
@ -111,10 +115,14 @@ class BasePaymentDriver
*/ */
public function refundPayment() {} public function refundPayment() {}
public function authorizeCreditCardView($data) {} public function authorizeCreditCardView(array $data) {}
public function authorizeCreditCardResponse($request) {} public function authorizeCreditCardResponse($request) {}
public function processPaymentView(array $data) {}
public function processPaymentResponse($request) {}
/************************************* Omnipay ****************************************** /************************************* Omnipay ******************************************
authorize($options) - authorize an amount on the customer's card authorize($options) - authorize an amount on the customer's card
completeAuthorize($options) - handle return from off-site gateways after authorization completeAuthorize($options) - handle return from off-site gateways after authorization

View File

@ -128,7 +128,7 @@ class StripePaymentDriver extends BasePaymentDriver
} }
} }
public function authorizeCreditCardView($data) public function authorizeCreditCardView(array $data)
{ {
$intent['intent'] = $this->getSetupIntent(); $intent['intent'] = $this->getSetupIntent();
@ -195,22 +195,24 @@ class StripePaymentDriver extends BasePaymentDriver
* @var amount_with_fee * @var amount_with_fee
* @var token * @var token
* @var payment_method_id * @var payment_method_id
* @var hashed_ids
*
* @param array $data variables required to build payment page * @param array $data variables required to build payment page
* @return view Gateway and payment method specific view * @return view Gateway and payment method specific view
*/ */
public function processPayment(array $data) public function processPaymentView(array $data)
{ {
$payment_intent_data = [ $payment_intent_data = [
'amount' => $data['amount_with_fee']*100, 'amount' => $data['amount_with_fee']*100,
'currency' => $this->client->getCurrencyCode(), 'currency' => $this->client->getCurrencyCode(),
'customer' => $this->findOrCreateCustomer(), 'customer' => $this->findOrCreateCustomer(),
'description' => $data['invoices']->pluck('id'), 'description' => $data['invoices']->pluck('id'), //todo more meaningful description here:
]; ];
if($data['token']) if($data['token'])
$payment_intent_data['payment_method'] = $data['token']->token; $payment_intent_data['payment_method'] = $data['token']->token;
else{ else{
// $payment_intent_data['setup_future_usage'] = 'off_session'; $payment_intent_data['setup_future_usage'] = 'off_session';
// $payment_intent_data['save_payment_method'] = true; // $payment_intent_data['save_payment_method'] = true;
// $payment_intent_data['confirm'] = true; // $payment_intent_data['confirm'] = true;
} }
@ -218,12 +220,88 @@ class StripePaymentDriver extends BasePaymentDriver
$data['intent'] = $this->createPaymentIntent($payment_intent_data); $data['intent'] = $this->createPaymentIntent($payment_intent_data);
$data['gateway'] = $this; $data['gateway'] = $this;
return view($this->viewForType($data['payment_method_id']), $data); return view($this->viewForType($data['payment_method_id']), $data);
} }
/**
* Payment Intent Reponse looks like this
+"id": "pi_1FMR7JKmol8YQE9DuC4zMeN3"
+"object": "payment_intent"
+"allowed_source_types": array:1 [
0 => "card"
]
+"amount": 2372484
+"canceled_at": null
+"cancellation_reason": null
+"capture_method": "automatic"
+"client_secret": "pi_1FMR7JKmol8YQE9DuC4zMeN3_secret_J3yseWJG6uV0MmsrAT1FlUklV"
+"confirmation_method": "automatic"
+"created": 1569381877
+"currency": "usd"
+"description": "[3]"
+"last_payment_error": null
+"livemode": false
+"next_action": null
+"next_source_action": null
+"payment_method": "pm_1FMR7ZKmol8YQE9DQWqPuyke"
+"payment_method_types": array:1 []
+"receipt_email": null
+"setup_future_usage": "off_session"
+"shipping": null
+"source": null
+"status": "succeeded"
*/
public function processPaymentResponse($request)
{
$server_response = json_decode($request->input('gateway_response'));
$payment_method = $server_response->payment_method;
$payment_status = $server_response->status;
$save_card = $request->input('store_card');
$gateway_type_id = $request->input('gateway_type_id');
$this->init()
$payment_intent = \Stripe\PaymentIntent::retrieve($server_response->id);
$customer = $payment_intent->customer;
if($save_card)
{
$this->init()
$stripe_payment_method = \Stripe\PaymentMethod::retrieve($payment_method);
$stripe_payment_method_obj = $stripe_payment_method->jsonSerialize();
$stripe_payment_method->attach(['customer' => $customer]);
$payment_meta = new \stdClass;
if($stripe_payment_method_obj['type'] == 'card') {
$payment_meta->exp_month = $stripe_payment_method_obj['card']['exp_month'];
$payment_meta->exp_year = $stripe_payment_method_obj['card']['exp_year'];
$payment_meta->brand = $stripe_payment_method_obj['card']['brand'];
$payment_meta->last4 = $stripe_payment_method_obj['card']['last4'];
$payment_meta->type = $stripe_payment_method_obj['type'];
}
$cgt = new ClientGatewayToken;
$cgt->company_id = $this->client->company->id;
$cgt->client_id = $this->client->id;
$cgt->token = $payment_method;
$cgt->company_gateway_id = $this->company_gateway->id;
$cgt->gateway_type_id = $gateway_type_id;
$cgt->gateway_customer_reference = $customer;
$cgt->meta = $payment_meta;
$cgt->save();
if($is_default == 'true' || $this->client->gateway_tokens->count() == 1)
{
$this->client->gateway_tokens()->update(['is_default'=>0]);
$cgt->is_default = 1;
$cgt->save();
}
}
}
/** /**
* Creates a new String Payment Intent * Creates a new String Payment Intent

View File

@ -2,17 +2,27 @@
@section('pay_now') @section('pay_now')
{!! Former::framework('TwitterBootstrap4'); !!}
{!! Former::horizontal_open()
->id('server_response')
->route('client.payments.response')
->method('POST'); !!}
{!! Former::hidden('gateway_response')->id('gateway_response') !!}
{!! Former::hidden('store_card')->id('store_card') !!}
{!! Former::hidden('hashed_ids')->value($hashed_ids) !!}
{!! Former::hidden('company_gateway_id')->value($payment_method_id) !!}
{!! Former::hidden('payment_method_id')->value($gateway->getCompanyGatewayId()) !!}
{!! Former::close() !!}
@if($token) @if($token)
<div class="py-md-5 ninja stripe"> <div class="py-md-5 ninja stripe">
<div class="form-group"> <div class="form-group">
<input class="form-control" id="cardholder-name" type="text" placeholder="{{ ctrans('texts.name') }}"> <button id="card-button" class="btn btn-primary pull-right" data-secret="{{ $intent->client_secret }}">
</div> {{ ctrans('texts.pay_now') }} - {{ $token->meta->brand }} - {{ $token ->meta->last4}}
<div class="form-group">
<div id="card-element"></div>
</div>
<div class="form-group">
<button id="card-button" data-secret="{{ $intent->client_secret }}">
Submit Payment
</button> </button>
</div> </div>
</div> </div>
@ -29,8 +39,8 @@
</div> </div>
<div class="form-check form-check-inline mr-1"> <div class="form-check form-check-inline mr-1">
<input class="form-check-input" id="proxy_is_default" type="checkbox"> <input class="form-check-input" id="token_billing_checkbox" type="checkbox">
<label class="form-check-label" for="proxy_is_default">{{ ctrans('texts.save_as_default') }}</label> <label class="form-check-label" for="token_billing_checkbox">{{ ctrans('texts.token_billing_checkbox') }}</label>
</div> </div>
@ -38,7 +48,7 @@
<div class="form-group"> <div class="form-group">
<button id="card-button" class="btn btn-primary pull-right" data-secret="{{ $intent->client_secret }}"> <button id="card-button" class="btn btn-primary pull-right" data-secret="{{ $intent->client_secret }}">
{{ ctrans('texts.pay_now') }} {{ ctrans('texts.pay_now') }}
</button> </button>
</div> </div>
</div> </div>
@ -53,9 +63,6 @@
var stripe = Stripe('{{ $gateway->getPublishableKey() }}'); var stripe = Stripe('{{ $gateway->getPublishableKey() }}');
var elements = stripe.elements(); var elements = stripe.elements();
var cardElement = elements.create('card');
cardElement.mount('#card-element');
var cardholderName = document.getElementById('cardholder-name'); var cardholderName = document.getElementById('cardholder-name');
var cardButton = document.getElementById('card-button'); var cardButton = document.getElementById('card-button');
@ -65,7 +72,7 @@
cardButton.addEventListener('click', function(ev) { cardButton.addEventListener('click', function(ev) {
stripe.handleCardPayment( stripe.handleCardPayment(
clientSecret, { clientSecret, {
payment_method: {{$token->token}}, payment_method: '{{$token->token}}',
} }
).then(function(result) { ).then(function(result) {
if (result.error) { if (result.error) {
@ -82,6 +89,10 @@
}); });
}); });
@else @else
var cardElement = elements.create('card');
cardElement.mount('#card-element');
cardButton.addEventListener('click', function(ev) { cardButton.addEventListener('click', function(ev) {
stripe.handleCardPayment( stripe.handleCardPayment(
clientSecret, cardElement, { clientSecret, cardElement, {
@ -103,7 +114,7 @@
} }
}); });
}); });
@endif
$("#card-button").attr("disabled", true); $("#card-button").attr("disabled", true);
$('#cardholder-name').on('input',function(e){ $('#cardholder-name').on('input',function(e){
@ -112,12 +123,13 @@
else else
$("#card-button").attr("disabled", true); $("#card-button").attr("disabled", true);
}); });
@endif
function postResult(result) function postResult(result)
{ {
$("#gateway_response").val(JSON.stringify(result.setupIntent)); $("#gateway_response").val(JSON.stringify(result.paymentIntent));
$("#is_default").val($('#proxy_is_default').is(":checked")); $("#store_card").val($('#token_billing_checkbox').is(":checked"));
$("#card-button").attr("disabled", true); $("#card-button").attr("disabled", true);
$('#server_response').submit(); $('#server_response').submit();
} }

View File

@ -103,7 +103,6 @@ $(function() {
data: function(data) { data: function(data) {
data.client_status = client_statuses; data.client_status = client_statuses;
data.filter = table_filter; data.filter = table_filter;
// data.search.value = table_filter;
} }
}, },

View File

@ -25,6 +25,7 @@ Route::group(['middleware' => ['auth:contact'], 'prefix' => 'client', 'as' => 'c
Route::get('payments', 'ClientPortal\PaymentController@index')->name('payments.index'); Route::get('payments', 'ClientPortal\PaymentController@index')->name('payments.index');
Route::post('payments/process', 'ClientPortal\PaymentController@process')->name('payments.process'); Route::post('payments/process', 'ClientPortal\PaymentController@process')->name('payments.process');
Route::post('payments/process/response', 'ClientPortal\PaymentController@response')->name('payments.response');
Route::get('profile/{client_contact}/edit', 'ClientPortal\ProfileController@edit')->name('profile.edit'); Route::get('profile/{client_contact}/edit', 'ClientPortal\ProfileController@edit')->name('profile.edit');
Route::put('profile/{client_contact}/edit', 'ClientPortal\ProfileController@update')->name('profile.update'); Route::put('profile/{client_contact}/edit', 'ClientPortal\ProfileController@update')->name('profile.update');