# Build InStore payment components Create payment processors and payment options, and assign them to locations and workstations. ## Configure payments For an overview of payment types, payment processors, and payment options, see [payment components overview](/instore/integration/payments/overview.md). Complete the payments setup in the following order: 1. [Authenticate InStore APIs](/instore/integration/payments/build-payment-components.md#authenticate-instore-apis) 2. [Create a payment processor](/instore/integration/payments/build-payment-components.md#create-a-payment-processor) 3. [Create a payment option](/instore/integration/payments/build-payment-components.md#create-a-payment-option) 4. [Assign payment options to locations and workstations](/instore/integration/payments/build-payment-components.md#assign-payment-options-to-locations-and-workstations) ### Authenticate InStore APIs Authenticate to the InStore APIs with an administrator account and obtain an access token for the following steps. You must configure the environment you are setting up to be your default environment in the InStore Center. If it isn't your default environment, do one of the following: - Access the InStore Center and [set this environment as the administrator's default](/instore/use-the-instore-center/users.md#set-a-default-environment-for-an-administrator); or - Use the InStore `/switchtenant` endpoint to get a new token: ```json title="Sample /switchtenant request body" { "client_id": "67e6976a0803e100127e311c", "tenant_id": "1ac0-9cd8-eb98-c521" } ``` A valid token is required each time you switch tenant environments. ### Create a payment processor Create a payment processor using the following endpoint: ```http title="Create a payment processor endpoint" POST https://api.instore.{region}.gcp.commercetools.com/{projectKey}/instore-tenants/{tenantKey}/payment-processors ``` Construct a separate call body for each tender type that this processor handles. The response contains the payment processor's ID, which you will use in subsequent steps when developing for the corresponding payment type. #### Sample payment processor payloads Use the following sample payloads as a starting point. Replace placeholder values and adapt fields to your integration. ```json title="Sample Create Payment Processor request body: Cash" { "key": "processor-cash-1", "name": { "en-US": "Pay with cash", "es-MX": "Pago con efectivo" }, "description": { "en-US": "Pay with cash" }, "integrationConfiguration": { "type": "Cash" } } ``` ```json title="Sample Create Payment Processor request body: BankCard" { "key": "processor-bankcard-1", "name": { "en-US": "Pay with card" }, "description": { "en-US": "Pay with card" }, "integrationConfiguration": { "type": "BankCard", "url": "https://api.example.com/bankcard", "timeout": 15000, "connectionTimeout": 15000, "setupSteps": ["Initialization", "Connection"], "delegatePaymentCreation": false } } ``` ```json title="Sample Create Payment Processor request body: PayOnAccount" { "key": "processor-poa-99", "name": { "en-US": "Pay on account" }, "description": { "en-US": "Pay On Account" }, "integrationConfiguration": { "type": "PayOnAccount", "url": "https://integration.example.com/pay-on-account", "apiKey": "YourSecureApiKeyHere", "timeout": 15000, "enableFields": [ "PaymentAccountIDs", "PurchaseOrder", "CompanyName", "AuthorizedPurchaser" ], "accountMethod": "Cart" } } ``` ```json title="Sample Create Payment Processor request body: Wallet" { "key": "processor-wallet", "name": { "en-US": "Digital Wallet" }, "description": { "en-US": "Digital Wallet" }, "integrationConfiguration": { "type": "Wallet", "url": "https://integration.example.com/wallet", "apiKey": "YourSecureApiKeyHere", "timeout": 15000 } } ``` ```json title="Sample Create Payment Processor request body: GiftCard via PED" { "key": "processor-giftcard-ped-1", "name": { "en-US": "Stored-value card (PED)", "es-MX": "Tarjeta de regalo (PED)" }, "description": { "en-US": "Stored-value card paid via a payment terminal", "es-MX": "Tarjeta de regalo pagada mediante terminal" }, "integrationConfiguration": { "type": "GiftCard", "url": "https://integration.example.com/gift-card", "apiKey": "YourSecureApiKeyHere", "timeout": 15000, "connectionTimeout": 15000, "setupSteps": ["Initialization", "Connection"], "cardNumberMethod": "Requested", "cardBalanceMethod": "Requested" } } ``` ```json title="Sample Create Payment Processor request body: GiftCard via scan or manual entry" { "key": "processor-giftcard-manual-1", "name": { "en-US": "Stored-value card (scan or manual entry)", "es-MX": "Tarjeta de regalo (escanear o ingresar)" }, "description": { "en-US": "Stored-value card paid by scanning or entering the card number", "es-MX": "Tarjeta de regalo pagada al escanear o ingresar el nĂºmero" }, "integrationConfiguration": { "type": "GiftCard", "url": "https://integration.example.com/gift-card", "apiKey": "YourSecureApiKeyHere", "timeout": 15000, "connectionTimeout": 15000, "cardNumberMethod": "Manual", "cardBalanceMethod": "Requested" } } ``` ```json title="Sample Create Payment Processor request body: Custom" { "key": "processor-custom-1", "name": { "en-US": "Custom payment", "es-MX": "Pago customizado" }, "description": { "en-US": "Payment method that is completely custom-built by the retailer", "es-MX": "Pago completamente construido por el minorista" }, "integrationConfiguration": { "type": "Custom", "url": "https://payments.example.com/remoteEntry.js", "module": "./Component" } } ``` ```json title="Sample Create Payment Processor response" { "id": "6918ee77f412799d8ef33ee8", "key": "processor-bankcard-1", "name": { "en-US": "Pay with card" }, "description": { "en-US": "Pay with card" }, "integrationConfiguration": "", "createdAt": "2025-11-15T21:19:51.306Z", "updatedAt": "2025-11-15T21:19:51.306Z" } ``` The fields are defined as follows: | Element | Data type | Description | | --- | --- | --- | | key | `String` | An identifier that you specify (for example, `processor-poa-99`). This string must be globally unique across payment processors, not just within a single type. | | name | `Object` | A name that you specify for your own convenience during development. | | description | `Object` | Optional. Not in use. | | integrationConfiguration | `Object` | The payment processor configuration. The fields in this object depend on `integrationConfiguration.type`. | | integrationConfiguration.type | `String` | The payment type. The value can be `Cash`, `BankCard`, `GiftCard`, `Wallet`, `PayOnAccount`, or `Custom`. | | integrationConfiguration.url | `String` | For `BankCard`, `Wallet`, `PayOnAccount`, and `GiftCard` processor configurations, this is the callback URL to your payment service. | | integrationConfiguration.module | `String` | For `Custom` payment processor configurations, this is the exposed module key from your webpack `ModuleFederationPlugin` configuration. | | integrationConfiguration.apiKey | `String` | The payment service key used for signature verification between InStore and your payment service provider. | | integrationConfiguration.enableFields | `Array` | For `PayOnAccount`, the fields to display to the store associate during checkout. Options are `PaymentAccountIDs`, `PurchaseOrder`, `CompanyName`, and `AuthorizedPurchaser`. | | integrationConfiguration.accountMethod | `String` | How the processor receives account information. For `PayOnAccount`, the only available option is `Cart`. | | integrationConfiguration.timeout | `Integer` | The request and read timeout. This value is the maximum time to wait for the entire HTTP request to complete, including reading the response body. | | integrationConfiguration.connectionTimeout | `Integer` | The timeout for establishing the TCP connection to the server. Specify the maximum time to wait before the underlying socket times out during connection setup. | | integrationConfiguration.setupSteps | `Array` | Optional. This field applies to configurations that require setup checks before payment, such as `BankCard` and some `GiftCard` integrations. Use this element to specify `Initialization` or `Connection`. With `Initialization`, InStore makes a request to the `initialize_credit_processor` endpoint to verify that the payment service provider (PSP) can be reached. With `Connection`, InStore makes a request to the `connect_card_reader` endpoint to verify that the PED is available for communication. For the supported processor webhook paths, see [Set up payment extensions](/instore/implement-instore/custom-payments/payment-extensions.md#processor-webhook-paths-that-we-provide-for-specific-payment-types). These endpoints are appended to the payment processor base `url`. If you don't need these steps to enable the payment method, leave `setupSteps` undefined. Depending on your implementation, you can define `setupSteps` for only one processor, with other processors sharing the definition. | | integrationConfiguration.cardNumberMethod | `String` | For `GiftCard`, specifies how InStore acquires the unique number of the stored-value account or stored-value card. Supported values are `Requested` to fetch card data from your payment extension, `Manual` for scan or manual entry, or `Injected` to send pre-loaded account data inline from the POS. | | integrationConfiguration.cardBalanceMethod | `String` | For `GiftCard`, specifies how InStore acquires the available balance for the stored-value account or stored-value card. Supported values are `Requested` to fetch the balance from your payment extension or `Injected` to send the balance inline from the POS. | | integrationConfiguration.delegatePaymentCreation | `Boolean` | Optional. For `BankCard`, controls whether InStore creates a minimal commercetools Payment resource for your payment processor to populate. When `true`, the InStore API Server creates a minimal Payment resource with `amountPlanned`. It passes the Payment resource ID with the payment request so your payment processor can populate the existing Payment resource. When `false`, InStore creates and populates the Payment resource using its default logic. Default is `false`. For details, see [Configure external Payment resource population](/instore/integration/payments/build-payment-components.md#configure-external-payment-resource-population). | ### Create a payment option Create a payment option using the following endpoint: ```http title="Create a payment option endpoint" POST https://api.instore.{region}.gcp.commercetools.com/{projectKey}/instore-tenants/{tenantKey}/payment-options ``` Construct a separate request body for each payment option you want to make available. The response contains the payment option ID, which you will use in subsequent steps. #### Sample payment option payloads Use the following sample payload as a starting point. Replace placeholder values and adapt fields to your requirements. ```json title="Sample Create Payment Option request body" { "key": "option-cash-1", "name": { "en-US": "Pay with cash", "es-MX": "Pagar con efectivo" }, "description": { "en-US": "Pay with cash", "es-MX": "Pagar con efectivo" }, "displayHint": "BuildOutlined", "allowSplit": false, "allowRepeat": true, "minCentAmount": 0, "maxCentAmount": 0, "default": true, "sortOrder": 0.5, "paymentModes": ["Online"], "allowedOperations": ["Sale"], "method": "Cash", "paymentProcessorId": "691bb93ff14fd895922ceb16" } ``` ```json title="Sample Create Payment Option response" { "id": "691bbaa4f1add2fda55a3950", "key": "option-cash-1", "name": { "es-MX": "Pagar con efectivo", "en-US": "Pay with cash" }, "description": { "es-MX": "description", "en-US": "description" }, "displayHint": "BuildOutlined", "allowSplit": false, "allowRepeat": true, "minCentAmount": 0, "maxCentAmount": 0, "default": true, "sortOrder": 0.5, "paymentModes": ["Online"], "allowedOperations": ["Sale"], "method": "Cash", "paymentProcessorId": "691bb93ff14fd895922ceb16", "createdAt": "2025-11-18T00:15:32.908Z", "updatedAt": "2025-11-18T00:15:32.908Z" } ``` The fields are defined as follows: | Element | Data type | Description | | --- | --- | --- | | key | `String` | An identifier that you specify (for example, `option-cash-us-1`). This string must be globally unique across payment options, not just within a single type. | | name | `Object` | A name that you specify for your own convenience during development. | | description | `Object` | Optional. Not in use. | | displayHint | `String` | The identifier for a supported Material UI icon. InStore supports a subset of icons. The list is available upon request. No default. | | allowSplit | `Boolean` | If `true`, a payment using this method may be shared with other payment options. If `false`, this payment option may be used only to pay the entire amount. | | allowRepeat | `Boolean` | If `true`, repeated or additional calls to this payment option within the same session don't change the state beyond the first successful call. | | minCentAmount | `Integer` | The minimum amount that can be paid for using this payment option. Expressed in the equivalent of cents for that currency. For example, 5 USD is `500`. | | maxCentAmount | `Integer` | The maximum amount that can be paid for using this payment option. Expressed in the equivalent of cents for that currency. For example, 5 USD is `500`. | | default | `Boolean` | If `true`, this payment option is available to all locations and workstations in the environment where it is created. The `true` setting requires `sortOrder`. | | sortOrder | `Number` | A number value between `0` and `1` inclusive that you specify to indicate this payment option button's display position on the tender selection screen. Required when `default` is `true`. | | paymentModes | `Array` | The payment modes supported by this payment option. Only `Online` is supported. | | allowedOperations | `Array` | The payment operations supported by this payment option. Only `Sale` is supported. | | method | `String` | The payment method. The value can be `Cash`, `Credit`, `PayOnAccount`, `Wallet`, `GiftCard`, or `Custom`, depending on the payment type of the associated processor. | | paymentProcessorId | `String` | The ID of the corresponding payment processor created during [Create a payment processor](/instore/integration/payments/build-payment-components.md#create-a-payment-processor). | When you use the `default: true` setting, you must assign `sortOrder` to all payment options the next time you access the API to work with payment options. If you specify a blank, null, or invalid value for `sortOrder` when `default: true`, the API returns an error and the payment option creation fails. You can have a mix of assigned and default payment options on a workstation or location. An assigned payment option takes precedence over default, so you can unassign it from the workstation or location to fall back to the default. ### Assign payment options to locations and workstations #### Assign to a location Assign payment options to a [location](/instore/use-the-instore-center/location.md) using the following endpoint: ```http title="Assign payment options to a location endpoint" POST https://api.instore.{region}.gcp.commercetools.com/{projectKey}/instore-tenants/{tenantKey}/location/{locationId} ``` Where `locationId` is the database identifier of the location, for example `682eebe13717b1001237e7ae`. #### Assign to a workstation Assign payment options to a [workstation](/instore/use-the-instore-center/devices.md#workstations) using the following endpoint: ```http title="Assign payment options to a workstation endpoint" POST https://api.instore.{region}.gcp.commercetools.com/{projectKey}/instore-tenants/{tenantKey}/workstation/{workstationId} ``` Where `workstationId` is the database identifier of the workstation, for example `683ec8c5f71306095b351b14`. #### Sample payment assignment payloads Use the following sample payloads as a starting point. Replace placeholder values and adapt fields to your requirements. ```json title="Sample assign single payment option" { "actions": [ { "action": "addPaymentOptionAssignment", "paymentOptionId": "68b1a9b06281bcbc94d503f7" } ] } ``` ```json title="Sample assign multiple payment options" { "actions": [ { "action": "setPaymentOptionAssignments", "paymentOptionIds": [ "68b1a9b06281bcbc94d503f7", "691bbaa4f1add2fda55a3950", "691bbac4f1add2fda55a3952" ] } ] } ``` ```json title="Sample unassign a payment option" { "actions": [ { "action": "removePaymentOptionAssignment", "paymentOptionId": "691bbaa4f1add2fda55a3950" } ] } ``` ```json title="Sample response when assigning payment options" { "paymentOptionAssignments": ["68b1a9b06281bcbc94d503f7"] } ``` The sequence in which you list payment options in the `SET` action for a location or workstation controls the button order that colleagues see. Otherwise, buttons are listed in the order in which they were added. If you set the `default` key to `true`, you can use the `sortOrder` field to control the order of the payment options. See [Sample payment option payloads](/instore/integration/payments/build-payment-components.md#sample-payment-option-payloads). Send a separate call for each payment option you want to remove. ### Configure external Payment resource population By default, the InStore API Server creates and populates the commercetools [Payment](/api/projects/payments.md) resource for a tender. For `BankCard` payment processors, you can instead configure InStore to create only a minimal Payment resource and pass its ID to your payment processor. Your payment processor then populates the existing Payment resource. This lets you populate the Payment and Transaction Custom Fields to match those used in your other checkout integrations. It also makes the tender flow more resilient to connection errors between the client, the InStore APIs, and the payment processor. To enable external Payment resource population, set `integrationConfiguration.delegatePaymentCreation` to `true`. Set the field when you [create the BankCard payment processor](/instore/integration/payments/build-payment-components.md#create-a-payment-processor). If you omit the field, it defaults to `false` and existing processors keep the default creation behavior. When `delegatePaymentCreation` is `true` for a `BankCard` payment processor, the payment flow changes as follows: 1. The `collect_credit_payment` route creates a minimal Payment resource that contains only the `amountPlanned` field. 2. InStore passes the ID of the minimal Payment resource as `data.paymentId` in the payment request to your payment processor. For details, see [Sample credit result](/instore/implement-instore/custom-payments/payment-extensions.md#sample-credit-result). 3. Your payment processor populates the Payment resource in commercetools, including its transactions for the full amount. 4. Your payment processor sets `paymentStatus.interfaceCode` to the literal string `"Success"` and adds successful transactions whose amounts total the Payment resource `amountPlanned.centAmount`. 5. The InStore client verifies the populated Payment resource with InStore so that InStore can record the tender on the session. This verification is client-driven; your payment processor doesn't call the verification endpoint. #### Verify the delegated payment After a successful credit payment, the InStore client automatically calls the following endpoint when `data.paymentId` is present in the `collect_credit_payment` result that your payment processor returns: ```http title="Verify a delegated Payment resource endpoint" POST https://api.instore.{region}.gcp.commercetools.com/{projectKey}/instore-tenants/{tenantKey}/payment/{paymentId} ``` Where `paymentId` is the ID that InStore sent as `data.paymentId` in the `collect_credit_payment` request to your payment processor. Use the following request body: ```json title="Sample verify delegated Payment resource request body" { "session_id": "687e4951a0b12d00124f4e8a", "tender_type": "Credit", "charge_card": { "processor_id": "pi_3OD88AC24rtrkoWS1yMbPozG", "last4": "2801", "brand": "visa", "account_type": "credit", "application_preferred_name": "", "dedicated_file_name": "", "authorization_response_code": "3030", "terminal_verification_results": "", "transaction_status_information": "" } } ``` On success, the endpoint returns the created or updated Tender Item that InStore records on the session: ```json title="Sample verify delegated Payment resource response" { "_id": "687e4a12a0b12d00124f4e9c", "external_id": "687e49e7a0b12d00124f4e91", "session_id": "687e4951a0b12d00124f4e8a", "transaction_type": "payment", "tender_type": "Credit", "payment_method": "CREDIT_CARD", "amount": { "currency": "USD", "amount": 1000, "precision": 2 }, "reference": "687e4930a0b12d00124f4e7f", "charge_card": { "processor_id": "pi_3OD88AC24rtrkoWS1yMbPozG", "last4": "2801", "brand": "visa" } } ``` The fields are defined as follows: | Field | Description | | --- | --- | | `session_id` | Required. The ID of the InStore checkout session that contains the tender. | | `tender_type` | Required. The tender type for the payment, such as `Credit`. | | `charge_card` | Required. Information about the payment card used. The `processor_id`, `last4`, and `brand` fields are required. Additional fields such as `account_type`, `authorization_response_code`, `application_preferred_name`, `dedicated_file_name`, `terminal_verification_results`, and `transaction_status_information` are optional and passed through from your payment processor. | The endpoint returns the following responses: | Status code | Description | | --- | --- | | `200 OK` | InStore records the tender on the session. | | `400 Bad Request` | `session_id`, `tender_type`, or `charge_card` is missing, or the session doesn't reference a Cart. | | `403 Forbidden` | The request doesn't include a valid token. | | `404 Not Found` | The session or Payment resource can't be found. | | `422 Unprocessable Entity` | `Payment is not yet complete`. The successful transactions don't total `amountPlanned`, so InStore doesn't record the Tender Item. | For a `200 OK` response, the Payment resource must have `paymentStatus.interfaceCode` set to `"Success"`. InStore records the Tender Item only when the sum of transactions with `state` set to `"Success"` equals `amountPlanned.centAmount`. Otherwise, InStore returns `422 Payment is not yet complete`. Cash payments are always created by InStore and don't support delegated creation. Built-in Adyen and Stripe flows are also not part of this delegated `BankCard` flow. ## Related pages - [Area overview page with navigation](/instore.md) - [Search documentation and API specs](/search.md)