Contents

Xcash Documentation

Deploy your own crypto payment gateway from scratch

Xcash is an open-source, self-hosted, non-custodial crypto payment gateway. Collection flows through smart contracts straight to your own collection address — Xcash never touches the funds. This documentation covers the introduction, how it works, deployment, operations and API integration; read it through and you can stand up a complete collection infrastructure of your own.

Introduction

What is Xcash

Xcash is an open-source, self-hosted, non-custodial crypto payment gateway for merchants, SaaS products, exchanges and wallet platforms, providing invoice collection, USDT collection and deposit collection.

Unlike custodial payment processors such as CoinGate or OpenNode, Xcash is non-custodial: collection flows through smart contracts straight to your collection address, Xcash never touches the funds and takes no platform fee — you only pay the small on-chain gas. It suits business systems that need multi-chain collection, deposit collection and Webhook notifications.

Use cases: e-commerce crypto invoice collection, USDT deposit-collection systems, cross-border stablecoin settlement, SaaS crypto subscription billing, on-chain collection infrastructure, and enterprise digital-asset intake management.

Xcash is open source under the MIT license; the code is fully public and auditable. If you would rather not self-host, the official cloud-hosted edition xca.sh is ready to use, zero maintenance.

Invoice collection vs deposit collection

Xcash offers two ways to take funds; understand the distinction before you integrate:

  • Invoice collection: invoice-style collection. Each transaction creates a fixed-amount, time-limited invoice; once the buyer pays, the invoice completes. It suits one-off collection such as e-commerce orders and subscription billing. Invoice collection supports two modes, direct-to-wallet and smart contract: funds can go straight into the project wallet, or each invoice can receive a dedicated contract address and sweep automatically.
  • Deposit collection: exchange-style deposit collection. Each user gets a dedicated deposit address, shared across chains and monitored in real time; users can transfer in anytime and the funds credit after block confirmations — no need to create an order per payment. It suits wallets and trading businesses that maintain user balances.
DimensionInvoice collectionDeposit collection
FormFixed-amount, time-limited one-off invoiceUser-specific, long-lived collection address
Typical scenarioE-commerce orders, subscription billingWallet top-ups, exchange deposits
Order creationAn invoice per transactionGet the address once, transfer in anytime after
AmountInvoice-specified amountAny amount
NotificationOne Webhook when the invoice completesA Webhook on each confirmed receipt

Core features

FeatureDescription
Invoice collectionFixed-amount, time-limited invoice collection for e-commerce orders, subscription billing and similar
Deposit collectionA dedicated deposit address per user; transfer in anytime, credit instantly — an exchange-grade experience
Non-custodialCollection flows through smart contracts straight to your wallet; Xcash never touches the funds
Zero platform feesNo per-transaction cut, only the small on-chain gas
Multi-chain & multi-tokenCovers major EVM chains, supports any ERC-20 token
Multi-merchant & multi-projectIsolate and manage multiple merchants and projects in a single instance
Smart contractGenerate a dedicated contract address per invoice, then sweep automatically after payment confirmation
On-chain risk controlIntegrates MistTrack to risk-score the source addresses of invoice and deposit collection
Webhook callbacksPushes invoice and deposit collection events in real time
EPay compatibleSupports the standard EPay V1 protocol for a smooth migration
Docker deploymentOne-command Docker Compose deployment for production

Chain & token support

Xcash covers major EVM chains and Tron; both invoice and deposit collection are available:

FeatureETHBNBArbitrumBaseTronPolygonOptimismOther EVM
Invoice collectionYesYesYesYesYesYesYesAlmost all
Deposit collectionYesYesYesYesYesYesYesAlmost all

For tokens:

Chain typeNative assetToken standardCurrent support
EVMETH, BNB, POL, etc. (for paying gas)ERC-20Supports any ERC-20 token; enable USDT, USDC or other on-chain assets as your business needs
TronTRXTRC-20Currently USDT and native TRX are enabled; other TRC-20 tokens are not yet available for collection

The actual chain-token combinations available also depend on what's enabled in the admin, the on-chain token deployment relationships, and the project's collection mode and collection-address configuration. The interface code for each chain is listed under Gateway URL & chain codes.

How it works

Non-custodial security model

Xcash's core design is to completely separate the fund path from the control plane. All collection runs through a smart contract, and the contract hard-wires the fund destination to your collection address. This yields a very strong security guarantee:

Even if the server running Xcash is breached, the database is dumped, or keys leak, funds stay completely safe as long as your collection address is not altered. After service is restored, funds from completed invoice or deposit collection still flow to your collection address — the attacker can do nothing.
Buyerinitiates payment
Collection contractdestination hard-wired
Your collection addressprivate key only with you
Xcash control plane · API / Worker / Database Only matches invoices, advances states and pushes notifications — it is not on the fund path.
Attacker Even with full control of the Xcash system, they cannot change the fund destination hard-wired into the contract.

This is possible because:

  • Xcash never handles your collection;
  • all collection runs through smart contracts, which hard-wire the fund destination to your collection address;
  • the collection contract is minimal, so the attack surface approaches zero.

System architecture

Xcash is made up of a few components with clear responsibilities; buyers and merchant systems interact with it through the API and Webhooks:

  • Xcash API: exposes endpoints to create invoice collection, query status, get deposit addresses and more — the unified entry point for merchants and buyers.
  • Xcash Worker: handles on-chain transaction monitoring, state transitions and collection scheduling — the core link between the blockchain and business state.
  • Xcash wallet engine: derives addresses from a BIP44 HD wallet and signs system transactions; the mnemonic is in your custody.
  • Xcash Webhook: asynchronously pushes events to the merchant when invoice or deposit collection enters key states.

The fund path only flows along "buyer → collection contract → your collection address"; the components above always act as the control plane and never handle funds. The tech stack:

LayerTechnology
BackendDjango 5.2 + Django REST Framework
Task queueCelery + Redis
DatabasePostgreSQL
Blockchain interactionweb3.py (EVM)
Wallet derivationBIP44 HD wallet (bip-utils)
Invoice collection pageReact 19 + Vite + Tailwind CSS
DeploymentDocker Compose

Two invoice collection modes

Only invoice collection distinguishes between collection modes. Depending on the chain type, two modes are supported; both offer identical fund security, differing only in address form and collection method:

  • Direct-to-wallet (Differ): the system collects to the project's configured direct-collection address, and may slightly adjust the buyer's payable amount (pay_amount) to distinguish different invoices that land on the same address. Funds go straight into your wallet — no sweep needed.
  • Smart contract (VaultSlot): the system assigns each invoice its own contract address; after the buyer's payment is confirmed, the system schedules a sweep that moves the funds into the project's collection address for the matching chain type. Addresses are naturally isolated and never collide, ideal for high concurrency.

Both EVM and Tron can be used for invoice collection. Which mode is actually used is configured per chain type in the admin; for chains using smart contract mode, you must configure a collection address for the matching chain type. Deposit collection does not distinguish between these two modes and always uses smart contract mode.

How smart contract mode works

Smart contract mode (VaultSlot) is Xcash's most distinctive mechanism; understanding it helps you understand the whole security model:

  • The address can be determined in advance: each invoice's collection address is deterministically derived from "factory contract + implementation contract + your collection address + a salt" (CREATE2-style prediction). In other words, the address is computed before the contract is actually deployed on-chain, and the buyer can pay this "counterfactual address" directly.
  • The fund destination is hard-wired: at deployment, your collection address is written into the contract. The contract's sweep method collect() can only move the balance to this hard-wired collection address — nothing has the permission to change the destination.
  • Anyone can trigger the sweep: because collect() only sends funds to the hard-wired collection address, it performs no caller permission check — the trigger only pays gas. So Xcash only needs to maintain a single global system hot wallet to pay the gas for deployment and sweeping.
  • Native coins and tokens: when sweeping a native coin the contract uses address(0) to mean "sweep the current native balance"; when sweeping an ERC-20 it takes the matching token contract address.
Precisely because the collection address is written into the contract and collect() has no power to redirect, not even Xcash itself can move a buyer's payment elsewhere. This is the technical root of "the funds can't be stolen even if the server is breached".

Deposit collection mechanism

Deposit collection is for businesses that maintain user balances. Via the API you request a deposit address for a given end user (uid) on a given chain; the same project, same uid and same chain consistently return the same contract address, shared across chains, so it's convenient to show users an address they can keep using long-term.

Once the user transfers to that address, the Worker detects the on-chain transfer and, when it meets the confirmation requirement, treats it as credited; it then schedules a sweep that moves the funds into the project's collection address for the matching chain type, and pushes a deposit Webhook to your notify URL. The whole process needs no order created per deposit.

Deposit addresses are contract addresses too, so sweeping requires the system hot wallet to keep a little gas / Energy on the matching chain. Deposit collection currently supports EVM and Tron, with USDT and native TRX enabled on Tron.

On-chain risk control (AML)

Xcash has built-in risk querying, caching, recording and display; the current risky-address detection relies on the external MistTrack service, not an in-house blacklist or on-chain risk model. Risk control covers two core fund entry points:

  • Invoice collection: once an invoice matches an on-chain payment, it runs an async risk query on the payer's address and syncs the risk level and score to the invoice record.
  • Deposit collection: once a deposit record is created, it runs an async risk query on the source address of the incoming funds and syncs the risk level and score to the deposit record.

Risk results are written to a separate risk assessment record (query status, target type, source address, transaction hash, risk level, risk score), viewable directly in the admin for manual review and handling. The API and Webhook output for invoice and deposit collection also carry risk_level and risk_score.

Xcash prefers the MistTrack OpenAPI V3; if its API key isn't configured it falls back to QuickNode's MistTrack add-on; if neither is configured, risk control is disabled.

Deployment

Before you start

Before deploying, prepare the following:

  • A Linux server, ideally Ubuntu 22.04+ or Debian 12+;
  • Docker and Docker Compose;
  • A domain that resolves to the server's IP;
  • RPC node endpoints for the public chains you want to enable;
  • A TronGrid API key if you want to enable Tron collection.

Performance tiers

EVM invoice collection and deposit collection both sense and confirm state via on-chain event scanning, and both are enabled by default and must be monitored together. The number of chains you can actually handle depends on RPC node throughput, block speed and event volume — configure conservatively. The performance tier is controlled by PERFORMANCE in .env, one of low / medium / high; if unset it defaults to low.

Performance modeHardwareChains supported
low1 core / 2 GB2 – 3 EVM chains
medium4 cores / 8 GB8 – 15 EVM chains
high8 cores / 16 GB15 – 30 EVM chains
env
PERFORMANCE=medium

Quick start

1. Clone the project

bash
git clone https://github.com/xca-sh/xcash.git
cd xcash

2. Initialize environment variables

This command generates .env and auto-fills the random secrets and database password needed to run. If .env already exists, the script refuses to overwrite and exits; to regenerate, back up and delete the old file first.

bash
./scripts/init_env.sh

3. Set the access domain

Edit .env to set SITE_DOMAIN:

env
SITE_DOMAIN=xcash.example.com

Make sure the domain's DNS resolves to the server's IP, and configure a reverse proxy such as Nginx or Caddy to forward traffic to http://localhost:6688. With Caddy, for example:

Caddyfile
your-domain.com {
    reverse_proxy localhost:6688
}

Optional: set ADMIN_PATH to move the admin entry to a custom path. If unset, the admin stays at the site root and shows a security reminder in the top-right corner of the admin.

env
ADMIN_PATH=secure-admin

4. Start the service

bash
docker compose up -d

On the first start, if there's no admin account in the database yet, the system automatically creates a default admin account:

text
username: admin
password: Admin@123456
The default password is for the first login only — change it immediately after logging in. For production we strongly recommend also setting ADMIN_PATH to hide the admin entry.

Configure chain RPC

The system ships with the basic info for major chains preset, but the RPC node endpoints must be filled in yourself so the gateway can talk to the blockchain. Log in to the admin, go to Blockchain → Chains, and enter an RPC endpoint for each chain you want to use.

We recommend node providers such as QuickNode, Alchemy or Infura. Tron collection requires registering at TronGrid and obtaining an API key.

Fund the system wallet with gas

Log in to the admin, go to System → System wallet, copy the system wallet address, and on each enabled EVM chain top it up with a small amount of native asset to pay gas (e.g. ETH, BNB, POL).

The system wallet is used only for platform infrastructure transactions — for example contract deployment and sweeping, the on-chain operations the system must initiate itself; business collection funds still flow to your collection address per the contract rules. You don't need to deposit business funds here, just keep a small amount of gas to cover recent operations so a low balance doesn't prevent contract deployment or sweep tasks from broadcasting.
Even if the system wallet runs low on gas, it causes no security issue. When a sweep transaction can't broadcast, the funds simply sit temporarily in the collection smart contract — the destination is still hard-wired to your collection address and no one can change it. Once gas is topped up, the system resumes sweeping automatically and moves the parked funds into your collection address.

Configure a project

Log in to the admin, go to Projects → Project list, and create or edit a project. A project is the basic isolation unit for API integration; each has its own Appid and HMAC key for endpoint authentication and signing. Confirm at least the following:

  • IP allowlist: restrict which merchant server IPs may call the gateway API; during testing you can use *, but in production narrow it to fixed egress IPs or ranges.
  • Notify URL: receives Webhook events for invoice and deposit collection; if it's not yet configured, the project shows as not ready.
  • Collection address: where business funds ultimately land. Before enabling smart contract mode or deposit collection you must configure an EVM multisig address; this address is written into the contract rules and cannot be changed once set. EVM collection addresses must be checksum addresses; Tron collection addresses must be Base58 addresses.

Once configured, follow API integration to integrate invoice collection, deposit collection and Webhook callbacks.

Operations

Common commands

Stop the service (stops and removes the production containers, but does not delete the database data volume):

bash
docker compose down

Start / bring the service back up:

bash
docker compose up -d

Upgrade to the latest version

The following command pulls the latest version from the main branch and runs the full production upgrade flow:

bash
./scripts/upgrade.sh

Gas maintenance

Smart contract mode and deposit collection rely on the system hot wallet to initiate contract deployment and sweep transactions on the matching chains. Periodically check the native-asset balance of the system wallet on each enabled chain and keep a small gas reserve. Once gas runs out, contract deployment or sweep tasks can't broadcast — but this does not affect fund security; the funds just sit temporarily in the collection contract and the system resumes sweeping automatically once gas is topped up.

Risk review

The admin lets you view the risk information in invoice and deposit collection records as well as the separate risk-assessment records, making it easy for operators to do manual review, release the business or take further action. The API and Webhook output for invoice and deposit collection also carry risk_level and risk_score, so your system can mirror the display or feed your own handling flow.

Risk control runs asynchronously. On the first Webhook delivery the risk fields may be temporarily null — don't treat the first notification as the final risk report; if needed, rely on the admin's risk-assessment record or a later query.

API integration

Gateway URL & chain codes

All Django/DRF API routes carry no trailing /; request the paths exactly as shown in the examples.

Self-hosted deployment

The API gateway URL is the SITE_DOMAIN you configured in .env, e.g. https://{your-domain}/v1/invoice; the admin is on the same domain.

Xcash official service

PurposeURL
API gatewayhttps://app.xca.sh (e.g. /v1/invoice)
EPay gatewayhttps://app.xca.sh/epay/submit.php
SaaS consolehttps://dash.xca.sh

Chains - chain

NameAPI parameterTypeGas tokenChain ID
EthereumethereumEVMETH1
BNB Smart ChainbscEVMBNB56
Polygon PoSpolygonEVMPOL137
Arbitrum Onearbitrum-oneEVMETH42161
OptimismoptimismEVMETH10
BasebaseEVMETH8453
TrontronTronTRX-
SepoliasepoliaEVM testnetETH11155111
NilenileTron testnetTRX-
Anvil LocalanvilEVM local chainETH31337

Tokens - crypto

crypto uses the token symbol, always uppercase. Common symbols:

NameAPI parameterTypeNotes
Tether USDUSDTStablecoinERC-20 / TRC-20, the most common collection token
USD CoinUSDCStablecoinERC-20, available on several EVM chains
DaiDAIStablecoinERC-20
EtherETHNative coinThe native gas coin of Ethereum and Arbitrum / Optimism / Base
BNBBNBNative coinThe native gas coin of BNB Smart Chain
PolygonPOLNative coinThe native gas coin of Polygon PoS
TRONTRXNative coinThe native gas coin of Tron

The table above is just common examples: EVM chains support any ERC-20 token — add the token contract address in the admin and enable it to collect; the available symbols aren't limited to these. Tron currently only enables USDT and native TRX. The actual chain-token combinations available depend on the chains, tokens and on-chain deployment relationships enabled in the admin. Test projects can only use testnets or local chains; non-test projects can only use mainnet chains.

Authentication & signing

Except for endpoints explicitly marked public, all /v1/* endpoints require an HMAC-SHA256 signature. After you create a project in the admin, the system generates an appid (e.g. XC-A3BK7NMG) and an hmac_key.

Request headers

http
XC-Appid: {appid}
XC-Timestamp: {unix_timestamp}
XC-Nonce: {unique_nonce}
XC-Signature: {hmac_signature}
Content-Type: application/json
HeaderDescription
XC-AppidThe project AppID
XC-TimestampCurrent Unix timestamp; in production a skew of up to 300 seconds from the server is allowed
XC-NonceMust not repeat within 300 seconds under the same AppID
XC-SignatureHMAC-SHA256 signature, lowercase hexadecimal

Signature computation

text
message   = XC-Nonce + XC-Timestamp + request_body
signature = HMAC-SHA256(message, hmac_key).hexdigest()

request_body must be the exact raw request-body string you send. When a GET request has no body, use the empty string "".

Python example

python
import hashlib
import hmac
import json
import time
import uuid

appid = "XC-A3BK7NMG"
hmac_key = "your_hmac_key"

payload = {
    "out_no": "order-001",
    "title": "Premium Plan",
    "currency": "USD",
    "amount": "29.99",
}
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
timestamp = str(int(time.time()))
nonce = str(uuid.uuid4())
signature = hmac.new(
    hmac_key.encode(),
    f"{nonce}{timestamp}{body}".encode(),
    hashlib.sha256,
).hexdigest()

headers = {
    "XC-Appid": appid,
    "XC-Timestamp": timestamp,
    "XC-Nonce": nonce,
    "XC-Signature": signature,
    "Content-Type": "application/json",
}

Node.js example

javascript
const crypto = require("crypto");

const appid = "XC-A3BK7NMG";
const hmacKey = "your_hmac_key";
const body = JSON.stringify({
  out_no: "order-001",
  title: "Premium Plan",
  currency: "USD",
  amount: "29.99",
});
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID();
const signature = crypto
  .createHmac("sha256", hmacKey)
  .update(`${nonce}${timestamp}${body}`)
  .digest("hex");

Responses & error codes

On success the business JSON is returned directly. Creation endpoints typically return HTTP 201, query endpoints return HTTP 200. A business error response:

json
{
  "code": "1001",
  "message": "Invalid AppID",
  "detail": ""
}

Framework-level errors (e.g. resource not found 404, method not allowed 405, rate limit 429) may return the DRF default format { "detail": "Not found." }.

Endpoint list

MethodPathDescriptionSignature
POST/v1/invoiceCreate invoice collectionRequired
GET/v1/invoice/{sys_no}Query the public status of an invoiceNot required
GET/v1/deposit/addressGet a deposit addressRequired
GET/POST/epay/submit.phpEPay V1 order creationEPay MD5

Error codes

Error codeDescriptionHTTP
1000Parameter error400
1001Invalid AppID400
1002IP forbidden403
1003Signature error403
1004Project not configured400
1007Duplicate out_no400
1008Timestamp not set or expired400
1009Duplicate request400
2000 / 2001 / 2002Invalid chain / invalid crypto / crypto not supported on this chain400
4000 / 4001 / 4002Invalid UID / project has no collection address for this chain / deposit user count at limit400 / 400 / 403
5000 / 5008 / 5009Invalid invoice collection type / no available collection method / too many pending records400
6000 / 6002 / 6004Invalid internal token / project not found / account frozen401 / 404 / 403

Create an invoice

POST /v1/invoice (signature required). Creates an invoice collection; on success it returns a pay_url, and the buyer opens the invoice collection page to pick token, pick chain and pay.

Request parameters

FieldTypeRequiredDescription
out_nostringYesMerchant order number, up to 32 chars, unique within a project
titlestringYesInvoice title, up to 32 chars
currencystringYesThe pricing fiat code (e.g. USD, CNY); the collection crypto is specified by methods
amountstringYesPriced amount, range 0.00000001 – 1000000
durationintegerNoValidity in minutes, 5 – 30, default 10
methodsobjectNoRestrict collection methods, format {"token": ["chain code"]}
notify_urlstringNoInvoice-level Webhook URL, takes precedence over the project's default notify URL
return_urlstringNoSynchronous redirect URL after the invoice completes

methods rules

  • Omit methods: the system generates the currently available chain-token combinations per the project configuration.
  • Pass methods: it must be a subset of the system-generated combinations, otherwise it returns no available collection method.
  • currency only determines the pricing unit (fiat) of amount; it's decoupled from the crypto the buyer actually pays, which is restricted by methods.
  • The chain, token, address and amount the buyer ultimately pays are whatever the invoice page / query endpoint returns as chain, crypto, pay_address and pay_amount — do not derive the on-chain payment amount from amount yourself.

Request example

json
{
  "out_no": "order-20260602-001",
  "title": "Premium Plan",
  "currency": "USD",
  "amount": "29.99",
  "duration": 15,
  "methods": {
    "USDT": ["ethereum"],
    "USDC": ["base"]
  },
  "notify_url": "https://merchant.example.com/xcash/webhook",
  "return_url": "https://merchant.example.com/payment/success"
}

Restrict the buyer to a single stablecoin (priced in USD + methods restricted to USDT):

json
{
  "out_no": "order-20260602-002",
  "title": "Contract Invoice",
  "currency": "USD",
  "amount": "100",
  "duration": 15,
  "methods": {
    "USDT": ["ethereum", "base"]
  },
  "notify_url": "https://merchant.example.com/xcash/webhook"
}

Response example

json
{
  "appid": "XC-A3BK7NMG",
  "sys_no": "INV2606028X7K2P9Q",
  "out_no": "order-20260602-001",
  "title": "Premium Plan",
  "currency": "USD",
  "amount": "29.99",
  "methods": { "USDT": ["ethereum"], "USDC": ["base"] },
  "chain": null,
  "crypto": null,
  "crypto_address": null,
  "pay_address": null,
  "pay_amount": null,
  "pay_url": "https://app.xca.sh/pay/INV2606028X7K2P9Q",
  "started_at": "2026-06-02T12:00:00Z",
  "created_at": "2026-06-02T12:00:00Z",
  "expires_at": "2026-06-02T12:15:00Z",
  "notify_url": "https://merchant.example.com/xcash/webhook",
  "return_url": "https://merchant.example.com/payment/success",
  "payment": null,
  "status": "waiting",
  "risk_level": null,
  "risk_score": null
}

If only one collection combination remains, the system selects it automatically at creation time, in which case chain, crypto, pay_address and pay_amount may already have concrete values. The default anonymous rate limit is 256/minute.

Query an invoice

GET /v1/invoice/{sys_no} (public endpoint, no signature needed). Used by the invoice collection page or the buyer side to poll status; it does not return appid, out_no or notify_url.

Key response fields

FieldDescription
sys_noSystem order number; prefix + 6-digit date (YYMMDD) + 8 uppercase alphanumerics. Invoice prefix INV, deposit prefix DXC
chainThe selected chain, null if not selected
cryptoThe selected token, null if not selected
pay_addressThe collection address
pay_amountThe crypto amount the buyer must pay
payment_uriEIP-681 payment URI available on EVM chains; empty for non-EVM or when the amount can't be encoded precisely
statuswaiting / completed / expired
paymentThe matched on-chain transfer object, including hash, block, from/to, amount, confirm_progress, etc.
risk_levelRisk level
risk_scoreRisk score

Rate limited to 60/minute, keyed by sys_no + IP.

Get a deposit address

GET /v1/deposit/address (signature required). Gets a deposit address for an end customer under a project; the same project, same uid and same chain consistently return the same address.

In implementation terms, a single uid under the same project may theoretically share one deposit address across all chains. For clearer integration semantics, call this endpoint again for each chain + crypto combination instead of directly reusing an address obtained under another chain or token combination.

FieldTypeRequiredDescription
uidstringYesEnd-customer identifier, 1 – 128 chars, letters, digits, underscores and hyphens only
chainstringYesChain code, e.g. ethereum, base, tron
cryptostringYesToken symbol, e.g. USDT

Request example

http
GET /v1/deposit/address?uid=user-10001&chain=base&crypto=USDC

When signing a GET request, request_body is the empty string. Response:

json
{
  "deposit_address": "0xAbCd1234..."
}

The requested chain, token and the chain-token relationship must all be enabled, and the project's test/mainnet attribute must match the chain. Rate limited to 60/minute, keyed by appid + IP.

Webhook callbacks

Xcash delivers a Webhook to the merchant when invoice or deposit collection enters a key state. In production, delivery is allowed only to public HTTPS URLs by default; http, localhost and private network ranges are rejected.

Signature headers

Xcash native-protocol events use POST application/json with HMAC headers; the signing algorithm is the same as for API requests:

http
XC-Appid: {appid}
XC-Nonce: {event_nonce}
XC-Timestamp: {unix_timestamp}
XC-Signature: {hmac_signature}
Content-Type: application/json

Response & retries

  • Success response: HTTP 200 with a body that equals ok after trimming whitespace (EPay V1 notifications use success).
  • Each request times out at 5 seconds; only network errors or 5xx are retried with exponential backoff — non-200 2xx, 3xx and 4xx are not retried.
  • The merchant should verify the signature and handle the same XC-Nonce idempotently. The project's notification toggle must be on, otherwise even a per-invoice notify_url won't be delivered.

Invoice collection Webhook

Sent once after the invoice enters completed (confirmed=true):

json
{
  "type": "invoice",
  "data": {
    "sys_no": "INV2606028X7K2P9Q",
    "out_no": "order-20260602-001",
    "crypto": "USDT",
    "chain": "ethereum",
    "pay_address": "0xAbCd1234...",
    "pay_amount": "29.870001",
    "hash": "0xabc123...",
    "block": 12345678,
    "confirmed": true,
    "risk_level": null,
    "risk_score": null
  }
}

Deposit collection Webhook

Sent once after the deposit's on-chain transfer meets the confirmation requirement (confirmed=true):

json
{
  "type": "deposit",
  "data": {
    "sys_no": "DXC2606026K9P2QWX",
    "uid": "user-10001",
    "chain": "base",
    "block": 12345678,
    "hash": "0xabc123...",
    "crypto": "USDC",
    "amount": "500",
    "confirmed": true,
    "risk_level": null,
    "risk_score": null
  }
}

EPay V1 compatibility

Xcash provides an EPay V1 compatible entry point that works with common EPay plugins for Typecho, WordPress, Discuz and more. The EPay entry does not use Xcash HMAC headers; it uses EPay's own MD5 signature.

Create an order

GET /epay/submit.php or POST /epay/submit.php. For POST, use application/x-www-form-urlencoded or multipart/form-data form encoding — do not send JSON. Core parameters: pid, out_trade_no, notify_url, name, money, sign, sign_type=MD5; optional currency (default CNY), param, return_url, type.

EPay signature

  1. Drop sign and sign_type;
  2. drop fields whose value is null or empty string;
  3. sort by field name in ascending ASCII order;
  4. concatenate key=value, joined by &;
  5. append secret_key directly at the end;
  6. MD5 the whole string and output lowercase hexadecimal.
python
import hashlib

params = {
    "pid": "1001",
    "out_trade_no": "order-001",
    "notify_url": "https://merchant.example.com/epay/notify",
    "return_url": "https://merchant.example.com/epay/return",
    "name": "Premium Plan",
    "money": "29.99",
    "sign_type": "MD5",
}

filtered = {
    k: str(v)
    for k, v in params.items()
    if k not in {"sign", "sign_type"} and v not in (None, "")
}
sign_string = "&".join(f"{k}={v}" for k, v in sorted(filtered.items()))
secret_key = "your_epay_secret_key"
params["sign"] = hashlib.md5(
    f"{sign_string}{secret_key}".encode("utf-8"),
    usedforsecurity=False,
).hexdigest()

Response & notification

  • Success: HTTP 302, redirect to the invoice collection page /pay/{sys_no}; failure: HTTP 400, plain text fail.
  • After the invoice completes, Xcash sends a GET notification to notify_url with trade_status=TRADE_SUCCESS; the merchant responding HTTP 200 with a body of success is treated as success.
  • Core fulfillment logic should rely on the async notification; the synchronous redirect only signals that the invoice collection page flow is finished.

More

FAQ & support

Full invoice collection flow

text
Merchant server -> Xcash:  POST /v1/invoice
Xcash -> Merchant server:  returns sys_no / pay_url
Buyer -> Xcash:            opens pay_url, picks token & chain
Buyer -> Blockchain:       sends the transfer
Xcash -> Merchant server:  Webhook invoice
Merchant server -> Xcash:  ok

Full deposit collection flow

text
Merchant server -> Xcash:  GET /v1/deposit/address
Xcash -> Merchant server:  returns deposit_address
Merchant system -> User:   shows deposit_address
User -> Blockchain:        sends the transfer
Xcash -> Merchant server:  Webhook deposit
Merchant server -> Xcash:  ok

Does Xcash charge fees?

The open-source edition is completely free (MIT license) with zero platform fees — you only pay on-chain gas. If you'd rather not self-host, an official cloud-hosted edition is available (subscription-based); see pricing. Either way, collection flows through smart contracts straight to your collection address.

Do funds pass through Xcash?

No. The fund destination is hard-wired to your collection address in the smart contract; Xcash never holds, handles or can move the crypto you collect. See Non-custodial security model.

Getting support

Run into a deployment or integration issue? Open an issue on GitHub, or email [email protected] for commercial technical support. You can also refer to the README and API.md in the repository.