Split Tender Payments API
Split Tender allows a customer to divide a single payment across two saved cards, both charged in parallel as one coordinated transaction. This guide covers how to integrate with the Split Tender Payments API — from making the request, to monitoring status, to handling errors.
Single-card payments? If the transaction uses only one card, see Single Allocation Payments — no rollback logic applies.
- Behaviour: All-or-nothing — both cards succeed or neither is charged
- Processing: Both cards charged in parallel; automatic rollback on partial failure
- Idempotent: Same
merchantTransactionIdcan be retried up to 5 times
Note: In split tender payments, each card is processed as a separate allocation under a single payment transaction. The API request and response will always reflect this, with each allocation representing an individual card’s processing status, errors, and remediation details.
For business rules, scenarios, and lifecycle details, see Split Tender Payments — Business Guide.
- Enable Split Tender: Ensure your merchant account is configured. See Merchant Configuration
- API Endpoint:
- Use
POST v2/paymentsfor API - Use
POST v2/sessionsfor widget flows. See Widget Integration Guide
- Use
- Scope: All operations are at the
merchantscope. - Specs: Payment API Reference | Payment Events & Webhooks
- API:
v2| Widget:v3 - Testing: Use Test Cards to simulate the flow. See Test Cards
- ⚠️ Error Handling: See Payment Error Codes for split tender–specific errors and remediate using resolution steps
Split tender Processing Architecture
Both cards are processed in parallel via pre-auth + capture. If either card fails at any stage, the successful card is automatically cancelled (rolled back).
Split tender Process
Processing Steps:
- Request received — CCG receives split tender payment request
- Pre-validation — Both payment methods validated before calling Stripe
- Parallel processing — Both cards charged simultaneously (pre-auth)
- Outcome determination — Based on success/failure of each card
- Action orchestration:
- Both succeed → auto-capture both cards
- One fails → cancel the successful card (rollback)
- Both fail → return failure immediately
- Webhook — Single event emitted at parent transaction level
API Request
For complete field definitions, constraints, and validations, refer to the Create Payment API Spec.
Stored Payment Method (v2/payments)
Submit a split tender payment using saved cards from the customer's wallet. The key difference from a standard payment is the paymentAllocations array — exactly 2 entries, each specifying a paymentMethodId and amount that together equal the total.
Sample Request — Stored Payment Method
curl -X POST "https://api-stg.uhg.com/api/financial/commerce/nonprodcheckout/v2/payments" \
-H "Authorization: <token>" \
-H "X-Merchant-Id: b955db5e-aef2-47de-bbb9-c80b9cc16e8f" \
-H "X-Upstream-Env: dev" \
-H "Accept: */*" \
-H "Content-Type: application/json" \
-d '{
"merchantTransactionId": "c407ecc7-25f0-4fd9-844b-e9baca53707a",
"amount": 100,
"authorizeCard": false,
"customer": {
"hsid": "b313c1d1-b5b6-4ec7-8b5a-3a9cf7755060",
"metadata": {}
},
"metadata": null,
"paymentAllocations": [
{
"paymentMethodId": "7b30960f-8a19-4ae0-8d73-46849365f81d",
"amount": 60
},
{
"paymentMethodId": "a1c2d3e4-f5a6-7b8c-9d0e-1f2a3b4c5d6e",
"amount": 40
}
]
}'
Session-Based (v2/sessions)
For widget-based (v3) flows, the customer selects cards and distributes amounts through the PAYMENT_WITH_WALLET UI. The session handles card selection and split allocation interactively.
Refer to the Widget Integration Guide for session setup.
API Response
The complex object is trimmed for brevity. Refer to the API Spec for the full response structure.
Successful Response
Sample Response — COMPLETED
{
"url": "http://api-stg.uhg.com:443/v2/payments/3222ac8a-c8fa-4426-bd99-e96a909569b0",
"data": {
"id": "3222ac8a-c8fa-4426-bd99-e96a909569b0",
"merchantTransactionId": "30d279fa-9d7d-48c9-8e61-c3783eecf18c",
"amount": 200,
"description": "V2Split Tender Payment",
"currencyCode": "USD",
"status": "COMPLETED",
"vendor": {
// ... see API Spec for full vendor object ...
},
"statementDescriptorSuffix": "splitTheTe",
"paymentDateUtc": "2026-03-21T00:24:01.741688Z",
"metadata": {
"orderId": "123",
"orderType": "prescription"
},
"merchant": {
// ... see API Spec for full merchant object ...
},
"paymentDetails": {
"healthcare": {
"iias": {
"qualifiedAmount": 82,
"qualifiedAmountDetails": {
"prescriptionAmount": 42
}
},
"visionAmount": 8
}
},
"paymentAllocations": [
{
"id": "8cd647c5-f9ff-42a0-b7d9-084c7f5d7d47",
"amount": 100,
"status": "COMPLETED",
"paymentMethod": {
// ... see API Spec for full paymentMethod object ...
},
"paymentDetails": {
"healthcare": {
"iias": {
"qualifiedAmount": 41,
"qualifiedAmountDetails": {
"prescriptionAmount": 21
}
},
"visionAmount": 4
}
}
},
{
"id": "777373ac-dccf-499f-8c77-1d5e198508ad",
"amount": 100,
"status": "COMPLETED",
"paymentMethod": {
// ... see API Spec for full paymentMethod object ...
},
"paymentDetails": {
"healthcare": {
"iias": {
"qualifiedAmount": 41,
"qualifiedAmountDetails": {
"prescriptionAmount": 21
}
},
"visionAmount": 4
}
}
}
],
"customer": {
"id": "fa6dcef4-6400-467b-aa28-0f02a7afd724"
},
"agent": {
"firstName": "AgentFirstName",
"lastName": "AgentLastName",
"isAccessVerified": null,
"userId": "s438"
}
}
}
Failure Response
When a split tender payment fails, the API response provides detailed information for each allocation (if allocations were attempted):
- For rollbacks (when one card succeeds and the other fails), check the
remediationobject in the rolled-back allocation. This object explains why the allocation was canceled and confirms that split payments are atomic (all-or-nothing). - For failures (when an allocation fails), refer to the
errorobject within each failed allocation. This contains the error code, message, and additionalerror.detailsfrom the payment processor (e.g., decline reason).
Business Rule Violations:
If the request violates a business rule (e.g., unsupported payment method, invalid configuration), the response will not include any paymentAllocations array. Instead, the error is described at the top level of the response.
Business Rule Violation (No Allocations)
Sample Response — Business Rule Violation
{
"title": "UNPROCESSABLE_ENTITY",
"detail": "Split payments cannot be processed using a bank account. Please select a different payment method",
"status": 422,
"data": {
"id": "de989762-5a8f-4a82-95e1-04c2a5356a15",
"merchantTransactionId": "13c7de91-b649-436e-8cdc-edff5e233238",
"amount": 200,
"currencyCode": "USD",
"status": "FAILED",
"vendor": {
// ... see API Spec for full vendor object ...
},
"paymentDateUtc": "2026-03-21T00:24:59.102492Z",
"merchant": {
// ... see API Spec for full merchant object ...
},
"customer": {
"id": "a903448b-7090-4f37-8400-26eac6b0a13b"
}
}
}
Both Allocations failed during processing
Sample Response — Both Allocations (CARD) Failed
{
"title": "PAYMENT_ERROR",
"detail": "One of the split tender card payments failed during processing",
"status": 422,
"data": {
"id": "9cbecc7b-6401-4f1b-bc1d-c2769d466e7f",
"merchantTransactionId": "95b8da69-cf36-49cb-8dbe-1f0948cd38ed",
"amount": 200,
"description": "RollBack",
"currencyCode": "USD",
"status": "FAILED",
"vendor": {
"name": "STRIPE"
},
"paymentDateUtc": "2026-03-21T00:25:45.206679Z",
"merchant": {
"id": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f",
"groupId": "3cf34cba-ffec-48fb-b7ca-09977db1077a",
"name": "ccg-optum-rx"
},
"paymentAllocations": [
{
"id": "99407904-f239-4a50-9968-c75fa4d99623",
"amount": 100,
"status": "FAILED",
"paymentMethod": {
// ... see API Spec for full paymentMethod object ...
},
"error": {
"title": "PAYMENT_ERROR",
"detail": "Your card was declined.",
"errorDetails": {
"code": "card_declined",
"message": "Your card was declined.",
"declineCode": "generic_decline",
"networkAdviceCode": null,
"networkDeclineCode": "01"
}
}
},
{
"id": "99407904-f239-4a50-9968-c75fa4d99623",
"amount": 100,
"status": "FAILED",
"paymentMethod": {
// ... see API Spec for full paymentMethod object ...
},
"error": {
"title": "PAYMENT_ERROR",
"detail": "Your card was declined.",
"errorDetails": {
"code": "card_declined",
"message": "Your card was declined.",
"declineCode": "generic_decline",
"networkAdviceCode": null,
"networkDeclineCode": "01"
}
}
}
],
"customer": {
"id": "5e395f2d-d000-4e79-9f33-ca68280bd2b6"
}
}
}
Rolled Back Response - One of the Allocation Failed during Processing
Sample Response — ROLLED_BACK
{
"title": "PAYMENT_ERROR",
"detail": "One of the split tender card payments failed during processing",
"status": 422,
"data": {
"id": "9cbecc7b-6401-4f1b-bc1d-c2769d466e7f",
"merchantTransactionId": "95b8da69-cf36-49cb-8dbe-1f0948cd38ed",
"amount": 200,
"description": "RollBack",
"currencyCode": "USD",
"status": "FAILED",
"vendor": { "name": "STRIPE" },
"paymentDateUtc": "2026-03-21T00:25:45.206679Z",
"merchant": {
"id": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f",
"groupId": "3cf34cba-ffec-48fb-b7ca-09977db1077a",
"name": "ccg-optum-rx"
},
"paymentAllocations": [
{
"id": "0da5b8ee-a75f-4468-8eaa-85c387251a7c",
"amount": 100,
"status": "ROLLED_BACK",
"paymentMethod": {
// ... see API Spec for full paymentMethod object ...
},
"remediation": {
"type": "CANCELLATION",
"message": "This paymentAllocation was canceled because another paymentAllocation failed. Split payments are atomic—all paymentAllocations must succeed"
}
},
{
"id": "99407904-f239-4a50-9968-c75fa4d99623",
"amount": 100,
"status": "FAILED",
"paymentMethod": {
// ... see API Spec for full paymentMethod object ...
},
"error": {
"title": "PAYMENT_ERROR",
"detail": "Your card was declined.",
"errorDetails": {
"code": "card_declined",
"message": "Your card was declined.",
"declineCode": "generic_decline",
"networkAdviceCode": null,
"networkDeclineCode": "01"
}
}
}
],
"customer": { "id": "5e395f2d-d000-4e79-9f33-ca68280bd2b6" }
}
}
Status Handling
How to Track Payment Status
Split tender payments are tracked at the parent transaction level. Each allocation (card) has its own status, but all polling and webhook notifications are provided at the parent level.
Polling Guidelines
- ✅ Supported:
- Poll the parent transaction using
paymentIdormerchantTransactionId. - Get real-time status for all allocations in a single response.
- See the complete transaction timeline and error details.
- Poll the parent transaction using
- ❌ Not Supported:
- Polling individual allocation (child) payment IDs.
- Partial status requests for specific allocations.
- Component-level webhook subscriptions.
Always use the parent transaction identifier for polling. Using child payment IDs will result in API errors.
How Parent Status is Determined
The parent payment status is derived from the statuses of all allocations:
- All allocations succeed: Parent status is
COMPLETED. - Any allocation fails: Parent status is
FAILED(successful allocations are rolled back automatically). - Processing: Parent status is
PENDINGwhile any allocation is still processing. - Weakest link: The most problematic allocation status determines the parent status.
Status Mapping Table
| Card 1 | Card 2 | Parent Status | Description |
|---|---|---|---|
| ⏳ PENDING | ⏳ PENDING | ⏳ PENDING | Both cards processing |
| ✅ COMPLETED | ✅ COMPLETED | ✅ COMPLETED | Transaction fully completed |
| ⏳ PENDING | ❌ FAILED | ⏳ PENDING | One card failed, rollback in progress |
| 🔄 ROLLED_BACK | ❌ FAILED | ❌ FAILED | Rollback completed |
| ❌ FAILED | ❌ FAILED | ❌ FAILED | Both cards failed |
CCG Internal Statuses
For SALE transactions, CCG internally processes as PRE-AUTH + CAPTURE (two-phase flow) to avoid refunds. The intermediate statuses below reflect this, but merchants only see the abstracted statuses: PENDING, COMPLETED, ROLLED_BACK, or FAILED.
These are the internal database statuses used within CCG. They are not exposed in merchant API responses — merchants see only the abstracted statuses above.
All Internal Status Combinations
| Card 1 Internal Status | Card 2 Internal Status | Parent Status | Internal Phase |
|---|---|---|---|
| AUTHORIZED | AUTHORIZED | PENDING | Both cards pre-authorized, capture not yet started |
| CAPTURE_INITIALIZED | AUTHORIZED | PENDING | Card 1 capture started |
| CAPTURE_INITIALIZED | CAPTURE_INITIALIZED | PENDING | Both cards capturing |
| COMPLETED | CAPTURE_INITIALIZED | PENDING | Card 1 captured, Card 2 still capturing |
| COMPLETED | COMPLETED | COMPLETED | Both cards captured — final state |
| AUTHORIZED | FAILED | PENDING | Card 2 auth failed, Card 1 awaiting cancel |
| CANCEL_INITIALIZED | FAILED | PENDING | Card 1 cancel in progress (rollback) |
| CANCELLED | FAILED | FAILED | Rollback complete — final state |
| FAILED | FAILED | FAILED | Both auth failed — final state |
Webhooks
CCG emits one webhook per parent transaction (not per allocation/card). Webhooks provide real-time status updates for split tender payments, allowing you to automate downstream actions and keep your system in sync.
Note: To configure webhooks, Refer Configuration guide
Webhook Event Summary
| Event | When Triggered |
|---|---|
PAYMENT_SUCCEEDED | Both cards completed successfully |
PAYMENT_FAILED | One or both cards failed |
PAYMENT_FAILED is not final if retries remain. It becomes final only after the 5th failure or when the merchant stops retrying. Always check the retry counter and parent status before taking irreversible actions.
Webhook Status Mapping (Internal)
| Card 1 | Card 2 | Transaction Status | Webhook Event | Card 1 Webhook Status | Card 2 Webhook Status |
|---|---|---|---|---|---|
| AUTHORIZED | AUTHORIZED | PENDING | — | — | — |
| COMPLETED | COMPLETED | COMPLETED | PAYMENT_SUCCEEDED | COMPLETED | COMPLETED |
| AUTHORIZED | FAILED | FAILED | PAYMENT_FAILED | ROLLED_BACK | FAILED |
| FAILED | FAILED | FAILED | PAYMENT_FAILED | FAILED | FAILED |
Error Handling
For the complete list of error codes, status codes, and error details, see Payment Error Codes.
Split Tender–Specific Errors
Beyond the standard payment errors, split tender has these use-case-specific failure modes:
- Partial failure → automatic rollback: When one card succeeds and the other fails, CCG cancels the successful card. The response includes
remediation.type: "CANCELLATION"on the rolled-back allocation. - 3DS in agent session: If either card triggers 3DS in an API or UI with agent flow, the entire payment fails — agents can't complete 3DS challenges.
Retry Handling
Split tender payments allow up to 5 attempts per merchantTransactionId (1 initial + 4 retries). The retry logic ensures atomicity and flexibility:
When to Retry
- Only retry if the parent payment status is
FAILEDand attempts remain.
How Many Retries Are Allowed
- You can retry up to 5 times per
merchantTransactionId(1 initial + 4 retries). - The retry counter increments automatically for each attempt with the same
merchantTransactionId.
Idempotency & Tracking
- All retries must use the same
merchantTransactionIdfor idempotency and tracking.
What Can Be Changed on Retry
- Each retry is all-or-nothing — you cannot retry just the failed card; both allocations are retried together.
- On each retry, you may change the cards and redistribute the amounts between them.
- After failure, you may switch to a single-card payment if desired.
After Max Retries
- After the 5th failure, the transaction is failed and no further retries are allowed for that
merchantTransactionId. New transaction should be initaited with newmerchantTransactionId
Retry & Rollback Flow
Retry & Rollback Flow (Internal)
When a split tender payment fails, CCG automatically rolls back the successful card. The merchant can then retry.
FAQ
Q: How do I correlate webhook events with my payment records?
- Use the parent
paymentIdormerchantTransactionIdprovided in both the API response and webhook payload to match events to your records.
Q: What should I do if a payment is stuck in PENDING?
- Continue polling the parent transaction. If the status does not resolve after a reasonable time, check for webhook delivery issues or contact support.
Q: Can I retry only the failed card in a split tender payment?
- No. All retries must include both allocations; split tender is all-or-nothing.
Q: How do I handle duplicate webhook events?
- Implement idempotency in your webhook handler by tracking processed event IDs or transaction IDs.
Q: What if I do not receive a webhook?
- Check your endpoint logs, firewall, and ensure your endpoint returns a 2xx status code. See Webhook Integration & Configuration for troubleshooting.
Q: When is PAYMENT_FAILED considered final?
- Only after the 5th failed attempt or when you stop retrying. Until then, retries are allowed.
Integration Checklist
- Merchant account is configured for split tender payments
- API endpoints and authentication are set up
- Payment request includes exactly two
paymentAllocationswith correct amounts - Error handling and remediation logic implemented
- Webhook endpoint is configured and tested
- Webhook handler is idempotent and logs all payloads
- Retry logic follows documented rules (max 5 attempts, all-or-nothing)
- All error codes and business rule violations are handled
- Integration tested with test cards and simulated failures