Pay a bill
Use this API to create a bill payment request.
- If the customer requires confirmation before automatic payment, Tingee sends an Email/Zalo notification to the customer.
- If the customer does not require payment confirmation, the bill is debited automatically from the customer's account.
POST
URLhttps://open-api.tingee.vn/v1/direct-debit/payment-bill
Request a bill payment from a linked customer account. Supports automatic payment or payment with confirmation.
Header Request
| Header | Required | Description |
|---|---|---|
Content-Type | ✓ | application/json |
x-client-id | ✓ | Partner identifier issued by TINGEE. |
x-signature | ✓ | HMAC SHA512 authentication signature. See How to generate the signature. |
x-request-timestamp | ✓ | Time the request is sent (format: yyyyMMddHHmmssSSS, UTC+7) |
Body Parameter
| Field | Type | Required | Description |
|---|---|---|---|
requestId | string | ✓ | ID of the request to Tingee (UUID), unique for each payment. This value is used as the transactionId of the debit transaction on TINGEE. |
subscriptionId | string | ✓ | ID of the linked bank account (UUID). The account must be active. |
tokenRef | string | ✓ | Reference code of the customer's account. |
amount | string | ✓ | Bill amount to pay (VND), sent as a numeric string. Example: "500000". |
description | string | ✓ | Payment description. |
partnerCode | string | ✓ | Debit partner. Valid value: one-pay. |
serviceProviderName | string | ✓ | Name of the partner using the service (shown in the confirmation notification sent to the customer). |
returnUrl | string | ✓ | URL to redirect back to the partner after completion. |
accountNumber | string | ✕ | Customer's account number. The system always debits the account linked to subscriptionId. |
additionalData | object | ✕ | Additional data (bill number, customer name, ...). The object's JSON string is at most 500 characters. The billNumber field (if any) is shown as the bill number in the Email/Zalo confirmation. |
Code samples
- cURL
- NestJS
- C#
- Java
- PHP
curl --location --request POST 'https://open-api.tingee.vn/v1/direct-debit/payment-bill' \
--header 'accept: application/json' \
--header 'x-client-id: YOUR_CLIENT_ID' \
--header 'x-request-timestamp: 20240509154004003' \
--header 'x-signature: YOUR_SIGNATURE' \
--header 'Content-Type: application/json' \
--data '{
"requestId": "550760e0-1cc2-44b9-85ba-eb318e228b4f",
"subscriptionId": "89188e53-eba3-40fa-9323-04422ced3637",
"tokenRef": "TKN-123456789",
"amount": "500000",
"description": "THANH TOAN HOA DON DV HD123456",
"partnerCode": "one-pay",
"serviceProviderName": "Công ty TMCP ABC",
"returnUrl": "https://example.com"
}'
import axios from "axios";
import { createHmac } from "crypto";
const CLIENT_ID = "YOUR_CLIENT_ID";
const SECRET_KEY = "YOUR_SECRET_KEY";
// Timestamp in UTC+7, format yyyyMMddHHmmssSSS
function getTimestamp() {
return new Date(Date.now() + 7 * 60 * 60 * 1000)
.toISOString()
.replace(/[-:TZ.]/g, "")
.slice(0, 17);
}
async function directDebitPaymentBill() {
const body = {
requestId: "550760e0-1cc2-44b9-85ba-eb318e228b4f",
subscriptionId: "89188e53-eba3-40fa-9323-04422ced3637",
tokenRef: "TKN-123456789",
amount: "500000",
description: "THANH TOAN HOA DON DV HD123456",
partnerCode: "one-pay",
serviceProviderName: "Công ty TMCP ABC",
returnUrl: "https://example.com",
};
const bodyJson = JSON.stringify(body);
const timestamp = getTimestamp();
const signature = createHmac("sha512", SECRET_KEY)
.update(`${timestamp}:${bodyJson}`)
.digest("hex");
const res = await axios.request({
method: "POST",
url: "https://open-api.tingee.vn/v1/direct-debit/payment-bill",
data: bodyJson,
headers: {
accept: "application/json",
"Content-Type": "application/json",
"x-client-id": CLIENT_ID,
"x-request-timestamp": timestamp,
"x-signature": signature,
},
});
console.log(res.data);
}
directDebitPaymentBill();
using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
public class Program
{
private const string ClientId = "YOUR_CLIENT_ID";
private const string SecretKey = "YOUR_SECRET_KEY";
private static readonly HttpClient Client = new HttpClient();
public static async Task Main()
{
var body = new
{
requestId = "550760e0-1cc2-44b9-85ba-eb318e228b4f",
subscriptionId = "89188e53-eba3-40fa-9323-04422ced3637",
tokenRef = "TKN-123456789",
amount = "500000",
description = "THANH TOAN HOA DON DV HD123456",
partnerCode = "one-pay",
serviceProviderName = "Công ty TMCP ABC",
returnUrl = "https://example.com"
};
// Do not escape non-ASCII / special characters so the JSON matches TINGEE's JSON.stringify
var jsonOptions = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
var bodyJson = JsonSerializer.Serialize(body, jsonOptions);
// Timestamp in UTC+7, format yyyyMMddHHmmssSSS
var timestamp = DateTime.UtcNow.AddHours(7).ToString("yyyyMMddHHmmssfff");
var signature = HmacSha512(SecretKey, $"{timestamp}:{bodyJson}");
var request = new HttpRequestMessage(HttpMethod.Post, "https://open-api.tingee.vn/v1/direct-debit/payment-bill");
request.Headers.Add("accept", "application/json");
request.Headers.Add("x-client-id", ClientId);
request.Headers.Add("x-request-timestamp", timestamp);
request.Headers.Add("x-signature", signature);
request.Content = new StringContent(bodyJson, Encoding.UTF8, "application/json");
var response = await Client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
private static string HmacSha512(string key, string message)
{
using var hmac = new HMACSHA512(Encoding.UTF8.GetBytes(key));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
return Convert.ToHexString(hash).ToLower();
}
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class Main {
private static final String CLIENT_ID = "YOUR_CLIENT_ID";
private static final String SECRET_KEY = "YOUR_SECRET_KEY";
public static void main(String[] args) throws Exception {
// Minified JSON, same as JSON.stringify()
String bodyJson = "{\"requestId\":\"550760e0-1cc2-44b9-85ba-eb318e228b4f\",\"subscriptionId\":\"89188e53-eba3-40fa-9323-04422ced3637\",\"tokenRef\":\"TKN-123456789\",\"amount\":\"500000\",\"description\":\"THANH TOAN HOA DON DV HD123456\",\"partnerCode\":\"one-pay\",\"serviceProviderName\":\"Công ty TMCP ABC\",\"returnUrl\":\"https://example.com\"}";
// Timestamp in UTC+7, format yyyyMMddHHmmssSSS
String timestamp = ZonedDateTime.now(ZoneId.of("Asia/Ho_Chi_Minh"))
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
String signature = hmacSha512(SECRET_KEY, timestamp + ":" + bodyJson);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://open-api.tingee.vn/v1/direct-debit/payment-bill"))
.header("accept", "application/json")
.header("Content-Type", "application/json")
.header("x-client-id", CLIENT_ID)
.header("x-request-timestamp", timestamp)
.header("x-signature", signature)
.method("POST", HttpRequest.BodyPublishers.ofString(bodyJson, StandardCharsets.UTF_8))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
private static String hmacSha512(String key, String data) throws Exception {
Mac mac = Mac.getInstance("HmacSHA512");
mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA512"));
byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : hash) hex.append(String.format("%02x", b));
return hex.toString();
}
}
<?php
$clientId = "YOUR_CLIENT_ID";
$secretKey = "YOUR_SECRET_KEY";
$body = [
"requestId" => "550760e0-1cc2-44b9-85ba-eb318e228b4f",
"subscriptionId" => "89188e53-eba3-40fa-9323-04422ced3637",
"tokenRef" => "TKN-123456789",
"amount" => "500000",
"description" => "THANH TOAN HOA DON DV HD123456",
"partnerCode" => "one-pay",
"serviceProviderName" => "Công ty TMCP ABC",
"returnUrl" => "https://example.com",
];
// Do not escape "/" or non-ASCII characters so the JSON matches TINGEE's JSON.stringify
$bodyJson = json_encode($body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
// Timestamp in UTC+7, format yyyyMMddHHmmssSSS
$now = new DateTime("now", new DateTimeZone("Asia/Ho_Chi_Minh"));
$timestamp = $now->format("YmdHisv");
$signature = hash_hmac("sha512", $timestamp . ":" . $bodyJson, $secretKey);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://open-api.tingee.vn/v1/direct-debit/payment-bill",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => $bodyJson,
CURLOPT_HTTPHEADER => [
"Accept: application/json",
"Content-Type: application/json",
"x-client-id: " . $clientId,
"x-request-timestamp: " . $timestamp,
"x-signature: " . $signature,
],
]);
$response = curl_exec($ch);
if ($response === false) {
echo "cURL Error: " . curl_error($ch);
} else {
echo $response;
}
curl_close($ch);
Sample Response
Automatic payment (the customer does not require confirmation):
{
"code": "00",
"message": "Success",
"data": {
"code": "00",
"status": "success",
"transactionCode": "TXN-0123456789",
"returnUrl": "https://example.com?message=Success&type=direct-debit-transaction"
}
}
Customer confirmation required (Email/Zalo confirmation is enabled):
{
"code": "00",
"message": "Success",
"data": {
"code": "00",
"isSentConfirmEmail": true,
"isSentConfirmZalo": false
}
}
| Field | Type | Description |
|---|---|---|
code | string | Response code (00 means success). |
message | string | Detailed message. |
data.code | string | Result of the payment request: - 00: Successful (or the confirmation request was sent to the customer). - 01: Failed. - 02: Processing. - 03: Unknown. |
data.status | string | Transaction status (automatic payment only): success, failure, pending, insufficient-balance (insufficient balance), exceed-daily-limit (limit exceeded). |
data.transactionCode | string | Transaction code at the debit gateway (automatic payment only). |
data.returnUrl | string | returnUrl with the result parameter message (Success/Failed) and type=direct-debit-transaction appended (automatic payment only). |
data.isSentConfirmEmail | boolean | true if a payment confirmation Email was sent to the customer (confirmation required only). |
data.isSentConfirmZalo | boolean | true if a payment confirmation Zalo notification was sent to the customer (confirmation required only). |
The final result of the transaction is sent via Webhook, or you can look it up with the Query transaction status API.
Common error codes
| Code | Description | How to handle |
|---|---|---|
1067 | No active automatic payment subscription was found | Check that the subscription's subscriptionId and tokenRef are still valid; if the customer has cancelled, ask them to register again via Register. |
1124 | requestId has already been used for a previous transaction | Generate a new requestId (UUID) for each debit; look up the earlier transaction instead of resending it. |
1121 | Missing returnUrl | Send a valid returnUrl so TINGEE can redirect the customer after payment. |
1120 | Missing tokenRef | Send the tokenRef you received when registration succeeded. |
1068, 1009 | Direct Debit is not configured for the Merchant, or the payment platform is not supported | Contact TINGEE to check the Merchant's Direct Debit configuration. |
90, 91, 92, 97 | Request authentication error (timestamp, client-id, signature) | See System error codes. |
Others | Other errors | See Error codes. |