Payout API
Skicka uttag från ditt handlarsaldo till valfri blockchain-adress.
Payout API:et låter dig programmatiskt göra uttag från ditt handlarsaldo till valfri blockchain-adress.
För alla Payout-endpoints måste du använda en separat Payout API key för att generera signaturen sign. Den här nyckeln skiljer sig från din vanliga API key och måste genereras i dina projektinställningar.
Skapa uttag
Skapar en uttagsbegäran från ditt handlarsaldo.
/v1/payoutParametrar i förfrågan
| Fält | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
currency | string | ja | Uttagsvaluta (se References) |
network | string | ja | Nätverkskod (se References) |
amount | string | ja | Uttagsbelopp |
to_address | string | ja | Mottagarens blockchain-adress |
order_id | string | nej | Idempotensnyckel — unik inom ett projekt. En upprepad POST med samma order_id skapar inte ett nytt uttag — den befintliga returneras istället |
url_callback | string | nej | URL för uttags-webhooks. Utelämna för att inaktivera webhooks för det här uttaget |
memo | string | null | nej | Destinationstagg / memo. Används för närvarande endast av nätverken TON och SOL; max 255 tecken |
from_currency | string | nej | Källsaldo att debitera och automatiskt konvertera till currency vid tidpunkten för uttaget. Låter dig betala ut i volatila tillgångar (BTC, ETH, …) samtidigt som ditt saldo hålls i en stablecoin som USDT — du behöver inte själv hålla den volatila kryptovalutan. Skicka "USDT" för att debitera USDT-saldot |
fee_option | string | nej | Hur avgifter debiteras. deduct (standard) — nätverks- + plattformsavgifter dras från amount, mottagaren får amount - fees. add — avgifter läggs ovanpå, handlaren debiteras amount + fees och mottagaren får exakt amount |
Idempotens. Inom ett projekt är ett uttag unikt baserat på order_id. Att skicka samma POST med samma order_id igen är säkert — API:et returnerar det befintliga uttaget istället för att skapa en dubblett. Skicka alltid med ett order_id för uttag i produktion.
Exempel på förfrågan
curl -X POST https://api.2328.io/api/v1/payout \
-H "Content-Type: application/json" \
-H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
-H "project: YOUR_PROJECT_UUID" \
-H "sign: YOUR_HMAC_SIGNATURE" \
-d '{"currency":"TRX","network":"TRX-TRC20","amount":"1.00","to_address":"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t","order_id":"9ed25264-8be4-439f-acf5-2a8732538d27","url_callback":"https://your-site.com/webhook/payout","memo":null,"fee_option":"deduct"}'<?php
function apiSign(string $body, string $apiKey): string {
return hash_hmac('sha256', base64_encode($body), $apiKey);
}
$project = 'YOUR_PROJECT_UUID';
$apiKey = 'YOUR_PAYOUT_API_KEY';
$data = [
'currency' => 'TRX',
'network' => 'TRX-TRC20',
'amount' => '1.00',
'to_address' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
'order_id' => '9ed25264-8be4-439f-acf5-2a8732538d27',
'url_callback' => 'https://your-site.com/webhook/payout',
'memo' => null,
'fee_option' => 'deduct',
];
$body = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$sign = apiSign($body, $apiKey);
$ch = curl_init('https://api.2328.io/api/v1/payout');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'User-Agent: MyShop/1.0 (+https://myshop.example)',
"project: $project",
"sign: $sign",
],
]);
$response = json_decode(curl_exec($ch), true);import { createHmac } from "crypto";
function apiSign(body, apiKey) {
const base64 = Buffer.from(body, "utf8").toString("base64");
return createHmac("sha256", apiKey).update(base64).digest("hex");
}
const PROJECT_UUID = "YOUR_PROJECT_UUID";
const PAYOUT_API_KEY = process.env.PAYOUT_API_KEY;
const data = {
currency: "TRX",
network: "TRX-TRC20",
amount: "1.00",
to_address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
order_id: "9ed25264-8be4-439f-acf5-2a8732538d27",
url_callback: "https://your-site.com/webhook/payout",
memo: null,
fee_option: "deduct",
};
const body = JSON.stringify(data);
const sign = apiSign(body, PAYOUT_API_KEY);
const res = await fetch("https://api.2328.io/api/v1/payout", {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": "MyShop/1.0 (+https://myshop.example)",
project: PROJECT_UUID,
sign,
},
body,
});
const json = await res.json();import json
import hmac
import hashlib
import base64
import httpx
def api_sign(body: str, api_key: str) -> str:
b64 = base64.b64encode(body.encode("utf-8")).decode()
return hmac.new(api_key.encode(), b64.encode(), hashlib.sha256).hexdigest()
PROJECT_UUID = "YOUR_PROJECT_UUID"
PAYOUT_API_KEY = "YOUR_PAYOUT_API_KEY"
data = {
"currency": "TRX",
"network": "TRX-TRC20",
"amount": "1.00",
"to_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"order_id": "9ed25264-8be4-439f-acf5-2a8732538d27",
"url_callback": "https://your-site.com/webhook/payout",
"memo": None,
"fee_option": "deduct",
}
body = json.dumps(data, separators=(",", ":"), ensure_ascii=False)
sign = api_sign(body, PAYOUT_API_KEY)
r = httpx.post(
"https://api.2328.io/api/v1/payout",
headers={
"Content-Type": "application/json",
"User-Agent": "MyShop/1.0 (+https://myshop.example)",
"project": PROJECT_UUID,
"sign": sign,
},
content=body.encode("utf-8"),
)
response = r.json()package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"net/http"
)
func apiSign(body []byte, apiKey string) string {
b64 := base64.StdEncoding.EncodeToString(body)
h := hmac.New(sha256.New, []byte(apiKey))
h.Write([]byte(b64))
return hex.EncodeToString(h.Sum(nil))
}
func marshalCanonical(v any) ([]byte, error) {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(false)
if err := enc.Encode(v); err != nil {
return nil, err
}
return bytes.TrimRight(buf.Bytes(), "\n"), nil
}
type CreatePayout struct {
Currency string `json:"currency"`
Network string `json:"network"`
Amount string `json:"amount"`
ToAddress string `json:"to_address"`
OrderID string `json:"order_id"`
URLCallback string `json:"url_callback"`
Memo *string `json:"memo"`
FeeOption string `json:"fee_option"`
}
func main() {
const projectUUID = "YOUR_PROJECT_UUID"
const payoutAPIKey = "YOUR_PAYOUT_API_KEY"
data := CreatePayout{
Currency: "TRX",
Network: "TRX-TRC20",
Amount: "1.00",
ToAddress: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
OrderID: "9ed25264-8be4-439f-acf5-2a8732538d27",
URLCallback: "https://your-site.com/webhook/payout",
Memo: nil,
FeeOption: "deduct",
}
body, err := marshalCanonical(data)
if err != nil {
panic(err)
}
sign := apiSign(body, payoutAPIKey)
req, _ := http.NewRequest("POST",
"https://api.2328.io/api/v1/payout",
bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "MyShop/1.0 (+https://myshop.example)")
req.Header.Set("project", projectUUID)
req.Header.Set("sign", sign)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}Exempel på svar
{
"state": 0,
"result": {
"uuid": "019dea62-1727-72aa-ac2c-eaf2ade193ef",
"order_id": "9ed25264-8be4-439f-acf5-2a8732538d27",
"status": "pending",
"currency": "TRX",
"network": "TRX-TRC20",
"amount": "1.00",
"merchant_amount": "1",
"network_amount": "0.89",
"amount_usd": "0.33",
"to_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"memo": null,
"txid": null,
"block_number": null,
"error_type": null,
"created_at": "2026-05-02T23:29:50+03:00",
"updated_at": "2026-05-02T23:29:50+03:00"
}
}Avgifter. Standard fee_option: deduct — nätverks- + plattformsavgifter dras från amount (mottagaren får amount - fees). Skicka fee_option: add för att lägga avgifter ovanpå — mottagaren får exakt amount och handlaren debiteras amount + fees.
Beräkna uttag
Uppskattar uttagsbelopp och avgifter utan att skapa ett uttag eller debitera ditt saldo. Använd för att visa användaren det exakta beloppet de kommer att få (eller betala) innan de bekräftar.
/v1/payout/calcParametrar i förfrågan
Identiska med Skapa uttag — samma fält, samma signering. order_id, url_callback, to_address och memo accepteras men ignoreras: inget uttag sparas och inga callbacks skickas.
Exempel på förfrågan
curl -X POST https://api.2328.io/api/v1/payout/calc \
-H "Content-Type: application/json" \
-H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
-H "project: YOUR_PROJECT_UUID" \
-H "sign: YOUR_HMAC_SIGNATURE" \
-d '{"currency":"USDT","network":"TRX-TRC20","amount":"100","fee_option":"add"}'Exempel på svar
{
"state": 0,
"result": {
"currency": "USDT",
"network": "TRX-TRC20",
"amount": "100",
"fee_option": "add",
"merchant_amount": "103.00000000",
"network_amount": "100",
"total_fee": "3.00000000",
"total_fee_usd": "3.00000000"
}
}Endast förhandsvisning. Den här endpointen är skrivskyddad — inget saldo debiteras och ingen uttagspost skapas. Anropa så ofta du behöver för att visa avgiftsfördelning i din UI.
Uttagsstatus
Hämta status för en uttagsbegäran.
/v1/payout/status/{uuid}Sökvägsparametrar
| Fält | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
uuid | string | ja | Uttagets UUID (från result.uuid vid skapandet) |
Exempel på svar
{
"state": 0,
"result": {
"uuid": "019dff1f-0dbd-7277-8d45-271e7775388f",
"order_id": "4dfdcc84402b1185b71cbe399321533e",
"status": "completed",
"currency": "TRX",
"network": "TRX-TRC20",
"amount": "3.00",
"merchant_amount": "3.00",
"network_amount": "3.00",
"amount_usd": "1.04",
"to_address": "THauRv5tcucQRohXg8NiyGTk16DX1XQG5x",
"memo": null,
"txid": "9242e533703704ef3eaba840f70b4a26333e72c943377ee375fea17badb53def",
"block_number": null,
"error_type": null,
"created_at": "2026-05-07T00:08:38+03:00",
"updated_at": "2026-05-07T00:08:54+03:00",
"from_currency": "USDT",
"debited_amount": "1.050735",
"debited_currency": "USDT"
}
}För den här GET-förfrågan beräknas signaturen från en tom body:
hash_hmac('sha256', base64_encode(''), $apiKey)
Svarsfält
Fält som returneras i result från POST /v1/payout och GET /v1/payout/status/{uuid}:
| Fält | Typ | Beskrivning |
|---|---|---|
uuid | string | UUID för uttaget som tilldelats av systemet |
order_id | string | Din interna identifierare för uttaget (unik inom projektet) |
status | string | Aktuell uttagsstatus (se nedan) |
currency | string | Uttagsvaluta |
network | string | Nätverkskod |
amount | string | Uttagsbelopp enligt begäran |
merchant_amount | string | Belopp som debiterats från handlarsaldot |
network_amount | string | Belopp som faktiskt skickats on-chain (efter nätverks- + plattformsavgifter) |
amount_usd | string | USD-motsvarighet av uttagsbeloppet |
to_address | string | Mottagarens blockchain-adress |
memo | string | null | Destinationstagg / memo (TON, SOL). null annars |
txid | string | null | Blockchain-transaktionshash. null tills transaktionen skickats |
block_number | int | null | Blocknummer där transaktionen inkluderats. null tills den inkluderats |
error_type | string | null | Orsak till fel när status = failed (se Feltyper nedan). null annars |
created_at | string (ISO 8601) | Tidpunkt då uttaget skapades |
updated_at | string (ISO 8601) | Tidpunkt för senaste statusändring |
from_currency | string | null | Källsaldo som uttaget debiterades från när automatisk konvertering användes (t.ex. USDT för ett uttag i BTC). null om ingen konvertering skedde |
debited_amount | string | null | Belopp som faktiskt debiterats från källsaldot efter konvertering. Finns endast vid automatisk konvertering |
debited_currency | string | null | Valuta för debited_amount — saldot som medlen debiterades från |
Uttagsstatusar
Fältet status kan ha följande värden:
| Status | Beskrivning |
|---|---|
pending | Skapat, väntar på bearbetning |
completed | Slutfört framgångsrikt — txid är satt |
failed | Sändningsfel — se error_type |
cancelled | Avbrutet |
Feltyper
När status = failed beskriver fältet error_type orsaken:
| Kod | Beskrivning |
|---|---|
aml_risk | Uttag blockerat av AML-riskkontroller (mottagaradressen flaggades som högrisk) |
Webhook-notifieringar
När ett uttags status ändras skickar systemet en POST webhook till den url_callback-URL som angavs när uttaget skapades. Om url_callback inte angavs skickas inga webhooks för det uttaget.
- Metod:
POST - Content-Type:
application/json - Signatur: fältet
signi body, beräknat med Payout API key (samma nyckel som används för att signera uttagsförfrågningar).
Payloaden speglar result-objektet från GET /v1/payout/status/{uuid} plus ett sign-fält för verifiering.
Payload
{
"uuid": "019dff1f-0dbd-7277-8d45-271e7775388f",
"order_id": "4dfdcc84402b1185b71cbe399321533e",
"status": "completed",
"currency": "TRX",
"network": "TRX-TRC20",
"amount": "3.00",
"merchant_amount": "3.00",
"network_amount": "3.00",
"amount_usd": "1.04",
"to_address": "THauRv5tcucQRohXg8NiyGTk16DX1XQG5x",
"memo": null,
"txid": "9242e533703704ef3eaba840f70b4a26333e72c943377ee375fea17badb53def",
"block_number": null,
"error_type": null,
"created_at": "2026-05-07T00:08:38+03:00",
"updated_at": "2026-05-07T00:08:54+03:00",
"from_currency": "USDT",
"debited_amount": "1.050735",
"debited_currency": "USDT",
"sign": "925ad7bf3d6841864101f7cc2c7e30652e70a06cdb04dbe07a0129480000ce4a"
}Verifiera signaturen. Använd samma algoritm som för betalnings-webhooks, men signera med din Payout API key istället för den vanliga API-nyckeln. Ta bort fältet sign, JSON-koda den återstående payloaden, Base64-koda den, beräkna sedan hash_hmac('sha256', $base64, $payoutApiKey) och jämför med den mottagna sign.