No merchant KYC · Customers pay fiat, you get stablecoins · Onboarding is openApply now →

Docs / Create payments via API

Integrate PayRam into your store: create a payment, send the customer to pay, reconcile

Whether you run a store, a SaaS product or an app, the integration flow is the same: create a payment, let the customer pay, confirm receipt, then fulfil the order.

The short version: integration takes four steps. Your server calls POST /api/v1/payment to create a payment and gets back a url and a reference_id. You send the customer to the url to pay. You learn about the payment through a webhook and confirm it with the status endpoint. Finally you reconcile by order number.

1. Before you start: base URL and API key

  • Base URL (BASE_URL): the address you use to open the dashboard, shown under Settings → Site URL. For PayRamHost merchants the dashboard and the API share one address.
  • API key: each project has its own key, generated in the dashboard. Send it in the API-Key header. Use one key per project so you can tell them apart and revoke them separately.

Keep the API key on your server only. Never put it in a web page or a mobile app. If you suspect it has leaked, replace it in the dashboard immediately.

2. Step 1: create the payment

From your server, create a payment from your order data:

curl --location 'https://pay.payramhost.com/api/v1/payment' \
  --header 'API-Key: <your_api_key>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "customerEmail": "buyer@example.com",
    "customerID": "1001",
    "amountInUSD": 49.9,
    "invoiceID": "ORDER-20260001"
  }'
FieldRequiredNotes
customerEmailYesThe customer's email address
customerIDYesA unique identifier for the customer in your system
amountInUSDYesThe amount in US dollars
invoiceIDNoYour own order number. It comes back unchanged in webhooks. Strongly recommended
expireNoExpiry time in RFC 3339 format. Defaults to 24 hours
currencyNoRestrict payment to one coin, for example USDC. If omitted the customer chooses
networkNoRestrict payment to one chain, for example BASE. Use it together with currency

The response:

{
  "host": "https://pay.payramhost.com",
  "reference_id": "c80f5363-0397-4761-aa1a-3155c3a21470",
  "url": "https://pay.payramhost.com/payments?reference_id=c80f5363-...&host=https://pay.payramhost.com"
}
  • url is a ready-made payment page you can share with the customer.
  • reference_id is the unique identifier of the payment. Store it against your order. Status checks and reconciliation depend on it.

Payment links expire after 24 hours by default. After that the payment moves to CANCELLED. If your orders should be held for a shorter or longer time, set expire yourself.

Node.js example

async function createPayment(order) {
  const res = await fetch(`${process.env.PAYRAM_BASE_URL}/api/v1/payment`, {
    method: 'POST',
    headers: {
      'API-Key': process.env.PAYRAM_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      customerEmail: order.email,
      customerID: String(order.userId),
      amountInUSD: order.totalUsd,
      invoiceID: order.id,
    }),
  });
  if (!res.ok) throw new Error(`PayRam ${res.status}`);
  return res.json(); // { host, reference_id, url }
}

Python example

import os, requests

def create_payment(order):
    r = requests.post(
        f"{os.environ['PAYRAM_BASE_URL']}/api/v1/payment",
        headers={"API-Key": os.environ["PAYRAM_API_KEY"]},
        json={
            "customerEmail": order["email"],
            "customerID": str(order["user_id"]),
            "amountInUSD": order["total_usd"],
            "invoiceID": order["id"],
        },
        timeout=15,
    )
    r.raise_for_status()
    return r.json()  # {"host": ..., "reference_id": ..., "url": ...}

3. Step 2: send the customer to the payment page

The simplest approach is to redirect the customer to the returned url. On that page the customer picks a coin and network, receives a dedicated deposit address, and the page keeps watching for the payment and updates its status automatically.

If you want your own payment UI, there are other endpoints, such as assigning a fixed deposit address to a user or listing supported coins. The official PayRam docs are authoritative for those details.

4. Step 3: confirm receipt

Never trust a customer clicking "I have paid". Rely on PayRam's confirmation, using two mechanisms together:

  1. Webhooks (the main route): PayRam notifies your server when the payment state changes. Always verify the signature and make the handler idempotent. See the webhook guide.
  2. The status endpoint (fallback and double check): query by reference_id.
curl --location 'https://pay.payramhost.com/api/v1/payment/reference/c80f5363-0397-4761-aa1a-3155c3a21470' \
  --header 'API-Key: <your_api_key>'

The returned paymentState is the current state. For what each value means and what to do about it, see payment statuses.

5. Step 4: reconcile

Webhooks can occasionally be missed, so run a scheduled job as a safety net:

  1. Every few minutes, find the orders in your system that are still waiting for payment.
  2. For each, call the status endpoint with the stored reference_id.
  3. If the state is FILLED, update the order as normal. If it is CANCELLED, close the order.

The invoiceID you sent comes back unchanged in webhooks, so your finance team can look payments up directly by your own order number.

6. A complete checkout flow

  1. The customer clicks "Pay with crypto". Your server creates the order in a "pending payment" state.
  2. The server calls the create-payment endpoint, stores the returned reference_id and redirects the customer to the url.
  3. The customer sends the payment on the payment page. PayRam detects it and waits for the required confirmations.
  4. PayRam sends a notification with status FILLED to your webhook endpoint.
  5. Your server verifies the signature, double-checks with the status endpoint, then marks the order paid and ships.
  6. SmartSweep moves the funds to your cold wallet automatically. See SmartSweep explained.

7. FAQ

Does the amount have to be in dollars?

When creating a payment the amount field is amountInUSD, in US dollars. The payment page converts it into an amount of the coin the customer selects.

Does a returning customer get the same address?

PayRam assigns each customer a dedicated deposit address. According to the official documentation, once assigned the address is permanent for that customer within the same blockchain family, and later payments reuse it. Ethereum, Base and Polygon share one address because they belong to the same EVM family.

How do I avoid creating duplicate payments?

Store the reference_id on your order. When a customer refreshes the page or clicks pay again, check whether the order already has an unexpired payment and reuse its url rather than creating a new one.

What about Shopify or WooCommerce?

PayRam provides plugins for them, and the official documentation covers installation and configuration. If you use PayRamHost as a hosted service, contact us first to confirm that the plugin fits your setup.

Is there a ready-made SDK?

Yes, an official TypeScript / JavaScript SDK removes a lot of boilerplate. See the SDK guide.

  • Webhook guide: How to receive PayRam webhooks: register an endpoint, verify X-Payram-Signature, understand retry rules and payment states, handle duplicates, with Node.js and Python examples.
  • Payment statuses: What PayRam's five payment statuses mean, how to handle underpaid (PARTIALLY_FILLED), overpaid (OVER_FILLED) and expired (CANCELLED) payments, and how to keep your order status in sync using webhooks and the status endpoint.
  • TypeScript SDK: Use the official TypeScript / JavaScript SDK to integrate PayRam: install, create a client, create payments, check status, receive webhooks (Express, Fastify, Next.js), how it verifies requests and what you should add.
  • Merchant guide: A step-by-step guide to the PayRam merchant dashboard, from logging in to your first payment: nodes, wallets, hot wallet, payment links, the API and webhooks.