Integration Guide

Integrate the gateway that is in this repository.

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.

Audience
Merchant backend
Public route
/v1/gateway/pay
Callback model
Provider -> Gateway -> Merchant
Status
/v1/public/status
00

Quickstart

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.

quickstart.json
{
"appId": "app_demo_001",
"appSecret": "your-app-secret",
"outOrderNo": "ORDER-20260426-001",
"amount": 88,
"currency": "USD",
"provider": "stripe"
}
Current architecture
Merchant systems do not integrate directly with Stripe, PayPal, or other adapters in this flow. The gateway owns the provider credentials and receives provider callbacks first.
STEP 01

Create a PayApp in the admin dashboard

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.

STEP 02

Configure one provider adapter

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.

STEP 03

Call the pay route from your backend

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.

merchant-pay.ts
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?.payUrl
STEP 04

Handle gateway notify callbacks

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

notify-payload.json
{
"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"
}
Important distinction
The provider callback route /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.
STEP 05

Query order state when needed

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.

STEP 06

Verify runtime status before go-live

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.