import crypto from "crypto" export interface WechatPayConfig { appId: string appSecret: string mchId: string apiKey: string notifyUrl: string } export class WechatPayService { constructor(private config: WechatPayConfig) {} // 创建微信支付订单(扫码支付) async createOrder(params: { outTradeNo: string body: string totalFee: number spbillCreateIp: string }) { const orderParams = { appid: this.config.appId, mch_id: this.config.mchId, nonce_str: this.generateNonceStr(), body: params.body, out_trade_no: params.outTradeNo, total_fee: Math.round(params.totalFee * 100).toString(), // 转换为分 spbill_create_ip: params.spbillCreateIp, notify_url: this.config.notifyUrl, trade_type: "NATIVE", // 扫码支付 } const sign = this.generateSign(orderParams) const xmlData = this.buildXML({ ...orderParams, sign }) // In production, make actual API call to WeChat // const response = await fetch("https://api.mch.weixin.qq.com/pay/unifiedorder", { // method: "POST", // body: xmlData, // headers: { "Content-Type": "application/xml" }, // }) // Mock response for development return { codeUrl: `weixin://wxpay/bizpayurl?pr=${this.generateNonceStr()}`, prepayId: `prepay_${Date.now()}`, outTradeNo: params.outTradeNo, } } // 生成随机字符串 private generateNonceStr(): string { return crypto.randomBytes(16).toString("hex") } // 生成签名 generateSign(params: Record): string { const sortedKeys = Object.keys(params).sort() const signString = sortedKeys .filter((key) => params[key] && key !== "sign") .map((key) => `${key}=${params[key]}`) .join("&") const signWithKey = `${signString}&key=${this.config.apiKey}` return crypto.createHash("md5").update(signWithKey, "utf8").digest("hex").toUpperCase() } // 验证签名 verifySign(params: Record): boolean { const receivedSign = params.sign if (!receivedSign) return false const calculatedSign = this.generateSign(params) return receivedSign === calculatedSign } // 构建XML数据 private buildXML(params: Record): string { const xml = [""] for (const [key, value] of Object.entries(params)) { xml.push(`<${key}>`) } xml.push("") return xml.join("") } // 解析XML数据 private async parseXML(xml: string): Promise> { const result: Record = {} const regex = /<(\w+)><\/\1>/g let match while ((match = regex.exec(xml)) !== null) { result[match[1]] = match[2] } return result } }