Capture, cancel, void, and refund
Certification cases for /capture, /cancel, /void, 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 the FlexFactor orderId, amount in cents, and currency. idempotencyKey is optional on the API, and this guide requires it on every capture so that a retry after a timeout replays the original response. 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 a problem-details body | HTTP status |
| 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 capturerequired; 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 capturerequired; totalBalancePaid is unchanged | Response, order balance |
| CAPTURE-07 | Capture a completed order | Capture a Completed order | HTTP 400; GET still returns status completed 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 reduce an order before capture, void part of it with /void and capture the remaining amount (Section 9.2). To return part of a charge after capture, use /refund with a partial amount.
9. Cancel and void
Two endpoints release an order before capture. /cancel in 9.1 always releases the full order amount. /void in 9.2 releases the full amount or part of it, and its cases apply only when you use that endpoint.
9.1 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 the same envelope as /void (success, result, status, statusCode, errors, customProperties, responseCode, responseMessage). Read success; do not key on status. 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 cancelled 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; success true; GET returns status 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 cancelled | 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; success true; order.cancelled received; GET returns status cancelled | 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 cancelled; 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 cancelled | Response |
| CANCEL-06 | Cancel that changes nothing | Repeat CANCEL-01 on the same order, then cancel an order in status completed | HTTP 200 with the same body both times; the first order remains status cancelled and the second remains status completed; no order.cancelled event; nothing is refunded | Responses, webhook count |
| CANCEL-07 | Cancel an unknown order | Cancel with a random GUID | HTTP 404 with a problem-details body | HTTP status |
Partial cancel is not availableA cancel releases the full order amount. The request body accepts no
amountorcurrencyfield. To release part of an order, use /void (Section 9.2).
9.2 Void
Applies to: VOID. /void takes an optional amount in cents and an idempotencyKey. An amount equal to the order amount is a full void and cancels the order. A lower amount is a partial void: the order keeps its pre-capture state, its amount drops by the voided value, and you capture the remainder later. Partial voids must be enabled for your sandbox account. Send a new idempotencyKey on every void and reuse it only on retries.
Read success in the response, then confirm the order with GET /v1/orders/{id}. The cases below use a new 9900-cent authorization in Capturerequired state from CIT-AUTHORIZE, MIT-ASYNC-AUTHORIZE, or SYNC-MIT unless the case says otherwise.
Reference: /void, /void HTTP status codes, Appendix A.5.
| ID | Scenario | Setup / action | Expected result | Evidence |
|---|---|---|---|---|
| VOID-01 | Full void | POST /v1/orders/{orderId}/void with amount 9900 and a new idempotencyKey | HTTP 200; success true; GET returns status cancelled; order.cancelled received when WEBHOOKS is In | orderId, response |
| VOID-02 | Partial void | amount 4000 with idempotencyKey K1 on a new order | HTTP 200; success true; GET returns the same status as before the void and amount 59.00; no order.cancelled event | orderId, response |
| VOID-03 | Partial void retry with the same key | Repeat VOID-02 with K1 | HTTP 200; the original response is replayed; amount stays 59.00 | Both responses |
| VOID-04 | Same key with a different amount | Repeat VOID-02 with K1 and amount 1000 | HTTP 409; amount stays 59.00 | Response |
| VOID-05 | Second distinct partial void | amount 1000 with a new key K2 on the VOID-02 order | HTTP 200; success true; GET returns amount 49.00 | Response |
| VOID-06 | Capture the remainder | /capture with amount 4900 on the VOID-05 order | HTTP 200; status SUCCESS; GET returns status completed and totalBalancePaid 49.00 | Response, order balance |
| VOID-07 | Void more than the order amount | amount 20000 on a new order | The void is not applied: HTTP 400, or HTTP 200 with success false. Record which; GET still returns amount 99.00 | Response |
| VOID-08 | Void a completed order | amount 9900 on an order in status completed | The void is not applied: HTTP 400, or HTTP 200 with success false. Record which; GET still returns status completed and nothing is refunded | Response |
| VOID-09 | Void an unknown order | Void with a random GUID | HTTP 404 with a problem-details body | HTTP status |
| VOID-10 | Partial void by external order ID | POST /v1/orders/{external-order-id}/external-void with your merchant orderId, amount 4000, and a new idempotencyKey on a new order | HTTP 200; success true; GET returns the same status as before the void and amount 59.00 | orderId, response |
10. Refund
Applies to: CORE. amount is in cents, the same as /capture and /void. The older amountToRefund field (dollars) is deprecated but still accepted; send one or the other, not both. REFUND-09 is required only if your integration sends amountToRefund; otherwise mark it Not applicable. 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 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 amount 9900 on a Completed 9900-cent order | HTTP 200; status SUCCESS; success true; transactionId present; order.refunded received with Amount 9900; GET returns status 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 | amount 2500 with idempotencyKey K1 on a new Completed 9900-cent order | HTTP 200; status SUCCESS; order.refunded with Amount 2500; GET returns status completed 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 | amount 2500 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 | amount 6000 on the REFUND-05 order (4900 cents remain) | HTTP 400; refundsAmount stays 50.00 | traceId |
| REFUND-07 | Refund an order that is not completed | Refund an order that is not in status completed, 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 |
| REFUND-09 | Partial refund with the deprecated amountToRefund field (required only if your integration sends amountToRefund) | amountToRefund 25.00 with a new idempotencyKey on a new Completed 9900-cent order, and no amount field | HTTP 200; status SUCCESS; order.refunded with Amount 2500; GET returns status completed and refundsAmount 25.00 | orderId |
Updated 13 days ago

