Migrate Convenient Checkout Gateway from V1 to V2
Use this how-to guide to plan, test, and release a V1-to-V2 migration while safely managing eligible V1-origin resources. Use the linked references for exact schemas, payloads, errors, and webhook contracts.
Before you beginβ
Before testing, review V2 API environment availability.
Last validated: 2026-08-26
Accountable owners: CCG Product and API owners
- V2 is available in production, subject to operation readiness and merchant enablement.
- Validate stage independently; do not infer stage status from production availability.
- Stubbed and deferred endpoints are not merchant-ready and must not be used.
Stop before testing or cutover if any in-scope endpoint is unavailable, deferred, not listed as production-ready in the environment matrix, not enabled for the merchant, or outside an approved rollout window. Confirm the current status with the accountable CCG Product or API owner before proceeding.
Confirm that:
- V2 and backward compatibility are enabled for your merchant where required.
- Every V1 operation in scope is Supported or Changed in the capability parity matrix.
- Every in-scope operation is explicitly marked production-ready; do not infer readiness from the general availability of V2.
- Credentials, merchant ID, environment-specific scope, required headers, support contacts, test data, and recovery owners are ready.
- V1 lifecycle dates and the parallel-operation period are agreed.
Use OAuth 2.0 client credentials and request only the scope for the target environment:
| Environment | API base URL | Merchant scope |
|---|---|---|
| Non-production | https://api-stg.uhg.com/api/financial/commerce/nonprodcheckout/v2 | financial/commerce/nonprodcheckout:merchant |
| Production | https://api.uhg.com/api/financial/commerce/checkout/v2 | financial/commerce/checkout:merchant |
Use Authorization and X-Merchant-Id on merchant requests. Add X-Upstream-Env in non-production and operation-specific headers where required. See API authorization for the complete header matrix, credential handling, and scopes.
Non-production request exampleβ
Obtain a token:
curl --request POST "https://api.uhg.com/oauth2/token" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "scope=financial/commerce/nonprodcheckout:merchant" \
--data-urlencode "client_id=<client-id>" \
--data-urlencode "client_secret=<client-secret>"
Retrieve a payment:
curl --request GET "https://api-stg.uhg.com/api/financial/commerce/nonprodcheckout/v2/payments/<payment-id>" \
--header "Authorization: Bearer <access-token>" \
--header "X-Merchant-Id: <merchant-id>" \
--header "X-Upstream-Env: dev"
Create a full linked refund:
curl --request POST "https://api-stg.uhg.com/api/financial/commerce/nonprodcheckout/v2/refunds" \
--header "Authorization: Bearer <access-token>" \
--header "X-Merchant-Id: <merchant-id>" \
--header "X-Upstream-Env: dev" \
--header "X-Source: <source>" \
--header "Content-Type: application/json" \
--data '{"paymentId":"<payment-id>","merchantTransactionId":"<new-unique-refund-id>","reason":"REQUESTED_BY_CUSTOMER"}'
Step 1: Choose the migration approachβ
| Approach | Use it when | Required control |
|---|---|---|
| Direct V2 cutover | No V1-origin resource needs later management | Complete contract, webhook, and recovery testing first |
| Parallel operation | V1-origin resources remain active while new V2 resources are created | Enable compatibility and route mixed webhook versions |
| Migrate before split tender | Contract stability is the first goal | Prove one-allocation V2 payments before adding split tender |
Default to migrating the existing single-card flow before introducing split tender. Record the selected approach, exclusions, rollback owner, and V1 retirement criteria.
Step 2: Inventory and map the V1 integrationβ
Inventory every API operation, session or widget flow, payment-method or token flow, 3DS action, webhook, stored field, report, reconciliation job, alert, and support tool that depends on V1.
For each operation, record its V1 path, V2 operation, readiness, contract status, cross-version support, environment availability, merchant action, and source. Use the operation-level capability parity matrix, V1 and V2 API comparison, Backward Compatibility, and V2 OpenAPI reference.
Compare the inventory to the V1 OpenAPI reference, not only to the rows already present in the matrix. If a V1 operation has no matrix row or approved V2 operation, record it as Unsupported/unconfirmed, name its owner and approved V1 fallback or exclusion, and stop that flow's migration until the accountable API owner adds an outcome.
Do not proceed while an in-scope operation is Deferred, Unsupported, unavailable in the target environment, or lacks an approved alternative.
Step 3: Update the application contractβ
Update request construction, response parsing, persistence, business rules, reports, reconciliation, logs, alerts, and tests.
| Concern | Required change |
|---|---|
| Payment request | Build paymentAllocations[]; validate allocation totals against the parent amount |
| Payment response | Parse the complete {url, data} envelope and iterate data.paymentAllocations[] |
| Refund response | Iterate data.refundAllocations[]; store parent and allocation IDs, amounts, statuses, and errors |
| Nested data | Replace assumptions based on flat customer, merchant, payment-method, and processor fields |
| V1-origin resource | Use the same V2 envelope parser as a V2-native resource |
Use the V2 OpenAPI reference for exact request and response schemas. Do not assume authentication, allocation handling, request construction, or webhook logic remains unchanged.
Step 4: Enable and verify backward compatibilityβ
Compatibility is one-directional: approved V2 operations can manage eligible V1-origin resources, but V1 operations cannot manage V2-native resources. A V1 operation attempted on a V2-native resource returns 409 VERSION_CONFLICT. Without compatibility enablement, a V1-origin GET through V2 normally returns 404, while a V2 cross-version write on a V1-origin resource returns 409 CROSS_VERSION_NOT_ENABLED.
For refunds on V1-origin payments:
- Full linked refund: include
paymentIdand a uniquemerchantTransactionId; omitrefundAllocations. - Partial linked refund: include
paymentId, a uniquemerchantTransactionId, and exactly onerefundAllocations[]item containingpaymentAllocationIdandamount. - A mismatched
paymentAllocationIdandpaymentIdrelationship returns422. - Every new refund request requires a unique
merchantTransactionId; reusing one returns422. It is not a replay key.
If a refund request times out, do not replay it with the same or a new ID. If a refundId was received, retrieve the refund. Otherwise, contact the CCG representative with the merchant ID, merchantTransactionId, timestamp, environment, and correlation ID to confirm whether the refund exists. Submit another refund only after the original outcome is confirmed and the recovery owner approves it; use a new unique merchantTransactionId only when it is confirmed that a new request is required.
Verify an existing V1 paymentβ
- Retrieve the V1-origin payment using
GET /v2/payments/{paymentId}. - Confirm the response uses the complete V2
{url, data}envelope. - Confirm
data.paymentAllocations[]contains one allocation. - Create the supported full or partial refund using
POST /v2/refunds. - Retrieve the refund using
GET /v2/refunds/{refundId}. - Verify the expected webhook version and deduplicate the event.
Use Backward Compatibility and the V2 OpenAPI reference for exact payloads.
Step 5: Update asynchronous, authentication and webhook handlingβ
- Treat
202as incomplete and inspect each allocation when207is supported. - Stop polling only at an approved terminal state or timeout threshold.
- Route V1 and V2 webhook payloads by the resource and operation rules in Backward Compatibility, not by arrival order.
- Because the webhook contract has no delivery ID, hash the canonical complete body with its version, event
name, and resource ID. Ignore an identical hash, upsert resource state idempotently, and retrieve the resource before an irreversible action when deliveries conflict or arrive out of order. - Expect V1 payment webhooks for supported actions on V1-origin payments and V2 refund webhooks for refunds created through V2.
- Never replay an uncertain payment or refund until its outcome is confirmed.
For 3DS, the customer-facing checkout application owns the challenge and calls PATCH /v2/payments/{paymentId}/allocations/{allocationId}/confirm; the CCG Widget handles this automatically in Widget flows. The merchant backend must not call it directly or as a recovery action. The operation uses financial/commerce/checkout:user in production or financial/commerce/nonprodcheckout:user in non-production. It requires Authorization, X-Merchant-Id, X-Checkout-Id, and X-Upstream-Env in non-production; X-Source is optional. See the User-scoped Confirm Payment Allocation operation and 3DS confirmation guide.
Step 6: Test the migrationβ
| Scenario | Expected result |
|---|---|
| Environment, token, and headers | Request reaches the intended available environment with its single environment scope |
| Inventory and contract | Every V1 operation has an approved V2 outcome; {url, data} and allocation arrays parse correctly |
| V1-origin retrieval | Enabled lookup succeeds; lookup without compatibility normally returns 404 |
| Cross-version errors | V1-on-V2 returns 409 VERSION_CONFLICT; disabled V2 write on V1 returns 409 CROSS_VERSION_NOT_ENABLED |
| Full and partial refund | Request shape, allocation relationship, status, retrieval, and webhook are correct |
| Duplicate refund ID | Reusing merchantTransactionId returns 422; no automatic replay occurs |
| Uncertain refund outcome | Lookup or support confirmation completes before any new request |
| Invalid allocation relationship | Mismatched paymentAllocationId returns 422 |
| 3DS | Customer-facing checkout uses the user scope and required headers; merchant backend does not confirm |
| Webhooks | V1 and V2 payloads validate, deduplicate, and tolerate retries and ordering changes |
| Deferred operation | Stub returns 501 and the migration stops for that in-scope operation |
For compatibility-disabled testing, use a known V1-origin resource before enablement: GET normally returns 404, and a supported V2 write returns 409 CROSS_VERSION_NOT_ENABLED. For deferred testing, call only the documented lower-environment stub with test data and expect 501; do not send a financial request merely to prove a route is deferred. Record the HTTP status, error code, correlation ID, and confirmation that no resource or side effect was created. Simulate client timeout after submission by withholding the response in the test client or proxy, then follow lookup or CCG confirmation without replaying the request.
Use payment errors, refund errors, and test data for detailed cases.
Step 7: Cut over and recover safelyβ
Before sending traffic, record approved numeric values for: ramp increments and observation duration; maximum V2 error rate; maximum pending age; minimum webhook processing success rate; maximum duplicate side effects (0); maximum unreconciled financial resources (0); and the sample size required at each increment. The merchant migration owner and accountable CCG Product or API owner must approve these values. A missing value or threshold breach pauses the ramp.
On a pause, stop new V2 traffic without replaying uncertain financial operations, preserve correlation IDs and payloads with sensitive data redacted, reconcile in-flight resources, and contact the CCG representative. Resume only after the failure is understood, affected resources are reconciled, and the rollback or recovery owner approves the next increment.
Migration troubleshootingβ
| Symptom | Required action |
|---|---|
409 VERSION_CONFLICT | A V1 operation was attempted on a V2-native resource. Use the V2 operation and verify its readiness in the parity and environment pages. |
409 CROSS_VERSION_NOT_ENABLED | A V2 cross-version write was attempted on a V1-origin resource without compatibility enablement. Enable compatibility before retrying a merchant-ready operation. |
404 for a V1-origin GET through V2 | Confirm merchant, environment, resource ID, and compatibility enablement. Without compatibility, this lookup normally returns 404. |
| Refund allocation does not belong to the supplied payment | Correct the paymentAllocationId and paymentId relationship. The documented response is 422. |
Duplicate merchantTransactionId returns 422 | Do not replay the refund. Confirm the existing refund outcome before creating any new request. |
Refund request timed out and no refundId was received | Contact the CCG representative with the merchant ID, merchantTransactionId, environment, timestamp, and correlation ID. Do not submit another refund until the outcome is confirmed. |
Refund request timed out after a refundId was received | Retrieve GET /v2/refunds/{refundId} and reconcile the result before taking another action. |
Send production traffic only after every in-scope operation is confirmed production-ready and enabled for the merchant. Retire V1 only when every inventoried operation has an approved V2 outcome or exclusion; V1 traffic is zero; no V1-origin resource still requires an unsupported V2 action; the approved observation period completes within every rollout threshold; duplicate side effects and unreconciled financial resources remain at zero; webhook delivery and reconciliation meet their approved targets; rollback is no longer required; and merchant and CCG owners sign off. Keep V1 available until every gate is recorded as passed.
Verify completionβ
- Every inventoried V1 operation has an approved V2 outcome or exclusion.
- Target environments and endpoints are available and compatibility is enabled where needed.
- Requests use the correct host, environment scope, and operation-specific headers.
- Parsers and downstream consumers support the V2 envelope and allocation arrays.
- Full and partial V1-origin refunds, duplicate IDs, and uncertain outcomes are handled safely.
- V1 and V2 webhooks are routed and deduplicated.
- The checkout-owned 3DS flow uses the user operation, scope, and headers.
- Monitoring, pause, escalation, recovery, and V1 retirement criteria are approved.
Stop and resolve before go-liveβ
Stop if an in-scope operation is Deferred, Unsupported, unavailable, undocumented, not production-ready, or not enabled for the merchant; compatibility or webhook behavior is unconfirmed; a financial reconciliation gap is open; or the guide and OpenAPI disagree. Resolve the issue with the accountable product or API owner before continuing.
Related documentationβ
Explanation: Why migrate to V2
How-to: Backward Compatibility, API authorization, 3DS confirmation, webhook configuration, webhook retries
Reference: Capability parity matrix, V2 environment availability, V2 changelog, API comparison, V2 OpenAPI, V1 payment webhooks, V2 webhooks