A plugin to enable Razorpay as a payment provider for Vendure E-commerce
This plugin have inside it a lot of stuff:
- A
PaymentMethodHandler
to createPayments and configure Razorpay transaction - A custom field
customFieldsRazorpay_order_id
on Order to set razorpayOrderId for corresponding vendure order id. - Refund payments in Admin UI
Here you can find out how to install
npm install vendure-razorpay-payment-plugin --save
import { RazorpayPlugin } from 'vendure-razorpay-payment-plugin';
const config: VendureConfig = {
...
plugins: [
RazorpayPlugin
]
}
You will need to enable and configure the options to make work. You can edit this in Payment Method section in Vendure Admin UI
It's done!
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
or load it on-demand for performance reasons like this.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ScriptService {
constructor() { }
scripts = [
{
name: 'razorpay',
src: 'https://checkout.razorpay.com/v1/checkout.js'
},
]
loadScript(name: string) {
return new Promise((resolve, reject) => {
const scriptObject = this.scripts.find((script) => {
if (script.name === name) {
return script;
}
return null;
});
if (scriptObject) {
let script = document.createElement('script');
script.src = scriptObject.src;
script.onload = () => {
resolve(true);
};
script.onerror = (error: any) => resolve(false);
document.getElementsByTagName('head')[0].appendChild(script);
} else {
resolve(false);
}
});
}
}
const isScriptLoaded = await this.scriptService.loadScript('razorpay');
private _razorpayOptions = {
key: environment.razorpayId,
order_id: '',
currency: 'INR',
description: 'Description',
// image: 'https://s3.amazonaws.com/rzp-mobile/images/rzp.png',
prefill: {
email: '',
contact: '',
name: '',
},
config: {
display: {
blocks: {
card: {
instruments: [
{
method: 'card',
// issuers: ["UTIB"],
networks: ['MasterCard', 'Visa', 'RuPay', 'Bajaj Finserv'],
},
],
},
upi: {
name: 'Pay using UPI',
instruments: [
{
method: 'upi',
flows: ['collect', 'intent', 'qr'],
apps: ['google_pay', 'bhim', 'paytm', 'phonepe'],
},
],
},
netbanking: {
// name for other block
name: 'Pay using netbanking',
instruments: [
{
method: 'netbanking',
},
],
},
wallet: {
name: 'Pay using wallets',
instruments: [
{
method: 'wallet',
wallets: ['phonepe', 'freecharge', 'airtelmoney'],
},
],
},
},
sequence: [
'block.card',
'block.upi',
'block.netbanking',
'block.wallet',
],
preferences: {
show_default_blocks: false,
},
},
},
handler: function (response: any) {
console.log(response);
},
modal: {
ondismiss: function () {},
},
};
3. Generate Razorpay order id by calling mutation from plugin ( Order must be in "ArrangingPayment" state )
razorpayService.ts
const GENERATE_RAZORPAY_ORDER_ID = gql`
mutation generateRazorpayOrderId($vendureOrderId: ID!) {
generateRazorpayOrderId(orderId: $vendureOrderId) {
__typename
... on RazorpayOrderIdSuccess {
razorpayOrderId
}
... on RazorpayOrderIdGenerationError {
errorCode
message
}
}
}
`;
generateRazorpayOrderId(vendureOrderId: string | number) {
return this.requestor
.mutate(GENERATE_RAZORPAY_ORDER_ID, {
vendureOrderId,
})
.pipe(map((res) => res.generateRazorpayOrderId)
}
checkout.component.ts
this.orderService.generateRazorpayOrderId(this.orderDetails.id).pipe(
this.updateOrderDetailsGlobally.operator(),
takeUntil(this.destroy$)
)
.subscribe((res) => {
this.onRazorpayIdGeneration(res);
});
onRazorpayIdGeneration(res: any) {
if (res.__typename === 'RazorpayOrderIdSuccess') {
const razorpayOrderId = res.razorpayOrderId;
this.openRazorpayPopup(razorpayOrderId); // Implemented below
} else {
console.log(
'Some error occurred while generating Razorpay orderId',
res.message,
res.errorCode
);
this.razorpayFlowActive = false;
this.cd.detectChanges();
}
}
get Razorpay() {
if (!(window as any).Razorpay) {
throw new Error(
'Can\'t find razorpay. Make sure you have added <script src="https://checkout.razorpay.com/v1/checkout.js"></script> in your index.html file'
);
}
return (window as any).Razorpay;
}
5.1 Get a reference to Angular changeDetector and NgZone
checkout.component.ts
constructor(
...
private cd: ChangeDetectorRef,
private zone: NgZone
...
) {
...
}
5.2 Construct the callbacks
checkout.component.ts
onRazorpayPaymentSuccess(metadata: Object) {
this.cd.detectChanges();
this.orderService
.addRazorpayPaymentToOrder(metadata)
.pipe(
takeUntil(this.destroy$))
.subscribe((res) => {
switch (res.__typename) {
case 'PaymentFailedError':
case 'PaymentDeclinedError':
case 'IneligiblePaymentMethodError':
case 'OrderPaymentStateError':
console.log(res.errorCode, res.message);
break;
case 'Order':
console.log('PAYMENT SUCCESSFUL');
this.zone.run(() => {
this.router.navigate(['..'], {
relativeTo: this.route,
});
})
}
});
}
onRazorpayManualClose() {
if (confirm('Are you sure, you want to close the form?')) {
console.log('Checkout form closed by the user');
this.razorpayFlowActive = false;
this.cd.detectChanges();
} else {
console.log('Complete the Payment');
}
}
razorpayService.ts
addRazorpayPaymentToOrder(paymentMetadata: Object): Observable<Mutation["addPaymentToOrder"]> {
const addPaymentMutationVariable = {
paymentInput: {
method: 'razorpay',
metadata: JSON.stringify(paymentMetadata),
},
};
return this.requestor
.mutate(
ADD_PAYMENT_TO_ORDER_MUTATION,
addPaymentMutationVariable
)
.pipe(map((res) => res.addPaymentToOrder));
}
where ADD_PAYMENT_TO_ORDER_MUTATION
is -
const ADD_PAYMENT_TO_ORDER_MUTATION = gql`
mutation addPaymentToOrder($paymentInput: PaymentInput!) {
addPaymentToOrder(input: $paymentInput) {
__typename
... on Order {
id
}
... on PaymentFailedError {
errorCode
paymentErrorMessage
message
}
... on OrderPaymentStateError {
errorCode
message
}
... on PaymentDeclinedError {
message
paymentErrorMessage
errorCode
}
}
}
`;
razorpayService.ts
get razorpayOptions() {
return this._razorpayOptions;
}
set razorpayOrderId(orderId: string) {
this._razorpayOptions.order_id = orderId;
}
set razorpayPrefill({
email,
contact,
name,
}: {email: string | null, contact: string | null, name: string | null}) {
email = email || '';
contact = contact || '';
name = name || '';
this._razorpayOptions.prefill = {
email, contact, name
};
}
checkout.component.ts
openRazorpayPopup(razorpayOrderId: string) {
try {
const Razorpay = this.razorpayService.Razorpay;
this.razorpayService.razorpayOrderId = razorpayOrderId;
this.razorpayService.razorpayPrefill = {
contact: this.customerDetails.customerPhNo,
email: this.customerDetails.customerEmail,
name: this.customerDetails.customerName,
};
this.razorpayService.razorpaySuccessCallback =
this.onRazorpayPaymentSuccess.bind(this);
this.razorpayService.razorpayManualCloseCallback =
this.onRazorpayManualClose.bind(this);
const rzp = new Razorpay(this.razorpayService.razorpayOptions);
rzp.on('payment.failed', (response: any) => {
// console.log(response);
});
rzp.open();
} catch (e) {
console.log(e);
}
}
Check razorpay docs here
Please, consider supporting my work as a lot of effort takes place to create this repo! Thanks a lot.
MIT