mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-31 00:54:32 -04:00
Payment responses and saving card details
This commit is contained in:
parent
89330e6e34
commit
5090c963d3
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -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');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user