JavaScript is required to view this page
Refunds

Refunds

The Refunds API lets you return money for a payment your customer has already made. One endpoint, POST /payments/v1/refunds, handles every refund, but what it does depends on how the customer paid. For a card payment, BlinkPay sends the money back through the card network. For a bank (account-to-account) payment, BlinkPay does not move any money: it gives you the account number the customer paid from, and you pay them back yourself.

Refunds must be enabled for your merchant account, and card refunds are enabled separately from bank refunds. If a refund returns BP047 or BP049, contact support@blinkpay.co.nz to have them enabled.

EndpointScopePurpose
POST /payments/v1/refundscreate:refundCreate a refund
GET /payments/v1/refunds/{refund_id}view:refundCheck a refund's status
GET /payments/v1/payments/{payment_id}view:paymentSee every refund on a payment in refunds[]

Refund Types by Payment Method

Each refund has a type. Which types you can use depends on the payment method:

Refund typeBank (account-to-account) paymentCard payment
account_numberSupported. No money moves: the refund is completed at once and carries the payer's account number, so you can pay them backNot supported (BP013)
full_refundNot supported (BP048)Supported. Returns the whole payment through the card network, or cancels the charge if it has not gone out yet
partial_refundNot supported (BP048)Supported. Returns the amount you specify through the card network

Refunds are not limited by consent type: a payment made under a quick payment, a single consent or an enduring consent can be refunded in the same way. The payment must have reached AcceptedSettlementCompleted first; until then a refund returns BP012. A refund can never take the total refunded for a payment above the amount charged for it (BP039).

Card refunds must be made within 60 days of the payment completing; after that the API refuses them with BP045 and you need to contact BlinkPay support. account_number refunds have no time limit.

Surcharged Payments

On a surcharged payment the customer was charged total_charge, your price plus the surcharge, and you can refund up to that total, surcharge included. On a card payment, a full_refund returns the whole of total_charge and partial_refund amounts can add up to it. On a bank payment you pay the customer back yourself, so a full refund means paying back total_charge.

Creating a Refund

Always send an idempotency-key header with a UUID you generate for this refund. See Idempotency and Retries for why it matters more here than anywhere else.

Account number refund (bank payments)

The request carries only the payment ID and the type.

POST /payments/v1/refunds HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
idempotency-key: 0f8a2c4e-6b1d-4f3a-8e9c-7d5b3a1f2e4c

{
  "type": "account_number",
  "payment_id": "7c1e4b2a-9d3f-4e8a-b6c5-2f1a0d9e8c7b"
}

Asking again for the same payment returns the existing refund rather than a new one.

Full refund (card payments)

pcr is required. It is stored with the refund for your records.

POST /payments/v1/refunds HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
idempotency-key: 3b9d1e7f-2a4c-4d8b-9f6e-1c5a7b3d9e2f

{
  "type": "full_refund",
  "payment_id": "a4f2c8e1-5b7d-4a9c-8e3f-6d1b2c9a7e5f",
  "pcr": {
    "particulars": "Refund",
    "code": "ORDER1234",
    "reference": "Returned"
  }
}

Partial refund (card payments)

amount and pcr are both required. The amount uses the same format as payment amounts (a string with one or two decimal places, greater than zero) and its currency must match the payment's.

POST /payments/v1/refunds HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
idempotency-key: 8e4a6c2b-1d3f-4b5e-a7c9-2f0d8b6e4a1c

{
  "type": "partial_refund",
  "payment_id": "a4f2c8e1-5b7d-4a9c-8e3f-6d1b2c9a7e5f",
  "amount": {
    "currency": "NZD",
    "total": "10.00"
  },
  "pcr": {
    "particulars": "Refund",
    "code": "ORDER1234",
    "reference": "1 item"
  }
}

Response (201 Created) for every type:

{
  "refund_id": "5e2d8f1c-3b4a-4c6d-9e7f-0a1b2c3d4e5f"
}

A 201 means the refund was accepted, not that the money has been returned. Check its status as described below.

Refund Statuses

StatusMeaning
processingA card refund has been sent to the card processor and is not yet confirmed. Keep polling.
completedFor an account_number refund: the account number is available and no money has moved. For a card refund: the card processor has accepted the refund, or the charge was cancelled before it went out.
failedThe card processor refused the refund. No money moved, and the amount is available to refund again with a new idempotency key.

completed and failed are terminal. On a card refund, completed means the refund has left your side; it usually reaches the customer within a few business days.

Checking Refund Status

GET /payments/v1/refunds/{refund_id} HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}

Response for an account number refund:

{
  "refund_id": "5e2d8f1c-3b4a-4c6d-9e7f-0a1b2c3d4e5f",
  "status": "completed",
  "creation_timestamp": "2026-10-07T10:15:00+13:00",
  "status_updated_timestamp": "2026-10-07T10:15:00+13:00",
  "account_number": "12-3456-7890123-00",
  "detail": {
    "type": "account_number",
    "payment_id": "7c1e4b2a-9d3f-4e8a-b6c5-2f1a0d9e8c7b"
  }
}

Pay the customer back to account_number from your own bank account. The account number is personal information: store it only as long as you need it and keep it out of logs.

For a card refund, account_number is N/A and detail repeats the request, including amount for a partial refund. A card refund is usually completed within seconds, but a refund can stay processing for days, so don't wait on it: initiate it, then check its status later, in the Client Portal or with this endpoint from a daily sweep. The same refunds also appear in the refunds array of GET /payments/v1/payments/{payment_id}.

Worked Example: Card Payment, Partial Refund, Poll

  1. A customer pays $49.99 by card through a gateway quick payment. GET /payments/v1/quick-payments/{quick_payment_id} shows consent.payments[0].status as AcceptedSettlementCompleted with payment_id a4f2c8e1-5b7d-4a9c-8e3f-6d1b2c9a7e5f. Store the payment_id against the order.
  2. The customer returns one item. Generate a new UUID, store it against the return, and send the partial refund request above for 10.00 with that UUID as the idempotency-key.
  3. BlinkPay responds 201 with a refund_id. Store it.
  4. Call GET /payments/v1/refunds/{refund_id} later, for example from a daily sweep. It shows processing until the card processor confirms it, then completed.
  5. If the request in step 2 timed out, send it again with the same key. You get the original refund_id back and the customer is not refunded twice.
  6. To return the remaining $39.99 later, send another partial_refund for 39.99 with a new key. A full_refund at this point would be refused with BP039, because it always claims the whole payment.

For a bank payment the flow is shorter: send an account_number refund, read account_number from the completed refund, and make the payment to the customer yourself.

Card Refunds: Cancel or Refund

For a card full_refund, BlinkPay checks with the card processor and chooses how to return the money. If the charge has not gone out yet and no refund has been attempted on the payment, it is cancelled and the customer sees no charge, although their bank may show a pending amount until it releases it. Otherwise the money comes back as a separate refund after the original charge. A partial_refund is always a separate refund. You cannot choose between the two; see Card Payments for what the customer sees.

A cancellation made in your card processor's portal appears as a completed full_refund on the payment. Other refunds made outside BlinkPay don't appear in the API.

Idempotency and Retries

Send an idempotency-key header (a UUID) with every refund and keep it until you know the outcome.

SituationWhat happensWhat to do
Same key, same body, first request finishedThe original 201 and refund_id are returned; nothing new is refundedUse the returned refund_id
Same key, same body, first request still in progress409 BP711Wait a few seconds and retry with the same key
Same key, different body409 BP702Use a new key for a different refund
No response (timeout, network error, 5xx)UnknownRetry with the same key
Refund failedThe key stays tied to the failed refund; reusing it returns the failed refund_idRetry with a new key
409 BP715 (a card charge settled while it was being cancelled)Nothing was refundedRetry with a new key; the retry is sent as a refund

Without a key, a retried partial_refund refunds the customer twice, up to the amount charged.

A 502 (BP903 to BP908) means the card processor's answer did not arrive, so the refund may or may not have gone through. Check the payment's refunds[] before doing anything else, and if you retry, use the same key.

Errors

A refund that can't be made is refused with a 422 and a code saying why: refunds aren't enabled for your account, the refund type doesn't match the payment method, the payment hasn't completed, the amount is more than is left to refund, or a card payment is over 60 days old. Show the reason to your operator and don't retry unchanged. The codes are listed under Refund Errors.

Best Practices

Store the payment_id against every order when the payment completes, because you need it to refund; you can also find it later from the consent, whose payments[] lists every payment with its payment_id. Generate the idempotency key when you decide to refund, store it with the refund request, and reuse it for every retry of that refund until you have a refund_id and a terminal status. Treat completed on an account_number refund as the start of your own payment to the customer, not the end of the refund, and keep a record of when you paid them back. Reconcile partial refunds against the running total so you never ask for more than is left.

Testing

Refunds work in the sandbox the same way as in production, provided refunds are enabled on your sandbox account. Make a payment with the sandbox test values, wait for AcceptedSettlementCompleted, then refund it. You can also produce the main refusals: refund a payment that has not completed (BP012), send partial_refund on a bank payment (BP048), or refund more than the payment (BP039).

Next Steps