npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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.

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-plugin

Configuration

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, email, and phone. The plugin automatically attempts to extract this from the Medusa cart's shipping/billing address. To avoid errors, you can explicitly pass shipping_address_phone (and ideally cart_id and customer_id for 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.

  1. Go to your PayU Dashboard → Webhooks.
  2. 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 push

Then in your main Medusa project:

npx yalc add medusa-payu-payment-plugin
yarn install

When 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.