PipraPay
This guide details the backend configuration and storefront integration for the PipraPay provider.
Backend Configuration
Section titled “Backend Configuration”Add the module to your medusa-config.ts file:
modules: [ { resolve: "@medusajs/medusa/payment", options: { providers: [ { resolve: "@crza69/medusa-plugins/providers/piprapay", id: "piprapay", options: { apiKey: process.env.PIPRAPAY_API_KEY, baseUrl: process.env.PIPRAPAY_URL, backendUrl: process.env.ADMIN_URL, }, }, ], }, },],Next.js Storefront Integration
Section titled “Next.js Storefront Integration”The following steps are based on the Next.js Starter Medusa.
You will be modifying the following files:
- medusa-config.ts - src - app - [countrycode] - cart - complete - page.tsx - lib - constants.tsx - data.ts - modules - checkout - components - payment-button - index.tsx
-
Add Payment Icon and Title
Update
src/lib/constants.tsxto include the PipraPay mapping inpaymentInfoMap:src/lib/constants.tsx export const paymentInfoMap: Record<string,{ title: string; icon: React.JSX.Element }> = {pp_stripe_stripe: {title: "Credit card",icon: <CreditCard />,},"pp_medusa-payments_default": {title: "Credit card",icon: <CreditCard />,},"pp_stripe-ideal_stripe": {title: "iDeal",icon: <Ideal />,},"pp_stripe-bancontact_stripe": {title: "Bancontact",icon: <Bancontact />,},pp_paypal_paypal: {title: "PayPal",icon: <PayPal />,},pp_system_default: {title: "Manual Payment",icon: <CreditCard />,},pp_piprapay_piprapay: {title: "PipraPay",icon: <CreditCard />,},}; -
Create Payment Component
Modify
src/modules/checkout/components/payment-button/index.tsxto include thePipraPayPaymentButtonand handle the selection logic.src/modules/checkout/components/payment-button/index.tsx const PaymentButton: React.FC<PaymentButtonProps> = ({cart,"data-testid": dataTestId,}) => {const notReady =!cart ||!cart.shipping_address ||!cart.billing_address ||!cart.email ||(cart.shipping_methods?.length ?? 0) < 1;const paymentSession = cart.payment_collection?.payment_sessions?.[0];switch (true) {case isStripeLike(paymentSession?.provider_id):return (<StripePaymentButtonnotReady={notReady}cart={cart}data-testid={dataTestId}/>);case isManual(paymentSession?.provider_id):return (<ManualTestPaymentButtonnotReady={notReady}data-testid={dataTestId}/>);case paymentSession?.provider_id === "pp_piprapay_piprapay":return (<PipraPayPaymentButtonnotReady={notReady}cart={cart}data-testid={dataTestId}/>);default:return <Button disabled>Select a payment method</Button>;}};const PipraPayPaymentButton = ({cart,notReady,"data-testid": dataTestId,}: {cart: HttpTypes.StoreCart;notReady: boolean;"data-testid"?: string;}) => {const [submitting, setSubmitting] = useState(false);const [errorMessage, setErrorMessage] = useState<string | null>(null);const session = cart.payment_collection?.payment_sessions?.find((s) => s.status === "pending");const handlePayment = async () => {setSubmitting(true);try {const { redirect_url } = session?.data || {};if (redirect_url) {window.location.href = redirect_url as string;return;}} catch (err: any) {setErrorMessage(err.message);setSubmitting(false);}};return (<><Buttondisabled={notReady}isLoading={submitting}onClick={handlePayment}size="large"data-testid={dataTestId}>Place order</Button><ErrorMessageerror={errorMessage}data-testid="piprapay-payment-error-message"/></>);}; -
Initiate Payment Session
Update
src/lib/data.ts(or your relevant data fetching file) to includeredirect_urlandcancel_urlwhen initiating the payment session.src/lib/data.ts export async function initiatePaymentSession(cart: HttpTypes.StoreCart,data: HttpTypes.StoreInitializePaymentSession) {const headers = {...(await getAuthHeaders()),};const baseUrl = getBaseURL();const countryCode = cart.region?.countries?.[0]?.iso_2?.toLowerCase();data.data = {...data.data,email: cart.email,redirect_url: `${baseUrl}/${countryCode}/cart/complete`,cancel_url: `${baseUrl}/${countryCode}/checkout?step=payment`,};return sdk.store.payment.initiatePaymentSession(cart, data, {}, headers).then(async (resp) => {const cartCacheTag = await getCacheTag("carts");revalidateTag(cartCacheTag);return resp;}).catch(medusaError);} -
Create Payment Completion Page
Create or update
src/app/[countrycode]/cart/complete/page.tsxto handle the payment completion redirected from PipraPay.src/app/[countrycode]/cart/complete/page.tsx "use client";import { placeOrder } from "@lib/data/cart";import { Heading, Text } from "@medusajs/ui";import { useEffect, useState } from "react";const CartCompletePage = () => {const [error, setError] = useState<string | null>(null);useEffect(() => {const complete = async () => {try {await placeOrder();} catch (err: any) {setError(err.message);}};complete();}, []);if (error) {return (<div className="flex flex-col items-center justify-center min-h-[50vh] gap-4"><Heading level="h1" className="text-2xl text-ui-fg-base">Payment Error</Heading><Text className="text-ui-fg-subtle">{error}</Text></div>);}return (<div className="flex flex-col items-center justify-center min-h-[50vh] gap-4"><Heading level="h1" className="text-2xl text-ui-fg-base animate-pulse">Processing Payment...</Heading><Text className="text-ui-fg-subtle">Please wait while we confirm your order.</Text></div>);};export default CartCompletePage;