medusa-unified-payment-react-test
v0.0.3
Published
Generic payment UI components for Medusa v2 storefronts
Downloads
298
Maintainers
Readme
@juspay/medusa-unified-payment-react
React UI components for Hyperswitch Prism payment connectors. Ships two layers of API:
- High-level Medusa components — drop-in components for a Medusa v2 Next.js storefront checkout
- Low-level connector wrappers — standalone React components for any custom checkout
Connector Support Matrix
| Connector | Connector Panel | Payment Button | Authorize Result |
|-----------|:---------------:|:--------------:|:----------------:|
| adyen | ✅ | ✅ | AUTHORIZED |
| paypal | ✅ | ✅ | CAPTURED |
| stripe | — | ✅ | AUTHORIZED |
| globalpay | ✅ | ✅ | CAPTURED |
| braintree | ○ | ○ | — |
| cybersource | ○ | ○ | — |
| mollie | ○ | ○ | — |
Legend
| Symbol | Meaning |
|--------|---------|
| ✅ | Supported — React component available |
| ○ | No React component — connector has no client-side UI (wallet / redirect / server-side only) |
| — | Not in HyperswitchPrismConnectorPanel; use StripeWrapper directly (Stripe handles its own Elements context) |
Authorize result
- AUTHORIZED (
adyen,stripe) — funds reserved; Capture or Void available as a next step- CAPTURED (
paypal,globalpay) — funds collected immediately at authorize time; only Refund available afterward
Note: All UI flows and connector integrations in this matrix are tested and verified under the sandbox / test environment of each connector. Production behavior should be validated separately before go-live.
Installation
npm install @juspay/medusa-unified-payment-react
# peer dependencies
npm install @adyen/adyen-web @stripe/react-stripe-js @stripe/stripe-jsLocal development (linked to this repo):
"@juspay/medusa-unified-payment-react": "file:/path/to/medusa-hyperswitch-prism/medusa-unified-payment-react"After source changes, rebuild with:
cd medusa-unified-payment-react && npm run build
# then yarn install in your storefrontEnvironment Variables
Add the following to your storefront .env.local:
# Adyen — client key from your Adyen Customer Area (required for Adyen drop-in)
NEXT_PUBLIC_ADYEN_CLIENT_KEY=test_...Exports
| Export | Type | Description |
|--------|------|-------------|
| HyperswitchPrismConnectorPanel | Component | Renders the correct connector UI (Adyen/PayPal/GlobalPay) for a selected payment method |
| HyperswitchPrismPaymentButton | Component | Auto-dispatches to the correct place-order button based on providerId |
| AdyenWrapper | Component | Low-level Adyen Web v6 drop-in wrapper |
| PayPalWrapper | Component | Low-level PayPal Buttons SDK wrapper |
| GlobalPayWrapper | Component | Low-level GlobalPay hosted card fields wrapper |
| StripeWrapper | Component | Low-level Stripe Payment Element wrapper |
| StripePaymentButton | Component | Place-order button for Stripe |
| AdyenPaymentButton | Component | Place-order button for Adyen |
| PayPalPaymentButton | Component | Place-order button for PayPal |
| GlobalPayPaymentButton | Component | Place-order button for GlobalPay |
| ManualTestPaymentButton | Component | Dev-only button for manual/test payment providers |
| isHyperswitchPrism | Utility | Returns true for any Hyperswitch Prism provider ID |
| isHyperswitchPrismStripe | Utility | Matches Stripe provider IDs (short and legacy forms) |
| isHyperswitchPrismAdyen | Utility | Matches Adyen provider IDs |
| isHyperswitchPrismPaypal | Utility | Matches PayPal provider IDs |
| isHyperswitchPrismGlobalpay | Utility | Matches GlobalPay provider IDs |
| HYPERSWITCH_PRISM_PROVIDER_IDS | Constant | Map of connector name → canonical provider ID |
High-level Medusa Components
HyperswitchPrismConnectorPanel
Renders the correct payment instrument UI for the selected Hyperswitch Prism provider. Returns null for Stripe (handled separately via @stripe/react-stripe-js) and unknown providers.
import { HyperswitchPrismConnectorPanel } from "@juspay/medusa-unified-payment-react"
<HyperswitchPrismConnectorPanel
providerId={paymentMethod.id}
sessionData={sessionForProvider(paymentMethod.id)?.data}
adyenClientKey={process.env.NEXT_PUBLIC_ADYEN_CLIENT_KEY}
onInitiateSession={async ({ paymentReference, id }) => {
// GlobalPay: re-initiate to persist the tokenized card reference
await initiatePaymentSession(cart, {
provider_id: paymentMethod.id,
data: { paymentReference, id },
})
}}
onPaymentCompleted={() => router.push("...?step=review")}
onError={(e) => setError(e.message)}
/>| Prop | Type | Required | Description |
|------|------|----------|-------------|
| providerId | string | Yes | Provider ID of the selected method (e.g. pp_hyperswitch-prism_adyen) |
| sessionData | Record<string, any> \| null | Yes | The .data field from the active Medusa payment session |
| adyenClientKey | string | Yes (Adyen) | Adyen client key from process.env.NEXT_PUBLIC_ADYEN_CLIENT_KEY — required when Adyen is enabled |
| onInitiateSession | (data: { paymentReference, id }) => Promise<void> | Yes | Called by GlobalPay after tokenization to persist the payment reference |
| onPaymentCompleted | (result?: any) => void | No | Called after Adyen authorises or PayPal approves |
| onError | (error: Error) => void | Yes | Called on any connector-level error |
| environment | "sandbox" \| "production" | No | Defaults to "sandbox" |
HyperswitchPrismPaymentButton
Auto-dispatches to the correct connector place-order button. Returns null for non-Hyperswitch Prism providers.
import { HyperswitchPrismPaymentButton } from "@juspay/medusa-unified-payment-react"
<HyperswitchPrismPaymentButton
providerId={paymentSession?.provider_id}
cart={cart}
notReady={notReady}
onPlaceOrder={placeOrder}
buttonComponent={Button}
data-testid="submit-order-button"
/>| Prop | Type | Required | Description |
|------|------|----------|-------------|
| providerId | string \| undefined | Yes | Active payment session provider ID |
| cart | StoreCart | Yes | Medusa cart |
| notReady | boolean | Yes | Disables the button when checkout prerequisites are incomplete |
| onPlaceOrder | () => Promise<void> | Yes | Called to place the order |
| buttonComponent | React.ComponentType | No | Custom button component |
| data-testid | string | No | Test identifier forwarded to the rendered button |
Storefront predicates
Define these inline in your src/lib/constants.tsx — do not re-export from this package, as it causes a createContext error in Next.js server components.
const matchesPrismConnector = (id: string | undefined, connector: string) =>
id === `pp_hyperswitch-prism_${connector}` ||
id === `pp_hyperswitch-prism_hyperswitch-prism-${connector}`
export const isHyperswitchPrismStripe = (id?: string) => matchesPrismConnector(id, "stripe")
export const isHyperswitchPrismAdyen = (id?: string) => matchesPrismConnector(id, "adyen")
export const isHyperswitchPrismPaypal = (id?: string) => matchesPrismConnector(id, "paypal")
export const isHyperswitchPrismGlobalpay = (id?: string) => matchesPrismConnector(id, "globalpay")
export const isHyperswitchPrism = (id?: string) =>
isHyperswitchPrismStripe(id) || isHyperswitchPrismAdyen(id) ||
isHyperswitchPrismPaypal(id) || isHyperswitchPrismGlobalpay(id)The dual-match pattern handles both canonical short IDs (pp_hyperswitch-prism_stripe) and legacy long IDs (pp_hyperswitch-prism_hyperswitch-prism-stripe) for backward compatibility with older backend configurations.
Low-level Connector Wrappers
Use these when building a custom (non-Medusa) checkout or embedding an individual payment form.
AdyenWrapper
Renders the Adyen Web v6 drop-in.
import { AdyenWrapper } from "@juspay/medusa-unified-payment-react"
<AdyenWrapper
sessionData={{
clientKey: "test_xxx",
session: { id: "CS...", sessionData: "Ab02b4c..." },
currency: "EUR",
minorAmount: 1000,
countryCode: "GB",
}}
onPaymentCompleted={(result) => console.log("Authorised", result)}
onPaymentFailed={(result) => console.error("Failed", result)}
onError={(e) => console.error(e)}
/>| Prop | Type | Required | Description |
|------|------|----------|-------------|
| sessionData | Record<string, any> | Yes | clientKey, session.id, session.sessionData, currency, minorAmount, countryCode |
| onPaymentCompleted | (result: any) => void | No | Called on resultCode: "Authorised" / "Pending" / "Received" |
| onPaymentFailed | (result: any) => void | No | Called on non-authorised result codes |
| onSubmit | (paymentData: unknown) => void | No | Advanced flow only |
| onError | (error: Error) => void | Yes | Called on SDK or network errors |
PayPalWrapper
Renders the PayPal Buttons SDK.
import { PayPalWrapper } from "@juspay/medusa-unified-payment-react"
<PayPalWrapper
clientId="AYour_PayPal_Client_Id"
currency="USD"
amount={49.99}
environment="sandbox"
onCreateOrder={() => Promise.resolve(sessionData.paypalOrderId)}
onSubmit={({ orderId, payerId }) => advanceToReview()}
onError={(e) => setError(e.message)}
/>| Prop | Type | Required | Description |
|------|------|----------|-------------|
| clientId | string | Yes | PayPal app client ID |
| currency | string | Yes | ISO 4217 currency code |
| amount | number | Yes | Amount in major units |
| environment | "sandbox" \| "production" | No | Defaults to "production" |
| onCreateOrder | () => Promise<string> | Yes | Must resolve to a PayPal order ID |
| onSubmit | (data: { orderId, payerId }) => void | Yes | Called after PayPal approval |
| onError | (error: Error) => void | Yes | Called on SDK or approval errors |
GlobalPayWrapper
Renders the GlobalPay hosted card fields. After tokenization, onSubmit is called with the paymentReference that must be persisted before place order.
import { GlobalPayWrapper } from "@juspay/medusa-unified-payment-react"
<GlobalPayWrapper
accessToken={sessionData.accessToken}
environment="sandbox"
onSubmit={async ({ paymentReference }) => {
await initiatePaymentSession(cart, {
provider_id: providerId,
data: { paymentReference },
})
}}
onError={(e) => setError(e.message)}
/>| Prop | Type | Required | Description |
|------|------|----------|-------------|
| accessToken | string | Yes | GlobalPay access token from the payment session |
| environment | "sandbox" \| "production" | No | Defaults to "sandbox" |
| onSubmit | (data: { paymentReference }) => Promise<void> | Yes | Called after successful card tokenization |
| onError | (error: Error) => void | Yes | Called on tokenization or SDK errors |
StripeWrapper
Renders the Stripe Payment Element (standalone — does not require an <Elements> context).
import { StripeWrapper } from "@juspay/medusa-unified-payment-react"
<StripeWrapper
publishableKey="pk_test_..."
clientSecret={sessionData.client_secret}
onSubmit={(paymentIntent) => advanceToReview()}
onError={(e) => setError(e.message)}
/>| Prop | Type | Required | Description |
|------|------|----------|-------------|
| publishableKey | string | Yes | Stripe publishable key |
| clientSecret | string | Yes | PaymentIntent client secret from the session |
| onSubmit | (paymentData: unknown) => void | Yes | Called after confirmPayment succeeds |
| onError | (error: Error) => void | Yes | Called on Stripe errors |
Backend Plugin
For the Medusa backend plugin, see @juspay/medusa-unified-payment.
License
MIT
