QuickPay API

QuickPay is a hosted crypto payment API. Send your customer to a QuickPay-hosted payment page, and poll for confirmation — no wallets, no node infrastructure required on your side. All amounts are denominated in USD; QuickPay handles the crypto conversion in real time.

Base URL: https://4376hdfdaf.shop


Authentication

Invoice creation requires an API key passed via the X-API-Key request header. Keys are issued per project from the admin panel. Status and cancel endpoints are unauthenticated.

Include your key on every invoice creation request:

X-API-Key: qp_a1b2c3d4e5f6...

POST /create-invoice

Creates a new payment invoice and redirects to the hosted payment page. The payment page displays the crypto address and amount your customer needs to send.

Headers
NameTypeDescription
X-API-Key required string Your project API key.
Form body
FieldTypeDescription
coin required string Cryptocurrency to accept. One of: eth, btc, usdt, usdc.
amount required float Invoice amount in USD (e.g. 49.99). Must be between 0.01 and 999,999.
callback_url string HTTPS URL to POST a webhook to when the invoice is confirmed. Must start with http:// or https://. See Webhooks.
metadata string Arbitrary string (e.g. a user ID or JSON blob) passed through unchanged in the webhook payload. Max use: identifying which user to upgrade.
Response

303 Redirect to /invoice/{tx_id} on success. Returns JSON error on invalid coin, amount, or missing/revoked API key.

Example — curl
curl -X POST https://4376hdfdaf.shop/create-invoice \
  -H "X-API-Key: qp_a1b2c3d4e5f6..." \
  -F "coin=eth" \
  -F "amount=49.99"
Example — JavaScript (fetch)
const body = new FormData();
body.append("coin", "eth");
body.append("amount", "49.99");

const resp = await fetch("https://4376hdfdaf.shop/create-invoice", {
  method: "POST",
  headers: { "X-API-Key": "qp_a1b2c3d4e5f6..." },
  body,
  redirect: "follow",
});

// resp.url will be the hosted payment page — redirect your user there
window.location.href = resp.url;

GET /invoice/{tx_id}

Returns the hosted HTML payment page for an invoice. Customers are automatically redirected here after POST /create-invoice. The page shows the deposit address, expected crypto amount, a live countdown to expiry, and auto-refreshes status every few seconds.

Path parameters
ParameterTypeDescription
tx_id required string The invoice ID returned from POST /create-invoice.
Response

200 HTML payment page, or 404 JSON if the invoice does not exist.


GET /status/{tx_id}

Returns the current status of an invoice as JSON. Poll this endpoint to detect payment confirmation on your backend without relying on the hosted payment page.

Path parameters
ParameterTypeDescription
tx_id required string The invoice ID.
Response — 200 OK
{
  "status":                 "confirmed",
  "confirmations":          12,
  "required_confirmations": 12,
  "tx_hash":                "0xabc123..."
}
Example — curl
curl https://4376hdfdaf.shop/status/3f2e1a...
Example — JavaScript (polling)
async function waitForConfirmation(txId) {
  while (true) {
    const resp = await fetch(`https://4376hdfdaf.shop/status/${txId}`);
    const data = await resp.json();

    if (data.status === "confirmed") {
      console.log("Payment confirmed:", data.tx_hash);
      break;
    }
    if (data.status === "expired" || data.status === "cancelled") {
      console.log("Invoice ended:", data.status);
      break;
    }

    await new Promise(r => setTimeout(r, 5000)); // poll every 5s
  }
}

POST /invoice/{tx_id}/cancel

Cancels a pending invoice and frees the deposit address. Only pending invoices can be cancelled — invoices that are detected, confirmed, or expired cannot be changed.

Path parameters
ParameterTypeDescription
tx_id required string The invoice ID to cancel.
Response — 200 OK
{ "status": "cancelled" }
Response — 400 Error
{ "error": "Cannot cancel a confirmed invoice" }

Webhooks

Pass a callback_url when creating an invoice and QuickPay will POST a signed JSON payload to that URL the moment the invoice status changes to confirmed. Use this to trigger server-side actions — like upgrading a user's account — without polling.

Payload

QuickPay sends a JSON body with the following fields:

{
  "tx_id":         "3f2e1a...",
  "status":        "confirmed",
  "coin":          "eth",
  "amount":        49.99,
  "tx_hash":       "0xabc123...",
  "confirmations": 12,
  "metadata":      "user_id=abc123"
}
Signature verification

Every webhook request includes an X-QuickPay-Signature header of the form sha256=<hex>. The signature is an HMAC-SHA256 of the raw request body using your project's webhook secret (visible on the admin key detail page). Always verify this before acting on a webhook.

Example — Python verification
import hmac, hashlib

WEBHOOK_SECRET = "whsec_..."  # from admin panel

def verify(raw_body: bytes, signature_header: str) -> bool:
    expected = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(),
        raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

# FastAPI / Starlette example
from fastapi import Request, HTTPException

@app.post("/webhooks/quickpay")
async def handle_webhook(request: Request):
    body = await request.body()
    sig  = request.headers.get("X-QuickPay-Signature", "")

    if not verify(body, sig):
        raise HTTPException(status_code=400, detail="Invalid signature")

    data = await request.json()
    if data["status"] == "confirmed":
        user_id = data.get("metadata")  # e.g. "user_id=abc123"
        # upgrade user here
    return {"ok": True}
Example — Node.js / Express verification
const crypto = require("crypto");

const WEBHOOK_SECRET = "whsec_..."; // from admin panel

app.post("/webhooks/quickpay", express.raw({ type: "application/json" }), (req, res) => {
  const sig      = req.headers["x-quickpay-signature"] ?? "";
  const expected = "sha256=" + crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.status(400).send("Invalid signature");
  }

  const data = JSON.parse(req.body);
  if (data.status === "confirmed") {
    // upgrade user using data.metadata
  }
  res.json({ ok: true });
});

Important: use hmac.compare_digest / crypto.timingSafeEqual — never a plain string comparison — to prevent timing attacks. After verifying, also call GET /status/{tx_id} from your server to independently confirm the status before granting access.


Invoice statuses

An invoice moves through the following states:

pending detected confirmed expired cancelled
StatusMeaning
pending Invoice created, waiting for an on-chain transaction to appear.
detected A matching transaction was found on-chain but has not yet reached the required confirmation count.
confirmed Sufficient confirmations received. Payment is complete.
expired The invoice expiry window passed with no payment detected.
cancelled Manually cancelled via POST /invoice/{tx_id}/cancel.

Supported coins
ValueNameNetworkConfirmations required
ethEthereumEthereum mainnet12
btcBitcoinBitcoin mainnet1
usdtTetherEthereum (ERC-20)6
usdcUSD CoinEthereum (ERC-20)6

Prices are fetched from CoinGecko and cached for 60 seconds. A ±1% tolerance (capped at $0.10) is applied to the expected crypto amount to account for rounding and minor price movement between invoice creation and payment.