Skip to main content
Crypto-pay · Live · Open-source

Invisible Web3 top-ups for your customers.

White-label, non-custodial fiat-to-stablecoin top-up infrastructure. End-users log in with email, top up in USDT or USDC, and authorise every transfer themselves. You receive a signed webhook and credit your own balance.

Why merchants pick us

Drop-in widget, signed webhooks, no licensing burden

5-minute integration

One JS snippet on the page, one HMAC verification on the backend. WordPress, Shopify, Next.js, plain HTML — all covered with copy-paste recipes.

Non-custodial by design

End-user keys live in a 2-of-3 MPC network. We never see or sign them. Every transfer is user-authorised in the browser via Web3Auth.

Multi-chain stablecoins

USDT & USDC native on Polygon, Base, Arbitrum, Optimism, BSC, Avalanche. Testnets included from day one for merchant sandbox flows.

Signed webhook events

HMAC-SHA256 over {timestamp}.{body}, replay-window enforced. Drop-in verifyWebhook() in our Node, Python and PHP SDKs.

Audit-ready

Append-only audit log, OFAC SDN screening before broadcast, configurable AML thresholds. We log; you decide.

Cloud-agnostic

Serverless adapters for AWS Lambda and GCP Cloud Functions. Postgres anywhere. KMS via AWS or GCP.

Flow

How a single top-up looks

Your customer never leaves your site. We never touch fiat. The fiat-to-crypto leg is handled by a licensed on-ramp partner.

1

User logs in with email

Web3Auth derives a persistent MPC wallet for that address. No seed phrases, no MetaMask.

2

User tops up in fiat

Embedded Transak widget converts card / bank → USDT or USDC into the user's wallet.

3

User clicks "Send"

The browser builds an ERC-20 transfer; the MPC network co-signs with the user. We never see the key.

4

We validate & relay

Server checks destination = your master wallet, screens sanctions, then broadcasts the user-signed tx.

5

Webhook to you

You receive transfer.broadcast and transfer.confirmed HMAC-signed events. Credit your own ledger.

SDKs

Verify a webhook in three languages

Same shape, same constants, same constant-time compare.

Node.js

import { verifyWebhook } from '@cryptopay/sdk';

if (!verifyWebhook({
  rawBody: req.rawBody,
  header:  req.headers['x-cryptopay-signature'],
  secret:  process.env.SECRET,
})) return res.sendStatus(401);

Python

from cryptopay import verify_webhook

if not verify_webhook(
    raw_body=request.body,
    header=request.headers["X-CryptoPay-Signature"],
    secret=settings.SECRET,
):
    return 401, b""

PHP

use CryptoPay\Webhook;

if (!Webhook::verify(
    file_get_contents('php://input'),
    $_SERVER['HTTP_X_CRYPTOPAY_SIGNATURE'],
    getenv('SECRET'),
)) {
    http_response_code(401); exit;
}

Or curl, for debugging

SIG=$(printf '%s.%s' "$TS" "$BODY" \
  | openssl dgst -sha256 -hmac "$SECRET" \
  | awk '{print $2}')
echo "t=$TS,v1=$SIG"

Want to integrate?

Pilot integrations are free during our testnet phase. Production tier launches once our on-ramp partner KYB completes. Drop us a line and we'll respond within a working day.