PayU
Enable payments with PayU
PayU Payment Plugin for MedusaJS 2
PayU India payment gateway plugin for MedusaJS 2.x with redirect-based checkout flow.
Features
- ✅ Redirect-based checkout - Seamless PayU hosted checkout integration
- ✅ Webhook support - Automatic payment status updates via PayU webhooks
- ✅ Refund support - Full and partial refunds through PayU API
- ✅ Hash verification - Secure SHA-512 transaction validation
- ✅ TypeScript - Full type safety with comprehensive type definitions
- ✅ Payment verification workflow - Built-in workflow for custom payment verification
Installation
123npm install medusa-payu-payment-plugin# oryarn add medusa-payu-payment-plugin
Configuration
1. Environment Variables
Add to your Copy to clipboard.env file:
123456789# PayU CredentialsPAYU_MERCHANT_KEY=your_merchant_keyPAYU_MERCHANT_SALT=your_merchant_saltPAYU_ENVIRONMENT=test # or "production"# Redirect URLsSTOREFRONT_URL=http://localhost:8000PAYU_REDIRECT_URL=/order/confirmedPAYU_REDIRECT_FAILURE_URL=/checkout?payment_status=failed
2. MedusaJS Config
Add to your Copy to clipboardmedusa-config.ts:
1234567891011121314151617181920import { defineConfig } from "@medusajs/framework/utils"export default defineConfig({// ... other configmodules: [{resolve: "@medusajs/medusa/payment",options: {providers: [{resolve: "medusa-payu-payment-plugin/providers/payu",id: "payu",options: {merchantKey: process.env.PAYU_MERCHANT_KEY,merchantSalt: process.env.PAYU_MERCHANT_SALT,environment: process.env.PAYU_ENVIRONMENT || "test",},},],},
3. Enable for Region
In Medusa Admin:
- Go to Settings → Regions
- Select your region
- Add Copy to clipboard
payuas a payment provider
Frontend Integration
Payment Flow Overview
- Customer selects PayU at checkout
- Frontend retrieves payment session from cart
- Frontend creates a form and redirects to PayU
- Customer completes payment on PayU's hosted page
- PayU redirects back to your storefront
- Webhook updates order status automatically
Required Customer Data
When creating a payment session, the following customer data is required:
- Email - Customer email address
- Name - Customer first name
- Phone - Uses fallback chain: customer phone → billing address phone (from context) → shipping address phone
- Cart ID & Customer ID - Passed as UDF fields (udf1/udf2) for traceability (optional but recommended)
The phone number fallback uses MedusaJS's Copy to clipboardPaymentProviderContext which provides the customer and billing address data. If the billing address phone is not available, pass the shipping address phone when initiating payment.
It is also highly recommended to pass Copy to clipboardcart_id and Copy to clipboardcustomer_id so they persist through to the webhook even if the session is lost.
1234567// When creating payment session, include in data:{shipping_address_phone: cart.shipping_address?.phone,cart_id: cart.id, // Mapped to udf1customer_id: customer.id, // Mapped to udf2country_code: "in" // For URL construction}
React/Next.js Example
1234567891011121314151617181920"use client"function PayUPaymentButton({ cart }) {const handlePayment = async () => {// Get PayU payment sessionconst paymentSession = cart.payment_collection?.payment_sessions?.find((session) => session.provider_id === "pp_payu_payu")if (!paymentSession?.data?.form_data) {console.error("PayU session not found")return}const { form_data, paymentUrl } = paymentSession.data// Create and submit hidden formconst form = document.createElement("form")form.method = "POST"form.action = paymentUrl
Payment Session Structure
The payment session data contains:
1234567891011121314151617181920{txnid: string // Unique transaction IDamount: string // Amount with 2 decimals (e.g., "999.00")productinfo: string // Product/order descriptionfirstname: string // Customer first nameemail: string // Customer emailphone: string // Customer phonehash: string // Security hash (SHA-512)paymentUrl: string // PayU checkout URLstatus: string // Payment statusform_data: { // Ready-to-submit form datakey: string // Merchant keytxnid: stringamount: stringproductinfo: stringfirstname: stringemail: stringphone: stringsurl: string // Success redirect URLfurl: string // Failure redirect URL
Webhook Setup
PayU webhooks (S2S callbacks) ensure reliable payment status updates even when browser redirects fail.
1. Configure Webhook URL in PayU Dashboard
- Log in to PayU Dashboard
- Go to Settings → Webhooks (or Developer Settings → Webhooks)
- Click Create Webhook or Add Webhook URL
- Enter your webhook URL:
1https://your-backend.com/hooks/payment/payu_payu
- Select events to subscribe:
- Copy to clipboard
payment.success- Payment completed successfully - Copy to clipboard
payment.failed- Payment failed - Copy to clipboard
payment.pending- Payment is pending (optional)
- Copy to clipboard
- Save the configuration
2. Webhook Security
The plugin automatically handles security:
- Hash Verification: Every webhook is verified using SHA-512 reverse hash
- Formula: Copy to clipboard
sha512(SALT|status||||||udf5|udf4|udf3|udf2|udf1|email|firstname|productinfo|amount|txnid|key) - Tampered webhooks are rejected and logged for investigation
The webhook also logs Copy to clipboardcart_id (from udf1) and Copy to clipboardcustomer_id (from udf2) for easier debugging and reconciliation.
3. Content Type Support
PayU sends webhooks as URL-encoded form data:
- Copy to clipboard
application/x-www-form-urlencoded - Copy to clipboard
multipart/form-data
MedusaJS handles both content types automatically.
4. What Happens on Webhook
Status Action Result Copy to clipboardsuccess Copy to clipboardauthorized Payment session authorized, cart completed, order created Copy to clipboardfailure/Copy to clipboardfailed Copy to clipboardfailed Payment session marked as failed Other Copy to clipboardnot_supported Logged for debugging, no action taken
API Reference
Provider ID
1pp_payu_payu
Supported Methods
Method Description Copy to clipboardinitiatePayment Creates payment session with hash and form data Copy to clipboardauthorizePayment Verifies payment status with PayU API Copy to clipboardcapturePayment Marks payment as captured (auto-capture enabled) Copy to clipboardrefundPayment Initiates full or partial refund Copy to clipboardcancelPayment Cancels pending payment Copy to clipboardgetWebhookActionAndData Handles PayU webhook callbacks
Exported Workflow
You can use the verify payment workflow in your custom code:
12345678910111213import { verifyPayuPaymentWorkflow } from "medusa-payu-payment-plugin/workflows"// In your API route or subscriberconst { result } = await verifyPayuPaymentWorkflow(container).run({input: {txnid: "TXN_1234567890_abcd",},})if (result.success) {console.log("Payment status:", result.status)console.log("Transaction details:", result.transaction)}
Environment Variables
Variable Description Required Copy to clipboardPAYU_MERCHANT_KEY PayU Merchant Key Yes Copy to clipboardPAYU_MERCHANT_SALT PayU Merchant Salt (Salt V1) Yes Copy to clipboardPAYU_ENVIRONMENT Copy to clipboardtest or Copy to clipboardproduction No (default: Copy to clipboardtest) Copy to clipboardSTOREFRONT_URL Your storefront base URL (e.g., Copy to clipboardhttp://localhost:8000) Yes Copy to clipboardPAYU_REDIRECT_URL Success redirect path (e.g., Copy to clipboard/order/confirmed) Yes Copy to clipboardPAYU_REDIRECT_FAILURE_URL Failure redirect path (e.g., Copy to clipboard/checkout?payment_status=failed) Yes
Testing
Use PayU test credentials in your test environment:
- Test URL: https://test.payu.in
- Test Cards: PayU Test Cards Documentation
Common Test Card Numbers
Card Type Number CVV Expiry Visa 4012001038443335 123 Any future date Mastercard 5123456789012346 123 Any future date
Troubleshooting
Hash Mismatch Error
Ensure:
- You're using the correct Salt version (this plugin uses Salt V1)
- Amount has exactly 2 decimal places (e.g., Copy to clipboard
"999.00") - All mandatory fields match exactly between hash generation and form submission
Webhook Not Received
- Verify webhook URL is correct in PayU dashboard
- Ensure your server is publicly accessible
- Check server logs for incoming webhook requests
- Verify SSL certificate is valid (required for production)
Payment Session Not Found
Ensure:
- PayU is enabled as a payment provider for the region
- Payment collection is initialized before accessing session
- Provider ID is Copy to clipboard
pp_payu_payu(includes the prefix)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (Copy to clipboard
git checkout -b feature/amazing-feature) - Commit your changes (Copy to clipboard
git commit -m 'Add amazing feature') - Push to the branch (Copy to clipboard
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT © SAM-AEL
See LICENSE for more information.


