Skip to main content
molnpay

Accept payments

Invoice, hosted checkout, webhook.

Price the order in the currency you sell in. We return a checkout URL, your customer picks a chain and pays there, and you fulfil when the signed webhook arrives. They never make an account — with you or with us.

Northwind Supply

order_8812

49.90 USD

Pay with

  • USDTTron
  • USDCBase
  • USDTBNB Chain

Your payer needs no account — with you or with us. You fulfil on the signed webhook, never on the redirect.

How it works.

  1. Create the invoice

    One POST with an amount, a pricing currency and your own reference. You get back a checkout URL, one payment address per chain, and a locked amount in every coin you accept.

  2. They pay, at a rate that stays honest

    Your customer is never asked to commit to a chain first — one address serves every EVM chain, so whatever arrives is attributable. Stablecoins are quoted once. A volatile coin is re-quoted every two minutes, and the figure it replaces is still honoured for five, so nobody who did as they were told is told they underpaid.

  3. You get a signed webhook

    HMAC-SHA256, retried for about 46 hours, replayable from the dashboard. The payment is credited to your balance per asset.

Your first call.

POST /v1/invoices
curl https://api.molnpay.com/v1/invoices \
  -H "Authorization: Bearer $MOLNPAY_SECRET_KEY" \
  -H "Idempotency-Key: order_8812" \
  -d amount="49.90" \
  -d currency="USD" \
  -d reference_id="order_8812"
201 Created
{
  "object": "invoice",
  "id": "9f3c1b2e-…",
  "status": "open",
  "price_amount": "4990",
  "price_currency": "USD",
  "checkout_url": "https://pay.molnpay.com/i/ck_9f3c…",
  "expires_at": "2026-09-16T12:30:00.000Z",
  "quotes": [
    { "chain": "bsc", "asset": "USDT",
      "amount": "49.90", "amount_atomic": "49900000000000000000", "decimals": 18 }
  ]
}

`price_amount` is minor units — 4990, not 49.90 — and `amount_atomic` is what your customer actually sends. The two human-readable figures beside them are derived for display and are never what a payment is checked against. `decimals` is 18 here because USDT on BSC is an 18-decimal token where everywhere else it is 6; the amount is computed with that number, so the amount is right.

Worth knowing before you build.

Amounts are strings, everywhere
Including here, where 49.90 would fit in a double. One rule with no exceptions — the moment the API has “amounts are strings except sometimes”, an integrator writes the exception into their deserializer and then meets a wei value.
Under- and overpayment are states, not tickets
A ±0.5% tolerance band per invoice, published rather than negotiated. Inside it, paid. Under by more than the band leaves the address live, shows your customer the remainder with its own QR, and extends the window so they do not lose it mid-transfer. Over by more than the band records the excess as its own figure — kept and credited to you, or offered back to your customer on the page, whichever you set.
The checkout routes are the only unauthenticated ones
They are authorised by the invoice's own opaque token rather than by a key, because the payer has no account with us and never will.
Refunds are free, and the address is asked for
We charge nothing to send one — nobody should pay twice for one sale. The network does charge, and that comes out of the refund rather than off your balance. Your customer confirms where it goes on the same page: prefilled with the address they paid from when that looks like their own wallet, and left blank when it looks like an exchange, because a refund into an exchange's deposit address credits nobody and cannot be undone.

The events you will listen for.

Only one of these means money has arrived, and it is invoice.paid. Fulfilling on anything earlier — or on a browser redirect — is the most common integration bug in this category.

invoice.created
Priced, addressed and quoted.
invoice.pending
Seen on chain, below finality. Not money.
invoice.paid
Final. This is the one that means money.
invoice.underpaid
Short by more than the band. Do not ship.
invoice.overpaid
Over by more than the band; the excess is its own figure.
invoice.expired
The window closed unpaid.
invoice.canceled
Cancelled while unpaid.
invoice.refunded
The excess went back, to an address the payer confirmed.

The rest of the integration.

One key, one dashboard, one webhook signature. These are capabilities, not separate products to buy.

All 15 chainsWhich stablecoins are on each, what a sender's wallet calls the network, and the differences that cost integrators time.