Skip to main content
Version: v2

Split Tender Payments API

DRAFT VERSION

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 merchantTransactionId can 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.

Business Context

For business rules, scenarios, and lifecycle details, see Split Tender Payments — Business Guide.


Integration Details

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:

  1. Request received — CCG receives split tender payment request
  2. Pre-validation — Both payment methods validated before calling Stripe
  3. Parallel processing — Both cards charged simultaneously (pre-auth)
  4. Outcome determination — Based on success/failure of each card
  5. Action orchestration:
    • Both succeed → auto-capture both cards
    • One fails → cancel the successful card (rollback)
    • Both fail → return failure immediately
  6. Webhook — Single event emitted at parent transaction level

API Request

API Specification

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

Response Structure

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

Understanding Failure Responses

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 remediation object 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 error object within each failed allocation. This contains the error code, message, and additional error.details from 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 paymentId or merchantTransactionId.
    • Get real-time status for all allocations in a single response.
    • See the complete transaction timeline and error details.
  • Not Supported:
    • Polling individual allocation (child) payment IDs.
    • Partial status requests for specific allocations.
    • Component-level webhook subscriptions.
warning

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 PENDING while any allocation is still processing.
  • Weakest link: The most problematic allocation status determines the parent status.

Status Mapping Table

Card 1Card 2Parent StatusDescription
⏳ PENDING⏳ PENDING⏳ PENDINGBoth cards processing
✅ COMPLETED✅ COMPLETED✅ COMPLETEDTransaction fully completed
⏳ PENDING❌ FAILED⏳ PENDINGOne card failed, rollback in progress
🔄 ROLLED_BACK❌ FAILED❌ FAILEDRollback completed
❌ FAILED❌ FAILED❌ FAILEDBoth cards failed

CCG Internal Statuses

important

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 StatusCard 2 Internal StatusParent StatusInternal Phase
AUTHORIZEDAUTHORIZEDPENDINGBoth cards pre-authorized, capture not yet started
CAPTURE_INITIALIZEDAUTHORIZEDPENDINGCard 1 capture started
CAPTURE_INITIALIZEDCAPTURE_INITIALIZEDPENDINGBoth cards capturing
COMPLETEDCAPTURE_INITIALIZEDPENDINGCard 1 captured, Card 2 still capturing
COMPLETEDCOMPLETEDCOMPLETEDBoth cards captured — final state
AUTHORIZEDFAILEDPENDINGCard 2 auth failed, Card 1 awaiting cancel
CANCEL_INITIALIZEDFAILEDPENDINGCard 1 cancel in progress (rollback)
CANCELLEDFAILEDFAILEDRollback complete — final state
FAILEDFAILEDFAILEDBoth 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

Webhook Payload Structure
EventWhen Triggered
PAYMENT_SUCCEEDEDBoth cards completed successfully
PAYMENT_FAILEDOne or both cards failed
caution

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 1Card 2Transaction StatusWebhook EventCard 1 Webhook StatusCard 2 Webhook Status
AUTHORIZEDAUTHORIZEDPENDING
COMPLETEDCOMPLETEDCOMPLETEDPAYMENT_SUCCEEDEDCOMPLETEDCOMPLETED
AUTHORIZEDFAILEDFAILEDPAYMENT_FAILEDROLLED_BACKFAILED
FAILEDFAILEDFAILEDPAYMENT_FAILEDFAILEDFAILED

Error Handling

Error Code Reference

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 FAILED and 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 merchantTransactionId for 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 new merchantTransactionId

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 paymentId or merchantTransactionId provided 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?

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 paymentAllocations with 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