Skip to content

Payments API Reference

Payments API Reference

Complete reference for the Payment Gateway REST API.

Base URL

https://api.sanpay.io/api/v1

Authentication

All API requests require authentication via Bearer token:

Authorization: Bearer YOUR_API_KEY

Payments

Create Payment

POST /payments

Create a new payment request.

Request Body
Field Type Required Description
fiatAmountnumberAmount in fiat currency
fiatCurrencystringFiat currency code: USD, EUR, etc.
coinIdstring (UUID)Specific coin ID. If omitted with allowUserSelection: true, customer chooses.
chainIdstringBlockchain identifier (e.g., BTC, ETH)
metadataobjectCustom key-value pairs
externalIdstringYour internal order/reference ID
callbackUrlstringURL for webhooks and redirect after payment
ttlSecondsintegerTime-to-live in seconds (default: 1800)
allowUserSelectionbooleanAllow customer to choose crypto asset (default: false)
confirmationsintegerRequired blockchain confirmations
isTestnetbooleanUse testnet mode (default: false)
idempotencyKeystringUnique key to prevent duplicate payments
Example
curl -X POST https://api.sanpay.io/api/v1/payments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fiatAmount": 99.99,
    "fiatCurrency": "USD",
    "chainId": "BTC",
    "externalId": "order_12345",
    "callbackUrl": "https://example.com/webhook"
  }'
Response 201 Created
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "PENDING_ASSIGNMENT",
  "requestedFiatAmount": 99.99,
  "fiatCurrency": "USD",
  "requestedCryptoAmount": "0.00250000",
  "coinSymbol": "BTC",
  "address": null,
  "qrData": null,
  "paymentUrl": "https://pay.sanpay.io/p/a1b2c3d4",
  "fxRateUsed": "39996.00",
  "fxLockedAt": "2024-01-15T11:00:00Z",
  "fxLockedUntil": "2024-01-15T11:30:00Z",
  "expiresAt": "2024-01-15T11:30:00Z",
  "createdAt": "2024-01-15T11:00:00Z"
}

Get Payment

GET /payments/{paymentId}

Retrieve a payment by ID.

Response 200 OK
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "CONFIRMED",
  "requestedFiatAmount": 99.99,
  "fiatCurrency": "USD",
  "requestedCryptoAmount": "0.00250000",
  "coinSymbol": "BTC",
  "address": "bc1qxy2kgdygjrsqvzq3rj...",
  "txHash": "abc123def456...",
  "confirmations": 6,
  "confirmedAt": "2024-01-15T11:15:00Z",
  "createdAt": "2024-01-15T11:00:00Z"
}

Payment Statuses

Status Description
PENDING_ASSIGNMENTPayment created, waiting for address assignment
PENDING_SELECTIONWaiting for customer to select crypto asset (deferred selection)
AWAITING_PAYMENTAddress assigned, waiting for customer to send funds
DETECTEDTransaction seen in mempool (0 confirmations)
PARTIALLY_PAIDPartial payment received, awaiting remaining amount
CONFIRMINGTransaction received, waiting for required confirmations
CONFIRMEDPayment confirmed with required block confirmations
CONFIRMED_PARTIALPartial amount confirmed
SETTLEDPayment fully settled — final success state
PARTIALLY_SETTLEDPartial amount settled
EXPIREDPayment window expired with no payment
EXPIRED_PARTIALPayment window expired with partial payment received
CANCELLEDPayment cancelled by merchant
REFUND_PENDING_ADDRESSRefund initiated, waiting for customer refund address
REFUND_PROCESSINGRefund transaction being processed
REFUNDEDRefund completed successfully
REFUND_FAILEDRefund transaction failed
REFUND_EXPIREDRefund address collection expired
REORG_DETECTEDBlockchain reorganization detected — payment under review

List Payments

GET /payments

List all payments for your account.

Query Parameters
Field Type Description
statusstringFilter by status
externalIdstringFilter by order ID
sincestringISO 8601 timestamp
untilstringISO 8601 timestamp
limitintegerMax results (default: 20, max: 100)
offsetintegerPagination offset
Response 200 OK
{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "status": "SETTLED",
      "requestedFiatAmount": 99.99,
      "fiatCurrency": "USD",
      "requestedCryptoAmount": "0.00250000",
      "coinSymbol": "BTC",
      "createdAt": "2024-01-15T11:00:00Z"
    }
  ],
  "pagination": {
    "total": 156,
    "limit": 20,
    "offset": 0
  }
}

Checkout Sessions

Create Checkout Session

POST /checkout/sessions

Create a hosted checkout session.

Request Body
Field Type Required Description
fiatAmountnumberAmount in fiat currency
fiatCurrencystringFiat currency code: USD, EUR, etc.
descriptionstringOrder description
externalIdstringYour internal order ID for correlation
successUrlstringRedirect URL on success
cancelUrlstringRedirect URL on cancel
customerEmailstringPre-fill customer email
ttlSecondsintegerTime-to-live in seconds (default: 1800)
Response 201 Created
{
  "id": "cs_xyz789",
  "url": "https://checkout.sanpay.io/cs_xyz789",
  "expiresAt": "2024-01-15T12:00:00Z"
}

Refunds

Create Refund

POST /payments/{paymentId}/refunds

Record a refund for a payment.

Request Body
Field Type Required Description
amountstringRefund amount in crypto
txHashstringRefund transaction hash
reasonstringReason for refund
Response 201 Created
{
  "id": "ref_abc123",
  "paymentId": "pay_abc123",
  "amount": "0.00250000",
  "currency": "BTC",
  "txHash": "abc123...",
  "status": "completed",
  "createdAt": "2024-01-16T10:00:00Z"
}

Webhooks

Payment events are delivered to your webhook URL:

Event Description
PAYMENT_CREATEDPayment request created
PAYMENT_ADDRESS_ASSIGNEDCrypto address assigned to payment
PAYMENT_DETECTEDTransaction seen in mempool
PAYMENT_CONFIRMATION_UPDATEConfirmation count updated
PAYMENT_CONFIRMEDPayment reached required confirmations
PAYMENT_SETTLEDPayment fully settled — final success event
PAYMENT_EXPIREDPayment window expired
PAYMENT_EXPIRED_PARTIALPayment expired with partial amount received
PAYMENT_CANCELLEDPayment cancelled by merchant
PAYMENT_REFUND_INITIATEDRefund process started
PAYMENT_REFUND_COMPLETEDRefund completed successfully
PAYMENT_REFUND_FAILEDRefund transaction failed
PAYMENT_TX_UNMATCHEDTransaction received but could not be matched
PAYMENT_REORGBlockchain reorganization detected
PAYMENT_LATE_DETECTEDPayment detected after expiry

Webhook Payload

{
  "eventId": "evt-a1b2c3d4-e5f6",
  "eventType": "PAYMENT_CONFIRMED",
  "timestamp": "2024-01-15T11:15:00Z",
  "tenantId": "your-tenant-id",
  "paymentRequestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "address": "bc1qxy2kgdygjrsqvzq3rj...",
  "coinSymbol": "BTC",
  "chainId": "BTC",
  "status": "CONFIRMED",
  "confirmations": 6,
  "requiredConfirmations": 3,
  "requestedFiatAmount": 99.99,
  "fiatCurrency": "USD",
  "requestedCryptoAmount": "0.00250000"
}

Error Responses

{
  "error": {
    "code": "PAYMENT_EXPIRED",
    "message": "The payment request has expired",
    "details": {
      "paymentId": "pay_abc123",
      "expiredAt": "2024-01-15T11:30:00Z"
    }
  }
}

Payment Error Codes

Code Status Description
PAYMENT_NOT_FOUND404Payment does not exist
PAYMENT_EXPIRED400Payment has expired
INVALID_AMOUNT400Invalid amount format
UNSUPPORTED_CURRENCY400Currency not supported
RATE_LIMITED429Too many requests

Cancel Payment

POST /payments/{paymentId}/cancel

Cancel a payment request. Only payments in `AWAITING_PAYMENT` or `PENDING_SELECTION` status can be cancelled. Payments with detected transactions cannot be cancelled.

Response 200 OK
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "CANCELLED",
  "cancelledAt": "2026-01-15T11:20:00Z",
  "reason": "Customer requested cancellation"
}

Get Checkout Session

GET /checkout/sessions/{sessionId}

Retrieve a checkout session by ID.

Response 200 OK
{
  "id": "cs_xyz789",
  "status": "completed",
  "paymentId": "pay_abc123",
  "amount": "99.99",
  "currency": "USD",
  "successUrl": "https://example.com/success",
  "cancelUrl": "https://example.com/cancel",
  "expiresAt": "2024-01-15T12:00:00Z",
  "createdAt": "2024-01-15T11:00:00Z"
}

Expire Checkout Session

DELETE /checkout/sessions/{sessionId}

Expire a checkout session immediately. The session can no longer be used for payment.

Response 200 OK
{
  "id": "cs_xyz789",
  "status": "expired",
  "expiredAt": "2024-01-15T11:30:00Z"
}

List Refunds

GET /payments/{paymentId}/refunds

List all refunds for a specific payment.

Response 200 OK
{
  "data": [
    {
      "id": "ref_abc123",
      "paymentId": "pay_abc123",
      "amount": "0.00125000",
      "currency": "BTC",
      "status": "completed",
      "reason": "Customer request",
      "createdAt": "2024-01-16T10:00:00Z"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 20,
    "offset": 0
  }
}

Get Refund

GET /refunds/{refundId}

Retrieve a refund by ID.

Response 200 OK
{
  "id": "ref_abc123",
  "paymentId": "pay_abc123",
  "amount": "0.00125000",
  "currency": "BTC",
  "txHash": "abc123def456...",
  "status": "completed",
  "reason": "Customer request",
  "createdAt": "2024-01-16T10:00:00Z"
}

Payment links are reusable URLs that allow customers to pay any amount. Ideal for donations, tips, or invoices.

POST /payment-links

Create a reusable payment link.

Request Body
Field Type Required Description
namestringDisplay name for the payment link
amountstringFixed amount (if omitted, customer enters amount)
currencystringFiat currency code: USD, EUR, etc.
descriptionstringDescription shown to customer
redirectUrlstringURL to redirect after payment
metadataobjectCustom key-value pairs
Response 201 Created
{
  "id": "plink_abc123",
  "name": "Premium Subscription",
  "url": "https://pay.sanpay.io/l/plink_abc123",
  "amount": "99.99",
  "currency": "USD",
  "active": true,
  "createdAt": "2024-01-15T11:00:00Z"
}

GET /payment-links

List all payment links for your account.

Query Parameters
Field Type Description
activebooleanFilter by active status
limitintegerMax results (default: 20, max: 100)
offsetintegerPagination offset
Response 200 OK
{
  "data": [
    {
      "id": "plink_abc123",
      "name": "Premium Subscription",
      "url": "https://pay.sanpay.io/l/plink_abc123",
      "amount": "99.99",
      "currency": "USD",
      "active": true,
      "paymentsCount": 42,
      "totalCollected": "4199.58",
      "createdAt": "2024-01-15T11:00:00Z"
    }
  ],
  "pagination": {
    "total": 5,
    "limit": 20,
    "offset": 0
  }
}

GET /payment-links/{linkId}

Retrieve a payment link by ID.


DELETE /payment-links/{linkId}

Deactivate a payment link. The link URL will no longer accept payments.

Response 200 OK
{
  "id": "plink_abc123",
  "active": false,
  "deactivatedAt": "2024-01-20T15:00:00Z"
}

Exchange Rates

Get real-time cryptocurrency exchange rates.

Get Exchange Rates

GET /exchange-rates

Retrieve current exchange rates for supported cryptocurrencies.

Query Parameters
Field Type Description
basestringBase fiat currency (default: USD)
symbolsstringComma-separated crypto symbols (e.g., BTC,ETH)
Response 200 OK
{
  "base": "USD",
  "timestamp": "2024-01-15T11:00:00Z",
  "rates": {
    "BTC": "0.000025",
    "ETH": "0.00045",
    "USDT": "1.0001",
    "USDC": "0.9999"
  }
}

Account

Get Account Balance

GET /account/balance

Retrieve your current account balances across all cryptocurrencies.

Response 200 OK
{
  "balances": [
    {
      "currency": "BTC",
      "available": "1.25000000",
      "pending": "0.05000000",
      "total": "1.30000000"
    },
    {
      "currency": "ETH",
      "available": "15.50000000",
      "pending": "0.00000000",
      "total": "15.50000000"
    }
  ],
  "updatedAt": "2024-01-15T11:00:00Z"
}

Rate Limiting

API requests are rate-limited to ensure fair usage. Rate limit information is included in response headers.

Rate Limit Headers

Header Description
X-RateLimit-LimitMaximum requests allowed per window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the window resets

Default Rate Limits

Endpoint Limit Window
POST /payments1001 minute
GET /payments3001 minute
POST /checkout/sessions1001 minute
GET /exchange-rates601 minute

Idempotency

Use idempotency keys to safely retry requests without risking duplicate operations.

Using Idempotency Keys

Include an `idempotencyKey` field in the request body with a unique value (e.g., UUID) for POST requests:

curl -X POST https://api.sanpay.io/api/v1/payments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fiatAmount": 99.99, "fiatCurrency": "USD", "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000"}'

Idempotency Behavior

  • Keys are valid for 24 hours after the first request
  • Subsequent requests with the same key return the original response
  • Keys are scoped to your API key
  • We recommend using UUID v4 for idempotency keys

API Examples

Here are examples of creating a payment in popular programming languages:

Node.js

const response = await fetch('https://api.sanpay.io/api/v1/payments', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.SANPAY_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    fiatAmount: 99.99,
    fiatCurrency: 'USD',
    externalId: 'ORDER-12345',
    description: 'Premium Plan'
  })
});
const payment = await response.json();

Python

import requests

response = requests.post(
    'https://api.sanpay.io/api/v1/payments',
    headers={
        'Authorization': f'Bearer {os.environ["SANPAY_API_KEY"]}',
        'Content-Type': 'application/json'
    },
    json={
        'fiatAmount': 99.99,
        'fiatCurrency': 'USD',
        'externalId': 'ORDER-12345',
        'description': 'Premium Plan'
    }
)
payment = response.json()

PHP

$ch = curl_init('https://api.sanpay.io/api/v1/payments');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('SANPAY_API_KEY'),
        'Content-Type: application/json'
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'fiatAmount' => 99.99,
        'fiatCurrency' => 'USD',
        'externalId' => 'ORDER-12345',
        'description' => 'Premium Plan'
    ])
]);
$payment = json_decode(curl_exec($ch), true);