Sign InGet Started
Back to Blog

Run a PayXor checkout on a testnet first

A testing app quotes only testnet chain IDs. Ethereum Sepolia is the network with a contract, mock tokens, and a faucet.

October 19, 2026•PayXor Team

A payment integration is easier to prove when the tokens are free and the chain is a testnet. PayXor keeps that trial on its own app. The stage you pick in the dashboard decides which chain IDs the API will sign, and the quote itself carries the chain ID so a signature from one network stays on that network.

A frosted-glass card on a dark navy field, with a wallet linked by a cyan line to three coins and one of eight nodes glowing behind it.

Eight testnets, one catalog

@payxor/types lists eight testnet chain IDs in TESTNET_CHAIN_IDS. isTestnet(chainId) is true for those IDs, and getTestnetChains() returns their names and configs.

  • Ethereum Sepolia, 11155111
  • Base Sepolia, 84532
  • Arbitrum Sepolia, 421614
  • Polygon Mumbai, 80001
  • Optimism Sepolia, 11155420
  • Avalanche Fuji, 43113
  • BNB Chain Testnet, 97
  • zkSync Sepolia, 300

Each of those chains has a USDC entry in STABLECOINS_BY_CHAIN. getMockTokensForChain adds MockUSDC, MockUSDT, and MockDAI. getAllTokensForChain returns both lists, and that combined list is what the API accepts when you enable tokens for an app.

Decimals follow the token, on testnets as on mainnet. USDC is 6 decimals on every testnet except BNB Chain Testnet, where it is 18. MockUSDC and MockUSDT match that split. MockDAI is 18 on every chain.

A testing app is a separate stage

Two frosted-glass panels on a dark navy field: a closed vault of dim coins on the left, and an open tray of cyan coins with a tap on the right.

On create app, the stage control is Production or Testing. Testing fills the network list from getTestnetChains(). Production fills it from getMainnetChains(). Changing the stage clears the networks and tokens you had selected, so a testnet selection is dropped before a production save.

The quote route checks the same boundary:

  • A chain the app did not enable returns Chain {id} is not supported by this app.
  • A testing app that requests a mainnet chain returns Testing apps can only use testnet chains. Chain {id} is a mainnet chain.
  • A production app that requests a testnet chain returns Production apps can only use mainnet chains. Chain {id} is a testnet chain.

Every enabled network needs at least one token address, and that address has to appear in STABLECOINS_BY_CHAIN or MOCK_TOKENS_BY_CHAIN for that chain. The edit screen uses the same stage control. Switching stage there clears the selection again, and the save is checked against the new stage.

What is deployed on Sepolia

The catalog lists eight testnets. The addresses the packages publish for a live checkout are on Ethereum Sepolia.

getContractAddress(11155111) in @payxor/sdk returns the PayXor contract 0x96555Bc5a0069168590266A6e1f074923ecC668c. The faucet address in @payxor/types is 0xbc513b7A213276A839Bdd07a4D82DcfaAd3a7982. The mock tokens on that chain are:

  • MockUSDC, 6 decimals, 0x61C16DdA462EB7dC80084655BF083855581B7D76
  • MockUSDT, 6 decimals, 0xacc10698EafB8E63dd542aB8ad1d58a75B4e5349
  • MockDAI, 18 decimals, 0x6da5567587c4B17e8E8273E08176508A0123a91E

On the other seven testnets, the PayXor contract, the faucet, and the mock-token addresses in those packages are the zero address. Treat those entries as placeholders. A quote is signed against the contract address for the requested chain, so an end-to-end payment uses the Sepolia contract until the other addresses are set.

Sepolia also lists a USDC token at 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 (6 decimals). You can enable it on the app. The PayXor faucet mints the mock tokens that were added to it.

Fund the wallet from the faucet

A glass tap pouring cyan coin discs into a geometric wallet, with a small hourglass of light above the stream.

Open the testing app in the dashboard and connect a wallet. The token faucet is shown when isTesting is true, and only for networks whose faucet address is set. With the addresses above, that network is Ethereum Sepolia.

requestTokens(token) on MockTokenFaucet mints 1000 * 10^18 raw units to the caller. The cooldown is one hour, tracked per address and per token. A second call inside that hour reverts with CooldownNotElapsed. A token the owner has not added reverts with TokenNotSupported.

Read that raw amount with the token's decimals. On MockDAI (18 decimals) it is 1,000 tokens. On MockUSDC and MockUSDT (6 decimals) the same raw amount displays as 1,000,000,000,000 tokens. The faucet mints the ERC-20. Gas for the request, the approval, and the payment comes from Sepolia ETH in that wallet.

The quote is bound to the chain

A sealed glass document with a glowing cyan stamp, linked to one chain disc and separated by a glass shield from a second disc.

Ask for the quote with the Sepolia chain ID and the mock token you enabled:

const response = await fetch("https://api.payxor.xyz/api/quote", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    appId,
    productId,
    chainId: 11155111,
    payer,
    tokenAddress: "0x61C16DdA462EB7dC80084655BF083855581B7D76",
  }),
});

appId and productId are bytes32 hex strings. payer is the wallet that will submit the transaction. Leave amount off unless the product is a receipt with custom amounts enabled. Session, feature, and pass products use the price stored on the product.

The backend signs an EIP-712 quote whose domain is name PayXor, version 1, chain ID 11155111, and the Sepolia verifying contract. The signed message includes chainId as its own field. The signature verifies against that domain, on the Sepolia contract. For how the signature keeps the payer from editing the price, see Backend-Authoritative Pricing.

Pay, then read the entitlement

Point the wallet at Ethereum Sepolia before you call the hooks. useTokenPayment and usePayment both take the connected chainId, and the stablecoin list is the tokens this app enabled on that chain. Approve the PayXor contract, submit the signed quote, and confirm the transaction with the backend.

The sequence, the approval amount, and the entitlement hooks are the same ones covered in From wallet connect to entitlement, in two React hooks. On this testnet, useSessionStatus and useFeatureStatus read the Sepolia contract. Unlock the UI from that read.

When the flow is proven, create a production app, or switch this app to Production and choose mainnet networks and mainnet stablecoins. The next quote is signed for that mainnet chain ID.