API v1https://paymecheck.com/api/v1

API Reference

Integrate PayMeCheck payment links, webhooks, and on-chain settlements into your product.

Non-custodial on-chain settlement
HMAC-signed webhooks with retries
Multi-chain USDC & USDT support
Dedicated contract per merchant

Introduction

The PayMeCheck REST API lets you programmatically create payment links, query payment status, and receive real-time notifications via webhooks. All payments settle on-chain through dedicated smart contracts — PayMeCheck never holds or custodies funds.

Each merchant account has a unique API key and dedicated proxy contracts on Ethereum, Polygon, Base, Arbitrum, BNB Chain, and Optimism. When a customer pays, the smart contract atomically splits USDC/USDT between the merchant wallet and the platform treasury.

Authentication

All API requests require a merchant API key sent as a Bearer token. Keys are issued when your merchant account is activated and can be found in the merchant dashboard (contact admin if you need a key rotated).

Header
Authorization: Bearer pmck_your_api_key

Only merchants with status active can use the API. Requests with invalid or missing keys return 401 Unauthorized.

Rate limits

API endpoints are rate-limited per IP address to protect the platform.

  • 60 requests per minute per IP for write operations (POST).
  • When exceeded, the API returns 429 Too Many Requests. Implement exponential backoff in your integration.

Errors

Errors use conventional HTTP status codes and a JSON body with an error field.

Error response
{ "error": "Invalid input" }

Common error codes

HTTPMeaning
400Invalid input or business rule violation
401Missing or invalid API key
404Resource not found
429Rate limit exceeded

Payments

Payments are indexed from on-chain PaymentProcessed events. They appear after block confirmation via the platform indexer. Amount fields in API responses use decimal strings (e.g. "150.00").

GET/api/v1/payments

List payments

Returns all indexed payments for the authenticated merchant, sorted by creation date (newest first).

Response

FieldTypeRequiredDescription
data[].paymentIdstringNoUnique bytes32 payment ID bound to this link.
data[].chainstringNoBlockchain network where payment was confirmed.
data[].txHashstringNoOn-chain transaction hash.
data[].payerAddressstringNoWallet address that initiated the payment.
data[].amountstringNoInteger amount in cents with two implied decimals (no decimal point). Examples: "100" = 1.00 USDC, "150" = 1.50, "15000" = 150.00. Used in API requests only.
data[].feestringNoPlatform fee amount (token units, string).
data[].merchantAmountstringNoAmount sent to merchant wallet (token units, string).
data[].statusstringNoin-progress | completed | failed

Webhooks

Configure your webhook endpoint in the merchant dashboard. PayMeCheck sends HTTP POST requests with JSON payloads when events occur.

  • Enable webhooks in Dashboard → Webhook. Set your HTTPS endpoint URL and toggle webhooks on. The signing secret (whsec_…) is generated automatically when you first enable webhooks — copy it once; it is not shown again in full.
  • Regenerate the secret from the same page if you lose it. Updating the secret invalidates HMAC verification until your server uses the new value.
  • Every delivery includes Content-Type: application/json, X-PayMeCheck-Signature (sha256={hex}), and X-PayMeCheck-Delivery-Id (MongoDB delivery ID for idempotency and support).
  • Webhook amounts use human-readable decimal strings (e.g. "150.00" USDC). The REST API uses integer cents in requests (e.g. "15000" = 150.00) — do not mix the two formats.
  • Use Send test event in Dashboard → Webhook to receive a full payment.completed payload (same shape as a real payment). Test deliveries include test: true in data. Use webhook.site or ngrok if you do not have an endpoint yet.
  • Dashboard → Webhook Monitor shows delivery stats, filters, full payload/attempt history, and manual retry for failed deliveries.
  • Failed deliveries are retried automatically up to 3 times (1 min, 5 min, 30 min). Monitor status and retry manually in Webhook Monitor.

Event types

payment.completed

Fired when an on-chain payment is indexed and confirmed. data includes top-level fields for quick parsing plus nested payment (full indexed record) and paymentLink (link snapshot: amount, metadata, external_order_id, description, redirect URLs, contract addresses, payUrl, explorer URLs).

payment.expired

Fired when an active link expires without payment (default max 1 hour after creation). data contains linkId, paymentId, amount (decimal string), and token — not the full link object.

payment.completed
{
  "event": "payment.completed",
  "timestamp": "2026-06-10T14:32:01.000Z",
  "deliveryId": "674abc...",
  "data": {
    "paymentId": "0x8f3a2b1c...",
    "linkId": "a1b2c3d4e5f678901234abcd",
    "amount": "150.00",
    "token": "USDC",
    "chain": "polygon",
    "txHash": "0xabc123...",
    "payer": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "merchantAmount": "148.50",
    "fee": "1.50",
    "contractAddress": "0xMerchantContract...",
    "blockNumber": 61234567,
    "feeBps": 100,
    "explorerTxUrl": "https://polygonscan.com/tx/0xabc123...",
    "payUrl": "https://paymecheck.com/pay/a1b2c3d4e5f678901234abcd",
    "payment": {
      "id": "...",
      "paymentLinkId": "...",
      "merchantId": "...",
      "paymentId": "0x8f3a2b1c...",
      "chain": "polygon",
      "token": "USDC",
      "contractAddress": "0xMerchantContract...",
      "txHash": "0xabc123...",
      "blockNumber": 61234567,
      "payerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
      "amount": "150.00",
      "fee": "1.50",
      "merchantAmount": "148.50",
      "feeBps": 100,
      "status": "completed",
      "confirmedAt": "2026-06-10T14:32:01.000Z",
      "createdAt": "2026-06-10T14:32:01.000Z",
      "updatedAt": "2026-06-10T14:32:01.000Z",
      "partnerCommissionBps": null,
      "partnerCommissionAmount": null,
      "explorerTxUrl": "https://polygonscan.com/tx/0xabc123...",
      "explorerContractUrl": "https://polygonscan.com/address/0xMerchantContract..."
    },
    "paymentLink": {
      "linkId": "a1b2c3d4e5f678901234abcd",
      "paymentId": "0x8f3a2b1c...",
      "amount": "150.00",
      "token": "USDC",
      "chains": ["polygon"],
      "feeBps": 100,
      "contractAddresses": { "polygon": "0xMerchantContract..." },
      "description": "Order #1234",
      "external_order_id": "WC-12345",
      "metadata": { "orderId": "1234" },
      "redirect_url_ok": "https://shop.example.com/order/success",
      "redirect_url_error": "https://shop.example.com/order/error",
      "status": "paid",
      "expiresAt": "2026-06-10T15:00:00.000Z",
      "createdAt": "2026-06-10T14:00:00.000Z",
      "updatedAt": "2026-06-10T14:32:01.000Z",
      "payUrl": "https://paymecheck.com/pay/a1b2c3d4e5f678901234abcd"
    }
  }
}
payment.expired
{
  "event": "payment.expired",
  "timestamp": "2026-06-10T18:00:00.000Z",
  "deliveryId": "674def...",
  "data": {
    "linkId": "a1b2c3d4e5f678901234abcd",
    "paymentId": "0x8f3a2b1c...",
    "amount": "150.00",
    "token": "USDC"
  }
}
payment.completed (test)
{
  "event": "payment.completed",
  "timestamp": "2026-06-10T14:32:01.000Z",
  "deliveryId": "674test...",
  "data": {
    "test": true,
    "paymentId": "0x8f3a2b1c...",
    "linkId": "a1b2c3d4e5f678901234abcd",
    "amount": "150.00",
    "token": "USDC",
    "chain": "polygon",
    "payment": { "...": "same shape as production" },
    "paymentLink": { "...": "same shape as production" }
  }
}

When data.test is true, the delivery is from Send test event or uses simulated data — treat as non-production.

Verify signatures

Compute HMAC-SHA256 of the raw request body using your webhook secret. Compare with the X-PayMeCheck-Signature header (format: sha256={hex}).

Node.js
const crypto = require("crypto")

function verifyWebhook(secret, rawBody, signatureHeader) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex")
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  )
}
Python
import hmac
import hashlib

def verify_webhook(secret: str, body: bytes, signature: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Checkout flow

  1. 1

    Create a payment link via API or dashboard.

  2. 2

    Redirect the customer to https://paymecheck.com/pay/{linkId}.

  3. 3

    Customer completes payment on the hosted checkout (connect wallet, confirm USDC or USDT).

  4. 4

    PayMeCheck confirms the payment on-chain and marks the link as paid.

  5. 5

    Webhook payment.completed is dispatched to your endpoint with the full payment and link payload.

  6. 6

    If redirect_url_ok or redirect_url_error were set on the link, the hosted checkout redirects the customer to your URL after success (~2.5 s) or after payment failure, expiry, or cancellation (~2 s).

Redirect customers to the pay URL — checkout is fully hosted. You receive payment.completed via webhook when settlement is confirmed. Post-checkout redirects are optional and configured per link.

Statuses

Payment link statuses

activeAwaiting payment
paidPayment confirmed on-chain
expiredUnpaid after expiry (default max 1 hour from creation)
cancelledManually cancelled via API

Payment statuses

in-progressTransaction detected, awaiting finality
completedIndexed and confirmed
failedIndexing or validation failed

Networks & tokens

Supported chains and stablecoins. Token availability varies by chain.

ChainTokens
ethereumUSDC, USDT
polygonUSDC, USDT
baseUSDC
arbitrumUSDC, USDT
bscUSDC, USDT
optimismUSDC, USDT