weixin.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. const crypto = require('crypto');
  2. const md5 = require('md5');
  3. module.exports = class extends think.Service {
  4. /**
  5. * 解析微信登录用户数据
  6. * @param sessionKey
  7. * @param encryptedData
  8. * @param iv
  9. * @returns {Promise.<string>}
  10. */
  11. async decryptUserInfoData(sessionKey, encryptedData, iv) {
  12. // base64 decode
  13. const _sessionKey = Buffer.from(sessionKey, 'base64');
  14. encryptedData = Buffer.from(encryptedData, 'base64');
  15. iv = Buffer.from(iv, 'base64');
  16. let decoded = '';
  17. try {
  18. // 解密
  19. const decipher = crypto.createDecipheriv('aes-128-cbc', _sessionKey, iv);
  20. // 设置自动 padding 为 true,删除填充补位
  21. decipher.setAutoPadding(true);
  22. decoded = decipher.update(encryptedData, 'binary', 'utf8');
  23. decoded += decipher.final('utf8');
  24. decoded = JSON.parse(decoded);
  25. } catch (err) {
  26. return '';
  27. }
  28. if (decoded.watermark.appid !== think.config('weixin.appid')) {
  29. return '';
  30. }
  31. return decoded;
  32. }
  33. /**
  34. * 统一下单
  35. * @param payInfo
  36. * @returns {Promise}
  37. */
  38. createUnifiedOrder(payInfo) {
  39. const WeiXinPay = require('weixinpay');
  40. const weixinpay = new WeiXinPay({
  41. appid: think.config('weixin.appid'), // 微信小程序appid
  42. openid: payInfo.openid, // 用户openid
  43. mch_id: think.config('weixin.mch_id'), // 商户帐号ID
  44. partner_key: think.config('weixin.partner_key') // 秘钥
  45. });
  46. return new Promise((resolve, reject) => {
  47. weixinpay.createUnifiedOrder({
  48. body: payInfo.body,
  49. out_trade_no: payInfo.out_trade_no,
  50. total_fee: payInfo.total_fee,
  51. spbill_create_ip: payInfo.spbill_create_ip,
  52. notify_url: think.config('weixin.notify_url'),
  53. trade_type: 'JSAPI'
  54. }, (res) => {
  55. if (res.return_code === 'SUCCESS' && res.result_code === 'SUCCESS') {
  56. const returnParams = {
  57. 'appid': res.appid,
  58. 'timeStamp': parseInt(Date.now() / 1000) + '',
  59. 'nonceStr': res.nonce_str,
  60. 'package': 'prepay_id=' + res.prepay_id,
  61. 'signType': 'MD5'
  62. };
  63. const paramStr = `appId=${returnParams.appid}&nonceStr=${returnParams.nonceStr}&package=${returnParams.package}&signType=${returnParams.signType}&timeStamp=${returnParams.timeStamp}&key=` + think.config('weixin.partner_key');
  64. returnParams.paySign = md5(paramStr).toUpperCase();
  65. resolve(returnParams);
  66. } else {
  67. reject(res);
  68. }
  69. });
  70. });
  71. }
  72. /**
  73. * 生成排序后的支付参数 query
  74. * @param queryObj
  75. * @returns {Promise.<string>}
  76. */
  77. buildQuery(queryObj) {
  78. const sortPayOptions = {};
  79. for (const key of Object.keys(queryObj).sort()) {
  80. sortPayOptions[key] = queryObj[key];
  81. }
  82. let payOptionQuery = '';
  83. for (const key of Object.keys(sortPayOptions).sort()) {
  84. payOptionQuery += key + '=' + sortPayOptions[key] + '&';
  85. }
  86. payOptionQuery = payOptionQuery.substring(0, payOptionQuery.length - 1);
  87. return payOptionQuery;
  88. }
  89. /**
  90. * 对 query 进行签名
  91. * @param queryStr
  92. * @returns {Promise.<string>}
  93. */
  94. signQuery(queryStr) {
  95. queryStr = queryStr + '&key=' + think.config('weixin.partner_key');
  96. const md5 = require('md5');
  97. const md5Sign = md5(queryStr);
  98. return md5Sign.toUpperCase();
  99. }
  100. /**
  101. * 处理微信支付回调
  102. * @param notifyData
  103. * @returns {{}}
  104. */
  105. payNotify(notifyData) {
  106. if (think.isEmpty(notifyData)) {
  107. return false;
  108. }
  109. const notifyObj = {};
  110. let sign = '';
  111. for (const key of Object.keys(notifyData)) {
  112. if (key !== 'sign') {
  113. notifyObj[key] = notifyData[key][0];
  114. } else {
  115. sign = notifyData[key][0];
  116. }
  117. }
  118. if (notifyObj.return_code !== 'SUCCESS' || notifyObj.result_code !== 'SUCCESS') {
  119. return false;
  120. }
  121. const signString = this.signQuery(this.buildQuery(notifyObj));
  122. if (think.isEmpty(sign) || signString !== sign) {
  123. return false;
  124. }
  125. return notifyObj;
  126. }
  127. };