Skip to main content
A payin is money coming in — a payment from your customer to you. It is the mirror of a payout, which is money going out from you to a recipient. Today payins are collected through the crypto payment gateway: your customer pays in a stablecoin on a hosted checkout page, and we credit your PontisGlobe balance. That balance is the same one your payouts spend from, so money you take in can be paid out again with no transfer step.
Crypto is currently the only payin rail. The integration itself is not crypto-specific — you create a payment, redirect the customer, and act on a callback. If other rails are added, those three steps do not change; only the fields describing the crypto leg stop being the only option.

Before you start

Four things must be set up on your account. Ask your account manager — none of them are self-service: You will also need your API credentials from Developer Tools — the same API Key, Encryption Secret and HMAC Secret the payout API uses.

1. Set up the request helper

Payins use the identical transport as every other endpoint: an AES-256-GCM encrypted body, an HMAC-signed request, and your API key. If you already integrated payouts, reuse the helper you already have — there is nothing new to write. If you are starting fresh, copy the call() helper from Quickstart step 2. It is available in Node, Python, Go and Java.
Payin endpoints take no JWT. Creating a payment link moves no money by itself, so it is authenticated by your API key, request signature and IP allowlist. A payin-only integration never calls /api/v1/user/login.

2. Create the payment

Call POST /api/v1/gateway/payments/create with your own order reference and the amount.
You get back a checkout_url and an expires_at.
merchant_reference is your idempotency key. Reusing it returns the existing payment instead of creating a second one, so a retry after a network timeout is safe and hands you back the same checkout_url. Use one reference per order.

3. Redirect your customer

Send them to checkout_url. That page is hosted by us and carries your branding.
Treat checkout_url as a bearer credential. Anyone holding it can see the order amount and the deposit address. Send it to the customer directly — do not log it, email it to a shared inbox, or put it anywhere indexable.
On that page your customer picks a currency and network, sees a deposit address and a QR code, and sends the funds from their own wallet. When they finish they are returned to your success_url; if they cancel, to your fail_url.
Never fulfil the order on the browser redirect. A customer can close the tab, lose signal, or pay and never come back. The redirect is a convenience for them; the callback is the fact.

4. Handle the callback

We POST a signed JSON body to your callback URL when the payment settles. This is the event you fulfil on — full details in Payin callbacks.
Three things to get right, each covered on that page:
  • Verify the signature. Same headers and same scheme as payout callbacks, so your existing verification code works unchanged.
  • Be idempotent. Unlike payouts, payin callbacks are retried — up to 8 attempts — so the same event can arrive more than once.
  • Handle all three settled statuses separately. paid and overpaid mean the customer paid in full — fulfil. underpaid means they paid less, by any amount, and must never auto-fulfil.

5. Confirm with a status lookup (optional)

POST /api/v1/gateway/payments/status reads back a payment by your own reference.
For reconciliation and support, not for driving fulfilment. Polling it in a loop will be rate limited and will always be slower than the callback already on its way to you.

What your customer can pay with

The checkout offers the assets you are configured to accept, on the networks we support for each. It is a matrix, not a list — an asset is not available on every network: Two things follow from accepting more than one:
  • You end up holding whichever your customer chose. Accepting USDC means you will have a USDC balance, separate from your USDT balance. There is no automatic conversion between them.
  • Your payout currencies must keep up. A balance in an asset you are not enabled to pay out from is money you can see and cannot move. Ask for both to be enabled together.

The three currencies on a payment

A single payin involves three currencies and they are frequently different. Confusing them is the most common integration mistake here, so every response names each one explicitly:
Reconcile against credited_amount and credited_currency together. credited_amount alone is ambiguous the moment you accept more than one asset.
On the status endpoint the same value is called deposit_asset — it is the asset the customer deposited, which is by definition the one you were credited in.

Amounts and fees

  • requested_amount — what you asked for
  • merchant_fee_amount — the total we charge you, as a single figure
  • credited_amount — what reached your balance, after our fee
All amounts are decimal strings, never numbers — "100.00", not 100.00. Parsing them into a floating-point type will eventually cost you precision on a real payment. Order amounts accept at most 2 decimal places. A finer amount is rejected at create time rather than rounded, so your customer is never charged a different figure than your order says.

Payment statuses

Expiry is not final

A checkout link has a deadline — 30 minutes by default, set per payment with expires_in_seconds (minimum 5 minutes, maximum 24 hours). When it passes, the payment stops being one we expect to settle.
30 minutes is short on purpose: it is roughly how long a customer stays in a checkout, and the window is also how long we hold a deposit address open. If your customers pay from an exchange withdrawal rather than a self-custody wallet, raise it — those can take far longer to clear.
The deposit address stays live. A customer who pays late is still credited, the status still moves to paid, and you still get the callback. Expiry stops us waiting; it does not close the door. Do not treat expired as a terminal refusal in your own system.

Reconciliation

Payins appear in your monthly statement in their own Payments Received section, separate from payouts, with totals per credited currency. A month in which you only took payments and sent none still produces a statement. They also appear in your transactions list as gateway_deposit rows, each showing the currency it was credited in.

Testing in sandbox

Change the base URL to https://sandbox.pontisglobe.com and everything else stays the same. Nothing is stored, no deposit address is reserved, and no money can move. The outcome is driven by the last two digits of your merchant_reference, so you can reproduce a paid, underpaid or overpaid payment on demand. Full walkthrough and the trigger table: Sandbox — payins.

Going live

Everything in Going live applies. Additionally, for payins:
  • Redirect hosts registered for production — your live success_url / fail_url hosts are allow-listed separately from any test ones
  • Callback handler idempotent and signature-verified — payin callbacks retry, so a handler that double-fulfils will double-fulfil in production
  • All three settled statuses handled, and underpaid handled separately — confirm a partial payment does not reach your fulfilment path
  • Payout currencies cover the assets you accept — otherwise a real customer payment lands in a balance you cannot move
  • One small live payment end to end — confirm the callback arrives, the balance moves, and the transaction appears before you send customers to it