Communitygithub.com

lexuanthanhhy/MKT.LANDINGPAGE.SKILLS

在 Next.js 项目中,通过 Sepay webhook 为成功付款设置 Telegram 机器人通知。该技能涵盖:创建专属 Telegram 机器人、编写可复用通知函数、集成到现有 webhook,并自动格式化越南语消息。

MKT.LANDINGPAGE.SKILLS 是什么?

此 AI Agent 技能旨在为使用 Next.js 构建的电商或落地页项目,在 Sepay 银行转账支付成功后,通过 Telegram 机器人向商家或销售团队发送实时告警。它不是一个独立应用,而是一个智能辅助工具,指导用户完成从创建 Telegram 机器人(通过 BotFather)、获取凭证(token 和 chat_id)、到将通知功能无缝集成到现有 Sepay webhook 处理逻辑中的全过程。该技能遵循一套清晰的“6 阶段”工作流程:自动检测项目中的 Sepay webhook 路由和潜在的用户数据存储(如 Vercel KV),引导用户设置机器人,提供可定制的消息模板(默认越南语,包含客户姓名、电话、金额等),并生成可直接使用的 TypeScript 代码。核心设计原则是“非侵入式且容错”:通知功能作为平行副作用执行,使用 `Promise.allSettled` 确保任何 Telegram 通知失败都不会阻塞对 Sepay 的 200 成功响应,从而避免 Sepay 的重试机制导致重复通知。该技能可在 Cursor、Claude Code 等支持技能定义的 AI 编码工具中激活,是已完成 Sepay 集成、希望获得移动端极致通知体验的越南开发者的必备工具。

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

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Biz Telegram Payment Notify — Sepay 收款实时告警

此技能将 Telegram 机器人通知 集成到项目现有的 Sepay webhook 中。每当客户成功转账到已链接 Sepay 的银行账户时,技能会通过 Telegram 机器人向所有者(或销售团队群组)实时推送一条消息——包含客户姓名、电话号码、金额、产品名称和交易时间。

设计理念:面向客户的邮件自动回复(Resend)和面向所有者的 Telegram 告警服务于两个不同的角色。邮件是给客户的正式回复。Telegram 是给所有者的“提醒”——无论您在哪里,打开 Telegram 立即看到新订单,无需在 100 封邮件中翻找 Gmail。Telegram 延迟 < 2 秒,而邮件需要 30-60 秒。

为何选择 Telegram 而非 Slack/Discord/Zalo:Telegram 机器人 5 分钟即可完成设置(1 个 token + 1 个 chat_id),无需工作空间/服务器管理员权限,免费且无限制消息,推送通知比邮件更强,拥有原生 iOS/Android/桌面应用。Slack 需要工作空间和邀请。Discord 需要服务器和权限配置。Zalo OA 需要经过认证的企业账户(审批需要 3-7 天)和月度活跃用户费用。对于所有者侧的告警,Telegram 是最简单直接的路径。

输出:代码补丁 + 分步机器人设置指南 + 环境变量列表 + 三级测试计划。技能 不会自动测试发送 Telegram——要求用户先提供 bot token 和 chat_id,因为这些凭证属于用户资产。


何时使用此技能

  • 用户刚完成 Sepay webhook 集成,希望在客户转账时通过 Telegram 接收实时通知。
  • 用户已有 /api/payment-success(或类似路由)处理 Resend 邮件,现在希望 并行 添加 Telegram 告警——不创建新路由,不重复逻辑。
  • 用户说“有订单时通知我”,并且上下文是带有 Sepay 集成的销售落地页。

不要在此情况下使用此技能:

  • 用户需要 Slack/Discord 通知 → 属于其他用例。
  • 用户需要 Zalo OA 机器人 → 需要经过认证的 OA 企业账户,结构不同且有成本。
  • 用户尚未设置 Sepay webhook(还没有链接 Sepay 的银行账户,没有接收 webhook 的端点)→ 此技能不从头设置 Sepay,仅集成到现有 webhook。
  • 用户只需要面向所有者的邮件通知 → biz-email-setup 中的邮件 B(所有者通知)已涵盖。

工作流程总览(6 个阶段)

阶段 0:检测现有的 Sepay webhook 路由(路径 + 框架 + 处理函数结构)
       ↓
阶段 0.5:检测客户数据存储(来自 /biz-setup-sepay-payment 的 lib/leads-kv.ts)
           → 是:使用实际的 KV 查找
           → 否:提供 2 个选项(原始模式 或者 先运行 /biz-setup-sepay-payment)
       ↓
阶段 1:引导用户创建 Telegram 机器人(BotFather → token → chat_id)
       ↓ [关卡——等待用户提供 token + chat_id]
       ↓
阶段 2:草拟消息格式 → 向用户显示预览 → 确认
       ↓
阶段 3:集成代码(lib/telegram.ts 辅助函数 + 集成到 webhook 处理函数 + 环境变量)
       ↓
阶段 4:测试计划(直接 curl 测试机器人 → 模拟 Sepay 负载 → 生产环境验证)

阶段 1 和阶段 4 有 等待用户的关卡,因为需要用户手动操作(获取 token、验证生产环境)。阶段 0/0.5/2/3 为技能主动执行。


阶段 0 — 检测现有的 Sepay webhook

此技能集成到现有 webhook,不创建新路由。首先定位处理函数。

查找 webhook 路由——根据常见关键词进行 grep 搜索:

# 在项目根目录下
sepay --include="*.ts" --include="*.js" --include="*.tsx" -l
grep -r "transferAmount|transferType|payment-success|payment-webhook" --include="*.ts" --include="*.js" -l
grep -r "transferAmount|transferType|payment-success|payment-webhook"

3 种常见场景:

场景已有路径操作
用户使用 biz-email-setup 流程app/api/payment-success/route.ts(或 pages/api/payment-success.ts)已有调用 sendCustomerEmail + sendOwnerEmail 的 Resend 代码将 sendTelegramNotification 通过 Promise.allSettled 集成到同一位置
用户有 Sepay 特定路由app/api/sepay-webhook/route.ts 或 app/api/webhook/sepay/route.ts在解析并验证 Sepay 负载后集成(transferType === 'in' 分支)
未找到任何路由—停止。向用户报告:“我还没有找到接收 Sepay 的 webhook 路由。请先设置 Sepay webhook(将银行账户链接到 Sepay → 创建指向您域名的 webhook URL),然后回来让我集成 Telegram。或者,如果 webhook 在其他端点,请告诉我路径。”

读取现有处理函数以了解其结构:

  • 是否对 Sepay 请求进行身份验证?(Authorization 头 Apikey 或 HMAC)
  • 是否根据 id 或 referenceCode 进行去重?(如果 Sepay 收到非 200 响应会重试 → 需要去重以避免重复通知)
  • 是否根据 content(包含订单 ID 的转账备注)查找客户?
  • 返回给 Sepay 的响应格式:{ success: true } 还是纯文本 200 OK?

如果现有处理函数 尚未去重——警告用户但不阻止。此技能仅集成 Telegram,去重是支付逻辑的职责。如有需要,建议用户单独添加去重逻辑。


阶段 0.5 — 检测客户数据存储(关键)

Sepay webhook 仅发送 content(客户转账时填写的备注)、transferAmount 和 referenceCode。Sepay 不知道客户是谁——必须根据 content(包含订单 ID)在本地存储中进行查找。

技能检查 3 项内容:

# 1. 检查客户数据存储辅助函数是否存在
test -f lib/leads-kv.ts || test -f lib/leads-kv.js && echo "lead store: ✓"

# 2. 检查 Vercel KV 环境变量
grep -E "KV_REST_API_URL|KV_REST_API_TOKEN" .env.local && echo "KV env: ✓"

# 3. 检查 @vercel/kv 是否已安装
grep '"@vercel/kv"' package.json && echo "KV package: ✓"

3 种场景:

场景操作
3 项 ✓ — 客户数据存储已就绪继续执行阶段 1,在 webhook 处理函数中使用 lib/leads-kv.ts 的 getLeadByOrderId()
缺少 1+ 项 — 尚未设置停止,向用户提供 2 个选项(见下文)

如果缺少,询问用户:

我发现您还没有客户数据存储(Vercel KV)。Sepay webhook 不知道客户是谁——只有转账备注和金额。您希望:

选项 1 — 通过 /biz-setup-sepay-payment 设置完整客户数据存储(推荐):

  • 该技能将设置 Vercel KV、构建 lib/leads-kv.ts,并连接表单提交以在用户注册时保存客户信息
  • 完成后回到这里,我将集成 Telegram,查找完整的客户姓名/电话/电子邮件
  • Telegram 告警将显示:“Nguyễn Văn A — 0901234567 — [email protected] — 499K — AI Agent 课程”

选项 2 — 原始模式:直接发送 Sepay 负载到 Telegram,不查找客户:

  • Telegram 仅显示:“Vietcombank — 499,000đ — 内容: DH001 0901234567 — 14:32”
  • 所有者通过内容自行判断客户,并与 Resend 邮件日志交叉核对
  • 简单,可立即运行,但用户体验较差

请选择 1 或 2。

如果用户选择 1 → 停止此技能,建议使用 /biz-setup-sepay-payment。用户完成设置后 → 再回到此技能。

如果用户选择 2 → 继续执行阶段 1,使用原始模板(参见 templates/lib-telegram-raw.ts)。


阶段 1 — 引导用户创建 Telegram 机器人

参考 references/telegram-bot-setup.md 获取带有文字截图的详细指南。向用户简要说明 3 个步骤:

步骤 1 — 通过 @BotFather 创建新机器人

1. 打开 Telegram(移动端/桌面端)
2. 搜索:@BotFather → 点击开始
3. 输入命令:/newbot
4. BotFather 询问机器人名称 — 输入显示名称(例如:“Brand X 订单通知”)
5. BotFather 询问用户名 — 输入以 "_bot" 结尾的用户名(例如:brandx_orders_bot)— 必须是全局唯一的
6. BotFather 返回一条消息,其中包含一行:
   "Use this token to access the HTTP API: 1234567890:AAxxxxxxxxxxxxxxxxxxxxxx"
7. 复制该 token,粘贴给我。

安全注意事项:此 token 等于对机器人的完全控制权。不要提交到 Git,不要公开分享。如果不慎泄露 → 返回 @BotFather → /token → /revoke 以生成新 token。

步骤 2 — 获取 chat_id

一对一聊天(仅所有者接收) — 推荐用于个人创业者:

1. 在 Telegram 中,搜索:@userinfobot → 点击开始
2. 机器人返回一条消息,包含 "Id: 123456789" — 复制该数字。
3. 这就是您的 chat_id。

群组聊天(销售团队共同接收) — 推荐用于团队:

1. 创建一个新的 Telegram 群组(或使用现有群组)
2. 点击群组名称 → 添加成员 → 搜索刚创建的机器人(根据用户名 @brandx_orders_bot)→ 添加
3. 在群组中,输入:/start@brandx_orders_bot(或任何消息)
4. 打开浏览器,访问:
   https://api.telegram.org/bot<TOKEN>/getUpdates
   (将 <TOKEN> 替换为步骤 1 中的 token)
5. 在 JSON 响应中,找到 "chat":{"id":-1001234567890,...} — 复制该数字(如果包含负号则保留)
6. 这就是群组的 chat_id。

频道(单向广播,不讨论)— chat_id 以 -100 开头。获取方式与群组相同,但需将机器人添加为频道管理员。

步骤 3 — 提供给技能

等待用户提供:

TELEGRAM_BOT_TOKEN=1234567890:AAxxxxxxxxxxxxxxxxxxxxxx
TELEGRAM_CHAT_ID=123456789

重要关卡:在用户提供这两个值之前,不要进入阶段 2。如果用户说“我已经有了,从其他地方”——仍然要求提供,因为需要在阶段 3 中将其注入到 .env.local。


阶段 2 — 草拟消息格式

默认越南语格式,适合移动端显示(Telegram 渲染 HTML):

🎉 <b>新订单</b>

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

🔗 交易码:<code>{{reference_code}}</code>

变量注入后效果:

🎉 新订单

👤 Nguyễn Văn A
📞 0901 234 567
📧 [email protected]
💰 499,000đ — AI Agent 课程
🕐 14:32 14/05/2026

🔗 交易码:FT26134567890

向用户显示预览,并询问:

  1. 此格式是否满意?是否需要添加/删除某些字段?(例如:添加 UTM 来源、当日订单数量、客户 Zalo 链接)
  2. 表情符号是否太多?(部分用户更喜欢纯文本)
  3. 是否希望添加一个内联按钮(CTA)链接到 CRM/Sheet?(高级功能——技能可以添加内联键盘)

格式规则:

  • HTML 模式(parse_mode: "HTML")— Telegram 支持 <b>、<i>、<u>、<code>、<pre>、<a href>。不支持 <br> — 使用 \n。
  • 越南盾金额格式:499,000đ(千位分隔符用逗号,末尾加 đ)或更美观的 499K / 1.99M。如果存在,从 offer.json 的 voice 设置中读取。
  • 电话号码格式:0901 234 567(3-3-3 分隔)— 便于在手机上快速浏览。
  • 越南时间:HH:mm dd/mm/yyyy — 设置为越南时间(UTC+7),而非 UTC。
  • 避免过度使用表情符号(单条消息超过 5 个表情符号会降低专业感)。

阶段 3 — 集成代码

3 项修改:

3.1 创建辅助函数 lib/telegram.ts

参考 templates/lib-telegram.ts(TypeScript)或 templates/lib-telegram.js(vanilla JS)。内容:

// lib/telegram.ts
type TelegramPayload = {
  name: string;
  phone: string;
  email: string;
  amount: number; // VND, raw number (e.g., 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; // 不抛出异常 — Telegram 失败不能阻止支付响应
  }

  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);
    // 吞掉异常 — 不要让 Telegram 失败导致 Sepay 重试
  }
}

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>新订单</b>',
    '',
    `👤 <b>${escapeHtml(p.name)}</b>`,
    `📞 ${phoneStr}`,
    `📧 ${escapeHtml(p.email)}`,
    `💰 <b>${amountStr}</b> — ${escapeHtml(p.productName)}`,
    `🕐 ${now}`,
    p.referenceCode ? `\n🔗 交易码:<code>${escapeHtml(p.referenceCode)}</code>` : '',
  ].filter(Boolean).join('\n');
}

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

为何吞掉异常:如果 Sepay 收到非 200 响应,它会重试 webhook。如果 Telegram 超时/宕机导致 webhook 崩溃 → Sepay 重试 → 每次重试都会再次发送邮件并记录支付 → 用户会为同一笔订单收到 5 封邮件和 5 条 Telegram 告警。Telegram 是 尽力而为的辅助通道,而非关键路径。

3.2 集成到 Sepay webhook 处理函数

读取现有处理函数,定位处理支付成功的代码段(在解析 Sepay 负载并验证 transferType === 'in' 之后)。添加:

// app/api/payment-success/route.ts (或类似路径)
import { sendTelegramNotification } from '@/lib/telegram';
import { sendCustomerEmail, sendOwnerEmail } from '@/lib/resend'; // 如果已有

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

  // [现有] 验证 Sepay 身份验证 + 解析负载
  if (payload.transferType !== 'in') {
    return Response.json({ success: true }); // 忽略转出交易
  }

  // [现有] 根据备注内容(包含订单 ID)或 referenceCode 查找客户
  const lead = await lookupLeadByTransferContent(payload.content);

  // [新] 并行展开 3 个副作用 — Telegram 失败不会阻塞邮件
  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 });
}

使用 Promise.allSettled 而非 Promise.all 的原因:

  • Promise.all 在 任何 promise 失败时都会 reject → 处理函数抛出异常 → Sepay 收到 500 → 重试 → 重复邮件。
  • Promise.allSettled 会等待所有任务完成(resolve 或 reject),不会抛出异常。

3.3 更新环境变量和 .gitignore

添加到 .env.local:

# Telegram Bot Notification
TELEGRAM_BOT_TOKEN=<paste_token>
TELEGRAM_CHAT_ID=<paste_chat_id>

验证 .gitignore 包含 .env.local:

# 如果不存在,添加
".env.local" >> .gitignore
echo "
# 如果不存在,添加
.vercel" >> .gitignore

重要:不要在代码中硬编码 token/chat_id。如果 token 泄露:

  1. 任何人都可以代表您的机器人发送消息。
  2. 恶意行为者可以向您的聊天发送垃圾消息。
  3. 步骤:返回 @BotFather → /token → /revoke → 生成新 token → 更新 .env.local。

阶段 4 — 测试计划

三级测试,从独立到集成:

级别 1 — 独立测试(curl)

在应用启动之前,在终端中直接测试 Telegram API:

# 语法:curl -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" -d "..."
TOKEN=1234567890:AAxxxxxxxxxxxxxxxxxx
CHAT_ID=123456789

curl -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
  -H "Content-Type: application/json" \
  -d '{
    "chat_id": "$CHAT_ID",
    "text": "🧪 <b>测试通知</b>\n\n从终端发送\n📆 $(date +\"%d/%m/%Y %H:%M\")",
    "parse_mode": "HTML"
  }'

期望结果:您在 Telegram 聊天中收到一条带有终端时间戳的消息。如果您没有收到:

  • 检查 token 是否正确(大小写敏感)
  • 检查 chat_id 是否正确(如果是一对一聊天,必须是正数且从 @userinfobot 获取;如果是群组/频道,通常包含负号)
  • 确保机器人是群组/频道的成员(对于群组和频道)

级别 2 — 集成测试(模拟 Sepay)

启动开发服务器,发送模拟的 Sepay 负载:

# 替换为您的 webhook URL
WEBHOOK_URL="http://localhost:3000/api/payment-success"

curl -X POST "$WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d '{
    "id": 99999,
    "referenceCode": "TEST99999",
    "transferType": "in",
    "transferAmount": 499000,
    "content": "DH001 0901234567",
    "transactionDate": "14/05/2026 14:32:00",
    "accountNumber": "1234567890"
  }'

检查:

  1. webhook 返回 { "success": true }(200)
  2. 检查服务器日志:[telegram] Sent successfully(或错误原因)
  3. 检查 Telegram 聊天:您必须收到一条格式正确的消息,包含测试金额、虚拟姓名(如果找不到客户则显示“系统客户”)

如果 webhook 返回 500:检查服务器日志中是否有错误。常见原因:

  • 环境变量未加载 → 重新启动 dev server
  • lib/telegram.ts 有语法错误 → 检查导入和类型
  • 在查找客户阶段的 lead 为 null → 原始模式应处理此情况

级别 3 — 生产环境验证

部署后,进行真实转账测试(最低 1,000 VND 或最小金额):

  1. 从您的银行账户向链接到 Sepay 的账户转账 1,000 VND
  2. 在转账备注中,输入正确的订单 ID 格式(例如:DH001)——确保与 webhook 中的 content 格式匹配
  3. 等待 30-60 秒
  4. 检查 Telegram:您必须收到一条通知,包含:付款金额(1,000đ)、根据备注查找的客户信息(姓名、电话、电子邮件)、产品名称(如果 lookupLeadByTransferContent 成功)、交易码、时间戳
  5. 检查电子邮件:客户和所有者都应该收到邮件(如果集成正确的话)
  6. 检查 Sepay 仪表盘(https://sepay.vn):加载 webhook 日志 — 确保只有 1 个成功请求(200),没有重试(如果有重试,说明您的处理函数在某些分支返回了 500)

常见错误:

错误原因修复
收到多条 Telegram 消息(2-5 条)Sepay 重试因为处理函数返回 500检查日志,处理所有分支(Promise.allSettled 不会抛出异常)
收到消息但金额为 0负载中 transferAmount 为 null/undefined检查 webhook 字段名称(一些 Sepay 版本使用 amountIn)
未收到消息,服务器日志显示“未找到 token”.env.local 未在生产环境中部署检查 Vercel 项目设置 → 环境变量
消息格式混乱(未解析的 HTML 标签)Telegram API 不支持嵌套 HTML仅允许 <b>、<i>、<u>、<code>、<pre>、<a>,不要嵌套

安全与故障排除

Token 泄露场景

如果您怀疑有人访问了您的 token:

  1. 返回 @BotFather → 输入 /token → 选择您的机器人 → 选择 /revoke → 确认
  2. BotFather 返回新 token
  3. 更新 .env.local 中的 TELEGRAM_BOT_TOKEN
  4. 重新部署

Telegram API 速率限制

Telegram 对通过 sendMessage 发送的消息数量有限制(大约每个 chat_id 每秒 20 条消息)。在正常使用中(每笔订单 1 条消息),很难达到限制。但是,如果您在 1 秒内收到多笔订单(例如促销活动、系统通知),请注意:

  • 发送后的响应不等待(fire-and-forget)
  • 如果达到限制,Telegram 返回 429 Too Many Requests,并带有 retry_after 字段(秒数)
  • 当前实现会吞掉此错误,因此您不会收到消息,但支付流程不受影响
  • 解决方案:添加一个基本队列(例如,使用 p-limit npm 包),限制为每秒 10 条消息

Webhook 重试与去重

Sepay 的超时窗口约为 10 秒。如果您的处理函数运行时间超过 10 秒或返回 5xx,Sepay 将重试(最多 5 次,间隔递增:15 秒、60 秒、5 分钟、30 分钟、2 小时)。

这是最大的风险:每次重试都会发送通知 → 客户会收到 5 封邮件 + 5 条 Telegram 消息。

为什么当前设计是安全的:

  1. Promise.allSettled 不会抛出异常 → 处理函数始终返回 200(假设没有未捕获的错误)
  2. Telegram 超时(fetch 默认 30 秒?)不会影响 webhook 响应
  3. 如果 Sepay 负载导致查找客户出错,封装的 try/catch 会捕获错误并返回 200,带有错误状态

建议:在 webhook 处理函数中添加一个简单的去重逻辑(基于 referenceCode + id):

// 在主处理函数顶部
const dedupKey = `payment:${payload.referenceCode}`;
const alreadyProcessed = await kv.get(dedupKey);
if (alreadyProcessed) {
  console.log('[payment] Duplicate webhook, skipping:', payload.referenceCode);
  return Response.json({ success: true });
}
await kv.set(dedupKey, '1', { ex: 86400 }); // 24 小时后过期

消息模板备注

越南通用字符

Telegram HTML 完全支持 Unicode,包括越南语字符(â、ê、ô、ơ、ư、ă、đ)。但是,在生产环境测试中,一些旧版 Telegram 客户端可能无法正确渲染某些字符。

安全做法:

  • 为越南语文本使用 Unicode(NFC 标准化)
  • 避免在 HTML 标签内使用组合字符
  • 使用 utf8 编码发送请求(库默认处理此问题)

电话号码格式化

formatMessage 中的正则表达式 (\d{4})(\d{3})(\d{3}) 设计用于标准的 10 位越南电话号码(例如:0901234567 → 0901 234 567)。

如果电话号码:

  • 少于 10 位:不应用格式化,返回原始字符串
  • 带有国家代码(例如:+84901234567):正则表达式将无法正确匹配 → 考虑添加辅助函数进行标准化
  • 座机号码(例如:02812345678 — 11 位):格式错误 → 可以忽略或添加条件分支

越南盾金额格式化

toLocaleString('vi-VN') 返回的格式为 499.000(千位分隔符用点号,与美国格式不同)。我们添加了 đ 符号,因此最终字符串为 499.000đ。

如果需要更美观的格式(499K 代替 499.000đ),添加一个辅助函数:

function formatAmount(amount: number): string {
  if (amount >= 1_000_000) return (amount / 1_000_000).toFixed(2) + 'M';
  if (amount >= 1_000) return Math.round(amount / 1_000) + 'K';
  return amount.toString();
}

项目结构

集成后,添加了以下文件(或根据现有模式调整):

project-root/
├── lib/
│   └── telegram.ts          # [新] Telegram 通知辅助函数
├── app/api/payment-success/
│   └── route.ts              # [修改] 添加了对 sendTelegramNotification 的调用
├── .env.local                 # [修改] 添加了 TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID
└── .gitignore                 # [验证] 确保 .env.local 被忽略

技能还可能会要求您创建 references/telegram-bot-setup.md,其中包含设置指南的文本版本(用于后续的 AI 参考)。


先决条件

您的项目必须满足:

  • Next.js(App Router 或 Pages Router)已与 TypeScript 或 JavaScript 一起设置
  • Sepay webhook 端点正在运行(例如 /api/sepay-webhook、/api/payment-success)——处理来自 Sepay 的 POST 请求
  • Vercel KV(或类似的数据存储)用于根据转账备注查找客户(原始模式可以跳过)
  • Resend(或其他邮件服务)用于面向客户/所有者的邮件通知(可选但推荐与 Telegram 并行使用)

注意:此技能不验证您是否已有 Resend,但建议从 biz-email-setup 开始,这样您就可以同时拥有邮件和 Telegram 通知。

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 orchestrator: **script → final MP4 16:9 (1920×1080) podcast-keynote**.

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

Pipeline đóng gói: từ MP4 quay sẵn (talking head 9:16) tới HyperFrames preview project hoàn chỉnh có captions, SFX, zoom hooks, b-roll toàn cảnh có hiệu ứng rich, và CTA finale.

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

相关技能