From c50a70a6f8a85ed244c313a184945fad508d9ace Mon Sep 17 00:00:00 2001 From: rodrigopavezi Date: Fri, 8 May 2026 08:48:47 -0300 Subject: [PATCH] docs(content): add use-case pages, Dashboard + Secure Payment tools pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build out the new Use Cases tab and Tools section around the actual product surface (Dashboard, Secure Payment, v2 API). New use-case pages: - quickstart.mdx — canonical end-to-end walkthrough adapted from the payment-destinations-and-payment-links reference; spine of the tab - no-code-payment-links.mdx — Dashboard-only flow for freelancers/SMBs - programmatic-payment-links.mdx — server-side flow with TS/Python/cURL side-by-side EVM and Tron examples - multi-chain-checkout.mdx — Li.Fi cross-chain payer story - batch-payouts.mdx — EVM-only mass payouts with worked examples (marketplace, refund, payroll) and explicit Tron exclusion - webhook-reconciliation.mdx — production-ready handler with HMAC verification, idempotency, retry-aware error handling, and event routing for all 12 event types - welcome.mdx rewritten as a three-card hero (Dashboard / Secure Payment / API) with refreshed Get Started cards New tools/ pages: - tools/dashboard.mdx — feature reference for dashboard.request.network with EVM and Tron sign-in tabs side by side, destinations, Client IDs, Get Paid, Pay - tools/secure-payments.mdx — feature reference for pay.request.network with payer flow, wallet support (EVM + Tron columns), Li.Fi cross-chain, defense-in-depth, error states docs.json: - Added "Use Cases" group with 5 new pages - Added "Tools" group in Resources tab - Added tools/dashboard to API Setup group --- docs.json | 21 +- tools/dashboard.mdx | 124 ++++++ tools/secure-payments.mdx | 146 +++++++ use-cases/batch-payouts.mdx | 200 ++++++++++ use-cases/multi-chain-checkout.mdx | 128 ++++++ use-cases/no-code-payment-links.mdx | 106 +++++ use-cases/programmatic-payment-links.mdx | 259 ++++++++++++ use-cases/quickstart.mdx | 477 +++++++++++++++++++++++ use-cases/webhook-reconciliation.mdx | 227 +++++++++++ use-cases/welcome.mdx | 90 +++-- 10 files changed, 1734 insertions(+), 44 deletions(-) create mode 100644 tools/dashboard.mdx create mode 100644 tools/secure-payments.mdx create mode 100644 use-cases/batch-payouts.mdx create mode 100644 use-cases/multi-chain-checkout.mdx create mode 100644 use-cases/no-code-payment-links.mdx create mode 100644 use-cases/programmatic-payment-links.mdx create mode 100644 use-cases/quickstart.mdx create mode 100644 use-cases/webhook-reconciliation.mdx diff --git a/docs.json b/docs.json index 8b221f8..35bb9b0 100644 --- a/docs.json +++ b/docs.json @@ -38,7 +38,18 @@ { "group": "🚀 Get Started", "pages": [ - "use-cases/welcome" + "use-cases/welcome", + "use-cases/quickstart" + ] + }, + { + "group": "🎯 Use Cases", + "pages": [ + "use-cases/no-code-payment-links", + "use-cases/programmatic-payment-links", + "use-cases/multi-chain-checkout", + "use-cases/batch-payouts", + "use-cases/webhook-reconciliation" ] } ] @@ -51,6 +62,7 @@ "pages": [ "api-setup/getting-started", "api-setup/integration-tutorial", + "tools/dashboard", "api-features/client-id-management" ] }, @@ -149,6 +161,13 @@ "resources/token-list" ] }, + { + "group": "🛠️ Tools", + "pages": [ + "tools/dashboard", + "tools/secure-payments" + ] + }, { "group": "🏗️ Architecture", "pages": [ diff --git a/tools/dashboard.mdx b/tools/dashboard.mdx new file mode 100644 index 0000000..c8d75de --- /dev/null +++ b/tools/dashboard.mdx @@ -0,0 +1,124 @@ +--- +title: "Dashboard" +description: "dashboard.request.network — sign in with your wallet to manage payment destinations, Client IDs, incoming payment requests, and outgoing payouts." +--- + +The **Request Network Dashboard** ([dashboard.request.network](https://dashboard.request.network)) is the no-code home for everything you do with Request Network as a receiver: setting up where you want to be paid, generating API credentials, sending and receiving payment links. Sign in with an EVM or Tron wallet — no email, no password. + + + Dashboard home with active payment destination + + +## Sign-in + +EVM and Tron are co-equal sign-in methods. The login screen offers parallel tabs: + + + +Connect MetaMask, Coinbase Wallet, WalletConnect, or any injected EIP-1193 provider. The Dashboard issues a SIWE challenge; you sign with `personal_sign`. + + + Dashboard sign-in (EVM) + + + +Connect TronLink, Guarda, Trust, or WalletConnect-Tron. The auth-api detects your `T...` address and issues a Tron-formatted challenge; you sign with `tronWeb.trx.signMessageV2`. + + + Dashboard sign-in (Tron) + + + + +The session is a 15-minute idle-timeout cookie shared across `dashboard.request.network`, `auth.request.network`, and `api.request.network`. + +## Payment destinations + +A **payment destination** registers where you want to receive payments. It's a single chain × token × wallet combo, encoded as an [ERC-7828 humanReadableInteropAddress](/api-features/payee-destinations). + + + Create payment destination dialog + + +Supported chain × token combinations (8 networks): + +| Chain | Tokens | +| --- | --- | +| Ethereum, Arbitrum One, Optimism, Base, Polygon, BSC | USDC, USDT | +| **Tron** | USDC, USDT (TRC-20) | +| Sepolia (testnet) | FAU, USDC, USDT | + +You can switch your active destination at any time via **Manage Destination → Update Receiving Route**. + +## Client ID management + +Client IDs are the credential the Dashboard (and your own apps) use to call the API on your behalf. Each Client ID can be: + +- **Domain-restricted** (frontend use) — set `allowedDomains` to one or more HTTPS origins +- **Backend-only** — leave `allowedDomains` empty for server-side use +- **Fee-aware** — set a default `feePercentage` and `feeAddress` +- **Operator-aware** — bind `operatorWalletAddress` for commerce payment flows +- **Destination-bound** — bind `payeeDestinationId` for orchestrator patterns + + + Manage destination and Client IDs + + + + Generate Client ID dialog + + +For full field reference, see [Client ID Management](/api-features/client-id-management). + +## Get Paid + +The **Get Paid** tab is your incoming-payments view. Create a request — amount, currency, optional reference and payer identifier — and the Dashboard generates a `pay.request.network` link to share. + + + Get Paid list + + +Each request shows status (Pending / Paid), the on-chain transaction hash once paid, and a copyable payment URL. Works identically for EVM and Tron destinations (single recipient on Tron). + + + Request detail with payment link + + +## Pay (outgoing) + +The **Pay** tab is your outgoing-payments view. Create a payment to one or more recipients on the same chain, sign the resulting transaction, and the Dashboard tracks settlement. + + + Pay list (outgoing payouts) + + +| Outgoing flow | EVM | Tron | +| --- | --- | --- | +| Single payout | ✓ | ✓ | +| Batch payout (one signature, many recipients) | ✓ | ✗ | + +For Tron payouts, send single payments per recipient. EVM batch payouts are documented at [Batch payouts](/use-cases/batch-payouts). + +## What the Dashboard does *not* do + +- It is not an admin / merchant control panel for downstream platforms — it's a user dashboard for your own wallet. +- It does not manage webhooks today — webhooks are managed via the [auth-api](https://auth.request.network/open-api). +- It does not host the payer-side checkout — that's [Secure Payment](/tools/secure-payments) at `pay.request.network`. + +## Open the Dashboard + + +Sign in with your EVM or Tron wallet to start. + + +## Related + + + + End-to-end walkthrough using the Dashboard. + + + + The payer-facing companion at pay.request.network. + + diff --git a/tools/secure-payments.mdx b/tools/secure-payments.mdx new file mode 100644 index 0000000..85191e1 --- /dev/null +++ b/tools/secure-payments.mdx @@ -0,0 +1,146 @@ +--- +title: "Secure Payment" +description: "pay.request.network — the hosted payment link app payers open to settle a Request Network payment. Defense-in-depth, multi-chain, EVM + Tron." +--- + +**Secure Payment** ([pay.request.network](https://pay.request.network)) is the hosted page your payers land on when they click a payment link. It's a defense-in-depth checkout: trusted contract addresses are hardcoded, every transaction is decoded and verified client-side, and the payer can pay from any of 8 supported chains in any supported wallet. + + + Secure Payment options view + + +## Why it exists + +The Secure Payment page is intentionally separated from the merchant frontend so a compromised merchant site or API endpoint can't trick a payer into signing the wrong transaction. The page: + +- Loads payment metadata from the Request API using a one-time token +- Validates every contract address against a hardcoded trusted set +- Decodes the transaction calldata and shows the payer what they're about to sign +- Resolves payee addresses to ENS where available +- Refuses to broadcast if anything looks off + +## What the payer sees + + + + The payer clicks a `pay.request.network/?token=...` URL you generated via [`POST /v2/secure-payments`](/api-reference/secure-payments). The page loads payment details (amount, recipient, reference). + + + + The connect modal lists EVM and Tron wallets side by side. + + + Connect wallet modal — EVM + + + + Connect wallet modal — Tron + + + + + The page reads the connected wallet's balances across supported chains and surfaces every chain × token combo with a sufficient balance. Same-chain payments show no swap; cross-chain shows a Li.Fi banner. + + + Cross-chain payment options + + + + + For ERC-20s, an approval transaction precedes the payment. The page shows a stepper (connect → confirm → setup → pay) and an optional **Advanced** view that decodes the calldata for inspection. + + + Advanced view with decoded calldata + + + + + The success screen shows the source and destination transaction hashes, with explorer links per chain. For cross-chain payments, a Li.Fi badge is displayed. + + + Payment success screen + + + + +## Wallet support + +| EVM wallets | Tron wallets | +| --- | --- | +| MetaMask | TronLink | +| Coinbase Wallet | Guarda | +| WalletConnect (any wallet that supports it) | Trust Wallet | +| Any injected EIP-1193 provider (Rabby, Phantom-EVM, Bitget, OKX, Ledger, …) | WalletConnect-Tron | + +EVM and Tron are presented as co-equal columns — neither is hidden behind a more-options click. + + +TronLink also injects an EVM provider that converts Tron addresses to EVM-style. Secure Payment **filters this out** of the EVM wallet list because it would mis-route Tron payments — Tron is handled exclusively via the TronWeb integration. + + +## Cross-chain via Li.Fi + +When the payer's selected source chain differs from the destination chain, Secure Payment routes through Li.Fi. The UI displays: + +- "You'll be paying across chain via [Li.Fi](https://li.fi/)" on the options view +- The bridge fee in the cost breakdown +- A Li.Fi badge on the success screen + +Supported source chains for Li.Fi swap-to-pay: Ethereum, Arbitrum One, Optimism, Base, Polygon, BSC, Tron. + +## Tron support + +Tron is fully supported as both a **source** and a **destination** for single-recipient payments: + +| Direction | Same-chain | Cross-chain (Li.Fi) | +| --- | --- | --- | +| Tron → Tron (USDT/USDC) | ✓ | — | +| EVM → Tron | — | ✓ | +| Tron → EVM | — | ✓ | + + +**Multi-recipient Tron Secure Payments are not supported.** A Secure Payment link with multiple `requests[]` entries pointing at Tron destinations is rejected at creation time with a 400. EVM batches up to 200 payees per link work as expected. + + +## Error states + +The Secure Payment app handles a few well-defined error states with clear copy: + +| State | Message | +| --- | --- | +| Link expired (>7 days, or already paid expired) | "You are coming too late. Please contact the recipient to send you a new payment link." | +| Token not found / invalid URL | "This URL is invalid. Please check the link and try again, or contact the recipient of this payment." | +| Already paid | "If that is not the case, please check the link and try again, or contact the recipient of this payment." | +| Calldata validation fails | "There is an issue with this payment call data. Please contact the recipient to send you a new payment link." | + +## What Secure Payment does *not* do + +- It does **not** persist transaction history beyond the immediate confirmation screen — explorers and webhook events are the source of truth. +- It does **not** provide a payee-side dashboard — that's [Dashboard](/tools/dashboard) at `dashboard.request.network`. +- It does **not** accept payments without a backing Request — the link must originate from `POST /v2/secure-payments`. + +## Open Secure Payment + + +Try a payment link in the live app. + + +## Related + + + + The payer experience and how it routes across chains. + + + + The API call that mints these links. + + + + Full request/response schemas. + + + + The payee-side companion. + + diff --git a/use-cases/batch-payouts.mdx b/use-cases/batch-payouts.mdx new file mode 100644 index 0000000..bf2caf6 --- /dev/null +++ b/use-cases/batch-payouts.mdx @@ -0,0 +1,200 @@ +--- +title: "Batch payouts (EVM)" +description: "Pay many recipients in one signed transaction. Marketplaces paying vendors, agencies paying contractors, refund campaigns, payroll-adjacent flows." +--- + +## What you'll build + +A workflow for paying many recipients at once with a single payer signature — gas-amortized, atomic, on EVM. Either pay programmatically by getting calldata and broadcasting it yourself, or hand the payer a hosted batch URL on `pay.request.network`. + +**Audience:** marketplaces paying out sellers, agencies paying contractors, refund campaigns, payroll-adjacent flows, any scenario where you'd otherwise sign N transactions back-to-back. + + +**EVM-only.** Tron does not support batch payments. Submitting a batch with Tron destinations returns a 400 with: `Batch payments are not supported for TRON networks. Please submit individual payment requests.` For Tron recipients, send single payouts in a loop using [`POST /v2/payouts`](/api-features/payouts) or [`POST /v2/secure-payments/payouts`](/api-reference/secure-payments#post-v2secure-paymentspayouts). + + +## Two modes + +| Mode | Endpoint | What you get back | +| --- | --- | --- | +| **Direct execution** | [`POST /v2/payouts/batch`](/api-features/payouts#batch-payout) | Approval calldata + a single batch payment transaction you broadcast from your wallet | +| **Hosted batch link** | [`POST /v2/secure-payments`](/api-reference/secure-payments) with multiple `requests[]` | A `pay.request.network` URL the payer opens, signs once, and pays everyone | + +Pick **direct execution** when your backend signs payouts (you control the payer wallet). Pick **hosted batch link** when the payer is a human who needs a UI. + +## Prerequisites + +Same as the [Quickstart](/use-cases/quickstart) — you need a Client ID. Webhooks are optional but recommended so you get notified when each leg of the batch settles. + +## Mode 1 — Direct execution + +`POST /v2/payouts/batch` accepts up to 200 payment requests. All must be on the same EVM network. Mixed payment types (ERC-20 + native) are allowed within the batch. + +```typescript +const response = await fetch( + "https://api.request.network/v2/payouts/batch", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-client-id": process.env.RN_CLIENT_ID!, + }, + body: JSON.stringify({ + requests: [ + { + payee: "0xb07d2398d2004378cad234da0ef14f1c94a530e4", + amount: "50", + invoiceCurrency: "USD", + paymentCurrency: "USDC-base", + }, + { + payee: "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", + amount: "75", + invoiceCurrency: "USD", + paymentCurrency: "USDC-base", + }, + // ...up to 200 recipients + ], + payer: process.env.PAYOUT_WALLET!, + }), + }, +); + +const { ERC20ApprovalTransactions, batchPaymentTransaction } = + await response.json(); +``` + +The response contains: +- `ERC20ApprovalTransactions[]` — zero or more approval transactions you must broadcast first (one per token) +- `batchPaymentTransaction` — the single transaction that settles all payments at once + +Sign and broadcast in order. If the wallet already has sufficient allowance, the approvals array is empty. + +```typescript +import { ethers } from "ethers"; + +const signer = wallet.connect(provider); + +for (const approvalTx of ERC20ApprovalTransactions) { + const tx = await signer.sendTransaction(approvalTx); + await tx.wait(); +} + +const batchTx = await signer.sendTransaction(batchPaymentTransaction); +await batchTx.wait(); +``` + +## Mode 2 — Hosted batch link + +If you don't want to handle calldata in your app, mint a hosted link with multiple `requests[]` and let the payer open it. + +```typescript +const response = await fetch( + "https://api.request.network/v2/secure-payments", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-client-id": process.env.RN_CLIENT_ID!, + }, + body: JSON.stringify({ + requests: contractors.map((c) => ({ + destinationId: c.destinationId, + amount: c.amount, + })), + reference: `payroll-${period}`, + }), + }, +); + +const { securePaymentUrl } = await response.json(); +// Send securePaymentUrl to whoever signs payouts +``` + +The Secure Payment app shows the full payee list, total amount, and a single approval-then-pay flow. The payer signs once. + +## Real-world examples + +### Marketplace seller payouts + +```typescript +// Pay every seller their week's earnings in one tx +const sellers = await db.sellers.findWithUnpaidBalance(); + +await fetch("https://api.request.network/v2/payouts/batch", { + method: "POST", + headers: { "Content-Type": "application/json", "x-client-id": clientId }, + body: JSON.stringify({ + requests: sellers.map((s) => ({ + payee: s.walletAddress, + amount: s.balanceUsdc.toString(), + invoiceCurrency: "USD", + paymentCurrency: "USDC-base", + })), + payer: marketplaceWallet, + }), +}); +``` + +### Refund campaign + +```typescript +// Refund 30 customers from a botched promo +await fetch("https://api.request.network/v2/payouts/batch", { + method: "POST", + headers: { "Content-Type": "application/json", "x-client-id": clientId }, + body: JSON.stringify({ + requests: refundList.map((r) => ({ + payee: r.walletAddress, + amount: r.refundAmount, + invoiceCurrency: "USD", + paymentCurrency: "USDC-arbitrum-one", + })), + payer: refundWallet, + }), +}); +``` + +### Contractor payroll + +```typescript +// Run end-of-month payroll for the team +await fetch("https://api.request.network/v2/payouts/batch", { + method: "POST", + headers: { "Content-Type": "application/json", "x-client-id": clientId }, + body: JSON.stringify({ + requests: contractors.map((c) => ({ + payee: c.walletAddress, + amount: c.monthlyRate.toString(), + invoiceCurrency: "USD", + paymentCurrency: "USDC-optimism", + })), + payer: payrollWallet, + }), +}); +``` + +## Webhook events + +Each request inside the batch produces its own `payment.confirmed` event when settled. For a batch of 50, expect 50 webhook deliveries — each carries its own `requestId` and `paymentReference`. Use the `reference` you set on creation (e.g. `"payroll-2026-04"`) to group them on your side. + +See [Webhook reconciliation](/use-cases/webhook-reconciliation) for the full handler pattern. + +## Limits + +- **Up to 200 recipients per batch transaction**, depending on payment type complexity +- **Same network only** — all `requests[]` must target the same chain. No mixing Base + Arbitrum in one batch. +- **EVM only** — see warning at top +- **Mixed currency types within a network are OK** — ERC-20 + native ETH/MATIC/BNB in one batch is supported + +## Related + + + + Single, batch, and recurring payout endpoints. + + + + Wire batch confirmations into your accounting / DB. + + diff --git a/use-cases/multi-chain-checkout.mdx b/use-cases/multi-chain-checkout.mdx new file mode 100644 index 0000000..b2ee702 --- /dev/null +++ b/use-cases/multi-chain-checkout.mdx @@ -0,0 +1,128 @@ +--- +title: "Multi-chain checkout" +description: "Let payers pay from any chain and any token. Receive on your preferred chain. Cross-chain swaps are routed through Li.Fi." +--- + +## Why multi-chain matters + +When you create a Secure Payment link, you fix the **destination** — the chain and token you want to receive on. The payer doesn't have to match it. They can hold USDC on Optimism, USDT on Polygon, or USDT on Tron, and Request Network's Secure Payment app will route the payment through Li.Fi to land in your destination token. + +This means your customers don't need to bridge anything before paying. They open the link, the app shows them every chain/token combination they hold a balance in, and they pick one. + +**Audience:** any merchant/integrator whose customers don't know or don't want to know about chains. E-commerce, SaaS, marketplaces. + +## Supported source chains + +Cross-chain swap-to-pay routes through Li.Fi. Source chains the Secure Payment app can pay *from*: + +- Ethereum (`ETHEREUM`) +- Arbitrum One (`ARBITRUM`) +- Optimism (`OPTIMISM`) +- Base (`BASE`) +- Polygon (`POLYGON`) +- BNB Smart Chain (`BNB`) +- **Tron** (paid same-chain or as a cross-chain leg) + +The destination chain can be any of the above plus Sepolia (testnet). USDC and USDT are the standard payment currencies; Sepolia adds FAU. + + +The Secure Payment UI displays a Li.Fi banner on the payment options screen when the payer's selected chain differs from your destination — so customers know exactly when a swap is happening. + + +## What the payer experiences + + + + Customer clicks the `pay.request.network` URL. + + + Connect wallet modal (EVM) + + + + + Any EVM wallet (MetaMask, Coinbase, WalletConnect, plus injected wallets like Rabby, Phantom-EVM) **or** any Tron wallet (TronLink, Guarda, Trust, WalletConnect-Tron). The modal lists both alongside. + + + Tron wallet selection + + + + + The app shows all the payer's holdings across supported chains, with USD-equivalent balances and a banner indicating same-chain vs cross-chain. + + + Cross-chain payment options with Li.Fi + + + + + For ERC-20s, an approval transaction precedes the payment. Both transactions are signed by the payer in their wallet. For cross-chain, the source-chain bridge transaction is signed; the destination-chain settlement is monitored by the API and reported via webhook. + + + + The success screen shows the source and destination transaction hashes (linked to the appropriate explorer per chain) and a Li.Fi badge for cross-chain payments. + + + Payment success screen + + + + +## Tron specifics + +Tron is supported as both a source and a destination for single-recipient payments: + +| Direction | Supported? | Notes | +| --- | --- | --- | +| Tron → Tron (USDT/USDC) | ✓ | Same-chain, no swap | +| EVM → Tron (USDT/USDC) | ✓ | Routed via Li.Fi | +| Tron → EVM (USDT/USDC) | ✓ | Routed via Li.Fi | +| **Multi-recipient on Tron** | ✗ | Batch is EVM-only — see below | + + +Multi-recipient (batch) Secure Payment links are EVM-only. If your destination is Tron and you submit multiple `requests[]`, the API returns a 400 with `Batch payments are not supported for TRON networks.` + + +## What you don't have to do + +- **No bridging UX of your own** — Li.Fi is integrated end-to-end inside the Secure Payment page +- **No quote management** — quotes are fetched per session, surfaced in the UI, refreshed if they expire before the payer signs +- **No source-chain config** — supplying a destination is enough; the Secure Payment app derives the rest from the payer's wallet + +## Code: nothing changes + +Creating a multi-chain-capable link uses the same `POST /v2/secure-payments` call as same-chain — there are no extra parameters. The payer choosing a different source chain happens in the hosted UI, not in your API call. + +```typescript +await fetch("https://api.request.network/v2/secure-payments", { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-client-id": process.env.RN_CLIENT_ID!, + }, + body: JSON.stringify({ + requests: [ + { + destinationId: process.env.MERCHANT_DESTINATION!, + amount: "100", + }, + ], + reference: order.id, + }), +}); +``` + +For the full request schema, see [`POST /v2/secure-payments`](/api-reference/secure-payments). + +## Related + + + + Full feature reference for pay.request.network. + + + + The API call that mints these links. + + diff --git a/use-cases/no-code-payment-links.mdx b/use-cases/no-code-payment-links.mdx new file mode 100644 index 0000000..1effbe9 --- /dev/null +++ b/use-cases/no-code-payment-links.mdx @@ -0,0 +1,106 @@ +--- +title: "No-code payment links" +description: "Generate payment links from the Dashboard UI — no API integration required. Best for freelancers, SMBs, and ops teams." +--- + +## What you'll build + +A repeatable workflow for sending one-off crypto payment links to customers, contractors, or partners — without writing a line of code. Sign in to the Dashboard with your wallet, set up your receiving destination once, then generate a fresh payment link each time you need to be paid. + +**Audience:** freelancers, SMBs, ops teams, anyone receiving low-to-medium volume crypto invoices who doesn't want to host their own checkout. + +**Apps used:** +- [Dashboard](https://dashboard.request.network) — sign in, create destination + Client ID, generate links +- [Secure Payment](https://pay.request.network) — what your customer sees when they open the link + +## Prerequisites + +- An EVM wallet (MetaMask, Coinbase Wallet, WalletConnect) **or** a Tron wallet (TronLink, Guarda, Trust) +- The wallet that will receive payments + +## The flow + + + + Open [dashboard.request.network](https://dashboard.request.network), connect your wallet, and sign the auth message. Both EVM and Tron wallets work — the Dashboard auto-detects the type and gives you parallel sign-in tabs. + + + Dashboard sign-in (EVM) + + + + + On the home page, click **Create Payment Destination**. Pick the chain (one of 7 EVM chains or Tron) and the token (USDC or USDT, plus FAU on Sepolia). Confirm. + + + Create payment destination dialog + + + The Dashboard generates a **destination ID** in the ERC-7828 format. You only need to do this once per chain/token combo you want to receive on. + + + + Open **Manage Destination → Client IDs → Generate New Client ID**. Give it a label (e.g. "freelance invoices"). For dashboard-only use, leave **Allowed Domains** empty. Save the generated value. + + + Generate Client ID dialog + + + The Client ID is the credential the Dashboard uses on your behalf when you create payment links. + + + + Click **Get Paid → New Request**. Fill in: + + - **Amount** — what you want to be paid (e.g. `500`) + - **Reference** — your invoice number or any tracking string + - **Payer identifier** (optional) — the customer's email or your internal ID + + + Create new request form + + + Click **Create**. The Dashboard returns a hosted payment URL on `pay.request.network`. + + + + Send the URL to your customer over email, Telegram, Slack, or paste it into your invoice PDF. They open it in any browser, connect any supported wallet (EVM or Tron, including cross-chain via Li.Fi), and pay. + + + + The **Get Paid** list shows status (Pending / Paid) for every link you've created, with on-chain transaction hashes for paid ones. + + + Get Paid list + + + + +## Tron parity + +The exact same flow works for Tron. Sign in with TronLink (or Guarda, Trust, WalletConnect-Tron) instead of MetaMask, pick the **Tron** chain when creating the destination, and pick **USDT** or **USDC**. Customers can pay your Tron link from any supported wallet — including paying *to* a Tron destination *from* an EVM wallet via Li.Fi swap-to-pay. + + +Tron secure payment links are **single-recipient only**. To pay multiple Tron addresses, generate a separate link per recipient. + + +## When to graduate from no-code + +This flow stays low-friction up to ~50 links per month. Beyond that — or if any of the below applies — switch to [programmatic payment links](/use-cases/programmatic-payment-links): + +- You want links generated automatically from your invoicing or e-commerce app +- You need to attribute payments to internal user IDs +- You want webhook-driven order fulfillment / accounting (see [Webhook reconciliation](/use-cases/webhook-reconciliation)) +- You're paying many recipients at once (see [Batch payouts](/use-cases/batch-payouts)) + +## Related + + + + Full feature reference for dashboard.request.network. + + + + Same flow with API and webhook details for when you outgrow no-code. + + diff --git a/use-cases/programmatic-payment-links.mdx b/use-cases/programmatic-payment-links.mdx new file mode 100644 index 0000000..d331f99 --- /dev/null +++ b/use-cases/programmatic-payment-links.mdx @@ -0,0 +1,259 @@ +--- +title: "Programmatic payment links" +description: "Generate Secure Payment links on demand from your backend. Best for e-commerce, marketplaces, SaaS, and any app that bills programmatically." +--- + +## What you'll build + +A backend that creates a Secure Payment link in response to your app's events — a customer hitting checkout, an invoice being approved, a subscription renewing. The customer opens the link, pays from any chain/token in any supported wallet, and your webhook fires when the payment confirms. + +**Audience:** developers integrating Request Network into an e-commerce app, marketplace, SaaS, or accounting product. + +**Apps used:** +- [Auth API](https://auth.request.network/open-api) — Client IDs, webhooks +- [Request API](https://api.request.network/open-api) — `POST /v2/secure-payments` and `POST /v2/secure-payments/payouts` +- [Secure Payment](https://pay.request.network) — what your customer sees when they open the link + +## Prerequisites + +Complete steps 1–4 of the [Quickstart](/use-cases/quickstart): +1. Sign in to the Dashboard with your wallet +2. Create a payment destination +3. Create a Client ID (with allowed domains for browser apps, empty for backend-only) +4. Register a webhook URL pointing at your handler + +You'll need: +- `clientId` — passed as `x-client-id` on every API call +- `destinationId` — composite ID combining `humanReadableInteropAddress` + `tokenAddress` +- Webhook signing secret — for verifying webhook payloads + +## Create an incoming payment link + +Use `POST /v2/secure-payments` to mint a link the customer pays into. + + + +```typescript +const response = await fetch("https://api.request.network/v2/secure-payments", { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-client-id": process.env.RN_CLIENT_ID!, + }, + body: JSON.stringify({ + requests: [ + { + destinationId: + "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:8453#ABCD1234:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + amount: "100", + }, + ], + reference: order.id, + payerIdentifier: customer.email, + }), +}); + +const { securePaymentUrl, requestIds, token } = await response.json(); +// Send securePaymentUrl to the customer (redirect, email, etc.) +``` + + +```python +import os +import requests + +response = requests.post( + "https://api.request.network/v2/secure-payments", + headers={ + "Content-Type": "application/json", + "x-client-id": os.environ["RN_CLIENT_ID"], + }, + json={ + "requests": [ + { + "destinationId": ( + "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7" + "@eip155:8453#ABCD1234:" + "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" + ), + "amount": "100", + } + ], + "reference": order.id, + "payerIdentifier": customer.email, + }, +) + +data = response.json() +secure_payment_url = data["securePaymentUrl"] +``` + + +```bash +curl -X POST "https://api.request.network/v2/secure-payments" \ + -H "Content-Type: application/json" \ + -H "x-client-id: $RN_CLIENT_ID" \ + -d '{ + "requests": [ + { + "destinationId": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:8453#ABCD1234:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amount": "100" + } + ], + "reference": "ORDER-2026-042", + "payerIdentifier": "alice@example.com" + }' +``` + + + +The response includes `securePaymentUrl` — share it with the customer. They open it on `pay.request.network`, connect a wallet, and pay. + +## EVM and Tron destinations side-by-side + +Tron is a drop-in replacement for any EVM destination as long as you submit a single recipient per call. The shape is identical; only the `destinationId` and addresses change format. + + + +```json +{ + "requests": [ + { + "destinationId": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:8453#ABCD1234:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amount": "100" + } + ] +} +``` + + +```json +{ + "requests": [ + { + "destinationId": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8@eip155:728126428#5F89A3B2:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", + "amount": "100" + } + ] +} +``` + + + + +Submitting multiple `requests[]` items where any destination is on Tron returns a 400 with `Batch payments are not supported for TRON networks. Please submit individual payment requests.` See [Batch payouts](/use-cases/batch-payouts) for the EVM-only batch flow. + + +## Send a payout link (outgoing) + +To *pay* someone (a contractor, vendor, refund recipient) via a hosted link they open and sign, use `POST /v2/secure-payments/payouts`. Same shape, single recipient, EVM or Tron. + + + +```typescript +const response = await fetch( + "https://api.request.network/v2/secure-payments/payouts", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-client-id": process.env.RN_CLIENT_ID!, + }, + body: JSON.stringify({ + recipient: "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", + creatorWalletAddress: process.env.PAYOUT_WALLET!, + network: "base", + currency: "USDC-base", + amount: "250", + reference: "INVOICE-2026-042", + }), + }, +); + +const { securePaymentUrl } = await response.json(); +``` + + +```typescript +const response = await fetch( + "https://api.request.network/v2/secure-payments/payouts", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-client-id": process.env.RN_CLIENT_ID!, + }, + body: JSON.stringify({ + recipient: "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8", + creatorWalletAddress: process.env.PAYOUT_WALLET_TRON!, + network: "tron", + currency: "USDT-tron", + amount: "250", + reference: "INVOICE-2026-042", + }), + }, +); +``` + + + +The hosted URL the response returns is opened by *you* (or whoever signs payouts). The signer's wallet must match `creatorWalletAddress`. + +## Receive payment notifications + +Your webhook endpoint receives signed POSTs as the payment lifecycle progresses. Verify the HMAC-SHA256 signature against your webhook secret before parsing the body. + +```typescript +import { createHmac, timingSafeEqual } from "node:crypto"; +import express from "express"; + +const app = express(); + +// Capture raw body for signature verification +app.use( + "/webhook", + express.raw({ type: "application/json" }), + (req, res) => { + const signature = req.headers["x-request-network-signature"] as string; + const expected = createHmac("sha256", process.env.WEBHOOK_SECRET!) + .update(req.body) + .digest("hex"); + + const ok = + signature.length === expected.length && + timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex")); + + if (!ok) return res.status(401).send("invalid signature"); + + const event = JSON.parse(req.body.toString("utf8")); + + if (event.event === "payment.confirmed") { + // Mark the order paid in your DB, fire fulfillment, etc. + } + + res.status(200).send("ok"); + }, +); +``` + +For the full webhook spec — all 12 events, retry policy, headers — see [Webhook reconciliation](/use-cases/webhook-reconciliation) and the [Webhooks reference](/api-reference/webhooks). + +## Next steps + + + + What the customer sees when they pay across chains. + + + + Pay many EVM recipients in one signed transaction. + + + + The reliable, signature-verified way to detect payments. + + + + Full request/response schemas for every endpoint. + + diff --git a/use-cases/quickstart.mdx b/use-cases/quickstart.mdx new file mode 100644 index 0000000..2fb119e --- /dev/null +++ b/use-cases/quickstart.mdx @@ -0,0 +1,477 @@ +--- +title: "Quickstart" +description: "End-to-end walkthrough — sign in to the Dashboard, create a payment destination, register a webhook, and create a hosted payment link." +--- + +This guide walks through the canonical flow for receiving payments via Request Network: creating a payment destination and Client ID in the Dashboard, registering a webhook for payment notifications, and creating a payment link. Every other use-case page links back to specific steps here. + +## Overview + +The flow involves three services: + +1. **Dashboard** ([dashboard.request.network](https://dashboard.request.network)) — Sign in with your wallet, create payment destinations, and generate Client IDs +2. **Auth API** ([auth.request.network](https://auth.request.network/open-api)) — Programmatic alternative for Client IDs, and the home of webhooks +3. **Request API** ([api.request.network](https://api.request.network/open-api)) — Create payment links (secure payments) + +``` +┌──────────────────────┐ +│ 1. Sign in with │ dashboard.request.network +│ wallet │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ 2. Create Payee │ dashboard.request.network +│ Destination │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ 3. Create Client ID │ dashboard.request.network +│ │ (or auth.request.network/open-api) +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ 4. Register Webhook │ auth.request.network/open-api +│ │ (POST /v1/webhook with x-client-id) +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ 5. Create Secure │ api.request.network/open-api +│ Payment (link) │ +└──────────────────────┘ +``` + +Your wallet session cookie (`session_token`) is shared across `dashboard.request.network`, `auth.request.network`, and `api.request.network` — so signing in once on the Dashboard gives you access to the API docs as well. + +## Prerequisites + +- An EVM wallet (e.g. MetaMask) **or** a Tron wallet (e.g. TronLink) that will receive payments +- The ability to sign messages with that wallet + +## Step 1: Sign in with Your Wallet + + + + Go to [dashboard.request.network](https://dashboard.request.network). + + + Connect your EVM or Tron wallet and sign the authentication message when prompted. + + + Once signed in, you have an active wallet session. This sets a `session_token` cookie in your browser that is shared across Request Network services. + + + + +Wallet sessions expire after 15 minutes of idle time. If your session expires during the following steps, return to the Dashboard and sign in again. + + +## Step 2: Create a Payment Destination + +A payment destination registers where you want to receive payments — it links your wallet address to a specific token on a specific chain. + + + + In the Dashboard, navigate to the payment destination setup. + + + Select the **chain** and **token** you want to receive payments in. + + + Confirm the creation. + + + +The Dashboard returns a **`destinationId`** (also shown as `humanReadableInteropAddress`). Save this value — you'll need it in Step 5. + +The `destinationId` follows the ERC-7828 format: + +``` +{walletAddress}@eip155:{chainId}#{checksum} +``` + +### Supported Chains and Tokens + +| Network | Chain ID | USDC | USDT | +| --- | --- | --- | --- | +| Ethereum | `1` | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | `0xdAC17F958D2ee523a2206206994597C13D831ec7` | +| Arbitrum One | `42161` | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` | `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9` | +| Optimism | `10` | `0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85` | `0x94b008aA00579c1307B0EF2c499aD98a8ce58e58` | +| Base | `8453` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2` | +| Polygon | `137` | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` | `0xc2132D05D31c914a87C6611C10748AEb04B58e8F` | +| BSC | `56` | `0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d` | `0x55d398326f99059ff775485246999027b3197955` | +| **Tron** | `728126428` | `TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8` | `TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t` | + +**Testnet:** + +| Network | Chain ID | Tokens | +| --- | --- | --- | +| Sepolia | `11155111` | FAU: `0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C`, USDC: `0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238`, USDT: `0xF046b3CA5ae2879c6bAcC4D42fAF363eE8379F78` | + +## Step 3: Create a Client ID + +A Client ID identifies your application and is required to create payment links and register webhooks. + +### Option A — Dashboard (recommended) + + + + Go to [dashboard.request.network](https://dashboard.request.network). + + + Open your payment destination settings. + + + Open the **Client IDs** section. + + + Click **Generate New Client ID**. + + + Enter a human-readable name. Add **Allowed Domains** if the Client ID will be used from a frontend application. Leave empty for backend/server-side usage only. + + + Confirm to generate the Client ID. Copy and save the generated `clientId` — you'll use it in Steps 4 and 5. + + + + +Allowed domains must use `https://` (except for `localhost`, `127.0.0.1`, or `::1` which allow `http://`). No path, query, or fragment allowed. + + +### Option B — Auth API (for automation) + +Open the Scalar docs at [auth.request.network/open-api](https://auth.request.network/open-api/#tag/client-ids/POST/v1/client-ids) and call: + +```http +POST https://auth.request.network/v1/client-ids +Content-Type: application/json + +{ + "label": "My Client ID", + "allowedDomains": ["https://mydomain.com"], + "feePercentage": null, + "feeAddress": null, + "operatorWalletAddress": null, + "payeeDestinationId": "" +} +``` + +Your wallet session from Step 1 is sent automatically via cookie. + +**Example response (201 Created):** + +```json +{ + "id": "01KJBN4KR5PFG4NAQG60EHR2Y0", + "clientId": "cli_nz1bj41szV2fvjm9pbxdIhro3ld4x4", + "label": "My Client ID", + "allowedDomains": ["https://mydomain.com"], + "feePercentage": null, + "feeAddress": null, + "operatorWalletAddress": null, + "defaultPreApprovalExpiry": null, + "defaultAuthorizationExpiry": null, + "status": "active", + "createdAt": "2026-03-02T19:42:37 GMT+0000" +} +``` + +Save the `clientId` value (e.g. `cli_nz1bj41szV2fvjm9pbxdIhro3ld4x4`) — you'll use it in Steps 4 and 5. + +## Step 4: Register a Webhook + +Webhooks let you receive real-time notifications when a payment is completed (or partially paid) for your payment links — no polling required. + +Webhooks are scoped to the Client ID that creates them. Any payment link created with that Client ID will trigger the webhook. The webhook payload includes the `clientId` field so you can identify which Client ID the payment was associated with. + +### Creating a webhook + +Open [auth.request.network/open-api](https://auth.request.network/open-api/#tag/webhook/POST/v1/webhook) and call: + +```http +POST https://auth.request.network/v1/webhook +x-client-id: +Content-Type: application/json + +{ + "url": "https://mydomain.com/webhook" +} +``` + +**Example response (201 Created):** + +```json +{ + "id": "01KJC2WX8EH4MP3DHZB2YQ7N9G", + "secret": "f3c189a4b5e6d7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2" +} +``` + + +The `secret` is only returned once at creation. Store it securely — you cannot retrieve it again. Use HTTPS in production. `localhost` URLs are accepted for local testing. + + +### Managing webhooks + +Use the same Scalar docs page to list, toggle, or delete webhooks (all accept `x-client-id`): + +| Method | Path | Purpose | +| --- | --- | --- | +| `GET` | `/v1/webhook` | List webhooks for this Client ID | +| `PUT` | `/v1/webhook/:webhookId` | Toggle active / inactive | +| `DELETE` | `/v1/webhook/:webhookId` | Permanently delete | +| `POST` | `/v1/webhook/test` | Body `{ "eventType": "payment.confirmed" }` — fire a test delivery | + +### Verifying webhook signatures + +Every webhook request includes a signature header so you can verify it came from Request Network: + +| Header | Description | +| --- | --- | +| `x-request-network-signature` | HMAC-SHA256 of the raw JSON body, signed with your webhook secret | +| `x-request-network-delivery` | Unique delivery ID — use as an idempotency key | +| `x-request-network-retry-count` | Retry attempt number (`0`–`3`) | +| `x-request-network-test` | `true` only for test deliveries via `/v1/webhook/test` | + +To verify, compute `HMAC-SHA256(rawBody, webhookSecret)` and compare it to `x-request-network-signature` using a constant-time comparison. + +```typescript +import { createHmac, timingSafeEqual } from "node:crypto"; + +function verify(rawBody: string, signature: string, secret: string) { + const expected = createHmac("sha256", secret).update(rawBody).digest("hex"); + const a = Buffer.from(expected, "hex"); + const b = Buffer.from(signature, "hex"); + return a.length === b.length && timingSafeEqual(a, b); +} +``` + +Always verify against the **raw** request body before parsing. + +**Retries:** up to 3 retries (4 attempts total), default delays 1s / 5s / 15s, triggered on any non-2xx response, timeout, or connection error. + +### Webhook events for payment links + +When a payer completes a payment on a payment link you created, your webhook receives a `payment.confirmed` event (or `payment.partial` for partial payments). For Client ID-scoped variants you'll also receive `payment.confirmed.client_id` / `payment.partial.client_id` with extra `clientId` and `origin` fields. + +**Example `payment.confirmed` payload:** + +```json +{ + "event": "payment.confirmed", + "requestId": "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb", + "paymentReference": "0xabc123...", + "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", + "amount": "10.0", + "totalAmountPaid": "10.0", + "expectedAmount": "10.0", + "timestamp": "2026-03-02T20:15:00.000Z", + "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "network": "sepolia", + "currency": "FAU", + "paymentCurrency": "FAU", + "clientId": "cli_nz1bj41szV2fvjm9pbxdIhro3ld4x4", + "origin": "https://mydomain.com" +} +``` + +Key fields to look for: + +| Field | Description | +| --- | --- | +| `event` | `payment.confirmed` (fully paid) or `payment.partial` (partial payment) | +| `requestId` | The request ID from when you created the payment link | +| `clientId` | The Client ID used to create the payment link — use this to route events if you have multiple Client IDs | +| `amount` | The amount paid in this transaction | +| `totalAmountPaid` | Cumulative amount paid so far | +| `expectedAmount` | The total amount expected | +| `txHash` | On-chain transaction hash | +| `network` | The blockchain network | +| `currency` | The token used for payment | + +### All supported webhook events + +| Event | Description | +| --- | --- | +| `payment.confirmed` | Payment fully confirmed | +| `payment.partial` | Partial payment received | +| `payment.confirmed.client_id` | Client ID-scoped variant of `payment.confirmed` | +| `payment.partial.client_id` | Client ID-scoped variant of `payment.partial` | +| `payment.confirmed.checkout` | Secure-payment-scoped variant of `payment.confirmed` | +| `payment.partial.checkout` | Secure-payment-scoped variant of `payment.partial` | +| `payment.failed` | Payment failed | +| `payment.refunded` | Payment refunded | +| `payment.processing` | Offramp processing started | +| `request.recurring` | A recurring request fired | +| `payment_detail.updated` | Payment detail metadata changed | +| `compliance.updated` | Compliance status changed | + +## Step 5: Create a Secure Payment (Payment Link) + +With the `destinationId` from Step 2 and the `clientId` from Step 3, you can now create a payment link. When the payment is completed, the webhook from Step 4 will be notified automatically. + + + + Open the Request API docs: [api.request.network/open-api](https://api.request.network/open-api/#tag/v2secure-payment/POST/v2/secure-payments). + + + Set the `x-client-id` header to the `clientId` value from Step 3. + + + ```json + { + "requests": [ + { + "destinationId": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:11155111#1f969856:0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C", + "amount": "1" + } + ] + } + ``` + + + Click **Send**. + + + +### Constructing the `destinationId` + +The `destinationId` in the request body is a composite value that combines the payee destination's `humanReadableInteropAddress` (from Step 2) with the `tokenAddress`, separated by `:`: + +``` +{humanReadableInteropAddress}:{tokenAddress} +``` + +For example: + +``` +0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7@eip155:11155111#1f969856:0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C +└──────────── humanReadableInteropAddress ─────────────────┘ └──────────── tokenAddress ──────────────────────┘ +``` + +### Request body fields + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `requests` | `array` | Yes | Array of payment request items (at least 1; multiple items create a batch on EVM only) | +| `requests[].destinationId` | `string` | Yes | Composite ID: `humanReadableInteropAddress:tokenAddress` | +| `requests[].amount` | `string` | Yes | Amount in human-readable format (e.g. `"10"` for 10 USDC) | +| `feePercentage` | `string` | No | Fee percentage (0–100). If set, `feeAddress` is required. | +| `feeAddress` | `string` | No | Address to receive fees. Required if `feePercentage` is set. | +| `reference` | `string` | No | Merchant reference (≤ 255 chars) | +| `payerIdentifier` | `string` | No | Payer identifier (≤ 255 chars) | + + +Submitting multiple `requests[]` items where any destination is on **Tron** returns a 400 with `Batch payments are not supported for TRON networks. Please submit individual payment requests.` Tron secure payments are single-recipient. EVM batches up to 200 payees per link work as expected. + + +**Example response (201 Created):** + +```json +{ + "requestIds": [ + "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb" + ], + "securePaymentUrl": "https://pay.request.network/?token=01KJRA0M9QG8MA4X887908T8A4", + "token": "01KJRA0M9QG8MA4X887908T8A4" +} +``` + +| Field | Type | Description | +| --- | --- | --- | +| `requestIds` | `string[]` | IDs of the created payment requests | +| `securePaymentUrl` | `string` | Shareable URL for the payer to complete payment | +| `token` | `string` | Unique token for this payment session | + +Share the `securePaymentUrl` with the payer. They can open it in their browser to complete the payment. + + +The payment link expires after 7 days or once it has been paid, whichever comes first. + + +## Step 6: Check Payment Status (Optional) + +In addition to receiving webhook notifications (Step 4), you can also poll for payment status using the `requestId` from Step 5. + +**Endpoint:** `GET https://api.request.network/v2/request/{requestId}` + +You can call this from the [Request API docs](https://api.request.network/open-api) using the same wallet session, or programmatically with the `x-client-id` header. + +**Example response (200 OK — paid):** + +```json +{ + "hasBeenPaid": true, + "requestId": "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb", + "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", + "isListening": false, + "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" +} +``` + +**Example response (200 OK — not yet paid):** + +```json +{ + "hasBeenPaid": false, + "requestId": "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb", + "payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7", + "isListening": true, + "txHash": null +} +``` + +| Field | Type | Description | +| --- | --- | --- | +| `hasBeenPaid` | `boolean` | Whether the request has been fully paid | +| `requestId` | `string` | The request ID | +| `payee` | `string` | The payee's wallet address | +| `isListening` | `boolean` | Whether the system is still listening for payment | +| `txHash` | `string` | Transaction hash of the payment (`null` if unpaid) | + +## Quick Reference + +| Step | Action | Where | +| --- | --- | --- | +| 1 | Sign in with wallet | [dashboard.request.network](https://dashboard.request.network) | +| 2 | Create payment destination | [dashboard.request.network](https://dashboard.request.network) | +| 3 | Create Client ID | [dashboard.request.network](https://dashboard.request.network) (or [auth.request.network/open-api](https://auth.request.network/open-api/#tag/client-ids/POST/v1/client-ids)) | +| 4 | Register webhook | [auth.request.network/open-api](https://auth.request.network/open-api/#tag/webhook/POST/v1/webhook) | +| 5 | Create payment link | [api.request.network/open-api](https://api.request.network/open-api/#tag/v2secure-payment/POST/v2/secure-payments) | +| 6 | Check payment status | [api.request.network/open-api](https://api.request.network/open-api) (or via webhook) | + +## Troubleshooting + +| Issue | Solution | +| --- | --- | +| `401 Unauthorized` on Dashboard / Auth API calls | Your wallet session has likely expired (15 min TTL). Go back to [dashboard.request.network](https://dashboard.request.network) and sign in again. | +| `Origin header is required` | When using `x-client-id` from a browser, include an `Origin` header matching one of the Client ID's allowed domains. | +| `Invalid destination ID format` | The `destinationId` must be `humanReadableInteropAddress:tokenAddress`. Make sure both values are joined with `:` as the separator. | +| `Batch payments are not supported for TRON networks` | Tron requests must be single-recipient. Submit one `requests[]` item per Tron destination. | +| Webhook signature doesn't match | Verify against the **raw** request body (no re-serialization), use HMAC-SHA256, and compare with a constant-time check. | +| Webhook secret lost | Secrets are only shown at creation. Delete the webhook (`DELETE /v1/webhook/:id`) and create a new one. | +| Payment link expired | Payment links expire after 7 days or once paid. Create a new one if needed. | + +## What's next + + + + Skip the code — generate payment links from the Dashboard UI. + + + + Server-side payment link creation with TS / Python / cURL examples. + + + + Let payers pay from any chain/token; receive on your preferred one. + + + + Automated payment notifications wired into your accounting/order systems. + + diff --git a/use-cases/webhook-reconciliation.mdx b/use-cases/webhook-reconciliation.mdx new file mode 100644 index 0000000..772d264 --- /dev/null +++ b/use-cases/webhook-reconciliation.mdx @@ -0,0 +1,227 @@ +--- +title: "Webhook reconciliation" +description: "Real-time payment notifications wired into your accounting, fulfillment, and order systems. The reliable way to detect payments without polling." +--- + +## What you'll build + +A webhook handler that receives signed payment events from Request Network, verifies the signature, and triggers your downstream systems — order fulfillment, invoice closeout, accounting entries, customer email. Polling-free, idempotent, retry-safe. + +**Audience:** any backend integrating Request Network where payment events drive state changes downstream. + +## The 12 events + +| Category | Event | When it fires | +| --- | --- | --- | +| Payment (core) | `payment.confirmed` | Payment fully settled on-chain | +| | `payment.partial` | Partial payment received, more expected | +| | `payment.failed` | Payment execution failed (recurring, cross-chain) | +| | `payment.refunded` | Payment refunded to payer | +| Payment (Client ID) | `payment.confirmed.client_id` | Same as `payment.confirmed`, request was created via Client ID — payload includes `clientId` and `origin` | +| | `payment.partial.client_id` | Client ID-scoped partial | +| Payment (Checkout) | `payment.confirmed.checkout` | Same as `payment.confirmed`, request originated from a Secure Payment link | +| | `payment.partial.checkout` | Secure Payment-scoped partial | +| Processing | `payment.processing` | Crypto-to-fiat offramp in progress (with detailed `subStatus`) | +| Request | `request.recurring` | A new recurring billing cycle fired | +| Compliance | `compliance.updated` | KYC or agreement status changed | +| Bank details | `payment_detail.updated` | Bank account verification status changed | + +For the full payload schemas, see the [Webhooks reference](/api-reference/webhooks). + +## Setup + + + + Complete steps 1–3 of the [Quickstart](/use-cases/quickstart). Note your `clientId`. + + + + `POST https://auth.request.network/v1/webhook` with header `x-client-id: ` and body `{ "url": "https://yourapp.com/webhooks/request-network" }`. + + Save the returned `secret` immediately — it's only shown once. + + + + Fire a test event from the [auth API docs](https://auth.request.network/open-api/#tag/webhook/POST/v1/webhook/test) with body `{ "eventType": "payment.confirmed" }`. The request will arrive with header `x-request-network-test: true`. + + + +## Handler — reference implementation + +A signature-verifying Express handler with idempotency. Hardened for production: verifies against the **raw** body, uses constant-time comparison, deduplicates on `x-request-network-delivery`. + +```typescript +import { createHmac, timingSafeEqual } from "node:crypto"; +import express from "express"; +import { Redis } from "ioredis"; + +const app = express(); +const redis = new Redis(process.env.REDIS_URL!); +const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!; + +app.post( + "/webhooks/request-network", + express.raw({ type: "application/json" }), + async (req, res) => { + const signature = req.headers["x-request-network-signature"] as string; + const deliveryId = req.headers["x-request-network-delivery"] as string; + const isTest = req.headers["x-request-network-test"] === "true"; + + if (!signature || !deliveryId) { + return res.status(400).send("missing headers"); + } + + // 1. Verify signature against the RAW body — never re-stringify + const expected = createHmac("sha256", WEBHOOK_SECRET) + .update(req.body) + .digest("hex"); + + const sigBuf = Buffer.from(signature, "hex"); + const expBuf = Buffer.from(expected, "hex"); + + if ( + sigBuf.length !== expBuf.length || + !timingSafeEqual(sigBuf, expBuf) + ) { + return res.status(401).send("invalid signature"); + } + + // 2. Idempotency: dedupe on delivery ID + const claim = await redis.set( + `webhook:${deliveryId}`, + "1", + "EX", + 86400, // 24h + "NX", + ); + if (!claim && !isTest) { + return res.status(200).send("duplicate, ignored"); + } + + // 3. Parse and route + const event = JSON.parse(req.body.toString("utf8")); + + try { + await handleEvent(event); + } catch (err) { + // Return non-2xx to trigger retry (1s, 5s, 15s — up to 3 retries) + console.error("handler failed", err); + return res.status(500).send("handler error"); + } + + res.status(200).send("ok"); + }, +); + +async function handleEvent(event: any) { + switch (event.event) { + case "payment.confirmed": + case "payment.confirmed.client_id": + case "payment.confirmed.checkout": + await markOrderPaid(event.requestId, event.txHash); + break; + + case "payment.partial": + case "payment.partial.client_id": + case "payment.partial.checkout": + await recordPartialPayment( + event.requestId, + event.amount, + event.totalAmountPaid, + ); + break; + + case "payment.failed": + await flagFailedPayment(event.requestId); + break; + + case "request.recurring": + await onRecurringInvoice(event.originalRequestId, event.requestId); + break; + + case "compliance.updated": + await syncKycStatus(event.clientUserId, event.kycStatus); + break; + + // ... others + } +} +``` + +## Headers reference + +| Header | Description | +| --- | --- | +| `x-request-network-signature` | HMAC-SHA256 of the raw JSON body, hex-encoded | +| `x-request-network-delivery` | ULID — use as idempotency key | +| `x-request-network-retry-count` | `0`–`3`, current retry attempt | +| `x-request-network-test` | `true` only for `/v1/webhook/test` deliveries | + +## Retry policy + +| Attempt | Delay | Cumulative time | +| --- | --- | --- | +| 0 (initial) | — | t=0 | +| 1 | 1s | t+1s | +| 2 | 5s | t+6s | +| 3 | 15s | t+21s | + +After 4 total attempts (initial + 3 retries) the delivery is dropped. Triggers: any non-2xx response, timeout, connection error. Default request timeout is 5s. + +## Common patterns + +### Idempotency + +The same `payment.confirmed` event might arrive twice (network blip, retry overlap). Dedupe on `x-request-network-delivery`. + +### Routing by Client ID + +If your platform has many merchants, give each their own Client ID. The webhook payload includes `clientId` so you can route events to the right tenant. + +```typescript +async function markOrderPaid(requestId: string, txHash: string) { + const order = await db.orders.findOne({ where: { requestId } }); + if (!order) return; // not ours + await db.orders.update({ where: { id: order.id }, data: { paidAt: new Date(), txHash } }); +} +``` + +### Slack alerts on failure + +```typescript +case "payment.failed": + await fetch(SLACK_WEBHOOK, { + method: "POST", + body: JSON.stringify({ + text: `:warning: Payment failed for request ${event.requestId}`, + }), + }); + break; +``` + +### Crypto-to-fiat status tracking + +The `payment.processing` event includes a `subStatus` field that progresses through `initiated → pending_internal_assessment → ongoing_checks → sending_fiat → fiat_sent`. Surface this in your UI so the payee sees real-time offramp progress. + +## Local development + +Use [ngrok](https://ngrok.com) to expose localhost during development: + +```bash +ngrok http 3000 +# Copy the https://xxxxx.ngrok-free.app URL into the Dashboard webhook field +``` + +Local URLs (`localhost`, `127.0.0.1`) are accepted by the auth API for testing. HTTPS is required in production. + +## Related + + + + Full payload schemas for every event type. + + + + High-level concepts and event categories. + + diff --git a/use-cases/welcome.mdx b/use-cases/welcome.mdx index e19fe30..1b66574 100644 --- a/use-cases/welcome.mdx +++ b/use-cases/welcome.mdx @@ -1,97 +1,101 @@ --- title: "Request Network Docs" -description: "A protocol for requests, payments, and **100% automated reconciliation**. Requests add business context to payments, eliminating manual accounting with cryptographic certainty." +description: "A protocol for requests, payments, and 100% automated reconciliation. Requests add business context to payments, eliminating manual accounting with cryptographic certainty." sidebarTitle: "Welcome" mode: "frame" --- -import { IntegratedDemo } from '/snippets/integrated-demo.jsx' -import { ComparisonTable } from '/snippets/comparison-table.jsx' -import { DemoContainer } from '/snippets/demo-container.jsx' -

Request Network Docs

- A protocol for requests, payments, and 100% automated reconciliation. + A protocol for requests, payments, and 100% automated reconciliation. Receive crypto payments — across EVM and Tron — with cryptographic certainty about who paid what.

- Identify Every Payment + Three on-ramps

- Requests add business context to payments, eliminating manual accounting with cryptographic certainty. See how unique Request IDs prevent payment collisions: + Request Network is delivered through three surfaces that share the same wallet session and Client IDs.

-
- - - +
+ + + No-code home for payment destinations, Client IDs, and incoming/outgoing payment links. EVM and Tron sign-in. + + + + Hosted payment links at pay.request.network. Cross-chain swap-to-pay via Li.Fi. Tron and EVM payer wallets. + + + + Programmatic creation of payment destinations, Client IDs, secure payments, payouts, and webhooks. + +

- Blockchain Payment Detection Comparison + Get Started

-

- How does Request Network compare to other approaches? -

- -
- - - -
-
- -
-

Get Started

-
- - End-to-end walkthrough: sign in to the Dashboard, create a payment destination, and ship a payment link. + End-to-end: sign in to the Dashboard, create a payment destination, register a webhook, ship a payment link. - - Explore specific scenarios: no-code links, programmatic payment links, multi-chain checkout, batch payouts, webhook reconciliation. + No-code links, programmatic links, multi-chain checkout, batch payouts, webhook reconciliation. - - - The easiest way to integrate. Payment types, webhooks, and developer tools. + Payment types, secure payment pages, payouts, webhooks, fees, and more.
- - Supported chains, currencies, smart contracts, and community resources + Supported chains (8 networks including Tron), currencies, smart contracts, and feature matrix. - - Legacy SDK and protocol documentation for advanced users + Legacy SDK and protocol documentation for advanced users.