[BUG] Airwallex: successful 3DS payment recorded as failed - CompleteAuthorize unconditionally calls confirm_continue on an already-succeeded intent
Bug Description
The Airwallex connector's CompleteAuthorize implementation unconditionally calls POST /api/v1/pa/payment_intents/{id}/confirm_continue (get_url, crates/hyperswitch_connectors/src/connectors/airwallex.rs:657-672 — no status branch).
With Airwallex's current sandbox behavior, once the 3DS challenge is completed on the challenge page, the PaymentIntent is already SUCCEEDED by the time the shopper is redirected back. The subsequent confirm_continue is then rejected:
invalid_status_for_operation: The PaymentIntent status SUCCEEDED is invalid for operation confirm_continue.build_error_response leaves attempt_status: None, and for a 4xx the core's error handling falls back to AttemptStatus::Failure — so a payment that actually charged the customer is recorded as failed. Result: funds divergence (connector charged / hyperswitch shows failed), invisible unless reconciliation catches it.
Expected Behavior
When the connector reports the intent is already SUCCEEDED, CompleteAuthorize should resolve to success (idempotent completion) — the common pattern across connectors for already-completed responses.
Actual Behavior
confirm_continue gets a 400 → the payment is finalized as failed while the Airwallex intent is SUCCEEDED and the customer is charged.
Sandbox sample (2026-08-12): $1.00 USD with 3DS test card 4012000300000062 — challenge completed, redirect carried succeeded=true, confirm_continue rejected as above; Airwallex intent int_sgpvj2scdhl9m2bljq6 shows SUCCEEDED while the hyperswitch payment shows failed. Non-3DS cards (4111...) are unaffected (no confirm_continue step). Payments still sitting in requires_customer_action do converge via force_sync — this report is specifically about the "redirect arrived, confirm_continue rejected" terminal misclassification.
Steps To Reproduce
- Airwallex sandbox MCA; card payment with 3DS test card
4012000300000062; confirm with the redirect flow. - Complete the challenge on the Airwallex page.
- Observe the redirect return with
succeeded=true, the router'sconfirm_continuecall rejected withinvalid_status_for_operation, and the payment finalized asfailed.
Context For The Bug
Two possible fixes (happy to implement either):
- Minimal: in the Airwallex error path, treat
code == "invalid_status_for_operation"whose message namesSUCCEEDEDas idempotent success for CompleteAuthorize —ErrorResponse.attempt_statusexists precisely for connector-level overrides (setSome(AttemptStatus::Charged)). - Or have CompleteAuthorize check the intent status before deciding to call
confirm_continue(costs an extra API round-trip).
Related: #13278 routes Airwallex via UCS and reimplements the 3DS confirm flow there; it does not touch this legacy path, and the UCS implementation likely needs the same guard. Aside: the Airwallex build_error_response contains payu_error_response debug logging and a handle_json_response_deserialization_failure(res, "tesouro") call — copy-paste artifacts suggesting this error path wasn't specifically reviewed for Airwallex.
I'm willing to submit a PR.
Environment
Reproduced on a self-hosted v1 deployment (2026.07.08.1); connector code verified byte-identical on main @ 83d1d0c1 (2026.08.13.0) — no commits to the Airwallex connector in between.
Source: juspay/hyperswitch