Payout API
Kirim penarikan dari saldo merchant Anda ke alamat blockchain mana pun.
Payout API memungkinkan Anda menarik dana secara terprogram dari saldo merchant Anda ke alamat blockchain mana pun.
Untuk semua endpoint Payout, Anda harus menggunakan Payout API key terpisah untuk menghasilkan tanda tangan sign. Key ini berbeda dari API key biasa Anda dan harus dibuat di pengaturan project.
Buat penarikan
Membuat permintaan penarikan dari saldo merchant Anda.
/v1/payoutParameter permintaan
| Field | Tipe | Wajib | Deskripsi |
|---|---|---|---|
currency | string | ya | Mata uang penarikan (lihat References) |
network | string | ya | Kode jaringan (lihat References) |
amount | string | ya | Jumlah penarikan |
to_address | string | ya | Alamat blockchain penerima |
order_id | string | tidak | Kunci idempotensi — unik dalam satu project. POST berulang dengan order_id yang sama tidak akan membuat penarikan baru — yang sudah ada akan dikembalikan |
url_callback | string | tidak | URL untuk webhook penarikan. Hilangkan untuk menonaktifkan webhook untuk penarikan ini |
memo | string | null | tidak | Destination tag / memo. Saat ini hanya digunakan oleh jaringan TON dan SOL; maks 255 karakter |
from_currency | string | tidak | Saldo sumber yang akan didebet dan dikonversi otomatis ke currency pada saat penarikan. Memungkinkan Anda menarik dalam aset volatil (BTC, ETH, …) sambil menyimpan saldo dalam stablecoin seperti USDT — Anda tidak perlu memegang sendiri kripto volatil tersebut. Berikan "USDT" untuk mendebet saldo USDT |
fee_option | string | tidak | Cara biaya dibebankan. deduct (default) — biaya jaringan + platform dikurangi dari amount, penerima mendapat amount - fees. add — biaya ditambahkan di atas, merchant didebit amount + fees, penerima menerima tepat amount |
Idempotensi. Dalam satu project, sebuah penarikan unik berdasarkan order_id. Mengirim ulang POST yang sama dengan order_id yang sama aman — API mengembalikan penarikan yang sudah ada alih-alih membuat duplikat. Selalu berikan order_id untuk penarikan produksi.
Contoh permintaan
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()
}Contoh respon
{
"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"
}
}Biaya. Default fee_option: deduct — biaya jaringan + platform dikurangi dari amount (penerima mendapat amount - fees). Berikan fee_option: add untuk membebankan biaya di atas — penerima mendapat tepat amount dan merchant didebit amount + fees.
Hitung penarikan
Menghitung estimasi jumlah dan biaya penarikan tanpa membuat penarikan dan tanpa mendebit saldo Anda. Gunakan untuk menampilkan kepada pengguna jumlah tepat yang akan diterima (atau dibayar) sebelum konfirmasi.
/v1/payout/calcParameter permintaan
Identik dengan Buat penarikan — field yang sama, penandatanganan yang sama. order_id, url_callback, to_address, dan memo diterima namun diabaikan: tidak ada penarikan yang disimpan dan tidak ada callback yang dikirim.
Contoh permintaan
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"}'Contoh respon
{
"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"
}
}Hanya pratinjau. Endpoint ini hanya-baca — tidak ada saldo yang didebit dan tidak ada rekam penarikan yang dibuat. Panggil sesering yang Anda perlukan untuk merender rincian biaya di UI Anda.
Status penarikan
Dapatkan status sebuah permintaan penarikan.
/v1/payout/status/{uuid}Parameter path
| Field | Tipe | Wajib | Deskripsi |
|---|---|---|---|
uuid | string | ya | UUID penarikan (dari result.uuid saat pembuatan) |
Contoh respon
{
"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"
}
}Untuk permintaan GET ini, tanda tangan dihitung dari body kosong:
hash_hmac('sha256', base64_encode(''), $apiKey)
Field respon
Field yang dikembalikan di result dari POST /v1/payout dan GET /v1/payout/status/{uuid}:
| Field | Tipe | Deskripsi |
|---|---|---|
uuid | string | UUID penarikan yang ditetapkan oleh sistem |
order_id | string | Pengenal penarikan internal Anda (unik dalam project) |
status | string | Status penarikan saat ini (lihat di bawah) |
currency | string | Mata uang penarikan |
network | string | Kode jaringan |
amount | string | Jumlah penarikan sesuai permintaan |
merchant_amount | string | Jumlah yang didebit dari saldo merchant |
network_amount | string | Jumlah yang sebenarnya dikirim on-chain (setelah biaya jaringan + platform) |
amount_usd | string | Ekuivalen USD dari jumlah penarikan |
to_address | string | Alamat blockchain penerima |
memo | string | null | Destination tag / memo (TON, SOL). null jika tidak ada |
txid | string | null | Hash transaksi blockchain. null hingga transaksi dikirim |
block_number | int | null | Nomor blok tempat transaksi disertakan. null hingga disertakan |
error_type | string | null | Alasan kegagalan saat status = failed (lihat Tipe error di bawah). null jika tidak ada |
created_at | string (ISO 8601) | Waktu pembuatan penarikan |
updated_at | string (ISO 8601) | Waktu perubahan status terakhir |
from_currency | string | null | Saldo sumber yang didebet untuk penarikan ketika konversi otomatis digunakan (mis. USDT untuk penarikan BTC). null jika tidak ada konversi |
debited_amount | string | null | Jumlah yang sebenarnya didebit dari saldo sumber setelah konversi. Hadir hanya saat konversi otomatis digunakan |
debited_currency | string | null | Mata uang dari debited_amount — saldo dari mana dana didebit |
Status penarikan
Field status dapat berisi nilai berikut:
| Status | Deskripsi |
|---|---|
pending | Dibuat, menunggu pemrosesan |
completed | Selesai dengan sukses — txid telah ditetapkan |
failed | Kesalahan pengiriman — lihat error_type |
cancelled | Dibatalkan |
Tipe error
Saat status = failed, field error_type menjelaskan alasannya:
| Kode | Deskripsi |
|---|---|
aml_risk | Penarikan diblokir oleh pemeriksaan risiko AML (alamat penerima ditandai berisiko tinggi) |
Notifikasi webhook
Saat status sebuah penarikan berubah, sistem mengirim webhook POST ke URL url_callback yang diteruskan saat penarikan dibuat. Jika url_callback tidak disediakan, tidak ada webhook yang dikirim untuk penarikan tersebut.
- Method:
POST - Content-Type:
application/json - Tanda tangan: field
signdi body permintaan, dihitung dengan Payout API key (key yang sama digunakan untuk menandatangani permintaan penarikan).
Payload mencerminkan objek result dari GET /v1/payout/status/{uuid} plus field sign untuk verifikasi.
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"
}Memverifikasi tanda tangan. Gunakan algoritma yang sama seperti untuk webhook pembayaran, tetapi tandatangani dengan Payout API key Anda alih-alih API key biasa. Hapus field sign, encode payload tersisa ke JSON, encode ke Base64, lalu hitung hash_hmac('sha256', $base64, $payoutApiKey) dan bandingkan dengan sign yang diterima.