一、参考支付宝api文档封装js

支付宝api文档参考:https://opendocs.alipay.com/apis/api_1

封装结果参考:https://blog.csdn.net/q3585914/article/details/72957548

对上面大佬的封装做了些许调整

(1) alipay.js

  1 /**
  2  * Created by ference on 2017/4/8.
  3  */
  4 
  5 var fs = require('fs');
  6 var path = require('path');
  7 var utl = require('./utl');
  8 
  9 var alipay_gate_way = 'https://openapi.alipay.com/gateway.do';
 10 var alipay_gate_way_sandbox = 'https://openapi.alipaydev.com/gateway.do';
 11 
 12 module.exports = Alipay;
 13 
 14 /**
 15  *
 16  * @param {Object} opts
 17  * @param {String} opts.appId  支付宝的appId
 18  * @param {String} opts.notifyUrl  支付宝服务器主动通知商户服务器里指定的页面http/https路径
 19  * @param {String} opts.rsaPrivate  商户私钥pem文件路径
 20  * @param {String} opts.rsaPublic  支付宝公钥pem文件路径
 21  * @param {String} opts.signType   签名方式, 'RSA' or 'RSA2'
 22  * @param {Boolean} [opts.sandbox] 是否是沙盒环境
 23  * @constructor
 24  */
 25 function Alipay(opts) {
 26     this.appId = opts.appId;
 27     this.sandbox = !!opts.sandbox;
 28     this.notifyUrl = opts.notifyUrl;
 29     this.signType = opts.signType;
 30 
 31     this.rsaPrivate = fs.readFileSync(opts.rsaPrivate, 'utf-8');
 32     this.rsaPublic = fs.readFileSync(opts.rsaPublic, 'utf-8');
 33 }
 34 
 35 var props = Alipay.prototype;
 36 
 37 props.makeParams = function(method, biz_content) {
 38     return {
 39         app_id: this.appId,
 40         method: method,
 41         format: 'JSON',
 42         charset: 'utf-8',
 43         sign_type: this.signType,
 44         timestamp: new Date().format('yyyy-MM-dd hh:mm:ss'),
 45         version: '1.0',
 46         biz_content: JSON.stringify(biz_content)
 47     };
 48 };
 49 
 50 /**
 51  * 生成支付参数供客户端使用
 52  * @param {Object} opts
 53  * @param {String} opts.subject              商品的标题/交易标题/订单标题/订单关键字等
 54  * @param {String} [opts.body]               对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body
 55  * @param {String} opts.outTradeId           商户网站唯一订单号
 56  * @param {String} [opts.timeout]            设置未付款支付宝交易的超时时间,一旦超时,该笔交易就会自动被关闭。
 57                                               当用户进入支付宝收银台页面(不包括登录页面),会触发即刻创建支付宝交易,此时开始计时。
 58                                               取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。
 59                                               该参数数值不接受小数点, 如 1.5h,可转换为 90m。
 60  * @param {String} opts.amount               订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
 61  * @param {String} [opts.sellerId]           收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID
 62  * @param {String} opts.goodsType            商品主类型:0—虚拟类商品,1—实物类商品 注:虚拟类商品不支持使用花呗渠道
 63  * @param {String} [opts.passbackParams]     公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。支付宝会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
 64  * @param {String} [opts.promoParams]        优惠参数(仅与支付宝协商后可用)
 65  * @param {String} [opts.extendParams]       业务扩展参数 https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.3oJPAi&treeId=193&articleId=105465&docType=1#kzcs
 66  * @param {String} [opts.enablePayChannels]  可用渠道,用户只能在指定渠道范围内支付。当有多个渠道时用“,”分隔。注:与disable_pay_channels互斥
 67  * @param {String} [opts.disablePayChannels] 禁用渠道,用户不可用指定渠道支付。当有多个渠道时用“,”分隔。 注:与enable_pay_channels互斥
 68  * @param {String} [opts.storeId]            商户门店编号
 69  */
 70 props.pay = function (opts) {
 71 
 72     var biz_content = {
 73         body: opts.body,
 74         subject: opts.subject,
 75         out_trade_no: opts.outTradeId,
 76         timeout_express: opts.timeout,
 77         total_amount: opts.amount,
 78         seller_id: opts.sellerId,
 79         product_code: 'QUICK_MSECURITY_PAY',
 80         goods_type: opts.goodsType,
 81         passback_params: opts.passbackParams,
 82         promo_params: opts.promoParams,
 83         extend_params: opts.extendParams,
 84         enable_pay_channels: opts.enablePayChannels,
 85         disable_pay_channels: opts.disablePayChannels,
 86         store_id: opts.storeId
 87     };
 88 
 89     var params = this.makeParams('alipay.trade.app.pay', biz_content);
 90     params.notify_url = this.notifyUrl;
 91 
 92     return utl.processParams(params, this.rsaPrivate, this.signType);
 93 };
 94 
 95 /**
 96  * 生成支付参数供web端使用
 97  * @param {Object} opts
 98  * @param {String} opts.subject              商品的标题/交易标题/订单标题/订单关键字等
 99  * @param {String} [opts.body]               对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body
100  * @param {String} opts.outTradeId           商户网站唯一订单号
101  * @param {String} [opts.timeout]            设置未付款支付宝交易的超时时间,一旦超时,该笔交易就会自动被关闭。
102                                               当用户进入支付宝收银台页面(不包括登录页面),会触发即刻创建支付宝交易,此时开始计时。
103                                               取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。
104                                               该参数数值不接受小数点, 如 1.5h,可转换为 90m。
105  * @param {String} opts.amount               订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
106  * @param {String} [opts.sellerId]           收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID
107  * @param {String} opts.goodsType            商品主类型:0—虚拟类商品,1—实物类商品 注:虚拟类商品不支持使用花呗渠道
108  * @param {String} [opts.passbackParams]     公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。支付宝会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
109  * @param {String} [opts.promoParams]        优惠参数(仅与支付宝协商后可用)
110  * @param {String} [opts.extendParams]       业务扩展参数 https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.3oJPAi&treeId=193&articleId=105465&docType=1#kzcs
111  * @param {String} [opts.enablePayChannels]  可用渠道,用户只能在指定渠道范围内支付。当有多个渠道时用“,”分隔。注:与disable_pay_channels互斥
112  * @param {String} [opts.disablePayChannels] 禁用渠道,用户不可用指定渠道支付。当有多个渠道时用“,”分隔。 注:与enable_pay_channels互斥
113  * @param {String} [opts.storeId]            商户门店编号
114  */
115 props.webPay = function (opts) {
116 
117     var biz_content = {
118         body: opts.body,
119         subject: opts.subject,
120         out_trade_no: opts.outTradeId,
121         timeout_express: opts.timeout,
122         total_amount: opts.amount,
123         seller_id: opts.sellerId,
124         product_code: 'FAST_INSTANT_TRADE_PAY',
125         goods_type: opts.goodsType,
126         passback_params: opts.passbackParams,
127         promo_params: opts.promoParams,
128         extend_params: opts.extendParams,
129         enable_pay_channels: opts.enablePayChannels,
130         disable_pay_channels: opts.disablePayChannels,
131         store_id: opts.storeId
132     };
133 
134     // var params = this.makeParams('alipay.trade.page.pay', biz_content); // PC
135     var params = this.makeParams('alipay.trade.wap.pay', biz_content);  // PHONE
136     params.notify_url = this.notifyUrl;
137 
138     return utl.processParams(params, this.rsaPrivate, this.signType);
139 };
140 
141 /**
142  * 签名校验
143  * @param {Object} response 支付宝的响应报文
144  */
145 props.signVerify = function (response) {
146     var ret = utl.copy(response);
147     var sign = ret['sign'];
148     ret.sign = undefined;
149     ret.sign_type = undefined;
150 
151     var tmp = utl.encodeParams(ret);
152     return utl.signVerify(tmp.unencode, sign, this.rsaPublic, this.signType);
153 }
154 
155 /**
156  * 查询交易状态 https://doc.open.alipay.com/doc2/apiDetail.htm?spm=a219a.7629065.0.0.PlTwKb&apiId=757&docType=4
157  * @param {Object} opts
158  * @param {String} [opts.outTradeId]    订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 tradeId,outTradeId如果同时存在优先取tradeId
159  * @param {String} [opts.tradeId]       支付宝交易号,和商户订单号不能同时为空
160  * @param {String} [opts.appAuthToken]  https://doc.open.alipay.com/doc2/detail.htm?treeId=216&articleId=105193&docType=1
161  */
162 props.query = function (opts) {
163 
164     var biz_content = {
165         out_trade_no: opts.outTradeId,
166         trade_no: opts.tradeId
167     };
168 
169     var params = {
170         app_id: this.appId,
171         method: 'alipay.trade.query',
172         format: 'JSON',
173         charset: 'utf-8',
174         sign_type: this.signType,
175         timestamp: new Date().format('yyyy-MM-dd hh:mm:ss'),
176         version: '1.0',
177         app_auth_token: opts.appAuthToken,
178         biz_content: JSON.stringify(biz_content)
179     };
180     var params = this.makeParams('alipay.trade.query', biz_content);
181     if(this.appAuthToken) {
182         params.app_auth_token = this.appAuthToken;
183     }
184 
185     var body = utl.processParams(params, this.rsaPrivate, this.signType);
186 
187     return utl.request({
188         method: 'GET',
189         url: (this.sandbox? alipay_gate_way_sandbox : alipay_gate_way) + '?' + body
190     });
191 };
192 
193 
194 /**
195  * 统一收单交易关闭接口 https://doc.open.alipay.com/doc2/apiDetail.htm?spm=a219a.7629065.0.0.6VzMcn&apiId=1058&docType=4
196  * @param {Object} opts
197  * @param {String} [opts.outTradeId]    订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 tradeId,outTradeId如果同时存在优先取tradeId
198  * @param {String} [opts.tradeId]       支付宝交易号,和商户订单号不能同时为空
199  * @param {String} [opts.operatorId]    卖家端自定义的的操作员 ID
200  * @param {String} [opts.appAuthToken]  https://doc.open.alipay.com/doc2/detail.htm?treeId=216&articleId=105193&docType=1
201  */
202 props.close = function (opts) {
203 
204     var biz_content = {
205         out_trade_no: opts.outTradeId,
206         trade_no: opts.tradeId,
207         operator_id: opts.operatorId
208     };
209 
210     var params = this.makeParams('alipay.trade.close', biz_content);
211     if(this.appAuthToken) {
212         params.app_auth_token = this.appAuthToken;
213     }
214 
215     var body = utl.processParams(params, this.rsaPrivate, this.signType);
216 
217     return utl.request({
218         method: 'GET',
219         url: (this.sandbox? alipay_gate_way_sandbox : alipay_gate_way) + '?' + body
220     });
221 };
222 
223 
224 /**
225  * 统一收单交易退款接口 https://doc.open.alipay.com/doc2/apiDetail.htm?spm=a219a.7629065.0.0.PlTwKb&apiId=759&docType=4
226  * @param {Object} opts
227  * @param {String} [opts.outTradeId]    订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 tradeId,outTradeId如果同时存在优先取tradeId
228  * @param {String} [opts.tradeId]       支付宝交易号,和商户订单号不能同时为空
229  * @param {String} [opts.operatorId]    卖家端自定义的的操作员 ID
230  * @param {String} [opts.appAuthToken]  https://doc.open.alipay.com/doc2/detail.htm?treeId=216&articleId=105193&docType=1
231  * @param {String} opts.refundAmount    需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数
232  * @param {String} [opts.refundReason]  退款的原因说明
233  * @param {String} [opts.outRequestId]  标识一次退款请求,同一笔交易多次退款需要保证唯一,如需部分退款,则此参数必传。
234  * @param {String} [opts.storeId]       商户的门店编号
235  * @param {String} [opts.terminalId]    商户的终端编号
236  */
237 props.refund  = function (opts) {
238 
239     // var biz_content = {
240     //     out_trade_no: opts.outTradeId,
241     //     trade_no: opts.tradeId,
242     //     operator_id: opts.operatorId,
243     //     refund_amount: opts.refundAmount,
244     //     refund_reason: opts.refundReason,
245     //     out_request_no: opts.outRequestId,
246     //     store_id: opts.storeId,
247     //     terminal_id: opts.terminalId
248     // };
249 
250     // var params = this.makeParams('alipay.trade.refund', biz_content);
251     // if(this.appAuthToken) {
252     //     params.app_auth_token = this.appAuthToken;
253     // }
254 
255     // var body = utl.processParams(params, this.rsaPrivate, this.signType);
256 
257     //  utl.request({
258     //     method: 'GET',
259     //     url:  body
260     // }).then(function (ret) {
261     //      console.log("***** ret.body=" + body);
262     //  });
263 };
264 
265 
266 /**
267  * 统一收单交易退款查询 https://doc.open.alipay.com/doc2/apiDetail.htm?docType=4&apiId=1049
268  * @param {Object} opts
269  * @param {String} [opts.outTradeId]    订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 tradeId,outTradeId如果同时存在优先取tradeId
270  * @param {String} [opts.tradeId]       支付宝交易号,和商户订单号不能同时为空
271  * @param {String} [opts.outRequestId]  请求退款接口时,传入的退款请求号,如果在退款请求时未传入,则该值为创建交易时的外部交易号
272  * @param {String} [opts.appAuthToken]  https://doc.open.alipay.com/doc2/detail.htm?treeId=216&articleId=105193&docType=1
273  */
274 props.refundQuery = function (opts) {
275 
276     var biz_content = {
277         out_trade_no: opts.outTradeId,
278         trade_no: opts.tradeId,
279         out_request_no: opts.outRequestId || opts.outTradeId
280     };
281 
282     var params = this.makeParams('alipay.trade.fastpay.refund.query', biz_content);
283     if(this.appAuthToken) {
284         params.app_auth_token = this.appAuthToken;
285     }
286 
287     var body = utl.processParams(params, this.rsaPrivate, this.signType);
288 
289     return utl.request({
290         method: 'GET',
291         url: (this.sandbox? alipay_gate_way_sandbox : alipay_gate_way) + '?' + body
292     });
293 };
294 
295 
296 /**
297  * 查询对账单下载地址 https://doc.open.alipay.com/doc2/apiDetail.htm?spm=a219a.7629065.0.0.iX5mPA&apiId=1054&docType=4
298  * @param {Object} opts
299  * @param {String} [opts.billType]     账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:
300                                         trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单;
301  * @param {String} [opts.billDate]     账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。
302  * @param {String} [opts.appAuthToken]  https://doc.open.alipay.com/doc2/detail.htm?treeId=216&articleId=105193&docType=1
303  */
304 props.billDownloadUrlQuery = function (opts) {
305 
306     var biz_content = {
307         bill_type: opts.billType,
308         bill_date: opts.billDate
309     };
310 
311     var params = this.makeParams('alipay.data.dataservice.bill.downloadurl.query', biz_content);
312     if(this.appAuthToken) {
313         params.app_auth_token = this.appAuthToken;
314     }
315 
316     var body = utl.processParams(params, this.rsaPrivate, this.signType);
317 
318     return utl.request({
319         method: 'GET',
320         url: (this.sandbox? alipay_gate_way_sandbox : alipay_gate_way) + '?' + body
321     });
322 };
View Code

(2) utl.js

  1 /**
  2  * Created by ference on 2017/4/8.
  3  */
  4 
  5 var crypto = require('crypto');
  6 var request = require('request');
  7 
  8 var utl = module.exports = {};
  9 
 10 Date.prototype.format = function (fmt) {
 11     var o = {
 12         "M+": this.getMonth() + 1, //月份
 13         "d+": this.getDate(), //
 14         "h+": this.getHours(), //小时
 15         "m+": this.getMinutes(), //
 16         "s+": this.getSeconds(), //
 17         "q+": Math.floor((this.getMonth() + 3) / 3), //季度
 18         "S": this.getMilliseconds() //毫秒
 19     };
 20     if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
 21     for (var k in o)
 22         if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
 23     return fmt;
 24 }
 25 
 26 /**
 27  * 浅拷贝
 28  * @param obj
 29  * @returns {{}}
 30  */
 31 utl.copy = function (obj) {
 32     var ret = {};
 33     for(var k in obj) {
 34         ret[k] = obj[k];
 35     }
 36     return ret;
 37 }
 38 
 39 /**
 40  * 对请求参数进行组装、编码、签名,返回已组装好签名的参数字符串
 41  * @param {{Object} params  请求参数
 42  * @param {String} privateKey 商户应用私钥
 43  * @param {String} [signType] 签名类型 'RSA2' or 'RSA'
 44  * @returns {String}
 45  */
 46 utl.processParams = function (params, privateKey, signType) {
 47     var ret = utl.encodeParams(params);
 48     var sign = utl.sign(ret.unencode, privateKey, signType);
 49     return ret.encode + '&sign=' + encodeURIComponent(sign);
 50 };
 51 
 52 /**
 53  * 对请求参数进行组装、编码
 54  * @param {Object} params  请求参数
 55  * @returns {Object}
 56  */
 57 utl.encodeParams = function (params) {
 58     var keys = [];
 59     for(var k in params) {
 60         var v = params[k];
 61         if (params[k] !== undefined && params[k] !== "") keys.push(k);
 62     }
 63     keys.sort();
 64 
 65     var unencodeStr = "";
 66     var encodeStr = "";
 67     var len = keys.length;
 68     for(var i = 0; i < len; ++i) {
 69         var k = keys[i];
 70         if(i !== 0) {
 71             unencodeStr += '&';
 72             encodeStr += '&';
 73         }
 74         unencodeStr += k + '=' + params[k];
 75         encodeStr += k + '=' + encodeURIComponent(params[k]);
 76     }
 77     return {unencode:unencodeStr, encode:encodeStr};
 78 };
 79 
 80 /**
 81  * 对字符串进行签名验证
 82  * @param {String} str 要验证的参数字符串
 83  * @param {String} sign 要验证的签名
 84  * @param {String} publicKey 支付宝公钥
 85  * @param {String} [signType] 签名类型
 86  * @returns {Boolean}
 87  */
 88 utl.signVerify = function (str, sign, publicKey, signType) {
 89     var verify;
 90     if(signType === 'RSA2') {
 91         verify = crypto.createVerify('RSA-SHA256');
 92     } else {
 93         verify = crypto.createVerify('RSA-SHA1');
 94     }
 95     verify.update(str, 'utf8');
 96     var result = verify.verify(publicKey, sign, 'base64');
 97     return result;
 98 };
 99 
100 /**
101  * 对字符串进行签名
102  * @param {String} str 要签名的字符串
103  * @param {String} privateKey 商户应用私钥
104  * @param {String} [signType] 签名类型
105  * @returns {String}
106  */
107 utl.sign = function (str, privateKey, signType) {
108     var sha;
109     if(signType === 'RSA2') {
110         sha = crypto.createSign('RSA-SHA256');
111     } else {
112         sha = crypto.createSign('RSA-SHA1');
113     }
114     sha.update(str, 'utf8');
115     console.log(str)
116     console.log(privateKey)
117     return sha.sign(privateKey, 'base64');
118 }
119 
120 
121 /**
122  * 发送请求 https://github.com/request/request
123  * @param {Object} opts 请求参数
124  * @param {String} opts.url 请求地址
125  * @param {String} opts.method  GET|POST|PUT...
126  * @param {String} [opts.type] text/xml | application/json | application/x-www-form-urlencoded ...
127  * @param {Object} [opts.headers] {}
128  * @param {Object} [opts.qs] query参数
129  * @param {Buffer|String|ReadStream} [opts.body] 请求体
130  * @param {Object} [opts.form] form表单
131  * @returns {Promise.<Object>} resolve({response, body})
132  */
133 utl.request = function(opts){
134     return new Promise(function(resolve, reject){
135         request(opts, function(err, res, body){
136             if(err){
137                 reject(err);
138                 return;
139             }
140             let ret = {response:res, body:body};
141             ret.ok = function() {
142                 return res.statusCode === 200;
143             };
144             ret.json = function () {
145                 if(res.body) return JSON.parse(res.body);
146                 return null;
147             };
148             resolve(ret);
149         });
150     });
151 };
View Code

(3) 调用testpay.js

 1 var express = require('express');
 2 var router = express.Router();
 3 var path = require('path');
 4 var Alipay = require('../lib/alipay');
 5 var utl = require('../lib/utl');
 6 
 7 var outTradeId = Date.now().toString();
 8 
 9 
10 
11 var ali = new Alipay({
12     appId: '',
13     notifyUrl: 'http://127.0.0.1:3000/pay/getback', // 回调参数传递
14     rsaPrivate: path.resolve('./alipay_configs/private-key.pem'),
15     rsaPublic: path.resolve('./alipay_configs/public-key.pem'),
16     sandbox: true,
17     return_url: 'http://www.baidu.com', // 要求支付宝可通过外网访问
18     signType: 'RSA' // 'RSA2'
19 });
20 
21 router.get('/getback', function(req, res, next) {
22     // req
23 });
24 
25 router.get('/', function(req, res, next) {
26     var url=  ali.webPay({
27         body: "ttt",
28         subject: "ttt1",
29         outTradeId: "201503200101010222",
30         timeout: '90m',
31         amount: "0.1",
32         sellerId: '',
33         product_code: 'FAST_INSTANT_TRADE_PAY',
34         goods_type: "1",
35         return_url:"127.0.0.1:9000",
36     })
37 
38     var url_API = 'https://openapi.alipaydev.com/gateway.do?'+url;
39     res.json({url:url_API})
40 });
41 
42 module.exports = router;
View Code

需要install  crypto、request、fs 包支持

 

二、支付宝开发平台配置

登录https://openhome.alipay.com/platform/developerIndex.htm,使用实名认证的支付宝账号

 

 

 点击后进入沙箱环境配置页面

 大致界面如下

 

点击设置密钥。

这里顺便记录下密钥的用处:

通过支付宝密钥生成工具会生成一个应用私钥+一个应用公钥,开发者通过把生成的应用公钥上传到这里配置,会得到支付宝生成的另一个公钥,记作支付宝公钥。在项目中通过应用私钥和支付宝公钥签名和之后的验签。

生成密钥的工具下载  和 具体操作流程可以参考支付宝文档 : https://opendocs.alipay.com/open/291/106097/

 

注意选择公钥不要选择证书,因为一旦选择证书就不可以使用RSA2的公钥验证了(血泪史),如果不小心使用过公钥证书,请选择RSA的密钥做验证,生成方法同RSA2

将生成的应用秘钥和支付宝公钥保存到项目中,即.pem后缀的文件(粘进来自己改后缀就行),另外需要添加密钥文件的头和尾

1 -----BEGIN RSA PRIVATE KEY-----
2 应用秘钥
3 -----END RSA PRIVATE KEY-----
1 -----BEGIN PUBLIC KEY-----
2 支付宝公钥
3 -----END PUBLIC KEY-----

最后项目路径如下(仅供参考)

 

运行项目后,生成二维码,用户端通过沙箱版支付宝客户端扫码支付即可,商家和用户账号如下