# Set up payment extensions Learn how to integrate with payment providers. Payment extensions allow you to connect InStore to your payment providers. Use the information below to set up connections for credit cards, stored-value cards, digital wallet, and pay on account. ## About asynchronous payment processing InStore supports asynchronous payment processing, which allows you to process payments of uncertain duration. When a payment is initiated, InStore sends a request to your payment extension endpoint. Instead of requiring a synchronous response, InStore provides callback URLs that your integration uses to send updates and the final result. Instead of relying on a standard request-response pattern, respond to the initial request with a `200` acknowledgment and continue the payment flow asynchronously. The request body includes two InStore-generated URLs: `statusUrl`, which you can use to send intermediate updates and client messages, and `callbackUrl`, which you use to send the final payment result. At the end of processing, use the callback URL to send the payment provider's final result, which is whether the payment was successful or failed. In contrast, the composable tender extension payment type is processed directly within the user interface rather than through a backend API call. See [Composable tender extension](/instore/integration/payments/overview.md#composable-tender-extension). ## Prepare to receive requests from InStore Details of setting up a custom extension and verifying the signature may vary depending on the programming language, framework, and server setup you are using. To set up a payment extension, do the following: 1. Set up endpoints in your payment extension service to receive the payment extension data. You need to create a script or program that listens for incoming requests at a specific URL and supports all endpoints for the payment types you use. 2. Make sure your endpoints can handle the request format used by each action. POST actions use JSON-formatted request and response bodies. GET actions use route or query parameters, as documented for the action. 3. Configure all endpoints to verify the HMAC-SHA256 signature that InStore sends with each request. This signature is a security measure that ensures the data being sent to your endpoint is actually from InStore and has not been tampered with. 4. To verify the signature, you'll need to perform the following steps: 1. Extract the signature from the incoming proxy request headers. The header key for the signature is `X-Signature`. 2. Compute an HMAC-SHA256 hash over the JSON-stringified request payload, using the `apiKey` value that you configured for the extension in the InStore Center. 3. Compare the generated hash to the signature included in the incoming request headers. If they match, the data has not been tampered with and can be safely processed by your endpoint. 4. Once you've verified the signature, you can process the incoming data as needed. This may involve storing it in a database, triggering other actions in your system, or sending a `200` response to acknowledge receipt. 5. Review the header information, shared request fields, and processor webhook paths below. Ensure that your endpoints are set up to receive and process this data correctly. 6. View the section [Send InStore the final result of an asynchronous payment](/instore/implement-instore/custom-payments/payment-extensions.md#send-instore-the-final-result-of-an-asynchronous-payment) to see the specific data that your payment provider must send. The API key is a symmetric secret, not an asymmetric private key. Both InStore and your extension must hold the same key. ### Header information that you receive in all InStore payment requests | Name | Purpose | | --- | --- | | `X-Signature` | Sends a hash that verifies the current payload value with the API key assigned to the payment processor. | | `X-Correlation-Id` | Sends an identifier to track the process from all applications: InStore Core, InStore API, and the webhook service. | | `X-Transaction-Id` | Sends an identifier to track the full checkout transaction from all applications: InStore Core, InStore API, and the webhook service. | | `X-API-Key` | Sends a key to authenticate the InStore API call to the payment processor service. | ### Fields that you receive in all InStore payment requests | Field | Purpose | | --- | --- | | `payload_url` | Identifies the services to call and always matches the webhook URL payload. | | `payload` | Sends the request data, which varies with the payment process type. | | `verification_signature` | Sends a hash that verifies the authenticity of the request. This serves to sign the current payload value with the API key that was assigned to the payment processor. | | `tenant_id` | Identifies the tenant that is being called. | | `requestId` | Identifies the current process. | | `callbackUrl` | Specifies the webhook service you call to send the messages to InStore. | | `statusUrl` | Specifies the webhook service you call to update status and send messages to the client. | ### Processor webhook paths that we provide for specific payment types | Payment Type | Processor Webhook Path | Purpose | | --- | --- | --- | | BankCard | `initialize_credit_processor` | Requests that your service initialize the payment provider. | | | `connect_card_reader` | Requests that your service connect to the credit reader. | | | `capture_payment` | Requests the capture of payment. Only applies if the provider supports payment capture before collecting payment. | | | `collect_credit_payment` | Requests the collection of the payment through a pin entry device (PED). | | | `create_credit_refund` | Requests a credit refund. | | | `cancel_credit_payment` | Requests that your service cancel a previous credit payment request. | | | `user_response` | Sends information that was provided by the user. | | GiftCard | `storedvaluecard` | Requests the creation of a new stored-value card payment. | | | `storedvaluecard_process` | Sends instructions for processing this stored-value card's information. | | Wallet | `initialize_wallet` | Requests the processing of a digital wallet payment. | | | `pay_wallet` | Sends information for processing this wallet payment. | | | `cancel_wallet` | Requests that your service cancel the wallet payment. | | | `refund_wallet` | Requests a refund for the wallet payment. | | Pay on Account (POA) | `pay_on_account` | Requests the processing of a pay-on-account payment. | | | `payonaccount/process` | Sends instructions for processing this POA payment's information. | | | `payonaccount/create_account` | Requests that your service create an account record for this pay-on-account payment. | | | `payonaccountlog` | Directs your service to log information about this pay-on-account payment. | ## Respond asynchronously to InStore requests Use callback URLs to [send optional messages and status updates](/instore/implement-instore/custom-payments/payment-extensions.md#send-intermediate-messages-and-updates-to-instore); [add user prompts](/instore/implement-instore/custom-payments/payment-extensions.md#add-user-prompts-during-payment-processing); [extend the payment timeout](/instore/implement-instore/custom-payments/payment-extensions.md#dynamically-replace-the-timeout-for-an-asynchronous-payment); and [send the final result of the payment](/instore/implement-instore/custom-payments/payment-extensions.md#send-instore-the-final-result-of-an-asynchronous-payment). InStore stops accepting messages and updates after receiving the final result of the payment process. ### Use the status callback to send updates during an asynchronous payment You can choose to send [updates](/instore/implement-instore/custom-payments/payment-extensions.md#about-asynchronous-payment-processing) back to InStore using the `statusUrl` callback. Use this callback to send optional messages and other updates: `/webhook/payment/:requestId/status` #### Send intermediate messages and updates to InStore Messaging is available for all payment types, as described in [Customize payment stepper messages](/instore/customization/payment-stepper-customization.md). To send messages, include the following body in the request: ```text { message: string severity: string properties?: object } ``` Where | Field | Purpose | | --- | --- | | `message` | The content of the message to display to the user. For example, `"Processing payment..."`. | | `severity` | The importance of the message, which can be `Info`, `Warning`, or `Error`. InStore uses this value to determine how to display the message. | | `properties` | An optional object that contains any additional information that the retailer can use with downstream processing. | #### Add user prompts during payment processing Prompts that require user input during payment are available for select payment types, specifically: - payments of type Credit - the `redeem` action for stored-value card payments. No prompts are available for the stored-value card number-retrieval action. Use the `status` callback. See [Send user prompts](/instore/implement-instore/custom-payments/user-prompts.md#top). #### Dynamically replace the timeout for an asynchronous payment You can use the `status` callback to dynamically replace the default timeout by specifying a different value in the `keepAliveMs` field within a message block. Use this body: ```json { "message": "Common.messages.authorizingCard", "severity": "Info", "keepAliveMs": 10000 } ``` In this example, the `keepAliveMs` field is set to `10000` milliseconds (10 seconds). Another way to control timeouts is to specify them in the [payment processor](/instore/integration/payments/build-payment-components.md#create-a-payment-processor) for the payment method. ### Send InStore the final result of an asynchronous payment The final result of the payment process is sent to InStore using the callback URL. The callback path is the same for all asynchronous payment types, but the payload varies by payment type. Depending on the payment type, some fields are returned inside a `data` object and others at the top level. After InStore receives the final callback payload, no further updates are accepted. Use this callback path to send the final result of the payment process: `/webhook/payment/:requestId` Many payment types use a payload shape similar to the following: ```text { result: string data?: object method?: string } ``` Where: | Field | Purpose | | --- | --- | | `result` | Indicates the final status for payment types that use the `result` field. Supported values vary by payment type. | | `data` | Contains payment-type-specific response fields. Some payment types return those fields at the top level instead of inside `data`. | | `method` | Is the value that your payment service uses to identify the payment method. If not specified in your result, InStore assigns the method from the [payment processor configuration](/instore/integration/payments/build-payment-components.md#create-a-payment-processor). | Review the examples for your payment type and return the payload shape documented for that payment type. #### Sample credit result Use a payload shape similar to the following when you return the result of the `collect_credit_payment` request through the callback URL: Processor webhook path: `collect_credit_payment` When the `BankCard` payment processor is configured with `delegatePaymentCreation: true`, the `collect_credit_payment` request that InStore sends includes `data.paymentId`. This is the ID of the minimal commercetools [Payment](/api/projects/payments.md) resource that InStore created with only `amountPlanned`. Your payment service must populate that Payment resource, including its transactions, and set `paymentStatus.interfaceCode` to `"Success"`. Return the same `paymentId` value inside `data` in your `collect_credit_payment` result so that the InStore client can verify the populated Payment resource. When `delegatePaymentCreation` is `false`, `paymentId` is not present and you don't return it. ```text { result: Success | Failed data: { cartKey: "123e4567-e89b-12d3-a456-426614174000" paymentId: "687e49e7a0b12d00124f4e91" processor: "Brand-X Debit Card" processor_id: "213121111" last4: "4242" brand: "visa" account_type: "credit" application_preferred_name: null dedicated_file_name: null authorization_response_code: "3030" application_cryptogram: null terminal_verification_results: null transaction_status_information: null amount: { amount: 123 currency: "USD" precision: 2 timestamp: "2024-06-02T14:20:55.488Z" } } } ``` Where | Attribute | Type | Description | | --- | --- | --- | | `cartKey` | String | Unique identifier for the cart, which was generated by InStore. | | `paymentId` | String | ID of the commercetools Payment resource, sent by InStore as `data.paymentId` in the `collect_credit_payment` request. Only present when `BankCard` is configured with `delegatePaymentCreation: true`. The client uses it to [verify the delegated Payment resource](/instore/integration/payments/build-payment-components.md#verify-the-delegated-payment). **Omit this field when `delegatePaymentCreation` is `false`.** | | `processor` | String | The label that represents this payment method in the Total area of the payment page. | | `processor_id` | String | The identifier for the payment processor that was used for the transaction. | | `last4` | String | Last four digits of the credit card number. | | `brand` | String | Brand of the credit card. | | `account_type` | String | Type of card account, such as `credit` or `debit`. | | `application_preferred_name` | String | Cardholder's name as displayed on their credit or debit card. | | `dedicated_file_name` | String | Identifier used by the card issuer. | | `authorization_response_code` | String | Code that indicates the status of the transaction request sent to the card issuer. | | `application_cryptogram` | String | Digital signature generated by the card’s embedded chip. | | `terminal_verification_results` | String | Series of bits set by the PED for deciding how to respond to the request for payment. | | `transaction_status_information` | String | Status of the transaction, which is returned by the card issuer. | | `amount` | Object | Amount object that contains the following fields: `amount`, `currency`, `precision`, `timestamp` | #### Sample stored-value inquiry result and redemption result You can use the following payload shapes for the two parts of a stored-value transaction. The first part returns the card information for `storedvaluecard/{cardNumber}`, and the second part returns the redemption result for `storedvaluecard_process`. Processor webhook path: `storedvaluecard/{cardNumber}` ```text { result?: "Success" | "Failed" balance?: { amount: number, currency: string, precision: number } cardType?: string issuer?: string number?: string pinRequired?: boolean reason?: string } ``` Processor webhook path: `storedvaluecard_process` Action: `Redeem` ```text { result?: "Success" | "Failed" reason?: string balance?: { amount: number, currency: string, precision: number } number?: string issuer?: string method?: string transaction_id?: string payment_id?: string } ``` Where: | Attribute | Type | Description | | --- | --- | --- | | `number` | String | Identifies the stored-value card number that is being redeemed or whose validity is being checked. | | `result` | String | Indicates the status of the card information retrieval or redemption attempt. Value can be `Success` or `Failed`. | | `balance` | Object | Provides the current balance `amount`, `currency`, and `precision`. | | `cardType` | String | Retailer's configuration for card type. | | `issuer` | String | Retailer's configuration for issuer. | | `pinRequired` | Boolean | If `true`, InStore prompts the user for a PIN during the transaction. | | `reason` | String | Retailer's configuration for supporting information. For example, this could be an error code for a result of `Failed`. | | `method` | String | The payment method used for the transaction. | | `transaction_id` | String | Identifier returned by the payment provider for the transaction. | | `payment_id` | String | Identifier returned by the payment provider for the payment. | #### Sample pay-on-account result You can use the following payload shape when you return the result of the `payonaccount/process` request through the callback URL. Processor webhook path: `payonaccount/process` ```text { status: succeeded | failed reason?: string balance: { amount: number, currency: string, precision: number } credit_limit: { amount: number, currency: string, precision: number } account_number: string company_name: string authorized_user_id?: string transaction_id: string payment_id: string method: string } ``` Where the following are the fields that InStore uses: | Attribute | Type | Description | | --- | --- | --- | | `status` | String | Indicates the result of the pay-on-account payment attempt. Value can be `succeeded` or `failed`. | | `transaction_id` | String | The transaction ID that will be passed to Composable Commerce. | | `payment_id` | String | The payment servicer's identifier for this payment. | | `method` | String | The payment method used for the transaction. | #### Sample wallet result You can use the following payload shape when you return the result of the `pay_wallet` request through the callback URL. Processor webhook path: `pay_wallet` ```text { code?: string result: string error_message?: string transaction_id?: string merchant_id?: string terminal_id?: string method?: string total?: number } ``` Where | Attribute | Type | Description | | --- | --- | --- | | `code` | String | Applies only when the `result` is `"failed"`. Options are `"A2"` (timeout) or `"A7"` (declined). | | `result` | String | Indicates the result of the wallet payment attempt. Value can be `"success"` or `"failed"`. | #### Sample cancel wallet request and result Use the following payload shape for `cancel_wallet`: ```text { transactionId: "dc3bcf40-b8dc-4ad6-a6df-fb7733a3ca67" paymentProcessorId: "6a0453a27d9e46013347f552" requestId: "c2ef250c-32c7-4ea6-8c70-a715c579b6e1" } ``` Use the `requestId` from the earlier `pay_wallet` process as the `transactionId`. Expected result shape: ```text { result: "success" error_message: "" code: "00" timestamp: "2026-06-02T14:20:55.488Z" } ``` InStore checks only the `result` field in the success response. The following is a failure result shape for the `pay_wallet` cancel request: ```text { code: "A10" error_message: "Payment Cancel" result: "failed" timestamp: "2026-06-02T14:20:58.811Z" } ``` Use any `code` value except `A2` or `A7` in the failure response. ## Related pages - [Area overview page with navigation](/instore.md) - [Search documentation and API specs](/search.md)