Shopment Log in Sign up

API documentation

Base URL: https://shopment.ir/api/v1 · All requests and responses are JSON.

Authentication and signing

Every request must carry these three headers:

X-Api-Key:   pk_...            (from the panel, "API & webhook" tab)
X-Timestamp: 1761300000        (Unix time in seconds, at most 5 minutes off)
X-Signature: hex( HMAC_SHA256( timestamp + "." + raw_body , api_secret ) )

GET requests have an empty body, so the signature is computed over timestamp + ".".

Create an invoice

POST /api/v1/invoices

{
  "amount": "25.50",              // in USD (required)
  "order_id": "ORDER-1001",       // unique per store (required)
  "currency": "USDT_TRC20",       // optional; if empty the customer chooses on the payment page
  "description": "Subscription",
  "callback_url": "https://shop.example.com/crypto/webhook",
  "success_url": "https://shop.example.com/thanks",
  "lifetime": 60                  // minutes, 5 to 1440
}

Response:

{
  "state": 0,
  "result": {
    "uuid": "9b1d...",
    "order_id": "ORDER-1001",
    "status": "waiting",          // pending | waiting | partial | paid | expired | cancelled
    "amount": "25.50",
    "currency": "USDT_TRC20",
    "network": "tron",
    "pay_amount": "25.5",
    "paid_amount": "0",
    "address": "TXyz...",
    "memo": null,                 // TON only
    "exact_amount": false,        // true = ordinary-address wallet; pay_amount must be sent exactly
    "txids": [],
    "payment_url": "https://shopment.ir/pay/9b1d...",
    "expires_at": "2026-10-24T12:00:00+00:00",
    "paid_at": null
  }
}

Redirect the customer to payment_url, or show the address and amount on your own page.

The payment page picks its language from the customer's browser. To force one, append ?lang=en or ?lang=fa to payment_url.

If the wallet for that network is set up as an ordinary address, every invoice shares that address and pay_amount carries a few unique decimals (e.g. 25.5001). If you display the amount yourself, show it in full without rounding. ETH and BNB are not accepted in this mode.

Other endpoints

GET  /api/v1/currencies                    currencies enabled for your store
GET  /api/v1/invoices/{uuid}               invoice status
GET  /api/v1/invoices/by-order/{order_id}  status by order ID
POST /api/v1/invoices/{uuid}/cancel        cancel an unpaid invoice

Currency codes

USDT_TRC20, USDT_BEP20, USDT_POLYGON, USDT_ARBITRUM, USDT_TON, USDT_ERC20

Get the exact list enabled for your store from GET /api/v1/currencies (only networks you have set up a wallet for). ETH, BNB and TON are supported too but disabled by default.

Webhook

On every status change (paid, partial, expired, cancelled) a POST is sent with the same structure as result plus an event field, with X-Timestamp and X-Signature headers signed with your api_secret exactly as above. If you don't answer with a 2xx, it is retried up to 6 times (over about 4 hours). Always verify the signature and status == "paid" before fulfilling an order, and use order_id to avoid processing it twice.

PHP example

<?php
$apiKey = 'pk_...';
$secret = 'sk_...';

function shopment(string $method, string $path, ?array $data = null) {
    global $apiKey, $secret;
    $body = $data ? json_encode($data) : '';
    $ts = (string) time();
    $ch = curl_init('https://shopment.ir/api/v1' . $path);
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_POSTFIELDS     => $body ?: null,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'Accept: application/json',
            'X-Api-Key: ' . $apiKey,
            'X-Timestamp: ' . $ts,
            'X-Signature: ' . hash_hmac('sha256', $ts . '.' . $body, $secret),
        ],
    ]);
    return json_decode(curl_exec($ch), true);
}

$invoice = shopment('POST', '/invoices', ['amount' => '10', 'order_id' => 'A-1']);
header('Location: ' . $invoice['result']['payment_url']);

// --- webhook.php ---
$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
if (!hash_equals(hash_hmac('sha256', $ts . '.' . $raw, $secret), $sig)) {
    http_response_code(401); exit;
}
$event = json_decode($raw, true);
if ($event['status'] === 'paid') {
    // mark order $event['order_id'] as paid (idempotently)
}
http_response_code(200);