Creating Payments
Payments are created via the POST /api/payment/init endpoint.
Authentication
All requests must include your API key in the x-api-key header:
Headertext
x-api-key: YOUR_API_KEYThe API key must be a valid UUID. Only CLIENT role accounts can use API keys.
POST
/api/payment/initCreate a new payment
Finds a matching Relay provider from the pool and creates a payment intent.
Auth: x-api-key header (CLIENT role only)
{
"amount": 50.00,
"maxFee": 10,
"currency": "usd",
"externalId": "order-123",
"suffixClient": "SHOP",
"theme": "light"
}Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Payment amount (2-1000, max 2 decimal places) |
maxFee | number | Yes | Maximum fee percentage you're willing to pay (max 2 decimal places). Must exceed any configured referral fees |
currency | string | Yes | Currency code (currently only usd) |
externalId | string | Yes | Your order reference (max 36 chars, alphanumeric + -_) |
suffixClient | string | No | 4 Latin letters appended to statement descriptor |
theme | string | No | Payment form theme: light (default) or dark |
Response Fields
| Field | Description |
|---|---|
id | Payment UUID — use this to track the payment |
status | Payment status (PENDING on creation) |
type | Always PAYMENT |
providerId | Matched provider UUID |
relayId | Relay user UUID |
clientId | Your user UUID |
relayPage | URL to embed in an iframe for the customer to pay |
externalId | Your order reference (echoed back) |
suffix | Relay's statement descriptor suffix |
suffixClient | Your statement descriptor suffix (if provided) |
amount | Payment amount (echoed back) |
currency | Currency code (echoed back) |
fee | Actual fee percentage charged |
maxFee | Your max fee (echoed back) |
send | Amount you'll receive after fees (in major units) |
relayWallet | Relay's Solana wallet address |
clientWallet | Your Solana wallet address |
createdAt | Payment creation timestamp (ISO 8601) |
theme | Payment form theme (echoed back) |
exchangeRoute | Cross-currency conversion details (if applicable) |
Provider Matching
The system tries up to 3 providers before giving up. If a provider fails (invalid key, insufficient balance, etc.), the next available provider is tried automatically.
Diagnosis Reasons
When no provider is available, the reason field explains why:
| Reason | Description |
|---|---|
no_providers_in_pool | No providers are currently in the pool |
currency_mismatch | No providers support the requested currency |
fee_too_low | Your maxFee is lower than any provider's fee |
amount_out_of_range | Payment amount is outside all providers' min/max range |
insufficient_relay_balance | Matching providers don't have enough balance |
rejected_by_all_relays | All matching providers have rejected your client ID |
Webhook Notifications
If you've configured a webhook URL, surstrom sends POST requests when payment status changes:
Webhook payload (header: X-Payment-Event: payment.event)json
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "PAYMENT",
"status": "COMPLETED",
"amount": 50.00,
"currency": "usd",
"fee": 7,
"send": 46.50,
"externalId": "order-123",
"clientId": "your-user-uuid",
"relayId": "relay-user-uuid",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:35:00.000Z",
"sent": "TRUE",
"txHash": "SolanaTxSignature...",
"theme": "light"
}