TUNGKAO API docs

🌐 ឯកសារនេះជាភាសាអង់គ្លេស

Integrate with TUNGKAO

One API style for every service — Base URL: https://api.tungkao.com

🚀

Getting started

Every service works the same way: create an API key in the merchant portal (one key per service) and send it in the Authorization header of every request.

How it works

  1. Sign in at app.tungkao.com with Gmail and top up credits (1 THB = 100 credits).
  2. API keys → pick a service card → choose the key lifetime → Create → copy the key (shown once).
  3. Keep the key on your server (env var TUNGKAO_KEY). Never put it in web pages or mobile apps.
  4. Start with a test key (tk_test_…) — no credits charged — then switch to live.
GET/v1/account — Test your key: credits left
curl -X GET https://api.tungkao.com/v1/account \
  -H "Authorization: Bearer $TUNGKAO_KEY"

Signing requests (when enabled)

import crypto from "node:crypto";
async function tungkao(method, path, body) {
  const raw = body ? JSON.stringify(body) : "";
  const ts = Math.floor(Date.now() / 1000).toString();
  const hash = crypto.createHash("sha256").update(raw).digest("hex");
  const sig = crypto.createHmac("sha256", process.env.TUNGKAO_SIGNING_SECRET).update(`${ts}.${method}.${path}.${hash}`).digest("hex");
  const r = await fetch("https://api.tungkao.com" + path, { method, body: raw || undefined, headers: {
    Authorization: `Bearer ${process.env.TUNGKAO_KEY}`, "Content-Type": "application/json",
    "X-Tungkao-Timestamp": ts, "X-Tungkao-Signature": sig } });
  return r.json();
}
console.log(await tungkao("GET", "/v1/account"));
  • Every response is JSON with success and request_id. Errors carry error.code (e.g. KEY_EXPIRED, INSUFFICIENT_CREDITS) — see /errors.
  • Keys expire as chosen (1–24 months or never). We warn 30/7/1 days before via the api_key.expiring webhook. Renew in the portal — the key string stays the same.
  • Domain verification: each live key is bound to one website. API keys → 'Domain verification' → enter your domain, upload tungkao-verify-….html to https://yourdomain/ (or add DNS TXT _tungkao-verify.yourdomain) and press Verify. Unverified keys get DOMAIN_NOT_VERIFIED; a verified domain can't change and no other account can use it.
  • Leaked secret? Press 'Rotate now' — unlimited, the old key stops at once. A key only works for its own service (WRONG_PRODUCT otherwise).
  • Advanced (optional): turn on 'Signed requests' on a key to get a tksign_… secret; every request then needs X-Tungkao-Timestamp and X-Tungkao-Signature = HMAC-SHA256(secret, ts.METHOD.path.sha256(body)). A leaked key alone becomes useless.
  • Send Accept-Language (th, en, lo, my, zh-CN, ko, ja, vi, km, id) to choose the message language.
🧾

Bank slip verification

slip_verify

Checks a slip is genuine, the amount matches, it was paid to you and hasn't been used — for shops where customers attach a slip.

How it works

  1. Customer uploads the slip; your site reads its QR text (starts with 0041…).
  2. Send qr_payload, the expected amount and your receiving account to the API.
  3. Accept the order only when status is VALID.
POST/v1/slip/verify
curl -X POST https://api.tungkao.com/v1/slip/verify \
  -H "Authorization: Bearer $TUNGKAO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"qr_payload":"0041000600000101030040220014…","expected_amount":"500.00","receivers":[{"bank":"KBANK","account_number":"1234567890"}]}'
  • Send an Idempotency-Key header (e.g. your order id): retries are never charged twice.
  • A slip is VALID once per key (the second time returns DUPLICATE).
🏦

Slip deposits

slip_deposit

Top-ups for your users: verifies the slip, records the deposit for that user and notifies your site by webhook.

How it works

  1. Set receiving accounts and a webhook URL on the key in the portal.
  2. Send the slip with customer_ref (your user id).
  3. On the deposit.succeeded webhook (verify the signature) credit the user.
POST/v1/deposits/slip
curl -X POST https://api.tungkao.com/v1/deposits/slip \
  -H "Authorization: Bearer $TUNGKAO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"qr_payload":"0041000600000101030040220014…","customer_ref":"member-1001","expected_amount":"300.00"}'
GET/v1/deposits/dep_xxx
curl -X GET https://api.tungkao.com/v1/deposits/dep_xxx \
  -H "Authorization: Bearer $TUNGKAO_KEY"

Verify the webhook signature

Every webhook carries X-Tungkao-Signature: t=…,v1=…. Verify it before trusting the data (the webhook secret is shown when you set the URL).

import crypto from "node:crypto";
// ใช้ raw body (ข้อความดิบ) เท่านั้น ห้าม JSON.stringify ซ้ำ
function verify(rawBody, header, secret) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const want = crypto.createHmac("sha256", secret).update(`${m[1]}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(m[2]));
}
🔐

Sign-in for your website (Google, LINE, Facebook, TikTok, Discord)

Add 'Sign in with …' to your site without registering with each provider. Get name, email, photo, a stable member id, and a member list with sign-in history. One service per provider: google_login, line_login, facebook_login, tiktok_login, discord_login.

How it works

  1. Create a key for the provider, then on 'Sign-in members' set your site name and redirect URI (https, exact match).
  2. Your sign-in button sends the user to /oauth/authorize with client_id (the key id), redirect_uri and state (fresh random, kept in the session).
  3. The user returns to redirect_uri?code=…&state=… → check state → your server calls POST /v1/login/token within 2 minutes.
  4. Use data.user (e.g. 7F3K9QX2-yourshopcom) as the member id — the same person always gets the same id.
POST/v1/login/token
curl -X POST https://api.tungkao.com/v1/login/token \
  -H "Authorization: Bearer $TUNGKAO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code":"จาก redirect","redirect_uri":"https://yourshop.com/auth/callback"}'
GET/v1/login/users?limit=50
curl -X GET https://api.tungkao.com/v1/login/users?limit=50 \
  -H "Authorization: Bearer $TUNGKAO_KEY"

Complete example (button + callback + code exchange)

// npm i express express-session
import express from "express";
import session from "express-session";
import crypto from "node:crypto";

const KEY = process.env.TUNGKAO_KEY;            // API key (tk_live_…) เก็บที่เซิร์ฟเวอร์เท่านั้น
const CLIENT_ID = process.env.TUNGKAO_CLIENT_ID; // id ของ key (เห็นในหน้าร้าน) ไม่ใช่ความลับ
const REDIRECT = "https://yourshop.com/auth/callback"; // ต้องตรงกับที่ลงทะเบียนทุกตัวอักษร

const app = express();
app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { secure: true, sameSite: "lax" } }));

// 1) ปุ่ม "เข้าสู่ระบบ" ลิงก์มาที่นี่
app.get("/auth/login", (req, res) => {
  req.session.state = crypto.randomBytes(24).toString("base64url");
  const u = new URL("https://api.tungkao.com/oauth/authorize");
  u.search = new URLSearchParams({ client_id: CLIENT_ID, redirect_uri: REDIRECT, state: req.session.state });
  res.redirect(u);
});

// 2) ผู้ใช้กลับมาพร้อม code
app.get("/auth/callback", async (req, res) => {
  if (!req.query.state || req.query.state !== req.session.state) return res.status(400).send("state ไม่ตรง");
  delete req.session.state;
  if (req.query.error) return res.redirect("/login?cancelled=1");
  // 3) แลก code เป็นข้อมูลผู้ใช้ (ภายใน 2 นาที)
  const r = await fetch("https://api.tungkao.com/v1/login/token", {
    method: "POST",
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ code: req.query.code, redirect_uri: REDIRECT }),
  });
  const j = await r.json();
  if (!j.success) return res.status(400).send(j.error.message);
  req.session.user = j.data; // j.data.user = รหัสสมาชิกถาวร เช่น 7F3K9QX2-yourshopcom
  res.redirect("/");
});
app.listen(3000);
ProviderServiceYou get
G Googlegoogle_loginname, verified email, photo
L LINEline_loginname, photo, email if the user agrees
f Facebookfacebook_loginname, photo, email
♪ TikToktiktok_logindisplay name, avatar (no email)
D Discorddiscord_loginname, avatar, email
  • Credits are charged on each successful sign-in plus once per new member — see current prices on the service card.
  • Test-mode keys allow http://localhost redirect URIs and are free.
  • Never put the API key in links or pages — /oauth/authorize only needs client_id (the key id), which is public.
✈️

Telegram notifications

telegram_notify

Message your users on Telegram through TUNGKAO's bot — no bot of your own. E.g. payment received, order status, OTP.

How it works

  1. Request a connect link for the user (ref = your user id) and show a 'Connect Telegram' button (valid 30 minutes, single use).
  2. The user taps Start → connected (see /v1/telegram/recipients).
  3. Send to one ref or up to 100 refs; bold text (parse_mode: HTML) and a link button are supported.
POST/v1/telegram/link
curl -X POST https://api.tungkao.com/v1/telegram/link \
  -H "Authorization: Bearer $TUNGKAO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ref":"member-1001"}'
POST/v1/telegram/send
curl -X POST https://api.tungkao.com/v1/telegram/send \
  -H "Authorization: Bearer $TUNGKAO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ref":"member-1001","text":"<b>ยอดเข้าแล้ว</b> 500 บาท","parse_mode":"HTML","button":{"text":"ดูรายการ","url":"https://yourshop.com/orders/1"}}'
GET/v1/telegram/messages
curl -X GET https://api.tungkao.com/v1/telegram/messages \
  -H "Authorization: Bearer $TUNGKAO_KEY"
  • Only delivered messages are charged — failures (e.g. the user blocked the bot) are refunded and that recipient is paused.
🔔

Discord notifications

discord_notify

Post messages into your or your customers' Discord channels — e.g. new orders for your team.

How it works

  1. In Discord: channel settings → Integrations → Webhooks → New Webhook → Copy Webhook URL.
  2. Register the URL with a ref (your name for the channel) — stored encrypted.
  3. Send by ref (@everyone mentions are disabled automatically).
POST/v1/discord/webhooks
curl -X POST https://api.tungkao.com/v1/discord/webhooks \
  -H "Authorization: Bearer $TUNGKAO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ref":"orders-room","url":"https://discord.com/api/webhooks/123/abc…"}'
POST/v1/discord/send
curl -X POST https://api.tungkao.com/v1/discord/send \
  -H "Authorization: Bearer $TUNGKAO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ref":"orders-room","text":"🛒 ออเดอร์ใหม่ #1001 ยอด 590 บาท"}'
₿

Crypto payments (coming soon)

crypto

Gives each user their own USDT-TRC20 / BTC / USDT-BEP20 address and notifies you by webhook — launching soon.

How it works

  1. Request an address for a user
  2. The user sends funds
  3. Credit them on crypto.deposit.confirmed
POST/v1/crypto/addresses
curl -X POST https://api.tungkao.com/v1/crypto/addresses \
  -H "Authorization: Bearer $TUNGKAO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"asset":"USDT-TRC20","customer_ref":"member-1001"}'