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.



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

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).



125
126
127
# File 'lib/tingee/client.rb', line 125

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



109
110
111
# File 'lib/tingee/client.rb', line 109

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.



99
100
101
# File 'lib/tingee/client.rb', line 99

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.



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

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:, account_number:, account_name:, identity:, mobile:, webhook_url:, account_type: "personal-account", is_notify_account_number: false) ⇒ Object

POST /v1/create-va — starts a manual 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). The bank then sends/pushes an OTP to mobile. Returns otpMethod. identity/mobile should never be persisted by the caller.

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.



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/tingee/client.rb', line 55

def create_va(bank_bin:, account_number:, account_name:, identity:, mobile:, webhook_url:,
               account_type: "personal-account", is_notify_account_number: false)
  payload = {
    accountType: , bankBin: bank_bin,
    accountNumber: , accountName: ,
    identity: identity, mobile: mobile,
    isNotifyAccountNumber: ,
    webhookUrl: webhook_url
  }
  payload[:shopId] = @config.shop_id if @config.shop_id
  post("/v1/create-va", payload)
end

#create_va_vcb(account_number:, mobile:, request_id: SecureRandom.uuid, bank_name: "VCB", merchant_id: nil, merchant_name: nil, merchant_address: nil, shop_id: nil, redirect_url: nil, webhook_url: nil, va_prefix: nil, va_suffix: nil, app_type: "baas", account_type: "personal-account") ⇒ Object

POST /v1/create-va — VCB personal-account variant (docs/tingee-vcb-personal-link.md). UNLIKE create_va above, VCB has no OTP confirm step: it returns a deepLink (vcbpartner://…) you open in VCB Digibank; the customer confirms there and the RESULT arrives asynchronously on your webhook_url as a webhook with status "confirm-va-success" | "confirm-va-failed".

request_id is echoed back on that webhook — pass and STORE your own to correlate (defaults to a fresh UUID otherwise). Optional params are only sent when given. Returns Tingee's data payload, an array: [deepLink].



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/tingee/client.rb', line 77

def create_va_vcb(account_number:, mobile:, request_id: SecureRandom.uuid, bank_name: "VCB",
                  merchant_id: nil, merchant_name: nil, merchant_address: nil, shop_id: nil,
                  redirect_url: nil, webhook_url: nil, va_prefix: nil, va_suffix: nil,
                  app_type: "baas", account_type: "personal-account")
  payload = {
    requestId: request_id, bankName: bank_name, accountNumber: ,
    accountType: , mobile: mobile, appType: app_type
  }
  payload[:merchantId]      = merchant_id      if merchant_id
  payload[:merchantName]    = merchant_name    if merchant_name
  shop_id ||= @config.shop_id # one shop per project — same grouping as create_va
  payload[:merchantAddress] = merchant_address if merchant_address
  payload[:shopId]          = shop_id          if shop_id
  payload[:redirectUrl]     = redirect_url     if redirect_url
  payload[:webhookUrl]      = webhook_url      if webhook_url
  payload[:vaPrefix]        = va_prefix        if va_prefix
  payload[:vaSuffix]        = va_suffix        if va_suffix
  post("/v1/create-va", payload)
end

#delete_va(bank_name:, va_account_number:) ⇒ Object

POST /v1/delete-va — starts an unlink. Params ride the QUERY STRING (not the JSON body) and identify the bank by bankName (Tingee's short bank CODE, e.g. "STB") — NOT bankBin. Verified live 2026-07-16; Tingee is inconsistent here (confirm-delete-va below takes the body instead). Returns confirmId.



117
118
119
# File 'lib/tingee/client.rb', line 117

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

#get_banksObject

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



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

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.



133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/tingee/client.rb', line 133

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.



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

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).



105
106
107
# File 'lib/tingee/client.rb', line 105

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