Communitygithub.com

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Setup Telegram bot notification cho thanh toán thành công từ Sepay webhook trong Next.js project. Skill này: (1) hướng dẫn user 3 bước tạo Telegram bot riêng qua @BotFather + lấy bot token + chat_id (qua @userinfobot cho chat 1-1, hoặc /getUpdates API cho group), (2) tạo helper function `sendTelegramNotification()` reusable trong `lib/telegram.ts`, (3) detect existing Sepay webhook route trong project (thường là `/api/sepay-webhook`, `/api/payment-success`, hoặc tương đương) và wire Telegram call CÙNG CHỖ với email auto-responder (Resend) — gửi parallel qua `Promise.allSettled` để Telegram fail KHÔNG block 200 response trả Sepay (Sepay sẽ retry webhook nếu nhận non-200, gây duplicate notification), (4) draft message format mặc định tiếng Việt: emoji + tên khách + SĐT + email + amount (charm pricing VND định dạng `499.000đ`) + tên sản phẩm + timestamp giờ VN dd/mm/yyyy HH:mm, (5) update `.env.local` với `TELEGRAM_BOT_TOKEN` + `TELEGRAM_CHAT_ID`, add vào `.gitignore`, nhắc user paste vào

MKT.LANDINGPAGE.SKILLS とは?

MKT.LANDINGPAGE.SKILLS is a Claude Code agent skill that setup Telegram bot notification cho thanh toán thành công từ Sepay webhook trong Next.js project. Skill này: (1) hướng dẫn user 3 bước tạo Telegram bot riêng qua @BotFather + lấy bot token + chat_id (qua @userinfobot cho chat 1-1, hoặc /getUpdates API cho group), (2) tạo helper function `sendTelegramNotification()` reusable trong `lib/telegram.ts`, (3) detect existing Sepay webhook route trong project (thường là `/api/sepay-webhook`, `/api/payment-success`, hoặc tương đương) và wire Telegram call CÙNG CHỖ với email auto-responder (Resend) — gửi parallel qua `Promise.allSettled` để Telegram fail KHÔNG block 200 response trả Sepay (Sepay sẽ retry webhook nếu nhận non-200, gây duplicate notification), (4) draft message format mặc định tiếng Việt: emoji + tên khách + SĐT + email + amount (charm pricing VND định dạng `499.000đ`) + tên sản phẩm + timestamp giờ VN dd/mm/yyyy HH:mm, (5) update `.env.local` với `TELEGRAM_BOT_TOKEN` + `TELEGRAM_CHAT_ID`, add vào `.gitignore`, nhắc user paste vào.

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lexuanthanhhy/MKT.LANDINGPAGE.SKILLS/tree/main/.claude/skills/biz-telegram-payment-notify

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Biz Telegram Payment Notify — Realtime alert khi Sepay nhận tiền

Skill này wire Telegram bot notification vào Sepay webhook đã có sẵn trong project Next.js. Mỗi khi khách chuyển khoản thành công vào tài khoản ngân hàng đã link với Sepay, skill làm Telegram bot đẩy 1 tin nhắn realtime vào chat của owner (hoặc group team sale) — kèm tên khách, SĐT, amount, sản phẩm, thời gian.

Triết lý: Email auto-responder cho khách (Resend) và Telegram alert cho owner phục vụ 2 actor khác nhau. Email là trang trọng cho khách. Telegram là "ping" cho owner — anh/chị đang đi ngoài đường, mở Telegram là thấy ngay đơn mới, không phải mở Gmail tìm trong 100 email khác. Latency Telegram < 2 giây vs email 30-60 giây.

Tại sao Telegram chứ không phải Slack/Discord/Zalo: Telegram bot setup 5 phút (1 token + 1 chat_id), không cần workspace/server admin, free unlimited message, push notification mạnh hơn email, app native iOS/Android/desktop. Slack cần workspace + invite. Discord cần server + permission setup. Zalo OA cần verified business account (3-7 ngày approve) + monthly active user fee. Telegram là path of least resistance cho owner-side alert.

Output skill: code patch + bot setup guide step-by-step + env vars list + 3-cấp test plan. Skill KHÔNG tự test gửi Telegram — yêu cầu user paste bot token + chat_id trước, vì credentials này thuộc tài sản user.


Khi nào dùng skill này

  • User vừa wire Sepay webhook xong và muốn được notify realtime trên Telegram khi có khách chuyển tiền.
  • User đã có /api/payment-success (hoặc tương đương) xử lý email Resend rồi và muốn bổ sung Telegram alert PARALLEL — không tách route mới, không duplicate logic.
  • User nói "báo cho tôi khi có đơn" và context là landing page bán hàng có Sepay integration.

KHÔNG dùng skill này khi:

  • User muốn Slack/Discord notification → use case khác.
  • User muốn Zalo OA bot → cần OA verified business + cost cấu trúc khác.
  • User chưa setup Sepay webhook (chưa có bank account link Sepay, chưa có endpoint nhận webhook) → skill này không setup Sepay từ đầu, chỉ wire vào webhook có sẵn.
  • User muốn email-only cho owner → biz-email-setup đã có Email B (owner notification).

Workflow tổng quan (6 phase)

Phase 0: DETECT existing Sepay webhook route (path + framework + handler shape)
       ↓
Phase 0.5: DETECT lead store (lib/leads-kv.ts từ /biz-setup-sepay-payment)
           → có: dùng KV lookup thực
           → không: offer 2 option (raw mode HOẶC chạy /biz-setup-sepay-payment trước)
       ↓
Phase 1: GUIDE user create Telegram bot (BotFather → token → chat_id)
       ↓ [GATE — đợi user paste token + chat_id]
       ↓
Phase 2: DRAFT message format → show user preview → confirm
       ↓
Phase 3: WIRE code (lib/telegram.ts helper + integrate vào webhook handler + env vars)
       ↓
Phase 4: TEST plan (curl bot direct → simulated Sepay payload → production verify)

Phase 1 và Phase 4 có gate đợi user vì cần user thao tác manual (lấy token, verify production). Phase 0/0.5/2/3 skill chủ động.


Phase 0 — Detect existing Sepay webhook

Skill này wire vào webhook có sẵn, KHÔNG tạo route mới. Đầu tiên locate handler.

Tìm webhook route — grep theo các keyword phổ biến:

# Trong project root
grep -r "sepay" --include="*.ts" --include="*.js" --include="*.tsx" -l
grep -r "transferAmount\|transferType\|payment-success\|payment-webhook" --include="*.ts" --include="*.js" -l

3 kịch bản phổ biến:

Kịch bảnPath đã cóHành động
User dùng pipeline biz-email-setupapp/api/payment-success/route.ts (hoặc pages/api/payment-success.ts) đã có Resend gọi sendCustomerEmail + sendOwnerEmailWire sendTelegramNotification vào cùng Promise.allSettled
User có route Sepay-specificapp/api/sepay-webhook/route.ts hoặc app/api/webhook/sepay/route.tsWire vào sau khi parse + validate Sepay payload (nhánh transferType === 'in')
Không tìm thấy route nào—DỪNG. Báo user: "Em chưa tìm thấy webhook route nhận Sepay. Anh/chị setup Sepay webhook trước (link bank vào Sepay → tạo webhook URL trỏ về domain) rồi quay lại em wire Telegram. Hoặc nếu webhook ở endpoint tên khác, anh/chị chỉ giúp em path."

Đọc handler hiện tại để hiểu shape:

  • Có authenticate Sepay request không? (Authorization header Apikey hoặc HMAC)
  • Có dedup theo id hoặc referenceCode không? (Sepay retry nếu non-200 → cần dedup tránh double notify)
  • Có lookup lead theo content (transfer message chứa order ID) không?
  • Format response trả Sepay: { success: true } hay 200 OK plain?

Nếu handler hiện tại chưa dedup — cảnh báo user nhưng KHÔNG block. Skill chỉ wire Telegram, dedup là concern của payment logic. Suggest user thêm dedup riêng nếu cần.


Phase 0.5 — Detect lead store (CRITICAL)

Sepay webhook chỉ gửi content (text khách dán khi chuyển khoản) + transferAmount + referenceCode. Sepay KHÔNG biết khách là ai — phải lookup local store theo content (chứa order ID).

Skill check 3 thứ:

# 1. Check lead store helper exists
test -f lib/leads-kv.ts || test -f lib/leads-kv.js && echo "lead store: ✓"

# 2. Check Vercel KV env vars
grep -E "KV_REST_API_URL|KV_REST_API_TOKEN" .env.local && echo "KV env: ✓"

# 3. Check @vercel/kv installed
grep '"@vercel/kv"' package.json && echo "KV package: ✓"

3 kịch bản:

Kịch bảnHành động
Cả 3 ✓ — lead store sẵn sàngĐi tiếp Phase 1, dùng getLeadByOrderId() từ lib/leads-kv.ts trong webhook handler
Thiếu 1+ — chưa setupDỪNG, hỏi user 2 option (xem dưới)

Nếu thiếu, hỏi user:

Em phát hiện anh/chị chưa có lead store (Vercel KV). Sepay webhook không biết khách là ai — chỉ có nội dung CK + amount. Anh/chị muốn:

Option 1 — Setup lead store đầy đủ qua /biz-setup-sepay-payment trước (recommended):

  • Skill đó sẽ setup Vercel KV + scaffold lib/leads-kv.ts + wire form submit để lưu lead khi user đăng ký
  • Sau đó quay lại đây em wire Telegram lookup tên/SĐT/email khách đầy đủ
  • Telegram alert sẽ hiện: "Nguyễn Văn A — 0901234567 — [email protected] — 499K — Khoá AI Agent"

Option 2 — Raw mode: gửi Telegram thẳng payload Sepay, không lookup lead:

  • Telegram chỉ hiện: "Vietcombank — 499.000đ — content: DH001 0901234567 — 14:32"
  • Owner tự đoán khách qua content + cross-check Resend email log
  • Đơn giản, chạy được ngay, nhưng kém UX

Anh/chị chọn 1 hay 2?

Nếu user chọn 1 → DỪNG skill này, suggest /biz-setup-sepay-payment. Sau khi user xong setup → quay lại skill này.

Nếu user chọn 2 → đi tiếp Phase 1, dùng raw template (xem templates/lib-telegram-raw.ts).


Phase 1 — Guide user create Telegram bot

Đọc references/telegram-bot-setup.md cho hướng dẫn chi tiết với screenshot text. Tóm tắt 3 bước cho user:

Bước 1 — Tạo bot mới qua @BotFather

1. Mở Telegram (mobile/desktop)
2. Search: @BotFather → bấm Start
3. Gõ lệnh: /newbot
4. BotFather hỏi tên bot — nhập tên hiển thị (ví dụ: "Đơn Hàng Brand X")
5. BotFather hỏi username — nhập username kết thúc bằng "_bot" (ví dụ: brandx_orders_bot) — phải unique global
6. BotFather trả về message kèm dòng:
   "Use this token to access the HTTP API: 1234567890:AAxxxxxxxxxxxxxxxxxxxxxx"
7. Copy token đó, paste cho em.

LƯU Ý BẢO MẬT: Token này = quyền điều khiển bot. Đừng commit lên Git, đừng share public. Nếu lỡ leak → quay lại @BotFather → /token → /revoke để tạo token mới.

Bước 2 — Lấy chat_id

Chat 1-1 (chỉ owner nhận) — recommended cho solopreneur:

1. Trong Telegram, search: @userinfobot → bấm Start
2. Bot trả về message kèm "Id: 123456789" — copy số đó.
3. Đó là chat_id của anh/chị.

Group chat (team sale cùng nhận) — recommended cho team:

1. Tạo group Telegram mới (hoặc dùng group có sẵn)
2. Bấm tên group → Add member → search bot vừa tạo (theo username @brandx_orders_bot) → Add
3. Trong group, gõ: /start@brandx_orders_bot (hoặc bất kỳ tin nhắn nào)
4. Mở browser, truy cập:
   https://api.telegram.org/bot<TOKEN>/getUpdates
   (thay <TOKEN> bằng token bước 1)
5. Trong JSON response, tìm "chat":{"id":-1001234567890,...} — copy số đó (nhớ giữ dấu trừ nếu có)
6. Đó là chat_id của group.

Channel (broadcast 1 chiều, không thảo luận) — chat_id bắt đầu bằng -100. Cách lấy giống group nhưng add bot làm admin của channel.

Bước 3 — Paste cho skill

Đợi user paste:

TELEGRAM_BOT_TOKEN=1234567890:AAxxxxxxxxxxxxxxxxxxxxxx
TELEGRAM_CHAT_ID=123456789

GATE QUAN TRỌNG: KHÔNG đi tiếp Phase 2 cho đến khi user paste cả 2 giá trị. Nếu user nói "em đã có rồi, ở chỗ khác" → vẫn đợi paste, vì cần inject vào .env.local trong Phase 3.


Phase 2 — Draft message format

Default format tiếng Việt, mobile-friendly (Telegram render HTML):

🎉 <b>ĐƠN HÀNG MỚI</b>

👤 <b>{{name}}</b>
📞 {{phone}}
📧 {{email}}
💰 <b>{{amount}}</b> — {{product_name}}
🕐 {{time_vn}}

🔗 Mã GD: <code>{{reference_code}}</code>

Sau khi inject biến:

🎉 ĐƠN HÀNG MỚI

👤 Nguyễn Văn A
📞 0901 234 567
📧 [email protected]
💰 499.000đ — Khoá học AI Agent
🕐 14:32 14/05/2026

🔗 Mã GD: FT26134567890

Show preview cho user, hỏi:

  1. Format này OK không? Có muốn thêm/bớt field gì? (ví dụ: thêm UTM source, lượt đơn trong ngày, link Zalo khách)
  2. Emoji có quá nhiều không? (1 số user prefer plain text)
  3. Có muốn thêm 1 nút inline (CTA) link đến CRM/Sheets không? (advanced — skill có thể add inline keyboard)

Quy tắc format:

  • HTML mode (parse_mode: "HTML") — Telegram support <b>, <i>, <u>, <code>, <pre>, <a href>. KHÔNG support <br> — dùng \n.
  • Amount format VN: 499.000đ (dấu chấm phân nghìn, kèm đ cuối) HOẶC charm 499K / 1.99M cho đẹp. Đọc từ offer.json voice setting nếu có.
  • Phone format: 0901 234 567 (cách 3-3-3) — easier scan trên mobile.
  • Time VN: HH:mm dd/mm/yyyy — đặt giờ Việt Nam (UTC+7), không UTC.
  • Tránh emoji spam (>5 emoji/message → giảm professional feel).

Phase 3 — Wire code

3 thay đổi:

3.1 Tạo helper function lib/telegram.ts

Đọc templates/lib-telegram.ts (TypeScript) hoặc templates/lib-telegram.js (vanilla JS). Nội dung:

// lib/telegram.ts
type TelegramPayload = {
  name: string;
  phone: string;
  email: string;
  amount: number; // VND, raw number (vd: 499000)
  productName: string;
  referenceCode?: string;
};

export async function sendTelegramNotification(payload: TelegramPayload): Promise<void> {
  const token = process.env.TELEGRAM_BOT_TOKEN;
  const chatId = process.env.TELEGRAM_CHAT_ID;

  if (!token || !chatId) {
    console.error('[telegram] Missing TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID');
    return; // Không throw — Telegram fail không được block payment response
  }

  const text = formatMessage(payload);

  try {
    const res = await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        chat_id: chatId,
        text,
        parse_mode: 'HTML',
        disable_web_page_preview: true,
      }),
    });
    if (!res.ok) {
      const errBody = await res.text();
      console.error(`[telegram] Send failed: ${res.status} ${errBody}`);
    }
  } catch (err) {
    console.error('[telegram] Network error:', err);
    // Swallow — đừng để Telegram fail làm Sepay retry
  }
}

function formatMessage(p: TelegramPayload): string {
  const amountStr = p.amount.toLocaleString('vi-VN') + 'đ';
  const phoneStr = p.phone.replace(/(\d{4})(\d{3})(\d{3})/, '$1 $2 $3');
  const now = new Date().toLocaleString('vi-VN', {
    timeZone: 'Asia/Ho_Chi_Minh',
    day: '2-digit', month: '2-digit', year: 'numeric',
    hour: '2-digit', minute: '2-digit',
  });
  return [
    '🎉 <b>ĐƠN HÀNG MỚI</b>',
    '',
    `👤 <b>${escapeHtml(p.name)}</b>`,
    `📞 ${phoneStr}`,
    `📧 ${escapeHtml(p.email)}`,
    `💰 <b>${amountStr}</b> — ${escapeHtml(p.productName)}`,
    `🕐 ${now}`,
    p.referenceCode ? `\n🔗 Mã GD: <code>${escapeHtml(p.referenceCode)}</code>` : '',
  ].filter(Boolean).join('\n');
}

function escapeHtml(s: string): string {
  return s.replace(/[<>&]/g, c => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;' }[c]!));
}

Vì sao swallow error: Sepay retry webhook nếu nhận non-200. Nếu Telegram timeout/down làm webhook crash → Sepay retry → mỗi retry gửi lại email + log lại payment → user nhận 5 email + 5 Telegram alert cho 1 đơn. Telegram là best-effort side-channel, không phải critical path.

3.2 Wire vào Sepay webhook handler

Đọc handler hiện tại, locate chỗ xử lý payment success (sau khi parse Sepay payload + validate transferType === 'in'). Thêm:

// app/api/payment-success/route.ts (hoặc tương đương)
import { sendTelegramNotification } from '@/lib/telegram';
import { sendCustomerEmail, sendOwnerEmail } from '@/lib/resend'; // nếu đã có

export async function POST(req: Request) {
  const payload = await req.json();

  // [Existing] Validate Sepay auth + parse payload
  if (payload.transferType !== 'in') {
    return Response.json({ success: true }); // ignore outgoing transfer
  }

  // [Existing] Lookup lead theo content (chứa order ID) hoặc referenceCode
  const lead = await lookupLeadByTransferContent(payload.content);

  // [NEW] Fan-out 3 side effects parallel — Telegram fail không block email
  await Promise.allSettled([
    sendCustomerEmail(lead),
    sendOwnerEmail(lead),
    sendTelegramNotification({
      name: lead.name,
      phone: lead.phone,
      email: lead.email,
      amount: payload.transferAmount,
      productName: lead.productName,
      referenceCode: payload.referenceCode,
    }),
  ]);

  return Response.json({ success: true });
}

Lý do dùng Promise.allSettled thay vì Promise.all:

  • Promise.all reject nếu BẤT KỲ promise nào fail → handler throw → Sepay nhận 500 → retry → duplicate email.
  • Promise.allSettled đợi tất cả settle (resolve hoặc reject), không throw. Email/Telegram fail vẫn return 200 cho Sepay.

Nếu user CHƯA có Resend integration (chỉ có Telegram), code đơn giản hơn:

await sendTelegramNotification({ ... }); // helper đã swallow internally
return Response.json({ success: true });

3.3 Env vars

Update .env.local:

# Telegram bot for payment alerts (lib/telegram.ts)
TELEGRAM_BOT_TOKEN=1234567890:AAxxxxxxxxxxxxxxxxxxxxxx
TELEGRAM_CHAT_ID=123456789

Check .gitignore có .env.local chưa, nếu chưa add ngay (CRITICAL — token trên Git = leak).

Reminder cho user về Vercel production:

Em đã add vào .env.local (chỉ chạy local). Trước khi deploy production, anh/chị vào Vercel dashboard → Project → Settings → Environment Variables → add TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID → chọn Production environment → Save. Sau đó redeploy (vercel --prod hoặc qua Git push).


Phase 4 — Test plan

3 cấp test, đi từ nhẹ đến nặng. Skill KHÔNG tự chạy test 1 và 3 (cần user paste token thật + verify trên Sepay dashboard) — chỉ chạy test 2 nếu user request.

Test 1 — Bot sanity check (curl trực tiếp Telegram API)

User chạy 1 lệnh để confirm bot token + chat_id đúng:

curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
  -H "Content-Type: application/json" \
  -d '{"chat_id":"'$TELEGRAM_CHAT_ID'","text":"Test from skill ✓","parse_mode":"HTML"}'

# Expect:
# - Response: {"ok":true,"result":{...}}
# - Telegram chat: tin nhắn "Test from skill ✓" xuất hiện trong < 2s

Nếu fail:

  • {"ok":false,"description":"Unauthorized"} → token sai (typo hoặc đã revoke).
  • {"ok":false,"description":"Bad Request: chat not found"} → chat_id sai HOẶC bot chưa được add vào group/channel.
  • {"ok":false,"description":"chat_id is empty"} → biến env chưa load (chạy source .env.local trước hoặc dùng dotenv-cli).

Test 2 — Simulated Sepay payload (localhost API endpoint)

Sau khi npm run dev, gửi payload mock vào webhook route:

curl -X POST http://localhost:3000/api/payment-success \
  -H "Content-Type: application/json" \
  -d '{
    "id": "test-001",
    "gateway": "Vietcombank",
    "transactionDate": "2026-05-14 14:32:01",
    "accountNumber": "1023456789",
    "code": null,
    "content": "TEST DH001",
    "transferType": "in",
    "transferAmount": 499000,
    "accumulated": 1000000,
    "subAccount": null,
    "referenceCode": "FT26134567890",
    "description": "TEST DH001"
  }'

# Expect:
# - Response: {"success":true}
# - Telegram chat: 🎉 ĐƠN HÀNG MỚI ... xuất hiện
# - (Nếu có Resend) email cũng được gửi

Nếu webhook trả 500: check log npm run dev console — thường là (a) lookup lead fail vì chưa có lead với order DH001 trong DB (test data), (b) parse payload sai shape.

Test 3 — Production verify (sau deploy)

User tự test sau deploy:

  1. Deploy: vercel --prod (hoặc qua skill /biz-deploy-vercel).
  2. Đăng nhập Sepay dashboard → Webhooks → Logs → check webhook URL = production domain.
  3. Tạo 1 đơn test thực tế (nội dung chuyển khoản chứa order ID), chuyển 1.000đ vào account đã link Sepay.
  4. Trong < 30s, Telegram chat phải nhận tin nhắn ĐƠN HÀNG MỚI.
  5. Check Sepay dashboard → Webhook log → status 200 OK (không retry).

Output cuối cùng skill trả về user

Sau Phase 4, summary:

✓ Đã wire Telegram notification vào Sepay webhook

📁 File thay đổi:
- lib/telegram.ts (NEW — helper function + format)
- app/api/payment-success/route.ts (MODIFIED — add sendTelegramNotification vào Promise.allSettled)
- .env.local (MODIFIED — add TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID)
- .gitignore (verified — .env.local đã ignore)

🤖 Bot setup:
- Bot tên: Đơn Hàng Brand X (@brandx_orders_bot)
- Chat target: 1-1 với owner (chat_id: 123456789)

🧪 Test ngay:
1. Local: curl test bot direct (xem Phase 4 — Test 1)
2. Local: curl simulated Sepay payload (Test 2)
3. Production: deploy → tạo 1 đơn 1.000đ test thật (Test 3)

🔧 TODO của anh/chị (manual gate):
1. Paste TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID vào Vercel dashboard → env vars production
2. Redeploy: vercel --prod (hoặc dùng /biz-deploy-vercel)
3. Test 1 đơn thật → confirm Telegram alert nhận được

Reference files

  • references/telegram-bot-setup.md — chi tiết BotFather + chat_id retrieval (1-1, group, channel) + screenshot text
  • references/sepay-webhook-payload.md — Sepay payload schema đầy đủ + auth methods + dedup pattern
  • references/troubleshooting.md — common issues: bot token sai, chat_id sai, webhook duplicate, Telegram rate limit, message escape HTML

Templates

  • templates/lib-telegram.ts — TypeScript helper (Next.js TS project)
  • templates/lib-telegram.js — JS variant (Next.js JS hoặc Vite/static)
  • templates/webhook-integration-snippet.ts — code snippet wire vào webhook handler (App Router + Pages Router variants)

Anti-pattern (đừng làm)

  • ❌ Throw error trong sendTelegramNotification → webhook trả 500 → Sepay retry → duplicate alert + duplicate email. Always swallow.
  • ❌ Dùng Promise.all thay vì Promise.allSettled → 1 fail block tất cả.
  • ❌ Hardcode bot token trong source → leak Git. Luôn qua env var.
  • ❌ Tạo route mới /api/telegram-notify riêng và gọi từ webhook handler qua fetch('/api/telegram-notify') → thêm 1 round-trip không cần thiết, network failure surface tăng. Inline vào webhook handler là đủ.
  • ❌ Quên parse_mode: 'HTML' → tag <b> hiện thành plain text trong Telegram.
  • ❌ Quên escape HTML user input (tên khách có thể chứa <, >) → Telegram trả Bad Request: can't parse entities.
  • ❌ Format amount sai locale: 499000 (raw) hoặc $499 thay vì 499.000đ → owner phải đoán đơn vị tiền.
  • ❌ Send Telegram trên CẢ transferType: 'out' → owner nhận alert mỗi khi anh/chị chuyển TIỀN RA → spam. Chỉ gửi khi transferType === 'in'.
  • ❌ Gửi quá chi tiết (full payload Sepay raw) → khó đọc trên mobile, format đẹp gọn quan trọng hơn completeness.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Best practices for Remotion - Video creation in React

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Scaffold trang `/admin` CRM-style trực quan vào Next.js project (Supabase backend) — KHÔNG còn variant Vercel KV. Layout 1CRM-inspired: sidebar 3 tab (Tổng quan / Khách hàng / Email marketing) + popup nhập mã đăng nhập. **Tab Tổng quan**: 4 KPI cards (Tổng leads / Đã thanh toán / Tỷ lệ chuyển đổi / Doanh thu) có delta arrow vs kỳ trước, line chart leads+paid+revenue theo ngày dùng `recharts`, donut trạng thái paid/pending/expired, period selector pill 7d/30d/90d, list 5 thanh toán gần nhất. **Tab Khách hàng**: bảng leads + search + filter trạng thái + filter ngày + CSV export. **Tab Email marketing**: form soạn chiến dịch (tên + tiêu đề + body chọn định dạng **văn bản thường HOẶC HTML** + placeholder `{{name}}` + audience 4 loại all/paid/pending/last_days + tên người gửi) — có nút **Xem trước** mở modal iframe render đúng email thật (text → auto convert + wrap khung; HTML fragment → wrap khung, HTML đầy đủ giữ nguyên) → gửi bulk qua SMTP (reuse `nodemailer` của `biz-email-setup` — KHÔN

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Deploy landing page / website (React, Next.js, Vite, static HTML) lên Vercel — tự kiểm tra Vercel CLI hoặc Vercel MCP đã cài chưa, nếu chưa thì cài qua npm/pnpm và hướng dẫn user authenticate (vercel login), tự detect framework (Next.js / Vite / CRA / static), auto-generate vercel.json khi cần, chạy `vercel --prod`, trả về live URL + inspect URL + hướng dẫn custom domain. USE WHEN user says 'deploy lên vercel', 'đẩy landing page lên vercel', 'publish website', 'go live', 'deploy nextjs', 'deploy react', 'deploy vite', 'tạo URL live', 'đưa lên production', 'biz-deploy-vercel', 'host landing page', 'vercel deploy', 'ship lên vercel', 'đẩy code lên vercel'. Skill này áp dụng cho cả case dùng Vercel CLI lẫn Vercel MCP server.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Setup SMTP-based email auto-responder cho landing page bán hàng — tự động gửi email sau khi user điền form (tên/SĐT/email), KHÔNG cố định vào provider nào. Skill này (1) hỏi user chọn SMTP provider (Gmail/Google Workspace, Resend SMTP, SendGrid, Mailgun, Brevo, Zoho, hoặc SMTP custom của hosting VN) và detect Next.js stack đang dùng, (2) đọc context từ `offer.json` + `conversion-copy.md` (từ `/biz-offer-alex-hormozi`) HOẶC đọc trực tiếp Next.js project đã build qua `ui-ux-pro-max` (file `app/page.tsx`) HOẶC đọc HTML landing page đã deploy để hiểu offer, dream outcome, mechanism, bonus. Lưu ý: skill `biz-sales-page-layout` đã DEPRECATED 2026-05-14 — pipeline mới skip layout/copy.json, (3) **draft 2 email**: email A — auto-responder gửi cho lead (warm welcome + deliver lead magnet/booking confirm/payment link tuỳ offer type), email B — notification gửi cho owner (lead alert + thông tin liên hệ), (4) **show draft cho user duyệt và chỉnh sửa** trước khi wire vào code, (5) cài `nodemailer`

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Cài chatbot AI dạng floating widget góc dưới phải vào project Next.js có sẵn (App Router hoặc Pages Router), responsive đầy đủ web + tablet + mobile, gọi LLM qua OpenRouter (mặc định google/gemini-3-flash-preview, có thể đổi sang anthropic/claude-sonnet-4.6), streaming response, RENDER MARKDOWN trong assistant bubble (bold / list / link / code) qua react-markdown + remark-gfm — KHÔNG hiện raw **bold** dạng text — có knowledge base từ FAQ + tài liệu sản phẩm/khóa học do user cung cấp để chatbot tự động trả lời khách hàng, và TỰ ĐỘNG EXTRACT + LƯU LEAD (tên + SĐT + email) vào Upstash Redis/Vercel KV dưới namespace `chat-lead:{phone}` khi user cung cấp đầy đủ thông tin trong chat — dedupe theo SĐT, TTL 90 ngày, fire-and-forget không block stream. Skill tự detect số lượng project Next.js — nếu có nhiều hơn 1 sẽ hỏi user chọn project nào để cài; tự detect TypeScript/JS, App Router/Pages Router, Tailwind/CSS module, KV/Upstash availability (skip auto-save nếu chưa có); tự install react-markd

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Đóng gói offer **không thể chối từ** (grand slam offer) theo phương pháp Alex Hormozi ($100M Offers) — nhận input pains/gains/customer jobs theo Value Proposition Design (Osterwalder 2014) + sản phẩm/dịch vụ, rồi sinh ra: (1) markdown report tiếng Việt đầy đủ Value Equation scoring + Core Offer + 3-4 Bonus stack + Guarantee + Urgency + Pricing, (2) offer.json structured cho pipeline downstream (`ui-ux-pro-max` build sales page production-ready), (3) conversion copy block (headline + subheadline + CTA) sẵn sàng paste landing page. 2 input mode: (B) user paste sẵn pains + gains + product, (C) user chỉ có sản phẩm — skill phỏng vấn theo VPD framework để surface pain/gain trước. Bonus stack mode hybrid: skill brainstorm 5-7 candidate có justification value rồi user pick/chỉnh 3-4 cái. Tiếng Việt thuần (xưng anh/chị), giá VND charm pricing (X99K), 3-tier decoy structure. USE WHEN user says 'đóng gói offer', 'tạo offer', 'thiết kế offer Hormozi', 'grand slam offer', 'value stack', 'offer sta

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Nâng cấp copy sales page tiếng Việt — biến copy có sẵn (từ `offer.json` + `conversion-copy.md` của `/biz-offer-alex-hormozi`, HOẶC từ Next.js project đã build qua `ui-ux-pro-max`, HOẶC user paste block copy bất kỳ) thành **copy chất lượng cao chốt đơn**: headline punchy hơn, pain agitation đau hơn, mechanism rõ hơn, benefit cụ thể hơn, FAQ xử lý đúng objection của thị trường VN, CTA mạnh hơn. Skill này có 3 INTENSITY LEVEL: (1) **Polish** — light edit + power word + sửa nhịp; (2) **Conversion-optimized** — rewrite 5 block critical + tạo A/B variants cho hero/CTA; (3) **Sales-letter** — long-form story-driven full rewrite kèm P.S.. Mỗi section áp dụng đúng formula thuyết phục (PAR cho pain, BAB cho solution, FEP cho benefit, PVEN cho final CTA, Star-Chain-Hook cho testimonial). Output gồm 4 file: `copy-upgraded.md`, `copy-variants.md` (A/B test bank — 3 hero + 3 CTA + 3 final-CTA), `copy-changes.md` (diff Trước/Sau + lý do), `copy.json` (structured downstream). Giọng văn tiếng Việt thuầ

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

⚠️ DEPRECATED (2026-05-14) — Skill này KHÔNG còn trong pipeline chuẩn. Quy trình mới đã bỏ bước wireframe markdown trung gian: sau `/biz-offer-alex-hormozi` đi THẲNG sang `ui-ux-pro-max` để build Next.js sales page production-ready (có design language nhất quán: style + palette + font pairing, không phải Tailwind defaults). Lý do deprecate: wireframe markdown làm thêm 1 bước trung gian không tạo giá trị — `ui-ux-pro-max` đọc trực tiếp `offer.json` đã đầy đủ anchor + core + bonus + guarantee + urgency + pricing rồi ra HTML production luôn. Chỉ dùng skill này KHI user EXPLICITLY yêu cầu wireframe markdown để review/in/share trước khi build code (rare case). KHÔNG trigger trên các keyword 'tạo sales page', 'tạo landing page bán hàng' nữa — những keyword đó giờ trigger `ui-ux-pro-max`.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

End-to-end Sepay VietQR payment infrastructure cho landing page Next.js — guide user setup Sepay account + bank linking + scaffold đầy đủ payment flow trong project. Skill này xử lý 7 phase: (0) detect Next.js stack + **HỎI user chọn lead store backend** (Vercel KV hoặc Supabase Free Postgres — default recommend Supabase do free tier rộng hơn ~50× + có SQL + admin UI), (1) GUIDE user đăng ký Sepay account tại my.sepay.vn + link bank account VN (Vietcombank/Techcombank/MB/ACB/VPBank/BIDV/...) + lấy API key + setup webhook URL trên Sepay dashboard, (2A nếu chọn KV) setup Vercel KV (install `@vercel/kv` + KV namespace + 4 env vars KV_*), (2B nếu chọn Supabase) setup Supabase project (tạo project tại Singapore region + chạy migration SQL tạo 4 bảng leads/phone_index/order_counter/webhook_dedup với pg_cron TTL cleanup + RLS deny-all + lấy SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY + Vercel Cron ping `/api/health` mỗi 6 ngày chống auto-pause), (3) scaffold `lib/leads-store.ts` (thin re-export

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Convert a single MP3 voiceover file into a single HeyGen avatar lip-sync MP4 video. Single-purpose — no planning, no SRT, no chunking, no Remotion compositing. Hybrid path — REST upload (helper script) + HeyGen MCP for video creation, because the post-2026 MCP no longer exposes an asset-upload tool. Reads avatar look pool (`HEYGEN_AVATAR_LOOKS`) from `.env` and HeyGen API key (`HEYGEN_API_KEY`) for the upload step. USE WHEN user says "tạo video heygen từ mp3", "mp3 to heygen", "heygen mp4 từ audio", "convert mp3 sang heygen video", "tạo avatar video từ file mp3", "lip sync mp3 heygen", "biến mp3 thành video heygen", or any time the user has exactly one MP3 file and wants exactly one HeyGen avatar MP4 out.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Demand-side market research cho digital product / service ở Việt Nam — đo cầu thật + WTP + channel-market fit + defensibility qua keyword volume, marketplace sales (Shopee/Unica/Edumall/Gitiho/KyNa), community signal (FB groups, TikTok hashtag, Zalo OA), competitor pricing, unit economics — không phải macro PESTEL. Output: keyword portfolio + Niche Score 100 điểm 7-dimension có evidence + go/no-go decision + falsification protocol. Force triangulate ≥3 sources cho mỗi claim quan trọng. USE WHEN user says 'nghiên cứu thị trường', 'market research', 'validate niche', 'keyword research', 'demand validation', 'niche có nên làm không', 'có ai làm chưa', 'thị trường đủ lớn không', 'có lời không', 'chọn ngách', 'kiểm tra cầu'. Cũng trigger khi user mô tả 1 ý tưởng kinh doanh cụ thể và muốn check feasibility. Skill này KHÔNG fit: high-ticket transactional sale (BĐS, ô tô), B2B enterprise (sales cycle 6+ tháng), hyper-local geo micro-market (1 toà nhà, 1 phường). KHÔNG làm: macro PESTEL/Porter

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Transcribe an MP3 voiceover into an SRT subtitle file and a word-level segments JSON using local OpenAI Whisper. Input is a single MP3 path; output is `<stem>.srt` + `<stem>_segments.json` written next to the MP3 (or to a custom output dir). USE WHEN the user says 'extract srt from mp3', 'trích srt từ mp3', 'transcribe voiceover', 'tạo file srt và segments', 'whisper transcribe', 'bóc transcript có timestamp', 'extract voiceover segments', 'tách SRT từ audio', or any time a downstream skill (plan-short-video-edit, heygen-remotion-short-video-editor) needs an SRT + word-level segments JSON from a voiceover MP3 and one does not already exist.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Convert Vietnamese/English script text to MP3 voiceover using ElevenLabs TTS API. Calls POST /v1/text-to-speech/{voice_id}, streams audio bytes, writes MP3 directly. Locked to Hoang's brand voice ID by default. USE WHEN user says 'tạo mp3 elevenlabs', 'elevenlabs tts', 'eleven labs voice', 'text to speech elevenlabs', 'tạo voiceover elevenlabs', 'đọc text bằng elevenlabs', 'tts elevenlabs to mp3', 'eleven labs script to mp3', 'voiceover bằng elevenlabs', 'giọng elevenlabs'.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

End-to-end LANDSCAPE 16:9 (1920×1080) talking-head + slide knowledge video pipeline. Orchestrator 3 phase ghép `mkt-elevenlabs-tts-to-mp3` + `heygen-mp3-to-mp4` + HyperFrames để ra MP4 dạng "podcast keynote" — HeyGen avatar trong floating frame bo tròn bên phải (claude-orange border), slide modern AI / Claude editorial bên trái, có PIP zoom-out moments (slide full-screen, avatar shrink xuống corner thumbnail), breathing zoom + beat-driven punch-in trên avatar, optional cream-paper hand-drawn editorial infographic ở root layer (KHÔNG embed trong composition). Phase 3 fan-out N parallel LLM sub-agents (1 per scene) để author composition HTML từ visual-plan.json — không dùng Python template generator. Hỗ trợ listicle 6+ scenes (tip-1..tip-N) với scene-{num}.html naming. Resume mode: nếu MP3 + source.mp4 đã tồn tại thì skip Phase 1 + 2, vào thẳng Phase 3. USE WHEN user nói "tạo video 16:9", "video ngang", "video landscape", "podcast keynote video", "talking head + slide video", "slide + av

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

End-to-end short-video pipeline — từ kịch bản (Việt/Anh) ra MP4 TikTok/Reels 9:16 hoàn chỉnh. Orchestrator 3 phase ghép skill có sẵn — (1) TTS provider chọn được giữa `mkt-elevenlabs-tts-to-mp3` (default, voice của Hoàng) hoặc `mkt-video-script-to-mp3` (MiniMax speech-2.8-hd), (2) checkpoint user duyệt MP3, (3) `heygen-mp3-to-mp4` lip-sync avatar HeyGen, (4) delegate Phase 3 packaging cho sub-agent `mkt-full-video-phase3-packager` (transcribe + scene outline + checkpoint + fan-out N scene writers parallel + scaffold + preview Studio). API key + voice ID cho cả 2 provider lưu ở `.env`. USE WHEN user nói "tạo full video từ script", "script to tiktok video", "pipeline full video heygen + hyperframe", "tạo video từ kịch bản đến mp4", "elevenlabs heygen hyperframe full pipeline", "minimax heygen pipeline", "kịch bản ra video tiktok", hoặc có sẵn 1 script + (optional) ảnh b-roll và muốn ra MP4 9:16 đóng gói có captions, SFX, b-roll.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Build a TikTok/Reels/YouTube **landscape 16:9 (1920×1080)** short video from a pre-recorded talking-head MP4 using HyperFrames — transcribe Vietnamese audio, scaffold a complete preview-ready 1920×1080 project with a slide pane (1200px) on the left and a floating avatar frame on the right, GSAP timeline có SPLIT↔PIP transitions (slide full-screen + avatar PIP corner thumbnail), breathing yoyo zoom + beat-driven punch-in trên avatar, 5 scene compositions kiểu "tier-row" pattern (post-stack hook / chats-stack problem / hero-orb solution / counter-row recap / terminal-row CTA), optional cream-paper editorial infographic image slots với `<img onerror>` fallback, 6 SFX wired tại scene boundaries, **NO captions** (giọng avatar là spine). USE WHEN user nói "edit video 16:9 từ HeyGen MP4", "scaffold project landscape", "podcast keynote editor", "talking head + slide editor", "build hyperframes 1920×1080", "tạo project 16:9 từ avatar mp4", "scaffold 16:9 hyperframes", "hyperframe landscape vide

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Build a TikTok/Reels 9:16 short video from a pre-recorded talking-head MP4 using HyperFrames — transcribe Vietnamese audio, auto-clean Whisper errors, generate synced caption groups, scaffold a complete preview-ready project with face-cam + 6 sound effects + 4 zoom hooks + ambient ken-burns drifts + full-screen b-roll scenes (each with rich GSAP effects: pulse rings, corner marks, word-slam titles, count-up numbers, light sweeps, scribble underlines, sparkles, particles) + caption track + CTA finale, then open the HyperFrames preview Studio for user review (NEVER auto-render MP4). USE WHEN user says "tạo video tiktok từ footage", "build hyperframe talking head", "tạo 9:16 video từ mp4 quay sẵn", "make tiktok video from face footage", "hyperframe video từ video quay", "tạo short video có caption + b-roll", "video chia sẻ kiến thức từ footage", "đóng gói video ngắn", or provides a 9:16 talking-head MP4 (with optional b-roll images/videos and stated purposes) asking to turn it into a fini

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Audit bài Facebook / Reels / YouTube để phát hiện 4 downward drivers (over-branding, over-production, stock imagery, standardized aesthetic) + frequency-over-quality. Mỗi anti-pattern bị drop ~75% performance nếu hiện diện. USE WHEN user says 'audit content', 'check anti pattern', 'content có lỗi gì không', 'kiểm tra over branding', 'vì sao video bị chết', 'vì sao post không ai xem', 'content audit', 'kiểm tra chất lượng content'.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Tìm viral pattern ở ngành khác (bác sĩ, luật sư, tài chính, bất động sản, thủ công) có thể apply cho niche AI/automation của Hoang. Ngách AI educator VN còn ít format được khai thác — cross-industry adaptation là blue ocean. USE WHEN user says 'tìm format ngành khác', 'cross industry research', 'học format từ ngành khác', 'blue ocean format', 'tìm format chưa ai làm', 'adapt format từ niche khác', 'cross industry viral'.

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

Rewrite CTA kiểu autocratic (Mua ngay! Follow ngay! Đăng ký liền!) sang 3 variants Democratic / Benevolent / Laissez-faire — reach 85% dân số thay vì chỉ 5% (action-based). Áp dụng cho landing page, caption FB, CTA video, email. USE WHEN user says 'rewrite cta', 'sửa cta', 'cta không autocratic', 'viết lại kêu gọi hành động', 'cta cho landing page', 'cta nhẹ nhàng hơn', 'inclusive cta', 'cta democratic benevolent'.

関連スキル