เริ่มต้นอย่างรวดเร็ว
การเชื่อมต่อรับชำระเงินแบบมินิมอล: การสร้างใบแจ้งหนี้ผ่าน API และการจัดการการแจ้งเตือนการชำระเงิน ส่วนสุดท้ายกล่าวถึงการส่งมอบสินค้า ตัวอย่างทั้งหมดครบถ้วนในตัว ไม่ต้องใช้ SDK
ทำงานร่วมกับ AI agent อยู่หรือไม่ ส่งเวอร์ชัน Markdown ของหน้านี้ให้กับมัน เนื้อหามีรายละเอียดเพียงพอสำหรับสร้างการเชื่อมต่อให้เว็บไซต์หรือร้านค้าของคุณ
ข้อกำหนดเบื้องต้น
| ค่า | รับได้จากที่ใด |
|---|---|
| คีย์ API | ส่วน การเชื่อมต่อ (Integrations) |
| ID ของโปรเจกต์ | ส่วน โปรเจกต์ (Projects) |
| คีย์ลับของ Webhook | การตั้งค่าโปรเจกต์ |
ในการตั้งค่าโปรเจกต์เดียวกันนั้น ให้ตั้งค่า Webhook URL — ที่อยู่ของตัวจัดการการแจ้งเตือนบนเซิร์ฟเวอร์ของคุณ รองรับ HTTPS เท่านั้น และจะไม่ติดตามการเปลี่ยนเส้นทาง
ตัวอย่างใช้ค่าทดสอบ — ให้แทนที่ด้วยค่าของคุณเอง
การสร้างใบแจ้งหนี้
POST https://api.bitsby.app/invoices/create โดยส่งคีย์ API ใน header Authorization
| พารามิเตอร์ | จำเป็น | ค่า |
|---|---|---|
projectId | ใช่ | ID ของโปรเจกต์ เป็น UUID |
amountFiat | ใช่ | จำนวนเงินของใบแจ้งหนี้ |
currencyFiat | ใช่ | สกุลเงิน: USD, EUR, RUB |
timeToPay | ใช่ | กำหนดเวลาชำระเงินเป็นชั่วโมง: 0.5, 1, 3, 6, 12 |
description | ไม่ | คำอธิบาย แสดงให้ลูกค้าเห็นบนหน้าชำระเงิน |
serviceData | ไม่ | ข้อมูลฝั่งบริการ ไม่แสดงให้ลูกค้าเห็น แนะนำให้ส่งหมายเลขคำสั่งซื้อ: ค่านี้จะถูกส่งกลับมาในการแจ้งเตือนการชำระเงิน |
- cURL
- PHP
- TypeScript
- JavaScript
- Python
curl -X POST https://api.bitsby.app/invoices/create \
-H "Authorization: Token MSvL2ltaDZdWVjmZURURMVWhqSJLT2NURjhL2Fla1Z1T1IxQTltKs1T3Ay" \
-F "projectId=9deea1e2-0c08-41a3-bdc2-a34eada3892d" \
-F "amountFiat=49.90" \
-F "currencyFiat=USD" \
-F "timeToPay=1" \
-F "description=Order 4172" \
-F "serviceData=order-4172"
<?php
$apiKey = 'MSvL2ltaDZdWVjmZURURMVWhqSJLT2NURjhL2Fla1Z1T1IxQTltKs1T3Ay';
$projectId = '9deea1e2-0c08-41a3-bdc2-a34eada3892d';
$ch = curl_init('https://api.bitsby.app/invoices/create');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => ['Authorization: Token '.$apiKey],
CURLOPT_POSTFIELDS => http_build_query([
'projectId' => $projectId,
'amountFiat' => 49.90,
'currencyFiat' => 'USD',
'timeToPay' => 1,
'description' => 'Order 4172',
'serviceData' => 'order-4172', // your order id
]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($response['result'] !== 'success') {
exit('API error: '.$response['data']);
}
$invoice = $response['data'];
// $invoice['id'] — invoice id, store it with the order
// $invoice['url'] — payment page for the customer
const API_KEY = 'MSvL2ltaDZdWVjmZURURMVWhqSJLT2NURjhL2Fla1Z1T1IxQTltKs1T3Ay';
const PROJECT_ID = '9deea1e2-0c08-41a3-bdc2-a34eada3892d';
interface Invoice {
id: string;
uid: string;
url: string;
timeToPayDatetime: string;
}
const response = await fetch('https://api.bitsby.app/invoices/create', {
method: 'POST',
headers: { Authorization: `Token ${API_KEY}` },
body: new URLSearchParams({
projectId: PROJECT_ID,
amountFiat: '49.90',
currencyFiat: 'USD',
timeToPay: '1',
description: 'Order 4172',
serviceData: 'order-4172', // your order id
}),
signal: AbortSignal.timeout(10_000),
});
const result: { result: string; data: Invoice | string } = await response.json();
if (result.result !== 'success') {
throw new Error(`API error: ${result.data}`);
}
const invoice = result.data as Invoice;
// invoice.id — invoice id, store it with the order
// invoice.url — payment page for the customer
const API_KEY = 'MSvL2ltaDZdWVjmZURURMVWhqSJLT2NURjhL2Fla1Z1T1IxQTltKs1T3Ay';
const PROJECT_ID = '9deea1e2-0c08-41a3-bdc2-a34eada3892d';
const response = await fetch('https://api.bitsby.app/invoices/create', {
method: 'POST',
headers: { Authorization: `Token ${API_KEY}` },
body: new URLSearchParams({
projectId: PROJECT_ID,
amountFiat: '49.90',
currencyFiat: 'USD',
timeToPay: '1',
description: 'Order 4172',
serviceData: 'order-4172', // your order id
}),
signal: AbortSignal.timeout(10_000),
});
const result = await response.json();
if (result.result !== 'success') {
throw new Error(`API error: ${result.data}`);
}
const invoice = result.data;
// invoice.id — invoice id, store it with the order
// invoice.url — payment page for the customer
import requests
API_KEY = 'MSvL2ltaDZdWVjmZURURMVWhqSJLT2NURjhL2Fla1Z1T1IxQTltKs1T3Ay'
PROJECT_ID = '9deea1e2-0c08-41a3-bdc2-a34eada3892d'
response = requests.post(
'https://api.bitsby.app/invoices/create',
headers={'Authorization': f'Token {API_KEY}'},
data={
'projectId': PROJECT_ID,
'amountFiat': '49.90',
'currencyFiat': 'USD',
'timeToPay': '1',
'description': 'Order 4172',
'serviceData': 'order-4172', # your order id
},
timeout=10,
)
result = response.json()
if result['result'] != 'success':
raise RuntimeError(f"API error: {result['data']}")
invoice = result['data']
# invoice['id'] — invoice id, store it with the order
# invoice['url'] — payment page for the customer
การตอบกลับ:
{
"result": "success",
"data": {
"id": "ade9550d-3dc7-4fd3-b94e-3b4c12aaaa0c",
"uid": "MXNj4m8HhcM4",
"createDatetime": "2026-09-14 10:12:03",
"timeToPayDatetime": "2026-09-14 11:12:03",
"commissionFiatUSD": 0.5,
"amountFiatUSD": 49.9,
"url": "https://dash.bitsby.app/invoices/pay/MXNj4m8HhcM4"
}
}
บันทึก data.id ไว้กับคำสั่งซื้อ และส่งลูกค้าไปยัง data.url — หน้าชำระเงิน ส่งลิงก์ด้วยวิธีใดก็ได้ตามสะดวก: การเปลี่ยนเส้นทาง อีเมล หรือข้อความจากบอท ใบแจ้งหนี้มีอายุถึง timeToPayDatetime (UTC)
การจัดการการแจ้งเตือน
เมื่อใบแจ้งหนี้เปลี่ยนเป็นสถานะชำระแล้ว (Paid) บริการจะส่งคำขอ POST ไปยัง Webhook URL ของโปรเจกต์ เนื้อหาคำขอเป็น JSON ส่วนลายเซ็นอยู่ใน header:
| Header | ค่า |
|---|---|
X-Timestamp | เวลาส่ง เป็น unix time หน่วยวินาที |
X-Signature | sha256= + HMAC-SHA256 แบบ hex ของสตริง <timestamp>.<request body> |
คีย์ที่ใช้เซ็นคือคีย์ลับของ Webhook ลายเซ็นคำนวณจากเนื้อหาคำขอแบบ raw ดังนั้นให้ตรวจสอบลายเซ็นก่อนแยกวิเคราะห์ JSON
- PHP
- TypeScript
- JavaScript
- Python
<?php
$secret = 'k7QwR2mZ9tXbN4vL8sJpH3dF6yA1cE0u';
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
// Reject replayed requests: allow up to 5 minutes of clock drift
if (abs(time() - (int)$timestamp) > 300) {
http_response_code(400);
exit;
}
$expected = 'sha256='.hash_hmac('sha256', $timestamp.'.'.$body, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(403);
exit;
}
$invoice = json_decode($body, true)['invoice'];
if ($invoice['status'] === 'paid') {
// $invoice['serviceData'] — the order id passed at creation: 'order-4172'
// $invoice['amountFiat'] — the original invoice amount: 49.9
// Issue the order here, see the next section
}
http_response_code(200);
import { createHmac, timingSafeEqual } from 'node:crypto';
import express, { type Request, type Response } from 'express';
const SECRET = 'k7QwR2mZ9tXbN4vL8sJpH3dF6yA1cE0u';
const app = express();
// express.raw: the signature is computed over the raw request body
app.post('/webhook', express.raw({ type: 'application/json' }), (req: Request, res: Response) => {
const timestamp = req.get('X-Timestamp') ?? '';
const signature = req.get('X-Signature') ?? '';
const body = (req.body as Buffer).toString('utf8');
// Reject replayed requests: allow up to 5 minutes of clock drift
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return res.sendStatus(400);
}
const expected = 'sha256=' + createHmac('sha256', SECRET)
.update(`${timestamp}.${body}`, 'utf8')
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature);
if (a.length !== b.length || !timingSafeEqual(a, b)) {
return res.sendStatus(403);
}
const { invoice } = JSON.parse(body);
if (invoice.status === 'paid') {
// invoice.serviceData — the order id passed at creation: 'order-4172'
// invoice.amountFiat — the original invoice amount: 49.9
// Issue the order here, see the next section
}
res.sendStatus(200);
});
app.listen(8080);
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';
const SECRET = 'k7QwR2mZ9tXbN4vL8sJpH3dF6yA1cE0u';
const app = express();
// express.raw: the signature is computed over the raw request body
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.get('X-Timestamp') ?? '';
const signature = req.get('X-Signature') ?? '';
const body = req.body.toString('utf8');
// Reject replayed requests: allow up to 5 minutes of clock drift
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return res.sendStatus(400);
}
const expected = 'sha256=' + createHmac('sha256', SECRET)
.update(`${timestamp}.${body}`, 'utf8')
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature);
if (a.length !== b.length || !timingSafeEqual(a, b)) {
return res.sendStatus(403);
}
const { invoice } = JSON.parse(body);
if (invoice.status === 'paid') {
// invoice.serviceData — the order id passed at creation: 'order-4172'
// invoice.amountFiat — the original invoice amount: 49.9
// Issue the order here, see the next section
}
res.sendStatus(200);
});
app.listen(8080);
import hashlib
import hmac
import time
from flask import Flask, request
SECRET = 'k7QwR2mZ9tXbN4vL8sJpH3dF6yA1cE0u'
app = Flask(__name__)
@app.post('/webhook')
def webhook():
timestamp = request.headers.get('X-Timestamp', '')
signature = request.headers.get('X-Signature', '')
# The signature is computed over the raw request body
body = request.get_data(as_text=True)
# Reject replayed requests: allow up to 5 minutes of clock drift
if abs(time.time() - int(timestamp or 0)) > 300:
return '', 400
expected = 'sha256=' + hmac.new(
SECRET.encode(),
f'{timestamp}.{body}'.encode(),
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(expected, signature):
return '', 403
invoice = request.get_json()['invoice']
if invoice['status'] == 'paid':
# invoice['serviceData'] — the order id passed at creation: 'order-4172'
# invoice['amountFiat'] — the original invoice amount: 49.9
# Issue the order here, see the next section
pass
return '', 200
ตอบกลับด้วยโค้ด 2xx ภายใน 10 วินาที โค้ดอื่น การเปลี่ยนเส้นทาง หรือการหมดเวลาจะนับเป็นการส่งที่ล้มเหลว: บริการจะลองส่งใหม่โดยเว้นช่วงนานขึ้นเรื่อย ๆ ตั้งแต่ 5 นาทีจนถึง 24 ชั่วโมง แล้วจึงหยุด ย้ายการประมวลผลที่ใช้เวลานานไปไว้ในคิว: ตอบกลับด้วย 200 ก่อน แล้วค่อยจัดการคำสั่งซื้อ
รูปแบบการแจ้งเตือนฉบับเต็มและข้อมูลอ้างอิงของฟิลด์อยู่ในส่วน Webhook URL
การส่งมอบสินค้า
การแจ้งเตือนที่ผ่านการตรวจสอบแล้วซึ่งมีสถานะ paid เป็นการยืนยันการชำระเงิน ขั้นตอนการส่งมอบ:
- ค้นหาคำสั่งซื้อจาก
invoice.serviceData— ค่าที่ส่งไว้ตอนสร้างใบแจ้งหนี้ (order-4172) - ตรวจสอบจาก
invoice.idว่าใบแจ้งหนี้นี้ได้ส่งมอบไปแล้วหรือยัง การแจ้งเตือนที่มีinvoice.idเดียวกันอาจมาถึงมากกว่าหนึ่งครั้ง — ส่งมอบคำสั่งซื้อเพียงครั้งเดียวและบันทึกแฟล็กว่าประมวลผลแล้ว - ตรวจสอบจำนวนเงินและสกุลเงินกับ
invoice.amountFiatและinvoice.currencyFiat— ค่าเหล่านี้เป็นค่าดั้งเดิมของใบแจ้งหนี้และไม่เปลี่ยนแปลง ส่วนamountFiatUSDจะถูกเขียนทับด้วยจำนวนเงินที่ได้รับจริง เปรียบเทียบจำนวนเงินแบบตัวเลข ไม่ใช่แบบสตริง: ศูนย์ท้ายจะถูกตัดออก ดังนั้น49.90จะมาถึงเป็น49.9หากค่าไม่ตรงกับคำสั่งซื้อ ให้ส่งใบแจ้งหนี้ไปตรวจสอบด้วยตนเองแทนการส่งมอบ — กรณีเหล่านี้อธิบายไว้ในการจับคู่การชำระเงินและยอดเงินไม่ตรงกัน - ส่งมอบคำสั่งซื้อและทำเครื่องหมายว่าประมวลผลแล้ว
อย่าตรวจสอบกำหนดเวลาชำระเงินตอนส่งมอบ: การชำระเงินอาจได้รับการยืนยัน on-chain หลังจาก timeToPayDatetime และร้านค้าสามารถจับคู่การชำระเงินกับใบแจ้งหนี้ด้วยตนเองได้ การแจ้งเตือนนั้นเองคือการยืนยันการชำระเงิน
ขั้นตอนถัดไป
- ข้อมูลอ้างอิง API — รายการใบแจ้งหนี้ การยกเลิก สถิติ และยอดคงเหลือ
- การตรวจสอบลายเซ็น Webhook — กลไกของลายเซ็นโดยละเอียด
- การจับคู่การชำระเงินและยอดเงินไม่ตรงกัน — ชำระไม่ครบ ชำระเกิน และการชำระเงินที่ยังไม่ได้จับคู่
- ฟอร์ม HTML — รับชำระเงินโดยไม่ต้องใช้ API