Xác thực
Mọi yêu cầu API đều phải bao gồm ba header xác thực. Finan sử dụng chữ ký HMAC SHA-256 để xác minh tính toàn vẹn của yêu cầu.
Header bắt buộc
| Header | Mô tả |
|---|---|
x-client-id | ID khách hàng duy nhất của bạn (được cung cấp bởi Finan) |
x-signature | Giá trị băm SHA-256 của dữ liệu yêu cầu (xem bên dưới) |
x-timestamp | Dấu thời gian Unix hiện tại — phải nằm trong khoảng 30 giây so với thời gian máy chủ |
curl -X POST 'https://api.finan.one/open/api/v1/payments' \
-H 'Content-Type: application/json' \
-H 'x-client-id: YOUR_CLIENT_ID' \
-H 'x-signature: YOUR_GENERATED_SIGNATURE' \
-H 'x-timestamp: 1699999999'
Cách tạo chữ ký
Bước 1: Chuẩn bị các trường
| Trường | Kiểu | Mô tả |
|---|---|---|
| Secret Key | string | Khóa bí mật của bạn được cung cấp bởi Finan |
| Method | string | Phương thức HTTP: GET, POST, PUT, DELETE |
| Relative Path | string | Đường dẫn API không bao gồm tiền tố /open (xem ghi chú bên dưới) |
| Payload | string | Nội dung JSON của yêu cầu dưới dạng chuỗi. Chuỗi rỗng "" cho các yêu cầu GET |
| Timestamp | string | Dấu thời gian Unix (cùng giá trị với header x-timestamp) |
URL gốc là https://api.finan.one/open/api/v1/... nhưng chữ ký chỉ sử dụng đường dẫn sau /open.
Bao gồm chuỗi truy vấn khi lọc theo tham số truy vấn:
| URL đầy đủ | Đường dẫn chữ ký |
|---|---|
https://api.finan.one/open/api/v1/payments | /api/v1/payments |
https://api.finan.one/open/api/v1/payouts/abc123/otps | /api/v1/payouts/abc123/otps |
https://api.finan.one/open/api/v1/master-bank-accounts?account_id=xxx | /api/v1/master-bank-accounts?account_id=xxx |
Bước 2: Nối chuỗi với dấu gạch dưới
{SECRET_KEY}_{METHOD}_{RELATIVE_PATH}_{PAYLOAD}_{TIMESTAMP}
Ví dụ POST (có nội dung JSON):
mySecretKey_POST_/api/v1/payments_{"amount":6000000,"payment_method":"bank_transfer"}_1699999999
Ví dụ GET (payload rỗng):
mySecretKey_GET_/api/v1/payments__1699999999
Đối với các yêu cầu GET, payload là một chuỗi rỗng. Điều này có nghĩa là chuỗi nối sẽ có hai dấu gạch dưới liên tiếp (__) giữa đường dẫn và dấu thời gian.
Bước 3: Băm với SHA-256
Áp dụng SHA-256 cho chuỗi đã nối. Kết quả được mã hóa hex chính là x-signature của bạn.
- Go
- JavaScript
- Python
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"time"
)
func generateSignature(secretKey, method, path, payload, timestamp string) string {
message := secretKey + "_" + method + "_" + path + "_" + payload + "_" + timestamp
hash := sha256.Sum256([]byte(message))
return hex.EncodeToString(hash[:])
}
func main() {
secretKey := "mySecretKey"
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
// POST request with body
postPayload := `{"amount":6000000,"payment_method":"bank_transfer"}`
postSig := generateSignature(secretKey, "POST", "/api/v1/payments", postPayload, timestamp)
fmt.Println("POST Signature:", postSig)
// GET request (empty payload)
getSig := generateSignature(secretKey, "GET", "/api/v1/payments", "", timestamp)
fmt.Println("GET Signature:", getSig)
}
import { createHash } from 'crypto';
function generateSignature(secretKey, method, path, payload, timestamp) {
const message = `${secretKey}_${method}_${path}_${payload}_${timestamp}`;
return createHash('sha256').update(message).digest('hex');
}
// POST request with body
const timestamp = Math.floor(Date.now() / 1000).toString();
const postPayload = JSON.stringify({ amount: 6000000, payment_method: 'bank_transfer' });
const postSig = generateSignature('mySecretKey', 'POST', '/api/v1/payments', postPayload, timestamp);
console.log('POST Signature:', postSig);
// GET request (empty payload)
const getSig = generateSignature('mySecretKey', 'GET', '/api/v1/payments', '', timestamp);
console.log('GET Signature:', getSig);
import hashlib
import time
def generate_signature(secret_key, method, path, payload, timestamp):
message = f"{secret_key}_{method}_{path}_{payload}_{timestamp}"
return hashlib.sha256(message.encode()).hexdigest()
timestamp = str(int(time.time()))
# POST request with body
post_payload = '{"amount":6000000,"payment_method":"bank_transfer"}'
post_sig = generate_signature("mySecretKey", "POST", "/api/v1/payments", post_payload, timestamp)
print("POST Signature:", post_sig)
# GET request (empty payload)
get_sig = generate_signature("mySecretKey", "GET", "/api/v1/payments", "", timestamp)
print("GET Signature:", get_sig)
Ví dụ request đầy đủ
Request POST (Tạo thanh toán)
- Go
- JavaScript
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"strconv"
"time"
)
func generateSignature(secretKey, method, path, payload, timestamp string) string {
message := secretKey + "_" + method + "_" + path + "_" + payload + "_" + timestamp
hash := sha256.Sum256([]byte(message))
return hex.EncodeToString(hash[:])
}
func main() {
clientID := "YOUR_CLIENT_ID"
secretKey := "YOUR_SECRET_KEY"
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
payload := `{"amount":6000000,"payment_method":"bank_transfer","reference_id":"ORDER-001"}`
path := "/api/v1/payments"
signature := generateSignature(secretKey, "POST", path, payload, timestamp)
req, _ := http.NewRequest("POST", "https://api.finan.one/open"+path, bytes.NewBufferString(payload))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-client-id", clientID)
req.Header.Set("x-signature", signature)
req.Header.Set("x-timestamp", timestamp)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Status: %d\nBody: %s\n", resp.StatusCode, string(body))
}
import { createHash } from 'crypto';
function generateSignature(secretKey, method, path, payload, timestamp) {
const message = `${secretKey}_${method}_${path}_${payload}_${timestamp}`;
return createHash('sha256').update(message).digest('hex');
}
const clientId = 'YOUR_CLIENT_ID';
const secretKey = 'YOUR_SECRET_KEY';
const timestamp = Math.floor(Date.now() / 1000).toString();
const payload = JSON.stringify({
amount: 6000000,
payment_method: 'bank_transfer',
reference_id: 'ORDER-001',
});
const path = '/api/v1/payments';
const signature = generateSignature(secretKey, 'POST', path, payload, timestamp);
const response = await fetch(`https://api.finan.one/open${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-client-id': clientId,
'x-signature': signature,
'x-timestamp': timestamp,
},
body: payload,
});
const data = await response.json();
console.log('Status:', response.status);
console.log('Data:', data);
Request GET (Danh sách thanh toán)
- Go
- JavaScript
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
path := "/api/v1/payments"
signature := generateSignature(secretKey, "GET", path, "", timestamp)
req, _ := http.NewRequest("GET", "https://api.finan.one/open"+path, nil)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-client-id", clientID)
req.Header.Set("x-signature", signature)
req.Header.Set("x-timestamp", timestamp)
const timestamp = Math.floor(Date.now() / 1000).toString();
const path = '/api/v1/payments';
const signature = generateSignature(secretKey, 'GET', path, '', timestamp);
const response = await fetch(`https://api.finan.one/open${path}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'x-client-id': clientId,
'x-signature': signature,
'x-timestamp': timestamp,
},
});
Môi trường
| Môi trường | URL gốc |
|---|---|
| Production | https://api.finan.one/open |
| Staging | https://api-stg.finan.one/open |
Lỗi thường gặp
| Lỗi | Nguyên nhân | Giải pháp |
|---|---|---|
401 Unauthorized | Chữ ký không hợp lệ | Xác minh khóa bí mật, kiểm tra payload khớp chính xác với nội dung |
401 Timestamp expired | Dấu thời gian cũ hơn 30 giây | Sử dụng dấu thời gian Unix hiện tại, kiểm tra đồng bộ thời gian máy chủ |
401 Invalid client | Sai ID khách hàng | Xác minh giá trị x-client-id |
Xác minh chữ ký Webhook
Khi bạn nhận được webhook từ Finan, hãy xác minh tính xác thực bằng cách tính lại chữ ký:
func verifyWebhookSignature(secretKey, body, receivedSignature, timestamp string) bool {
expected := generateSignature(secretKey, "POST", "/your-webhook-path", body, timestamp)
return expected == receivedSignature
}
Sử dụng các header x-signature và x-timestamp từ yêu cầu webhook để xác minh.
Bộ sưu tập Postman
Nhập bộ sưu tập Postman của chúng tôi để kiểm thử nhanh:
Bước tiếp theo
- Quản lý tài khoản — Thiết lập tài khoản ngân hàng
- Cổng thanh toán — Tạo yêu cầu thanh toán