PayU
Enable payments with PayU
Medusa PayU Payment Plugin
A seamless PayU India integration for Medusa v2.
This plugin enables a redirect-based checkout flow with PayU, complete with robust webhook handling to automatically verify and capture transactions even if the user drops off during the redirect.
Installation
1yarn add medusa-payu-payment-plugin
Configuration
First, add your PayU credentials to your Copy to clipboard.env file:
123456789# RequiredPAYU_MERCHANT_KEY="your_merchant_key"PAYU_MERCHANT_SALT="your_merchant_salt" # Note: This plugin uses Salt V1 for hashing!PAYU_ENVIRONMENT="test" # or "production"# Optional Base URLs (used to build the redirect URLs dynamically based on context)STOREFRONT_URL="http://localhost:8000"# PAYU_REDIRECT_URL="/order/confirmed"# PAYU_REDIRECT_FAILURE_URL="/checkout"
Next, configure the plugin in your Copy to clipboardmedusa-config.ts:
1234567891011121314151617181920import { defineConfig } from "@medusajs/framework/utils"export default defineConfig({modules: [{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",},},],},},
Finally, make sure you go into your Medusa Admin → Settings → Regions and add Copy to clipboardpayu as a payment provider to your Indian region.
Frontend Integration
PayU requires a redirect-based flow. Because MedusaJS's Copy to clipboardinitiatePayment doesn't directly redirect the user natively, the plugin generates the required raw signature hashes and returns them in the Copy to clipboardform_data attribute of the payment session.
You are expected to construct a hidden HTML form using this data and automatically submit it on the frontend to execute the redirect.
PayU requires Copy to clipboardfirstname, Copy to clipboardphone. The plugin automatically attempts to extract this from the Medusa cart's shipping/billing address. To avoid errors, you can explicitly pass Copy to clipboardshipping_address_phone(and ideally Copy to clipboardcart_idand Copy to clipboardcustomer_idfor tracking) in the data payload when initializing standard payment sessions on the frontend.
Example in React/Next.js
1234567891011121314151617181920"use client"function PayUPaymentButton({ cart }) {const handlePayment = async () => {const paymentSession = cart.payment_collection?.payment_sessions?.find((session) => session.provider_id === "pp_payu_payu")if (!paymentSession?.data?.form_data) return;const { form_data, paymentUrl } = paymentSession.data// Create a hidden form to POST to PayUconst form = document.createElement("form")form.method = "POST"form.action = paymentUrlObject.entries(form_data).forEach(([key, value]) => {const input = document.createElement("input")input.type = "hidden"
Webhooks (Critical for Production)
Users often close the browser before completing the redirect back to the store. Setting up Server-to-Server callbacks ensures Medusa captures the payment regardless.
- Go to your PayU Dashboard → Webhooks.
- Add your webhook URL: Copy to clipboard
https://your-medusa-backend.com/hooks/payment/payu_payu
The plugin handles reverse SHA-512 verification automatically to ensure incoming webhooks are strictly from PayU and have not been tampered with.
Development & Local Testing
If you want to modify this plugin locally:
12345# Build the pluginyarn build# Push to local yalc storenpx yalc push
Then in your main Medusa project:
12npx yalc add medusa-payu-payment-pluginyarn install
When using Copy to clipboardtest environment, use PayU's standard test cards (e.g., Copy to clipboard4012 0010 3844 3335, CVV: Copy to clipboard123, any future expiry). Note that for webhook testing locally, you will need a tunneling service like ngrok to expose your local Medusa instance to PayU's webhook dispatcher.


