Apinganhang API v1

REST API xác thực giao dịch ngân hàng, quản lý tài khoản, kết nối OTP, và tích hợp cổng thanh toán tự động.

Thuộc tínhGiá trị
Base URLhttps://client.apinganhang.net/api/v1
Content-Typeapplication/json
AuthAuthorization: Bearer <token>
EncodingUTF-8
Health check: GET /api/v1/ping.php — không cần token, trả {"ok":true}

Xác thực

Tất cả endpoint (trừ ping.php) yêu cầu header Authorization: Bearer <token>.

Token nhận được từ endpoint POST /auth/login.php. Token không có thời hạn hết hạn — cookie được duy trì lâu dài trừ khi user đăng xuất hoặc admin reset token.

401 Unauthorized: Khi token sai hoặc không tồn tại, API trả HTTP 401. Client nên redirect về trang login.

Mã lỗi & Response format

Mọi response đều là JSON với field success (boolean).

HTTPÝ nghĩaVí dụ
200Thành công{"success":true,"data":{...}}
400Bad request / login bank thất bại{"success":false,"message":"..."}
401Chưa đăng nhập / token không hợp lệ
403Gói API hết hạn
404Không tìm thấy
405Method không hỗ trợ
422Thiếu / sai field
500Lỗi server

Rate Limit

Chưa áp dụng rate limiting cứng. Khuyến nghị polling giao dịch tối đa mỗi 10–30 giây. Bank cron cập nhật mỗi 1 phút.

Số dư ngân hàng — GET /accounts/balances.php

Endpoint gọn dành cho đối tác (site cá cược, cổng nạp...) lấy nhanh số dư các tài khoản ngân hàng của mình, phục vụ việc tự chọn ngân hàng công ty nhận tiền — ví dụ: luôn ưu tiên ngân hàng đang có số dư thấp nhất để dàn đều dòng tiền.

Khác gì accounts/index.php? index.php là endpoint của dashboard: trả rất nhiều field và có thể gọi live balance sang API ngân hàng (chậm, phụ thuộc ngân hàng). balances.php chỉ đọc số dư đã đồng bộ trong DB (cron poll ngân hàng chạy mỗi 1 phút) nên trả lời nhanh và ổn định — phù hợp gọi ngay lúc member bấm nạp tiền.

Xác thực

Bearer token như mọi endpoint /api/v1 khác: Authorization: Bearer <token>. Không có token → HTTP 401.

Query params (tất cả tuỳ chọn)

Tham sốMặc địnhÝ nghĩa
banksLọc theo mã ngân hàng, phân tách bằng dấu phẩy. Chấp nhận alias (VCB, MB, VIETINBANK...). VD: banks=ACB,BIDV
accountsLọc chính xác theo cặp BANK:SỐ_TK, phân tách bằng dấu phẩy. VD: accounts=ACB:68868983,BIDV:96247TPH8866. Cũng chấp nhận chỉ số tài khoản: accounts=68868983. So khớp bỏ qua khoảng trắng/gạch nối.
active_only1Chỉ lấy tài khoản đang bật và không bị need_relogin. Đặt 0 để lấy hết.
include_crypto01 = lấy cả ví TRON USDT / BEP20 USDT / BINANCE / BTC / ETH.
sortbalance_ascbalance_asc | balance_desc | none
picklowest = trả thêm data.selected là tài khoản có số dư thấp nhất. Nếu nhiều tài khoản bằng nhau thì chọn ngẫu nhiên trong nhóm thấp nhất.

Ví dụ

curl -H "Authorization: Bearer $TOKEN" \
  "https://client.apinganhang.net/api/v1/accounts/balances.php?accounts=ACB:68868983,ACB:22769257,BIDV:96247TPH8866&pick=lowest"
{
  "success": true,
  "message": "Balances loaded",
  "data": {
    "items": [
      {
        "id": 80,
        "source_table": "account_acb",
        "bank_code": "ACB",
        "account_number": "68868983",
        "holder_name": "TUNG VU THANH",
        "active": true,
        "connected": true,
        "need_relogin": false,
        "last_error": "",
        "sync_source": "direct",
        "balance": 31321,
        "balance_source": "last_transaction",
        "transaction_count": 223,
        "last_transaction_at": 1787032562
      }
    ],
    "count": 3,
    "selected": { "bank_code": "ACB", "account_number": "68868983", "balance": 31321 },
    "generated_at": 1787000000
  }
}

Ý nghĩa balance_source

Giá trịÝ nghĩa
last_transactionSố dư thật do ngân hàng trả về ở giao dịch mới nhất (balance_after). Áp dụng cho tài khoản sync_source = direct (cron poll thẳng ngân hàng).
calculatedSố dư tính = tổng tiền vào − tổng tiền ra. Áp dụng cho tài khoản sync_source = webhook (SePay/Casso — không có snapshot số dư), hoặc tài khoản chưa từng có snapshot.

Use case: chọn ngân hàng công ty có số dư thấp nhất

  1. Site lấy danh sách ngân hàng công ty đang mở trong admin của mình.
  2. Gọi balances.php?accounts=<danh sách BANK:STK>&pick=lowest.
  3. Dùng data.selected làm ngân hàng nhận tiền cho lệnh nạp; nếu API lỗi/timeout thì fallback chọn ngẫu nhiên như cũ.
  4. Nên cache kết quả 30–60 giây phía site — cron ngân hàng cũng chỉ cập nhật mỗi 1 phút.
Lưu ý: tài khoản chưa phát sinh giao dịch nào sẽ có balance = 0transaction_count = 0. Nếu không muốn nó luôn được chọn làm "thấp nhất", hãy lọc bằng accounts= đúng các STK đang thực sự dùng để nhận tiền.

USDT Crypto — Tổng quan

Hệ thống hỗ trợ nhận tiền tự động qua 2 mạng USDT: TRC20 (Tron)BEP20 (BSC). Cron job chạy mỗi 1 phút để quét giao dịch mới, quy đổi sang VND và ghi vào logs_history.

Tỷ giá USDT/VND — Binance P2P

Tỷ giá được lấy tự động từ Binance P2P API — không cần cấu hình thủ công:

  1. Gọi API P2P Binance, lấy top 10 merchant đang đăng bán USDT nhận VND (SELL order, amount ≥ 10,000,000 VND).
  2. Tính trung bình cộng các mức giá (chỉ lấy mức ≥ 20,000 VND/USDT để loại outlier).
  3. Kết quả làm tròn xuống (floor) → ví dụ: 25,740 VND/USDT.
  4. Cache 5 phút — tự refresh mỗi lần cache hết hạn.
  5. Fallback 25,000 VND/USDT nếu Binance không phản hồi.
Lưu ý: Tỷ giá này được dùng chung cho cả TRON USDT và BEP20 USDT. Giao dịch được lưu amount (VND) và usdt_amount (USDT thực nhận) song song.

Luồng tổng quát

  1. User thêm địa chỉ ví USDT vào hệ thống tại app.apinganhang.net → Tài khoản → Thêm ví USDT.
  2. Cron job chạy mỗi 1 phút, gọi API blockchain để lấy giao dịch 14 ngày gần nhất.
  3. Lọc chỉ giao dịch vào ví (to === địa_chỉ_ví), bỏ qua giao dịch ra.
  4. Dedup theo tx_hash + method + ABS(amount VND) — mỗi giao dịch chỉ ghi 1 lần.
  5. Quy đổi USDT → VND theo tỷ giá Binance P2P hiện tại, ghi vào logs_history.
  6. Gửi thông báo Telegram + dispatch webhook transaction.received về site.

USDT TRC20 — Tron Network

Thuộc tínhGiá trị
MạngTron (TRC20)
method trong logs_historyTRON USDT
Smart contractTR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
API nguồnTronGrid (api.trongrid.io/v1/accounts/{address}/transactions/trc20)
Cửa sổ quét200 tx gần nhất, tối đa 14 ngày
Cron intervalMỗi 1 phút
Định dạng địa chỉBắt đầu bằng T, 34 ký tự (base58)

Cấu hình TronGrid API Key

TronGrid miễn phí nhưng giới hạn rate nếu không có API key. Khi thiếu key, cron sẽ gặp lỗi "Không lấy được lịch sử từ TronGrid".

  1. Đăng ký tại trongrid.io → lấy API key miễn phí.
  2. Vào app.apinganhang.net → Cài đặt → nhập TronGrid API Key.
  3. Hệ thống tự thêm header TRON-PRO-API-KEY: <key> vào mọi request TronGrid.

Thêm ví USDT Tron

Gọi API POST /accounts/store.php (hoặc dùng giao diện app) với:

{
  "bank_code": "TRON USDT",
  "account_number": "THa1vvKpShrkGNo2XjZsDVKzQdHwSMjfCp",
  "account_name": "Ví USDT Tron chính"
}

Response giao dịch USDT Tron (trong logs_history)

{
  "id": 1234,
  "bank":        "TRON USDT",
  "trans_id":    "abc123def456...",
  "amount":      2574000,
  "usdt_amount": 100.0,
  "type":        "in",
  "description": "Nhận 100 USDT (2,574,000 VND) từ TXabc...",
  "account":     "THa1vvKpShrkGNo2XjZsDVKzQdHwSMjfCp",
  "transaction_time": 1751234567,
  "status":      "SUCCESS"
}
trans_id = Transaction Hash trên blockchain Tron (64 ký tự hex). Có thể tra cứu tại tronscan.org.

USDT BEP20 — Binance Smart Chain

Thuộc tínhGiá trị
MạngBSC (BEP20)
method trong logs_historyBEP20 USDT
Smart contract0x55d398326f99059ff775485246999027b3197955
API nguồnBscScan / Etherscan v2 (api.etherscan.io/v2/api?chainid=56)
Cửa sổ quét100 tx gần nhất, tối đa 14 ngày
Cron intervalMỗi 1 phút
Định dạng địa chỉBắt đầu bằng 0x, 42 ký tự (EVM-compatible)

Cấu hình BscScan API Key (bắt buộc)

BEP20 bắt buộc phải có BscScan API key — không có key sẽ không lấy được giao dịch.

  1. Đăng ký tại bscscan.com/myapikey → tạo API key miễn phí.
  2. Khi thêm ví BEP20 tại app.apinganhang.net, nhập API key vào trường Password / API Key.
  3. API key được mã hoá AES-256-GCM trước khi lưu DB.

Thêm ví USDT BEP20

{
  "bank_code":      "BEP20 USDT",
  "account_number": "0xAbCd1234...",
  "account_name":   "Ví USDT BEP20 chính",
  "password":       "<BscScan API Key>"
}

Response giao dịch USDT BEP20 (trong logs_history)

{
  "id": 5678,
  "bank":        "BEP20 USDT",
  "trans_id":    "0xabc123...",
  "amount":      2574000,
  "usdt_amount": 100.0,
  "type":        "in",
  "description": "Nhận 100 USDT (2,574,000 VND) từ 0xsender...",
  "account":     "0xAbCd1234...",
  "transaction_time": 1751234567,
  "status":      "SUCCESS"
}
trans_id = Transaction Hash trên BSC. Tra cứu tại bscscan.com.

Webhook — Giao dịch USDT

Khi cron phát hiện giao dịch USDT mới hợp lệ, hệ thống dispatch event transaction.received về webhook URL đã cấu hình. Payload tương tự giao dịch ngân hàng nhưng bổ sung field usdt_amount:

FieldTypeMô tả
eventstring"transaction.received"
bankstring"TRON USDT" hoặc "BEP20 USDT"
trans_idstringTransaction hash (tx_hash) trên blockchain
amountintegerSố tiền VND (đã quy đổi theo tỷ giá Binance P2P)
usdt_amountfloatSố USDT thực tế nhận được (ví dụ: 100.5)
typestring"in" — chỉ nhận giao dịch vào ví
descriptionstringMô tả: "Nhận 100 USDT (2,574,000 VND) từ 0xabc..."
accountstringĐịa chỉ ví nhận (của mình)
balance_afternullLuôn null với USDT (không query số dư sau GD)

Ví dụ payload webhook USDT Tron

{
  "event":        "transaction.received",
  "bank":         "TRON USDT",
  "trans_id":     "f3a4b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5",
  "amount":       2574000,
  "usdt_amount":  100.0,
  "type":         "in",
  "description":  "Nhận 100 USDT (2,574,000 VND) từ TXsenderAddress...",
  "account":      "THa1vvKpShrkGNo2XjZsDVKzQdHwSMjfCp",
  "balance_after": null
}

Xác minh thanh toán USDT

Luồng xác minh cho site nhận USDT — so sánh usdt_amount (không phải amount VND, vì tỷ giá biến động):

  1. Tạo đơn, hiển thị địa chỉ ví USDT + số USDT cần chuyển (ví dụ: 100 USDT).
  2. Khách gửi đúng số USDT đến địa chỉ ví trên Tron/BSC.
  3. Webhook nhận event transaction.received, kiểm tra usdt_amount === 100bank === "TRON USDT".
  4. Hoặc polling: GET /transactions/index.php?bank=TRON+USDT&limit=10, tìm usdt_amount khớp.
Khuyến nghị: So sánh theo usdt_amount (dung sai ±0.01 USDT) thay vì amount VND, vì tỷ giá thay đổi giữa lúc tạo đơn và lúc khách chuyển tiền.

BTC / ETH — Tổng quan

Ngoài USDT (TRC20/BEP20), hệ thống hỗ trợ nhận tiền tự động qua 2 đồng coin gốc (native coin): Bitcoin (BTC)Ethereum (ETH). Cron job chạy mỗi 1 phút để quét giao dịch mới, quy đổi sang VND và ghi vào logs_history — cùng cơ chế với USDT/ngân hàng.

Tỷ giá BTC/USDT, ETH/USDT

Lấy trực tiếp từ Binance Spot API (GET /api/v3/ticker/price), cache 60 giây. Sau đó quy đổi USDT → VND theo tỷ giá Binance P2P hiện tại (giống USDT TRC20/BEP20).

Luồng tổng quát

  1. User thêm ví BTC/ETH tại app.apinganhang.net → Tài khoản → Thêm tài khoản.
  2. Cron job chạy mỗi 1 phút, gọi API blockchain lấy giao dịch tối đa 14 ngày gần nhất.
  3. Lọc chỉ giao dịch vào ví (nhận tiền), bỏ qua giao dịch ra.
  4. Dedup theo trans_id + method + ABS(amount VND) — mỗi giao dịch chỉ ghi 1 lần.
  5. Quy đổi ra VND, ghi logs_history, gửi Telegram + dispatch webhook transaction.received.

Bitcoin (BTC)

Thuộc tínhGiá trị
MạngBitcoin mainnet
method trong logs_historyBTC
API nguồnBlockstream Esplora API (miễn phí, không cần API key)
Cửa sổ quét50 tx gần nhất, tối đa 14 ngày
Cron intervalMỗi 1 phút
Định dạng địa chỉBắt đầu bằng 1, 3 (Base58Check) hoặc bc1 (Bech32/SegWit)
Không cần API key. Blockstream miễn phí, không cần cấu hình gì thêm ngoài địa chỉ ví.

Thêm ví BTC

Gọi API POST /accounts/store.php (hoặc dùng giao diện app) với:

{
  "bank_code": "BTC",
  "account_number": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "account_name": "Ví BTC chính"
}

Chỉ cần địa chỉ ví công khai — không cần private key hay API key, hệ thống chỉ theo dõi giao dịch nhận (không thể và không cần quyền chuyển tiền đi).

Response giao dịch BTC (trong logs_history)

{
  "id": 9001,
  "bank":        "BTC",
  "trans_id":    "6d292792a6b3d1078bd6edcc15e30a5f9d123198db1f736ebb1c003948b20f8",
  "amount":      167990,
  "type":        "in",
  "description": "Nhận 0.0001 BTC (~6.51 USDT / 167,990 VND)",
  "account":     "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "transaction_time": 1786304133,
  "status":      "SUCCESS"
}
trans_id = Transaction Hash (txid) trên blockchain Bitcoin. Tra cứu tại blockstream.info.

Ethereum (ETH)

Thuộc tínhGiá trị
MạngEthereum mainnet
method trong logs_historyETH
API nguồnEtherscan API v2 (api.etherscan.io/v2/api?chainid=1)
Cửa sổ quét100 tx gần nhất, tối đa 14 ngày
Cron intervalMỗi 1 phút
Định dạng địa chỉBắt đầu bằng 0x, 42 ký tự (EVM-compatible)

Cấu hình Etherscan API Key (bắt buộc)

ETH bắt buộc phải có Etherscan API key — không có key sẽ trả lỗi "Thiếu Etherscan API key" và không lấy được giao dịch.

  1. Đăng ký tại etherscan.io/myapikey → tạo API key miễn phí.
  2. Khi thêm ví ETH tại app.apinganhang.net, nhập API key vào trường Password / API Key.
  3. API key được mã hoá AES-256-GCM trước khi lưu DB (giống BEP20/BSC).

Thêm ví ETH

{
  "bank_code":      "ETH",
  "account_number": "0xAbCd1234...",
  "account_name":   "Ví ETH chính",
  "password":       "<Etherscan API Key>"
}

Response giao dịch ETH (trong logs_history)

{
  "id": 9002,
  "bank":        "ETH",
  "trans_id":    "0xabc123...",
  "amount":      2574000,
  "type":        "in",
  "description": "Nhận 0.052 ETH (~100 USDT / 2,574,000 VND)",
  "account":     "0xAbCd1234...",
  "transaction_time": 1751234567,
  "status":      "SUCCESS"
}
trans_id = Transaction Hash trên Ethereum. Tra cứu tại etherscan.io.

Webhook — Giao dịch BTC/ETH

Khi cron phát hiện giao dịch BTC/ETH mới hợp lệ, hệ thống dispatch event transaction.received về webhook URL đã cấu hình — cùng cơ chế với USDT/ngân hàng.

FieldTypeMô tả
eventstring"transaction.received"
bankstring"BTC" hoặc "ETH"
trans_idstringTransaction hash trên blockchain tương ứng
amountintegerSố tiền VND (đã quy đổi qua USDT theo tỷ giá Binance P2P)
btc_amount / eth_amountfloatSố BTC/ETH thực nhận (chỉ có ở giao dịch tương ứng)
typestring"in" — chỉ nhận giao dịch vào ví
accountstringĐịa chỉ ví nhận (của mình)
balance_afternullLuôn null (không query số dư sau GD)

Xác minh thanh toán BTC/ETH

  1. Tạo đơn, hiển thị địa chỉ ví BTC/ETH + số tiền VND cần thanh toán.
  2. Khách gửi BTC/ETH đến đúng địa chỉ ví trên đúng mạng (Bitcoin mainnet / Ethereum mainnet).
  3. Webhook nhận event transaction.received, kiểm tra bank === "BTC" hoặc "ETH" + amount.
  4. Hoặc polling: GET /transactions/index.php?bank=BTC&limit=10 (tương tự cho ETH).
Khuyến nghị: Do BTC/ETH biến động giá liên tục, nên chấp nhận dung sai ±2–3% so với số tiền VND yêu cầu, hoặc yêu cầu khách xác nhận số coin đã gửi qua UI trước khi đối soát.

Use Case: Xác minh thanh toán tự động

Luồng xác minh giao dịch cho website bán hàng / SaaS / game topup.

  1. Tạo đơn hàng trên website với order_code duy nhất + số tiền.
  2. Hiển thị QR / thông tin CK — yêu cầu khách ghi order_code vào nội dung.
  3. Polling API mỗi 10-30s: GET /transactions/index.php?search={order_code}&type=INCOME&limit=5
  4. Khớp giao dịch: so amount + description chứa order_code + type=in
  5. Cập nhật đơn paid khi tìm thấy.

Use Case: Webhook Callback

Nhận push notification thay vì polling. Server gửi callback khi có giao dịch mới.

Payload mẫu

FieldTypeMô tả
eventstring"transaction.confirmed"
order_codestringMã đơn hàng
amountintegerSố tiền
bankstringNgân hàng
trans_idstringMã GD ngân hàng
signstringHMAC-SHA256 chữ ký
Bảo mật: Luôn verify sign = hmac_sha256(secret, order_code|amount|trans_id|paid_at) trước khi xử lý.

Use Case: OTP Flow — Kết nối BIDV trong app

  1. User nhập login_id + password + account_number BIDV SmartBanking.
  2. Gọi Step 1: POST /accounts/connect.php với action: "request_otp".
  3. Hệ thống login BIDV, BIDV gửi SMS OTP đến SĐT user.
  4. User nhập OTP vào ô trên app.
  5. Gọi Step 2: POST /accounts/connect.php với action: "verify_otp" + otp: "123456".
  6. Nếu đúng, tài khoản được lưu với session đã xác thực, cron tự động đồng bộ giao dịch.

Demo trực tiếp

Test API ngay từ trình duyệt.

Lấy thông tin tài khoản + token hiện tại.

* Giữ nguyên login_id/password/account từ Step 1

Lấy cấu hình site (logo, title, contact).

// Kết quả sẽ hiện ở đây...

Base URL

https://client.apinganhang.net/api/v1

Auth Header

Authorization: Bearer eyJhbGci...

Login

curl -X POST https://client.apinganhang.net/api/v1/auth/login.php \
  -H "Content-Type: application/json" \
  -d '{"username":"demo","password":"123456"}'

200 Response:

{
  "success": true,
  "token": "eyJhbGci...",
  "user": {
    "id": 5,
    "username": "demo",
    "email": "[email protected]",
    "money": 150000,
    "time_momo": 1788998400
  }
}

Profile

curl https://client.apinganhang.net/api/v1/auth/profile.php \
  -H "Authorization: Bearer <token>"

200 Response:

{
  "success": true,
  "data": {
    "id": 5,
    "username": "demo",
    "email": "[email protected]",
    "money": 150000,
    "time_momo": 1788998400,
    "token": "eyJhbGci...",
    "id_telegram": 123456789
  }
}

List Accounts

curl "https://client.apinganhang.net/api/v1/accounts/index.php?bank=MBBANK" \
  -H "Authorization: Bearer <token>"

200 Response:

{
  "success": true,
  "data": [
    {
      "id": 268,
      "bank_code": "MBBANK",
      "account_number": "0368492618",
      "holder_name": "NGUYEN VAN A",
      "balance": 19437345,
      "active": 1
    }
  ]
}

Connect OTP — Step 1

curl -X POST https://client.apinganhang.net/api/v1/accounts/connect.php \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "request_otp",
    "bank_code": "BIDV",
    "login_id": "bidv_user",
    "password": "bidv_pass",
    "account_number": "8877172056"
  }'

200

{
  "success": true,
  "step": "otp_required",
  "message": "Mã OTP đã được gửi đến SĐT"
}

Connect OTP — Step 2

curl -X POST https://client.apinganhang.net/api/v1/accounts/connect.php \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "verify_otp",
    "bank_code": "BIDV",
    "login_id": "bidv_user",
    "password": "bidv_pass",
    "account_number": "8877172056",
    "otp": "123456"
  }'

200

{
  "success": true,
  "step": "done",
  "message": "Xác thực OTP thành công",
  "data": { "id": 68, "bank_code": "BIDV" }
}

Transactions

# 20 giao dịch mới nhất
curl "https://client.apinganhang.net/api/v1/transactions/index.php?limit=20" \
  -H "Authorization: Bearer <token>"

# Lọc theo bank + ngày
curl "https://client.apinganhang.net/api/v1/transactions/index.php?\
bank=MBBANK&date_from=2026-04-01&date_to=2026-04-16" \
  -H "Authorization: Bearer <token>"

# Tìm kiếm
curl "https://client.apinganhang.net/api/v1/transactions/index.php?\
search=INV2026001&type=INCOME" \
  -H "Authorization: Bearer <token>"

200

{
  "success": true,
  "data": [
    {
      "id": 25533,
      "amount": 1000000,
      "bank": "MBBANK",
      "ref_no": "FT26105370000088",
      "description": "NGUYEN VAN A CK",
      "type": "in",
      "account": "0368492618",
      "balance_after": 19437345,
      "transaction_time": "15/04/2026 09:30:00",
      "created_at": 1776395400
    }
  ],
  "pagination": {
    "page": 1, "limit": 20,
    "total": 150, "total_pages": 8
  },
  "totals": {
    "total_in": 5000000,
    "total_out": 2000000,
    "net": 3000000
  }
}

Xác minh thanh toán (PHP)

<?php
$token = 'YOUR_TOKEN';
$base  = 'https://client.apinganhang.net/api/v1';
$order = 'INV20260416001';
$expect = 500000;

$url = "$base/transactions/index.php?" .
  http_build_query([
    'search' => $order,
    'type'   => 'INCOME',
    'limit'  => 10,
  ]);

$ch = curl_init($url);
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer $token",
  ],
  CURLOPT_TIMEOUT => 15,
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);

foreach ($res['data'] ?? [] as $txn) {
  if (
    abs($txn['amount']) === $expect &&
    stripos($txn['description'], $order) !== false
  ) {
    echo "Paid! ref={$txn['ref_no']}\n";
    break;
  }
}
?>

Xác minh thanh toán (Node.js)

const axios = require('axios');
const TOKEN = 'YOUR_TOKEN';
const BASE = 'https://client.apinganhang.net/api/v1';

async function verify(order, amount, bank) {
  const { data } = await axios.get(
    `${BASE}/transactions/index.php`,
    {
      params: { search: order, type: 'INCOME', limit: 10, bank },
      headers: { Authorization: `Bearer ${TOKEN}` },
    }
  );
  return (data.data || []).find(t =>
    Math.abs(t.amount) === amount &&
    (t.description || '').toLowerCase()
      .includes(order.toLowerCase())
  );
}

verify('INV20260416001', 500000, 'MBBANK')
  .then(m => console.log(m ? 'Paid' : 'Pending'));

Webhook Callback Payload

POST https://your-site.com/payment/callback
Content-Type: application/json

{
  "event": "transaction.confirmed",
  "order_code": "INV20260416001",
  "amount": 500000,
  "bank": "MBBANK",
  "trans_id": "FT26105370000088",
  "account": "0368492618",
  "paid_at": 1776395400,
  "sign": "hmac_sha256(secret, ...)"
}

Verify Webhook Signature (PHP)

<?php
$secret = 'YOUR_SECRET';
$body = json_decode(
  file_get_contents('php://input'), true
);
$expected = hash_hmac('sha256',
  implode('|', [
    $body['order_code'],
    $body['amount'],
    $body['trans_id'],
    $body['paid_at'],
  ]),
  $secret
);
if (!hash_equals($expected, $body['sign'])) {
  http_response_code(403);
  die('Invalid signature');
}
echo json_encode(['received' => true]);
?>

Site Settings

curl https://client.apinganhang.net/api/v1/services/site-settings.php \
  -H "Authorization: Bearer <token>"

200

{
  "success": true,
  "data": {
    "title": "API Ngân hàng",
    "description": "Hệ thống API ngân hàng...",
    "logo": "https://client.apinganhang.net/public/assets/img/logo.png",
    "hotline": "0123456789",
    "link_facebook": "https://...",
    "link_zalo": ""
  }
}

Bank Packages

curl https://client.apinganhang.net/api/v1/services/packages.php \
  -H "Authorization: Bearer <token>"

200

{
  "success": true,
  "data": [
    {
      "id": 1,
      "bank_code": "MBBANK",
      "name": "MB Bank API",
      "price": 50000,
      "features": "Thông báo GD, lấy số dư",
      "active": 1
    }
  ]
}