Skip to main content

Fiat Orders

Fiat orders are card payments processed by us. This page covers:
  • List fiat orders (GET /api/fiat/orders) — cursor pagination
  • Create a fiat order (POST /api/fiat/orders) — returns payUrl + 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

Base URL

All examples use:
  • https://www.niftipay.com

Authentication

These endpoints support two authentication methods: Send your API key in the x-api-key header.
curl -X GET "https://www.niftipay.com/api/fiat/orders" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"
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)
When you send 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).
If 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)
Then it checks cross-type uniqueness for your account:
  • cannot conflict with a crypto order reference
  • cannot conflict with an existing fiat order reference
If there is a conflict, the API returns 409.

Pricing and fee snapshots

Responses include:

pricing

A stable, UI-friendly breakdown:
  • payer (customer or merchant)
  • subtotalCents, serviceFeeCents, totalCents
  • serviceFeePercent (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
Older orders may have some snapshot fields null; the API still returns a stable object with safe defaults.

Payment URL + QR Code

All fiat order responses include:
  • payUrl — the masked payment link to redirect the customer to
  • qrUrl — a QR code image URL encoding the payUrl, ready to embed in checkout UIs
The qrUrl uses the same external QR API as crypto orders:
https://api.qrserver.com/v1/create-qr-code?size=300x300&data=<encoded payUrl>
You can display the QR image directly in an <img> tag or embed it in mobile checkout flows.

Supported fiat currencies

The following fiat currencies are supported for fiat card orders:
CodeCurrencyCodeCurrency
AEDUAE DirhamISKIcelandic Krona
AMDArmenian DramJPYJapanese Yen
AUDAustralian DollarKESKenyan Shilling
BAMBosnia-Herzegovina MarkKRWSouth Korean Won
BGNBulgarian LevMDLMoldovan Leu
BRLBrazilian RealMKDMacedonian Denar
BYNBelarusian RubleMWKMalawian Kwacha
CADCanadian DollarMXNMexican Peso
CHFSwiss FrancMYRMalaysian Ringgit
CLPChilean PesoMZNMozambican Metical
CNYChinese YuanNGNNigerian Naira
COPColombian PesoNOKNorwegian Krone
CRCCosta Rican ColonPABPanamanian Balboa
CZKCzech KorunaPENPeruvian Sol
DKKDanish KronePHPPhilippine Peso
DOPDominican PesoPLNPolish Zloty
EUREuroPYGParaguayan Guarani
GBPBritish PoundRONRomanian Leu
GELGeorgian LariRSDSerbian Dinar
GHSGhanaian CediRUBRussian Ruble
GIPGibraltar PoundSEKSwedish Krona
GTQGuatemalan QuetzalSGDSingapore Dollar
HUFHungarian ForintTHBThai Baht
IDRIndonesian RupiahTRYTurkish Lira
TWDNew Taiwan Dollar
TZSTanzanian Shilling
UAHUkrainian Hryvnia
UGXUgandan Shilling
USDUS Dollar
UYUUruguayan Peso
VNDVietnamese Dong
XAFCentral African CFA Franc
XOFWest African CFA Franc
ZARSouth African Rand
ZMWZambian Kwacha
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

NameTypeDefaultNotes
statusstringOptional filter.
limitnumber20Min 1, max 100.
cursornumberUse nextCursor from the previous response to fetch older records.

Example: list latest orders

curl -X GET "https://www.niftipay.com/api/fiat/orders?limit=20" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

Example response

{
  "orders": [
    {
      "id": "6a4a2be6-6c6c-4a4a-bf9f-4f5b3e6a90aa",
      "orderKey": 2221,
      "userId": "user-id",
      "integrationId": "integration-id",
      "kind": "order",
      "amountCents": 7004,
      "subtotalCents": 6800,
      "serviceFeePayer": "customer",
      "serviceFeePercent": 3.0,
      "serviceFeeCents": 204,
      "currency": "GBP",
      "status": "new",
      "psp": "nopayn",
      "pspOrderId": "np_123",
      "pspProjectId": "proj_abc",
      "pspStatus": "new",
      "pspPaymentLinkId": null,
      "pspPaymentUrl": null,
      "orderUrl": "https://pay.nopayn.example/order/np_123",
      "returnUrl": "https://merchant.example/return",
      "failureUrl": "https://merchant.example/failure",
      "webhookUrl": "https://merchant.example/webhook",
      "merchantReference": "POS-1234",
      "email": null,
      "createdAt": "2026-02-11T10:00:00.000Z",
      "updatedAt": "2026-02-11T10:00:05.000Z",
      "completedAt": null,
      "pricing": {
        "payer": "customer",
        "subtotalCents": 6800,
        "serviceFeeCents": 204,
        "totalCents": 7004,
        "serviceFeePercent": 3.0,
        "lines": [
          { "type": "subtotal", "amountCents": 6800, "label": "Subtotal" },
          { "type": "service_maintenance_fee", "amountCents": 204, "label": "Service maintenance fee" },
          { "type": "total", "amountCents": 7004, "label": "Total" }
        ]
      },
      "qrUrl": "https://api.qrserver.com/v1/create-qr-code?size=300x300&data=https%3A%2F%2Fwww.niftipay.com%2Fpaylink%2F6a4a2be6-6c6c-4a4a-bf9f-4f5b3e6a90aa"
    }
  ],
  "nextCursor": 2221
}

Pagination example

If nextCursor is not null, pass it back as cursor to fetch older results:
curl -X GET "https://www.niftipay.com/api/fiat/orders?limit=20&cursor=2221" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

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 the payUrl)
  • nopayn info

Payment method gating

This endpoint enforces fiat availability for your account. If fiat card payments are disabled, it returns 403.

Request body

{
  "integrationId": "YOUR_FIAT_INTEGRATION_ID",
  "amount": "19.95",
  "currency": "EUR",
  "description": "Order #1001",
  "reference": "POS-1001",
  "serviceFeePayer": "customer",
  "email": "customer@example.com"
}

Fields

FieldTypeRequiredNotes
integrationIdstringMust exist and belong to you.
currencystringISO currency code. See supported currencies below.
amountstring/number⚠️Required unless amountCents is provided.
amountCentsnumber⚠️Alternative to amount. Must be a positive integer.
descriptionstringPassed to PSP for display.
referencestringPreferred merchant reference (normalized).
merchantReferencestringLegacy alias; used if reference is missing.
serviceFeePayerstring"customer" or "merchant". Defaults to your fiat settings.
emailstringOptional customer email. Stored with the order and returned in responses.
Provide either amount or amountCents.

Example: create order with amount string

curl -X POST "https://www.niftipay.com/api/fiat/orders" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "integrationId": "integration-id",
    "amount": "68.00",
    "currency": "GBP",
    "description": "POS checkout",
    "reference": "POS-1234",
    "serviceFeePayer": "customer",
    "email": "customer@example.com"
  }'

Example success response (201)

{
  "order": {
    "id": "6a4a2be6-6c6c-4a4a-bf9f-4f5b3e6a90aa",
    "orderKey": 2221,
    "userId": "user-id",
    "integrationId": "integration-id",
    "kind": "order",
    "amountCents": 7004,
    "subtotalCents": 6800,
    "serviceFeePayer": "customer",
    "serviceFeePercent": 3.0,
    "serviceFeeCents": 204,
    "currency": "GBP",
    "status": "new",
    "psp": "nopayn",
    "pspProjectId": "proj_abc",
    "pspOrderId": "np_123",
    "pspStatus": "new",
    "orderUrl": "https://pay.nopayn.example/order/np_123",
    "returnUrl": "https://merchant.example/return",
    "failureUrl": "https://merchant.example/failure",
    "webhookUrl": "https://merchant.example/webhook",
    "merchantReference": "POS-1234",
    "email": "customer@example.com",
    "createdAt": "2026-02-11T10:00:00.000Z",
    "updatedAt": "2026-02-11T10:00:05.000Z",
    "completedAt": null
  },
  "pricing": {
    "payer": "customer",
    "subtotalCents": 6800,
    "serviceFeeCents": 204,
    "totalCents": 7004,
    "serviceFeePercent": 3.0,
    "lines": [
      { "type": "subtotal", "amountCents": 6800, "label": "Subtotal" },
      { "type": "service_maintenance_fee", "amountCents": 204, "label": "Service maintenance fee" },
      { "type": "total", "amountCents": 7004, "label": "Total" }
    ],
    "serviceFeeBreakdown": {
      "providerPercent": 2.0,
      "platformPercent": 1.0,
      "totalPercent": 3.0,
      "providerCents": 136,
      "platformCents": 68,
      "totalCents": 204
    }
  },
  "display": {
    "subtotal": "68.00",
    "serviceFee": "2.04",
    "total": "70.04",
    "currency": "GBP"
  },
  "reference": "POS-1234",
  "nopayn": {
    "id": "np_123",
    "status": "new",
    "order_url": "https://pay.nopayn.example/order/np_123"
  },
  "payUrl": "https://pay.nopayn.example/order/np_123",
  "qrUrl": "https://api.qrserver.com/v1/create-qr-code?size=300x300&data=https%3A%2F%2Fwww.niftipay.com%2Fpaylink%2F6a4a2be6-6c6c-4a4a-bf9f-4f5b3e6a90aa"
}
payUrl is what you should open/redirect the customer to. qrUrl is a ready-to-use QR code image that encodes the same payment link.

Example: create order with amountCents

curl -X POST "https://www.niftipay.com/api/fiat/orders" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "integrationId": "integration-id",
    "amountCents": 1995,
    "currency": "EUR",
    "description": "Order #1001",
    "reference": "POS-1001"
  }'

Common create errors

Invalid JSON (400)

{ "error": "Invalid JSON" }

Payment method disabled (403)

{ "error": "Payment method disabled" }

Missing required fields (400)

{ "error": "integrationId is required" }
{ "error": "currency is required" }

Invalid amount (400)

{ "error": "amount is required (e.g. 19.95)" }
{ "error": "amountCents must be a positive integer" }
{ "error": "amount has too many decimals for JPY" }

Invalid serviceFeePayer (400)

{ "error": "serviceFeePayer must be \"customer\" or \"merchant\"" }

Reference conflict (409)

{ "error": "Reference used by crypto order" }
or
{ "error": "Reference already exists" }

Integration not found (404)

{ "error": "Integration not found" }

Upstream PSP error (502)

If NoPayn fails, the order is marked status="error" locally and you get:
{ "error": "Upstream error", "details": null }

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:
  1. first tries strict match: fiatOrder.merchantReference === reference (newest first)
  2. fallback: if reference is numeric, match by fiatOrder.orderKey === Number(reference)

Safety rules

Deletion is conservative:
  • You cannot delete completed, paid, or refunded orders
  • If pspOrderId exists, the order must already be cancelled before deletion

Example request

curl -X DELETE "https://www.niftipay.com/api/fiat/orders?reference=POS-1234" \
  -H "x-api-key: YOUR_API_KEY"

Success response

  • 204 No Content

Error responses

Missing reference (400)
{ "error": "reference query-param required" }
Not found (404)
{ "error": "Order not found" }
Cannot delete completed/paid/refunded (409)
{ "error": "Cannot delete – order status is \"completed\"" }
Must cancel first (409)
{
  "error": "Cannot delete – order must be cancelled first (call DELETE /api/fiat/orders/cancel?reference=...)"
}

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

curl -X GET "https://www.niftipay.com/api/fiat/orders/2221" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

Example response

{
  "order": {
    "id": "6a4a2be6-6c6c-4a4a-bf9f-4f5b3e6a90aa",
    "orderKey": 2221,
    "userId": "user-id",
    "integrationId": "integration-id",
    "amountCents": 7004,
    "subtotalCents": 6800,
    "serviceFeePayer": "customer",
    "serviceFeePercent": 3.0,
    "serviceFeeCents": 204,
    "currency": "GBP",
    "status": "new",
    "psp": "nopayn",
    "pspOrderId": "np_123",
    "pspProjectId": "proj_abc",
    "pspStatus": "new",
    "orderUrl": "https://pay.nopayn.example/order/np_123",
    "returnUrl": "https://merchant.example/return",
    "failureUrl": "https://merchant.example/failure",
    "webhookUrl": "https://merchant.example/webhook",
    "merchantReference": "POS-1234",
    "email": "customer@example.com",
    "createdAt": "2026-02-11T10:00:00.000Z",
    "updatedAt": "2026-02-11T10:00:05.000Z",
    "completedAt": null,

    "serviceFeeProviderPercent": 2.0,
    "serviceFeePlatformPercent": 1.0,
    "serviceFeeProviderCents": 136,
    "serviceFeePlatformCents": 68,
    "payoutFeePercent": 0,
    "payoutFeeCents": 0,
    "retentionPercent": 10,
    "retentionHoldCents": 680,
    "payableNowCents": 6120,
    "feeSnapshotAt": "2026-02-11T10:00:05.000Z",
    "feeSnapshotVersion": 1
  },
  "pricing": {
    "payer": "customer",
    "subtotalCents": 6800,
    "serviceFeeCents": 204,
    "totalCents": 7004,
    "serviceFeePercent": 3.0,
    "lines": [
      { "type": "subtotal", "amountCents": 6800, "label": "Subtotal" },
      { "type": "service_maintenance_fee", "amountCents": 204, "label": "Service maintenance fee" },
      { "type": "total", "amountCents": 7004, "label": "Total" }
    ]
  },
  "fees": {
    "snapshotAt": "2026-02-11T10:00:05.000Z",
    "snapshotVersion": 1,
    "serviceFee": {
      "payer": "customer",
      "totalCents": 204,
      "providerCents": 136,
      "platformCents": 68,
      "providerPercent": 2.0,
      "platformPercent": 1.0
    },
    "payout": {
      "payoutFeePercent": 0,
      "payoutFeeCents": 0,
      "retentionPercent": 10,
      "retentionHoldCents": 680,
      "payableNowCents": 6120
    },
    "vendor": {
      "netAfterFeesCents": 6120,
      "payableNowAfterServiceFeeCents": 6120,
      "serviceFeeMerchantDeductionCents": 0
    }
  },
  "payUrl": "https://www.niftipay.com/paylink/6a4a2be6-6c6c-4a4a-bf9f-4f5b3e6a90aa",
  "qrUrl": "https://api.qrserver.com/v1/create-qr-code?size=300x300&data=https%3A%2F%2Fwww.niftipay.com%2Fpaylink%2F6a4a2be6-6c6c-4a4a-bf9f-4f5b3e6a90aa",
  "matchedBy": "orderKey"
}

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 pspOrderId to exist.
  • Only psp="nopayn" is supported here.

Example request

curl -X DELETE "https://www.niftipay.com/api/fiat/orders/2221" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

Example response

{
  "order": {
    "orderKey": 2221,
    "status": "cancelled",
    "pspOrderId": "np_123",
    "pspStatus": "cancelled",
    "orderUrl": "https://pay.nopayn.example/order/np_123"
  },
  "pricing": {
    "payer": "customer",
    "subtotalCents": 6800,
    "serviceFeeCents": 204,
    "totalCents": 7004,
    "serviceFeePercent": 3.0,
    "lines": [
      { "type": "subtotal", "amountCents": 6800, "label": "Subtotal" },
      { "type": "service_maintenance_fee", "amountCents": 204, "label": "Service maintenance fee" },
      { "type": "total", "amountCents": 7004, "label": "Total" }
    ]
  },
  "fees": {
    "snapshotAt": "2026-02-11T10:00:05.000Z",
    "snapshotVersion": 1,
    "serviceFee": { "payer": "customer", "totalCents": 204, "providerCents": 136, "platformCents": 68, "providerPercent": 2.0, "platformPercent": 1.0 },
    "payout": { "payoutFeePercent": 0, "payoutFeeCents": 0, "retentionPercent": 10, "retentionHoldCents": 680, "payableNowCents": 6120 },
    "vendor": { "netAfterFeesCents": 6120, "payableNowAfterServiceFeeCents": 6120, "serviceFeeMerchantDeductionCents": 0 }
  },
  "nopayn": {
    "id": "np_123",
    "status": "cancelled",
    "order_url": "https://pay.nopayn.example/order/np_123"
  }
}

Cancel error examples

Invalid orderKey (400)
{ "error": "Invalid orderKey" }
Not found (404)
{ "error": "Not found" }
Completed cannot be cancelled (409)
{ "error": "Order is completed and cannot be cancelled" }
Missing pspOrderId (409)
{ "error": "Missing PSP order id (pspOrderId)" }
PSP not supported (501)
{ "error": "Cancel not implemented for psp=stripe" }
Upstream cancel failed (502 or provider status code)
{ "error": "Upstream cancel failed", "details": null }

Cancel Fiat Order (by reference)

Endpoint

DELETE /api/fiat/orders/cancel?reference=<reference> Cancel using the same matching rules as delete-by-reference:
  1. strict merchantReference match
  2. numeric fallback to orderKey

Example request

curl -X DELETE "https://www.niftipay.com/api/fiat/orders/cancel?reference=POS-1234" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

Responses

  • Returns order + pricing + nopayn on 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 paid or completed
  • Prevents over-refund by reading current refunds from NoPayn first
Refund ceiling depends on service fee payer:
  • 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

curl -X GET "https://www.niftipay.com/api/fiat/orders/2221/refunds" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

Example response

{
  "order": {
    "orderKey": 2221,
    "amountCents": 7004,
    "subtotalCents": 6800,
    "serviceFeePayer": "customer",
    "currency": "GBP",
    "status": "completed",
    "pspOrderId": "np_123"
  },
  "nopaynOrderId": "np_123",
  "refunds": [
    { "id": "rf_1", "amount": 1000, "status": "completed", "created": "2026-02-11T12:00:00.000Z" }
  ],
  "meta": {
    "serviceFeePayer": "customer",
    "maxRefundableCents": 7004,
    "refundedCents": 1000,
    "remainingRefundableCents": 6004
  }
}

Common list-refunds errors

Invalid orderKey (400)
{ "error": "Invalid orderKey" }
Not found (404)
{ "error": "Not found" }
Missing pspOrderId (400)
{
  "error": "Order is missing pspOrderId (NoPayn order id). It may not have been created in NoPayn yet."
}
NoPayn upstream error (502)
{ "error": "Failed to fetch refunds from NoPayn", "details": null }

Create refund

Endpoint

POST /api/fiat/orders/:orderKey/refunds

Request body

{
  "amountCents": 1000,
  "description": "Partial refund"
}
Optional: specify order lines (for item-based refunds):
{
  "amountCents": 1000,
  "description": "Refund 1 item",
  "orderLines": [
    { "merchantOrderLineId": "line-1", "quantity": 1 }
  ]
}

Fields

FieldTypeRequiredNotes
amountCentsnumberPositive integer. Must not exceed remaining refundable.
descriptionstringOptional PSP description.
orderLinesarray/nullIf provided: must be a non-empty array with { merchantOrderLineId, quantity }, or null.
orderLines validation:
  • orderLines must be non-empty if provided as an array
  • each line requires a non-empty merchantOrderLineId
  • quantity must be a positive integer

Example request

curl -X POST "https://www.niftipay.com/api/fiat/orders/2221/refunds" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "amountCents": 1000,
    "description": "Partial refund"
  }'

Example response (201)

{
  "order": {
    "id": "6a4a2be6-6c6c-4a4a-bf9f-4f5b3e6a90aa",
    "orderKey": 2221,
    "status": "completed",
    "pspOrderId": "np_123",
    "currency": "GBP",
    "amountCents": 7004,
    "subtotalCents": 6800,
    "serviceFeePayer": "customer"
  },
  "nopaynOrderId": "np_123",
  "refundOrder": {
    "id": "rf_2",
    "amount": 1000,
    "status": "completed"
  },
  "refunds": [
    { "id": "rf_1", "amount": 1000, "status": "completed" },
    { "id": "rf_2", "amount": 1000, "status": "completed" }
  ],
  "meta": {
    "serviceFeePayer": "customer",
    "maxRefundableCents": 7004,
    "refundedCents": 2000,
    "remainingRefundableCents": 5004
  }
}
If the order becomes fully refunded (remaining refundable reaches 0), the local fiatOrder.status is updated to "refunded".

Common refund errors

Invalid JSON (400)
{ "error": "Invalid JSON" }
Refunds only for paid/completed (409)
{ "error": "Refunds are only allowed for paid/completed orders (current status: \"new\")" }
Refunds only for kind=order (400)
{ "error": "Refunds can only be created for kind=order (not payment_link)." }
Missing pspOrderId (400)
{
  "error": "Order is missing pspOrderId (NoPayn order id). It may not have been created in NoPayn yet."
}
Nothing left to refund (409)
{ "error": "Nothing left to refund for this order." }
Amount exceeds remaining refundable (400)
{
  "error": "amountCents cannot exceed remaining refundable (6004)",
  "meta": { "maxRefundableCents": 7004, "refundedCents": 1000, "remainingRefundableCents": 6004 }
}
Invalid orderLines (400)
{ "error": "orderLines cannot be an empty array" }
{ "error": "orderLines[].merchantOrderLineId is required" }
{ "error": "orderLines[].quantity must be a positive integer" }
NoPayn refund create failed (502)
{ "error": "Failed to create refund in NoPayn", "details": null }

Node.js Example

A complete Node.js example showing how to create a fiat payment order with pricing breakdown and response parsing.
const result = await apiRequest("POST", "/api/fiat/orders", {
  // Your fiat integration ID (Dashboard → Settings → Fiat)
  integrationId: "YOUR_INTEGRATION_ID",

  // Fiat currency (ISO 4217 code)
  currency: "EUR",

  // Amount in major units (e.g. 19.95 = €19.95)
  amount: 19.95,
  // Or use minor units: amountCents: 1995

  // Short description shown on payment page (optional)
  description: "Widget purchase",

  // Your unique reference (optional — max 80 characters)
  reference: "FIAT-ORDER-001",

  // Who pays the processing fee: "customer" or "merchant" (optional)
  serviceFeePayer: "customer",

  // Customer email for receipts (optional)
  email: "jane.doe@example.com",
});

// Key response fields:
// result.order.payUrl              — redirect customer here
// result.order.id                  — Niftipay order ID
// result.reference                 — your reference
// result.order.status              — "new" initially
// result.qrUrl                     — QR code for payment page
// result.display.subtotal          — requested amount
// result.display.serviceFee        — processing fee
// result.display.total             — total charged
// result.pricing.payer             — who pays the fee
// result.pricing.serviceFeeBreakdown.totalPercent — fee %
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’s status), plus local states:
  • new — created locally (and usually also created upstream)
  • cancelled — cancelled locally (and upstream when possible)
  • completed / paid — successful payment
  • refunded — fully refunded (local state set when refundable remaining reaches 0)
  • error — upstream create failed