> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stablepayfi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# QR Pay

> Create a dedicated hosted payment page for a specific customer, with QR codes, on-chain transfers, and wallet payments.

Create a dedicated payment page for a specific customer and let them pay in stablecoins by scanning a QR code.

QR Pay is a hosted payment page that StablePay issues for a specific customer. A merchant creates a QR Pay session through the Open API or in the Merchant Portal, receives the hosted page URL, and turns that URL into a QR code for a counter, a printed poster, a chat message, or an email. The customer scans the code, enters an email address and an amount, picks a stablecoin and a network, and pays either by on-chain transfer or with a wallet. Address allocation, the payment QR code, on-chain monitoring, payment confirmation, and result notifications are all handled by StablePay.

Unlike Payment Links, every QR Pay session is bound to one customer, so the same customer can reuse the same payment entry point and the merchant can report cumulative payment volume and successful payment count per customer. The product was previously called Deposit Pay; its internal resources still use the `customer_deposit` and `deposit_session_id` naming, which refers to the same object described here.

## Use Cases

Use QR Pay when the payer is known and the same customer is expected to pay more than once:

* A fixed payment QR code at a checkout counter, service desk, or store poster.
* A dedicated payment link or QR code sent to a specific customer through support chat, a community group, or email.
* Balance top-ups for trading platforms, wallets, and account-based products.
* A payment entry point that must stay valid for long-term customers without expiring.
* Customers spread across networks, where the hosted page should guide network and token selection.

If you want a reusable payment page that can be shared publicly with anyone, use Payment Links. If you need a formal bill with a due date, use Invoicing. If your backend should create a one-time checkout session, use Checkout. QR Pay does not provide a refund endpoint; to return funds to a customer, use the Payouts Open API.

## Workflow

1. The merchant creates a QR Pay session in the Merchant Portal, or calls the create endpoint with a 10-digit `external_customer_id` and `allow_multiple_deposits`, optionally passing customer details, an expiry time, a description, and redirect URLs.
2. StablePay looks up the customer by the current merchant and `external_customer_id`, creating the customer if it does not exist and reusing it if it does.
3. The response returns only `deposit_session_id` and the hosted page url. No on-chain address is returned.
4. The merchant renders the url as a QR code, or shares it with the customer directly.
5. The customer opens the page, enters an email address and an amount, and selects a payment method.
6. For on-chain transfer, the page shows the address, Memo/Tag, and QR code for the selected token and network, and the customer sends the transfer. For wallet payment, the entered amount becomes the fixed payable amount and the standard wallet payment flow takes over.
7. Once the payment is confirmed, StablePay creates the payment record, shows the success state on the page, and delivers the `payment.completed` webhook.
8. Successful payments follow the standard settlement pipeline and settle in the same token on a T+1 calendar-day basis.

## Core Capabilities

| Capability | Description |
| :- | :- |
| Customer binding | Each QR Pay session is bound to one customer, created or reused by `external_customer_id`. |
| Hosted payment page | StablePay provides token and network selection, the address, the QR code, and the success page. No merchant-built UI required. |
| Repeat payments | `allow_multiple_deposits` decides whether the session is single-use or reusable. |
| Optional expiry | Leave `expires_at` empty for a session that never expires. There is no default and no maximum lifetime. |
| Address isolation | Every QR Pay session gets its own address, so two sessions for the same customer never share one. |
| QR payments | The on-page QR code meets the error-correction level needed by common mobile wallets. |
| Two payment methods | On-chain transfer and wallet payment are both supported. |
| Payment notifications | Reuses the existing payment.\* webhooks and Payments API queries. No new event types. |
| Portal management | List and detail views, cumulative volume, successful payment count, related payment records, and export. |

## Customer Model

A customer is a merchant-scoped object shared with subscriptions. There is no separate deposit-customer subtype. An `external_customer_id` is unique within a merchant and cannot be changed after creation; the same ID under two different merchants refers to two completely independent customers, with no visibility into each other's profile or on-chain addresses.

Name, email, and phone passed when creating a QR Pay session are only used to create a new customer. An existing customer is never overwritten. One customer can own many QR Pay sessions, each with its own address, cumulative volume, and successful payment count. Merchants do not need a separate customer endpoint: the customer page in the Portal can create customers directly, and the Open API creates them on demand while creating a QR Pay session. The email field on the hosted page is prefilled with the customer email and can be edited; the submitted value is a snapshot for that payment only and is never written back to the customer.

## Addresses and QR Codes

Addresses are allocated deterministically by `merchant_id + deposit_session_id + address_family`, so two QR Pay sessions never share an address even when they belong to the same customer. Within one session, EVM networks such as Ethereum, Base, and Arbitrum reuse a single EVM address, while non-EVM families such as TRON and Solana each get their own address. An address is allocated lazily the first time its family is requested and stays the same afterwards.

Networks that require a Memo/Tag must display the address and the Memo/Tag together, since both are needed as the complete deposit credential. The page also tells the customer to send only the selected token on the selected network. On-chain addresses are shown only to the end customer on the hosted page after token and network selection. Neither the create nor the retrieve endpoint returns an address, and the Portal list, session detail, and customer detail pages never display addresses, address families, supported networks, or Memo/Tag values.

## Expiry and Repeat Payments

`allow_multiple_deposits` is required on creation and has no default. When set to false, the session is single-use: after the first successful payment the status becomes `closed`, the page stops showing the address, and there is no way to pay again. When set to true, the customer can pay repeatedly while the session is valid.

`expires_at` takes a Unix timestamp and must be later than the current time. Omitting it means the session never expires, and the response returns `null`. There is no default expiry and no maximum lifetime. The public status has three values: `active` (payable), `closed` (`closed` after a single-use payment completed), and `expired`.

Note that losing validity does not invalidate the on-chain address. If a customer saved the address in their wallet and sends funds after the session was `closed` or `expired`, StablePay still recognises the transfer, books it, and creates a payment record marked `outside_session`. Such payments follow the same fee, settlement, and success webhook rules as normal payments.

## Amounts, Tokens, and Fees

QR Pay supports only the USD stablecoins configured by StablePay. There is no fiat funding, fiat billing, or token conversion. All amounts travel as decimal fixed-point strings, never as floating point numbers.

On-chain transfer does not compare the amount entered by the customer with the amount that arrives on chain, so there are no underpayment, overpayment, or partial-payment states: any positive amount that qualifies for booking and reaches the required confirmations is a successful payment. Wallet payment treats the entered amount as a fixed payable amount and reuses the standard payment validation and status flow. In both cases the final record is based on the amount actually `paid`.

USDT is charged in USDT and USDC in USDC, with no conversion between them. The fee is calculated on the successfully `paid` amount, `fee_amount = actual_amount × fee_rate`, with a default rate of 1% that can be configured per merchant. The rate and fee are snapshotted when the payment succeeds, so later rate changes never apply retroactively. The settled amount is `net_amount = actual_amount − fee_amount`. Fee rate, fee amount, and net amount are internal settlement data and are never returned through the Payments API, webhooks, or the Merchant Portal.

## Payment Results and Notifications

For on-chain transfers, the Trade Order is created only after the transfer reaches the required confirmations and passes risk checks, so the external status is `paid` from the start with no intermediate `processing` state. For wallet payments, a `pending` order is created when the customer selects that method and then follows the standard `pending`, `processing`, `paid`, `failed`, `expired`, and `canceled` flow.

Successful payments are reported through the existing `payment.completed` event, with `data.object.payment_type` set to `customer_deposit` and with `customer_id` and `deposit_session_id` included. No webhook is sent while a transfer is detected or confirming, none is sent when a session is `closed` or `expired`, and settlement completion does not add a new event type. Signature verification, Event ID, retry behaviour, and idempotency follow the existing webhook contract, so merchants must handle duplicate deliveries.

## Settlement

Successful payments follow the standard settlement pipeline and settle on T+1 calendar days, with weekends and holidays processed normally: payments that succeed on day T enter a settlement batch on day T+1. The settled amount is net\_amount in the same token, with no fiat conversion.

## Merchant Portal

The Portal lists QR Pay sessions as the primary record instead of flattening individual payments into the list. Filters cover customer ID, QR Pay ID, status, and a creation-time range. Each row shows the customer, cumulative payment volume, successful payment count, status, and creation time, with actions to open the detail view and copy the payment link. Cumulative volume is the sum of all payments with `payment_status=paid` under that session; USDT and USDC are displayed together as USD-equivalent value without any conversion between tokens.

Creating a session requires selecting an existing customer from search results; free-text customer entry is not allowed. The form sets expiry, whether repeat payments are allowed, a description, and `metadata`. The Portal does not expose token or network configuration, because the hosted page always offers every supported stablecoin and network enabled for the merchant. After creation the Portal only shows the copyable payment link and never returns or displays an on-chain address.

The detail page shows configuration, cumulative volume, and successful payment count first, then the payment records linked to that session, without fee rate, fee amount, or net amount. The customer detail page has a dedicated QR Pay section listing all sessions for that customer, again without on-chain addresses.

## API Overview

| Purpose | Endpoint |
| :- | :- |
| [Create QR Pay](https://docs.stablepayfi.ai/api-reference/二维码支付/创建二维码支付.md) | `POST /api/v1/customer-deposits` |
| [Retrieve QR Pay](https://docs.stablepayfi.ai/api-reference/二维码支付/查询二维码支付.md) | `GET /api/v1/customer-deposits/{deposit_session_id}` |

Requests use a Bearer API key plus the `X-StablePay-Timestamp`, `X-StablePay-Nonce`, and `X-StablePay-Signature` headers, and creation requests must also carry an `Idempotency-Key`. The signature payload is `{timestamp}.{nonce}.{requestBody}`, signed with HMAC-SHA256 and encoded as lowercase hex; for GET requests requestBody is an empty string. See Getting Started for the full authentication and signing guide. The API reference pages linked below are currently published in Simplified Chinese.

### Create QR Pay

```bash theme={"system"}
curl --request POST 'https://api.stablepay.co/api/v1/customer-deposits' \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: 8f4d0c1e-3b7a-4d2f-9c55-2f0a6d9b1e73' \
  --header 'X-StablePay-Timestamp: 1791878400' \
  --header 'X-StablePay-Nonce: 550e8400-e29b-41d4-a716-446655440000' \
  --header 'X-StablePay-Signature: <SIGNATURE>' \
  --data '{
    "external_customer_id": "1234567890",
    "customer_email": "customer@example.com",
    "allow_multiple_deposits": true,
    "description": "Counter QR code",
    "success_url": "https://merchant.example/qr-pay/success",
    "cancel_url": "https://merchant.example/qr-pay/cancel",
    "metadata": {"store": "SH-001"}
  }'
```

Example response (201 on first creation, 200 on idempotent replay; values are placeholders)

```json theme={"system"}
{
  "deposit_session_id": "20261013110310801082920000001001",
  "customer_id": "10000001",
  "status": "active",
  "allow_multiple_deposits": true,
  "expires_at": null,
  "url": "<hosted checkout URL>",
  "metadata": {"store": "SH-001"},
  "created": 1791878400
}
```

Creation returns 201; a replay with the same `Idempotency-Key` returns 200 with the same session. A 400 is returned when `external_customer_id` is not 10 digits, `allow_multiple_deposits` is missing, or a redirect URL is not an absolute HTTPS URL. An idempotency-key conflict returns 409. A session that does not exist or belongs to another merchant returns 404 `resource_not_found`.

## QR Pay vs. Other Payment Options

| Aspect | QR Pay | Payment Links | Invoicing |
| :- | :- | :- | :- |
| Payer | A bound, specific customer | Public, anyone | A specific billing customer |
| Reuse | Repeat payments by the same customer | Shareable with many payers | One-time bill with a due date |
| Amount source | Entered by the customer, or fixed for wallet payment | Product pricing | Bill amount |
| Creation | Open API or Portal | Portal | Portal or API |

## Important Notes

* QR Pay does not provide a refund endpoint. To return funds to a customer, use the Payouts Open API and record the original payment ID in `metadata` for auditing. This does not create an automatic refund relationship.
* Customer withdrawals, fiat funding, fiat billing, and token conversion are not supported.
* Closing or expiring a session does not change how its address is booked. Transfers that arrive outside the session window are marked `outside_session` and keep the same fee and settlement rules.
* On-chain addresses are shown only to the end customer on the hosted page. No API response or Portal page returns an address.
* Available tokens, networks, and minimum amounts depend on the token-network configuration enabled for the merchant. See Supported Wallets and Supported Networks.
* Customer-facing pages support merchant logo and brand colour. See [Branding Settings](https://docs.stablepayfi.ai/en/product/Branding-Settings.md).

## Related Pages

* [Create QR Pay](https://docs.stablepayfi.ai/api-reference/二维码支付/创建二维码支付.md)
* [Retrieve QR Pay](https://docs.stablepayfi.ai/api-reference/二维码支付/查询二维码支付.md)
* [Getting Started](https://docs.stablepayfi.ai/en/api/getting-started.md)
* [Webhook Notifications](https://docs.stablepayfi.ai/en/api/webhook-notifications.md)
* [Payment Links](https://docs.stablepayfi.ai/en/product/Payment-Links.md)
* [Checkout](https://docs.stablepayfi.ai/en/product/Stablecoin-Checkout.md)
* [Invoicing](https://docs.stablepayfi.ai/en/product/Invoicing.md)
* [Supported Wallets](https://docs.stablepayfi.ai/en/product/Supported-Wallets.md)
* [Supported Networks](https://docs.stablepayfi.ai/en/product/Supported-Networks.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.