Tino Developers
View .md

No-code integrations

TinoPay ships four integrations configured in the TinoPay web app (menu Tích hợp, Integrations), with no endpoint to build: Google Sheets writes transactions to a spreadsheet; the WordPress / WooCommerce plugin, the HostBill module and Sapo show a VietQR code and confirm orders and invoices automatically when the customer pays. For a deeper integration with your own system, see webhooks and the public API.

Updated: 2026-10-25. Source: the TinoPay documentation, no-code integrations section.

Labels below are the web app's Vietnamese labels, with an English gloss in parentheses.

The three store integrations match transactions by the payment code in the transfer description: the prefix set in the TinoPay web app (Cài đặt → Cấu trúc mã thanh toán, Settings → Payment code structure) must match the prefix configured in the plugin, module or store.

Google Sheets

TinoPay writes every matching transaction to one of your Google spreadsheets as soon as the bank reports it; handy for accountants and shop owners who track cash flow in a spreadsheet. TinoPay batches transactions for about 10 seconds and writes several rows at once to stay within the Google Sheets API quota, so new rows usually appear after a few seconds rather than instantly.

You need a Google account (Gmail or Google Workspace) and at least one bank account connected to TinoPay.

Setting up Google Sheets

Menu Tích hợp → Google Sheets → Thêm tích hợp (Add integration), four steps:

  1. Google account: the first time, click Đăng nhập bằng Google (Sign in with Google) and pick the account holding the spreadsheet; if already linked, pick it from the list or click Liên kết tài khoản Google khác (Link another Google account). Google's consent screen has a Google Drive access checkbox that is unchecked by default: you must tick it, or TinoPay cannot create or write any spreadsheet.
  2. Spreadsheet: Tạo mới bảng tính (Create a new spreadsheet: TinoPay creates it with the right columns; name it and pick a folder in My Drive) or Chọn bảng tính có sẵn (Use an existing spreadsheet: TinoPay adds the header row if the sheet is empty). Pick the sheet that receives data and the option Tự động xóa giao dịch cũ sau (Delete old transactions after): Không xóa (never: keep everything, the spreadsheet keeps growing), 60 minutes, 4 hours, 8 hours or 1 day (rows older than that are deleted; cleanup runs every 10 minutes).
  3. Scope: Tất cả ngân hàng (All banks: every account, including ones added later) or Tùy chỉnh ngân hàng (Custom: only the accounts you pick, optionally filtered by virtual account; picking no virtual account means every transaction of that account). Transaction type: all, incoming, outgoing. Ẩn số dư giao dịch (Hide balance): leaves the Lũy kế (running balance) column empty, for spreadsheets you share with others.
  4. Confirm: give it a memorable name, check the summary and click Lưu tích hợp (Save integration). From then on every matching transaction is written to the spreadsheet.

Rows deleted from the spreadsheet stay in TinoPay: look them up in menu Giao dịch → Lịch sử giao dịch (Transactions → Transaction history) or through the transaction history API.

Spreadsheet columns

TinoPay writes exactly 10 columns in this order (headers in Vietnamese):

ColumnHeaderContent
ANgân hàngfull bank name
BNgày giao dịchtransaction date, YYYY-MM-DD HH:MM:SS
CSố tài khoảnbank account number that received or paid
DTài khoản phụvirtual account number, empty when the transaction did not use one
ECode TTpayment code extracted from the transfer description with your payment code structure; empty when nothing matches
FNội dung thanh toántransfer description
GLoạiTiền vào (incoming) or Tiền ra (outgoing)
HSố tiềnamount
IMã tham chiếuthe bank's reference code
JLũy kếbalance after the transaction, empty when Hide balance is on

Permissions and notes

  • TinoPay asks for a single Google Drive permission: drive.file, only files TinoPay creates or that you pick in the file picker; TinoPay cannot read other files in your Drive. Revoke it any time at myaccount.google.com/permissions or by deleting the integration.
  • Do not rename, insert or delete the 10 default columns: TinoPay writes by column order, and a changed structure puts data in the wrong place. Add your own columns to the right of column J. Do not delete the header row (row 1).
  • If you revoke the Google permission or delete the spreadsheet, the integration switches to Tạm dừng (Paused) with an error message; link the Google account again to continue.
  • You can create several integrations pointing at different spreadsheets, e.g. one per branch.
  • The Đẩy gần nhất (Last push) column on the list page shows the last successful write; on errors, a warning icon next to the integration name shows details on hover.
SymptomCommon cause
the spreadsheet has no rowsno transaction matches the step 3 filters yet, or the integration is paused
the integration switches to paused by itselfthe Google permission was revoked, or the spreadsheet was deleted or changed owner
a spreadsheet cannot be createdthe Google Drive checkbox was not ticked on the consent screen
data lands in the wrong columnsthe 10 default columns were changed

WordPress / WooCommerce

The TinoPay Gateway plugin adds a VietQR bank-transfer payment method to WooCommerce and confirms orders automatically when the customer pays. It uses webhooks behind the scenes but configures them itself, so you build no endpoint.

  1. The customer places an order; the thank-you page shows a VietQR code with the transfer description DH<order number>.
  2. The customer scans and pays; TinoPay records the transaction and extracts DH<order number>.
  3. The webhook reaches the website; the plugin matches the order and changes its status.
  • Full amount: the order moves to the status you chose (Processing by default) and stock is reduced.
  • Short or excess amount: the order keeps its status, and the plugin adds a note with the difference for you to handle.
  • The customer's payment page shows "Bạn đã thanh toán thành công" (payment successful) once the order is confirmed.

Requirements: WordPress 5.6+, PHP 7.2+, WooCommerce installed; the website on HTTPS (required to receive webhooks and connect securely); a TinoPay account (sign in with your Tino account) and at least one connected bank account. Automatic confirmation currently supports BIDV and needs a virtual account.

Installing the plugin

  1. Download the TinoPay Gateway plugin (.zip), or in the TinoPay web app: Tích hợp → WordPress → Tải plugin (.zip) (Download plugin).
  2. In WordPress: Plugins → Add New → Upload Plugin, choose the zip file, click Install, then Activate.
  3. WooCommerce → Settings → Payments → TinoPay, click Kết nối TinoPay (Connect TinoPay): the browser goes to TinoPay; sign in with your Tino account, check the website name and click Cho phép kết nối (Allow connection).
  4. Pick the bank account (BIDV: also pick a virtual account, whose number the QR code uses) and click Hoàn tất thiết lập (Finish setup). The plugin creates the webhook on TinoPay (visible under Tích hợp → Webhooks) and syncs the payment code structure: prefix DH by default, so DH1234 matches order #1234.
  5. Place a small test order, choose TinoPay, scan the QR code and pay: a few seconds later the order changes status with the note "TinoPay: Đã nhận thanh toán…" (payment received).

A connected website may only view bank account and virtual account details, create webhooks pointing at itself, and view and update the payment code structure. It cannot see passwords or move money.

Plugin optionMeaning
Payment code prefixmust match Cài đặt → Cấu trúc mã thanh toán on TinoPay
Order status on completionstatus given to the order when the full amount arrives (Processing by default)
Success messagetext shown to the customer after payment
Download modedigital products: show the download link right after payment
Bank name displayshort name, full name or both

Managing connections: TinoPay web app → Tích hợp → WordPress, section Website đã kết nối (Connected websites) lists websites and when each was last used. Ngắt kết nối (Disconnect) revokes access at once and the website must connect again; webhooks it created stay, managed on the Webhooks page.

Configuring the plugin manually

Without Connect TinoPay (OAuth), configure it by hand:

  1. In the plugin: fill in the bank, account number (or virtual account number), account holder, payment code prefix and an API Key of your choice (letters and digits only, at least 10 characters).
  2. On TinoPay → Tích hợp → Webhooks → Thêm webhook: URL https://<your-website>/wp-json/tinopay-gateway/v2/add-payment, Token equal to the API Key from step 1, event Tiền vào (incoming), and turn on Bỏ qua nếu không có mã thanh toán (skip if no payment code).
  3. On TinoPay → Cài đặt → Cấu trúc mã thanh toán: add the same prefix as the plugin, with a suffix length that fits your order numbers (e.g. 1–10, type alphanumeric or digits only).
SymptomCommon cause
the customer paid but the order status did not changethe transfer description lacks or misspells DH<order number>; the plugin prefix differs from the code structure on TinoPay; the webhook is paused
webhook history shows Invalid API Keythe webhook token on TinoPay differs from the plugin's API Key (manual setup)
webhook history shows Order not foundthe extracted code matches no order: check the suffix length in the payment code structure
the order has a short / excess payment notethe customer paid the wrong amount: the order keeps its status for you to handle
Connect TinoPay failsthe website is not on HTTPS, or the connection session passed 15 minutes: click again
no QR code on the thank-you pageno bank account or virtual account selected in the plugin settings

Each delivery (HTTP status, data sent, the website's answer) is in the TinoPay web app → Tích hợp → Lịch sử webhook (Webhook history).

HostBill

The TinoPay payment module for HostBill shows a VietQR code on the invoice and records the payment automatically when the customer pays: the invoice turns Paid within seconds, with no manual reconciliation. The module uses webhooks: every incoming transaction whose description contains an invoice code is sent to HostBill.

  1. The customer opens an invoice in HostBill and chooses TinoPay; the invoice shows a VietQR code with the description <prefix><invoice number> (e.g. TNG2026).
  2. The customer pays; TinoPay extracts the payment code TNG2026.
  3. The webhook reaches HostBill; the module records the transaction and the invoice turns Paid.
  • Full amount: the transaction is added to the invoice and the invoice turns Paid.
  • Short or excess amount: ignored by default; turn on Allow lower / higher amount to record it anyway.
  • The customer's invoice page reloads by itself once the invoice is paid.

Requirements: permission to install HostBill payment modules (the includes/modules/Payment/ folder); a TinoPay account and at least one connected bank account; for BIDV, receive money on a virtual account.

Installing the module

  1. Download the HostBill TinoPay module (.zip) and extract it into HostBill's payment module folder, giving the structure in the block below.
  2. HostBill Admin → Settings → Modules → Payment Modules → activate TinoPay - Quét QR Code chuyển khoản. Note the module ID, used in the webhook URL in step 4.
  3. Configure the module with the table below.
  4. On TinoPay: Cài đặt → Cấu trúc mã thanh toán, add a prefix equal to Payment Code Prefix (e.g. TNG), suffix type digits only, with a length that fits invoice numbers (e.g. 1–10). Then Tích hợp → Webhooks → Thêm webhook with the last table of this section; you can turn on skip if no payment code so only transactions with an invoice code reach HostBill.
  5. Test: create a small invoice and choose TinoPay; a QR code on the invoice means the module works. Make a test transfer: a few seconds later the invoice turns Paid. Each delivery is in Tích hợp → Lịch sử webhook on TinoPay.
includes/modules/Payment/tinopay/class.tinopay.php
includes/modules/Payment/tinopay/user/class.tinopay_controller.php
Module fieldDescription
TinoPay URLhttps://pay.tino.vn
Bank Infoone receiving account per line, in the format of the block below; for BIDV use the virtual account number; empty minimum / maximum means no limit; with several lines the customer can choose the bank
Payment Code Prefixtransfer description prefix, e.g. TNG → description TNG2026 for invoice 2026
Template QRTinoPay frame with the bank logo, or the QR code only
Webhook Tokensecret string, must equal the webhook's verification token on TinoPay
Allow lower / higher amountrecord payments that are short of or above the invoice amount
Periodic invoice status check intervalhow often the invoice page checks whether it is paid, 1–10 seconds, default 3
bank_code|account_holder_name|account_number|minimum_amount|maximum_amount
BIDV|CONG TY ABC|9624QA01||
Webhook on TinoPayValue
Receiving URLhttps://<hostbill-address>/index.php?cmd=callback&module=<module ID>
Bank accountthe account listed in Bank Info (BIDV: pick the virtual account)
Verification tokenequal to the module's Webhook Token
EventTiền vào (incoming)

The webhook sent to HostBill

HostBill receives TinoPay's standard webhook request. The module takes the invoice code from code (or searches content when it is empty), strips the prefix and HostBill's InvoicePrefix, checks the amount, then adds the transaction to the invoice with transaction_id as the transaction code, so resending the same transaction does not record it twice.

ResultHTTP status the module returns
recorded201
skipped: already recorded, outgoing, no invoice code, invoice not found, short or excess amount (when not allowed)200
wrong token401
invalid data400

TinoPay resends the webhook when it gets a non-2xx status: see Responses and retries.

SymptomCommon cause
no QR code on the invoicemalformed Bank Info, or the invoice amount is outside every line's minimum / maximum
webhook history shows 401the Webhook Token in HostBill differs from the webhook's verification token on TinoPay
webhook history shows 200 but the invoice is not Paidread message in the answer: no invoice code (the prefix on TinoPay differs from Payment Code Prefix), short or excess amount, or a wrong invoice code
webhooks do not reach HostBillthe webhook has the wrong account or virtual account, the event is not incoming, or the URL has the wrong module ID

Sapo

Confirms bank-transfer payments automatically for orders on a Sapo online store: the thank-you page shows a VietQR code with the transfer description; a few seconds after the customer pays, the Sapo order is recorded as paid.

  1. The customer places an order and chooses bank transfer; the thank-you page shows a VietQR code with the description DH<order number>.
  2. The customer pays; TinoPay extracts the order number from the transfer description.
  3. TinoPay calls the Sapo Admin API to record the payment; the order becomes paid.
  • Full amount (or more): TinoPay records a payment of exactly the order total.
  • Short amount: the order stays as is, and TinoPay lists it under recent reconciliation for you to handle.
  • Order already paid or cancelled: skipped, never recorded twice.
  • The thank-you page switches to a "received … for order …" message once the order is recorded.

Requirements: a Sapo online store (admin address like https://ten-cua-hang.mysapo.net/admin); Sapo Omni / POS are not supported yet; a TinoPay account and at least one connected bank account; for BIDV, pick a receiving virtual account.

Setting up Sapo

  1. Sapo Admin → Ứng dụng → Ứng dụng riêng → Tạo ứng dụng riêng (Apps → Private apps → Create private app), named e.g. TinoPay. Permissions: set the orders, transactions and shipping row to read and write, everything else to not allowed. Save, then copy the API Key and API Secret.
  2. TinoPay web app → Tích hợp → Sapo → Thêm cửa hàng (Add store), filled in with the table below. TinoPay tests the connection to Sapo before saving; click Kiểm tra kết nối (Test connection) to try first.
  3. Sapo Admin → Cấu hình → Phương thức thanh toán → Phương thức thanh toán thủ công → Thêm phương thức (Settings → Payment methods → Manual payment methods → Add). Type: bank transfer. The name must contain "Chuyển khoản" (bank transfer), e.g. Chuyển khoản ngân hàng (Tự động xác nhận): the thank-you page shows the QR code only for orders using a bank-transfer method, never for COD. Add payment instructions, e.g. scan the QR code on the order confirmation page and keep the transfer description; the order is confirmed automatically within seconds. Save.
  4. On TinoPay, on the store card, click Copy next to Mã nhúng trang thanh toán (Checkout embed code). In Sapo Admin → Cấu hình → Trang thanh toán (Settings → Checkout), section order processing, paste it into the additional message field (or the checkout footer field) and save.
  5. Test: place an order with the bank-transfer method from step 3; a QR code on the order confirmation page means the setup is right. Pay a small amount: the Sapo order becomes paid and a reconciliation row appears on the Tích hợp → Sapo page.
Store fieldDescription
Integration nameany name, e.g. Sapo Web
Receiving bankbank account shown on the QR code; for BIDV also pick a virtual account, and only transactions to it are reconciled
Payment code prefixone of the prefixes set in Cài đặt → Cấu trúc mã thanh toán (e.g. DH); the transfer description is prefix + Sapo order number, e.g. order #1001 → DH1001
Store URLlike https://ten-cua-hang.mysapo.net (the .mysapo.net address, not a custom domain)
API Key / API Secretfrom step 1; the secret is encrypted when saved, and left empty on edit it keeps the old secret
Parameter settingsparameters of the transaction TinoPay creates on the Sapo order, next section
Statusactive, paused

The embed code looks like the block below (paste the copied code as is, script address included). It only holds the store's public key, never the API Key or API Secret. The thank-you page shows the QR code only for orders created in the last 7 days.

<script src="…/sapo/<store-key>.js" data-order="{{ order.order_number }}" async></script>

Sapo transaction parameters

When the customer pays the full amount, TinoPay creates a payment transaction on the order through the Sapo Transaction API (POST /admin/orders/{id}/transactions.json) for the order total. Three parameters shape that transaction:

ParameterValuesMeaning
kindcapture (default), sale, authorizationtransaction kind; capture collects money for a placed order, which suits manual bank-transfer orders
source_namenot customised, web, pos, iphone, androidtransaction source; left empty, Sapo sets it
statusnot customised, success, pending, failure, errortransaction status; left empty, Sapo sets it

The defaults (capture, not customised) suit most stores; change them only when a Sapo order does not become paid after the payment is recorded.

Sapo reconciliation

The Tích hợp → Sapo page shows the 20 latest transactions with an order code:

StatusMeaning
Đã ghi nhận (Recorded)the payment was recorded on the Sapo order
Chuyển thiếu (Short)less than the order total, not recorded; handle it manually in Sapo
Đã thanh toán trước (Already paid)the order was already paid, skipped
Không thấy đơn (Order not found)no order has the number in the transfer description
Đơn đã huỷ (Cancelled)the order is cancelled, not recorded
Lỗi (Error)Sapo refused or could not be reached (wrong API Key / API Secret, app deleted…)

Click Gửi lại hook (Resend hook) on the transaction's detail page to reconcile again after fixing the configuration; paid orders are never recorded twice.

SymptomCommon cause
no QR code on the thank-you pagethe embed code is not pasted, the integration is paused, or the Sapo payment method name lacks "Chuyển khoản"
saving the integration reports a wrong API Key / API Secretthe private app lacks read and write access to orders, or characters were lost when copying
the customer paid but the order is not confirmedthe transfer description lacks the order code (DH1001), the amount is short, or the money went to a different account or virtual account than the one selected; check recent reconciliation