alipay.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /**
  2. * 支付宝相关函数
  3. */
  4. const common = require('./common');
  5. const crypto = require('crypto');
  6. const certutil = require('./certutil');
  7. const fs = require("fs");
  8. const ALIPAY_ALGORITHM_MAPPING = {
  9. RSA: 'RSA-SHA1',
  10. RSA2: 'RSA-SHA256'
  11. }
  12. var alipay = {
  13. /**
  14. * 获取openid
  15. */
  16. async getOpenid(data) {
  17. let {
  18. config={},
  19. code,
  20. } = data;
  21. if (!config.appId) throw new Error("uni-pay配置alipay.mp节点下的appId不能为空");
  22. if (!config.privateKey) throw new Error("uni-pay配置alipay.mp节点下的privateKey不能为空");
  23. let timestamp = common.timeFormat(new Date(), "yyyy-MM-dd hh:mm:ss");
  24. let method = "alipay.system.oauth.token";
  25. let params = {
  26. timestamp,
  27. code,
  28. grant_type: "authorization_code"
  29. };
  30. let signData = this._getSign(method, params, config);
  31. // 格式化url和请求参数
  32. const { url, execParams } = this._formatUrl(signData);
  33. let res = await uniCloud.httpclient.request(url, {
  34. method: 'POST',
  35. data: execParams,
  36. dataType: 'text',
  37. });
  38. const result = JSON.parse(res.data)
  39. let response = result.alipay_system_oauth_token_response;
  40. if (res.status === 200 && response && response.user_id) {
  41. return {
  42. errCode: 0,
  43. errMsg: 'ok',
  44. openid: response.user_id,
  45. user_id: response.user_id,
  46. }
  47. } else {
  48. return {
  49. errCode: -1,
  50. errMsg: result.error_response.sub_msg
  51. }
  52. }
  53. },
  54. /**
  55. * 签名
  56. * @param {String} method 方法名
  57. * @param {Object} params 参数
  58. * @param {Object} config 配置
  59. */
  60. _getSign(method, params, config) {
  61. let signParams = Object.assign({
  62. method,
  63. app_id: config.appId,
  64. charset: config.charset || "utf-8",
  65. version: config.version || "1.0",
  66. sign_type: config.signType || "RSA2",
  67. }, params);
  68. let {
  69. alipayPublicCertContent,
  70. alipayRootCertContent,
  71. appCertContent,
  72. alipayPublicCertPath,
  73. alipayRootCertPath,
  74. appCertPath,
  75. appCertSn,
  76. alipayRootCertSn
  77. } = config;
  78. if (!alipayPublicCertContent && alipayPublicCertPath) {
  79. alipayPublicCertContent = fs.readFileSync(alipayPublicCertPath);
  80. }
  81. if (!alipayRootCertContent && alipayRootCertPath) {
  82. alipayRootCertContent = fs.readFileSync(alipayRootCertPath);
  83. }
  84. if (!appCertContent && appCertPath) {
  85. appCertContent = fs.readFileSync(appCertPath);
  86. }
  87. if (!appCertSn && appCertContent) {
  88. appCertSn = certutil.alipay.getSN(appCertContent, false);
  89. }
  90. if (!alipayRootCertSn && alipayRootCertContent) {
  91. alipayRootCertSn = certutil.alipay.getSN(alipayRootCertContent, true);
  92. }
  93. if (!appCertSn && appCertContent) {
  94. appCertSn = certutil.alipay.getSN(appCertContent, false);
  95. }
  96. if (!alipayRootCertSn && alipayRootCertContent) {
  97. alipayRootCertSn = certutil.alipay.getSN(alipayRootCertContent, true);
  98. }
  99. if (appCertSn && alipayRootCertSn) {
  100. signParams = Object.assign({
  101. app_cert_sn: appCertSn,
  102. alipay_root_cert_sn: alipayRootCertSn,
  103. }, signParams);
  104. }
  105. const bizContent = params.biz_content;
  106. if (bizContent) {
  107. signParams.biz_content = JSON.stringify(bizContent);
  108. }
  109. // 排序
  110. const decamelizeParams = this._sortObj(signParams);
  111. // 拼接url参数
  112. let signStr = this._objectToUrl(decamelizeParams);
  113. let keyType = config.keyType || 'PKCS8';
  114. const privateKeyType = keyType === 'PKCS8' ? 'PRIVATE KEY' : 'RSA PRIVATE KEY'
  115. let privateKey = this._formatKey(config.privateKey, privateKeyType);
  116. // 计算签名
  117. const sign = crypto.createSign(ALIPAY_ALGORITHM_MAPPING[signParams.sign_type])
  118. .update(signStr, 'utf8').sign(privateKey, 'base64');
  119. return Object.assign(decamelizeParams, { sign });
  120. },
  121. _formatKey(key, type) {
  122. return `-----BEGIN ${type}-----\n${key}\n-----END ${type}-----`
  123. },
  124. _sortObj(params) {
  125. let keysArr = Object.keys(params).sort();
  126. let sortObj = {};
  127. for (let i in keysArr) {
  128. sortObj[keysArr[i]] = (params[keysArr[i]]);
  129. }
  130. return sortObj;
  131. },
  132. _objectToUrl(obj) {
  133. let str = "";
  134. for (let key in obj) {
  135. if (obj[key]) {
  136. str += `&${key}=${obj[key]}`;
  137. }
  138. }
  139. if (str) str = str.substring(1);
  140. return str;
  141. },
  142. _formatUrl(params, url = "https://openapi.alipay.com/gateway.do") {
  143. let requestUrl = url;
  144. // 需要放在 url 中的参数列表
  145. const urlArgs = [
  146. 'app_id',
  147. 'method',
  148. 'format',
  149. 'charset',
  150. 'sign_type',
  151. 'sign',
  152. 'timestamp',
  153. 'version',
  154. 'notify_url',
  155. 'return_url',
  156. 'auth_token',
  157. 'app_auth_token'
  158. ]
  159. for (const key in params) {
  160. if (urlArgs.indexOf(key) > -1) {
  161. const val = encodeURIComponent(params[key])
  162. requestUrl = `${requestUrl}${requestUrl.includes('?') ? '&' : '?'
  163. }${key}=${val}`
  164. // 删除 postData 中对应的数据
  165. delete params[key]
  166. }
  167. }
  168. return { execParams: params, url: requestUrl }
  169. }
  170. };
  171. module.exports = alipay;