Skip to content

การยืนยันตัวตน

EasySlip API ใช้ HMAC-SHA256 Signature Authentication เพื่อความปลอดภัยในการเข้าถึง API ทุก request ต้องลงนามด้วย Secret Key เพื่อรับรองความถูกต้องและความสมบูรณ์ของข้อมูล

Credentials ที่ต้องใช้

เมื่อสร้างแอปพลิเคชัน คุณจะได้รับ credentials 3 ตัว:

Credentialรูปแบบหน้าที่ได้มาจากไหน
Partners API Keyhex 64 ตัวอักษรระบุตัวตน ส่งเป็น X-API-Key ในทุก requestสร้างในหน้าตั้งค่าแอปพลิเคชัน
Secret Keyhex 64 ตัวอักษรใช้ลงนาม (sign) ทุก request ด้วย HMAC-SHA256แสดงครั้งเดียวตอนสร้าง Partners Client
Branch KeyUUID 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

  1. เข้าสู่ระบบ EasySlip Developer Portal
  2. ไปที่การตั้งค่าแอปพลิเคชัน
  3. สร้างคู่ Partners API Key และ Secret Key
  4. จดบันทึก Branch Key (UUID) จากการตั้งค่าสาขา
  5. เก็บ 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-KeyX-Branch-KeyEndpoint
ตรวจสอบสลิป/ดูข้อมูล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-KeystringPartners API Key (hex 64) — ส่งทุก request
X-Branch-KeystringBranch Key (UUID) — ส่งเมื่อ endpoint ต้องระบุสาขา
X-TimestampstringUnix timestamp เป็นวินาที
X-NoncestringUUID v4 ไม่ซ้ำกันในแต่ละ request
X-SignaturestringHMAC-SHA256 hex signature

การคำนวณ Signature

ขั้นตอนที่ 1: สร้าง String-to-Sign

{METHOD}\n{PATH}\n{TIMESTAMP}\n{NONCE}\n{BODY_SHA256}
ส่วนประกอบคำอธิบาย
METHODHTTP method ตัวพิมพ์ใหญ่ (GET, POST, PATCH, PUT, DELETE)
PATHเส้นทาง request รวมเครื่องหมาย / นำหน้า (เช่น /b2b/branches)
TIMESTAMPค่าเดียวกับ header X-Timestamp
NONCEค่าเดียวกับ header X-Nonce
BODY_SHA256SHA-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)

javascript
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' } : {}),
    }
}
python
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
<?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,
    ];
}
bash
#!/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)

javascript
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)
python
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'])
php
$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']);
bash
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)

javascript
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)
python
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'])
php
$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']);
bash
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)

javascript
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)
python
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'])
php
$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']);
bash
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

javascript
// ❌ ไม่ควรทำ
const API_KEY = 'your-api-key-here';

// ✅ ควรทำ - ใช้ Environment Variables
const API_KEY = process.env.EASYSLIP_API_KEY;

2. ใช้ Environment Variables

bash
EASYSLIP_PARTNERS_API_KEY=your-partners-api-key
EASYSLIP_SECRET_KEY=your-secret-key
EASYSLIP_BRANCH_KEY=your-branch-uuid
javascript
import 'dotenv/config';

const partnersApiKey = process.env.EASYSLIP_PARTNERS_API_KEY;
const secretKey = process.env.EASYSLIP_SECRET_KEY;
const branchKey = process.env.EASYSLIP_BRANCH_KEY;
php
$partnersApiKey = getenv('EASYSLIP_PARTNERS_API_KEY');
$secretKey = getenv('EASYSLIP_SECRET_KEY');
$branchKey = getenv('EASYSLIP_BRANCH_KEY');
python
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 เท่านั้น

javascript
// ❌ ไม่ควรทำ - 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 ที่กำหนด:

  1. ไปที่ ตั้งค่าแอปพลิเคชัน
  2. เพิ่ม IP Address ของ Server ลงใน Whitelist
  3. ใช้ * เพื่ออนุญาตทุก IP (ไม่แนะนำสำหรับ Production)

ข้อผิดพลาดการยืนยันตัวตน

Error CodeHTTP Statusคำอธิบายวิธีแก้ไข
INVALID_AUTH_HEADERS401Headers ไม่ถูกต้องหรือไม่ครบตรวจสอบว่าส่ง X-API-Key, X-Timestamp, X-Nonce, X-Signature ครบ
INVALID_API_KEY401API Key ไม่ถูกต้องตรวจสอบ Partners API Key
MISSING_BRANCH_KEY401ไม่ได้ส่ง X-Branch-Keyเพิ่ม header X-Branch-Key สำหรับ endpoint ที่ต้องระบุสาขา
INVALID_BRANCH_KEY401Branch Key ไม่ถูกต้องตรวจสอบ Branch Key (UUID)
INVALID_TIMESTAMP401Timestamp อยู่นอกช่วง ±5 นาทีตรวจสอบนาฬิกาเครื่อง
DUPLICATE_NONCE401Nonce ซ้ำสร้าง UUID ใหม่ทุก request
INVALID_SIGNATURE401Signature ไม่ตรงตรวจสอบ Secret Key และวิธีคำนวณ signature
SERVICE_SUSPENDED403แอปพลิเคชันถูกระงับติดต่อ Support
BRANCH_INACTIVE403Branch ถูกปิดใช้งานเปิดใช้งาน Branch ใหม่
IP_NOT_ALLOWED403IP ไม่อยู่ใน Whitelistเพิ่ม IP ลงใน Whitelist
BRANCH_QUOTA_EXCEEDED403เกินโควต้าสาขาอัพเกรดแพ็กเกจ หรือรอให้โควต้ารีเซ็ต
PERMISSION_DENIED403ไม่มีสิทธิ์เข้าถึงตรวจสอบ permissions ของ Partners Client

รองรับหลาย Branch

EasySlip รองรับหลาย Branch ต่อแอปพลิเคชัน แต่ละ Branch มี Branch Key แยกกัน:

  • Main Branch: Branch Key หลัก มีโควต้าเต็ม
  • Sub-Branches: Branch Key เพิ่มเติม มีการติดตามโควต้าแยก

ช่วยให้คุณ:

  • ติดตามการใช้งานต่อจุดเชื่อมต่อ
  • กำหนด IP Restrictions ที่ต่างกันต่อ Branch
  • จัดการการจัดสรรโควต้าระหว่างทีม

ขั้นตอนถัดไป

Bank Slip Verification API for Thai Banking