Confirming a Payment Allocation (3DS)
When a payment allocation requires 3D Secure (3DS) authentication, it enters either the AUTH_REQUIRED or CONFIRMATION_REQUIRED status. The customer must complete the 3DS challenge in their browser or app before the allocation can proceed. Once the challenge is complete, the merchant calls the Confirm Allocation endpoint to continue the payment flow for that specific allocation.
For business rules and lifecycle details, see Split Tender Payments β Business Guide.
- Prerequisites: Merchant account must be configured for split-tender payments. The allocation must be in
AUTH_REQUIREDorCONFIRMATION_REQUIREDstatus. - API Endpoint:
PATCH /v2/payments/{paymentId}/allocations/{allocationId}/confirm - API Spec: Confirm Payment Allocation
- Scope:
merchant - API Version: v2
- Required Headers:
X-Merchant-Id(UUID),X-Checkout-Id(UUID) - Request Body: None
- Response:
202 Acceptedβ updated transaction with the confirmed allocation inCONFIRMATION_INITIALIZED - Error Handling: See Payment Error Codes for applicable error codes.
When to Call This Endpointβ
Call this endpoint when polled or webhook-notified that an allocation has transitioned to:
| Status | Meaning |
|---|---|
AUTH_REQUIRED | 3DS authentication is required before authorization can proceed |
CONFIRMATION_REQUIRED | Authorization is complete but explicit confirmation is needed before capture |
Do not call this endpoint for allocations in any other status. The service will reject the request if the allocation is not in a confirmable state.
If another allocation in the same transaction is already in FAILED, PAYMENT_METHOD_FAILED, CANCELED, CANCEL_INITIALIZED, or CANCEL_FAILED, the confirm call will be rejected with 422. The transaction is already heading toward cancellation β completing a 3DS challenge would only result in an immediate rollback of the confirmed leg.
π InternalInternal information β not visible in the public (merchant) site.
Confirm Allocation Processing Flowβ
Confirm Allocation Sequence
Processing Stepsβ
- Receive request β PATCH with
paymentId,allocationId,X-Merchant-Id, andX-Checkout-Id - Merchant ownership β Verify
X-Merchant-Idmatches the transaction's merchant - Sibling abort check β Reject if any sibling allocation is in
FAILED,PAYMENT_METHOD_FAILED,CANCELED,CANCEL_INITIALIZED, orCANCEL_FAILED; the transaction is already heading toward cancellation - Allocation membership β Verify
allocationIdbelongs to this transaction - Checkout ID match β Compare
X-Checkout-Idagainst thecheckoutIdstored in the allocation's metadata - Status check β Verify the allocation is in
AUTH_REQUIREDorCONFIRMATION_REQUIRED - Checkout session validation β Auth service confirms the session is active and non-agent
- Transition β Allocation moves to
CONFIRMATION_INITIALIZED; event published (dataVersion: "0.1") - Stripe processing β Stripe adapter processes the PaymentIntent; allocation eventually transitions to
AUTHORIZEDorCOMPLETED
Processor-Specificsβ
- V1 delegation: The endpoint delegates to V1
ConfirmPaymentCommandHandler. Stripe-facing logic is unchanged β onePaymentIntentper allocation. - Event version: Published event carries
dataVersion: "0.1"β the existing stripe adapterPaymentConsumerhandles it; no new consumer is required. - Advisory lock: An advisory lock is acquired per allocation to prevent concurrent confirm calls.
- Agent sessions: Checkout session validation rejects agent-initiated sessions β agents cannot complete 3DS challenges.
API Requestβ
Path Parametersβ
| Parameter | Type | Description |
|---|---|---|
paymentId | UUID | The V2 transaction ID (id from the create payment response) |
allocationId | UUID | The specific allocation ID (paymentAllocations[n].id) to confirm |
Required Headersβ
| Header | Type | Description |
|---|---|---|
Authorization | string | Bearer token |
X-Merchant-Id | UUID | Merchant identifier β must match the merchant that owns the transaction |
X-Checkout-Id | UUID | Checkout session ID β must match the checkout session under which the payment was created |
Request Bodyβ
None β this is a PATCH with no body.
Sample Request
curl -X PATCH "https://api-stg.uhg.com/api/financial/commerce/nonprodcheckout/v2/payments/{paymentId}/allocations/{allocationId}/confirm" \
-H "Authorization: Bearer <token>" \
-H "X-Merchant-Id: b955db5e-aef2-47de-bbb9-c80b9cc16e8f" \
-H "X-Checkout-Id: f3a1c2d4-e5f6-7890-ab12-cd34ef567890" \
-H "X-Upstream-Env: dev" \
-H "Accept: application/json"
Validationβ
The endpoint performs the following checks in order. The first failure short-circuits and returns an error.
| Order | Check | Failure |
|---|---|---|
| 1 | Transaction exists | 404 Not Found |
| 2 | X-Merchant-Id matches the transaction's merchant | 403 Forbidden |
| 3 | Payment legs exist for the transaction | 404 Not Found |
| 4 | All payment legs belong to X-Merchant-Id | 403 Forbidden |
| 5 | No sibling allocation is in an abort state (FAILED, PAYMENT_METHOD_FAILED, CANCELED, CANCEL_INITIALIZED, CANCEL_FAILED) | 422 Unprocessable Entity |
| 6 | The target allocation ID exists within this transaction | 422 Unprocessable Entity |
| 7 | X-Checkout-Id matches the checkout ID stored in the allocation's metadata | 422 Unprocessable Entity |
| 8 | The allocation status is AUTH_REQUIRED or CONFIRMATION_REQUIRED | 422 Unprocessable Entity |
| 9 | Checkout session is active (not expired or in a terminal state) | 422 Unprocessable Entity |
API Responseβ
Success β 202 Acceptedβ
Returns the full updated transaction. The confirmed allocation transitions to CONFIRMATION_INITIALIZED. The transaction-level status reflects the combined state of all allocations.
The complex object is trimmed for brevity. Refer to the API Spec for the full response structure.
Sample Response β Allocation CONFIRMATION_INITIALIZED
{
"url": "https://api-stg.uhg.com/v2/payments/3222ac8a-c8fa-4426-bd99-e96a909569b0",
"data": {
"id": "3222ac8a-c8fa-4426-bd99-e96a909569b0",
"merchantTransactionId": "c407ecc7-25f0-4fd9-844b-e9baca53707a",
"amount": 100,
"status": "AUTH_REQUIRED",
"paymentAllocations": [
{
"id": "8cd647c5-f9ff-42a0-b7d9-084c7f5d7d47",
"amount": 60,
"status": "CONFIRMATION_INITIALIZED",
"paymentMethod": {
"// ... see API Spec for full paymentMethod object ...": "..."
}
},
{
"id": "777373ac-dccf-499f-8c77-1d5e198508ad",
"amount": 40,
"status": "AUTH_REQUIRED",
"paymentMethod": {
"// ... see API Spec for full paymentMethod object ...": "..."
}
}
]
}
}
Sample Response β Validation Failure (422)
{
"title": "UNPROCESSABLE_ENTITY",
"detail": "Payment cannot be confirmed",
"status": 422
}
Status Handlingβ
After confirming an allocation, track the overall transaction status by polling GET /v2/payments/{paymentId} or listening to webhooks. The parent transaction status is derived from the combined statuses of all allocations.
Use webhooks as the primary mechanism for status updates (PAYMENT_SUCCEEDED, PAYMENT_FAILED). Fall back to polling only if webhooks are unavailable.
Post-Confirm Status Progressionβ
| Allocation Status After Confirm | Meaning |
|---|---|
CONFIRMATION_INITIALIZED | Confirm accepted; processing with Stripe underway |
AUTHORIZED | Stripe confirms the PaymentIntent; awaiting capture |
COMPLETED | Allocation fully captured and complete |
FAILED | Confirmation processing failed at the processor |
Status Mapping Tableβ
The parent transaction's status reflects the lowest-ranked allocation status across all legs (i.e., the allocation that is furthest behind drives the parent). If one allocation has been confirmed but another still requires 3DS, the parent remains in AUTH_REQUIRED.
| Allocation 1 | Allocation 2 | Parent Status | Description |
|---|---|---|---|
β³ CONFIRMATION_INITIALIZED | β³ AUTH_REQUIRED | β³ AUTH_REQUIRED | One confirmed, one still pending 3DS |
β³ CONFIRMATION_INITIALIZED | β³ CONFIRMATION_INITIALIZED | β³ CONFIRMATION_INITIALIZED | Both confirmed, awaiting Stripe processing |
β
COMPLETED | β
COMPLETED | β
COMPLETED | Both complete β final state |
β FAILED | β³ CONFIRMATION_INITIALIZED | β FAILED | One failed β parent immediately reflects failure |
β PAYMENT_METHOD_FAILED | β³ AUTH_REQUIRED | β PAYMENT_METHOD_FAILED | Payment method failure β cancel proceeds; no need to wait for 3DS |
π ROLLED_BACK | β FAILED | β FAILED | Rollback complete β final state |
When any allocation is in AUTH_REQUIRED, the orchestrator pauses the capture pipeline for the entire transaction. No allocation will be captured until all pending 3DS challenges are resolved. This prevents partial captures across a split-tender transaction.
π InternalInternal information β not visible in the public (merchant) site.
Internal Statusesβ
Merchants see abstracted statuses (AUTH_REQUIRED, COMPLETED, FAILED). Internally, CCG uses finer-grained statuses. The confirm call transitions a single allocation from AUTH_REQUIRED/CONFIRMATION_REQUIRED β CONFIRMATION_INITIALIZED.
AUTH_REQUIRED is a publishable split-payment status. When all allocations share AUTH_REQUIRED, CCG publishes a transaction event. Mixed statuses (e.g., one AUTH_REQUIRED, one CONFIRMATION_INITIALIZED) do not trigger an event publish β the transaction waits for all legs to align.
Internal Status Combinations After Confirm
| Allocation Internal Status | Merchant-Visible Status | Description |
|---|---|---|
AUTH_REQUIRED | AUTH_REQUIRED | 3DS challenge pending β no capture until resolved |
CONFIRMATION_REQUIRED | PENDING | 3DS complete; awaiting merchant confirm call |
CONFIRMATION_INITIALIZED | PENDING | Confirm accepted; awaiting Stripe processing |
AUTHORIZED | PENDING | Stripe PaymentIntent authorized (SALE flow) |
CAPTURE_INITIALIZED | PENDING | Capture in progress |
COMPLETED | COMPLETED | Captured β terminal |
FAILED | FAILED | Processing failed β terminal |
PAYMENT_METHOD_FAILED | FAILED | Payment method rejected β terminal; maps same as FAILED |
CANCEL_INITIALIZED | PENDING | Rollback initiated for sibling allocation |
CANCELED | CANCELED | Rolled back β terminal |
Error Handlingβ
For the complete list of error codes and remediation steps, see Payment Error Codes.
Confirm-Specific Error Scenariosβ
| Scenario | HTTP Status | Description |
|---|---|---|
Missing X-Checkout-Id header | 400 | Required header not present in the request |
| Merchant mismatch | 403 | X-Merchant-Id does not own the transaction |
| Transaction not found | 404 | paymentId does not exist |
| Allocation not found | 422 | allocationId is not part of this transaction |
| Checkout ID mismatch | 422 | X-Checkout-Id does not match the checkout session in the allocation's metadata |
| Sibling allocation in abort state | 422 | A sibling is in FAILED, PAYMENT_METHOD_FAILED, CANCELED, CANCEL_INITIALIZED, or CANCEL_FAILED β completing 3DS would trigger an immediate rollback |
| Ineligible allocation status | 422 | Allocation is not in AUTH_REQUIRED or CONFIRMATION_REQUIRED |
| Checkout session invalid | 422 | Checkout session has expired, completed, or is agent-initiated |
If a split-tender allocation requires 3DS authentication during an agent-assisted session, the entire payment fails. Agents cannot complete 3DS challenges. The confirm endpoint will reject requests from agent checkout sessions.
FAQβ
Q: Can I call this endpoint for both allocations at the same time?
A: Yes, but each allocation requires a separate PATCH call. The service applies an advisory lock per allocation to prevent duplicate concurrent confirms.
Q: What if the customer doesn't complete the 3DS challenge?
A: The allocation stays in AUTH_REQUIRED or CONFIRMATION_REQUIRED. No automatic timeout is applied β the allocation remains in that status until confirmed or the checkout session expires.
Q: What happens if the checkout session expires before I call confirm?
A: The endpoint will return a 422 at validation step 8 (checkout session active check). Initiate a new checkout session and payment.
Q: Will the other allocation be affected if one confirm fails?
A: If one allocation's confirm fails after the other has already progressed, CCG automatically rolls back the successful allocation (cancellation). The parent transaction moves to FAILED.
Q: Does the confirm endpoint publish a new event to the stripe adapter?
A: The event is published with dataVersion: "0.1". The existing stripe adapter PaymentConsumer handles it β no new consumer or routing is required.
Integration Checklistβ
Before going live with the confirm allocation flow, verify:
- Your integration polls or subscribes to webhooks to detect
AUTH_REQUIRED/CONFIRMATION_REQUIREDstatus on individual allocations - You store the
allocationIdfrom the create payment response for each payment leg - You store the
X-Checkout-Idused during the original payment creation β the same value must be passed to the confirm call - Your 3DS challenge flow returns control to your application before calling confirm
- You handle
422responses gracefully β log the detail field for diagnostic context - You do not call confirm from an agent-assisted session (agent flows will always result in a
422) - You test against staging using the Payment Error Codes test card scenarios for 3DS flows