Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | import { PaymentProvider } from '../payment-provider.interface';
import Razorpay from 'razorpay';
import * as crypto from 'crypto';
import { Logger } from '@nestjs/common';
export class RazorpayProvider implements PaymentProvider {
name = 'Razorpay';
private razorpay: any;
private readonly logger = new Logger(RazorpayProvider.name);
private webhookSecret: string;
constructor(keyId: string, keySecret: string, webhookSecret: string = '') {
this.razorpay = new Razorpay({
key_id: keyId,
key_secret: keySecret,
});
this.webhookSecret = webhookSecret;
}
async createCheckoutLink(params: {
tenantId: string;
amount: number;
currency: string;
invoiceId: string;
callbackUrl: string;
}): Promise<{ checkoutUrl: string; providerIntentId: string }> {
try {
// Razorpay Payment Links API
const paymentLink = await this.razorpay.paymentLink.create({
amount: params.amount,
currency: params.currency,
accept_partial: false,
reference_id: params.invoiceId,
description: `Invoice ${params.invoiceId}`,
customer: {
name: params.tenantId, // Ideally get from DB
contact: '',
email: '',
},
notify: {
sms: false,
email: false,
},
reminder_enable: false,
notes: {
invoiceId: params.invoiceId,
tenantId: params.tenantId,
},
callback_url: params.callbackUrl,
callback_method: 'get',
});
return {
checkoutUrl: paymentLink.short_url,
providerIntentId: paymentLink.id,
};
} catch (error: any) {
this.logger.error(`Razorpay create checkout failed: ${error.message}`);
throw error;
}
}
async verifyWebhook(
payload: any,
signature: string,
): Promise<{
success: boolean;
transactionId: string;
amountPaid: number;
status: 'SUCCESS' | 'FAILED';
}> {
// Verify signature
if (this.webhookSecret && typeof payload === 'string') {
const expectedSignature = crypto
.createHmac('sha256', this.webhookSecret)
.update(payload)
.digest('hex');
if (expectedSignature !== signature) {
this.logger.error('Razorpay webhook signature verification failed');
return {
success: false,
transactionId: '',
amountPaid: 0,
status: 'FAILED',
};
}
}
const event = typeof payload === 'string' ? JSON.parse(payload) : payload;
if (event.event === 'payment_link.paid') {
const paymentLink = event.payload.payment_link.entity;
return {
success: true,
transactionId: paymentLink.id,
amountPaid: paymentLink.amount_paid || 0,
status: 'SUCCESS',
};
}
return {
success: true,
transactionId: '',
amountPaid: 0,
status: 'FAILED',
};
}
async refund(
transactionId: string,
amount: number,
): Promise<{ success: boolean; refundId: string }> {
try {
// Razorpay refunds require the actual payment ID, which we'd typically store
// Assuming transactionId here is the payment ID
const refund = await this.razorpay.payments.refund(transactionId, {
amount,
});
return { success: true, refundId: refund.id };
} catch (error: any) {
this.logger.error(`Razorpay refund failed: ${error.message}`);
return { success: false, refundId: '' };
}
}
}
|