Create a payment (Alsopay)
The recommended integration: one signed call creates an order, supporting multiple networks and tokens.
Before you start
- Create an API key pair in the console under API keys:
api_key_idandsecret_key - Configure your callback URL (
callback_url) and IP allow-list
Endpoint
POST https://api.alsoluna.xyz/payments/v1/orders
The exact address (with your environment's API domain) is shown in the console under Payment setup → Integration docs.
Signing
- Take every parameter except
signature - Sort the parameter names in ASCII order and join as
key1=value1&key2=value2... signature = hex(HMAC-SHA256(joined, secret_key)), lowercase
const crypto = require('crypto')
function sign(params, secretKey) {
const sorted = Object.keys(params)
.filter((k) => k !== 'signature')
.sort()
.map((k) => k + '=' + params[k])
.join('&')
return crypto.createHmac('sha256', secretKey).update(sorted).digest('hex')
}
const order = await fetch('https://api.alsoluna.xyz/payments/v1/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key_id: 'your api_key_id',
reference: 'MERCHANT_ORDER_001',
callback_url: 'https://merchant.example.com/notify',
return_url: 'https://merchant.example.com/return',
amount: '100.00',
token: 'usdt',
network: 'tron',
signature: sign({ ... }, secretKey),
}),
})
Request parameters
| Parameter | Required | Description |
|---|---|---|
api_key_id | Yes | API key id, created under API keys |
reference | Yes | Your order id (customer transaction id); must be unique |
callback_url | Yes | Asynchronous callback URL |
amount | Yes | Order amount (fiat) |
token | No | Token, e.g. usdt / trx / ton |
network | No | Network, e.g. tron / ton |
return_url | No | Redirect URL after payment |
payment_channel | No | Compatibility marker; part of the signature |
signature | Yes | HMAC-SHA256 signature (lowercase hex) |
Response fields
| Field | Description |
|---|---|
transaction_id | Platform transaction id (use it for status checks and reconciliation) |
amount_due | Amount due — transfer this exact amount (the platform matches by address and amount; a different amount is treated as unpaid) |
receiving_address | Receiving address |
token | Token (empty on status=4 placeholder orders) |
network | Network (empty on status=4 placeholder orders) |
status | Order status, see "Check payment status" below |
expires_at | Expiry time (unix seconds) |
checkout_url | Checkout URL (send the payer here by default) |
Two ways to get paid
Option 1: redirect to the hosted checkout (recommended)
Open the payer at the checkout_url from the response. The QR code, countdown, network/token switching and manual top-ups are all handled by the checkout page — you do not need to render a payment UI.
Option 2: build your own payment page
Render your own QR from receiving_address + amount_due and poll the status endpoint to detect payment (that endpoint needs no signature, so poll it from your server).
Caveats for a self-built page:
- The payer must transfer the exact amount; a different amount is never matched
- Pass
tokenandnetworkexplicitly when creating the order — otherwise you get astatus=4placeholder and must first call the switch endpoint to pick a network/token - If the browser polls instead of your server, add your site origin to the platform's cross-origin allow-list
Check payment status
GET https://api.alsoluna.xyz/gateway/check-status/{transaction_id}
No signature required.
| Value | Meaning |
|---|---|
1 | Waiting for payment |
2 | Paid (credited; a callback is sent at the same time) |
3 | Expired |
4 | Waiting for the payer to choose a network/token (placeholder order) |
Domain split
- Single-domain deployment: the endpoint above shares the domain with the checkout page — use it as is
- Split deployment: your server talks only to the API domain; the checkout page lives on the checkout domain and its address comes from
checkout_urlin the order response — use it directly, never build it yourself. The checkout page calls the status endpoint cross-origin, and the allow-list is configured by the platform - The checkout page domain does not serve
/payments/*merchant endpoints — never use the page address as the API address
