Skip to main content
Version: v2

Split Tender Refunds API

DRAFT VERSION

Split Tender Refund allows merchants to refund a split tender transaction β€” either in full across both cards, or partially against a specific card. Each card refund is processed independently. This guide covers how to initiate refunds, monitor status, and handle errors and webhook notifications.

Single-card refunds? If the original payment used only one allocation, standard refund processing applies β€” see Refunds. No PARTIAL_SUCCESS or per-allocation logic is involved.

  • Refund model: Full Refund and Per-payment allocation β€” each allocation is refunded independently
  • Partial refunds: Supported per card; merchant must specify card and amount
  • No rollback on partial failure: Unlike payments, if one card refund fails, the other is not reversed β€” result is PARTIAL_SUCCESS
  • Status: Rolled up from both card refund statuses

When to Use​

Use Split Tender Refund when:

  • The original payment was a split tender transaction (two cards charged)
    • A full refund is needed β€” both cards should be refunded for their original amounts
    • A partial refund is needed β€” a specific card should be refunded for a specific amount
    • A subsequent refund is needed after a previous partial refund on the same transaction

Note: Each refund allocation is processed independently. If one card fails (e.g., expired or disputed), the other card's refund still proceeds, resulting in PARTIAL_SUCCESS. Unlike payments, there is no automatic rollback.

Business Context

For business context, eligibility, scenarios see Split Tender Refunds β€” Business Document.


Integration Details
  • API Endpoint:
    • POST v2/refunds β€” create a refund
    • GET v2/refunds/{refundId} β€” poll refund status
  • Scope: All operations are at the merchant scope.
  • API Specs: Create Refund | Get Refund
  • Webhook Specs: Refund Webhooks
  • API: v2
  • Testing: Use Test Cards to simulate refund scenarios. See Test Cards
  • ⚠️ Error Handling: See Refund Error Codes for split tender–specific errors and resolution steps

Refund Processing Flow​

Both cards are refunded independently. If one card fails, the other still proceeds, resulting in a PARTIAL_SUCCESS.

Refund Flow

Refund Processing Steps:

  1. Request received β€” CCG receives split tender refund request.
  2. Eligibility check β€” CCG validates refund eligibility for each card.
  3. Independent processing β€” Each card refund is processed separately (in parallel).
  4. Outcome determination:
    • Both succeed β†’ REFUND_SUCCESS webhook (status: COMPLETED)
    • One fails β†’ REFUND_PARTIAL_SUCCESS webhook (status: PARTIAL_SUCCESS)
    • Both fail β†’ REFUND_FAILED webhook (status: FAILED)
  5. Webhook β€” Single event emitted at parent refund level, not per card.
  6. Manual action (if needed) β€” For PARTIAL_SUCCESS, merchant may need to resolve failed allocation manually.

API Request​

API Specification
Sample Request β€” POST v2/refunds
curl -X POST "https://api-stg.uhg.com/api/financial/commerce/checkout/v2/refunds" \
-H "Authorization: <token>" \
-H "X-Merchant-Id: <merchantId>" \
-H "Content-Type: application/json" \
-d '{
"reason": "REQUESTED_BY_CUSTOMER",
"paymentId": "620bba81-10d1-4abc-9e4a-c2c1752da349",
"merchantTransactionId": "28402815-8048-4725-b092-809f5fd28470",
"refundAllocations": [
{
"paymentAllocationId": "1ff243ca-e0f3-436f-a5b2-53c205715619",
"amount": 1000
},
{
"paymentAllocationId": "37cf8ca3-4e54-4599-ab36-2c9fd2ad42f4",
"amount": 100
}
]
}'

Poll Refund Status​

Refund processing is asynchronous β€” the API returns INITIATED immediately. Poll GET v2/refunds/{refundId} until the status settles to a terminal state (COMPLETED, PARTIAL_SUCCESS, or FAILED).

Sample Request β€” GET v2/refunds/refundId
curl -X GET "https://api-stg.uhg.com/api/financial/commerce/checkout/v2/refunds/<refundId>" \
-H "Authorization: <token>" \
-H "X-Merchant-Id: <merchantId>" \
-H "Accept: application/json"

API Response​

Response Structure

The complex object is trimmed for brevity. Refer to the API Spec for the full response structure.

understanding refund API Response
  • The refundAllocations array contains one entry per card, each with its own status and error details if failed.
  • The paymentMethod object in each allocation is trimmed for brevity (only id, type, last4, and cardBrand shown).
  • The parent refund status is computed from both allocations (see Status Handling).
  • For PARTIAL_SUCCESS, check each allocation for error details and take manual action if needed.
  • Top-level status is always the parent refund status; allocation statuses may differ.
  • For all error codes and troubleshooting, see Refund Error Codes.

Final Response β€” COMPLETED​

Sample Response β€” COMPLETED (HTTP 200)
{
"url": "http://api-stg.uhg.com:443/v2/refunds/be7149e8-f286-4c7d-a0ea-a715966965ae",
"data": {
"id": "be7149e8-f286-4c7d-a0ea-a715966965ae",
"status": "COMPLETED",
"reason": "DUPLICATE",
"merchantTransactionId": "e4a42f8b-c668-4fb8-ae09-47c80e0f3cd5",
"metadata": {},
"payment": {
"amount": 1000,
"capturedAmount": 1000,
"authorizedAmount": 0,
"description": "PartialSplitRefundSuccess",
"id": "4266d148-7406-441e-a523-9a2ac81b39d5",
"merchantTransactionId": "9104ea96-5afb-4ea9-a40a-09661afdd706",
"paymentDateUtc": "2026-03-20T00:28:05"
},
"merchant": {
"id": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f"
},
"refundAllocations": [
{
"id": "6f90c43d-7904-4744-9741-290a93b9bb95",
"amount": 150,
"status": "COMPLETED",
"paymentAllocation": {
"id": "ae59cc49-56cc-436c-a99f-4520c82d6263",
"paymentMethod": {
// refer complete payment method in API Spec
}
}
},
{
"id": "f1e46dee-65b2-4d54-a42b-75f7125dbd1b",
"amount": 100,
"status": "COMPLETED",
"paymentAllocation": {
"id": "f7b29404-497c-4447-910b-be25c8eb19e1",
"paymentMethod": {
// refer complete payment method in API Spec
}
}
}
]
}
}

Final Response β€” PARTIAL_SUCCESS​

Sample Response β€” PARTIAL_SUCCESS (HTTP 207)
{
"url": "http://api-stg.uhg.com:443/v2/refunds/6ddd4069-2096-48f1-8394-1fe13953aa5f",
"data": {
"id": "6ddd4069-2096-48f1-8394-1fe13953aa5f",
"status": "PARTIAL_SUCCESS",
"reason": "REQUESTED_BY_CUSTOMER",
"merchantTransactionId": "28402815-8048-4725-b092-809f5fd28470",
"metadata": {},
"payment": {
"amount": 2000,
"capturedAmount": 2000,
"authorizedAmount": 0,
"description": "PartialSplitRefundSuccess",
"id": "620bba81-10d1-4abc-9e4a-c2c1752da349",
"merchantTransactionId": "3c5c2899-fe71-44d8-be9b-bd520e5e6539",
"paymentDateUtc": "2026-03-20T00:27:36"
},
"merchant": {
"id": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f"
},
"refundAllocations": [
{
"id": "f56b5fb8-578b-4ea2-bcf2-66bb4350a6aa",
"amount": 100,
"status": "COMPLETED",
"paymentAllocation": {
"id": "37cf8ca3-4e54-4599-ab36-2c9fd2ad42f4",
"paymentMethod": {
// refer complete payment method in API Spec
}
}
},
{
"id": "b1cc58d4-0e0c-4388-a92f-1869fccb7667",
"amount": 1000,
"status": "FAILED",
"error": {
"title": "REFUND_ERROR",
"detail": "A previous attempt to refund this payment has failed"
},
"paymentAllocation": {
"id": "1ff243ca-e0f3-436f-a5b2-53c205715619",
"paymentMethod": {
// refer complete payment method in API Spec
}
}
}
]
}
}

Final Response β€” FAILED (HTTP 422)​

Sample Response β€” FAILED (HTTP 422)
{
"title": "REFUND_ERROR",
"detail": "Refund allocation processing failed for all records. Check individual records for error details",
"status": 422,
"refund": {
"id": "2aa92c90-3d54-4fb1-8655-19b18eb2e13c",
"status": "FAILED",
"reason": "DUPLICATE",
"merchantTransactionId": "bbc12922-9ebb-4647-869a-63a29e90ddfa",
"metadata": {
"orderId": "123496",
"purpose": "failedRefund"
},
"payment": {
"amount": 1000,
"capturedAmount": 1000,
"authorizedAmount": 0,
"description": "PartialSplitRefundSuccess",
"id": "4266d148-7406-441e-a523-9a2ac81b39d5",
"merchantTransactionId": "9104ea96-5afb-4ea9-a40a-09661afdd706",
"paymentDateUtc": "2026-03-20T00:28:05"
},
"merchant": {
"id": "b955db5e-aef2-47de-bbb9-c80b9cc16e8f"
},
"refundAllocations": [
{
"id": "3d62cf0c-89ea-424d-a637-aa887c5b9fab",
"amount": 2,
"status": "FAILED",
"error": {
"title": "REFUND_ERROR",
"detail": "This payment is already refunded"
},
"paymentAllocation": {
"id": "ae59cc49-56cc-436c-a99f-4520c82d6263",
"paymentMethod": {
// refer complete payment method in API Spec
}
}
},
{
"id": "b7316cf6-8c00-4d30-bcca-a5874e114efe",
"amount": 1,
"status": "FAILED",
"error": {
"title": "REFUND_ERROR",
"detail": "This payment is already refunded"
},
"paymentAllocation": {
"id": "f7b29404-497c-4447-910b-be25c8eb19e1",
"paymentMethod": {
// refer complete payment method in API Spec
}
}
}
]
}
}

Status Handling​

Polling Guidelines​

Refund processing is asynchronous β€” the API returns INITIATED immediately. Poll GET v2/refunds/{refundId} until the status settles to a terminal state (COMPLETED, PARTIAL_SUCCESS, or FAILED).

  • βœ… Supported:
    • Poll the parent refund using refundId only.
    • Get real-time status for all allocations in a single response.
    • See per-card error details when a card refund fails.
  • ❌ Not Supported:
    • Polling by merchantTransactionId (not available).
    • Polling individual allocation (child) refund IDs.
    • Component-level webhook subscriptions per card.
warning

Always use the parent refund refundId for polling. Polling by merchantTransactionId or allocation IDs is not supported and will result in API errors.


Status Definitions​

Both the parent refund and each allocation (card) carry their own status. The parent status is computed from both allocation outcomes.

Refund Status Definitions
LevelStatusDescription
Parent RefundINITIATEDRefund request accepted; async validation and submission started
PENDINGAt least one allocation is being processed by the payment processor
COMPLETEDAll allocations succeeded; total refund amount returned
FAILEDAll allocations failed; no funds returned
PARTIAL_SUCCESSOne allocation succeeded, one failed β€” no rollback
Allocation (R1/R2)INITIATEDAllocation created; async validation and submission to processor started
PENDINGProcessor is actively processing this allocation
COMPLETEDAllocation processed successfully; funds returned to card
FAILEDAllocation failed; no funds returned for this card

Note: PENDING is optional β€” allocations may transition directly from INITIATED β†’ COMPLETED or INITIATED β†’ FAILED.


Status Mapping: R1 + R2 β†’ Parent​

R1 (Card 1)R2 (Card 2)Parent StatusDescription
⏳ INITIATED⏳ INITIATED⏳ INITIATEDBoth allocations queued for processing
⏳ PENDING⏳ PENDING⏳ PENDINGBoth allocations actively processing
βœ… COMPLETEDβœ… COMPLETEDβœ… COMPLETEDAll funds returned to both cards
βœ… COMPLETED❌ FAILED⚠️ PARTIAL_SUCCESSOne allocation refunded; one failed β€” no rollback
❌ FAILEDβœ… COMPLETED⚠️ PARTIAL_SUCCESSOne allocation refunded; one failed β€” no rollback
❌ FAILED❌ FAILED❌ FAILEDNo funds returned

What is PARTIAL_SUCCESS?​

When one card refund succeeds and the other fails (e.g., expired, closed, or disputed card, or transient processor error), the parent status is PARTIAL_SUCCESS. The successful refund is not rolled back. The failed allocation includes an error object with details. Review the refundAllocations array, communicate with the customer if needed, and issue a manual/unlinked refund for the failed card if required. See Partial Success Refund Response. Refer Recovery Steps for handling.


Status Lifecycle​

Allocation Lifecycle​

Each refund allocation independently moves through these states:

Overall Refund Lifecycle​

The parent status is derived from both allocation outcomes once processing completes:


Error Handling​

Error Code Reference

For the complete list of error codes, status codes, and error details, see Refund Error Codes.

Split Tender–Specific Errors​

Beyond standard refund errors, split tender refunds have these specific failure modes:

  • Partial failure β€” no rollback: Unlike payments, if one card refund fails, the other card's refund is not rolled back. The result is PARTIAL_SUCCESS and the merchant must resolve the failed allocation separately.
  • Already refunded: If a paymentAllocationId has already been fully refunded, the allocation returns REFUND_ERROR: This payment is already refunded.
  • Expired or closed card: Refunds to expired or closed cards fail at the processor level β€” the other card's refund still proceeds, resulting in PARTIAL_SUCCESS.

Webhooks​

CCG sends one webhook per refund request β€” not per card. Subscribe to these events to get real-time refund outcomes without polling.

Webhook Events​

Webhook Spec
EventTriggerNotes
REFUND_SUCCESSBoth cards refunded successfullyRefund fully settled
REFUND_PARTIAL_SUCCESSOne payment refunded, one failedCheck per-refund allocation status
REFUND_FAILEDAll cards failed to refundNo funds returned

Webhook Status Mapping (Internal)​

Card 1Card 2Refund StatusWebhook Event
COMPLETEDCOMPLETEDCOMPLETEDREFUND_SUCCESS
COMPLETEDFAILEDPARTIAL_SUCCESSREFUND_PARTIAL_SUCCESS
FAILEDCOMPLETEDPARTIAL_SUCCESSREFUND_PARTIAL_SUCCESS
FAILEDFAILEDFAILEDREFUND_FAILED

Stripe(Payment Processor) Considerations​

  • Timing: Card refunds typically process in 1–3 business days; each card may complete at different times depending on issuer
  • Stripe allows refunds on payments for 180 days

Retry Handling​

Split tender refunds do not support retries in the same way as payments. Each refund request is a distinct operation:

  • Full refund: Submit once; cannot re-submit if both cards are fully refunded.
  • Partial failure (PARTIAL_SUCCESS): The failed refund allocation is not retried automatically. The merchant must handle this manually:
    • Submit a new individual refund request for only the failed payment using its paymentAllocationId.
    • Consider issuing an unlinked/manual refund for the failed card if the card is expired, closed, or disputed.
  • Already refunded: If a paymentAllocationId has already been fully refunded, it cannot be refunded again β€” submitting will result in REFUND_ERROR: This payment is already refunded.

Recovery Steps for PARTIAL_SUCCESS​

SituationAction
Card expired/closedIssue unlinked refund or manual credit outside CCG
Card disputedRefund the undisputed card only; contact support for disputed card
Processor error (transient)Retry only the failed allocation with a new merchantTransactionId
Already refundedAdjust the refund amount to the remaining balance

V1 vs V2 API Comparison​

Split Tender Refund is a new capability in v2 that extends the single-payment refund model. This section helps to understand the differences and migrate from v1 to v2.

Downloadable Field-by-Field Comparison​

A comprehensive comparison workbook is available for detailed field mapping:

πŸ“₯ Refund Payload Comparison v1 to v2

What's included:

TabCoverage
RequestPOST /refunds (v1) vs POST /v2/refunds (v2) β€” all request fields
Response (Success)Success responses with all mandatory and optional fields
Response (Error)Error responses by HTTP status: 400, 401, 403, 404, 422, 500
Webhook (Success)REFUND_PENDING, REFUND_SUCCESS, REFUND_PARTIAL_SUCCESS events
Webhook (Error)REFUND_FAILED event payload
How to read the workbook

Each tab displays v1 JSON on the left and v2 JSON on the right. Field values use type labels (string, int, boolean, null, array, object) rather than actual data.

BadgeMeaning
πŸ”΄ <REMOVED>Field exists in v1 but not in v2
🟒 <ADDED>Field exists in v2 but not in v1
No badgeField exists in both versions

What to look for:

  • πŸ”΄ Removed fields β€” Stop sending these in v2 requests
  • 🟒 Added fields β€” New split tender capabilities (e.g., refundAllocations array)
  • Nested structure changes β€” v2 may nest or flatten fields differently
  • Type changes β€” Field may exist in both but with different type or format

Version-Specific Documentation​


FAQ​

Q: How do I refund only one card in a split tender transaction?

  • Specify the paymentAllocationId of the card to refund along with the amount in refundAllocations. Omit the other allocation.

Q: What happens if one card refund fails?

  • The other card's refund still proceeds. The result is PARTIAL_SUCCESS β€” unlike payments, there is no rollback. Handle the failed allocation manually.

Q: Can I refund the full amount without specifying allocations?

  • Yes. Omit refundAllocations entirely for a full refund. CCG will refund both cards for their original amounts.

Q: Can I poll individual allocation status?

  • No. Always use the parent refundId or merchantTransactionId to poll. Individual allocation IDs are not queryable directly.

Q: When is a refund status final?

  • COMPLETED, PARTIAL_SUCCESS, and FAILED are all terminal states. INITIATED and PENDING are transient.

Q: How do I correlate webhook events with refund records?

  • Use the refundId or merchantTransactionId in both the API response and webhook payload to match events.

Q: What if I don't receive a webhook?

  • Poll GET v2/refunds/{refundId} to get the current status. Check your endpoint logs and ensure it returns a 2xx status. See Webhook Configuration.

Integration Checklist​

  • Merchant account is configured for split tender payments
  • API endpoints and authentication are set up
  • Refund request specifies correct paymentId and merchantTransactionId
  • For partial refunds: refundAllocations includes paymentAllocationId and amount
  • Status polling uses parent refundId β€” not allocation IDs
  • PARTIAL_SUCCESS handling is implemented (manual resolution for failed card)
  • Webhook endpoint is configured and returns 2xx
  • Webhook handler is idempotent and logs all payloads
  • All error codes and PARTIAL_SUCCESS scenarios are handled
  • Integration tested with test cards and simulated partial failures