PayWicket Developer & Merchant Docs
Accept stablecoin payments (USDC on Polygon) with a familiar checkout, automatic on-chain settlement to a wallet you control, signed webhooks, and one-click reconciliation. PayWicket is non-custodial — funds never sit with us.
Base URL: https://paywicket.com
Overview
PayWicket turns a crypto payment into an ordinary checkout. A buyer completes a payment with a wallet or a card; USDC settles directly to the merchant's payout address on Polygon through a small on-chain router contract (source verified on Polygonscan) that splits the platform fee automatically; and your system receives a signed payment.confirmed webhook so you can fulfil the order.
- Non-custodial by design. Each merchant controls the destination wallet; PayWicket never holds or moves customer funds off its rails.
- Stablecoin settlement. Payments settle in USDC (6-decimal ERC-20) on Polygon — final, low-fee, and 1:1 with USD.
- Card-friendly for buyers. Buyers without crypto can fund with a debit/credit card via a licensed on-ramp, or create a wallet with just an email — the crypto layer stays invisible to them.
- Refundable. Confirmed payments can be returned (ERC-20 chargeback) to the payer or any address.
How a payment flows
- Your backend calls
POST /api/paymentswith the USD amount and an optionalcallbackUrlandidempotencyKey. You get back a payment object with a hostedcheckoutUrl. - You redirect the customer to
checkoutUrl(or embed it). They pay with a wallet (gasless), a card-funded wallet, or an email-created wallet. - The
PaymentRoutercontract transfers USDC to the merchant payout address and sends the platform fee to the treasury in the same transaction. APaidevent is indexed and matched to your payment by itspaymentRef. - PayWicket marks the payment confirmed and fires a signed
payment.confirmedwebhook to yourcallbackUrl. - You reconcile via the webhook, the
GET /api/payments/:idread, or the ledger CSV export — each carries the on-chain tx hash and a block-explorer link.
Quickstart
1) Create an account and a wallet in the dashboard — the wallet is issued a scoped API key. 2) Create a payment and redirect the buyer:
curl -X POST https://paywicket.com/api/payments \
-H "x-api-key: $PAYWICKET_API_KEY" \
-H "content-type: application/json" \
-d '{
"amountUsd": 49.99,
"currency": "USDC",
"description": "Order #1234",
"customerEmail": "buyer@example.com",
"callbackUrl": "https://yourstore.com/webhooks/paywicket",
"idempotencyKey": "order-1234",
"metadata": { "orderId": "1234" }
}'
Response (trimmed):
{
"id": "pay_9f2c1a8b7d6e5f40",
"status": "pending",
"amountUsd": 49.99,
"currency": "USDC",
"amountToken": { "human": "49.99", "base": "49990000" },
"checkoutUrl": "https://paywicket.com/checkout.html?pid=pay_9f2c1a8b7d6e5f40",
"expiresAt": 1734112800000
}
Send the customer to checkoutUrl. When they pay, you'll receive a payment.confirmed webhook.
Authentication
There are two credential types:
- Wallet API key — sent as the
x-api-keyheader. Used to create and read payments for that wallet. Issued per wallet and shown in the dashboard. - Account bearer token — from
POST /api/auth/login, sent asAuthorization: Bearer <token>. Used for management endpoints (creating wallets, issuing refunds, analytics). Supports TOTP MFA.
Payments API
Create a payment intent. Authenticate with x-api-key.
| Field | Type | Notes |
|---|---|---|
amountUsd | number | Required. > 0, ≤ 1,000,000. USD value; converted to token at current rate. |
currency | string | USDC (default) or MATIC. |
description | string | Shown on checkout; ≤ 200 chars. |
customerName, customerEmail | string | Optional; stored for reconciliation/receipts. |
callbackUrl | string | HTTPS endpoint to receive webhooks. Falls back to the wallet's default webhook. |
idempotencyKey | string | Repeated create with the same key returns the existing payment — safe on retries. |
metadata | object | Free-form JSON echoed back on reads and webhooks. |
expiresInMinutes | number | Optional. Default 30; from 5 up to 86,400 (60 days) for invoices and payment requests. |
Returns a payment object: id, status, amountUsd, currency, amountToken {human, base}, address, checkoutUrl, checkoutQrDataUrl (QR of the checkout link, for print or in-person), paymentUri, qrDataUrl, cardEnabled / cardMaxUsd, metadata, createdAt, expiresAt, settlement, and (in on-chain mode) an onchain block with router/token/paymentRef.
Related: GET /api/payments/:id/qr.png returns the checkout QR as a PNG; GET /pay/:id/print is a printable card with the QR and amount, for counters, invoices and events.
Fetch current state of a payment (poll as a fallback to webhooks).
List payments for a wallet.
Public runtime config: active network, chainId, settlement token (symbol/address/decimals), current fees (crypto and card rails), and paymentExpiryMinutes.
Subscriptions (recurring invoices)
A subscription bills a customer on a schedule. On each billing date PayWicket creates that cycle's payment (a normal checkout, payable by card or crypto), emails the customer the pay link, reminds them before the due date, and marks the subscription past_due when an invoice goes unpaid. Because the customer approves every payment from their own wallet or card, no card or key is ever stored by PayWicket or by you.
Authenticate with the wallet's x-api-key (or an account token plus walletId).
| Field | Type | Notes |
|---|---|---|
name | string | Required. Plan or service name shown on each invoice. |
amountUsd | number | Required. Amount per cycle. |
interval, intervalCount | string, number | week, month or year; count defaults to 1 (e.g. every 3 months). |
customer | object | { name, email }; a valid email is required (invoices are sent there). |
startAt | ISO date | First billing date. Default: now (first invoice is created immediately unless invoiceNow:false). |
dueDays | number | Days the customer has to pay each invoice (1–60, default 7). The invoice link stays valid until then. |
description, metadata | string, object | Copied onto every invoice payment; metadata.subscriptionId and metadata.cycle are added automatically. |
List, or fetch one with its invoices (cycle, status open | paid | overdue | void, dueAt, paymentId and the payment itself).
Pause stops new invoices; resume continues from the next billing date; cancel voids open invoices (paid cycles are untouched); invoice-now issues the next cycle immediately.
Webhooks: subscription.invoice_created, subscription.paid, subscription.past_due, subscription.paused, subscription.active, subscription.canceled, plus the usual payment.confirmed for each invoice. Everything here is also available in the dashboard under Subscriptions.
Payment states
| State | Meaning | Transition |
|---|---|---|
| pending | Created, awaiting payment. | Initial state. |
| confirmed | Funds received on-chain and matched. | From pending when the router Paid event (or balance) is observed. Fires payment.confirmed. |
| expired | Not paid before expiresAt. | From pending after the expiry window (default 30 min). |
| refunded | Returned to the payer. | From confirmed via refund. Fires payment.refunded. |
Webhooks
PayWicket POSTs a JSON event to your callbackUrl (or the wallet's default webhook URL, editable in the dashboard under Wallets) on state changes. Events: payment.confirmed, payment.refunded, and the subscription.* events listed above.
| Header | Value |
|---|---|
x-cryptopay-event | The event type, e.g. payment.confirmed. |
x-cryptopay-signature | HMAC-SHA256 hex digest of the raw request body, keyed with the wallet's signing secret (whsec_…, shown in the dashboard under Wallets → Webhook, where you can also rotate it and send a test event). |
x-cryptopay-event-id | Unique id for the event; the same id is reused on retries, so you can de-duplicate. |
x-cryptopay-attempt | 1, 2 or 3. Failed deliveries (network error or non-2xx) are retried after 30 seconds and again after 5 minutes. |
Body:
{
"id": "evt_9b1c0d2e3f4a5b6c",
"type": "payment.confirmed",
"sentAt": "2026-08-20T14:03:11.204Z",
"data": {
"id": "pay_9f2c1a8b7d6e5f40",
"status": "confirmed",
"amountUsd": 49.99,
"currency": "USDC",
"metadata": { "orderId": "1234" },
"settlement": {
"txHash": "0xabc…",
"fromAddress": "0x…",
"amountReceivedBase": "49990000",
"confirmedAt": 1734112991204,
"explorerUrl": "https://polygonscan.com/tx/0xabc…"
}
}
}
Verify the signature (Node)
import { createHmac, timingSafeEqual } from "node:crypto";
app.post("/webhooks/paywicket",
express.raw({ type: "application/json" }), (req, res) => {
const sig = req.get("x-cryptopay-signature");
const expected = createHmac("sha256", process.env.PAYWICKET_WEBHOOK_SECRET)
.update(req.body) // raw bytes, not parsed JSON
.digest("hex");
const ok = sig && timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
if (!ok) return res.status(400).send("bad signature");
const evt = JSON.parse(req.body);
if (evt.type === "payment.confirmed") { /* fulfil order evt.data.metadata.orderId */ }
res.sendStatus(200); // respond 2xx within ~5s
});
id / metadata, and treat webhooks as idempotent — the same event may be delivered more than once. Always confirm state with GET /api/payments/:id before shipping high-value goods.Reconciliation
Every confirmed/refunded payment carries the on-chain settlement.txHash and an explorerUrl, so your books tie directly to the ledger. Options:
- Real-time: the confirmation webhook (with tx hash + amount received).
- Pull:
GET /api/payments/:id(by payment id) orGET /api/merchants/:id/payments(send your wallet'sx-api-keyor your account token). - Batch/accounting: CSV export below.
Columns: date_received, customer_name, customer_email, cryptocurrency_received, amount_crypto, usd_value_at_receipt, wallet_transaction_id, receiving_address, status, refund_tx, description.
Errors & rate limits
Errors return a non-2xx status with a JSON body { "error": "message" }.
| Status | Meaning |
|---|---|
400 | Invalid request (e.g. bad amountUsd, missing fields). |
401 | Unknown/missing credential (send x-api-key or bearer token). mfa_required when TOTP is needed. |
403 | Not permitted (e.g. refunding a payment you don't own). |
404 | Resource not found. |
429 | Rate limited. Auth and write endpoints (payments/refunds) are throttled per IP. |
Use idempotencyKey on create to make retries safe. Webhook receivers should always return quickly (2xx within ~5s) and process asynchronously.
Payment methods & currencies
- Settlement currency: USDC on Polygon (also supports native MATIC). USDC is a 6-decimal ERC-20 pegged 1:1 to USD.
- Existing wallet (gasless): buyers sign an EIP-3009 authorization; a relayer pays the gas, so the buyer needs no MATIC.
- Card → USDC: buyers without crypto fund the payment with a debit/credit card via a licensed on-ramp (Coinbase). PayWicket never touches card data.
- Email-created wallet: buyers with no wallet can create a self-custodial wallet with an email (embedded wallets) and then fund it — the crypto stays invisible.
Settlement
Settlement is direct and non-custodial. When a payment confirms, the PaymentRouter smart contract, in a single transaction:
- transfers the net USDC to the merchant's payout address (either a PayWicket-generated wallet you control the keys to, or your own bring-your-own address), and
- routes the platform fee to the treasury. The fee depends on how the buyer paid (see Pricing); current rates are returned by
GET /api/config(fees).
There is no T+N payout delay and no pooled balance — funds arrive at the merchant address as the transaction confirms (seconds on Polygon).
Pricing
Fees are paid by the merchant; the buyer always pays the list price. Each payment carries a fees object (rail, platformFeeUsd, merchantNetUsd) so your reconciliation matches to the cent.
| How the buyer pays | Fee | Notes |
|---|---|---|
| Crypto wallet (USDC, gasless) | 0.5% with a $0.05 minimum | Enforced on-chain by the router (max(percentage, floor)). No card network involved. |
| Card, Apple Pay, Google Pay | 5.9% + $1.80 per transaction | Covers licensed card processing, identity verification and fraud/chargeback liability carried by our payment partner, then converts to USDC. The card leg carries no chargeback risk for you. |
Example: a $100 card sale nets the merchant $92.60; a $100 crypto-wallet sale nets $99.50. There are no monthly, setup, or payout fees on either rail.
Refunds & disputes
Authenticate with the account bearer token. Only confirmed payments can be refunded, once. Body: toAddress (the customer's wallet; defaults to the original payer for wallet payments), or method: "external" with an optional txHash and note to record a refund you made outside PayWicket (bank, cash, store credit).
Card payments: the payer address on a card payment belongs to the card processor, not the customer, so a refund requires an explicit toAddress the customer gave you, or an external refund record. The dashboard's Refund dialog walks through both.
payment.refunded.Sandbox & testing
PayWicket runs in three modes so you can integrate before touching real funds:
- Simulation — end-to-end flow with fabricated confirmations, no chain. Ideal for wiring your webhook handler and order fulfilment.
- Testnet (Polygon Amoy) — real on-chain settlement with test USDC. A faucet endpoint issues demo USDC so you can complete a real signed payment.
- Production (Polygon mainnet) — live USDC.
Ask us to provision a sandbox wallet + API key, or self-serve from the dashboard. The recommended acceptance test: create a payment → complete it on the hosted checkout → assert your endpoint received a valid, signature-verified payment.confirmed → reconcile it.
Integration options
- Hosted checkout — redirect to
checkoutUrl; zero UI to build. Supports a return-to-store URL. - Drop-in SDK —
/sdk/cryptopay.jsfor an embedded pay button. - WooCommerce plugin — native "Pay with card or crypto" gateway (install guide).
- Payment links & QR codes — create a link in the dashboard, share it, or print its QR for a counter or invoice.
- Subscriptions — recurring invoices with automatic emails and past-due tracking (API).
- Agent-native —
x402(HTTP 402) for machine-to-machine payments and an MCP server exposing PayWicket as agent tools.
WooCommerce plugin
Download paywicket-woocommerce.zip (v1.1.0). Works with the classic and block checkout and with High-Performance Order Storage.
- WordPress admin → Plugins → Add New → Upload Plugin → choose the zip → Activate. WooCommerce must be active.
- WooCommerce → Settings → Payments → PayWicket (card or crypto) → Manage.
- Enable it, keep the API URL as
https://paywicket.com, and paste the wallet API key (sk_…) from your dashboard → Developers → API keys. That key decides which wallet receives the funds. - Place a test order. The customer is sent to the hosted checkout and returned to your order-received page; the order is marked paid once the payment confirms.
To test without real money, use https://sandbox.paywicket.com as the API URL with a key from a sandbox account. Refunds are issued from your PayWicket dashboard; the order flips to refunded on the next callback.
Merchant technical requirements
- An HTTPS webhook endpoint that verifies the HMAC signature and returns 2xx quickly.
- A payout wallet address you control, or a wallet created in your browser from the dashboard (the recovery phrase is shown once and never leaves your device; we store only the public key).
- Server-side storage of your wallet API key (
sk_…) and webhook signing secret (whsec_…), both from the dashboard. - Idempotent order handling keyed on the payment
id/ youridempotencyKey. - Reconciliation against on-chain
txHashfor finance/audit.
Legal & disclosures
Because crypto settlement is final and irreversible, your customers should agree to that up front. Add the clause below to your checkout terms (adapt with your own counsel), and link buyers to our Terms & Conditions.
Drop-in checkout clause for your store
Cryptocurrency payments. This site accepts cryptocurrency payments
facilitated by PayWicket, a non-custodial payment technology provider.
If you choose to pay by card, your card payment is processed by an
independent, licensed payment provider (not this store and not
PayWicket), and is subject to that provider's terms and identity-
verification requirements. Once your payment is converted to
cryptocurrency and confirmed on the blockchain, the transaction is
final and cannot be reversed or charged back. Any refund is issued
voluntarily by the merchant in cryptocurrency according to the
merchant's refund policy. By completing a cryptocurrency payment, you
acknowledge and accept these terms and assume the risks associated with
cryptocurrency transactions.
Questions or need a sandbox key? Chat with Wick (our assistant, available 24/7 by chat or by phone at +1 (978) 574-7641), or email support@paywicket.com. Some deeper architecture material (settlement internals, custom routing, commercial terms) is shared under NDA.
ADA Accessibility Compliant · Terms · © 2026 PayWicket