@promactinfo/medusa-payment-phonepe
v2.5.0
Published
PhonePe payment provider for Medusa Commerce
Maintainers
Readme
MEDUSA-PAYMENT-PHONEPE
PhonePe Payment provider for MedusaJS v2.x
PHONEPE is an immensely popular payment gateway with a host of features. This plugin enables the PhonePe payment interface on MedusaJS v2.x commerce stack.
Version
v2.0.0 - This version is compatible with:
- MedusaJS v2.x
- PhonePe Payment Gateway API v2 (with OAuth authentication)
Installation
Install the plugin using your package manager:
yarn add medusa-payment-phonepe
# or
npm install medusa-payment-phonepe
# or
pnpm add medusa-payment-phonepePrerequisites
- MedusaJS v2.x installed and running
- PhonePe merchant account with:
- Merchant ID
- Salt key (for webhook verification)
- OAuth Client ID
- OAuth Client Secret
Configuration
Environment Variables
Add the following to your .env file:
PHONEPE_SALT=<your-supplied-salt>
PHONEPE_MODE=production|uat|test
PHONEPE_MERCHANT_ID=<your-merchant-id>
PHONEPE_CLIENT_ID=<your-oauth-client-id>
PHONEPE_CLIENT_SECRET=<your-oauth-client-secret>
PHONEPE_REDIRECT_URL=<your-redirect-url>
PHONEPE_CALLBACK_URL=<your-callback-url>
PHONEPE_MERCHANT_USERNAME=<your-merchant-username> # Required for webhook validation
PHONEPE_MERCHANT_PASSWORD=<your-merchant-password> # Required for webhook validationMedusaJS Configuration
Add the plugin to your medusa-config.ts (or medusa-config.js):
import { defineConfig } from "@medusajs/framework/utils";
export default defineConfig({
// ... other config
plugins: [
// ... other plugins
{
resolve: "medusa-payment-phonepe",
options: {
redirectUrl:
process.env.PHONEPE_REDIRECT_URL ||
"http://localhost:8000/api/payment-confirmed",
callbackUrl:
process.env.PHONEPE_CALLBACK_URL ||
"http://localhost:9000/phonepe/hooks",
salt: process.env.PHONEPE_SALT,
merchantId: process.env.PHONEPE_MERCHANT_ID,
clientId: process.env.PHONEPE_CLIENT_ID,
clientSecret: process.env.PHONEPE_CLIENT_SECRET,
clientVersion: 1, // PhonePe client version (typically 1)
mode:
(process.env.PHONEPE_MODE as "production" | "uat" | "test") || "test",
merchantUsername: process.env.PHONEPE_MERCHANT_USERNAME, // Required for webhook validation
merchantPassword: process.env.PHONEPE_MERCHANT_PASSWORD, // Required for webhook validation
tokenCacheEnabled: true, // Optional: Enable OAuth token caching (default: true)
enabledDebugLogging: false, // Optional: Enable debug logging
},
},
],
});Configuration Options
| Option | Type | Required | Description |
| --------------------- | ------- | ----------- | --------------------------------------------------------------------- |
| redirectUrl | string | Yes | URL to redirect customers after payment |
| callbackUrl | string | Yes | Server-to-server webhook callback URL |
| salt | string | Yes | PhonePe provided salt key (legacy, kept for backward compatibility) |
| merchantId | string | Yes | Your PhonePe merchant ID |
| clientId | string | Yes | OAuth client ID for PhonePe API v2 |
| clientSecret | string | Yes | OAuth client secret for PhonePe API v2 |
| clientVersion | number | Yes | PhonePe client version (typically 1) |
| mode | string | Yes | production, uat, or test |
| merchantUsername | string | Recommended | Merchant username for webhook validation (recommended for production) |
| merchantPassword | string | Recommended | Merchant password for webhook validation (recommended for production) |
| tokenCacheEnabled | boolean | No | Enable OAuth token caching (default: true) |
| enabledDebugLogging | boolean | No | Enable debug logging (default: false) |
API Endpoints
The plugin uses PhonePe Payment Gateway API v2 with the following endpoints:
- Payment Creation:
/checkout/v2/pay - Order Status:
/checkout/v2/order/{merchantOrderId}/status - Refund:
/checkout/v2/refund - OAuth Token:
/v1/oauth/token
Webhook Events
The plugin supports the following PhonePe webhook events:
checkout.order.completed(PG_ORDER_COMPLETED) - Payment succeededcheckout.order.failed(PG_ORDER_FAILED) - Payment failedpg.refund.completed(PG_REFUND_COMPLETED) - Refund completedpg.refund.failed(PG_REFUND_FAILED) - Refund failed
Webhook Validation
The plugin uses PhonePe's official SDK (pg-sdk-node) for webhook validation. The SDK's validateCallback method is used to verify the authenticity of webhook callbacks from PhonePe.
Recommended Configuration:
- Set
merchantUsernameandmerchantPasswordin your configuration to enable SDK-based webhook validation - The plugin will automatically use the SDK's validation method when these credentials are provided
- If credentials are not provided, the plugin falls back to legacy signature-based validation
Webhook Endpoint:
- Default endpoint:
/phonepe/hooks - Configure this URL in your PhonePe merchant dashboard as the callback URL
Migration from v1.x to v2.x
Breaking Changes
- MedusaJS Version: Requires MedusaJS v2.x (no backward compatibility with v1.x)
- OAuth Authentication: PhonePe API v2 requires OAuth authentication (clientId and clientSecret)
- API Endpoints: Updated to PhonePe API v2 endpoints
- Configuration: New required fields (
clientId,clientSecret)
Migration Steps
Upgrade MedusaJS: Ensure your MedusaJS installation is upgraded to v2.x
Update Plugin Version: Install the latest version of the plugin:
yarn add medusa-payment-phonepe@^2.0.0Obtain OAuth Credentials: Get your OAuth Client ID and Client Secret from PhonePe dashboard
Update Configuration: Add OAuth credentials to your plugin configuration:
{ resolve: "medusa-payment-phonepe", options: { // ... existing options clientId: process.env.PHONEPE_CLIENT_ID, // NEW clientSecret: process.env.PHONEPE_CLIENT_SECRET, // NEW }, }Update Environment Variables: Add OAuth credentials to your
.envfileTest Integration: Test the payment flow in UAT mode before going to production
Client-Side Integration
Next.js Example
For Next.js applications, create a payment confirmation route:
// app/api/payment-confirmed/route.ts
import { NextRequest, NextResponse } from "next/server";
import { medusaClient } from "@lib/config";
export async function POST(request: NextRequest) {
const data = await request.formData();
const merchantOrderId = data.get("merchantOrderId") as string;
const code = data.get("code") as string;
if (!merchantOrderId || code !== "SUCCESS") {
return NextResponse.redirect(new URL("/cart", request.url));
}
// Extract cart ID from merchantOrderId (format: cartId_sequence)
const cartIdParts = merchantOrderId.split("_");
const cartId = `${cartIdParts[0]}_${cartIdParts[1]}`;
try {
// Wait for order to be created by webhook
let orderId;
let attempts = 0;
while (!orderId && attempts < 10) {
await new Promise((resolve) => setTimeout(resolve, 1000));
try {
const order = await medusaClient.orders.retrieveByCartId(cartId);
orderId = order.order.id;
} catch (e) {
attempts++;
}
}
if (orderId) {
return NextResponse.redirect(
new URL(`/order/confirmed/${orderId}`, request.url)
);
}
} catch (error) {
console.error("Payment confirmation error:", error);
}
return NextResponse.redirect(new URL("/cart", request.url));
}Features
- ✅ OAuth authentication for PhonePe API v2
- ✅ Automatic token caching and refresh
- ✅ Payment initiation and status checking
- ✅ Webhook handling for payment events
- ✅ Refund support
- ✅ Multiple payment modes (UPI, Cards, Net Banking)
- ✅ UAT and Production mode support
Testing
UAT Mode
Set mode: "uat" in your configuration to test against PhonePe's UAT environment:
{
mode: "uat",
// ... other options
}Test Mode
Set mode: "test" for local testing (mocked responses).
Troubleshooting
OAuth Token Issues
If you encounter OAuth authentication errors:
- Verify your
clientIdandclientSecretare correct - Check that your PhonePe account has API access enabled
- Ensure your environment variables are properly loaded
Webhook Issues
If webhooks are not being received:
- Verify your
callbackUrlis publicly accessible - Check webhook signature verification
- Ensure your
saltkey matches PhonePe dashboard
Payment Status Issues
If payment status checks fail:
- Verify the
merchantOrderIdformat - Check that the order exists in PhonePe system
- Review debug logs (enable
enabledDebugLogging: true)
Contributing
Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
Please make sure to:
- Update tests as appropriate
- Follow the existing code style
- Update documentation for any API changes
License
Support
For issues and questions:
- Open an issue on GitHub
- Check the PhonePe Developer Documentation
- Review the MedusaJS Documentation
Disclaimer
The code was tested on a limited number of usage scenarios. There may be unforeseen bugs. Please raise issues as they come, or create pull requests if you'd like to submit fixes.
