Blade + Alpine JS
Implementing the payment gateways using Alpine JS in the frontend.
Include this Alpine JS component into your application.
export default function useTakaden({ routes, redirects, orderAmount, getPayload, enabledProviders = [], selectedProvider, init = () => { }, ...otherProps }) {
return {
orderAmount,
getPayload,
getInitiateUrl() {
return routes.initiate + '/' + this.selectedProvider;
},
getExecuteUrl() {
return routes.execute + '/' + this.selectedProvider;
},
getSuccessUrl() {
return redirects.success + '?' + new URLSearchParams(this.getPayload()).toString();
},
getFailureUrl() {
return redirects.failure + '?' + new URLSearchParams(this.getPayload()).toString();
},
getCompleteUrl() {
return redirects.complete + '?' + new URLSearchParams(this.getPayload()).toString();
},
errors: {},
selectedProvider,
loading: false,
initResponse: {},
paymentProviders: [
{
name: 'bkash',
label: 'bKash',
enabled: enabledProviders.includes('bkash'),
},
{
name: 'rocket',
label: 'Rocket',
enabled: enabledProviders.includes('rocket'),
},
{
name: 'nagad',
label: 'Nagad',
enabled: enabledProviders.includes('nagad'),
},
{
name: 'upay',
label: 'Upay',
enabled: enabledProviders.includes('upay'),
},
{
name: 'bank_transfer',
label: 'Bank Transfer',
enabled: enabledProviders.includes('bank_transfer'),
},
{
name: 'cash',
label: 'Cash on Checkin ',
enabled: enabledProviders.includes('cash'),
},
],
getBkashConfig() {
return {
paymentMode: 'checkout',
paymentRequest: {
amount: this.orderAmount,
intent: 'authorization',
},
createRequest: () => {
this.initPayment()
},
executeRequestOnAuthorization: () => {
this.executePayment({
payment_id: this.initResponse.paymentID,
}).catch(error => {
bKash.execute().onError();
console.log('Bkash execution error', error);
});
},
onClose: () => {
console.log('Close Bkash Modal');
}
};
},
init() {
bKash.init(this.getBkashConfig());
this.$watch('orderAmount', () => {
document.getElementById('bKashFrameWrapper')?.remove();
bKash.init(this.getBkashConfig())
});
},
handleCheckoutFormSubmit() {
switch (this.selectedProvider) {
case 'bkash':
document.getElementById('bKash_button').click();
break;
default:
this.initPayment();
break;
}
},
async initPayment() {
this.loading = true;
this.errors = {};
return axios.post(this.getInitiateUrl(), this.getPayload()).then((response) => {
if (response.data) {
this.initResponse = response.data;
this.proceedWithProvider(true, response.data);
console.log(response.data);
} else {
throw new Error('Whoops! Something went wrong.');
}
}).catch(error => {
if (error.response?.status === 422) {
this.errors = error.response.data.errors;
} else {
this.errors = {
0: error.response?.data?.message || error.message,
};
}
// this.proceedWithProvider(false, error.response);
throw error;
}).finally(() => this.loading = false);
},
async executePayment(payload) {
return axios.post(this.getExecuteUrl(), payload)
.then(response => { window.location.href = this.getSuccessUrl(); })
.catch(error => {
if (error.response?.status === 422) {
this.errors = error.response.data.errors;
} else {
this.errors = {
1: error.response?.data?.message || error.message,
};
}
this.proceedWithProvider(false, error.response);
throw error;
});
},
async proceedWithProvider(success, responseData, provider = this.selectedProvider) {
switch (provider) {
case 'cash':
return this.proceedWithCash(success, responseData);
case 'balance':
return this.proceedWithBalance(success, responseData);
case 'bank_transfer':
return this.proceedWithBankTransfer(success, responseData);
case 'upay':
return this.proceedWithUpay(success, responseData);
case 'bkash':
return this.proceedWithBkash(success, responseData);
case 'nagad':
return this.proceedWithNagad(success, responseData);
case 'rocket':
return this.proceedWithRocket(success, responseData);
}
},
proceedWithCash(success, responseData) {
if (success) {
return window.location.href = this.getCompleteUrl();
}
},
proceedWithBalance(success, responseData) {
if (success) {
return window.location.href = this.getCompleteUrl();
}
},
proceedWithBankTransfer(success, responseData) {
if (success) {
return window.location.href = this.getCompleteUrl();
}
},
proceedWithUpay(success, responseData) {
if (success) {
return window.location.href = responseData;
}
},
proceedWithBkash(success, responseData) {
if (success) {
return bKash.create().onSuccess(responseData);
}
bKash.create().onError();
},
proceedWithNagad(success, responseData) {
if (success) {
return window.location.href = responseData;
}
window.location.href = this.getFailureUrl();
},
proceedWithRocket(success, responseData) {
},
...otherProps,
}
}
if (typeof window !== typeof undefined) {
window.useTakaden = useTakaden;
}
Bkash & SSLCommerz requires JQuery for their payment widget 😒. So, if you want to use either of them you need to include jQuery in the head of your checkout page.
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
If you want to use Bkash you'll also have to include their script after jQuery.
<script src="{{ config('takaden.providers.bkash.script_url') }}"></script>
// Or
<script src="https://scripts.sandbox.bka.sh/versions/1.2.0-beta/checkout/bKash-checkout-sandbox.js"></script>
Now the main portion of your checkout page:
<div
class="max-w-6xl mx-auto"
x-data="takadenCheckout({
routes: {
initiate: '{{ route('takaden.checkout.initiate') }}',
execute: '{{ route('takaden.checkout.execute') }}',
},
redirects: {
success: '{{ route('checkout.success') }}',
failure: '{{ route('checkout.failure') }}',
complete: '{{ route('checkout.complete') }}',
},
orderAmount: @js($payment->due_total),
enabledProviders: ['bkash', 'rocket', 'nagad', 'upay', 'bank_transfer', 'cash'],
selectedProvider: '',
getPayload() {
return {
payment_method: this.selectedProvider,
orderable_id: @js($payment->id),
orderable_type: @js($payment::class),
};
},
getPayableAmount() {
return {{ $payment->due_total }};
},
handleCheckoutFormSubmit() {
switch (this.selectedProvider) {
case 'bkash':
document.getElementById('bKash_button').click();
break;
default:
this.initPayment();
break;
}
},
})"
>
<div class="flex flex-col gap-4 lg:flex-row">
{{-- Payment Info --}}
<x-payment.info :payment="$payment" />
{{-- Checkout --}}
<div class="flex-1 p-4 card">
<form @submit.prevent="handleCheckoutFormSubmit()">
<div>
<p class="mb-2 text-lg font-semibold">Select Payment Method</p>
<div class="flex flex-col gap-2">
<template
x-for="(provider, i) in paymentProviders"
x-key="i"
>
<label class="inline-flex items-center gap-2">
<input
x-model="selectedProvider"
type="radio"
name="payment_provider"
:value="provider.name"
>
<span x-text="provider.label"></span>
</label>
</template>
</div>
</div>
<div class="flex mt-4">
<button
type="submit"
class="btn btn-primary"
:disabled="loading"
>
<i :class="{ 'fa fa-arrow-right': !loading, 'fa fa-circle-notch animate-spin': loading }"></i>
<span>Continue</span>
</button>
{{-- Invisible buttons for triggering popop --}}
<button
type="button"
id="bKash_button"
class="invisible w-0 h-0"
>Pay with bKash</button>
</div>
<template x-if="errors && Object.keys(errors).length > 0">
<ul class="mt-4 space-y-1 text-red-500">
<template x-for="error in errors">
<li x-text="error"></li>
</template>
</ul>
</template>
</form>
</div>
</div>
</div>
Last updated