快速上手
- 01
取得 API 金鑰
註冊並登入後台,在「開發者 → API 金鑰」建立金鑰,再到「IPN」設定 IPN 密鑰。建議先在測試環境完成串接。
- 02
建立付款
呼叫
POST /v1/payment,帶入金額、計價幣別與收款幣種。回應中的pay_address是這筆付款的專屬地址,pay_amount是應付金額。POST /v1/paymentcurlcurl -X POST "https://omniwallet.ewin888.com/api/v1/payment" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-A-1024" \ -d '{ "price_amount": 120, "price_currency": "usd", "pay_currency": "usdttrc20", "order_id": "A-1024", "order_description": "Pro plan, 12 months", "ipn_callback_url": "https://example.com/ipn" }'回應JSON{ "payment_id": 355446374401130, "payment_status": "waiting", "pay_address": "TJ8nq3M5yVfQ9C2dWkPz7aH4sLrE6uXbGt", "price_amount": 120, "price_currency": "usd", "pay_amount": 120.02, "pay_currency": "usdttrc20", "order_id": "A-1024", "network": "TRC20", "expiration_estimate_date": "2026-10-02T09:15:00.000Z", "omni_payment_url": "https://omniwallet.ewin888.com/payment/4fKq9ZtR2mXcL8vBnW1pYs" } - 03
引導客人付款
把地址與金額(或 QR)顯示給客人;也可以改用
POST /v1/invoice建立託管發票頁,再把客人導向回應中的invoice_url。POST /v1/invoicecurlcurl -X POST "https://omniwallet.ewin888.com/api/v1/invoice" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "price_amount": 49.9, "price_currency": "usd", "order_id": "A-1025", "order_description": "Starter plan", "ipn_callback_url": "https://example.com/ipn", "success_url": "https://example.com/thanks", "cancel_url": "https://example.com/cart" }'回應JSON{ "id": "355447603200201", "order_id": "A-1025", "price_amount": "49.9", "price_currency": "usd", "pay_currency": null, "invoice_url": "https://omniwallet.ewin888.com/invoice/8hTz2QpLm4XwN7cVb1RkYe", "success_url": "https://example.com/thanks", "cancel_url": "https://example.com/cart", "created_at": "2026-10-01T09:20:00.000Z" } - 05
切換到正式環境
商戶審核通過後,在正式環境建立新的 API 金鑰與 IPN 密鑰,並把基本網址換成正式環境。
驗證 IPN 簽章
我們以您的 IPN 密鑰,對「鍵依字母排序後的 JSON」計算 HMAC-SHA512,並以十六進位字串放在 x-omni-sig 標頭。我們送出的請求本文就是這段排序後的 JSON,所以直接對收到的原始本文驗證即可。
- 以常數時間比較簽章
- IPN 可能重複或不依順序送達:以
payment_id與狀態去重,必要時呼叫GET /v1/payment/{id}取得最新狀態 - 回應任何 2xx 代表已收到;其他回應或逾時都會自動重試
ExpressNode.js
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const IPN_SECRET = process.env.OMNI_IPN_SECRET;
// Keep the raw body: the signature covers the exact bytes we send.
app.post('/ipn', express.raw({ type: 'application/json' }), (req, res) => {
const received = String(req.get('x-omni-sig') || '');
const expected = crypto
.createHmac('sha512', IPN_SECRET)
.update(req.body)
.digest('hex');
const valid = received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return res.status(401).send('invalid signature');
const payment = JSON.parse(req.body.toString('utf8'));
// Idempotent update: the same status can be delivered more than once.
// await orders.applyStatus(payment.order_id, payment.payment_status);
res.sendStatus(200);
});
app.listen(3000);PHPPHP
<?php
$secret = getenv('OMNI_IPN_SECRET');
$raw = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_OMNI_SIG'] ?? '';
// The signature covers the exact bytes we send.
$expected = hash_hmac('sha512', $raw, $secret);
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit('invalid signature');
}
$payment = json_decode($raw, true);
// Idempotent update: the same status can be delivered more than once.
// apply_status($payment['order_id'], $payment['payment_status']);
http_response_code(200);FlaskPython
import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
IPN_SECRET = os.environ["OMNI_IPN_SECRET"].encode()
@app.post("/ipn")
def ipn():
raw = request.get_data() # the exact bytes we signed
expected = hmac.new(IPN_SECRET, raw, hashlib.sha512).hexdigest()
received = request.headers.get("x-omni-sig", "")
if not hmac.compare_digest(expected, received):
abort(401)
payment = json.loads(raw)
# Idempotent update: the same status can be delivered more than once.
# apply_status(payment["order_id"], payment["payment_status"])
return "", 200IPN 本文範例JSON
{
"actually_paid": 120.02,
"actually_paid_at_fiat": 120,
"invoice_id": null,
"order_description": "Pro plan, 12 months",
"order_id": "A-1024",
"outcome_amount": 119.42,
"outcome_currency": "usdttrc20",
"parent_payment_id": null,
"pay_address": "TJ8nq3M5yVfQ9C2dWkPz7aH4sLrE6uXbGt",
"pay_amount": 120.02,
"pay_currency": "usdttrc20",
"payment_id": 355446374401130,
"payment_status": "finished",
"price_amount": 120,
"price_currency": "usd",
"purchase_id": null,
"updated_at": "2026-10-01T09:17:42.000Z"
}付款狀態
狀態值與 NOWPayments 相同。
| 狀態 | 說明 |
|---|---|
waiting等待付款 | 等待客人付款 |
confirming確認中 | 已偵測到交易,正在等待區塊確認 |
confirmed已確認 | 已確認入帳 |
sending結算中 | 正在結算到您的錢包 |
partially_paid部分付款 | 付款不足,客人可以在到期前補足 |
finished已完成 | 付款完成 |
failed失敗 | 付款失敗,例如命中制裁名單 |
refunded已退款 | 已退款給付款人 |
expired已過期 | 逾期未付款 |
從 NOWPayments 遷移
API 相容,大部分程式碼都不需要修改。
- 換掉基本網址把
https://api.nowpayments.io/v1/換成下方的 OmniWallet 網址。 - 換成 OmniWallet 的 API 金鑰標頭名稱一樣是
x-api-key。 - 更新 IPN 驗證改用 OmniWallet 的 IPN 密鑰與
x-omni-sig標頭。若希望現有程式完全不改,可以在後台開啟x-nowpayments-sig相容標頭。 - 確認支援的幣種幣種代碼相同(例如
usdttrc20、usdcsol),但只能使用我們支援的幣種,請以GET /v1/merchant/coins或支援幣種頁面確認。 - 檢查出款設定出款同樣以
POST /v1/auth取得 5 分鐘有效的 JWT;此外,具出款權限的 API 金鑰必須設定 IP 白名單,收款地址也要先加入白名單。
API 基本網址diff
- https://api.nowpayments.io/v1/
+ https://omniwallet.ewin888.com/api/v1/維持不變
- 端點路徑與 HTTP 方法
- 請求與回應的欄位名稱
- 付款與出款的狀態值
- 幣種代碼
- IPN 簽章演算法
需要注意的差異
- IPN 簽章標頭為
x-omni-sig(可以選擇同時送出x-nowpayments-sig) - OmniWallet 的擴充欄位以
omni_開頭 - 不支援法幣出款(回應 HTTP 501)
- 結算一律付到後台綁定的錢包
- 測試環境使用真實的區塊鏈測試網
測試環境(Sandbox)
測試環境是獨立的一套部署,連接各鏈的測試網,API 與正式環境完全相同。
- 帳號、API 金鑰與資料都和正式環境分開,需要另外註冊;商戶審核會自動通過
- 使用各測試網的測試幣付款,交易可以在測試網的區塊瀏覽器查到
- 測試幣沒有實際價值,請勿把主網資產轉到測試環境的地址
| 網路 | 測試網 | 可用幣種代碼 |
|---|---|---|
| Ethereum | Ethereum Sepolia | eth usdc |
| BNB Smart Chain | BNB Smart Chain Testnet | bnbbsc usdtbsc |
| Polygon | Polygon Amoy | maticmainnet usdcmatic |
| Avalanche C-Chain | Avalanche Fuji | avax usdcavax |
| Arbitrum | Arbitrum Sepolia | etharb usdcarb |
| Base | Base Sepolia | ethbase usdcbase |
| Optimism | OP Sepolia | ethop usdcop |
| TRON | TRON Nile | trx usdttrc20 |
| Solana | Solana Devnet | sol usdcsol |
| Bitcoin | Bitcoin Testnet4 | btc |
冪等與重試
所有建立資源的 POST 請求都可以帶 Idempotency-Key 標頭。遇到網路逾時,用同一個金鑰重送,不會重複建立付款或出款。
- 同一個金鑰、相同內容:回傳第一次的結果
- 同一個金鑰、內容不同:回應 422
IDEMPOTENCY_KEY_REUSED - 第一次請求仍在處理:回應 409
IDEMPOTENCY_IN_FLIGHT,請稍後重試
HTTPHTTP
POST /api/v1/payout HTTP/1.1
Host: omniwallet.ewin888.com
x-api-key: YOUR_API_KEY
Authorization: Bearer YOUR_JWT
Idempotency-Key: 6c0a8e6e-3f5b-4f7a-9a52-1f0c2d9b7e41
Content-Type: application/json