2026-05-26 13:37:55 +08:00
|
|
|
const fetch = require('node-fetch')
|
|
|
|
|
|
|
|
|
|
const APPID = process.env.WECHAT_APPID
|
|
|
|
|
const SECRET = process.env.WECHAT_SECRET
|
|
|
|
|
|
|
|
|
|
let accessToken = null
|
|
|
|
|
let tokenExpiresAt = 0
|
|
|
|
|
|
|
|
|
|
async function getAccessToken() {
|
|
|
|
|
if (!APPID || !SECRET) {
|
|
|
|
|
throw new Error('WECHAT_APPID and WECHAT_SECRET must be configured in .env')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (accessToken && Date.now() < tokenExpiresAt) {
|
|
|
|
|
return accessToken
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const res = await fetch(
|
|
|
|
|
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}`
|
|
|
|
|
)
|
|
|
|
|
const data = await res.json()
|
|
|
|
|
|
|
|
|
|
if (data.errcode) {
|
|
|
|
|
throw new Error(`微信API错误: ${data.errmsg}`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
accessToken = data.access_token
|
|
|
|
|
tokenExpiresAt = Date.now() + (data.expires_in - 300) * 1000
|
|
|
|
|
return accessToken
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function sendSubscribeMessage(openid, templateId, data, page = '') {
|
|
|
|
|
const token = await getAccessToken()
|
|
|
|
|
|
|
|
|
|
const body = {
|
|
|
|
|
touser: openid,
|
|
|
|
|
template_id: templateId,
|
|
|
|
|
page,
|
|
|
|
|
data,
|
|
|
|
|
miniprogram_state: 'formal'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const res = await fetch(
|
|
|
|
|
`https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${token}`,
|
|
|
|
|
{
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify(body)
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
const result = await res.json()
|
|
|
|
|
|
|
|
|
|
if (result.errcode && result.errcode !== 0) {
|
|
|
|
|
console.error('发送订阅消息失败:', result)
|
|
|
|
|
return { success: false, error: result.errmsg }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { success: true }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function sendOrderStatusNotification(openid, orderId, status, totalPrice) {
|
|
|
|
|
const statusText = {
|
|
|
|
|
paid: '已付款',
|
|
|
|
|
completed: '已完成',
|
|
|
|
|
cancelled: '已取消'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const templateId = process.env.WECHAT_ORDER_TEMPLATE_ID
|
|
|
|
|
if (!templateId) {
|
|
|
|
|
console.warn('WECHAT_ORDER_TEMPLATE_ID not configured, skipping notification')
|
|
|
|
|
return { success: false, error: 'template not configured' }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sendSubscribeMessage(openid, templateId, {
|
|
|
|
|
thing1: { value: `订单 ${orderId}` },
|
|
|
|
|
phrase2: { value: statusText[status] || status },
|
|
|
|
|
amount3: { value: `¥${totalPrice}` },
|
|
|
|
|
date4: { value: new Date().toLocaleString('zh-CN') },
|
|
|
|
|
thing5: { value: '点击查看订单详情' }
|
2026-06-03 14:15:55 +08:00
|
|
|
}, `/pages/customer-extra/order-detail/order-detail?id=${orderId}`)
|
2026-05-26 13:37:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
getAccessToken,
|
|
|
|
|
sendSubscribeMessage,
|
|
|
|
|
sendOrderStatusNotification
|
|
|
|
|
}
|