Class: Tingee::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/tingee/client.rb

Overview

HTTP + endpoint methods. Only live-verified endpoints are wrapped — no speculative "complete SDK". Responses use a uniform message, data envelope EXCEPT get-banks, which returns a bare array (verified live).

Instance Method Summary collapse

Constructor Details

#initialize(config = Tingee.config, read_timeout: 90) ⇒ Client

read_timeout: web requests keep the 90s default (stay under your proxy's response timeout); background jobs should pass a larger value — bank-side OTP verification on confirm_va can take minutes.



13
14
15
16
17
# File 'lib/tingee/client.rb', line 13

def initialize(config = Tingee.config, read_timeout: 90)
  @config = config
  @read_timeout = read_timeout
  @config.validate!
end

Instance Method Details

#confirm_delete_va(bank_bin:, confirm_id:, otp_number:) ⇒ Object

POST /v1/confirm-delete-va — finishes the unlink with the bank's OTP. Params ride the BODY this time (unlike delete_va's query string above), and the bank is identified by bankBin here — bankName gets ignored and Tingee then fails with "Lỗi hệ thống phương thức xác thực" (seen live 2026-07-17).



136
137
138
# File 'lib/tingee/client.rb', line 136

def confirm_delete_va(bank_bin:, confirm_id:, otp_number:)
  post("/v1/confirm-delete-va", { bankBin: bank_bin, confirmId: confirm_id, otpNumber: otp_number })
end

#confirm_register_notify(bank_bin:, confirm_id:, otp_number:) ⇒ Object



111
112
113
# File 'lib/tingee/client.rb', line 111

def confirm_register_notify(bank_bin:, confirm_id:, otp_number:)
  post("/v1/confirm-register-notify", { bankBin: bank_bin, confirmId: confirm_id, otpNumber: otp_number })
end

#confirm_va(bank_bin:, confirm_id:, otp_number:) ⇒ Object

POST /v1/confirm-va — finishes the link with the bank's OTP. Returns accountType, accountNumber (real), vaAccountNumber (routing key), shopId.



101
102
103
# File 'lib/tingee/client.rb', line 101

def confirm_va(bank_bin:, confirm_id:, otp_number:)
  post("/v1/confirm-va", { bankBin: bank_bin, confirmId: confirm_id, otpNumber: otp_number })
end

POST /v1/create-bank-link-session — returns the SDK URL string (data). No merchant_id is needed for the default merchant; pass one only for a sub-merchant.



26
27
28
29
30
31
32
33
34
# File 'lib/tingee/client.rb', line 26

def create_bank_link_session(merchant_id: nil, redirect_url: nil, allowed_banks: nil, bank_name: nil, shop_id: nil)
  payload = { type: "bank-link" }
  payload[:merchantId]   = merchant_id          if merchant_id
  payload[:redirectUrl]  = redirect_url         if redirect_url
  payload[:allowedBanks] = Array(allowed_banks) if allowed_banks
  payload[:bankName]     = bank_name            if bank_name
  payload[:shopId]       = shop_id              if shop_id
  post("/v1/create-bank-link-session", payload)
end

#create_va(bank_bin:, webhook_url:, account_number: nil, account_name: nil, identity: nil, mobile: nil, account_type: "personal-account", is_notify_account_number: false, app_type: nil, redirect_url: nil, request_id: nil, merchant_id: nil, merchant_name: nil, merchant_address: nil, shop_id: nil, va_prefix: nil, va_suffix: nil) ⇒ Object

POST /v1/create-va — starts a bank link (the raw API chain, usable when Tingee's hosted JS SDK is unavailable or broken — docs/tingee-api-reference.md §create-va, live-verified). ONE method serves all three bank shapes; which one you get is decided by the bank, not by a different endpoint:

OTP banks (STB, ACB, MBB, …) — pass account_number/account_name/identity/mobile.
The bank sends/pushes an OTP to `mobile`. Returns {confirmId, otpMethod};
finish with #confirm_va.
Redirect-authorize banks (VCB) — additionally pass app_type: "baas" +
redirect_url + request_id. Returns an authorizeLink/deepLink instead of
sending an OTP; the owner confirms in the bank's app and the result arrives
asynchronously on webhook_url as {status: "confirm-va-success"|"confirm-va-failed"}.
There is NO confirm_va step for these.
No-account-field banks (TPB, doc-sourced) — pass neither account nor identity
fields; the owner picks the account on the bank's own web. TPB returns NO
confirmId at all, so the request_id YOU send is the only key that can
correlate the settle webhook back to your pending request. Always pass and
STORE your own request_id for any redirect-authorize flow.

Every optional field is omitted from the payload when nil, so a bank only ever receives the params its contract actually defines (VCB tolerates and overrides what it doesn't use — verified live; TPB's contract has none of them).

is_notify_account_number: FALSE is the DEFAULT because it is the mode proved end-to-end — a real VietQR transfer to the linked real account fired the webhook on a notify=false link (2026-07-16). notify=true is documented as "watch the real account" and MAY also work, but was never confirmed to fire on a plain transfer; do not switch the default to true without a real-transfer test on a true link.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/tingee/client.rb', line 71

def create_va(bank_bin:, webhook_url:, account_number: nil, account_name: nil,
               identity: nil, mobile: nil, account_type: "personal-account",
               is_notify_account_number: false, app_type: nil, redirect_url: nil,
               request_id: nil, merchant_id: nil, merchant_name: nil,
               merchant_address: nil, shop_id: nil, va_prefix: nil, va_suffix: nil)
  payload = {
    accountType: , bankBin: bank_bin,
    isNotifyAccountNumber: ,
    webhookUrl: webhook_url
  }
  payload[:accountNumber]   =    if 
  payload[:accountName]     =      if 
  payload[:identity]        = identity         if identity
  payload[:mobile]          = mobile           if mobile
  payload[:appType]         = app_type         if app_type
  payload[:redirectUrl]     = redirect_url     if redirect_url
  payload[:requestId]       = request_id       if request_id
  payload[:merchantId]      = merchant_id      if merchant_id
  payload[:merchantName]    = merchant_name    if merchant_name
  payload[:merchantAddress] = merchant_address if merchant_address
  payload[:vaPrefix]        = va_prefix        if va_prefix
  payload[:vaSuffix]        = va_suffix        if va_suffix
  # One shop per project; an explicit arg wins over the configured default.
  shop_id ||= @config.shop_id
  payload[:shopId]          = shop_id          if shop_id
  post("/v1/create-va", payload)
end

#delete_va(bank_bin:, va_account_number:) ⇒ Object

POST /v1/delete-va — starts an unlink. Params ride the QUERY STRING (not the JSON body); the bank is identified by bankBin, per the documented contract.

A bankName variant (Tingee's short bank CODE, e.g. "STB") also returns a confirmId AND triggers the bank's OTP, so it looks like it worked — but the session it creates cannot be confirmed: #confirm_delete_va then 400s with "Lỗi hệ thống phương thức xác thực" (seen live 2026-07-17). Do not reintroduce it. Unlink is the only way to stop Tingee's per-webhook billing, so an unlink that silently cannot complete is expensive.

Returns confirmId (OTP banks). Bank-shape variations — TPB answers with an authorizeLink, VCB returns {} because it detaches immediately — are the caller's to branch on; see docs/tingee-api-reference.md §6.



128
129
130
# File 'lib/tingee/client.rb', line 128

def delete_va(bank_bin:, va_account_number:)
  request(:post, "/v1/delete-va", query: { bankBin: bank_bin, vaAccountNumber:  })
end

#get_banksObject

GET /v1/get-banks — returns the bare bank array (the supported-bank/BIN map).



20
21
22
# File 'lib/tingee/client.rb', line 20

def get_banks
  get("/v1/get-banks")
end

#get_transactions(start_time:, end_time:, filter: nil, skip_count: nil, max_result_count: nil, merchant_id: nil, shop_ids: nil, va_account_numbers: nil, bank_bin: nil) ⇒ Object

POST /v1/transaction/get-paging — transaction history. start_time/end_time are required, format "yyyyMMddHHmmss" (UTC+7); Tingee caps each query at a 10-day window (over that it returns an error — not enforced here). Optional params are only sent when given. Returns items.



144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/tingee/client.rb', line 144

def get_transactions(start_time:, end_time:, filter: nil, skip_count: nil, max_result_count: nil,
                     merchant_id: nil, shop_ids: nil, va_account_numbers: nil, bank_bin: nil)
  payload = { startTime: start_time, endTime: end_time }
  payload[:filter]           = filter                   if filter
  payload[:skipCount]        = skip_count               if skip_count
  payload[:maxResultCount]   = max_result_count         if max_result_count
  payload[:merchantId]       = merchant_id              if merchant_id
  payload[:shopIds]          = Array(shop_ids)          if shop_ids
  payload[:vaAccountNumbers] = Array() if 
  payload[:bankBin]          = bank_bin                 if bank_bin
  post("/v1/transaction/get-paging", payload)
end

#get_va_paging(merchant_id: nil) ⇒ Object

POST /v1/get-va-paging — returns items of linked virtual accounts.



37
38
39
40
41
# File 'lib/tingee/client.rb', line 37

def get_va_paging(merchant_id: nil)
  payload = {}
  payload[:merchantId] = merchant_id if merchant_id
  post("/v1/get-va-paging", payload)
end

#register_notify(bank_bin:, va_account_number:) ⇒ Object

POST /v1/register-notify — ACB only, run once right after confirm_va succeeds. Returns confirmId for a second OTP round (see #confirm_register_notify).



107
108
109
# File 'lib/tingee/client.rb', line 107

def register_notify(bank_bin:, va_account_number:)
  post("/v1/register-notify", { vaAccountNumber: , bankBin: bank_bin })
end