Invoices

An invoice is a one-time payment request. Create it, send your customer the payment_url, get a signed webhook when we credit your balance.

POST /api/v1/merchant/invoices

Required scope: invoices.write. Required header: Idempotency-Key.

Body

FieldTypeNotes
amountstringDecimal, e.g. "5.00".
currencystringAsset symbol. Must be in your allowed list.
chainstringNetwork id (tron, ethereum, base, bsc, bitcoin).
descriptionstring≤ 500 chars. Shown to the customer.
order_idstring≤ 100 chars. Your internal id. Unique per merchant.
customer_idstringOpaque tag. Not visible to the customer.
customer_emailstringStored hashed.
success_urlstringHTTPS. Customer returns here after payment.
fail_urlstringHTTPS. Used for cancelled / expired flows.
webhook_urlstringPer-invoice override of the merchant default.
metadataobject≤ 4 KB JSON object; echoed in webhooks.

Examples

curl
curl -X POST https://api.swapss.lol/api/v1/merchant/invoices \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "5.00",
    "currency": "USDT",
    "chain": "tron",
    "description": "Order #1042",
    "order_id": "1042"
  }'
javascript
import { randomUUID } from 'node:crypto';

async function createInvoice(payload) {
  const res = await fetch('https://api.swapss.lol/api/v1/merchant/invoices', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SWAP_PAY_SECRET}`,
      'Idempotency-Key': randomUUID(),
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  });
  if (!res.ok) {
    const { error } = await res.json();
    throw new Error(`${error.code}: ${error.message}`);
  }
  return res.json();
}
python
import os, uuid, requests

def create_invoice(payload):
    r = requests.post(
        "https://api.swapss.lol/api/v1/merchant/invoices",
        headers={
            "Authorization": f"Bearer {os.environ['SWAP_PAY_SECRET']}",
            "Idempotency-Key": str(uuid.uuid4()),
            "Content-Type": "application/json",
        },
        json=payload,
        timeout=10,
    )
    r.raise_for_status()
    return r.json()
php
<?php
function create_invoice(array $payload): array {
    $ch = curl_init('https://api.swapss.lol/api/v1/merchant/invoices');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getenv('SWAP_PAY_SECRET'),
            'Idempotency-Key: ' . bin2hex(random_bytes(16)),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode($payload),
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    if ($status >= 300) {
        throw new RuntimeException("Swap Pay error: $body");
    }
    return json_decode($body, true);
}
go
package swappay

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"

    "github.com/google/uuid"
)

func CreateInvoice(payload map[string]any) (map[string]any, error) {
    body, _ := json.Marshal(payload)
    req, _ := http.NewRequest("POST", "https://api.swapss.lol/api/v1/merchant/invoices", bytes.NewReader(body))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("SWAP_PAY_SECRET"))
    req.Header.Set("Idempotency-Key", uuid.NewString())
    req.Header.Set("Content-Type", "application/json")
    res, err := http.DefaultClient.Do(req)
    if err != nil { return nil, err }
    defer res.Body.Close()
    var out map[string]any
    if err := json.NewDecoder(res.Body).Decode(&out); err != nil { return nil, err }
    if res.StatusCode >= 300 {
        return nil, fmt.Errorf("swap pay: %v", out["error"])
    }
    return out, nil
}

GET /api/v1/merchant/invoices/{public_id}

Required scope: invoices.read or invoices.write.

GET /api/v1/merchant/invoices

Cursor-paginated list. Filters: status, chain, currency, created_after, created_before, cursor, limit (1..=100, default 20).

POST /api/v1/merchant/invoices/{public_id}/cancel

Only awaiting_deposit invoices can cancel. Idempotent — a second cancel returns 200 with the current state.

Lifecycle

An invoice walks: awaiting_deposit → seen → confirming → confirmed → paid. The customer's hosted page subscribes to status events; your server receives signed webhooks per transition (see webhooks).