Hosted Payment Page
Let customers choose the network and token on a PayzCore-hosted page. Create a payment without a network, share payment_url, and receive webhooks as usual.
Hosted Payment Page
Every payment created through the API has a hosted page at https://pay.payzcore.com/{payment_id}. The API returns this link as payment_url. You can send customers there instead of building your own payment screen.
The hosted page shows the amount, the deposit address with a QR code, a countdown until expiry and the live status. The funds still go straight to your wallet address. PayzCore only watches the blockchain and notifies you.
Two Ways to Use It
| Flow | You send network? | What the customer sees |
|---|---|---|
| Fixed network | Yes (e.g. TRC20) | Address and QR code right away |
| Customer selects | No | A network/token picker first, then the address and QR code |
Customer-selects flow
- Create a payment without
network(and withouttoken):
curl -X POST https://api.payzcore.com/v1/payments \
-H "x-api-key: pk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"amount": 50, "external_ref": "customer-123", "external_order_id": "ORD-456"}'- The response has no address yet.
address,network,tokenandqr_codearenull, andavailable_networkslists what your wallet supports:
{
"success": true,
"payment": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"address": null,
"amount": "50.00",
"network": null,
"token": null,
"status": "pending",
"expires_at": "2026-10-15T13:00:00.000Z",
"external_order_id": "ORD-456",
"qr_code": null,
"payment_url": "https://pay.payzcore.com/550e8400-e29b-41d4-a716-446655440000",
"available_networks": [
{ "network": "TRC20", "name": "Tron", "tokens": ["USDT"], "confirmations": 19, "explorer": "https://tronscan.org" },
{ "network": "BEP20", "name": "BSC", "tokens": ["USDT", "USDC"], "confirmations": 15, "explorer": "https://bscscan.com" }
]
}
}- Redirect the customer to
payment_url. They pick a network and token, and the page assigns a deposit address from your wallet (using your project's address mode). - From then on the payment behaves like any other:
GET /v1/payments/:idreturns the address and network, and you receive the usual webhooks.
While no network is selected, GET /v1/payments/:id returns "awaiting_network": true.
If you do not pass external_order_id, PayzCore generates one (PAY-…) for network-less payments.
What the Customer Sees
- Store name and logo from your Project Settings (see Store Branding)
- Verified badge if PayzCore has verified your project. Unverified projects show an "Unverified merchant" label instead of the store name, to protect buyers from impersonation. Contact support to get your project verified.
- Amount, token and network, deposit address, QR code (black-on-white) and a countdown
- Live status. The page checks the status every 10 seconds and shows the final state (paid, partial, overpaid, expired, cancelled).
- In pool + txid mode, a field to paste the transaction hash after sending (see Address Modes)
The page never asks for wallet keys or card data. It only displays where to send funds.
Network Selection Endpoint
The hosted page calls this endpoint when the customer picks a network. It is served on pay.payzcore.com only (not on api.payzcore.com) and needs no API key:
POST https://pay.payzcore.com/api/pay/{payment_id}/select-network
Content-Type: application/json
{ "network": "TRC20", "token": "USDT" }| Status | Response |
|---|---|
200 | { "success": true, "address", "network", "token", "amount", "qr_code" } |
400 | network and token are required, USDC is not supported on TRC20, X on Y is not available for this merchant, No addresses available for this network, This merchant is currently not accepting payments |
404 | Payment not found |
409 | Payment is not in pending state or Network already selected for this payment |
410 | Payment has expired |
429 | Too many requests (5 per minute per IP, 3 per minute per payment) or Payment capacity exceeded. Please try again later. |
A network can be selected once. You normally do not call this endpoint yourself; build your own picker with POST /v1/payments + network instead.
Expiry
The payment window is expires_in seconds (300–86,400) from creation. If you omit it, your project's payment expiry setting applies (60 minutes unless changed). Picking a network does not extend the window.
No payment.expired webhook without a network. A payment that expires before the customer selects a network has no address. It moves to expired (visible in GET /v1/payments/:id and the list endpoint), but no payment.expired webhook is sent for it. If your order flow depends on expiry (for example to release stock), also poll GET /v1/payments?status=expired&updated_after=… or check the payment when your own timer runs out.
The hosted page and GET /v1/payments/:id show a pending payment as expired only 3 minutes after expires_at, so a last-second transfer still has a chance to be detected.
Payment Links vs. Hosted Payment Page
| Hosted Payment Page | Payment Links | |
|---|---|---|
| Created by | Your server, via POST /v1/payments | You, in the dashboard (no code) |
| URL | pay.payzcore.com/{payment_id} (one per payment) | pay.payzcore.com/l/{slug} (reusable) |
| Amount | Set by your server | Fixed or entered by the buyer |
| Buyer info | No | Optional email, name, note |
Both flows send the same webhooks. Payment-link payments also include buyer_email, buyer_name, buyer_note, payment_link_id and payment_link_slug.
See Also
- Getting Started - Create your first payment
- Address Modes - How the deposit address is chosen
- Payment Links - Reusable no-code links
- Webhooks - Events and payload