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.

IDScenarioSetup / actionExpected resultEvidence
CAPTURE-01Replay with the same keyRepeat the capture from CIT-AUTHORIZE-02, MIT-ASYNC-AUTHORIZE-03, or SYNC-MIT-04 with the same idempotencyKeyHTTP 200; the original response is replayed; GET shows one capture in activityItemsBoth responses
CAPTURE-02Double capture with a new keyRepeat the same capture with a new idempotencyKeyHTTP 400; no second capture; totalBalancePaid is unchangedResponse, order balance
CAPTURE-03Unknown orderIdCapture with a random GUIDHTTP 404 with a problem-details bodyHTTP status
CAPTURE-05Amount does not match the authorizationAuthorize 9900 cents, then capture 6000 cents, then capture 12000 centsHTTP 400 both times; GET still returns status capturerequired; totalBalancePaid is unchangedResponses, order balance
CAPTURE-06Currency mismatchCapture with a currency that differs from the authorizationHTTP 400; GET still returns status capturerequired; totalBalancePaid is unchangedResponse, order balance
CAPTURE-07Capture a completed orderCapture a Completed orderHTTP 400; GET still returns status completed with an unchanged balanceResponse
📘

Partial capture is not available

The 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.

IDScenarioSetup / actionExpected resultEvidence
CANCEL-01Cancel a CIT authorization (CIT-AUTHORIZE)POST /v1/orders/{orderId}/cancel with {} on a capturerequired CIT orderHTTP 200; success true; GET returns status cancelled; order.cancelled received when WEBHOOKS is InorderId
CANCEL-02Capture after cancel (CIT-AUTHORIZE, MIT-ASYNC-AUTHORIZE)Capture the CANCEL-01 orderHTTP 400; no capture occurs; GET still returns status cancelledResponse
CANCEL-03Cancel a pending MIT order (MIT-ASYNC-PURCHASE, MIT-ASYNC-AUTHORIZE, SYNC-MIT)Cancel an order in draft or processingHTTP 200; success true; order.cancelled received; GET returns status cancelledWebhook IdempotencyKey
CANCEL-04Cancel an MIT authorization (MIT-ASYNC-AUTHORIZE)Cancel an order after order.capturerequired and before captureHTTP 200; GET returns status cancelled; no funds captured; order.cancelled receivedorderId
CANCEL-05Cancel by external order IDPOST /v1/orders/{external-order-id}/external-cancel with your merchant orderId on a pending orderHTTP 200 with the same body; GET returns status cancelledResponse
CANCEL-06Cancel that changes nothingRepeat CANCEL-01 on the same order, then cancel an order in status completedHTTP 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 refundedResponses, webhook count
CANCEL-07Cancel an unknown orderCancel with a random GUIDHTTP 404 with a problem-details bodyHTTP status
📘

Partial cancel is not available

A cancel releases the full order amount. The request body accepts no amount or currency field. 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.

IDScenarioSetup / actionExpected resultEvidence
VOID-01Full voidPOST /v1/orders/{orderId}/void with amount 9900 and a new idempotencyKeyHTTP 200; success true; GET returns status cancelled; order.cancelled received when WEBHOOKS is InorderId, response
VOID-02Partial voidamount 4000 with idempotencyKey K1 on a new orderHTTP 200; success true; GET returns the same status as before the void and amount 59.00; no order.cancelled eventorderId, response
VOID-03Partial void retry with the same keyRepeat VOID-02 with K1HTTP 200; the original response is replayed; amount stays 59.00Both responses
VOID-04Same key with a different amountRepeat VOID-02 with K1 and amount 1000HTTP 409; amount stays 59.00Response
VOID-05Second distinct partial voidamount 1000 with a new key K2 on the VOID-02 orderHTTP 200; success true; GET returns amount 49.00Response
VOID-06Capture the remainder/capture with amount 4900 on the VOID-05 orderHTTP 200; status SUCCESS; GET returns status completed and totalBalancePaid 49.00Response, order balance
VOID-07Void more than the order amountamount 20000 on a new orderThe void is not applied: HTTP 400, or HTTP 200 with success false. Record which; GET still returns amount 99.00Response
VOID-08Void a completed orderamount 9900 on an order in status completedThe void is not applied: HTTP 400, or HTTP 200 with success false. Record which; GET still returns status completed and nothing is refundedResponse
VOID-09Void an unknown orderVoid with a random GUIDHTTP 404 with a problem-details bodyHTTP status
VOID-10Partial void by external order IDPOST /v1/orders/{external-order-id}/external-void with your merchant orderId, amount 4000, and a new idempotencyKey on a new orderHTTP 200; success true; GET returns the same status as before the void and amount 59.00orderId, 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.

IDScenarioSetup / actionExpected resultEvidence
REFUND-01Full refundPOST /v1/orders/{orderId}/refund with amount 9900 on a Completed 9900-cent orderHTTP 200; status SUCCESS; success true; transactionId present; order.refunded received with Amount 9900; GET returns status completed and refundsAmount 99.00orderId, webhook IdempotencyKey
REFUND-02Full refund duplicate without a keyRepeat REFUND-01 on the same order with no idempotencyKeyNo second credit; refundsAmount stays 99.00; one order.refunded event in totalOrder record, webhook count
REFUND-03Partial refundamount 2500 with idempotencyKey K1 on a new Completed 9900-cent orderHTTP 200; status SUCCESS; order.refunded with Amount 2500; GET returns status completed and refundsAmount 25.00orderId
REFUND-04Partial refund retry with the same keyRepeat REFUND-03 with K1HTTP 200; the original response is replayed; refundsAmount stays 25.00Both responses
REFUND-05Second distinct partial refundamount 2500 with a new key K2 on the REFUND-03 orderHTTP 200; refundsAmount 50.00; a second order.refunded eventWebhook IdempotencyKey
REFUND-06Refund above the remaining balanceamount 6000 on the REFUND-05 order (4900 cents remain)HTTP 400; refundsAmount stays 50.00traceId
REFUND-07Refund an order that is not completedRefund an order that is not in status completed, for example a cancelled order from Section 9 or the declined CIT-PURCHASE-02 orderThe refund is not applied: HTTP 400, or HTTP 200 with status FAILED. Record whichResponse
REFUND-08Refund an unknown orderRefund with a random GUIDHTTP 404HTTP status
REFUND-09Partial 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 fieldHTTP 200; status SUCCESS; order.refunded with Amount 2500; GET returns status completed and refundsAmount 25.00orderId


Did this page help you?