Vendor Configuration — Session Contract
🔒 InternalInternal information — not visible in the public (merchant) site.
Overview
V2 checkout sessions support scoping to a specific payment processor, payment method type, and set of channels via config.vendors[]. At creation time the platform resolves the merchant’s processor configuration and embeds a client-safe snapshot — vendorConfigs — directly in the session. This snapshot drives SDK initialization on the frontend and is the authoritative record of which processor handles the transaction.
Key characteristics:
vendorConfigsis resolved once at session creation and never changes- Vendor-incompatible channels (TEXT, EMAIL) are stripped from
vendorConfigsfor non-agent sessions; WEBFORM is always added for non-agent sessions - Legacy sessions (V1 or pre-migration V2) have
vendorConfigs: null— they usepaymentMethodChannelinstead config.vendors[]is stored internally for child session inheritance but is not returned in the API response
Session Scoping (config.vendors[])
The vendors filter is optional. When present, only the specified processors, payment method types, and channels are active for the session. All scoping decisions are final at creation time.
| Input | Result |
|---|---|
vendors omitted | Full merchant configuration used — all enabled processors and channels |
vendors: [] (empty array) | Same as omitted |
| Vendor not in merchant config | Silently excluded from vendorConfigs; session still created |
| Channel not supported by vendor | Silently excluded; no error |
| TEXT or EMAIL in channel list | Non-agent: stripped; WEBFORM added as fallback. Agent: TEXT/EMAIL may appear in vendorConfigs for both CARD and BANK_ACCOUNT |
| Filter leaves no valid channels | Session created; vendorConfigs: [] (empty, not null) |
vendor field missing on an entry | 400 Bad Request — rejected at validation |
Unrecognized vendor value | 400 Bad Request — rejected at deserialization |
WEBFORM Default
WEBFORM is always included in vendorConfigs for a payment method type when the processor supports it — even if not explicitly listed in config.vendors[].channels. This applies to non-agent sessions only.
Vendor-compatible channels per payment method type:
| Type | Vendor SDK channels |
|---|---|
CARD | WEBFORM, GOOGLE_PAY, APPLE_PAY |
BANK_ACCOUNT | WEBFORM |
Agent sessions do not get WEBFORM force-added. Agent-restricted channels (GOOGLE_PAY, APPLE_PAY) are always removed. Agents receive exactly the channels they are authorized for.
Sample Request
Restrict to STRIPE CARD — APPLE_PAY only
curl -X POST "https://api-stg.uhg.com/api/financial/commerce/nonprodcheckout/v2/sessions" \
-H "Authorization: Bearer <token>" \
-H "X-Merchant-Id: <merchant-id>" \
-H "Content-Type: application/json" \
-d '{
"customer": { "hsid": "<customer-hsid>" },
"payment": {
"merchantTransactionId": "<unique-id>",
"amount": 1500,
"authorizeCard": false
},
"config": {
"modes": ["PAYMENT"],
"vendors": [
{
"vendor": "STRIPE",
"paymentMethods": [
{ "type": "CARD", "channels": ["APPLE_PAY"] }
]
}
]
}
}'
Restrict to STRIPE BANK_ACCOUNT
curl -X POST "https://api-stg.uhg.com/api/financial/commerce/nonprodcheckout/v2/sessions" \
-H "Authorization: Bearer <token>" \
-H "X-Merchant-Id: <merchant-id>" \
-H "Content-Type: application/json" \
-d '{
"customer": { "hsid": "<customer-hsid>" },
"payment": {
"merchantTransactionId": "<unique-id>",
"amount": 1500,
"authorizeCard": false
},
"config": {
"modes": ["PAYMENT"],
"vendors": [
{
"vendor": "STRIPE",
"paymentMethods": [
{ "type": "BANK_ACCOUNT", "channels": ["WEBFORM", "TEXT"] }
]
}
]
}
}'
Sample Response
vendorConfigs is embedded in checkoutRequest.config. WEBFORM is always present for non-agent sessions when supported by the processor.
{
"data": {
"checkoutSession": {
"id": "d561f8bf-d569-4db4-b3b2-1470a04df874",
"checkoutRequest": {
"config": {
"modes": ["PAYMENT"],
"paymentMethodChannel": {
"cardChannels": [
{ "type": "APPLE_PAY", "order": 0, "enabled": true },
{ "type": "WEBFORM", "order": 1, "enabled": true }
],
"card": ["APPLE_PAY", "WEBFORM"]
},
"vendorConfigs": [
{
"vendor": "STRIPE",
"vendorMerchantId": "acct_xxxxxxxxxxxx",
"clientConfig": { "publicKey": "pk_live_xxxxxxxxxxxx" },
"paymentMethods": [
{
"type": "CARD",
"channels": [
{ "type": "APPLE_PAY", "order": 0, "enabled": true },
{ "type": "WEBFORM", "order": 1, "enabled": true }
]
}
]
}
]
}
},
"checkoutSessionStatus": "INITIATED"
}
}
}
vendorConfigs contains only client-safe fields. Secret keys, requestedVendorConfigs, and internal storage fields are never returned.
Legacy Sessions (vendorConfigs: null)
Sessions created before V2 vendor config support, or on pre-migration merchant accounts, have vendorConfigs absent from the response. The paymentMethodChannel field applies instead.
vendorConfigs value | Meaning |
|---|---|
| Absent (null) | V1 or pre-migration session — use paymentMethodChannel |
[] (empty array) | V2 session — vendor filter applied, no processor matched |
[...] (non-empty) | V2 session — processor resolved successfully |
A V2 session with vendorConfigs: [] does not fall back to paymentMethodChannel. The empty result is authoritative — no payment can proceed.
Client-Safe Fields in vendorConfigs
All fields in vendorConfigs are safe to pass to the frontend. Nothing server-only is included.
| Field | Description |
|---|---|
vendor | Processor identifier (STRIPE, OPTUM_BANK) |
vendorMerchantId | Merchant’s account ID with the processor |
clientConfig.publicKey | Processor’s publishable API key for SDK initialization |
paymentMethods[].type | Payment method type (CARD, BANK_ACCOUNT) |
paymentMethods[].channels[].type | Channel (WEBFORM, APPLE_PAY, GOOGLE_PAY) |
paymentMethods[].channels[].enabled | Whether the channel is active |
paymentMethods[].channels[].order | Display order |
Deterministic Processor Routing
- The processor is selected once at session creation and does not change
CARDroutes to STRIPE;BANK_ACCOUNTroutes to STRIPE (ACH) or OPTUM_BANK (FSA/HSA);HEALTH_SAVINGS_ACCOUNTroutes to OPTUM_BANK exclusively- If the processor is unavailable at payment time, the transaction fails — there is no automatic fallback or re-routing
- Create a new session to retry with a different scope
Child Session Inheritance
Child sessions (TEXT_TO_PAY, EMAIL_TO_PAY, TEXT_TO_ADD, EMAIL_TO_ADD) start from the parent’s vendorConfigs and apply additional filtering.
| Child mode | Channels permitted in vendorConfigs |
|---|---|
TEXT_TO_PAY / EMAIL_TO_PAY | WEBFORM, GOOGLE_PAY, APPLE_PAY |
TEXT_TO_ADD / EMAIL_TO_ADD | WEBFORM only |
Payment method types are further restricted to those for which the merchant has the delivery channel (TEXT or EMAIL) configured.
Agent sessions: When a parent agent session’s vendorConfigs contains only WEBFORM (because APPLE_PAY was agent-restricted), child sessions can still surface APPLE_PAY for the customer. The original config.vendors[] is stored internally on the parent session and is used to recover the originally-requested wallet channels during child session creation.
Error Reference
These errors are returned by GET /v2/checkout-sessions/{id} when the session has no usable payment methods. All return 422 Unprocessable Entity.
| Scenario | Error message |
|---|---|
| None of the requested vendors are configured for this merchant | "The requested vendor is not configured or supported for this merchant" |
| Vendor matched but payment method type not supported | "Payment method type is not configured." |
| Vendor + type matched but no channels survived the filter | "No supported channels remain after applying the requested vendor filter" |
| General — no capture methods available | "You have no capture methods available." |
Sample — 422 No capture methods
{
"title": "UNPROCESSABLE_ENTITY",
"status": 422,
"detail": "You have no capture methods available."
}
Related Code
wallet-auth-service—CreateSessionCommandHandler,VendorConfigResolver,CreateChildCheckoutSessionCommandHandler,GetCheckoutSessionCommandHandlerCheckoutSessionConfig— API-visible DTO containingvendorConfigsSessionConfig— Internal DTO storingrequestedVendorConfigsin therequest_configJSONB column