Credit card (Stripe):

- Wrapped Stripe card widget into div
- Default intent option is setup_future_use now
- Support for token billing
This commit is contained in:
Benjamin Beganović 2021-01-21 14:03:38 +01:00
parent 2fccfe2df1
commit 056bee1c47
6 changed files with 79 additions and 53 deletions

View File

@ -66,13 +66,7 @@ class CreditCard
'description' => collect($data['invoices'])->pluck('id'), // TODO: More meaningful description. 'description' => collect($data['invoices'])->pluck('id'), // TODO: More meaningful description.
]; ];
if ($data['token']) { $payment_intent_data['setup_future_usage'] = 'off_session';
$payment_intent_data['payment_method'] = $data['token']->token;
} else {
$payment_intent_data['setup_future_usage'] = 'off_session';
// $payment_intent_data['save_payment_method'] = true;
// $payment_intent_data['confirm'] = true;
}
$data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data); $data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data);
$data['gateway'] = $this->stripe; $data['gateway'] = $this->stripe;

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@
"/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=935645b176c73b7831f4", "/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=935645b176c73b7831f4",
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=c4012ad90f17d60432ad", "/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=c4012ad90f17d60432ad",
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=6dbe9316b98deea55421", "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=6dbe9316b98deea55421",
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=f4659d26a26d2f408397", "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=e2ccaed999d2ae077c1f",
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=9b9fd56d655ad238f149", "/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=9b9fd56d655ad238f149",
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1b8f9325aa6e8595e7fa", "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1b8f9325aa6e8595e7fa",
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12", "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12",

View File

@ -9,9 +9,8 @@
*/ */
class StripeCreditCard { class StripeCreditCard {
constructor(key, token, secret, onlyAuthorization) { constructor(key, secret, onlyAuthorization) {
this.key = key; this.key = key;
this.token = token;
this.secret = secret; this.secret = secret;
this.onlyAuthorization = onlyAuthorization; this.onlyAuthorization = onlyAuthorization;
} }
@ -36,9 +35,11 @@ class StripeCreditCard {
} }
completePaymentUsingToken() { completePaymentUsingToken() {
let payNowButton = document.getElementById('pay-now-with-token'); let token = document.querySelector('input[name=token]').value;
let payNowButton = document.getElementById('pay-now');
this.payNowButton = payNowButton; this.payNowButton = payNowButton;
this.payNowButton.disabled = true; this.payNowButton.disabled = true;
this.payNowButton.querySelector('svg').classList.remove('hidden'); this.payNowButton.querySelector('svg').classList.remove('hidden');
@ -46,7 +47,7 @@ class StripeCreditCard {
this.stripe this.stripe
.handleCardPayment(this.secret, { .handleCardPayment(this.secret, {
payment_method: this.token, payment_method: token,
}) })
.then((result) => { .then((result) => {
if (result.error) { if (result.error) {
@ -71,7 +72,7 @@ class StripeCreditCard {
this.stripe this.stripe
.handleCardPayment(this.secret, this.cardElement, { .handleCardPayment(this.secret, this.cardElement, {
payment_method_data: { payment_method_data: {
billing_details: { name: cardHolderName.value }, billing_details: {name: cardHolderName.value},
}, },
}) })
.then((result) => { .then((result) => {
@ -126,7 +127,7 @@ class StripeCreditCard {
this.stripe this.stripe
.handleCardSetup(this.secret, this.cardElement, { .handleCardSetup(this.secret, this.cardElement, {
payment_method_data: { payment_method_data: {
billing_details: { name: cardHolderName.value }, billing_details: {name: cardHolderName.value},
}, },
}) })
.then((result) => { .then((result) => {
@ -158,23 +159,33 @@ class StripeCreditCard {
return this.handleAuthorization(); return this.handleAuthorization();
}); });
} else { } else {
if (this.token) { Array
document .from(document.getElementsByClassName('toggle-payment-with-token'))
.getElementById('pay-now-with-token') .forEach((element) => element.addEventListener('click', (element) => {
.addEventListener('click', () => { document.getElementById('stripe--payment-container').classList.add('hidden');
document.querySelector('input[name=token]').value = element.target.dataset.token;
}));
document
.getElementById('toggle-payment-with-credit-card')
.addEventListener('click', (element) => {
document.getElementById('stripe--payment-container').classList.remove('hidden');
document.querySelector('input[name=token]').value = "";
});
this.createElement().mountCardElement();
document
.getElementById('pay-now')
.addEventListener('click', () => {
let tokenInput = document.querySelector('input[name=token]');
if (tokenInput.value) {
return this.completePaymentUsingToken(); return this.completePaymentUsingToken();
}); }
}
if (!this.token) { return this.completePaymentWithoutToken();
this.createElement().mountCardElement(); });
document
.getElementById('pay-now')
.addEventListener('click', () => {
return this.completePaymentWithoutToken();
});
}
} }
} }
} }
@ -182,12 +193,10 @@ class StripeCreditCard {
const publishableKey = const publishableKey =
document.querySelector('meta[name="stripe-publishable-key"]').content ?? ''; document.querySelector('meta[name="stripe-publishable-key"]').content ?? '';
const token = document.querySelector('meta[name="stripe-token"]').content ?? '';
const secret = const secret =
document.querySelector('meta[name="stripe-secret"]').content ?? ''; document.querySelector('meta[name="stripe-secret"]').content ?? '';
const onlyAuthorization = const onlyAuthorization =
document.querySelector('meta[name="only-authorization"]').content ?? ''; document.querySelector('meta[name="only-authorization"]').content ?? '';
new StripeCreditCard(publishableKey, token, secret, onlyAuthorization).handle(); new StripeCreditCard(publishableKey, secret, onlyAuthorization).handle();

View File

@ -3,7 +3,6 @@
@section('gateway_head') @section('gateway_head')
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}"> <meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
<meta name="stripe-secret" content="{{ $intent->client_secret }}"> <meta name="stripe-secret" content="{{ $intent->client_secret }}">
<meta name="stripe-token" content="{{ optional($token)->token }}">
<meta name="only-authorization" content=""> <meta name="only-authorization" content="">
@endsection @endsection
@ -16,6 +15,8 @@
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}"> <input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}"> <input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
<input type="hidden" name="token">
</form> </form>
<div class="alert alert-failure mb-4" hidden id="errors"></div> <div class="alert alert-failure mb-4" hidden id="errors"></div>
@ -26,16 +27,36 @@
@include('portal.ninja2020.gateways.includes.payment_details') @include('portal.ninja2020.gateways.includes.payment_details')
@if($token) @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
@include('portal.ninja2020.gateways.stripe.includes.pay_with_token') @if(count($tokens) > 0)
@include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'pay-now-with-token']) @foreach($tokens as $token)
@else <label class="mr-4">
@include('portal.ninja2020.gateways.stripe.includes.card_widget') <input
@include('portal.ninja2020.gateways.includes.pay_now') type="radio"
@endif data-token="{{ $token->token }}"
name="payment-type"
class="form-radio cursor-pointer toggle-payment-with-token"/>
<span class="ml-1 cursor-pointer">**** {{ optional($token->meta)->last4 }}</span>
</label>
@endforeach
@endisset
<label>
<input
type="radio"
id="toggle-payment-with-credit-card"
class="form-radio cursor-pointer"
name="payment-type"
checked/>
<span class="ml-1 cursor-pointer">{{ __('texts.credit_card') }}</span>
</label>
@endcomponent
@include('portal.ninja2020.gateways.stripe.includes.card_widget')
@include('portal.ninja2020.gateways.includes.pay_now')
@endsection @endsection
@section('gateway_footer') @section('gateway_footer')
<script src="https://js.stripe.com/v3/"></script> <script src="https://js.stripe.com/v3/"></script>
<script src="{{ asset('js/clients/payments/stripe-credit-card.js') }}"></script> <script src="{{ asset('js/clients/payments/stripe-credit-card.js') }}"></script>
@endsection @endsection

View File

@ -1,13 +1,15 @@
@unless(isset($show_name) && $show_name == false) <div id="stripe--payment-container">
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.name')]) @unless(isset($show_name) && $show_name == false)
<input class="input w-full" id="cardholder-name" type="text" placeholder="{{ ctrans('texts.name') }}"> @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.name')])
@endcomponent <input class="input w-full" id="cardholder-name" type="text" placeholder="{{ ctrans('texts.name') }}">
@endunless @endcomponent
@endunless
@unless(isset($show_card_element) && $show_card_element == false) @unless(isset($show_card_element) && $show_card_element == false)
@component('portal.ninja2020.components.general.card-element-single') @component('portal.ninja2020.components.general.card-element-single')
<div id="card-element"></div> <div id="card-element"></div>
@endcomponent @endcomponent
@endunless @endunless
</div>
@include('portal.ninja2020.gateways.includes.save_card') @include('portal.ninja2020.gateways.includes.save_card')