Capture, cancel, and refund
Certification cases for /capture, /cancel, and /refund.
Part of the Certification guide. Mark your scope on that page before you run these cases. Base payloads and test cards are on the Reference page.
8. Capture
Applies to: CIT-AUTHORIZE, MIT-ASYNC-AUTHORIZE, SYNC-MIT. /capture requires idempotencyKey, the FlexFactor orderId, amount in cents, and currency. The amount and currency must match the authorization. A double capture is always prevented on the combination of orderId, amount, and currency.
The response status is the capture outcome: SUCCESS or FAILED. result is always null on this endpoint, and success stays true when the processor declines the capture, so key your logic on status alone. The full capture is proved by CIT-AUTHORIZE-02, MIT-ASYNC-AUTHORIZE-03, or SYNC-MIT-04; the cases below reuse that order or a new authorization from the same flow.
Reference: /capture, /capture HTTP status codes, Appendix A.3.
| ID | Scenario | Setup / action | Expected result | Evidence |
|---|---|---|---|---|
| CAPTURE-01 | Replay with the same key | Repeat the capture from CIT-AUTHORIZE-02, MIT-ASYNC-AUTHORIZE-03, or SYNC-MIT-04 with the same idempotencyKey | HTTP 200; the original response is replayed; GET shows one capture in activityItems | Both responses |
| CAPTURE-02 | Double capture with a new key | Repeat the same capture with a new idempotencyKey | HTTP 400; no second capture; totalBalancePaid is unchanged | Response, order balance |
| CAPTURE-03 | Unknown orderId | Capture with a random GUID | HTTP 404 with an empty body | HTTP status |
| CAPTURE-04 | Missing idempotencyKey | Capture with idempotencyKey omitted | HTTP 400 with an errors object naming idempotencyKey | traceId |
| CAPTURE-05 | Amount does not match the authorization | Authorize 9900 cents, then capture 6000 cents, then capture 12000 cents | HTTP 400 both times; GET still returns status 5; totalBalancePaid is unchanged | Responses, order balance |
| CAPTURE-06 | Currency mismatch | Capture with a currency that differs from the authorization | HTTP 400; GET still returns status 5; totalBalancePaid is unchanged | Response, order balance |
| CAPTURE-07 | Capture a completed order | Capture a Completed order | HTTP 400; GET still returns status 7 with an unchanged balance | Response |
Partial capture is not availableThe capture amount must equal the authorized amount. CAPTURE-05 proves that a lower amount is rejected. To return part of a charge after capture, use /refund with a partial amount. Partial voids are planned; check the changelog for release details.
9. Cancel
Applies to: CIT-AUTHORIZE, MIT-ASYNC-PURCHASE, MIT-ASYNC-AUTHORIZE, SYNC-MIT. Cancel is a void. It applies to MIT orders in any state other than Completed or Cancelled (Draft, Onhold, Processing, Capturerequired) and to CIT orders in Capturerequired or Onhold. A captured order must be refunded instead. The only accepted body field is the optional idempotencyKey; there is no amount or currency field, and a cancel always applies to the full order amount.
A cancel returns HTTP 200 with { "success": true, "responseMessage": null }. There is no status field. The same body is returned when the order was not in a cancellable state and nothing changed, so a 200 does not by itself prove that the order was cancelled. Mark the order cancelled only after GET /v1/orders/{id} returns status 2 or order.cancelled arrives.
Reference: /cancel, /cancel response, Workflows §2.6.
| ID | Scenario | Setup / action | Expected result | Evidence |
|---|---|---|---|---|
| CANCEL-01 | Cancel a CIT authorization (CIT-AUTHORIZE) | POST /v1/orders/{orderId}/cancel with {} on a Capturerequired CIT order | HTTP 200 with { "success": true, "responseMessage": null }; GET returns status 2 Cancelled; order.cancelled received when WEBHOOKS is In | orderId |
| CANCEL-02 | Capture after cancel (CIT-AUTHORIZE, MIT-ASYNC-AUTHORIZE) | Capture the CANCEL-01 order | HTTP 400; no capture occurs; GET still returns status 2 | Response |
| CANCEL-03 | Cancel a pending MIT order (MIT-ASYNC-PURCHASE, MIT-ASYNC-AUTHORIZE, SYNC-MIT) | Cancel an order in Draft or Processing | HTTP 200 with { "success": true, "responseMessage": null }; order.cancelled received; GET returns status 2 | Webhook IdempotencyKey |
| CANCEL-04 | Cancel an MIT authorization (MIT-ASYNC-AUTHORIZE) | Cancel an order after order.capturerequired and before capture | HTTP 200; GET returns status 2; no funds captured; order.cancelled received | orderId |
| CANCEL-05 | Cancel by external order ID | POST /v1/orders/{external-order-id}/external-cancel with your merchant orderId on a pending order | HTTP 200 with the same body; GET returns status 2 | Response |
| CANCEL-06 | Cancel that changes nothing | Repeat CANCEL-01 on the same order, then cancel an order in status 7 Completed | HTTP 200 with the same body both times; the first order remains status 2 and the second remains status 7; no order.cancelled event; nothing is refunded | Responses, webhook count |
| CANCEL-07 | Cancel an unknown order | Cancel with a random GUID | HTTP 404 with an empty body | HTTP status |
Partial cancel is not availableA cancel releases the full order amount. The request body accepts no
amountorcurrencyfield. Partial voids are planned; check the changelog for release details.
10. Refund
Applies to: CORE. amountToRefund is in dollars, not cents. Full refunds are de-duplicated by order ID. Partial refunds are not, so every partial refund must carry an idempotencyKey. FlexFactor refunds the cardholder directly and adjusts merchant settlement, so a refund on a rescued order must call /refund and must not go to the original processor.
The response status is the refund outcome: SUCCESS or FAILED. result is always null on this endpoint. A failed refund normally returns HTTP 200 with status FAILED and success false. A refunded order stays at status 7 Completed; GET /v1/orders/{id} reports the refunded total in refundsAmount, in dollars, and order.refunded carries EventData.Amount in cents.
Reference: /refund, /refund HTTP status codes, Workflows §2.7, Appendix A.4.
| ID | Scenario | Setup / action | Expected result | Evidence |
|---|---|---|---|---|
| REFUND-01 | Full refund | POST /v1/orders/{orderId}/refund with amountToRefund 99.00 on a Completed 9900-cent order | HTTP 200; status SUCCESS; success true; transactionId present; order.refunded received with Amount 9900; GET returns status 7 Completed and refundsAmount 99.00 | orderId, webhook IdempotencyKey |
| REFUND-02 | Full refund duplicate without a key | Repeat REFUND-01 on the same order with no idempotencyKey | No second credit; refundsAmount stays 99.00; one order.refunded event in total | Order record, webhook count |
| REFUND-03 | Partial refund | amountToRefund 25.00 with idempotencyKey K1 on a new Completed 9900-cent order | HTTP 200; status SUCCESS; order.refunded with Amount 2500; GET returns status 7 and refundsAmount 25.00 | orderId |
| REFUND-04 | Partial refund retry with the same key | Repeat REFUND-03 with K1 | HTTP 200; the original response is replayed; refundsAmount stays 25.00 | Both responses |
| REFUND-05 | Second distinct partial refund | amountToRefund 25.00 with a new key K2 on the REFUND-03 order | HTTP 200; refundsAmount 50.00; a second order.refunded event | Webhook IdempotencyKey |
| REFUND-06 | Refund above the remaining balance | amountToRefund 60.00 on the REFUND-05 order (49.00 remains) | HTTP 400; refundsAmount stays 50.00 | traceId |
| REFUND-07 | Refund an order that is not completed | Refund an order that is not in status 7, for example a cancelled order from Section 9 or the declined CIT-PURCHASE-02 order | The refund is not applied: HTTP 400, or HTTP 200 with status FAILED. Record which | Response |
| REFUND-08 | Refund an unknown order | Refund with a random GUID | HTTP 404 | HTTP status |
Updated about 8 hours ago

