Skip to main content

Overview

This guide shows how to integrate Secure Payment Pages in a production-ready way. The recommended pattern is:
  1. Create secure payment links with POST /v2/secure-payments
  2. Store returned requestIds in your system
  3. Redirect the payer to the secure page
  4. Use webhooks as the source of truth for payment status updates

Prerequisites

Before you integrate, make sure you have:
  • An API key or a Client ID linked to your integration domain
  • A webhook endpoint configured in Request Portal
  • Your webhook signing secret stored securely on your backend
For setup details, see:

Quick start

1

Create secure payment links

Call POST /v2/secure-payments and store the returned requestIds.
curl -X POST "https://api.request.network/v2/secure-payments" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requests": [
      {
        "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7",
        "amount": "10",
        "invoiceCurrency": "USDC-base",
        "paymentCurrency": "USDC-base"
      }
    ]
  }'
{
  "requestIds": [
    "01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb"
  ],
  "securePaymentUrl": "https://pay.request.network/?token=01ABC123DEF456GHI789JKL",
  "token": "01ABC123DEF456GHI789JKL"
}
2

Persist payment mapping in your database

Store at least:
  • your internal metadata
  • returned requestIds
  • token
  • securePaymentUrl
This mapping lets you reconcile webhook events back to your internal records.
3

Redirect payer to secure page

Redirect in the same tab or open the secure URL in a new tab.
4

Process webhook events and update order status

Handle payment events from webhooks and update your order/payment state from those events.

Integration pattern: generated URL + redirect

Backend example (Node.js/Express)

server.js
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/checkout/secure-payment", async (req, res) => {
  const { orderId, payee, amount, currencyId } = req.body;

  const apiResponse = await fetch("https://api.request.network/v2/secure-payments", {
    method: "POST",
    headers: {
      "x-api-key": process.env.REQUEST_API_KEY,
      "content-type": "application/json",
    },
    body: JSON.stringify({
      requests: [
        {
          payee,
          amount,
          invoiceCurrency: currencyId,
          paymentCurrency: currencyId,
        },
      ],
    }),
  });

  if (!apiResponse.ok) {
    const errorBody = await apiResponse.text();
    return res.status(apiResponse.status).json({ error: errorBody });
  }

  const securePayment = await apiResponse.json();

  // Persist in your DB
  // Example payload:
  // {
  //   orderId,
  //   requestIds: securePayment.requestIds,
  //   token: securePayment.token,
  //   securePaymentUrl: securePayment.securePaymentUrl,
  //   status: "pending"
  // }

  return res.status(200).json({
    orderId,
    securePaymentUrl: securePayment.securePaymentUrl,
  });
});

Frontend redirect examples

window.location.href = securePaymentUrl;

Payment status updates with webhooks

Use webhook events as your payment status source of truth. Typical mapping:
  • payment.confirmed -> mark order as paid
  • payment.partial -> mark order as partially paid
  • payment.failed -> mark order as failed

Webhook handler example (signature verification + reconciliation)

webhook.js
import crypto from "node:crypto";
import express from "express";

const app = express();

app.use(
  express.raw({
    type: "application/json",
    verify: (req, _res, buf) => {
      req.rawBody = buf;
    },
  }),
);

app.post("/webhooks/request", async (req, res) => {
  const signature = req.headers["x-request-network-signature"];
  const secret = process.env.REQUEST_WEBHOOK_SECRET;

  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(req.rawBody)
    .digest("hex");

  if (!signature) {
    return res.status(401).json({ error: "Missing signature" });
  }

  try {
    const isValid = crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expectedSignature),
    );
    if (!isValid) {
      return res.status(401).json({ error: "Invalid signature" });
    }
  } catch {
    return res.status(401).json({ error: "Invalid signature format" });
  }

  const event = JSON.parse(req.rawBody.toString("utf8"));
  const requestId = event.requestId || event.requestID;

  // Find internal record by requestId in your DB, then update order status.
  // Example:
  // const checkout = await db.findCheckoutByRequestId(requestId)
  // if (event.event === "payment.confirmed") await db.markPaid(checkout.orderId)

  return res.status(200).json({ received: true });
});

Using destination IDs

Instead of specifying payee, invoiceCurrency, and paymentCurrency separately, you can use a destinationId that encodes all three in a single ERC-7828 composite ID:
{
  "requests": [
    {
      "destinationId": "0x6923...C7D7@eip155:8453#ABCD1234:0x8335...2913",
      "amount": "10"
    }
  ]
}
When using a Client ID bound to a payee destination, the destinationId can be omitted entirely — the API resolves it from the Client ID configuration.

Crosschain payments on secure pages

Secure payment pages support crosschain payments automatically. When the payer connects a wallet, the page checks balances across supported chains and shows payment options. The payer selects a route, and the page handles the rest. The two-endpoint flow is:
  1. GET /v2/secure-payments/:token — returns metadata and paymentOptions with balance info per chain
  2. GET /v2/secure-payments/:token/pay?wallet=&chain=&token= — returns executable calldata for the selected route
For crosschain tracking, after the payer broadcasts the source-chain transaction, the page calls POST /v2/secure-payments/:token/intent with the transaction hash. See the Secure Payments API Reference for full endpoint details.

Expiry handling

Secure payment links expire after one week by default. If a payer opens an expired link, create a new secure payment link and redirect again.

Troubleshooting

  • Verify your x-api-key or x-client-id header
  • If using Client ID in browser, verify the request origin is in allowed domains
  • The token may be expired
  • Create a fresh secure payment link and retry
  • Payment is already completed
  • Show a paid/completed state in your app instead of retrying payment
  • Verify HMAC signature validation uses raw request body
  • Ensure your endpoint returns 2xx after successful processing
  • Confirm your DB lookup maps incoming requestId/requestID to stored request IDs

Secure Payments API Reference

Full request and response schema details.

Webhooks

Event types, signing, retries, and payload details.

Authentication

API key and Client ID setup.