# Forward stored credentials Use the [Forward API](https://api-reference.checkout.com/#tag/Forward) to enrich a payment request with payment credentials you've stored in the [Checkout.com Vault](https://www.checkout.com/docs/payments/accept-payments/accept-a-payment-on-your-website/extend-your-flow-integration/accept-payments-with-stored-card-credentials.md). You can enrich a payment with: - The billing information stored in a payment instrument or token - The card details stored in an instrument or token - The details of a network token - The [metadata](#Metadata_placeholders) you've stored with a payment instrument or token The request is then securely forwarded to your specified third-party API endpoint. For example, you can forward the request to external fraud engines, [3D Secure (3DS)](https://www.checkout.com/docs/payments/authenticate-payments/3d-secure.md) engines, and other payment services providers (PSPs) such as [Stripe](#Stripe_forwarding_example) or [Adyen](#Adyen_forwarding_example). > **Information** > > To enable forwarding, contact your account manager or [request support](https://dashboard.checkout.com/support/new?topic=case_account_management_and_access&issue=issue_account_changes). This enables you to: - Store payment credentials in a single location and use them across multiple PSPs, while remaining [PCI compliant](https://www.checkout.com/docs/business-operations/ensure-regulatory-compliance/pci-compliance.md). - Provide additional data to third-party fraud engines. - Extract stored payment credentials from one client's vault and forward them to another client's vault, if you have a multi-client account structure. - Provide card-linked offers to an issuer or card network. > **Information** > > You must ensure that the third-party endpoint you forward the request to belongs to a PCI-compliant entity. --- ## Retrieve JSON Web Key You need Checkout.com's JSON Web Key (JWK) to encrypt sensitive request headers using JSON Web Encryption (JWE). In your terminal, run the following command to retrieve our JWK: ```curl curl --location 'https://forward.checkout.com/.well-known/jwks' \ --header 'Content-Type: application/json' \ ``` --- ## Retrieve the instrument or token Before you can create a forward request, retrieve the ID for the source of the stored credentials. This can be one of: - A [payment instrument](https://www.checkout.com/docs/payments/store-and-manage-credentials/store-credentials/payment-instruments.md) ID you've created and stored in the Vault, prefixed with `src_`. - A [token](https://api-reference.checkout.com/#tag/Tokens) ID you've generated, prefixed with `tok_`. This can be a temporary token for the full card details, or cards originated from a digital wallet, such as Apple Pay and Google Pay. > **Information** > > Tokens expire after 15 minutes. If you want to reuse the credentials in future payments, you can [convert a token into a payment instrument](https://api-reference.checkout.com/tag/Instruments/#operation/createAnInstrument). --- ## Create a forward request Call the [Forward an API request](https://api-reference.checkout.com/tag/Forward/#operation/forwardRequest) endpoint. Authenticate the request using one of the following: - Public key – If using an API key, we recommend encrypting it first. - An API key - An access key – If using [OAuth 2.0 authentication](https://www.checkout.com/docs/developer-resources/api/manage-api-keys/oauth-2-0-client-credentials.md) > **Note** > > Checkout.com does not store the API keys you provide in encrypted headers. We only use the key to process the request in which it's provided. In the request body, provide the following: **Payment instrument** For a payment instrument or a network token stored as a payment instrument: - `source.type` – Set to `id`. - `source.id` – The payment instrument ID (prefixed with `src_`) or token ID (prefixed with `nt_`) - `destination_request.url` – The third-party endpoint you want to forward the request to - `destination_request.method` – The HTTP method you want to use for the request - `destination_request.headers.raw` – The raw HTTP headers you want to include in the request - `destination_request.body` – The JSON payload formatted according to the key-value pairs expected by the destination PSP Optionally: - `source.cvv_token` – The token ID for the card verification value (CVV), prefixed with `tok_` - `source.pin_token` – The token ID for the card's personal identification number (PIN), prefixed with `tok_` **Token** For a temporarily tokenized payment method: - `source.type` – Set to `token`. - `source.token` – The token ID, prefixed with `tok_` - `destination_request.url` – The third-party endpoint you want to forward the request to - `destination_request.method` – The HTTP method you want to use for the request - `destination_request.headers.raw` – The raw HTTP headers you want to include in the request - `destination_request.body` – The JSON payload formatted according to the key-value pairs expected by the destination PSP Optionally: - `source.store_for_future_use` – Store the token as a payment instrument. ### Using network tokens Network tokens enhance transaction security by replacing raw card details. This helps ensure compliance with PCI DSS standards and reduces the risk of fraud. To process a request which includes network tokens, you must include the [forwarding logic](#Forwarding_logic) that checks they're available and the required fields to forward. - Set the `network_token.enabled` field to `true`. - Set the `network_token.request_cryptogram` field to: - `true` – For customer-initiated transactions (CITs) - `false` – For merchant-initiated transactions (MITs) If the network token is not available, the Forward API does not automatically fall back to card details. To handle scenarios where a network token is unavailable, use [conditional logic](#Conditional_logic) in your `destination_request.body` to define a card fallback branch. ### Placeholder values You can use the following placeholder values in the `destination_request.body` field. When the request is forwarded to the third-party endpoint, the placeholder values are replaced with the respective payment credentials stored in the specified payment instrument or token. When you set `network_token.enabled` to `true`, the Forward API checks if a network token is available for the instrument. If a token is available and you also set `request_cryptogram` to `true`, the API also requests a new cryptogram. The token values then become available to use as placeholders in your request body. **Card details** | Placeholder value | Description | | --- | --- | | `{{card_cvv}}` | The card verification value.

This is also referred to as the card's security code. | | `{{card_expiry_month}}` | The card's expiry month, without a leading zero.

Format – `M` | | `{{card_expiry_month_mm}}` | The card's expiry month, with a leading zero.

Format – `MM` | | `{{card_expiry_year_yy}}` | The card's expiry year.

Format – `YY` | | `{{card_expiry_year_yyyy}}` | The card's expiry year.

Format – `YYYY` | | `{{card_number}}` | The full card number. | | `{{card_pin}}` | The first two digits of the card's PIN. | | `{{cardholder_name}}` | The cardholder's name as shown on the card. | **Billing information** | Placeholder value | Description | | --- | --- | | `{{billing_address_city}}` | The city of the billing address. | | `{{billing_address_country}}` | The country of the billing address. | | `{{billing_address_line1}}` | The first line of the billing address. | | `{{billing_address_line2}}` | The second line of the billing address. | | `{{billing_address_state}}` | The state of the billing address. | | `{{billing_address_zip}}` | The ZIP code or post code of the billing address. | **Network token details** | Placeholder value | Description | | --- | --- | | `{{network_token_number}}` | The network token number from the card scheme. | | `{{network_token_type}}` | The type of network token. This can be one of:

- `mdes` for Mastercard
- `vts` for Visa | | `{{network_token_cryptogram}}` | The token authentication verification value (TAVV). This is also referred to as a one-time cryptogram. | | `{{network_token_eci}}` | The [Electronic Commerce Indicator (ECI)](https://www.checkout.com/docs/developer-resources/codes/eci-values.md) received from the issuer. | | `{{network_token_expiry_year_yy}}` | The network token's expiry year.

Format – `YY` | | `{{network_token_expiry_year_yyyy}}` | The network token's expiry year

Format – `YYYY` | | `{{network_token_expiry_month}}` | The network token's expiry month

Format – `MM` | **Digital wallet details** | Placeholder value | Description | | --- | --- | | `{{is_wallet}}` | A boolean that indicates whether the card came from a digital wallet, such as Apple Pay or Google Pay. | | `{{wallet_type}}` | The type of the wallet. This can be one of:
- `applepay`
- `googlepay` | | `{{wallet_token_format}}` | The format of the wallet. This can be one of:
- `cryptogram_3ds`
- `pan_only` | | `{{wallet_cryptogram}}` | The wallet cryptogram.

Required for CIT transactions. | | `{{wallet_eci}}` | The electronic commerce indicator (ECI) for the transaction. | > **Information** > > If you use the Forward API to perform an authorization with a third party, you must handle any post-transaction actions directly with them. For example, captures, refunds, and disputes. **Production** `POST` `https://forward.checkout.com/forward` **Sandbox** `POST` `https://forward.sandbox.checkout.com/forward` ### Request example ```json { "source": { "type": "id", "id": "src_wmlfc3zyhqzehihu7giusaaawu" }, "reference": "ORD-5023-4E89", "processing_channel_id": "pc_azsiyswl7bwe2ynjzujy7lcjca", "destination_request": { "url": "string", "method": "POST", "headers": { "encrypted": "", "raw": { "Idempotency-Key": "xe4fad12367dfgrds", "Content-Type": "application/json" } }, "body": "{\"amount\": 1000,\"currency\": \"USD\",\"reference\": \"Test\",\"source\": {\"type\": \"card\",\"number\": \"{{card_number}}\",\"expiry_month\": \"{{expiry_month}}\",\"expiry_year\": \"{{expiry_year_yyyy}}\",\"name\": \"Hannah Bret\"},\"payment_type\": \"Regular\",\"authorization_type\": \"Final\",\"capture\": true,\"processing_channel_id\": \"pc_xxxxxxxxxxx\",\"risk\": {\"enabled\": false},\"merchant_initiated\": true}" } } ``` ### Response example ```json { "request_id": "fwr_5fa7ee8c-f82d-4440-a6dc-e8c859b03235", "destination_response": { "status": 201, "headers": { "Cko-Request-Id": "5fa7ee8c-f82d-4440-a6dc-e8c859b03235", "Content-Type": "application/json" }, "body": "{ \n \"id\": \"pay_mbabizu24mvu3mela5njyhpit4\", \n \"action_id\": \"act_mbabizu24mvu3mela5njyhpit4\", \n \"amount\": 6540,\"currency\": \"USD\", \n \"approved\": true,\"status\": \"Authorized\", \n \"auth_code\": \"770687\", \n \"response_code\": \"10000\", \n \"response_summary\": \"Approved\", \n \"_links\": \n {\"self\": \n {\"href\": \"https://{prefix}.api.sandbox.checkout.com/payments/pay_mbabizu24mvu3mela5njyhpit4\"}, \n \"action\": \n {\"href\": \"https://{prefix}.api.sandbox.checkout.com/payments/pay_mbabizu24mvu3mela5njyhpit4/actions\"}, \n \"void\": \n {\"href\": \"https://{prefix}.api.sandbox.checkout.com/payments/pay_mbabizu24mvu3mela5njyhpit4/voids\"}, \n \"capture\": \n {\"href\": \"https://{prefix}.api.sandbox.checkout.com/payments/pay_mbabizu24mvu3mela5njyhpit4/captures\"}\n}\n}" } } ``` If you receive a `200` response, the request was successfully forwarded to the specified third-party endpoint. The `response.status` field specifies the response code returned by the third-party endpoint. The `response.body` field contains the response body returned by the third-party endpoint, with any sensitive PCI information removed. > **Note** > > Checkout.com cannot guarantee that the third-party processor will return a specific response to any requests you forward. If they are unresponsive, you must contact them directly to resolve the issue. --- ## Enrich the request Enrich your request by using: - Variables, query parameters, and JWE encryption to build complex requests for your destination endpoints. - Static values or template expressions to inject dynamic values. You can use static values or template expressions to inject dynamic values. ### Variables You can use variables to define reusable values that you can later reference in your request. Variables are processed before anything else, so you can use them in headers, query parameters, and the request body. To define variables for your request, use the `variables` array in the `destination_request` object. Each variable has a `name` and `value` field. You cannot name a variable `secret` or `jwe_encrypt`, in any casing. If you do, you receive a `422` response with the `destination_request_variables_name_reserved` error code. > **Note** > > Do not name a variable `metadata`. If you do, the variable overrides the [metadata placeholders](#Metadata_placeholders) in the request. #### Request example ```json { "source": { "type": "id", "id": "src_xxxxxxxxxxxxxxxxxxxxxxxxxx" }, "reference": "your-reference", "processing_channel_id": "pc_xxxxxxxxxxxxxxxxxxxxxxxxxx", "destination_request": { "url": "https://api.example.com/v1/endpoint", "method": "POST", "headers": { "raw": { "Content-Type": "application/json", "X-Expiry-Date": "{{ formatted_expiry }}" } }, "body": "{ \"card\": { \"number\": \"{{ card_number }}\", \"expiry\": \"{{ formatted_expiry }}\", \"cvv\": \"{{ card_cvv }}\" }, \"cardData\": \"{{ card_payload }}\" }", "variables": [ { "name": "formatted_expiry", "value": "{{ card_expiry_month }}/{{ card_expiry_year_yy }}" }, { "name": "card_payload", "value": "{\"pan\":\"{{ card_number }}\",\"expiryMonth\":\"{{ card_expiry_month }}\",\"expiryYear\":\"{{ card_expiry_year_yyyy }}\"}" } ] } } ``` ### Query parameters You can use query parameters to append key-value pairs to your destination URL. They are automatically appended to the URL when the request is sent to the destination. To define query parameters, use the `query` array in the `destination_request` object. Each parameter has a `name` and a `value` field. For example, if your destination URL is `https://api.example.com/v1/charges` and you set the following query parameters: - `merchantId` to `12345` - `version` to `2024-01` - `cardBin` to a dynamic value defined by the variable `{{ card_bin }}` #### Request example ```json { "source": { "type": "id", "id": "src_xxxxxxxxxxxxxxxxxxxxxxxxxx" }, "reference": "your-reference", "processing_channel_id": "pc_xxxxxxxxxxxxxxxxxxxxxxxxxx", "destination_request": { "url": "https://api.example.com/v1/charges", "method": "POST", "headers": { "raw": { "Content-Type": "application/json" } }, "body": "{ \"amount\": 1000, \"currency\": \"USD\" }", "variables": [ { "name": "card_bin", "value": "{{ card_number }}" } ], "query": [ { "name": "merchantId", "value": "12345" }, { "name": "version", "value": "2024-01" }, { "name": "cardBin", "value": "{{ card_bin }}" } ] } } ``` The final URL becomes
`https://api.example.com/v1/charges?merchantId=12345&version=2024-01&cardBin=411111` ### JWE encryption You can use JWE to encrypt sensitive data within your request using either a public or symmetric key. To encrypt data in your request, use the `jwe_encrypt` function, available within your templates: 1. Define the data to encrypt – Create a variable containing the JSON payload with card details. For example, `card_data`. 2. Define the encryption key – Create a variable with the JWK key provided by the destination. For example, a `public_key` or `symmetric_key`. 3. Call the function – Use `jwe_encrypt(data=card_data, key=public_key, alg='RSA-OAEP', enc='A256GCM')` in your body field. The function returns the encrypted JWE token string that replaces the placeholder. The `jwe_encrypt` function accepts the following parameters: | Parameter | Description | Example values | | --- | --- | --- | | `data` | The data to encrypt. Typically a variable containing card data. | `card_payload` | | `key` | The encryption key in JWK format (RSA public key or symmetric key).

Supported values are:

- `RSA-OAEP`
- `RSA-OAEP-256`
- `A256GCMKW` | `jwk_key` | | `alg` | The key encryption algorithm. | `RSA-OAEP` | | `enc` | The content encryption algorithm.

Supported values are:

- `A128GCM`
- `A256GCM`
- `A128CBC-HS256`
- `A256CBC-HS512` | `A256GCM` | | `headers`

`optional` | Additional JWE headers, as JSON. | `{"kid":"key-id","iat":1234567890}` | **Public key** For RSA-based encryption, you must provide the public key as a JWK in a variable in the `variables` array: ```json { "source": { "type": "id", "id": "src_xxxxxxxxxxxxxxxxxxxxxxxxxx" }, "reference": "your-reference", "processing_channel_id": "pc_xxxxxxxxxxxxxxxxxxxxxxxxxx", "destination_request": { "url": "https://api.example.com/v1/endpoint", "method": "POST", "headers": { "raw": { "Content-Type": "application/json" } }, "body": "{ \"encryptedCard\": \"{{ jwe_encrypt(data=card_data, key=public_key, alg='RSA-OAEP', enc='A256GCM') }}\"}", "variables": [ { "name": "card_data", "value": "{\"primaryAccountNumber\":\"{{ card_number }}\",\"panExpirationMonth\":\"{{ card_expiry_month }}\",\"panExpirationYear\":\"{{ card_expiry_year_yyyy }}\",\"cvv\":\"{{ card_cvv }}\"}" }, { "name": "public_key", "value": "{ \"kty\": \"RSA\", \"e\": \"AQAB\", \"use\": \"enc\", \"kid\": \"key-001\", \"alg\": \"RSA-OAEP\", \"n\": \"your-public-key-modulus\" }" } ] } } ``` **Symmetric key** For destinations that require symmetric key encryption, use algorithms like `A256GCMKW`. This requires a 256-bit (32 bytes) symmetric key encoded in `base64url` format. You must provide the symmetric key as a variable in the `variables` array, and a JWK with the `k` parameter containing the base64url-encoded key: ```json { "kty": "oct", "k": "" } ``` > **Note** > > Some APIs require the key to be derived as `base64url(sha256())`. Check your destination API documentation for the exact key derivation requirements. ```json { "source": { "type": "id", "id": "src_xxxxxxxxxxxxxxxxxxxxxxxxxx" }, "reference": "your-reference", "processing_channel_id": "pc_xxxxxxxxxxxxxxxxxxxxxxxxxx", "destination_request": { "url": "https://api.example.com/v1/tokenize", "method": "POST", "headers": { "raw": { "Content-Type": "application/json" } }, "body": "{ \"encryptedPayload\": \"{{ jwe_encrypt(data=payment_data, key=symmetric_key, alg='A256GCMKW', enc='A256GCM', headers=jwe_headers) }}\"}", "variables": [ { "name": "payment_data", "value": "{\"accountNumber\":\"{{ card_number }}\",\"expirationMonth\":\"{{ card_expiry_month }}\",\"expirationYear\":\"{{ card_expiry_year_yyyy }}\"}" }, { "name": "symmetric_key", "value": "{ \"kty\": \"oct\", \"k\": \"\" }" }, { "name": "jwe_headers", "value": "{ \"kid\": \"your-key-id\", \"iat\": 1234567890 }" } ] } } ``` ### Conditional logic Use conditional logic in your `destination_request.body` to handle different scenarios based on the availability of payment data at the time the request is forwarded. This is required when you want to use a network token if one is available, and fall back to card details if it is not. Use `{% if %}`, `{% else %}`, and `{% endif %}` blocks in the body string to define different request structures for each scenario. Use the `IsNetworkTokenAvailable` condition to know whether when a network token is available and active for the payment instrument. #### Request example ```json { "source": { "type": "id", "id": "src_xxxxxxxxxxxxxxxxxxxxxxxxxx" }, "processing_channel_id": "pc_xxxxxxxxxxxxxxxxxxxxxxxxxx", "network_token": { "enabled": true, "request_cryptogram": true }, "destination_request": { "url": "https://api.example.com/v1/payments", "method": "POST", "headers": { "raw": { "Content-Type": "application/json" } }, "body": "{% if IsNetworkTokenAvailable %}{\"source\":{\"is_network_token_available\":true,\"type\":\"network_token\",\"token\":\"{{network_token_number}}\",\"expiry_month\":\"{{network_token_expiry_month}}\",\"expiry_year\":\"{{network_token_expiry_year_yyyy}}\",\"cryptogram\":\"{{network_token_cryptogram}}\",\"eci\":\"{{network_token_eci}}\"}}{% else %}{\"source\":{\"is_network_token_available\":false,\"type\":\"card\",\"number\":\"{{card_number}}\",\"expiry_month\":\"{{card_expiry_month}}\",\"expiry_year\":\"{{card_expiry_year_yyyy}}\"}}{% endif %}" } } ``` ### Metadata placeholders If you've stored [metadata](https://www.checkout.com/docs/payments/store-and-manage-credentials/store-credentials/payment-instruments.md#Store_metadata_with_an_instrument) with the payment instrument or card token you provide in `source`, you can insert its values into your request, for example to send a customer's document number to the destination with every request. Use the following placeholders in `destination_request.body` and `destination_request.headers.raw`: - `{{metadata.KEY}}` – Replace `KEY` with the metadata key. For example, `{{metadata.documentNumber}}`. - `{{metadata["KEY"]}}` – Use this format for keys that are not valid identifiers. For example, keys that contain a hyphen or a space. You can also use metadata placeholders in `destination_request.variables` values, if the variable is used in the body or headers. Metadata placeholders do not work in `destination_request.url`, `destination_request.query`, or signature parameters. In those fields, the placeholder is replaced with an empty string and no error is returned. The metadata is read from the source you provide: - For a payment instrument ID, prefixed with `src_`, the metadata is read from the instrument. - For a token, prefixed with `tok_`, the metadata is read from the token. If you set `source.store_for_future_use` to `true`, the metadata is stored with the new payment instrument. You cannot provide `metadata` in the forward request itself, and it's not returned in the Forward an API request or Get forward request responses. When you use metadata placeholders: - Keys are case-sensitive and must match exactly. For example, `documentNumber` and `documentnumber` are different keys. - If the key does not exist, the placeholder is replaced with an empty string, and the request is still forwarded. To detect a missing value, check the request your destination receives. - Values are inserted exactly as stored, without escaping. Ensure a value cannot break the syntax of your body template, for example a `"` character inside a JSON string. > **Note** > > Metadata values you insert into `destination_request.headers.raw` may be stored unmasked in the forward request log returned by the [Get forward request](https://api-reference.checkout.com/tag/Forward/#operation/getForwardRequest) endpoint. Some sensitive headers, such as `Authorization`, are redacted in the log, and encrypted headers are not logged in plain text, but other header values are logged as sent. > > Metadata values in `destination_request.body` are not stored in the log, because the body is logged as the template before placeholders are replaced. #### Request example ```json { "source": { "type": "id", "id": "src_wmlfc3zyhqzehihu7giusaaawu" }, "reference": "ORD-5023-4E89", "processing_channel_id": "pc_azsiyswl7bwe2ynjzujy7lcjca", "destination_request": { "url": "https://api.example.com/payments", "method": "POST", "headers": { "raw": { "Content-Type": "application/json", "X-Partner-Id": "{{metadata.partnerId}}" } }, "body": "{\"card\": {\"number\": \"{{card_number}}\", \"expiry_month\": \"{{card_expiry_month}}\", \"expiry_year\": \"{{card_expiry_year_yyyy}}\"}, \"customer_document\": \"{{metadata.documentNumber}}\"}" } } ``` --- ## Store and forward secrets The Forward API enables you to securely store and manage sensitive data such as credentials, API keys, and encryption keys. You can reference these secrets in your requests without exposing them in the request payload. To forward secrets in the Forward API, you must: 1. Store the secret. 2. Use the secret in your Forward API requests. ### Store the secret To store a secret, call the [Create secret](https://api-reference.checkout.com/tag/Forward#operation/createSecret) endpoint, providing a unique name and the sensitive value. **Production** `POST` `https://forward.checkout.com/secrets` **Sandbox** `POST` `https://forward.sandbox.checkout.com/secrets` #### Request example ```json { "name": "my_api_key", "value": "sk_live_xxxxxxxxxxxxxxxxxxxxx" } ``` #### Response example ```json { "id": "sec_xxxxxxxxxxxxxxxxxxxxxxxxxx", "name": "my_api_key", "created_at": "2024-01-15T10:30:00Z" } ``` ### Use the secret Once you've stored the secret, you can reference it in a request by using the `{{ secret. }}` placeholder. Replace `` with the name you provided when you stored the secret. For example, `{{ secret.my_api_key }}`. The spaces inside the braces are optional. You can: - Use the secrets in headers, as API keys - Reference secrets within variables to use them with the `jwe_encrypt` function **In headers** ```json { "source": { "type": "id", "id": "src_xxxxxxxxxxxxxxxxxxxxxxxxxx" }, "reference": "your-reference", "processing_channel_id": "pc_xxxxxxxxxxxxxxxxxxxxxxxxxx", "destination_request": { "url": "https://api.example.com/v1/charges", "method": "POST", "headers": { "raw": { "Content-Type": "application/json", "Authorization": "{{ secret.destination_api_key }}" } }, "body": "{ \"amount\": 1000, \"currency\": \"USD\", \"card\": { \"number\": \"{{ card_number }}\", \"expiry_month\": \"{{ card_expiry_month }}\", \"expiry_year\": \"{{ card_expiry_year_yyyy }}\", \"cvv\": \"{{ card_cvv }}\" } }" } } ``` **In variables** ```json { "source": { "type": "id", "id": "src_xxxxxxxxxxxxxxxxxxxxxxxxxx" }, "reference": "your-reference", "processing_channel_id": "pc_xxxxxxxxxxxxxxxxxxxxxxxxxx", "destination_request": { "url": "https://api.example.com/v1/tokenize", "method": "POST", "headers": { "raw": { "Content-Type": "application/json" } }, "body": "{ \"encryptedCard\": \"{{ jwe_encrypt(data=card_data, key=encryption_key, alg='RSA-OAEP', enc='A256GCM') }}\"}", "variables": [ { "name": "card_data", "value": "{\"primaryAccountNumber\":\"{{ card_number }}\",\"panExpirationMonth\":\"{{ card_expiry_month }}\",\"panExpirationYear\":\"{{ card_expiry_year_yyyy }}\"}" }, { "name": "encryption_key", "value": "{{ secret.partner_public_key }}" } ] } } ``` --- ## Forwarding logic The fields forwarded to the third-party endpoint depend on the type of request: - [CIT using network token and cryptogram](#CIT_using_network_token_and_cryptogram) - [MIT using network token](#MIT_using_network_token) - [Network token with card fallback](#Network_token_with_card_fallback) ### CIT using a network token and cryptogram If you set `network_token.request_cryptogram` to `true` and the network token and cryptogram are available, the following values are forwarded: - `network_token_number` - `network_token_expiry_month` - `network_token_expiry_year_yyyy`, or `network_token_expiry_year_yy` - `network_token_type` - `network_token_cryptogram` - `network_token_eci` **Production** `POST` `https://forward.checkout.com/forward` **Sandbox** `POST` `https://forward.sandbox.checkout.com/forward` #### Request example ```json { "source": { "type": "id", "id": "src_5i3imssmshlu5apj2sgdozyht4" }, "processing_channel_id": "pc_cvbbk7dyapdeho2qmuzozupapi", "network_token": { "enabled" : true, "request_cryptogram": true }, "destination_request": { "url": "https://api.stripe.com/v1/payment_methods", "method": "POST", "headers": { "raw": { } }, "body": "{ \n \"source\": \n {\"type\": \"network_token\", \n \"token\": \"{{network_token_number}}\", \n \"expiry_month\": \"{{network_token_expiry_month}}\", \n \"expiry_year\": \"{{network_token_expiry_year_yyyy}}\", \n \"token_type\": \"{{network_token_type}}\", \n \"cryptogram\": \"{{network_token_cryptogram}}\", \n \"eci\": \"{{network_token_eci}}\"}, \n \"amount\": 200, \n \"currency\": \"CHF\", \n \"capture\": true, \n \"processing_channel_id\" :\"pc_cvbbk7dyapdeho2qmuzozupapi\"}" } } ``` ### MIT using a network token If you set `network_token.request_cryptogram` to `false`, the following values are forwarded: - `network_token_number` - `network_token_expiry_month` - `network_token_expiry_year_yyyy`, or `network_token_expiry_year_yy` - `network_token_type` **Production** `POST` `https://forward.checkout.com/forward` **Sandbox** `POST` `https://forward.sandbox.checkout.com/forward` #### Request example ```json { "source": { "type": "id", "id": "src_5i3imssmshlu5apj2sgdozyht4" }, "processing_channel_id": "pc_cvbbk7dyapdeho2qmuzozupapi", "network_token": { "enabled" : true, "request_cryptogram": false }, "destination_request": { "url": "https://api.stripe.com/v1/payment_methods", "method": "POST", "headers": { "raw": { } }, "body": "{ \n \"source\": { \n\"type\": \"network_token\", \n\"token\": \"{{network_token_number}}\", \n\"expiry_month\": \"{{network_token_expiry_month}}\", \n\"expiry_year\": \"{{network_token_expiry_year_yyyy}}\", \n\"token_type\": \"{{network_token_type}}\", \n\"previous_payment_id\": 1923712462419 \n}, \n\"amount\": 200, \n\"currency\": \"CHF\", \n\"capture\": true, \n \"processing_channel_id\" :\"pc_cvbbk7dyapdeho2qmuzozupapi\" \n}" } } ``` ### Network token with card fallback Forward a network token if one is available, and fall back to card details if it is not. Because there is no automatic fallback, you must use [conditional logic](#Conditional_logic) in the request body to define both scenarios: - Network token – used when `IsNetworkTokenAvailable` is `true`. - Card fallback – used when `IsNetworkTokenAvailable` is `false`. **Production** `POST` `https://forward.checkout.com/forward` **Sandbox** `POST` `https://forward.sandbox.checkout.com/forward` #### Request example ```json { "source": { "type": "id", "id": "src_5i3imssmshlu5apj2sgdozyht4" }, "processing_channel_id": "pc_cvbbk7dyapdeho2qmuzozupapi", "network_token": { "enabled": true, "request_cryptogram": true }, "destination_request": { "url": "https://api.stripe.com/v1/payment_methods", "method": "POST", "headers": { "raw": { "Idempotency-Key": "xe4fad12367dfgrds", "Content-Type": "application/json" } }, "body": "{% if IsNetworkTokenAvailable %}{\"source\":{\"is_network_token_available\":true,\"type\":\"network_token\",\"token\":\"{{network_token_number}}\",\"expiry_month\":\"{{network_token_expiry_month}}\",\"expiry_year\":\"{{network_token_expiry_year_yyyy}}\",\"cryptogram\":\"{{network_token_cryptogram}}\",\"eci\":\"{{network_token_eci}}\",\"billing_address\":{\"address_line1\":\"123 High St.\",\"address_line2\":\"Flat 456\",\"city\":\"London\",\"state\":\"GB\",\"zip\":\"SW1A 1AA\",\"country\":\"GB\"}}}{% else %}{\"source\":{\"is_network_token_available\":false,\"type\":\"card\",\"number\":\"{{card_number}}\",\"expiry_month\":\"{{card_expiry_month}}\",\"expiry_year\":\"{{card_expiry_year_yyyy}}\",\"billing_address\":{\"address_line1\":\"123 High St.\",\"address_line2\":\"Flat 456\",\"city\":\"London\",\"state\":\"GB\",\"zip\":\"SW1A 1AA\",\"country\":\"GB\"}}}{% endif %}" } } ``` When the Forward API evaluates the body template, it replaces the placeholders with the stored credentials and forwards it to the destination. **Network token available** If the `{% if IsNetworkTokenAvailable %}` logic evaluates to `true`, the destination receives a network token body with the cryptogram populated. ```json { "source": { "is_network_token_available": true, "type": "network_token", "token": "4242424242424242", "expiry_month": "06", "expiry_year": "2027", "cryptogram": "AAABCSIIBAAAAAAHxgA=", "eci": "05", "billing_address": { "address_line1": "123 High St.", "address_line2": "Flat 456", "city": "London", "state": "GB", "zip": "SW1A 1AA", "country": "GB" } } } ``` **Network token unavailable** If the `{% if IsNetworkTokenAvailable %}` logic evaluates to `false`, the `{% else %}` branch runs, and the destination receives a card body instead. ```json { "source": { "is_network_token_available": false, "type": "card", "number": "4111111111111111", "expiry_month": "01", "expiry_year": "2029", "billing_address": { "address_line1": "123 High St.", "address_line2": "Flat 456", "city": "London", "state": "GB", "zip": "SW1A 1AA", "country": "GB" } } } ``` --- ## Stripe forwarding example To forward a payment request to Stripe, follow these steps: 1. Create a payment method. 2. Create a payment intent. 3. Confirm the payment intent. 4. Review the response. ### Create a payment method If you're using the authorization header, we recommend you encrypt the value and provide it in the `destination_request.headers.encrypted` field in the request. The Stripe API expects each key-value pair to be separated with an `&` character. The `Content-Type` in the header must match the format of the content you send. ```curl curl --location 'https://forward.sandbox.checkout.com/forward' \ --header 'Content-Type: application/json' \ --header 'Authorization: YOUR_OAUTH_TOKEN' \ --data '{ "source": { "type": "id", "id": "src_u422hics5oouvix25ous63grcy" }, "reference": "string", "processing_channel_id": "pc_f2vtqcmhgykurkorrnnpz3kneq", "destination_request": { "url": "https://api.stripe.com/v1/payment_methods", "method": "POST", "headers": { "encrypted": "", "raw": { "Content-Type": "application/x-www-form-urlencoded", "Authorization": "Basic c2tfdGVzdF8yNlBIZW05QWhKWnZVNjIzRGZFMXg0c2Q6", "Host":"api.stripe.com" } }, "body": "type=card&card[number]={{card_number}}&card[exp_month]={{card_expiry_month}}&card[exp_year]={{card_expiry_year_yyyy}}&card[cvc]=314" } }' ``` You receive a payment method ID, which you need to confirm the payment intent. ### Create a payment intent ```curl curl --location 'https://forward.sandbox.checkout.com/forward' \ --header 'Content-Type: application/json' \ --header 'Authorization: YOUR_OAUTH_TOKEN' \ --data '{ "source": { "type": "id", "id": "src_u422hics5oouvix25ous63grcy" }, "reference": "string", "processing_channel_id": "pc_f2vtqcmhgykurkorrnnpz3kneq", "destination_request": { "url": "https://api.stripe.com/v1/payment_intents", "method": "POST", "headers": { "encrypted": { }, "raw": { "Content-Type": "application/x-www-form-urlencoded", "Authorization": "Basic c2tfdGVzdF8yNlBIZW05QWhKWnZVNjIzRGZFMXg0c2Q6", "Host":"api.stripe.com" } }, "body": "amount=1000¤cy=usd&payment_method_types[]=card" } }' ``` You receive a payment intent ID, which you need to confirm the payment intent. ### Confirm the payment intent To confirm the payment intent, update the `destination_request.url` and `destination_request.body` fields with the payment method ID and payment intent ID. ```curl curl --location 'https://forward.sandbox.checkout.com/forward' \ --header 'Content-Type: application/json' \ --header 'Authorization: YOUR_OAUTH_TOKEN' \ --data '{ "source": { "type": "id", "id": "src_u422hics5oouvix25ous63grcy" }, "reference": "string", "processing_channel_id": "pc_f2vtqcmhgykurkorrnnpz3kneq", "destination_request": { "url": "https://api.stripe.com/v1/payment_intents//confirm", "method": "POST", "headers": { "encrypted": { }, "raw": { "Content-Type": "application/x-www-form-urlencoded", "Authorization": "Basic c2tfdGVzdF8yNlBIZW05QWhKWnZVNjIzRGZFMXg0c2Q6", "Host":"api.stripe.com" } }, "body": "payment_method=" } }' ``` ### Review the response ```json { "request_id": "fwd_da32zq6rgymepl4sxiz3nnnicy", "destination_response": { "status": "ok", "headers": { "Server": [ "nginx" ], "Date": [ "Fri, 21 Jun 2024 16:48:37 GMT" ], "Connection": [ "keep-alive" ], "Access-Control-Allow-Credentials": [ "true" ], "Access-Control-Allow-Methods": [ "GET,HEAD,PUT,PATCH,POST,DELETE" ], "Access-Control-Allow-Origin": [ "*" ], "Access-Control-Expose-Headers": [ "Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required" ], "Access-Control-Max-Age": [ "300" ], "Cache-Control": [ "no-store, no-cache" ], "Content-Security-Policy": [ "report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self'" ], "Cross-Origin-Opener-Policy-Report-Only": [ "same-origin; report-to=\"coop\"" ], "Idempotency-Key": [ "b7c44bf5-aa75-4fb3-9c6e-ad17a3e003c3" ], "Original-Request": [ "req_UT51MBBX26MSzC" ], "Report-To": [ "{\"group\":\"coop\",\"max_age\":8640,\"endpoints\":[{\"url\":\"https://q.stripe.com/coop-report?s=payins-bapi-srv\"}],\"include_subdomains\":true}" ], "Reporting-Endpoints": [ "coop=\"https://q.stripe.com/coop-report?s=payins-bapi-srv\"" ], "Request-Id": [ "req_UT51MBBX26MSzC" ], "Stripe-Should-Retry": [ "false" ], "Stripe-Version": [ "2024-04-10" ], "Vary": [ "Origin" ], "X-Content-Type-Options": [ "nosniff" ], "X-Stripe-Priority-Routing-Enabled": [ "true" ], "X-Stripe-Routing-Context-Priority-Tier": [ "api-testmode" ], "Strict-Transport-Security": [ "max-age=63072000; includeSubDomains; preload" ] }, "body": "{\n \"id\": \"pm_1PUAdd2x6R10KRrhGCbuX0hq\",\n \"object\": \"payment_method\",\n \"allow_redisplay\": \"unspecified\",\n \"billing_details\": {\n \"address\": {\n \"city\": null,\n \"country\": null,\n \"line1\": null,\n \"line2\": null,\n \"postal_code\": null,\n \"state\": null\n },\n \"email\": null,\n \"name\": null,\n \"phone\": null\n },\n \"card\": {\n \"brand\": \"visa\",\n \"checks\": {\n \"address_line1_check\": null,\n \"address_postal_code_check\": null,\n \"cvc_check\": \"unchecked\"\n },\n \"country\": \"US\",\n \"display_brand\": \"visa\",\n \"exp_month\": 3,\n \"exp_year\": 2030,\n \"fingerprint\": \"NXzK7YAyyprPKex2\",\n \"funding\": \"credit\",\n \"generated_from\": null,\n \"last4\": \"1111\",\n \"networks\": {\n \"available\": [\n \"visa\"\n ],\n \"preferred\": null\n },\n \"three_d_secure_usage\": {\n \"supported\": true\n },\n \"wallet\": null\n },\n \"created\": 1718988517,\n \"customer\": null,\n \"livemode\": false,\n \"metadata\": {},\n \"type\": \"card\"\n}" } } ``` --- ## Adyen forwarding example To forward a payment request to Adyen, follow these steps: 1. Create a payment. 2. Review the response. ### Create a payment The Adyen API expects a JSON payload, without any blank spaces between the key-value pairs. Provide the entire value in double quotes to ensure that single quotes are escaped correctly. The `Content-Type` in the header must match the format of the content you send. ```json { "source": { "type": "token", "token": "tok_zjri4ogrqjuudnhsr7ooq5z6pq" }, "reference": "string", "processing_channel_id": "pc_f2vtqcmhgykurkorrnnpz3kneq", "destination_request": { "url": "https://checkout-test.adyen.com/v71/payments", "method": "POST", "headers": { "raw": { "Content-Type": "application/json", "X-API-KEY": "" } }, "body": "{\"amount\":{\"currency\":\"EUR\",\"value\":10},\"paymentMethod\":{\"type\":\"scheme\",\"encryptedCardNumber\":\"test_{{card_number}}\",\"encryptedExpiryMonth\":\"test_{{card_expiry_month}}\",\"encryptedExpiryYear\":\"test_{{card_expiry_year_yyyy}}\",\"encryptedSecurityCode\":\"test_737\"},\"reference\":\"my_reference\",\"merchantAccount\":\"PrizeTechnologyECOM\",\"returnUrl\":\"https://httpstat.us/\"}" } } ``` ### Review the response ```json { "request_id": "fwd_01KGJ2HG36Z9AJX6FV94G6Q1SS", "destination_response": { "status": "ok", "headers": { "traceparent": [ "00-22b8dcd266e756081fc1f50019293cdd-f151c37ad9b00661-01" ], "Strict-Transport-Security": [ "max-age=31536000" ], "X-Frame-Options": [ "SAMEORIGIN" ], "X-Content-Type-Options": [ "nosniff" ], "Set-Cookie": [ "JSESSIONID=ED43C7B0865E2210929050D36DA7EAE6.7d99355f-347b-18a0-fa83-70c08e69704f_0; Path=/checkout; Secure; HttpOnly" ], "pspReference": [ "NPJ9LM2B88B26675" ], "Transfer-Encoding": [ "chunked" ], "Date": [ "Fri, 21 Jun 2024 16:10:38 GMT" ] }, "body": "{\"additionalData\":{\"scaExemptionRequested\":\"transactionRiskAnalysis\"},\"pspReference\":\"QXBP9RM6Q327DN65\",\"resultCode\":\"Authorised\",\"amount\":{\"currency\":\"EUR\",\"value\":10},\"merchantReference\":\"my_reference\",\"paymentMethod\":{\"brand\":\"visa\",\"type\":\"scheme\"}}" } } ``` --- ## View forward request outcomes You can view the outcome of your forward requests in the Dashboard. 1. Sign in to the [Dashboard](https://dashboard.checkout.com). 2. Go to _Vault_ > _Credential forwarding_. On this page you can view: - The total number of forward requests - The response rate for your forward requests - A breakdown of the responses received from the destinations you forwarded the requests to - A history of all forward requests you've sent Select a specific forward request to open its details page. On this page you can view: - The forward request ID - The date and time you forwarded the request - Information about the destination you forwarded the request to – for example, the destination endpoint, the response code received, and the response time - Information about the HTTP request sent and the HTTP response received - Information about the stored credentials that you forwarded – for example, the payment instrument ID, card scheme, or issuing country