Upgraded to medusajs 2.5.0 and the razorpay payment module is tested now. It is accepting basic payment. Some functions might still not be working.
This commit is contained in:
35
package.json
35
package.json
@ -22,29 +22,29 @@
|
||||
"test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/admin-sdk": "^2.4.0",
|
||||
"@medusajs/cache-redis": "^2.4.0",
|
||||
"@medusajs/cli": "^2.4.0",
|
||||
"@medusajs/event-bus-redis": "^2.4.0",
|
||||
"@medusajs/file-s3": "^2.4.0",
|
||||
"@medusajs/framework": "^2.4.0",
|
||||
"@medusajs/medusa": "^2.4.0",
|
||||
"@medusajs/workflow-engine-redis": "^2.4.0",
|
||||
"@mikro-orm/core": "^6.4.3",
|
||||
"@mikro-orm/knex": "^6.4.3",
|
||||
"@mikro-orm/migrations": "^6.4.3",
|
||||
"@mikro-orm/postgresql": "^6.4.3",
|
||||
"@medusajs/admin-sdk": "^2.5.0",
|
||||
"@medusajs/cache-redis": "^2.5.0",
|
||||
"@medusajs/cli": "^2.5.0",
|
||||
"@medusajs/event-bus-redis": "^2.5.0",
|
||||
"@medusajs/file-s3": "^2.5.0",
|
||||
"@medusajs/framework": "^2.5.0",
|
||||
"@medusajs/medusa": "^2.5.0",
|
||||
"@medusajs/workflow-engine-redis": "^2.5.0",
|
||||
"@mikro-orm/core": "6.4.3",
|
||||
"@mikro-orm/knex": "6.4.3",
|
||||
"@mikro-orm/migrations": "6.4.3",
|
||||
"@mikro-orm/postgresql": "6.4.3",
|
||||
"awilix": "^8.0.1",
|
||||
"pg": "^8.13.0",
|
||||
"razorpay": "^2.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@medusajs/test-utils": "^2.4.0",
|
||||
"@mikro-orm/cli": "^6.4.3",
|
||||
"@medusajs/test-utils": "^2.5.0",
|
||||
"@mikro-orm/cli": "6.4.3",
|
||||
"@swc/core": "1.5.7",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/react": "^18.3.2",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"jest": "^29.7.0",
|
||||
@ -54,9 +54,10 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.2",
|
||||
"vite": "^5.2.11"
|
||||
"vite": "^5.2.11",
|
||||
"yalc": "^1.0.0-pre.53"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">=22"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,39 @@
|
||||
import Razorpay from 'razorpay';
|
||||
// Adjust these imports to match your actual Medusa versions/paths
|
||||
import { AbstractPaymentProvider, BigNumber, MedusaError, PaymentSessionStatus } from '@medusajs/framework/utils';
|
||||
|
||||
import {
|
||||
CreatePaymentProviderSession,
|
||||
AuthorizePaymentInput,
|
||||
AuthorizePaymentOutput,
|
||||
CancelPaymentInput,
|
||||
CancelPaymentOutput,
|
||||
CapturePaymentInput,
|
||||
CapturePaymentOutput,
|
||||
DeletePaymentInput,
|
||||
DeletePaymentOutput,
|
||||
GetPaymentStatusInput,
|
||||
GetPaymentStatusOutput,
|
||||
InitiatePaymentInput,
|
||||
InitiatePaymentOutput,
|
||||
Logger,
|
||||
ProviderWebhookPayload,
|
||||
RefundPaymentInput,
|
||||
RefundPaymentOutput,
|
||||
RetrievePaymentInput,
|
||||
RetrievePaymentOutput,
|
||||
UpdatePaymentInput,
|
||||
UpdatePaymentOutput,
|
||||
WebhookActionResult,
|
||||
} from '@medusajs/framework/types';
|
||||
import { AbstractPaymentProvider, BigNumber, MedusaError, PaymentSessionStatus } from '@medusajs/framework/utils';
|
||||
import Razorpay from 'razorpay';
|
||||
// Adjust these imports to match your actual Medusa versions/paths
|
||||
|
||||
import {
|
||||
UpdatePaymentProviderSession,
|
||||
PaymentProviderError,
|
||||
PaymentProviderSessionResponse,
|
||||
} from '@medusajs/framework/types';
|
||||
|
||||
type RazorpayProviderOptions = {
|
||||
interface RazorpayProviderOptions {
|
||||
key_id: string;
|
||||
key_secret: string;
|
||||
// Add other config options if needed
|
||||
};
|
||||
}
|
||||
|
||||
type InjectedDependencies = {
|
||||
interface InjectedDependencies {
|
||||
logger: Logger;
|
||||
};
|
||||
}
|
||||
|
||||
export class RazorpayPaymentProvider extends AbstractPaymentProvider<RazorpayProviderOptions> {
|
||||
protected logger_: Logger;
|
||||
@ -54,51 +64,31 @@ export class RazorpayPaymentProvider extends AbstractPaymentProvider<RazorpayPro
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a payment session (e.g. create an order in Razorpay).
|
||||
* Return a PaymentProviderSessionResponse:
|
||||
* { session_data: Record<string, unknown> }
|
||||
*/
|
||||
async initiatePayment(
|
||||
context: CreatePaymentProviderSession,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
||||
// Example: create an order in Razorpay
|
||||
const { currency_code, context: customerDetails } = context;
|
||||
async initiatePayment(data: InitiatePaymentInput): Promise<InitiatePaymentOutput> {
|
||||
// Create an order in Razorpay
|
||||
const { currency_code, amount } = data;
|
||||
try {
|
||||
const amount = (context.amount as number) * 100;
|
||||
// const currency = (data.currency as string) || "INR"
|
||||
const order = await this.razorpay.orders.create({
|
||||
amount,
|
||||
amount: 100 * (amount as number),
|
||||
currency: currency_code.toUpperCase(),
|
||||
receipt: `receipt_${Date.now()}`,
|
||||
payment_capture: true, // auto-capture
|
||||
});
|
||||
|
||||
// Return a PaymentProviderSessionResponse
|
||||
return {
|
||||
...order,
|
||||
id: order.id,
|
||||
data: {
|
||||
id: order.id,
|
||||
...order,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the payment session data from Razorpay or from your local records.
|
||||
* Must return either session_data or throw a PaymentProviderError.
|
||||
*/
|
||||
async retrievePayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
const externalId = paymentSessionData.id;
|
||||
async retrievePayment(data: RetrievePaymentInput): Promise<RetrievePaymentOutput> {
|
||||
const externalId = data.data.id;
|
||||
try {
|
||||
const razorpayOrderId = externalId as string;
|
||||
if (!razorpayOrderId) {
|
||||
@ -108,158 +98,95 @@ export class RazorpayPaymentProvider extends AbstractPaymentProvider<RazorpayPro
|
||||
// Example: fetch the order from Razorpay
|
||||
const order = await this.razorpay.orders.fetch(razorpayOrderId);
|
||||
|
||||
return {
|
||||
...order,
|
||||
status: order.status || 'created',
|
||||
id: externalId,
|
||||
};
|
||||
return { data: { ...order, status: order.status || 'created', id: externalId } };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the payment session. The signature typically expects:
|
||||
* (context: UpdatePaymentProviderSession) => Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
||||
*/
|
||||
async updatePayment(
|
||||
context: UpdatePaymentProviderSession,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
||||
// Deconstruct the data from context
|
||||
const { amount, currency_code, context: customerDetails, data } = context;
|
||||
const externalId = data.id;
|
||||
async updatePayment(data: UpdatePaymentInput): Promise<UpdatePaymentOutput> {
|
||||
const { amount, currency_code, data: ppiData } = data;
|
||||
const externalId = ppiData.id;
|
||||
try {
|
||||
const response = await this.razorpay.payments.edit(externalId as string, {
|
||||
notes: {
|
||||
amount: amount as number,
|
||||
currency: currency_code,
|
||||
currency: currency_code.toUpperCase(),
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
...response,
|
||||
data: {
|
||||
id: response.id,
|
||||
},
|
||||
data: { ...response },
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize the payment (e.g., verify webhook signature, check that payment is successful).
|
||||
* Return updated session data or PaymentProviderError.
|
||||
*/
|
||||
async authorizePayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
context: Record<string, unknown>,
|
||||
): Promise<
|
||||
| PaymentProviderError
|
||||
| {
|
||||
status: PaymentSessionStatus;
|
||||
data: PaymentProviderSessionResponse['data'];
|
||||
}
|
||||
> {
|
||||
const externalId = paymentSessionData.id;
|
||||
async authorizePayment(data: AuthorizePaymentInput): Promise<AuthorizePaymentOutput> {
|
||||
const externalId = data.data.id;
|
||||
try {
|
||||
console.log('authorizePayment externalId', externalId);
|
||||
// Here you could verify a signature or confirm the payment status in Razorpay
|
||||
const paymentData = await this.razorpay.orders.fetchPayments(externalId as string);
|
||||
// if the externalId is paymentId, then use this.razorpay.payments.fetch(externalId as string)
|
||||
// const paymentData = await this.razorpay.payments.fetch(externalId as string)
|
||||
return {
|
||||
data: {
|
||||
...paymentSessionData,
|
||||
id: externalId,
|
||||
rporder: data.data,
|
||||
rpauth: paymentData,
|
||||
id: paymentData.items[0].id,
|
||||
status: this.rpToMedusaStatus(paymentData.items[0].status),
|
||||
},
|
||||
status: PaymentSessionStatus.AUTHORIZED,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error,
|
||||
code: 'unknown',
|
||||
detail: error,
|
||||
status: this.rpToMedusaStatus(paymentData.items[0].status),
|
||||
};
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture the payment if not auto-captured.
|
||||
*/
|
||||
async capturePayment(
|
||||
paymentData: Record<string, unknown>,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
async capturePayment(data: CapturePaymentInput): Promise<CapturePaymentOutput> {
|
||||
try {
|
||||
const externalId = paymentData.id;
|
||||
const { amount } = paymentData;
|
||||
if (data.data.status === 'captured') {
|
||||
return data;
|
||||
}
|
||||
|
||||
const externalId = data.data.id;
|
||||
const amount = (data.data.rporder as { amount: number }).amount;
|
||||
console.log('capturePayment', data, externalId, amount);
|
||||
// e.g. this.razorpay.payments.capture(paymentId, amount) if not auto-captured
|
||||
const newData = await this.razorpay.payments.capture(externalId as string, amount as number, 'INR');
|
||||
return {
|
||||
...newData,
|
||||
// status: "captured",
|
||||
id: externalId,
|
||||
};
|
||||
return { data: { ...newData, id: externalId } };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
console.log('capturePayment error', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refund the payment in Razorpay.
|
||||
*/
|
||||
async refundPayment(
|
||||
paymentData: Record<string, unknown>,
|
||||
refundAmount: number,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
const externalId = paymentData.id;
|
||||
async refundPayment(data: RefundPaymentInput): Promise<RefundPaymentOutput> {
|
||||
const externalId = data.data.id;
|
||||
try {
|
||||
const newData = await this.razorpay.payments.refund(externalId as string, { amount: refundAmount });
|
||||
return {
|
||||
...newData,
|
||||
id: externalId,
|
||||
// refunded_amount: refundAmount,
|
||||
// status: "refunded",
|
||||
};
|
||||
const newData = await this.razorpay.payments.refund(externalId as string, { amount: data.amount as number });
|
||||
return { data: { ...newData, id: externalId } };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a payment if it hasn't been paid. For Razorpay, you may just mark the order as canceled in your system.
|
||||
*/
|
||||
async cancelPayment(
|
||||
paymentData: Record<string, unknown>,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
async cancelPayment(data: CancelPaymentInput): Promise<CancelPaymentOutput> {
|
||||
try {
|
||||
const externalId = paymentData.id;
|
||||
return {
|
||||
id: externalId,
|
||||
status: 'canceled',
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error,
|
||||
code: 'unknown',
|
||||
detail: error,
|
||||
};
|
||||
const externalId = data.data.id;
|
||||
return { data: { id: externalId, status: 'canceled' } };
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,23 +196,16 @@ export class RazorpayPaymentProvider extends AbstractPaymentProvider<RazorpayPro
|
||||
* (paymentSessionData: Record<string, unknown>) => Promise<Record<string, unknown> | PaymentProviderError>
|
||||
* or might require returning PaymentProviderSessionResponse. Adjust if needed.
|
||||
*/
|
||||
async deletePayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
const externalId = paymentSessionData.id;
|
||||
async deletePayment(data: DeletePaymentInput): Promise<DeletePaymentOutput> {
|
||||
const externalId = data.data.id;
|
||||
try {
|
||||
// If Razorpay does not allow deleting an order, just mark locally as deleted
|
||||
// Return the final state or an empty object
|
||||
return {
|
||||
id: externalId,
|
||||
deleted: true,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error,
|
||||
code: 'unknown',
|
||||
detail: error,
|
||||
data: { id: externalId, deleted: true },
|
||||
};
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,11 +213,15 @@ export class RazorpayPaymentProvider extends AbstractPaymentProvider<RazorpayPro
|
||||
* Get the status (PaymentSessionStatus) for the payment.
|
||||
* Must return a PaymentSessionStatus enum value (e.g., "AUTHORIZED", "PENDING", "REQUIRES_MORE", "ERROR", etc.)
|
||||
*/
|
||||
async getPaymentStatus(paymentSessionData: Record<string, unknown>): Promise<PaymentSessionStatus> {
|
||||
async getPaymentStatus(data: GetPaymentStatusInput): Promise<GetPaymentStatusOutput> {
|
||||
// For a minimal example:
|
||||
const payment = await this.razorpay.payments.fetch(paymentSessionData.id as string);
|
||||
const payment = await this.razorpay.payments.fetch(data.data.id as string);
|
||||
const status = payment.status;
|
||||
switch (status?.toLowerCase()) {
|
||||
return { status: this.rpToMedusaStatus(status) };
|
||||
}
|
||||
|
||||
rpToMedusaStatus(rpStatus: string): PaymentSessionStatus {
|
||||
switch (rpStatus.toLowerCase()) {
|
||||
case 'created':
|
||||
return PaymentSessionStatus.PENDING;
|
||||
case 'authorized':
|
||||
@ -305,7 +229,7 @@ export class RazorpayPaymentProvider extends AbstractPaymentProvider<RazorpayPro
|
||||
case 'captured':
|
||||
return PaymentSessionStatus.CAPTURED;
|
||||
case 'refunded':
|
||||
return PaymentSessionStatus.CANCELED; // Payment was authorized before refund
|
||||
return PaymentSessionStatus.CANCELED;
|
||||
case 'failed':
|
||||
return PaymentSessionStatus.ERROR;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user