// app/api/miniprogram/qrcode/route.ts // 生成带参数的小程序码 - 绑定推荐人ID和章节ID import { NextRequest, NextResponse } from 'next/server' const APPID = process.env.WECHAT_APPID || 'wxb8bbb2b10dec74aa' const APPSECRET = process.env.WECHAT_APPSECRET || '3c1fb1f63e6e052222bbcead9d07fe0c' // 简单的内存缓存 let cachedToken: { token: string; expireAt: number } | null = null // 获取access_token(带缓存) async function getAccessToken() { // 检查缓存 if (cachedToken && cachedToken.expireAt > Date.now()) { return cachedToken.token } const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}` const res = await fetch(url) const data = await res.json() if (data.access_token) { // 缓存token,提前5分钟过期 cachedToken = { token: data.access_token, expireAt: Date.now() + (data.expires_in - 300) * 1000 } return data.access_token } throw new Error(data.errmsg || '获取access_token失败') } export async function POST(req: NextRequest) { try { const body = await req.json() const { scene, page, width = 280, chapterId, userId } = body // 构建scene参数 // 格式:ref=用户ID&ch=章节ID(用于分享海报) let finalScene = scene if (!finalScene) { const parts = [] if (userId) parts.push(`ref=${userId.slice(0, 15)}`) if (chapterId) parts.push(`ch=${chapterId}`) finalScene = parts.join('&') || 'soul' } console.log('[QRCode] 生成小程序码, scene:', finalScene) // 获取access_token const accessToken = await getAccessToken() // 生成小程序码(使用无限制生成接口) const qrcodeUrl = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${accessToken}` const qrcodeRes = await fetch(qrcodeUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ scene: finalScene.slice(0, 32), // 最多32个字符 page: page || 'pages/index/index', width: Math.min(width, 430), // 最大430 auto_color: false, line_color: { r: 0, g: 206, b: 209 }, // 品牌色 is_hyaline: false, env_version: 'trial' // 体验版,正式发布后改为 release }) }) // 检查响应类型 const contentType = qrcodeRes.headers.get('content-type') if (contentType?.includes('application/json')) { // 返回了错误信息 const errorData = await qrcodeRes.json() console.error('[QRCode] 生成失败:', errorData) return NextResponse.json({ success: false, error: errorData.errmsg || '生成小程序码失败', errcode: errorData.errcode }, { status: 200 }) // 返回200但success为false } // 返回图片 const imageBuffer = await qrcodeRes.arrayBuffer() if (imageBuffer.byteLength < 1000) { // 图片太小,可能是错误 console.error('[QRCode] 返回的图片太小:', imageBuffer.byteLength) return NextResponse.json({ success: false, error: '生成的小程序码无效' }, { status: 200 }) } const base64 = Buffer.from(imageBuffer).toString('base64') console.log('[QRCode] 生成成功,图片大小:', base64.length, '字符') return NextResponse.json({ success: true, image: `data:image/png;base64,${base64}`, scene: finalScene }) } catch (error) { console.error('[QRCode] Error:', error) return NextResponse.json({ success: false, error: '生成小程序码失败: ' + String(error) }, { status: 200 }) } }