当前位置: 首页 > news >正文

微信支付

1、安装依赖及使用教程链接

(NodeJS >= v12.9.0)
npm install wechatpay-axios-plugin
https://developers.weixin.qq.com/community/develop/article/doc/000ca44ae3cff894e9fbb46ba5b413
https://gitee.com/TheNorthMemory/wechatpay-axios-plugin

 

2、接入微信商店,查看文件信息

商户号、商户证书序列号、商户私钥(apiclient_key.pem),商户证书(apiclient_cert.pem)、自己随机生成的32位秘钥、

3、初始化

const {Wechatpay, Formatter} = require('wechatpay-axios-plugin')
const wxpay = new Wechatpay({// 商户号mchid: 'your_merchant_id',// 商户证书序列号serial: 'serial_number_of_your_merchant_public_cert',// 商户API私钥 PEM格式的文本字符串或者文件bufferprivateKey: '-----BEGIN PRIVATE KEY-----\n-FULL-OF-THE-FILE-CONTENT-\n-----END PRIVATE KEY-----',certs: {// CLI `wxpay crt -m {商户号} -s {商户证书序列号} -f {商户API私钥文件路径} -k {APIv3密钥(32字节)} -o {保存地址}` 生成'serial_number': '-----BEGIN CERTIFICATE-----\n-FULL-OF-THE-FILE-CONTENT-\n-----END CERTIFICATE-----',},// APIv2密钥(32字节) v0.4 开始支持secret: 'your_merchant_secret_key_string',// 接口不要求证书情形,例如仅收款merchant对象参数可选
    merchant: {// 商户证书 PEM格式的文本字符串或者文件buffercert: '-----BEGIN CERTIFICATE-----\n-FULL-OF-THE-FILE-CONTENT-\n-----END CERTIFICATE-----',// 商户API私钥 PEM格式的文本字符串或者文件bufferkey: '-----BEGIN PRIVATE KEY-----\n-FULL-OF-THE-FILE-CONTENT-\n-----END PRIVATE KEY-----',},
})

 


4、Native下单

wxpay.v3.pay.transactions.native.post({"appid": wxAppid,"mchid": "1900006XXX","out_trade_no": "native12177525012014070332333","appid": "wxdace645e0bc2cXXX","description": "Image形象店-深圳腾大-QQ公仔","notify_url": "https://weixin.qq.com/","amount": {"total": 1,"currency": "CNY"}}).then(({data: {code_url}}) => console.info(code_url)).catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))

 

参数名变量类型[长度限制]必填描述示例
应用ID appid string[1,32] 由微信生成的应用ID,全局唯一。请求基础下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID。 wxd678efh567hg6787
直连商户号 mchid string[1,32] 直连商户的商户号,由微信支付生成并下发。 1230000109
商品描述 description string[1,127] 商品描述 Image形象店-深圳腾大-QQ公仔
商户订单号 out_trade_no string[6,32] 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 1217752501201407033233368018
交易结束时间 time_expire string[1,64] 订单失效时间,遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。 2018-06-08T10:34:56+08:00
附加数据 attach string[1,128] 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用,实际情况下只有支付完成状态才会返回该字段。 自定义数据
通知地址 notify_url string[1,256] 通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。 格式:URL
订单优惠标记 goods_tag string[1,32] 订单优惠标记 WXG
电子发票入口开放标识 support_fapiao boolean 传入true时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效。true:是 false:否 true

5、Native调起支付

6、在实例中演示

6.1 controller>wxpay.js

'use strict';
const BaseController = require('../core/base_controller');
// const moment = require('moment');
const crypto = require('crypto');
const {Rsa, Formatter} = require('wechatpay-axios-plugin')
const { readFileSync } = require('fs');
const {wx_appId,wxpaySecret} = require('../extend/indexconfig');
const helper = require("../extend/helper");// 从本地文件中加载「商户API私钥」
const merchantPrivateKeyFilePath = './app/extend/wxpay/merchant/apiclient_key.pem';class WxPayController extends BaseController {/*** 商户下单*/async pay(){const ctx = this.ctx;let entity = { ...ctx.request.body };// console.log("entity>>>>>>>>>>",entity);// 查找一下订单中有没有相同的未支付的订单const orderinfo = await ctx.service.orderInfo.searchOrderInfo(entity);// console.log(">>>>orderinfo",orderinfo);if(orderinfo&&orderinfo.code_url){this.success({code_url:orderinfo.code_url,order_no:orderinfo.order_no});return;}else{// 生成订单编号let out_trade_no = 'owl_order_'+ helper.orderNo();//订单编号// 创建订单const addOrderInfo = await ctx.service.orderInfo.addOrderInfo({user_id:entity.user_id,order_no:out_trade_no,order_status:'未支付',alarminfo_id:entity.id,total_fee:entity.amount,code_url:'',create_time:new Date(),note:entity.note})if(!addOrderInfo){this.error({message:'添加订单信息失败'});// return;
      }entity['order_no'] = out_trade_no;let code_url = await ctx.service.wxpay.prepaid(entity);if(code_url){// 更新订单地址await ctx.service.orderInfo.updateOrderInfo('code_url',code_url,entity.user_id,out_trade_no);this.success({code_url,order_no:out_trade_no});}else{this.error({message:'请求出错'})}}}// 对称解密decode(params) {const AUTH_KEY_LENGTH = 16;// ciphertext = 密文,associated_data = 随机字符串, nonce = 随机字符串const { ciphertext, associated_data, nonce } = params;// 密钥APV3const key_bytes = Buffer.from(wxpaySecret, 'utf8');// 位移const nonce_bytes = Buffer.from(nonce, 'utf8');// 填充内容const associated_data_bytes = Buffer.from(associated_data, 'utf8');// 密文Bufferconst ciphertext_bytes = Buffer.from(ciphertext, 'base64');// 计算减去16位长度const cipherdata_length = ciphertext_bytes.length - AUTH_KEY_LENGTH;// upodataconst cipherdata_bytes = ciphertext_bytes.slice(0, cipherdata_length);// tagconst auth_tag_bytes = ciphertext_bytes.slice(cipherdata_length, ciphertext_bytes.length);const decipher = crypto.createDecipheriv('aes-256-gcm', key_bytes, nonce_bytes);decipher.setAuthTag(auth_tag_bytes);decipher.setAAD(Buffer.from(associated_data_bytes));const decryptionInfo = Buffer.concat([decipher.update(cipherdata_bytes),decipher.final(),]);let req_info = decryptionInfo.toString('utf-8');//BUffer数据转化成字符串let decryption_req_info = JSON.parse(req_info);//将字符串转化成JSON数据return decryption_req_info;}// 支付结果通知async notify(){console.info('支付结果通知》》》')try {const {req:{headers,body}} = this.ctx;console.info('验证是否已重复');console.log('支付成功通知>>>',headers,body);// TODO 签名验证// headers.wechatpay-timestamp// headers.wechatpay-nonce// 解密(获得的是响应的支付信息)let decryption_req_info  = await this.decode(body.resource);console.log('解密==============>>>>:',decryption_req_info);// TODO 如何处理多线程并发控制?// 处理重复的通知let result = await this.ctx.service.orderInfo.searchOrderStatus(decryption_req_info.out_trade_no);if(result && result.order_status != '未支付'){return;}// 修改订单状态await this.ctx.service.orderInfo.updateOrderStatus(decryption_req_info);// 添加支付记录await this.ctx.service.paymentRecords.addPaymentRecordse(decryption_req_info);// 成功应答this.ctx.status = 200;} catch (error) {// 失败应答this.ctx.status = 500;this.ctx.body = {'code':'FAL','message':'失败'};}}// 查询订单状态async getOrderStatus(){const ctx = this.ctx;const { order_no } = ctx.params;let result = await this.ctx.service.orderInfo.searchOrderStatus(order_no);if(result){if(result.order_status == '支付成功'){this.success({'code':1,'message':result.order_status})}else{this.success({'code':0,'message':'支付中...'})}}else{this.error({'code':204,'message':'查找失败'})}// if(result){//   if(result.order_status == '支付成功'){//     this.success({'code':1,'message':result.order_status})//   }else if(result.order_status == '订单未支付'|| result.order_status == '用户已取消'){//     this.success({'code':2,'message':result.order_status})//   }else{//     this.success({'code':0,'message':'支付中...'})//   }// }else{//   this.error({'code':204,'message':'查找失败'})// }
    }// 取消订单(根据订单号)async cancelOrder(){const {order_no} = this.ctx.request.body;let result = await this.ctx.service.wxpay.cancelOrder(order_no);if(result.status == 204){this.success()}else{this.error(result)}}// 查询订单async searchOrder(offminute){// 获取 超过5分钟未支付的订单 的编号let resultOrderInfoNo = await this.ctx.service.orderInfo.getNoPayOrderByDuration(offminute);// console.log(resultOrderInfoNo);if(resultOrderInfoNo){resultOrderInfoNo.map( async order => {const {dataValues: {order_no}} = order;// 通过订单编号查询微信订单状态let result = await this.ctx.service.wxpay.searchOrder(order_no);if(result){const {trade_state,trade_state_desc,out_trade_no} = result;if(trade_state_desc == '支付成功'){console.info('支付成功')// 更新本地订单状态await this.ctx.service.orderInfo.updateOrderStatus(result);// 记录支付日志await this.ctx.service.paymentRecords.addPaymentRecordse(result)}if(trade_state_desc == '订单未支付'){console.info('订单未支付')// 未支付,调用微信关单功能await this.ctx.service.wxpay.closeOrder(out_trade_no);// 更新本地订单状态await this.ctx.service.orderInfo.updateOrderStatus(result);}}})  }}// 小程序查询订单状态async JAppletOrderStatus(){const ctx = this.ctx;const { order_no } = ctx.params;let result = await this.ctx.service.wxpay.searchOrder(order_no);if(result){// 支付成功if(result.trade_state == 'SUCCESS'){// 修改订单状态await this.ctx.service.orderInfo.updateOrderStatus(result);// 记录支付日志await this.ctx.service.paymentRecords.addPaymentRecordse(result)}this.success({status:result.trade_state,message:result.trade_state_desc})}else{this.error({message:"查询订单状态失败"})}}// 小程序数据签名
  JAppletSignature(prepay_id){if(!prepay_id)return;const privateKey = readFileSync(merchantPrivateKeyFilePath)const params = {appId: wx_appId,timeStamp: `${Formatter.timestamp()}`,nonceStr: Formatter.nonce(),package: `prepay_id=${prepay_id}`,signType: 'RSA',}params.paySign = Rsa.sign(Formatter.joinedByLineFeed(params.appId, params.timeStamp, params.nonceStr, params.package), privateKey)return params}// 小程序 下单async JApplet(){const ctx = this.ctx;let entity = { ...ctx.request.body };// 查找一下订单中有没有相同的未支付的订单const orderinfo = await ctx.service.orderInfo.searchOrderInfo(entity);if(orderinfo&&orderinfo.prepay_id){let prepaySignature = this.JAppletSignature(orderinfo.prepay_id);//获取签名,小程序调起支付this.success({order_no:orderinfo.order_no,prepaySignature});}else{// 生成订单编号let out_trade_no = 'owl_order_'+ helper.orderNo();//订单编号// 创建订单const addOrderInfo = await ctx.service.orderInfo.addOrderInfo({user_id:entity.user_id,order_no:out_trade_no,order_status:'未支付',alarminfo_id:entity.id,total_fee:entity.amount,code_url:'',prepay_id:'',create_time:new Date(),note:entity.note})if(!addOrderInfo){this.error({message:'添加订单信息失败'});return;}entity['order_no'] = out_trade_no;let resultPrepay = await ctx.service.wxpay.prepaidJs(entity);if(resultPrepay){let prepaySignature = this.JAppletSignature(resultPrepay.prepay_id);//获取签名,小程序调起支付// 更新订单地址await ctx.service.orderInfo.updateOrderInfo('prepay_id',resultPrepay.prepay_id,entity.user_id,out_trade_no);this.success({order_no:out_trade_no,prepaySignature});}else{this.error({message:'请求出错'})}}}// 获取微信小程序的openidasync jscode2session(){const ctx = this.ctx;const { code } = ctx.query;if(code ==null){this.error({message:'code为空'});return;}let result = await ctx.service.wxpay.getOpenId(code); if(result){this.success(result);}else{this.error({message:'获取用户openId失败'})}}
}module.exports = WxPayController;

 server>wxpay.js

'use strict';const Service = require('egg').Service;
const { Wechatpay } = require('wechatpay-axios-plugin');
const { readFileSync } = require('fs');
const moment = require('moment');
const {wx_appId,wx_appSecret,wxAppid,merchantId,merchantCertificateSerial,platformCertificateSerial,wxpaySecret} = require('../extend/indexconfig');// //绑定的公众号
// const wxAppid = '';// // 商户号,支持「普通商户/特约商户」或「服务商商户」
// const merchantId = '';// // 「商户API证书」的「证书序列号」
// const merchantCertificateSerial = '';// 从本地文件中加载「商户API私钥」
const merchantPrivateKeyFilePath = './app/extend/wxpay/merchant/apiclient_key.pem';
const merchantPrivateKeyInstance = readFileSync(merchantPrivateKeyFilePath);// // 「微信支付平台证书」的「证书序列号」,下载器下载后有提示`serial`序列号字段
// const platformCertificateSerial = '';// 从本地文件中加载「微信支付平台证书」,用来验证微信支付请求响应体的签名
const platformCertificateFilePath = './app/extend/wxpay/tmp/wechatpay_cert.pem';
const platformCertificateInstance = readFileSync(platformCertificateFilePath);const wxpay = new Wechatpay({mchid: merchantId,serial: merchantCertificateSerial,privateKey: merchantPrivateKeyInstance,certs: { [platformCertificateSerial]: platformCertificateInstance },miyao:wxpaySecret
});class WxPayService extends Service {/**  * 下单* @param {JSON} entity * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml*/async prepaid(entity) {if(!entity.user_id){return;}let result = await wxpay.v3.pay.transactions.native.post({'appid': wxAppid,'mchid': merchantId,'out_trade_no': entity.order_no,'description': '预警充值','notify_url': 'https地址对应的是controller中的notify  /wxpay/native/notify','amount': {'total': entity.amount * 100  ,//单位为分'currency': 'CNY'}})if(result){const {data: {code_url}} = result;return code_url;}else{return false;}}// 微信关单async closeOrder(order_no){try {let result = await wxpay.v3.pay.transactions.outTradeNo[order_no].close.post({mchid: merchantId});if(result.status == 204){console.info('成功关闭订单');return {status: result.status};}else{const {status, statusText, data} =result;console.info('关闭订单失败',status, statusText, data)return {status, statusText, data};}} catch (error) {}// wxpay.v3.pay.transactions.outTradeNo[order_no].close.post({mchid: merchantId})// .then(({status, statusText}) => {//   console.info(status, statusText)//   if(status == 204){//     console.log('成功关闭订单');//     return true;//   }else{//     console.info('关闭订单失败',status, statusText)//     return {status, statusText}//   }// })// .catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
  }// 用户取消订单async cancelOrder(order_no){// 微信关单接口let result = await this.closeOrder(order_no);// 更新订单状态await this.ctx.service.orderInfo.updateOrderStatus({trade_state_desc:'用户已取消',out_trade_no:order_no});return result;}// 查询订单async searchOrder(order_no){let result = await wxpay.v3.pay.transactions.outTradeNo['{out-trade-no}'].get({params: {mchid:merchantId}, 'out-trade-no': order_no});// console.log(result);if(result.status == 200){return result.data}else{return false}}// 小程序下单async prepaidJs(entity){let result = await wxpay.v3.pay.transactions.jsapi.post({'appid': wx_appId,'mchid': merchantId,'out_trade_no': entity.order_no,'description': '预警充值','notify_url': 'https://www.owl-smart.com:7002/api/wxpay/native/notify','amount': {'total': entity.amount * 100  ,//单位为分'currency': 'CNY'},'payer': {'openid': entity.openid}})if(result.status == 200){return result.data;}else{return;}}// 获取微信小程序用户的 openIdasync getOpenId(code){let url = `https://api.weixin.qq.com/sns/jscode2session?appid=${wx_appId}&secret=${wx_appSecret}&js_code=${code}&grant_type=authorization_code`let option={method:'GET',dataType: 'json',contentType: 'application/x-www-form-urlencoded'}let res = await this.app.httpclient.request(url, option);if(res.status ==200){return res.data;}else{return ;}}}module.exports = WxPayService;

 

http://www.hskmm.com/?act=detail&tid=30907

相关文章:

  • 102500410 杜惟真 10月14日作业
  • alpline 构建lnmp
  • 2025 年最新推荐操作台厂家排行榜:覆盖指挥中心 / 控制室 / 中控室 / 监控室 / 调度室场景,为用户选购优质产品提供专业参考
  • NVR设备ONVIF接入平台EasyCVR智慧小区视频监控系统建设方案
  • FPGA开发流程
  • 毕业论文技巧:Word中使用Mathtype对公式自动编号(带章节号)
  • 试验2
  • 浩辰CAD 2025 SP2安装包下载与安装教程
  • 高级程序语言设计第一次作业
  • Java word文档中的图片抽离方法
  • Kerberos认证(Elasticsearch)
  • 2025 年聚氨酯砂浆厂家最新推荐排行榜:聚焦欧洲技术与一站式服务的国内优质企业甄选指南水性聚氨酯砂浆/聚氨酯砂浆自流平厂家推荐
  • 在Anolis OS 8.10 GA上安装和配置VNC系统
  • 钩子(HOOK):改变系统行为的 “隐形抓手”
  • 浅谈InheritableThreadLocal---线程可继承的小书包
  • 2025 年涡街流量计厂家推荐,湖北南控仪表科技有限公司技术创新与行业应用解决方案解析
  • 2025 年超声波流量计厂家推荐,湖北南控仪表科技有限公司产品技术与行业应用解决方案解析
  • ArcGIS 10.2.2 字符串长度为20却仅能输入3个汉字的解决方法
  • 2025 年涡轮流量计厂家推荐:湖北南控仪表科技有限公司设备供应与多行业适配解决方案
  • OAuth/OpenID Connect安全测试全指南
  • 2025 年电磁流量计厂家推荐:湖北南控仪表科技有限公司专业设备供应与行业适配解决方案
  • 90%企业忽略的隐藏成本:Data Agent如何降低数据分析总拥有成本(TCO)
  • 123123123
  • adb logcat 根据Tag 过滤日志
  • 2025 年艺术涂料厂家最新推荐排行榜,全方位呈现优质品牌特色与竞争力
  • 爬虫遇到的问题与解
  • 自动化测试框架选型指南:数据驱动、关键字驱动还是混合模式?
  • 直播软件搭建避坑!从直播源码选型到运维,3步搞定上线+降本60%
  • LatchUtils:简化Java异步任务同步的利器
  • Qoder + ADB Supabase :5分钟GET超火AI手办生图APP