SDK quickstarts
Official SDKs are coming Q4 2026. Until then, these copy-paste snippets cover the three operations you need for a complete integration: create an invoice, check its status, and verify webhook deliveries.
Replace sspay_sec_test_demo_YOUR_KEY with your actual API key (found in the cabinet under API Keys). Never hardcode keys in source code; use environment variables.
1. Create an invoice
POST to /api/v1/merchant/invoices. Returns a payment_url to send your customer and a unique id to track the invoice in your database.
Node / TypeScript
import crypto from 'node:crypto';
const API_BASE = 'https://swapss.lol/api/v1/merchant';
const API_KEY = process.env.SWAPSS_PAY_API_KEY!; // sspay_sec_test_demo_xxxxxxx
interface CreateInvoiceParams {
amount: string; // decimal string, e.g. "5.00"
currency: string; // e.g. "USDT"
chain: string; // e.g. "tron"
orderId: string; // your unique order reference
description?: string;
successUrl?: string;
failUrl?: string;
webhookUrl?: string;
}
async function createInvoice(params: CreateInvoiceParams) {
const idempotencyKey = crypto.randomUUID();
const res = await fetch(`${API_BASE}/invoices`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Idempotency-Key': idempotencyKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: params.amount,
currency: params.currency,
chain: params.chain,
order_id: params.orderId,
description: params.description,
success_url: params.successUrl,
fail_url: params.failUrl,
webhook_url: params.webhookUrl,
}),
});
if (!res.ok) {
const err = await res.json();
throw new Error(`SwapSS Pay error: ${err.error?.code} — ${err.error?.message}`);
}
return res.json();
}
// Example
const invoice = await createInvoice({
amount: '5.00',
currency: 'USDT',
chain: 'tron',
orderId: 'ORD-1042',
successUrl: 'https://example.com/thanks',
failUrl: 'https://example.com/oops',
webhookUrl: 'https://example.com/swap-pay-webhook',
});
console.log(invoice.payment_url); // send this to your customerPython
import os
import uuid
import requests
API_BASE = "https://swapss.lol/api/v1/merchant"
API_KEY = os.environ["SWAPSS_PAY_API_KEY"] # sspay_sec_test_demo_xxxxxxx
def create_invoice(
amount: str,
currency: str,
chain: str,
order_id: str,
description: str = "",
success_url: str = "",
fail_url: str = "",
webhook_url: str = "",
) -> dict:
idempotency_key = str(uuid.uuid4())
resp = requests.post(
f"{API_BASE}/invoices",
headers={
"Authorization": f"Bearer {API_KEY}",
"Idempotency-Key": idempotency_key,
"Content-Type": "application/json",
},
json={
"amount": amount,
"currency": currency,
"chain": chain,
"order_id": order_id,
"description": description,
"success_url": success_url,
"fail_url": fail_url,
"webhook_url": webhook_url,
},
timeout=30,
)
resp.raise_for_status()
return resp.json()
# Example
invoice = create_invoice(
amount="5.00",
currency="USDT",
chain="tron",
order_id="ORD-1042",
success_url="https://example.com/thanks",
fail_url="https://example.com/oops",
webhook_url="https://example.com/swap-pay-webhook",
)
print(invoice["payment_url"]) # send this to your customerPHP
<?php
use GuzzleHttp\Client;
$apiBase = 'https://swapss.lol/api/v1/merchant';
$apiKey = getenv('SWAPSS_PAY_API_KEY'); // sspay_sec_test_demo_xxxxxxx
$http = new Client(['timeout' => 30]);
function createInvoice(
Client $http,
string $apiBase,
string $apiKey,
string $amount,
string $currency,
string $chain,
string $orderId,
string $description = '',
string $successUrl = '',
string $failUrl = '',
string $webhookUrl = '',
): array {
$idempotencyKey = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
$response = $http->post("{$apiBase}/invoices", [
'headers' => [
'Authorization' => "Bearer {$apiKey}",
'Idempotency-Key' => $idempotencyKey,
'Content-Type' => 'application/json',
],
'json' => [
'amount' => $amount,
'currency' => $currency,
'chain' => $chain,
'order_id' => $orderId,
'description' => $description,
'success_url' => $successUrl,
'fail_url' => $failUrl,
'webhook_url' => $webhookUrl,
],
]);
return json_decode($response->getBody(), true);
}
// Example
$invoice = createInvoice(
$http, $apiBase, $apiKey,
amount: '5.00', currency: 'USDT', chain: 'tron',
orderId: 'ORD-1042',
successUrl: 'https://example.com/thanks',
failUrl: 'https://example.com/oops',
webhookUrl: 'https://example.com/swap-pay-webhook',
);
echo $invoice['payment_url']; // send this to your customer2. Check invoice status
Polling works for simple integrations, but webhooks are better for production: you don't need a tight poll loop and you handle every state transition automatically.
Node / TypeScript
async function getInvoice(invoiceId: string) {
const res = await fetch(`${API_BASE}/invoices/${invoiceId}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// Poll until paid or terminal state
async function waitForPayment(invoiceId: string, maxWaitMs = 60_000) {
const terminalStates = new Set(['paid', 'expired', 'cancelled', 'failed']);
const start = Date.now();
while (Date.now() - start < maxWaitMs) {
const inv = await getInvoice(invoiceId);
if (terminalStates.has(inv.status)) return inv;
await new Promise(r => setTimeout(r, 5_000)); // poll every 5s
}
throw new Error('timeout waiting for payment');
}Python
import time
def get_invoice(invoice_id: str) -> dict:
resp = requests.get(
f"{API_BASE}/invoices/{invoice_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
resp.raise_for_status()
return resp.json()
def wait_for_payment(invoice_id: str, max_wait: int = 60) -> dict:
terminal = {"paid", "expired", "cancelled", "failed"}
deadline = time.monotonic() + max_wait
while time.monotonic() < deadline:
inv = get_invoice(invoice_id)
if inv["status"] in terminal:
return inv
time.sleep(5) # poll every 5s
raise TimeoutError("payment not received in time")PHP
function getInvoice(Client $http, string $apiBase, string $apiKey, string $invoiceId): array {
$response = $http->get("{$apiBase}/invoices/{$invoiceId}", [
'headers' => ['Authorization' => "Bearer {$apiKey}"],
]);
return json_decode($response->getBody(), true);
}
function waitForPayment(Client $http, string $apiBase, string $apiKey, string $invoiceId, int $maxWait = 60): array {
$terminal = ['paid', 'expired', 'cancelled', 'failed'];
$deadline = time() + $maxWait;
while (time() < $deadline) {
$inv = getInvoice($http, $apiBase, $apiKey, $invoiceId);
if (in_array($inv['status'], $terminal, true)) return $inv;
sleep(5);
}
throw new RuntimeException('payment not received in time');
}3. Verify a webhook
Every delivery carries a Swap-Pay-Signature header. Verify it before processing. Full explanation at Webhook verification.
Node / TypeScript
import crypto from 'node:crypto';
const WEBHOOK_SECRET = process.env.SWAP_PAY_WEBHOOK_SECRET!;
function verifySignature(header: string, rawBody: Buffer | string, toleranceSec = 300): boolean {
const parts: Record<string, string> = {};
for (const kv of header.split(',')) {
const eq = kv.trim().indexOf('=');
if (eq > 0) parts[kv.slice(0, eq).trim()] = kv.slice(eq + 1).trim();
}
const t = Number.parseInt(parts['t'] ?? '', 10);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const body = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');
const mac = crypto.createHmac('sha256', WEBHOOK_SECRET).update(`${t}.${body}`).digest('hex');
const a = Buffer.from(mac, 'hex');
const b = Buffer.from(parts['v1'] ?? '', 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express handler
app.post('/swap-pay-webhook', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifySignature(req.headers['swap-pay-signature'] as string ?? '', req.body)) {
return res.status(401).json({ error: 'invalid signature' });
}
const event = JSON.parse(req.body.toString());
// Dedupe by event.event_id, then handle event.type
res.json({ ok: true });
});Python
import hmac, hashlib, time, os, json
from flask import Flask, request, abort
WEBHOOK_SECRET = os.environ["SWAP_PAY_WEBHOOK_SECRET"].encode()
def verify_signature(header: str, raw_body: bytes, tolerance: int = 300) -> bool:
parts = {}
for kv in header.split(","):
kv = kv.strip()
eq = kv.find("=")
if eq > 0:
parts[kv[:eq].strip()] = kv[eq+1:].strip()
try:
t = int(parts["t"])
except (KeyError, ValueError):
return False
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(WEBHOOK_SECRET, f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
app = Flask(__name__)
@app.post("/swap-pay-webhook")
def webhook():
if not verify_signature(request.headers.get("Swap-Pay-Signature", ""), request.get_data()):
abort(401)
event = json.loads(request.get_data())
# Dedupe by event["event_id"], then handle event["type"]
return {"ok": True}PHP
<?php
function verifySignature(string $header, string $rawBody, string $secret, int $tolerance = 300): bool {
$parts = [];
foreach (explode(',', $header) as $kv) {
$kv = trim($kv);
$eq = strpos($kv, '=');
if ($eq !== false) $parts[trim(substr($kv, 0, $eq))] = trim(substr($kv, $eq + 1));
}
$t = (int)($parts['t'] ?? 0);
if (!$t || abs(time() - $t) > $tolerance) return false;
$expected = hash_hmac('sha256', "{$t}.{$rawBody}", $secret);
return hash_equals($expected, $parts['v1'] ?? '');
}
$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_SWAP_PAY_SIGNATURE'] ?? '';
$secret = getenv('SWAP_PAY_WEBHOOK_SECRET');
if (!verifySignature($header, $rawBody, $secret)) {
http_response_code(401);
echo json_encode(['error' => 'invalid signature']);
exit;
}
$event = json_decode($rawBody, true);
// Dedupe by $event['event_id'], then handle $event['type']
echo json_encode(['ok' => true]);Environment variables used
| Variable | Value |
|---|---|
SWAPSS_PAY_API_KEY | Your secret API key (sspay_sec_test_demo_xxxxxxx). Found in the cabinet under API Keys. |
SWAP_PAY_WEBHOOK_SECRET | Your webhook signing secret. Shown once when you create the webhook subscription in the cabinet. |
What's next
- Full quickstart guide: end-to-end walkthrough with curl
- API reference: every endpoint, parameter, and response schema
- Webhooks overview: event catalog, retry policy, IP allowlist
- Idempotency guide: how to safely retry failed requests
Want to contribute an SDK? Email sdk@swapss.lol with a link to your repository. Community SDKs are listed here once they pass a basic review.