A storefront takes online payments through your own merchant account at a payment provider, and every attempt is recorded as a Payment Transaction that you can trace from start to receipt. The provider and its credentials are configuration — a Payment Gateway Account record plus two references into your tenant's secret store — so changing provider is a configuration change, not a release. Payments come after checkout (see Direct to consumer) and before the order and its receipt reach finance (see invoicing and payments).
Overview
Three records do the work:
| Record | What it is | Who writes it |
|---|---|---|
| Payment Gateway Account | Your merchant account at one provider, in one environment (Test or Live) | An administrator |
| Payment Transaction | One payment attempt. A retry is a second row with the next attempt number | The storefront and the payment services only |
| Payment transaction event | The append-only trail beneath an attempt: initiated, intent created, webhook received, signature verified, captured, failed, reconciled, refunded | The payment services only. Events are never edited or deleted |
Two kinds of payment use this machinery:
- Checkout payments — a shopper pays for a basket. A confirmed payment places the sales order and writes its receipt.
- Invoice payments — a signed-in trade customer pays all or part of an outstanding invoice from the portal's Pay outstanding dialog. A confirmed payment writes a receipt against that invoice and reduces its outstanding amount.
Pay on account and cash on delivery do not use a gateway and create no payment transaction. The order is placed directly and the money is collected through the normal receivables flow.
When to use it
- You are opening a storefront that takes card, UPI, net banking or wallet payments.
- You want trade customers to settle invoices online from the portal.
- You are moving a storefront from one provider or merchant account to another.
- A customer or the support team asks what happened to a payment.
How xMatix handles it
The webhook decides, not the browser. When the shopper finishes on the provider's payment sheet or page, the browser's "success" is only a claim. The provider's signed webhook, checked against the exact bytes it sent, is what marks the attempt Captured and places the order or writes the invoice receipt. The storefront keeps polling until the order exists. A closed payment sheet is not treated as a failure, because a UPI request can still be approved from the payer's app afterwards.
Everything is frozen before money moves. For a checkout payment, the basket is repriced, stock and coupons are reserved, the delivery slot is held and the totals are locked before the provider is asked for money. The intent is created for exactly the frozen amount. So once a payment is captured, nothing commercial can stop the order from being placed. The steps are listed under Business rules.
A safety net runs every five minutes. The payment recovery job places orders for captured payments that have no order yet, writes missing invoice receipts, and asks the provider directly about attempts that have not finished. A lost webhook therefore delays an order by minutes; it does not lose it.
Money is never refunded automatically. A captured payment that cannot be turned into an order is retried and then left for an operator, never reversed by the platform.
Prerequisites
- The Commerce product licensed and the Commerce module switched on. With the module off, the webhook endpoint and the storefront's payment configuration answer "not found", and the recovery job does not run.
- A merchant account at one of the supported providers, with an API key pair and a webhook signing secret for each environment.
- Access to the tenant secret store (an integration app's Secrets tab under Setup → Platform → Integrations). See Credentials and rotation.
- Create, Read and Write permission on Payment Gateway Account. See Permissions.
- A commerce channel for the storefront that will take the payments.
Procedure
Step 1 — Store the two secrets
- Open Setup → Platform → Integrations, open an integration app and go to its Secrets tab.
- Add one secret holding the API key pair as a single value in the form
publicKey:secret, with the public key, a colon, then the secret key. - Add a second secret holding the provider's webhook signing secret.
- Note the two reference names. You type them into the gateway account in the next step.
The value must contain exactly that colon-separated pair. A value with no colon, or with nothing on one side of it, makes every payment fail with "Payment is temporarily unavailable." Never type a key into a record field: it would be stored in the tenant database, in backups and in every export.
Step 2 — Create the Payment Gateway Account
- In the App Launcher, type Payment Gateway Account and open the list. The shipped Commerce navigation has no Settings entry, so the App Launcher is the way in.
- Create a record and fill in Name, Provider, Environment (start with Test), Default Currency, Secret Reference and Webhook Secret Reference. Leave Is Active on.
- Turn on Is Default if storefronts that do not name an account should use this one. Otherwise leave it off and point the channel at the account in Step 3.
- If a different company and branch from the one that sells should receive the settlement, fill in Partner Account and Branch in the Settlement org section. See Fields.
- Save. The record opens with a Related tab, which lists the commerce channels that use this account and its payment transactions, and a Details tab.

As shipped, the list has no New button and the record page has no Edit or Delete, because the record ships without those actions. Open the create form by adding ?create=1 to the list address (/PaymentGatewayAccount?create=1), or create and change the record through the Setup Copilot or the Data API. An administrator can add the buttons; see Commerce screens and records.
Step 3 — Point the channel at the account
Open the storefront's Commerce Channel and set Payment Gateway Account. If you leave it empty, the channel uses the tenant's default gateway account. A channel that names an account never falls back to the default: if the named account is inactive or deleted, card and UPI payments are not offered on that channel.
Step 4 — Register the webhook with the provider
In the provider's dashboard, add a webhook for this account that points at your tenant's API address with the path /api/1.0/commerce/webhooks/<provider>/<gateway account id>. Use the lowercase provider name as it is stored on the account and the account record's id. Each account has its own URL, so each delivery is checked against that account's own signing secret.
Subscribe to the payment authorized, captured, failed and refund events that the provider's onboarding guide lists for your account type. The built-in adapters recognise events by their names: a name containing "refund" counts as a refund, "captur", "succeed" or "paid" as a capture, "authoriz" as an authorisation and "fail" as a failure. Other events are recorded and change nothing.
Step 5 — Test end to end, then go live
- In a Test environment, buy something on the storefront with the provider's test card or UPI flow.
- Open Payment Transactions in the Commerce app and find the attempt. Its Status should be Captured and its Order filled in. The Events tab should show Initiated, Intent Created, Webhook Received, Signature Verified and Captured.
- When the test is clean, create a second gateway account with Environment set to Live and the live secrets, register its own webhook URL, and point the live channel at it.
The environment check runs when a payment starts, not when you save, so only a real payment start in the target environment proves the setup.
Step 6 — Follow up a payment
- Open Payment Transactions and search by order, invoice, amount or the provider's payment id (Gateway Payment).
- Read Status, then the Events tab, newest last. Each event records who wrote it (the customer's request, a webhook or the recovery job) and keeps the provider's message and payload.
- For a failure, read Failure Category and Failure Message. A declined card or bank failure needs the payer to try another method. A gateway error or timeout is worth retrying. A run of signature invalid failures means the webhook secret is wrong, or that someone other than the provider is calling the endpoint.
- For a checkout attempt, open the linked Checkout Session to see where the shopper stopped. See Commerce screens and records.
Step 7 — Issue a refund
There is no refund button in xMatix. Refund the payment in the provider's own dashboard. The provider's signed refund webhook is then recorded as a Refunded event on the attempt's Events tab. The attempt's Status stays Captured, because a captured attempt never changes status again, and Refunded Amount is not filled in. The order and its receipt are not changed. Raise a credit note or reverse the receipt in Sales and Finance as you would for any refund.
Fields
Payment Gateway Account
| Field | What it means | Notes |
|---|---|---|
| Name | The account's name, for example "Cards (Live)" | Required |
| Label | A display caption | Optional |
| Provider | The adapter this account charges through | Required. The list offers the two built-in adapters. A provider added by the platform operator as a descriptor is stored by its lowercase name. It cannot be chosen from this list, so it is set through the Data API |
| Environment | Test or Live | Required, defaults to Test. Must match the environment the platform runs in; see Business rules |
| Merchant Account | Your account identifier at the provider | For reference only. One provider, merchant account and environment can be registered only once |
| Default Currency | The three-letter currency the account settles in | Used when the basket carries no currency, and for every invoice payment |
| Secret Reference | The name of the secret holding publicKey:secret | Required in practice: without it every payment fails with "This payment account has no credentials configured." Only the public key is ever sent to the browser |
| Webhook Secret Reference | The name of the secret holding the webhook signing secret | Without it the webhook endpoint refuses every delivery, so payments never reach Captured until the recovery job asks the provider |
| Is Active | Whether the account can be used | Inactive accounts are never selected, not even for a channel that names them |
| Is Default | The account used by channels that name none, and by invoice payments on such channels | At most one default per provider. Keep only one default across all providers: the platform picks any active default, and with two it is not defined which one is used |
| Capabilities Json | What the provider supports | Shown on the Details tab, Capabilities section. Leave it as it is |
| Partner Account | The company whose bank account receives this account's settlements | Settlement org section. Optional. Fill it in together with Branch, or leave both empty |
| Branch | The branch of that company that receipts are booked against | When both are filled in, the receipt for a checkout payment is booked on this company and branch instead of the order's. Invoice-payment receipts always follow the invoice |
Payment Transaction
Every field is written by the payment services; the record is read-only on screen. On the Details tab:
| Field | What it means | Notes |
|---|---|---|
| Name | The attempt's number | PAY- followed by the start time (UTC, year to second) and the attempt number for checkout payments. INVPAY- for invoice payments |
| Status | Created, Authorized, Captured or Failed | Follows the event trail. Captured and Failed are final and never change. Refunded and Partially Refunded are in the list, but a captured attempt never moves to them |
| Gateway | The provider the attempt ran through | Copied from the gateway account |
| Method | — | Not filled in by the current payment flows |
| Attempt Number | 1 for the first attempt, then 2, 3… | Counted per checkout session or per invoice |
| Request Idempotency Key | The key that makes a retried request return this attempt instead of charging again | Technical |
| Payment Gateway Account | The account the money ran through | Use it to reconcile when you have several accounts |
| Amount | The amount the provider was asked for | For checkout, exactly the frozen basket total |
| Currency Iso Code | The currency of the amount | |
| Refunded Amount | — | Not filled in. Refunds show as events only |
| Settled Utc | When the attempt reached a final status | In UTC |
| Gateway Intent | The provider's id for the payment request | Webhooks are matched to the attempt by this id |
| Gateway Payment | The provider's id for the actual payment | Search by it when the provider's dashboard gives you a payment id. Copied to the receipt's transaction reference |
| Gateway Refund | — | Not filled in |
| Checkout Session, Cart | The checkout this attempt paid for | Empty for invoice payments |
| Order | The order the payment placed | Empty until the order exists. A captured attempt with no order is the recovery job's queue |
| Invoice | The invoice being paid | Invoice payments only |
| Failure Category | Declined, Gateway Error, Timeout, Signature Invalid, User Abandoned or Unknown | Filled in when the provider reports a failure code. When the payment could not even be started, the category is on the Failed event only |
| Failure Message | The provider's or the platform's message | After the recovery job finds a capture, this shows "captured, discovered by reconciliation" — that is a note, not a failure |
Business rules
Starting a checkout payment. These checks run in order and stop at the first failure, all before the shopper is charged:
- The checkout must be open or awaiting payment; otherwise "This checkout can no longer be paid."
- The basket is repriced. A change beyond the Accepted price drift at payment (%) setting (default 0) fails with
PRICE_CHANGED, and the shopper confirms the new total. - On channels that refuse to oversell, stock is reserved; otherwise
OUT_OF_STOCK. - A coupon is checked again and, for limited codes, reserved; otherwise
COUPON_EXHAUSTED. - A chosen delivery window is reserved; otherwise
DELIVERY_SLOT_UNAVAILABLE. - The totals are locked and the checkout moves to Awaiting Payment. The cart cannot be changed from now on.
- The gateway account is resolved (see below). If that fails, the holds are released, the checkout reopens and the shopper sees the reason.
- The attempt is recorded and a payment intent is created for exactly the locked amount. If the provider refuses, the attempt is marked Failed (Gateway Error), the holds are released and the shopper sees "Payment could not be started. Please try again."
Resolving the gateway account. The channel's own account is used if it is set, otherwise the tenant's active default. Then, in order: no account gives GATEWAY_NOT_CONFIGURED; an account whose Environment differs from the platform's gives GATEWAY_ENVIRONMENT_MISMATCH; a provider the platform does not know gives GATEWAY_UNSUPPORTED; an empty Secret Reference or a secret that cannot be read or split into publicKey:secret gives GATEWAY_SECRETS_UNAVAILABLE. The secret is read from the tenant's own store first. A tenant secret that is missing never falls through to another merchant account.
Payment options shown to the shopper. Before checkout the storefront asks for the channel's payment configuration: the provider name, the currency and whether cash on delivery is on. No ids, references or secrets are returned. If the channel has no usable active account, card and UPI options are not offered. Pay on account and cash on delivery do not depend on a gateway.
Webhook deliveries. The endpoint takes no sign-in; the provider's signature is the authentication. A delivery is refused when:
- the account id in the URL does not exist or belongs to another provider (not found);
- the account has no webhook signing secret (service unavailable). Fix the reference; the provider retries;
- the signature does not verify (unauthorised). For one of the built-in adapters, a signature timestamp more than five minutes old also fails.
A verified delivery is recorded and answered with success even if acting on it fails, because the recovery job will finish the work. A repeated delivery of the same provider event is recorded once and changes nothing. A delivery for an attempt that does not exist is ignored.
What a verified event does.
- Captured — the attempt becomes Captured. For a checkout, the session becomes Paid, the order is placed and the receipt is written. The receipt has payment type Payment Taken, mode of payment Bank and the provider's payment id as its reference. For an invoice, the receipt and its adjustment against the invoice are written and the invoice's paid and outstanding amounts are recalculated.
- Authorized — the attempt becomes Authorized and waits for the capture.
- Failed — the attempt becomes Failed. For a checkout that was awaiting payment, the session reopens with
PAYMENT_FAILEDand the delivery window is released straight away, so the shopper can try another card. - Refund — recorded as a Refunded event. It changes the status only of an attempt that was not yet final.
Final statuses are final. Once an attempt is Captured or Failed, a later event is still recorded on the trail but never changes the status. A late "failed" after a capture cannot un-pay an order.
Invoice payments.
- Only the signed-in customer's own invoices can be paid. Another customer's invoice is reported as not found.
- No amount means the whole outstanding amount. Part payments are allowed. An amount of zero or less is refused, and so is an amount above what is still payable: "That is more than this invoice's outstanding amount."
- Money already in progress counts against what can still be paid. While an earlier attempt is unfinished, or captured but not yet receipted, a new payment can take only the rest: "Only 1200.00 of this invoice can still be paid — 800.00 is already in progress or awaiting posting." When the full amount is in progress: "A payment for the full outstanding amount is already in progress."
- An invoice with nothing outstanding is refused with "This invoice has nothing outstanding."
- Two clicks on Pay now return the same attempt and charge nothing twice.
- The receipt carries the invoice's company and branch, applies at most the invoice's open amount, and leaves any excess on the customer's account.
The recovery job runs every five minutes per tenant while the Commerce module is on:
- Place paid orders. A captured checkout payment with no order, older than two minutes, is placed again. Every try is a Reconciled event. After five tries the payment is parked: a last event says it needs an operator, and the job does not select it again.
- Write missing invoice receipts. A captured invoice payment with no receipt, older than two minutes, is settled.
- Reconcile. An attempt still at Created or Authorized for longer than Reconcile a pending payment after (minutes) (default 10) is checked with the provider. If the provider reports a capture, the job captures the attempt and places the order or writes the receipt. A reported failure marks the attempt Failed. Any other answer adds a "gateway reports…" event. After five such answers the attempt is parked with a final event and is not queried again; its status is left unchanged. A webhook can still settle it later.
Example
A homeware brand sells on its storefront through one merchant account.
- The administrator stores the live key pair,
pub7Hq2:sec93Lw, as the secret storefront-live-keys and the signing secret as storefront-live-webhook. - They create the Payment Gateway Account "Cards (Live)", with the built-in provider, Environment Live, Default Currency INR, both references, Is Active on and Is Default on. Partner Account and Branch are left empty, because the selling company also receives the money.
- They register
/api/1.0/commerce/webhooks/<provider>/<id of Cards (Live)>in the provider's dashboard and subscribe to the payment and refund events. - A shopper pays ₹2,480 by UPI. The attempt PAY-20261102101530-1 is created for ₹2,480 and the payment sheet opens. The shopper approves the request in their UPI app.
- The captured webhook arrives within seconds. The attempt shows Captured, the checkout Placed, and the new sales order is linked in Order. A receipt of ₹2,480 (mode Bank) is written against the order.
- A week later the shopper returns one item. Support refunds ₹620 in the provider's dashboard, and the attempt gains a Refunded event. Finance raises the credit note and refund entry in xMatix.
Training
Practice exercise
In a sandbox running in Test:
- Create a Payment Gateway Account with Environment Live and point a test channel at it. Try to pay on the storefront. Expected: card and UPI payment is refused with "The configured payment account belongs to a different environment." No intent is created.
- Change Environment to Test and clear Webhook Secret Reference. Pay with a test card. Expected: the payment sheet completes, but the attempt stays Created. After the reconcile window (default 10 minutes, then the next five-minute run), the job captures it and places the order. The Events tab shows Captured written by the job, and Failure Message reads "captured, discovered by reconciliation".
- Put the webhook secret reference back, pay again and compare. Expected: the attempt is Captured within seconds, with Webhook Received and Signature Verified events.
Quick reference
- One Payment Gateway Account per provider, merchant account and environment.
- The account holds secret names. The secrets live in the tenant secret store, with the key pair stored as
publicKey:secret. - Test and Live are enforced when a payment starts, not when you save.
- A channel that names an account never falls back to the default.
- Keep a single Is Default account across all providers.
- The signed webhook confirms a payment. The browser's success page never does.
- The recovery job runs every five minutes and parks a payment after five tries.
- Refund in the provider's dashboard, then raise the credit note in xMatix. The attempt's status stays Captured.
- Payment Transactions are read-only; read the Events tab to see what happened.
Permissions
Access follows the security profile's grants on each record type:
| Profile (as shipped) | Payment Gateway Account | Payment Transaction and its events |
|---|---|---|
| System Admin | Full | Full grant, read-only on screen |
| Standard User | Full | Full grant, read-only on screen |
| Sales Executive | Full | Full grant, read-only on screen |
| Sales Manager | Read and View All | Read and View All |
| Sales, Service | No grant | Full grant on Payment Transaction, read-only on screen |
Payment Transactions are written only by the payment services. Whatever the profile grants, the screen offers no New or Edit, and the page header has no actions. Because the shipped Standard User and Sales Executive profiles can change gateway accounts, narrow them under security profiles before you go live. Anyone who can edit a gateway account can redirect where storefront money is collected. A user with no Read grant on Payment Gateway Account cannot open the list.
The storefront side needs no back-office permission. Shoppers pay by card or UPI without signing in; paying an invoice requires a signed-in portal customer.
Configuration
| Setting | Where | What it changes |
|---|---|---|
| Payment Gateway Account records | App Launcher → Payment Gateway Account | Provider, environment, credentials, default account and settlement org |
| Payment Gateway Account on the channel | The Commerce Channel record | Which account the storefront charges through. Empty uses the default account |
| Cash on delivery | payments.cashOnDelivery in the channel's Settings Json | Whether the cash-on-delivery option is offered; see Direct to consumer |
| Reconcile a pending payment after (minutes) | Setup → Feature Hub → Commerce → Settings, Payments group | How long an unfinished attempt waits before the job asks the provider. Default 10, from 1 to 1440 |
| Accepted price drift at payment (%) | Same page, Checkout group | How far the reprice at payment may move before the shopper must re-confirm. Default 0, up to 50 |
| Checkout session lifetime (minutes) | Same page, Checkout group | How long the checkout and its stock and coupon holds last; allow for slow bank redirects. Default 45, from 5 to 480 |
| Secrets | Setup → Platform → Integrations → an integration app → Secrets | The key pair and webhook signing secret. Rotate by saving a new value under the same reference; see Credentials and rotation |
| Provider webhook | The provider's own dashboard | The per-account webhook URL and subscribed events |
Adding a provider. Providers are written by the platform, never by tenants. A hosted-payment-page provider whose webhooks are signed with HMAC-SHA256 over the raw body can be added by the platform operator as a descriptor, a configuration document deployed without a release. The engine accepts only HTTPS addresses, keeps secrets out of addresses and bodies, uses the Test addresses for Test accounts, and reports any event it cannot map as unknown. An invalid descriptor makes only that provider unsupported (GATEWAY_UNSUPPORTED). Providers that need an on-page payment sheet or another signature scheme need a built-in adapter, which ships with the platform. Once a provider is available, you select it on a gateway account; descriptor providers open the provider's hosted page through the storefront's redirect path, with no storefront change.
Common problems
Card and UPI options do not appear on the storefront
The channel has no usable gateway account. Either the channel names an account that is inactive or deleted, or it names none and there is no active Is Default account. Activate the account or set Payment Gateway Account on the channel.
"No payment gateway account is configured for this storefront."
As above: the shopper reached payment without a resolvable account (GATEWAY_NOT_CONFIGURED). The checkout reopens, and its holds are released.
"The configured payment account belongs to a different environment."
The account's Environment does not match the environment the platform runs in. A Test account cannot take payments on a live deployment, and a Live account cannot take them on a test one. Point the channel at the account for this environment.
"Payment provider '…' is not supported."
The Provider value names no built-in adapter and no valid descriptor. Correct the value, or ask the platform operator whether the descriptor loaded.
"This payment account has no credentials configured." or "Payment is temporarily unavailable."
Secret Reference is empty, the secret cannot be read from the tenant store, or its value is not a publicKey:secret pair. Check the reference name against the Secrets tab and the value's format.
The shopper paid, but the order shows up only after several minutes
The webhook did not arrive or was refused, so the recovery job found the capture instead. Look at the attempt's Events tab: no Webhook Received event means the provider is not delivering. Check the URL, the account id in it and the subscribed events. If deliveries are refused, check that Webhook Secret Reference is filled in and matches the signing secret in the provider's dashboard.
A payment is captured but has no order
Open the attempt's Events tab. Reconciled events reading "placement retry failed" show the job is still trying. After five tries the last event says the payment is parked and needs an operator. The money stays captured. Find the cause from the checkout session and the error code in the event, then place the order by hand or refund the payment in the provider's dashboard.
A payment stays at Created and the job has stopped asking
After five "gateway reports…" answers the attempt is parked. When the provider reported it was still waiting, the shopper most likely walked away and no money was taken. When it could not find the payment at all, compare the attempt's Gateway Intent with the provider's dashboard.
"Only … of this invoice can still be paid" when paying an invoice
Part of the outstanding amount is held by an earlier attempt that has not finished, or that was captured but not yet receipted. Wait a few minutes for it to complete or be reconciled, then pay the rest.
Pay now on Pay outstanding shows "started", but no payment sheet opens
On the shipped portal, Pay outstanding → Pay now records the payment attempt and confirms it with "Payment of … started", but it does not open the provider's payment sheet. The attempt stays at Created, the reconcile pass checks it, and it is parked after five checks. Until the portal opens the sheet for invoice payments, collect invoice payments another way and receipt them in Finance.
A refunded payment still shows Captured
That is expected. A captured attempt keeps its status. The refund appears as a Refunded event on the Events tab, and the credit note and refund entry are raised in xMatix separately.
Common questions
Can different storefronts use different providers?
Yes. Each channel can name its own gateway account, so one storefront can charge through one provider and another through a second, or you can move a storefront by changing the account on its channel. The shopper sees only a different payment sheet or page. Keep at most one Is Default account across all providers, and name the account explicitly on every channel that should not use it. With two defaults, a channel that names no account could charge through either one, and you cannot control which.
Where does the money get booked when a distributor fulfils the order?
By default, the receipt for a storefront payment is booked on the order's own company and branch. When the brand's single merchant account collects the money for orders that distributors fulfil, set Partner Account and Branch in the gateway account's Settlement org section. Receipts for payments through that account are then booked on that company and branch, so the ledger follows the money. Fill in both fields or neither; with only one filled in, the order's own pair is used.
What happens if a webhook never arrives?
Within the reconcile window (default 10 minutes) plus up to five minutes for the next run, the recovery job asks the provider about the attempt. If the provider reports a capture, the job captures the attempt and places the order or writes the receipt. Meanwhile the storefront shows the checkout as pending rather than failed. The Events tab then shows the capture as written by the job instead of the webhook, so you can tell that the delivery went missing and fix the webhook registration.
Why did the payment succeed but no order appear immediately?
The order is placed by the verified webhook or by the recovery job, never by the browser's success message. Normally the gap is a few seconds. A checkout session showing Paid but not Placed is that window. If it lasts longer than a few minutes, see A payment is captured but has no order.
Can xMatix refund a payment?
No. Refunds are issued in the provider's dashboard. The refund webhook is recorded on the attempt's event trail, but it does not change the order, the receipt or the attempt's status. Raise the credit note and refund entry in Sales and Finance as for any other refund.
