medusa-payu-payment-plugin
v1.3.6
Published
PayU India payment gateway plugin for MedusaJS 2.x with redirect-based checkout, webhook support, hash verification, and refunds.
Maintainers
Readme
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
yarn add medusa-payu-payment-pluginConfiguration
First, add your PayU credentials to your .env file:
# Required
PAYU_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 medusa-config.ts:
import { 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 payu as a payment provider to your Indian region.
Frontend Integration
PayU requires a redirect-based flow. Because MedusaJS's initiatePayment doesn't directly redirect the user natively, the plugin generates the required raw signature hashes and returns them in the form_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
firstname,phone. The plugin automatically attempts to extract this from the Medusa cart's shipping/billing address. To avoid errors, you can explicitly passshipping_address_phone(and ideallycart_idandcustomer_idfor tracking) in the data payload when initializing standard payment sessions on the frontend.
Example in React/Next.js
"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 PayU
const form = document.createElement("form")
form.method = "POST"
form.action = paymentUrl
Object.entries(form_data).forEach(([key, value]) => {
const input = document.createElement("input")
input.type = "hidden"
input.name = key
input.value = String(value)
form.appendChild(input)
})
document.body.appendChild(form)
form.submit()
}
return <button onClick={handlePayment}>Pay with PayU</button>
}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:
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:
# Build the plugin
yarn build
# Push to local yalc store
npx yalc pushThen in your main Medusa project:
npx yalc add medusa-payu-payment-plugin
yarn installWhen using test environment, use PayU's standard test cards (e.g., 4012 0010 3844 3335, CVV: 123, 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.
