การยืนยันตัวตน
EasySlip API ใช้ HMAC-SHA256 Signature Authentication เพื่อความปลอดภัยในการเข้าถึง API ทุก request ต้องลงนามด้วย Secret Key เพื่อรับรองความถูกต้องและความสมบูรณ์ของข้อมูล
Credentials ที่ต้องใช้
เมื่อสร้างแอปพลิเคชัน คุณจะได้รับ credentials 3 ตัว:
| Credential | รูปแบบ | หน้าที่ | ได้มาจากไหน |
|---|---|---|---|
| Partners API Key | hex 64 ตัวอักษร | ระบุตัวตน ส่งเป็น X-API-Key ในทุก request | สร้างในหน้าตั้งค่าแอปพลิเคชัน |
| Secret Key | hex 64 ตัวอักษร | ใช้ลงนาม (sign) ทุก request ด้วย HMAC-SHA256 | แสดงครั้งเดียวตอนสร้าง Partners Client |
| Branch Key | UUID v4 | ระบุ Branch ส่งเป็น X-Branch-Key สำหรับ endpoint ที่ต้องระบุสาขา | สร้างอัตโนมัติเมื่อสร้าง Branch ใหม่ |
สรุปง่ายๆ
- ทุก request → ส่ง Partners API Key เป็น
X-API-Keyแล้ว sign ด้วย Secret Key - ตรวจสอบสลิป / จัดการสาขาเฉพาะ? → ส่ง Branch Key เป็น
X-Branch-Keyเพิ่ม
DANGER
อย่าเปิดเผย Secret Key ในโค้ดฝั่ง client, repository สาธารณะ หรือ logs Secret Key ควรใช้ในแอปพลิเคชันฝั่ง server เท่านั้น
การรับ API Key
- เข้าสู่ระบบ EasySlip Developer Portal
- ไปที่การตั้งค่าแอปพลิเคชัน
- สร้างคู่ Partners API Key และ Secret Key
- จดบันทึก Branch Key (UUID) จากการตั้งค่าสาขา
- เก็บ Secret Key ไว้อย่างปลอดภัย — จะแสดงให้เห็นเพียงครั้งเดียว
Diagram
┌──────────────────────────────────────────────────────┐
│ Application │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Partners Client │ │
│ │ Partners API Key ──→ X-API-Key (ทุก request) │ │
│ │ Secret Key ──→ ลงนามทุก request │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ Branch A │ │ Branch B │ ... │
│ │ Branch Key │ │ Branch Key │ │
│ │ ──→ X-Branch- │ │ ──→ X-Branch- │ │
│ │ Key │ │ Key │ │
│ └───────────────┘ └───────────────┘ │
└──────────────────────────────────────────────────────┘วิธีเลือก Header ที่จะส่ง
X-API-Key ส่ง Partners API Key เสมอ ส่วน X-Branch-Key ส่งเฉพาะ endpoint ที่ต้องระบุสาขา:
| ต้องการทำอะไร | X-API-Key | X-Branch-Key | Endpoint |
|---|---|---|---|
| ตรวจสอบสลิป/ดูข้อมูล | Partners API Key (hex 64) | Branch Key (UUID) | /verify/bank, /verify/truewallet, /info |
| จัดการสาขาเฉพาะ/โควตา/บัญชีธนาคาร | Partners API Key (hex 64) | Branch Key (UUID) | /b2b/branch, /b2b/branch/* |
| ดูรายการสาขา/สร้างสาขา/สร้างบัญชีธนาคาร | Partners API Key (hex 64) | ไม่ต้องส่ง | /b2b/branches, /b2b/bank-accounts |
Headers ที่จำเป็น
| Header | รูปแบบ | คำอธิบาย |
|---|---|---|
X-API-Key | string | Partners API Key (hex 64) — ส่งทุก request |
X-Branch-Key | string | Branch Key (UUID) — ส่งเมื่อ endpoint ต้องระบุสาขา |
X-Timestamp | string | Unix timestamp เป็นวินาที |
X-Nonce | string | UUID v4 ไม่ซ้ำกันในแต่ละ request |
X-Signature | string | HMAC-SHA256 hex signature |
การคำนวณ Signature
ขั้นตอนที่ 1: สร้าง String-to-Sign
{METHOD}\n{PATH}\n{TIMESTAMP}\n{NONCE}\n{BODY_SHA256}| ส่วนประกอบ | คำอธิบาย |
|---|---|
METHOD | HTTP method ตัวพิมพ์ใหญ่ (GET, POST, PATCH, PUT, DELETE) |
PATH | เส้นทาง request รวมเครื่องหมาย / นำหน้า (เช่น /b2b/branches) |
TIMESTAMP | ค่าเดียวกับ header X-Timestamp |
NONCE | ค่าเดียวกับ header X-Nonce |
BODY_SHA256 | SHA-256 hex digest ของ request body |
ขั้นตอนที่ 2: คำนวณ Body Hash
- สำหรับ request ที่มี body (POST, PUT, PATCH): คำนวณ SHA-256 ของ raw JSON body string
- สำหรับ request ที่ไม่มี body (GET, DELETE): คำนวณ SHA-256 ของ empty string (
"")
TIP
SHA-256 ของ empty string จะเป็นค่าคงที่: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
ขั้นตอนที่ 3: ลงนามด้วย HMAC-SHA256
HMAC-SHA256(secretKey, stringToSign)ผลลัพธ์เป็น lowercase hex string
ค่าเผื่อ Timestamp
เซิร์ฟเวอร์ยอมรับ timestamp ภายใน ±5 นาที จากเวลาปัจจุบันของเซิร์ฟเวอร์ request ที่อยู่นอกช่วงนี้จะถูกปฏิเสธด้วย INVALID_TIMESTAMP
ความเป็นเอกลักษณ์ของ Nonce
แต่ละ nonce ต้องเป็น UUID v4 และต้องไม่ซ้ำกัน เซิร์ฟเวอร์จะติดตาม nonce ล่าสุดและจะปฏิเสธ nonce ที่ซ้ำด้วย DUPLICATE_NONCE ต้องสร้าง UUID ใหม่สำหรับทุก request
ตัวอย่างโค้ด
ฟังก์ชัน signRequest (ใช้ได้กับทุก endpoint)
import crypto from 'crypto'
function signRequest({ method, path, body, apiKey, secretKey }) {
const timestamp = Math.floor(Date.now() / 1000).toString()
const nonce = crypto.randomUUID()
const bodyString = body ? JSON.stringify(body) : ''
const bodyHash = crypto.createHash('sha256').update(bodyString).digest('hex')
const stringToSign = `${method}\n${path}\n${timestamp}\n${nonce}\n${bodyHash}`
const signature = crypto.createHmac('sha256', secretKey).update(stringToSign).digest('hex')
return {
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Nonce': nonce,
'X-Signature': signature,
...(body ? { 'Content-Type': 'application/json' } : {}),
}
}import hashlib
import hmac
import json
import time
import uuid
def sign_request(method, path, body, api_key, secret_key):
timestamp = str(int(time.time()))
nonce = str(uuid.uuid4())
body_string = json.dumps(body, separators=(',', ':')) if body else ''
body_hash = hashlib.sha256(body_string.encode()).hexdigest()
string_to_sign = f'{method}\n{path}\n{timestamp}\n{nonce}\n{body_hash}'
signature = hmac.new(
secret_key.encode(),
string_to_sign.encode(),
hashlib.sha256
).hexdigest()
return {
'X-API-Key': api_key,
'X-Timestamp': timestamp,
'X-Nonce': nonce,
'X-Signature': signature,
}<?php
function signRequest(string $method, string $path, ?array $body, string $apiKey, string $secretKey): array
{
$timestamp = (string) time();
$nonce = sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
$bodyString = $body ? json_encode($body, JSON_UNESCAPED_UNICODE) : '';
$bodyHash = hash('sha256', $bodyString);
$stringToSign = implode("\n", [$method, $path, $timestamp, $nonce, $bodyHash]);
$signature = hash_hmac('sha256', $stringToSign, $secretKey);
return [
'X-API-Key' => $apiKey,
'X-Timestamp' => $timestamp,
'X-Nonce' => $nonce,
'X-Signature' => $signature,
];
}#!/bin/bash
API_KEY="your_partners_api_key"
SECRET_KEY="your_secret_key"
METHOD="GET"
PATH_URL="/info"
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]')
# สำหรับ GET/DELETE ให้ hash empty string
BODY_HASH=$(printf '' | shasum -a 256 | cut -d' ' -f1)
STRING_TO_SIGN="${METHOD}\n${PATH_URL}\n${TIMESTAMP}\n${NONCE}\n${BODY_HASH}"
SIGNATURE=$(printf "${STRING_TO_SIGN}" | openssl dgst -sha256 -hmac "${SECRET_KEY}" | cut -d' ' -f2)ตัวอย่าง: ตรวจสอบสลิป (ต้องส่ง X-Branch-Key)
const PARTNERS_API_KEY = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'
const SECRET_KEY = 'your_secret_key'
const BRANCH_KEY = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
const body = { payload: '00020101021230...' }
const headers = signRequest({
method: 'POST',
path: '/verify/bank',
body,
apiKey: PARTNERS_API_KEY,
secretKey: SECRET_KEY,
})
const response = await fetch('https://api.easyslip.com/v2/verify/bank', {
method: 'POST',
headers: { ...headers, 'X-Branch-Key': BRANCH_KEY },
body: JSON.stringify(body),
})
const result = await response.json()
console.log(result.data)import requests
PARTNERS_API_KEY = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'
SECRET_KEY = 'your_secret_key'
BRANCH_KEY = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
body = {'payload': '00020101021230...'}
headers = sign_request('POST', '/verify/bank', body, PARTNERS_API_KEY, SECRET_KEY)
headers['X-Branch-Key'] = BRANCH_KEY
headers['Content-Type'] = 'application/json'
response = requests.post(
'https://api.easyslip.com/v2/verify/bank',
headers=headers,
json=body,
)
result = response.json()
print(result['data'])$partnersApiKey = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789';
$secretKey = 'your_secret_key';
$branchKey = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
$body = ['payload' => '00020101021230...'];
$headers = signRequest('POST', '/verify/bank', $body, $partnersApiKey, $secretKey);
$headers['X-Branch-Key'] = $branchKey;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.easyslip.com/v2/verify/bank',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => array_merge(
array_map(fn($k, $v) => "$k: $v", array_keys($headers), array_values($headers)),
['Content-Type: application/json']
),
CURLOPT_POSTFIELDS => json_encode($body),
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result['data']);curl -X POST "https://api.easyslip.com/v2/verify/bank" \
-H "X-API-Key: ${API_KEY}" \
-H "X-Branch-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Nonce: ${NONCE}" \
-H "X-Signature: ${SIGNATURE}" \
-H "Content-Type: application/json" \
-d '{"payload": "00020101021230..."}'ตัวอย่าง: ดูรายการสาขา (ไม่ต้องส่ง X-Branch-Key)
const headers = signRequest({
method: 'GET',
path: '/b2b/branches',
body: null,
apiKey: PARTNERS_API_KEY,
secretKey: SECRET_KEY,
})
const response = await fetch('https://api.easyslip.com/b2b/branches', { headers })
const result = await response.json()
console.log(result.data)headers = sign_request('GET', '/b2b/branches', None, PARTNERS_API_KEY, SECRET_KEY)
response = requests.get('https://api.easyslip.com/b2b/branches', headers=headers)
result = response.json()
print(result['data'])$headers = signRequest('GET', '/b2b/branches', null, $partnersApiKey, $secretKey);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.easyslip.com/b2b/branches',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array_map(fn($k, $v) => "$k: $v", array_keys($headers), array_values($headers)),
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result['data']);curl -X GET "https://api.easyslip.com/b2b/branches" \
-H "X-API-Key: ${API_KEY}" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Nonce: ${NONCE}" \
-H "X-Signature: ${SIGNATURE}"ตัวอย่าง: ดูโควต้าสาขา (ต้องส่ง X-Branch-Key)
const headers = signRequest({
method: 'GET',
path: '/b2b/branch/quota',
body: null,
apiKey: PARTNERS_API_KEY,
secretKey: SECRET_KEY,
})
const response = await fetch('https://api.easyslip.com/b2b/branch/quota', {
headers: { ...headers, 'X-Branch-Key': BRANCH_KEY },
})
const result = await response.json()
console.log(result.data)headers = sign_request('GET', '/b2b/branch/quota', None, PARTNERS_API_KEY, SECRET_KEY)
headers['X-Branch-Key'] = BRANCH_KEY
response = requests.get('https://api.easyslip.com/b2b/branch/quota', headers=headers)
result = response.json()
print(result['data'])$headers = signRequest('GET', '/b2b/branch/quota', null, $partnersApiKey, $secretKey);
$headers['X-Branch-Key'] = $branchKey;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.easyslip.com/b2b/branch/quota',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array_map(fn($k, $v) => "$k: $v", array_keys($headers), array_values($headers)),
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result['data']);curl -X GET "https://api.easyslip.com/b2b/branch/quota" \
-H "X-API-Key: ${API_KEY}" \
-H "X-Branch-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Nonce: ${NONCE}" \
-H "X-Signature: ${SIGNATURE}"สิทธิ์การใช้งาน (Permissions)
แต่ละ Partners API key จะมีชุดสิทธิ์ที่กำหนดว่าสามารถทำอะไรได้บ้าง:
| Permission | คำอธิบาย |
|---|---|
branch:read | ดูรายการและข้อมูลสาขา |
branch:write | สร้าง แก้ไข และลบสาขา |
quota:read | ดูข้อมูลโควตา |
quota:write | กำหนด ปรับ และรีเซ็ตโควตา |
bank-account:read | ดูบัญชีธนาคารที่เชื่อมต่อกับสาขา |
bank-account:write | สร้าง เชื่อมต่อ และยกเลิกการเชื่อมต่อบัญชีธนาคาร |
WARNING
หาก API key ของคุณไม่มีสิทธิ์ที่จำเป็นสำหรับ endpoint นั้น request จะถูกปฏิเสธด้วย 403 Forbidden
แนวทางปฏิบัติด้านความปลอดภัย
1. อย่าเปิดเผย Key ในโค้ด Client-side
// ❌ ไม่ควรทำ
const API_KEY = 'your-api-key-here';
// ✅ ควรทำ - ใช้ Environment Variables
const API_KEY = process.env.EASYSLIP_API_KEY;2. ใช้ Environment Variables
EASYSLIP_PARTNERS_API_KEY=your-partners-api-key
EASYSLIP_SECRET_KEY=your-secret-key
EASYSLIP_BRANCH_KEY=your-branch-uuidimport 'dotenv/config';
const partnersApiKey = process.env.EASYSLIP_PARTNERS_API_KEY;
const secretKey = process.env.EASYSLIP_SECRET_KEY;
const branchKey = process.env.EASYSLIP_BRANCH_KEY;$partnersApiKey = getenv('EASYSLIP_PARTNERS_API_KEY');
$secretKey = getenv('EASYSLIP_SECRET_KEY');
$branchKey = getenv('EASYSLIP_BRANCH_KEY');import os
partners_api_key = os.environ.get('EASYSLIP_PARTNERS_API_KEY')
secret_key = os.environ.get('EASYSLIP_SECRET_KEY')
branch_key = os.environ.get('EASYSLIP_BRANCH_KEY')3. ทำ Request จาก Server เท่านั้น
// ❌ ไม่ควรทำ - Client-side request เปิดเผย API key และ secret
fetch('https://api.easyslip.com/v2/verify/bank', {
headers: { 'X-API-Key': 'your-key', 'X-Signature': '...' }
});
// ✅ ควรทำ - Request ผ่าน Server ของคุณ
fetch('/api/verify-slip', {
method: 'POST',
body: JSON.stringify({ image: slipImage })
});4. IP Whitelisting
ตั้งค่า IP Whitelist เพื่อจำกัดการเข้าถึง API เฉพาะ IP ที่กำหนด:
- ไปที่ ตั้งค่าแอปพลิเคชัน
- เพิ่ม IP Address ของ Server ลงใน Whitelist
- ใช้
*เพื่ออนุญาตทุก IP (ไม่แนะนำสำหรับ Production)
ข้อผิดพลาดการยืนยันตัวตน
| Error Code | HTTP Status | คำอธิบาย | วิธีแก้ไข |
|---|---|---|---|
INVALID_AUTH_HEADERS | 401 | Headers ไม่ถูกต้องหรือไม่ครบ | ตรวจสอบว่าส่ง X-API-Key, X-Timestamp, X-Nonce, X-Signature ครบ |
INVALID_API_KEY | 401 | API Key ไม่ถูกต้อง | ตรวจสอบ Partners API Key |
MISSING_BRANCH_KEY | 401 | ไม่ได้ส่ง X-Branch-Key | เพิ่ม header X-Branch-Key สำหรับ endpoint ที่ต้องระบุสาขา |
INVALID_BRANCH_KEY | 401 | Branch Key ไม่ถูกต้อง | ตรวจสอบ Branch Key (UUID) |
INVALID_TIMESTAMP | 401 | Timestamp อยู่นอกช่วง ±5 นาที | ตรวจสอบนาฬิกาเครื่อง |
DUPLICATE_NONCE | 401 | Nonce ซ้ำ | สร้าง UUID ใหม่ทุก request |
INVALID_SIGNATURE | 401 | Signature ไม่ตรง | ตรวจสอบ Secret Key และวิธีคำนวณ signature |
SERVICE_SUSPENDED | 403 | แอปพลิเคชันถูกระงับ | ติดต่อ Support |
BRANCH_INACTIVE | 403 | Branch ถูกปิดใช้งาน | เปิดใช้งาน Branch ใหม่ |
IP_NOT_ALLOWED | 403 | IP ไม่อยู่ใน Whitelist | เพิ่ม IP ลงใน Whitelist |
BRANCH_QUOTA_EXCEEDED | 403 | เกินโควต้าสาขา | อัพเกรดแพ็กเกจ หรือรอให้โควต้ารีเซ็ต |
PERMISSION_DENIED | 403 | ไม่มีสิทธิ์เข้าถึง | ตรวจสอบ permissions ของ Partners Client |
รองรับหลาย Branch
EasySlip รองรับหลาย Branch ต่อแอปพลิเคชัน แต่ละ Branch มี Branch Key แยกกัน:
- Main Branch: Branch Key หลัก มีโควต้าเต็ม
- Sub-Branches: Branch Key เพิ่มเติม มีการติดตามโควต้าแยก
ช่วยให้คุณ:
- ติดตามการใช้งานต่อจุดเชื่อมต่อ
- กำหนด IP Restrictions ที่ต่างกันต่อ Branch
- จัดการการจัดสรรโควต้าระหว่างทีม
ขั้นตอนถัดไป
- Partners API Overview - ดู Endpoints ทั้งหมด
- API v2 Reference - สำรวจ Endpoints ของ v2
- รหัสข้อผิดพลาด - รายการ Error Code ทั้งหมด