Reusable payment links
A reusable payment link is a public Rupay URL with pricing configured ahead of time. Share the same URL with many customers; Rupay creates a separate Checkout Session only after each customer enters their contact information and continues.
This is a different product from the Hosted Checkout API:
| Capability | Reusable payment link | Hosted Checkout Session |
|---|---|---|
| Public URL | Reused for many customers | Unique to one payment attempt |
| Amount and description | Fixed when the link is created | Supplied by the merchant backend per request |
| Creation | Rupay admin interface | Authenticated server API |
| API key needed by the customer-facing app | No | Yes, on its backend only |
| Best fit | Fixed-price service, membership, or general contribution | Dynamic campaign contribution, creator tip, order, or invoice |
Create a link
Rupay currently creates reusable links through /admin/payment-links; there is
no public merchant API for creating them.
The admin creation merchant is selected with RUPAY_ADMIN_MERCHANT_ID.
Production requires this value and fails closed if it is missing or does not
identify an existing merchant; it never silently creates a sandbox merchant.
The configured merchant may be in test or live mode, and the admin page
shows the selected name and mode before creation. Outside production, leaving
the value unset uses the local development sandbox.
Provision a live merchant before creating production links:
bun run merchant:manage provision \
--name "Fundyatra" \
--mode live \
--redirect-origin https://web-production-be68e.up.railway.app
Store the returned API key securely and configure the returned merchant ID as
RUPAY_ADMIN_MERCHANT_ID in Rupay's deployment.
Choose one source:
- Existing product: select an active catalog product. Rupay snapshots its current title, description, currency, and amount into the link.
- Custom invoice line: enter a free-text title, optional detail, amount, and either NPR or USD without creating a catalog product.
After creation, copy the public URL shown by the admin:
https://your-rupay-host.example/pay/plink_example
The plink_... URL is the reusable URL. A cs_... Checkout Session URL is for
one customer attempt and must not be shared as the reusable link.
Customer flow
- The customer opens
/pay/{PAYMENT_LINK_ID}. - Rupay displays the fixed description, amount, and currency.
- The customer supplies the required email and phone number and selects an available payment method.
- Rupay creates a new internal Checkout Session for that customer and begins the selected provider flow.
- A provider-confirmed payment completes that individual session.
Query parameters cannot override the configured amount, currency, description, or metadata.
Webhooks and reconciliation
Reusable-link payments use the same signed merchant webhook contract as Hosted
Checkout. Subscribe to checkout.session.completed and inspect these fields:
{
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_example",
"payment_status": "paid",
"amount_total": 250000,
"currency": "npr",
"metadata": {
"checkout_surface": "payment_link",
"payment_link_id": "plink_example",
"payment_link_source": "custom"
}
}
}
}
Verify the signature, deduplicate event.id, and confirm the immutable amount
and currency before fulfillment. See Webhooks.
Which should Fundyatra use?
Fundyatra campaign support and creator tips normally have a dynamic amount and must be linked to a specific Fundyatra payment record. Use the Hosted Checkout Session API and the Fundyatra guide for those flows.
A reusable payment link is suitable only when Fundyatra deliberately wants one fixed description and amount shared repeatedly, with no per-request campaign or supporter context supplied by its backend.
Current limitations
- Link creation is admin-only; there is no public create/list/update API.
- One configured merchant is available to admin creation at a time.
- Pricing is a creation-time snapshot and cannot be changed by the payer.
- The link collects email and phone before creating the customer's session.
- Provider availability still depends on Rupay's configured currency and live provider credentials.