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);