# API ที่เข้ากันได้กับ NOWPayments

> ย้ายจาก NOWPayments มา blockwallets ด้วยการเปลี่ยน base URL และคีย์: endpoint ที่รองรับ IPN callback และลายเซ็น สัญลักษณ์เหรียญ และข้อแตกต่าง
>
> HTML: https://blockwallets.io/docs/nowpayments?lang=th · English: https://blockwallets.io/docs/nowpayments.md?lang=en

## API ที่เข้ากันได้กับ NOWPayments

พาธ เฮดเดอร์ `x-api-key` ฟิลด์คำขอและการตอบกลับ สถานะ และลายเซ็น IPN `x-nowpayments-sig` เป็นไปตามเอกสารของ NOWPayments โค้ดเดิมจึงมักต้องการเพียง base URL, API key และ IPN secret ใหม่ ย้ายมาจากที่อื่น? ดู[ภาพรวมการย้าย](https://blockwallets.io/docs/migrate?lang=th)

ภายในเป็นใบแจ้งหนี้ blockwallets ปกติ: ที่อยู่รับเงินเป็นของคุณ หน้าชำระเงินคือหน้าชำระเงินที่เราโฮสต์ และการชำระจะนับก็ต่อเมื่อยืนยันบนเชนแล้วและตรวจไขว้กับโหนดที่สอง ต่อไปนี้: สามขั้นตอน จากนั้นสิ่งที่แต่ละ API รองรับและ**ต่างจากต้นฉบับอย่างไร** พารามิเตอร์ที่เราไม่ได้ทำจะตอบข้อผิดพลาด ไม่ถูกเมินไปเงียบ ๆ

### สามขั้นตอน

- สมัครบัญชีร้านค้า ([ขอใช้งานที่หน้าแรก](https://blockwallets.io/?lang=th#join)) และเพิ่มโดเมนของคุณที่ “โดเมนเว็บไซต์” ใน “นักพัฒนา › การเชื่อมต่อและคีย์” ของแดชบอร์ด URL callback (`ipn_callback_url`) และ URL เปลี่ยนเส้นทาง (`success_url` / `cancel_url` / `redirect_url`) ต้องอยู่ในโดเมนเหล่านั้นหรือโดเมนของ URL callback ของคุณ คีย์ที่รั่วไหลจึงชี้ Webhook ไปที่อื่นไม่ได้

- สร้างข้อมูลรับรองที่เข้ากันได้ในแดชบอร์ดร้านค้าที่ “นักพัฒนา › การเชื่อมต่อและคีย์” ชุดใช้งานจริงและชุดทดสอบอย่างละชุด: NOWPayments ได้ **API key + IPN secret** ส่ง Coinbase Commerce ได้ **API key + webhook shared secret** แสดงเพียงครั้งเดียว เราเก็บเฉพาะค่าแฮช / ข้อความเข้ารหัส

- ชี้โค้ดของคุณไปที่ base URL ด้านล่างและใช้คีย์ใหม่ รันบนเทสต์เน็ตด้วยข้อมูลรับรองทดสอบก่อน แล้วค่อยสลับเป็นใช้งานจริง

| ก่อน | หลัง | เฮดเดอร์การยืนยันตัวตน | ลายเซ็น Webhook |
| --- | --- | --- | --- |
| `https://api.nowpayments.io/v1` | `https://blockwallets.io/nowpayments/v1` | `x-api-key` | `x-nowpayments-sig` (HMAC-SHA512) |

### NOWPayments

```javascript
- 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 ที่รองรับ

| Endpoint | หมายเหตุ |
| --- | --- |
| `GET /status` | `{"message":"OK"}` ไม่ต้องใช้คีย์ |
| `GET /currencies` · `/merchant/coins` · `/full-currencies` | เหรียญที่รับได้ตอนนี้ (สัญลักษณ์ด้านล่าง) `fixed_rate` ถูกเมิน |
| `GET /min-amount` · `GET /estimate` | ยอดขั้นต่ำและการประเมิน กำหนดราคาเป็น USD เท่านั้น (USDT / USDC คิด 1:1) |
| `POST /payment` | สร้างการชำระ คืน `pay_address` / `pay_amount` ทันที (white-label) |
| `POST /invoice` | สร้างใบแจ้งหนี้ `invoice_url` คือหน้าชำระเงินที่เราโฮสต์ ลูกค้าเลือกเหรียญและเครือข่ายเอง |
| `POST /invoice-payment` | เลือกเหรียญสำหรับใบแจ้งหนี้ คืนการชำระ |
| `GET /payment/:id` | สถานะการชำระ |
| `GET /payment/` | รายการ: `limit` (1–500) / `page` / `orderBy` / `dateFrom` / `dateTo` / `invoiceId` ส่วน `sortBy` ใช้ได้เฉพาะ `created_at` |
| `POST /auth` | รายการต้องใช้เพียง `x-api-key` ไม่ต้องใช้ JWT endpoint นี้คืนโทเค็นตัวแทนที่ไม่ได้ตรวจสอบ เพื่อให้โค้ดแบบ “เข้าสู่ระบบก่อนแล้วค่อยดูรายการ” ยังทำงานได้ (ไม่ตรวจอีเมล / รหัสผ่าน) |

ไม่มี: การจ่ายออก การแปลงสกุล sub-partner แบบฝากเก็บ การสมัครสมาชิกแบบรายคาบ `update-merchant-estimate` การชำระด้วยเงินตรา เส้นทางเหล่านี้ตอบ 404

#### IPN callback

เมื่อการชำระถึงสถานะสุดท้าย เราส่ง POST ไปที่ `ipn_callback_url` ที่ระบุตอนสร้าง โดย `x-nowpayments-sig` = HMAC-SHA512(IPN secret, JSON ที่เรียงตามคีย์แบบเรียกซ้ำ) ซึ่งตรงกับโค้ดตรวจสอบของ NOWPayments ทุกประการ เนื้อหาที่เราส่งเรียงลำดับและกะทัดรัดอยู่แล้ว จึงใช้ HMAC บนเนื้อหาดิบก็ได้เช่นกัน (และปลอดภัยที่สุดใน PHP / Python ที่การเข้ารหัสใหม่ของทศนิยมที่เล็กมากอาจต่างจาก JS) callback ที่ล้มเหลวจะลองใหม่หลัง 1 นาที, 5 นาที, 30 นาที, … รวม 8 ครั้ง

```nodejs
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)
```

```php
$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 | เมื่อ | IPN ที่ส่ง |
| --- | --- | --- |
| `waiting` | รอการชำระ (รวมใบแจ้งหนี้ที่ยังไม่ได้เลือกเหรียญ) | — |
| `confirming` | พบการชำระบนเชน รอการยืนยัน | — (เห็นได้เมื่อค้นสถานะ) |
| `finished` | ชำระแล้ว (รวมจ่ายเกิน `actually_paid` คือยอดที่เข้ามาจริง) | ✓ |
| `partially_paid` | จ่ายขาด: ลูกค้ายังเติมเข้าที่อยู่เดิมได้ก่อนหมดอายุ (ส่งครั้งเดียวเมื่อพบการจ่ายขาดครั้งแรก) และส่งอีกครั้งหากหมดอายุแล้วยังขาด | ✓ |
| `expired` | หมดอายุโดยไม่มีการชำระ | ✓ |
| `refunded` | คุณคืนเงินการชำระทั้งหมดจากแดชบอร์ดและส่งออกแล้ว | — (เห็นได้เมื่อค้นสถานะ) |

`confirmed`, `sending` และ `failed` ไม่เคยปรากฏ: เมื่อยืนยันแล้วเป็น `finished` และบันทึกเข้ายอดคงเหลือของคุณ ไม่มีการโอนต่อ

#### สัญลักษณ์เหรียญ

| เหรียญ | เครือข่าย | pay_currency | network | ขั้นต่ำ |
| --- | --- | --- | --- | --- |
| 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 |

#### ข้อแตกต่างจาก NOWPayments

- `price_currency` ใช้ได้เฉพาะ `usd` (และ `usdt` / `usdc` ที่ 1:1) เงินตราอื่นตอบ 400

- `pay_amount` (จำนวนคริปโตคงที่), `payout_address` / `payout_currency` / `payout_extra_id` และ `customer_email` ไม่รองรับและตอบ 400

- `is_fixed_rate` รับได้แต่ไม่มีผล: เราล็อกอัตราเสมอ (BTC / LTC เป็นต้นด้วยค่ามัธยฐานจากหลายกระดานเทรด เป็นเวลา 30 นาที) เมื่อ `is_fee_paid_by_user` เป็น true ค่าธรรมเนียมแพลตฟอร์มจะบวกเข้า `pay_amount` (ค่าธรรมเนียมแพลตฟอร์มคือ 1% ลดลงเมื่อยอดสูงขึ้น ดู[ราคา](https://blockwallets.io/pricing?lang=th))

- การชำระมีอายุได้สูงสุด 12 ชั่วโมง (NOWPayments: 7 วัน) `partially_paid_url` ถูกเก็บและส่งกลับ แต่หน้าชำระเงินไม่เปลี่ยนเส้นทางไปที่นั่น

- `payment_id` เป็นตัวเลข 10 หลัก สำหรับใบแจ้งหนี้ที่สร้างด้วย `/invoice` `payment_id` ของการชำระเท่ากับ `id` ของใบแจ้งหนี้ และหนึ่งใบแจ้งหนี้มีการชำระเดียวพอดี (ไม่มีการฝากซ้ำแบบ `parent_payment_id`)

- ส่งเฉพาะ IPN ของสถานะสุดท้ายและ `partially_paid` (ตารางด้านบน) ไม่มี `waiting` / `confirming` `fee.serviceFee` คือค่าธรรมเนียมแพลตฟอร์ม (ยอดที่ได้รับ × อัตราตอนสร้างใบแจ้งหนี้ มาตรฐาน 1%) ค่าธรรมเนียมอื่นเป็น 0

### โหมดทดสอบ

charge และการชำระที่สร้างด้วยข้อมูลรับรองทดสอบรันบนเทสต์เน็ตของแต่ละเชน และ Webhook ของมันเซ็นด้วย IPN secret / shared secret ของชุดทดสอบ แยกจากใช้งานจริงโดยสิ้นเชิง: คีย์ทดสอบมองไม่เห็นการชำระจริง

ส่วนที่เหลือทั้งหมด (หน้าชำระเงิน การยืนยัน เครือข่ายและยอดขั้นต่ำ การคืนเงิน) ทำงานเหมือนใน[เอกสารหลัก](https://blockwallets.io/docs/cryptomus?lang=th)
