This guide follows the current Marseille UPG implementation: configure a PayApp in the admin UI, let the gateway talk to providers, and receive merchant-facing notifications from the gateway.
The shortest path is: create a PayApp in the admin dashboard, configure one adapter, call /v1/gateway/pay, redirect the user to payUrl, then handle your own notify URL when the gateway pushes the final order status.
{ "appId": "app_demo_001", "appSecret": "your-app-secret", "outOrderNo": "ORDER-20260426-001", "amount": 88, "currency": "USD", "provider": "stripe"}In the current codebase, merchant access is represented by a PayApp. It stores appId, appSecret, notifyUrl,adapterType, and encrypted adapterConfig.
You create and manage this through the admin side, not through a public management API on the marketing site.
Choose the adapter you actually want the gateway to use for this app, for examplestripe, paypal, worldpay,dodopayments, creem, or nowpayments.
Save the adapter credentials into adapterConfig through the admin UI. The gateway will decrypt and use that config when creating orders and when running provider connectivity checks.
Your backend should call POST /v1/gateway/pay. If you use simplified mode, send appId and appSecret. If you are maintaining an older integration, you can keep using the signed mode with timestamp,nonce, and sign.
const res = await fetch("https://your-gateway.example.com/v1/gateway/pay", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ appId: "app_demo_001", appSecret: process.env.UPG_APP_SECRET, outOrderNo: "ORDER-20260426-001", amount: 88, currency: "USD", provider: "stripe", returnUrl: "https://merchant.example.com/billing/return" })})const payload = await res.json()const payUrl = payload.data?.payUrlAfter the gateway verifies the upstream provider callback, it sends a downstream notify payload to your configured notifyUrl. That payload includesgatewayOrderNo, outOrderNo, status,provider, paymentId, paidAmount,timestamp, and sign.
{ "gatewayOrderNo": "ORD-1777027063851", "outOrderNo": "ORDER-20260426-001", "amount": 88, "currency": "USD", "status": "success", "provider": "stripe", "paymentId": "cs_test_xxx", "paidAmount": 88, "timestamp": 1777027063, "sign": "generated-signature"}/v1/gateway/callback/:provider is not your merchant callback URL. That route is for Stripe, PayPal, and other upstream providers to call into the gateway.If you need to check the latest order status from your backend, callPOST /v1/gateway/query. This route currently uses the signed request mode.
Use either gatewayOrderNo or outOrderNo together with the signed authentication fields.
The marketing-site status page and /v1/public/status now reflect the real gateway runtime: API process, PostgreSQL, Redis, and configured provider connectivity.
Before go-live, make sure your configured provider shows healthy connectivity there and that your admin-side testConnection check also passes.