API kompatibel NOWPayments
Path, header x-api-key, field permintaan dan respons, status, serta tanda tangan IPN x-nowpayments-sig mengikuti dokumentasi NOWPayments, sehingga kode yang ada biasanya hanya perlu base URL, API key, dan IPN secret baru. Datang dari tempat lain? Lihat ringkasan migrasi.
Di baliknya adalah tagihan blockwallets biasa: alamat penerima milik Anda, halaman pembayaran adalah kasir yang kami host, dan pembayaran baru dihitung setelah terkonfirmasi di chain dan diperiksa silang dengan node kedua. Di bawah ini: tiga langkah, lalu apa yang didukung setiap API dan apa bedanya dengan aslinya — parameter yang tidak kami implementasikan mengembalikan error, bukan diabaikan diam-diam.
Tiga langkah
- Dapatkan akun merchant (ajukan di beranda) dan tambahkan domain Anda di “Domain situs web” pada “Developer › Integrasi & kunci” di dasbor. URL callback (
ipn_callback_url) dan URL pengalihan (success_url/cancel_url/redirect_url) harus berada di domain itu atau di domain URL callback Anda — kunci yang bocor tidak bisa mengarahkan webhook ke tempat lain. - Buat kredensial kompatibel di dasbor merchant pada “Developer › Integrasi & kunci”, satu set untuk live dan satu untuk uji: NOWPayments mendapat API key + IPN secret, Coinbase Commerce mendapat API key + webhook shared secret. Semuanya ditampilkan sekali; kami hanya menyimpan hash / ciphertext.
- Arahkan kode Anda ke base URL di bawah dan pakai kunci baru. Jalankan dulu di testnet dengan kredensial uji, lalu beralih ke live.
| Sebelum | Sesudah | Header autentikasi | Tanda tangan webhook |
|---|---|---|---|
https://api.nowpayments.io/v1 | https://blockwallets.io/nowpayments/v1 | x-api-key | x-nowpayments-sig (HMAC-SHA512) |
NOWPayments
- const NP = "https://api.nowpayments.io/v1";
+ const NP = "https://blockwallets.io/nowpayments/v1";
const res = await fetch(`${NP}/payment`, {
method: "POST",
headers: { "x-api-key": process.env.NOWPAYMENTS_API_KEY, "content-type": "application/json" },
body: JSON.stringify({
price_amount: 49.9, price_currency: "usd", pay_currency: "usdttrc20",
order_id: "A-1001", order_description: "Pro plan",
ipn_callback_url: "https://shop.example/ipn/nowpayments",
}),
});
const { payment_id, pay_address, pay_amount } = await res.json();Endpoint yang didukung
| Endpoint | Catatan |
|---|---|
GET /status | {"message":"OK"}, tanpa kunci |
GET /currencies · /merchant/coins · /full-currencies | Koin yang bisa Anda terima saat ini (ticker di bawah); fixed_rate diabaikan |
GET /min-amount · GET /estimate | Minimum dan estimasi; hanya harga USD (USDT / USDC dihitung 1:1) |
POST /payment | Membuat pembayaran; langsung mengembalikan pay_address / pay_amount (white-label) |
POST /invoice | Membuat invoice; invoice_url adalah kasir yang kami host tempat pelanggan memilih koin dan jaringan |
POST /invoice-payment | Memilih koin untuk sebuah invoice, mengembalikan pembayarannya |
GET /payment/:id | Status pembayaran |
GET /payment/ | Daftar: limit (1–500) / page / orderBy / dateFrom / dateTo / invoiceId; sortBy hanya created_at |
POST /auth | Daftar hanya butuh x-api-key, tanpa JWT; endpoint ini mengembalikan token placeholder yang tidak diperiksa agar kode “login lalu tampilkan daftar” tetap berjalan (email / password tidak diperiksa) |
Tidak tersedia: payout, konversi, sub-partner kustodian, langganan, update-merchant-estimate, pembayaran fiat. Path tersebut mengembalikan 404.
Callback IPN
Saat pembayaran mencapai status akhir, kami mengirim POST ke ipn_callback_url yang diberikan saat pembuatan, dengan x-nowpayments-sig = HMAC-SHA512(IPN secret, JSON yang diurutkan berdasarkan key secara rekursif) — persis kode verifikasi NOWPayments. Body yang kami kirim sudah berupa JSON ringkas yang terurut, jadi HMAC atas body mentah juga berfungsi (dan paling aman di PHP / Python, yang encode ulang desimal sangat kecil bisa berbeda dari JS). Callback yang gagal dicoba lagi setelah 1 mnt, 5 mnt, 30 mnt, … — total 8 percobaan.
import crypto from "node:crypto";
// Same as the NOWPayments docs (our body is already sorted, so hashing the raw body works too)
function sortObject(obj) {
return Object.keys(obj).sort().reduce((r, k) => {
r[k] = obj[k] && typeof obj[k] === "object" ? sortObject(obj[k]) : obj[k];
return r;
}, {});
}
function verifyIpn(params, sig, ipnSecret) {
const hmac = crypto.createHmac("sha512", ipnSecret).update(JSON.stringify(sortObject(params))).digest("hex");
return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(sig || ""));
}
// verifyIpn(req.body, req.headers["x-nowpayments-sig"], process.env.NOWPAYMENTS_IPN_SECRET)$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
tksort($data); // the recursive ksort from the NOWPayments docs
$hmac = hash_hmac('sha512', json_encode($data, JSON_UNESCAPED_SLASHES), trim($ipnSecret));
$ok = hash_equals($hmac, $_SERVER['HTTP_X_NOWPAYMENTS_SIG'] ?? '');| payment_status | Kapan | IPN yang dikirim |
|---|---|---|
waiting | Menunggu pembayaran (juga invoice yang belum dipilih koinnya) | — |
confirming | Pembayaran terlihat di chain, menunggu konfirmasi | — (terlihat saat Anda melakukan kueri) |
finished | Lunas (termasuk kelebihan bayar; actually_paid adalah jumlah yang masuk) | ✓ |
partially_paid | Kurang: pelanggan masih bisa menambah ke alamat yang sama sebelum kedaluwarsa (dikirim sekali saat pembayaran kurang pertama), dan lagi jika masih kurang saat kedaluwarsa | ✓ |
expired | Kedaluwarsa tanpa pembayaran | ✓ |
refunded | Anda me-refund seluruh pembayaran dari dasbor dan sudah terkirim | — (terlihat saat Anda melakukan kueri) |
confirmed, sending, dan failed tidak pernah muncul: setelah terkonfirmasi statusnya finished dan dikreditkan ke saldo Anda; tidak ada yang diteruskan.
Ticker mata uang
| Koin | Jaringan | pay_currency | network | Minimum |
|---|---|---|---|---|
| USDT | TRON | usdttrc20 | trx | $1 |
| USDT | BNB Chain | usdtbsc usdtbep20 | bsc | $1 |
| USDC | BNB Chain | usdcbsc usdcbep20 | bsc | $1 |
| USDT | Ethereum | usdterc20 | eth | $5 |
| USDC | Ethereum | usdc usdcerc20 | eth | $5 |
| USDC | Base | usdcbase | base | $1 |
| USDT | Polygon | usdtmatic usdtpolygon | matic | $1 |
| USDC | Polygon | usdcmatic usdcpolygon | matic | $1 |
| USDT | Arbitrum | usdtarb | arbitrum | $1 |
| USDC | Arbitrum | usdcarb | arbitrum | $1 |
| USDT | Optimism | usdtop | op | $1 |
| USDC | Optimism | usdcop | op | $1 |
| USDT | Avalanche | usdtarc20 usdtavax / usdtavaxc | avaxc | $1 |
| USDC | Avalanche | usdcarc20 usdcavax / usdcavaxc | avaxc | $1 |
| USDT | X Layer | usdtxlayer | xlayer | $1 |
| USDC | X Layer | usdcxlayer | xlayer | $1 |
| USD1 | BNB Chain | usd1bsc | bsc | $1 |
| FDUSD | BNB Chain | fdusdbsc | bsc | $1 |
| DAI | Ethereum | dai daierc20 | eth | $5 |
| DAI | Arbitrum | daiarb | arbitrum | $1 |
| DAI | Base | daibase | base | $1 |
| BTC | Bitcoin | btc | btc | $5 |
| LTC | Litecoin | ltc | ltc | $1 |
| DOGE | Dogecoin | doge | doge | $1 |
| BCH | Bitcoin Cash | bch | bch | $1 |
| TRX | TRON | trx | trx | $1 |
| BNB | BNB Chain | bnbbsc bnb | bsc | $1 |
| ETH | Ethereum | eth | eth | $5 |
| ETH | Base | ethbase | base | $1 |
| ETH | Arbitrum | etharb | arbitrum | $1 |
| ETH | Optimism | ethop | op | $1 |
| POL | Polygon | maticmainnet pol / polmainnet / matic | matic | $1 |
| AVAX | Avalanche | avaxc avax | avaxc | $1 |
| OKB | X Layer | okbxlayer | xlayer | $1 |
| USDT | Solana | usdtsol | sol | $1 |
| USDC | Solana | usdcsol | sol | $1 |
| SOL | Solana | sol | sol | $1 |
| USDT | TON | usdtton | ton | $1 |
| TON | TON | ton | ton | $1 |
Perbedaan dari NOWPayments
price_currencyhanyausd(plususdt/usdcpada 1:1); fiat lain mengembalikan 400.pay_amount(jumlah kripto tetap),payout_address/payout_currency/payout_extra_id, dancustomer_emailtidak didukung dan mengembalikan 400.is_fixed_ratediterima tetapi tidak berpengaruh: kami selalu mengunci kurs (BTC / LTC dll. pada median beberapa exchange, selama 30 menit). Jikais_fee_paid_by_usertrue, biaya platform ditambahkan kepay_amount(biaya platform 1%, makin rendah pada volume lebih tinggi; lihat harga).- Pembayaran berlaku hingga 12 jam (NOWPayments: 7 hari).
partially_paid_urldisimpan dan dikembalikan apa adanya, tetapi kasir tidak mengalihkan ke sana. payment_idadalah angka 10 digit; untuk invoice yang dibuat dengan/invoice,payment_idpembayarannya sama denganidinvoice, dan satu invoice punya tepat satu pembayaran (tanpa setoran ulangparent_payment_id).- Hanya IPN berstatus akhir dan
partially_paidyang dikirim (tabel di atas), tanpawaiting/confirming;fee.serviceFeeadalah biaya platform (jumlah yang diterima × tarif saat tagihan dibuat, 1% standar), biaya lainnya 0.
Mode uji
Charge dan pembayaran yang dibuat dengan kredensial uji berjalan di testnet tiap chain dan webhook-nya ditandatangani dengan IPN secret / shared secret uji — sepenuhnya terpisah dari live: kunci uji tidak bisa melihat pembayaran live.
Sisanya (kasir, konfirmasi, jaringan dan minimum, refund) bekerja seperti di dokumentasi utama.