Fiat Orders
Fiat orders are card payments processed by us. You create an order from your backend, we return a hosted payment page (payUrl), the customer pays by card,
and we notify your server with a webhook.
This page covers:
- List fiat orders (
GET /api/fiat/orders) — cursor pagination - Create a fiat order (
POST /api/fiat/orders) — returnspayUrl+qrUrl - Delete a fiat order by reference (
DELETE /api/fiat/orders?reference=...) — proxy-friendly safety delete - Fetch a fiat order by
orderKey(GET /api/fiat/orders/:orderKey) — includes pricing + fee snapshot - Cancel a fiat order by
orderKey(DELETE /api/fiat/orders/:orderKey) — cancels upstream PSP order - Cancel a fiat order by reference (
DELETE /api/fiat/orders/cancel?reference=...) - List refunds (
GET /api/fiat/orders/:orderKey/refunds) - Create a refund (
POST /api/fiat/orders/:orderKey/refunds) — supports partial/multiple refunds
Before you start
You need three things before your firstPOST /api/fiat/orders:
If fiat card payments are disabled for your account,POST /api/fiat/ordersreturns403 Payment method disabled. See payment method gating.
How to get your integrationId
A fiat integration is the per-site configuration an order is attached to:
which URLs the customer comes back to, and where status updates are sent. Every
fiat order must reference exactly one integration.
From the dashboard (easiest)
- Open Dashboard → Settings → Fiat.
- Fill the Create integration form:
- Name — anything you recognise, e.g.
My Shop. - Return URL (required) — where the customer lands after paying.
- Failure URL — where the customer lands after a failed/cancelled payment.
- Merchant webhook URL (required) — where we POST status updates for this integration’s orders.
- Contact URL — your contact/support page, shown to blocked customers.
- Name — anything you recognise, e.g.
- The new integration appears under Your integrations with
Integration ID: <id>. Press Copy ID.
integrationId you send when creating an order.
From the API
integrations[].id → use as integrationId. Full CRUD (create, update, delete)
is documented in Fiat integrations.
One integration per site/storefront. Running several shops on one account? Create one integration each — that keeps return URLs and webhooks from crossing over, and lets you filter orders per site.
What each integration URL does
Webhooks are how you get paid-status. The card result arrives on your
server, not in the browser redirect. Register the URL either on the
integration (merchantWebhookUrl) or in Settings → Webhooks bound to this
integration — see scoping webhooks to integrations.
Verify the signature before trusting a payload.
IDs you will see, and which to store
Store
orderKey and your own reference against your order record —
everything else can be re-fetched from them.
End-to-end flow
- Create the order —
POST /api/fiat/orderswithintegrationId,amount(oramountCents),currency, and yourreference. - Redirect the customer to
payUrl(or showqrUrlfor in-person/mobile). - Customer pays; we bounce them to
returnUrlorfailureUrl. - Webhook fires to your endpoint with the new status — this is the event you fulfil the order on.
- Optionally fetch
GET /api/fiat/orders/:orderKeyfor the pricing and fee snapshot, or issue refunds.
Base URL
All examples use:https://www.niftipay.com
Authentication
These endpoints support two authentication methods:1) API Key (recommended for integrations)
Send your API key in thex-api-key header.
2) Session cookie (browser / dashboard usage)
If you are authenticated via the dashboard.Concepts
Amounts are stored in minor units
Fiat orders store monetary values as minor units (e.g. cents):amountCents— what the customer will pay (total)subtotalCents— the base subtotal (before service fee if customer pays)serviceFeeCents— total service maintenance fee (provider + platform), if applicable
Currency minor unit rules
This implementation supports currencies with:- 0 decimals (e.g. JPY)
- 2 decimals (most currencies)
- 3 decimals (e.g. BHD)
amount as a string/number, it is converted to minor units using the currency’s decimal rules and rejects too many decimals.
Service fee payer
The fiat checkout supports a service maintenance fee with two payer modes:-
serviceFeePayer = "customer"
Customer pays: total = subtotal + fee
The fee is shown as a line item in pricing. -
serviceFeePayer = "merchant"
Customer pays: total = subtotal
The fee is deducted from merchant earnings (not shown to customer as a separate line in pricing).
serviceFeePayer is not provided on create, the system falls back to the user’s default (GET /api/fiat/settings).
References and uniqueness
On creation, the route normalizes a reference from:reference(preferred)- else
merchantReference(fallback)
- cannot conflict with a crypto order reference
- cannot conflict with an existing fiat order reference
409.
Pricing and fee snapshots
Responses include:pricing
A stable, UI-friendly breakdown:
payer(customerormerchant)subtotalCents,serviceFeeCents,totalCentsserviceFeePercent(may be null)lines(Subtotal / Service maintenance fee / Total)
fees (order details endpoint only)
A stable snapshot structure (written by registerFiatOrderFees()), including:
- service fee split (provider vs platform)
- payout fee / retention hold
- vendor net / payable now after deductions
Payment URL + QR Code
All fiat order responses include:payUrl— the masked payment link to redirect the customer toqrUrl— a QR code image URL encoding thepayUrl, ready to embed in checkout UIs
qrUrl uses the same external QR API as crypto orders:
<img> tag or embed it in mobile checkout flows.
Supported fiat currencies
The following fiat currencies are supported for fiat card orders:Currencies with 0 decimal places (e.g.JPY,KRW,VND,XAF,XOF) must be sent as whole numbers. Most currencies use 2 decimal places.
List Fiat Orders
Endpoint
GET /api/fiat/orders
Lists your fiat orders, newest first, using cursor pagination.
Query parameters
Example: list latest orders
Example response
Pagination example
IfnextCursor is not null, pass it back as cursor to fetch older results:
Create Fiat Order
Endpoint
POST /api/fiat/orders
Creates a fiat order and attempts to create the upstream NoPayn order. On success, returns:
order(DB row)pricing(decorated)display(decimal strings for UI)payUrl(where to redirect the customer)qrUrl(QR code image encoding thepayUrl)nopayninfo
Payment method gating
This endpoint enforces fiat availability for your account. If fiat card payments are disabled, it returns403.
Request body
Fields
Provide eitheramountoramountCents.
The order’swebhookUrlis always copied from the integration’smerchantWebhookUrl— it cannot be overridden per order. Change it in Settings → Fiat, or viaPATCH /api/fiat/integrations/:id.
Example: create order with amount string
Example success response (201)
payUrlis what you should open/redirect the customer to.qrUrlis a ready-to-use QR code image that encodes the same payment link.
Example: create order with amountCents
Common create errors
Invalid JSON (400)
Payment method disabled (403)
Missing required fields (400)
Invalid amount (400)
Invalid serviceFeePayer (400)
Reference conflict (409)
Integration not found (404)
Upstream PSP error (502)
If NoPayn fails, the order is markedstatus="error" locally and you get:
Delete Fiat Order (by reference)
Endpoint
DELETE /api/fiat/orders?reference=<reference>
This is a proxy-friendly delete that mirrors your crypto delete behavior.
Matching rules:
- first tries strict match:
fiatOrder.merchantReference === reference(newest first) - fallback: if reference is numeric, match by
fiatOrder.orderKey === Number(reference)
Safety rules
Deletion is conservative:- You cannot delete
completed,paid, orrefundedorders - If
pspOrderIdexists, the order must already becancelledbefore deletion
Example request
Success response
204 No Content
Error responses
Missing reference (400)Get Fiat Order (by orderKey)
Endpoint
GET /api/fiat/orders/:orderKey
Returns:
order(DB row)pricing(decorated)fees(snapshot breakdown)payUrl(masked payment link)qrUrl(QR code image for the payment link)
Example request
Example response
Cancel Fiat Order (by orderKey)
Endpoint
DELETE /api/fiat/orders/:orderKey
This attempts to cancel the upstream PSP order (NoPayn) and then updates the local DB.
Behavior
- If already cancelled, returns current state (idempotent).
- If status is
completed, cancellation is blocked (safe default). - Requires
pspOrderIdto exist. - Only
psp="nopayn"is supported here.
Example request
Example response
Cancel error examples
Invalid orderKey (400)Cancel Fiat Order (by reference)
Endpoint
DELETE /api/fiat/orders/cancel?reference=<reference>
Cancel using the same matching rules as delete-by-reference:
- strict merchantReference match
- numeric fallback to orderKey
Example request
Responses
- Returns
order+pricing+nopaynon success - Same error patterns as cancel by orderKey
Refunds (Fiat)
Refunds are executed at the PSP level (NoPayn) and support partial / multiple refunds. Refund creation is conservative:- Only for
kind="order"(not payment links) - Only allowed when order is
paidorcompleted - Prevents over-refund by reading current refunds from NoPayn first
- If
serviceFeePayer="customer": refundable ceiling = total paid (amountCents) - If
serviceFeePayer="merchant": refundable ceiling = subtotal only (subtotalCents)
(Because the customer never paid the service fee)
List refunds
Endpoint
GET /api/fiat/orders/:orderKey/refunds
Returns:
refunds(from NoPayn)- computed meta:
maxRefundableCents,refundedCents,remainingRefundableCents
Example request
Example response
Common list-refunds errors
Invalid orderKey (400)Create refund
Endpoint
POST /api/fiat/orders/:orderKey/refunds
Request body
Fields
orderLinesvalidation:
orderLinesmust be non-empty if provided as an array- each line requires a non-empty
merchantOrderLineIdquantitymust be a positive integer
Example request
Example response (201)
fiatOrder.status is updated to "refunded".
Common refund errors
Invalid JSON (400)Node.js Example
A complete Node.js example showing how to create a fiat payment order with pricing breakdown and response parsing.See the full working script with setup and helper function: API Examples (Node.js)
Status notes
Fiat order status values come from the PSP status field in practice (e.g. NoPayn’sstatus), plus local states:
new— created locally (and usually also created upstream)cancelled— cancelled locally (and upstream when possible)completed/paid— successful paymentrefunded— fully refunded (local state set when refundable remaining reaches 0)error— upstream create failed