阿里的文档非常恼火,一路的坑,最后总算弄出来了。
直接上代码。。。
const fs = require('fs');
const crypto = require('crypto');
const Base = require('./base.js');
const AlipaySdk = require('alipay-sdk').default;
const AlipayFormData = require('alipay-sdk/lib/form').default;
const app_pri_key = fs.readFileSync('./path/app-pri-key.txt', 'ascii');
const ali_pub_key = fs.readFileSync('./path/ali-pub-key.txt', 'ascii');
const alipaySdk = new AlipaySdk(
{
appId : think.config('alipay').appid,
privateKey : app_pri_key,
alipayPublicKey : ali_pub_key,
sign_type : 'RSA2',
charset : 'utf-8',
}
);
module.exports = class extends Base {
verify(param) {
let map = [];
let keys = Object.keys( param ); // 获取所有的key
keys.sort(); // 排序
for( let key of keys )
{
//剔除空值及签名字段
if( key != 'sign' && key != 'sign_type' && param[key] )
{
map.push(`${key}=${param[key]}`);
}
}
let content = map.join('&');
// 准备公钥的格式
let publickey = ['-----BEGIN PUBLIC KEY-----\n'];
let i = 0;
while( i < ali_pub_key.length )
{
publickey.push( ali_pub_key.substring(i, i + 64) + '\n' );
i += 64;
}
publickey.push('-----END PUBLIC KEY-----');
let verify;
if( param["sign_type"].toUpperCase() === 'RSA2' ) {
verify = crypto.createVerify( "RSA-SHA256" );
}
else
if( param["sign_type"].toUpperCase() === 'RSA' ) {
verify = crypto.createVerify( "RSA-SHA1" );
}
if( think.isEmpty(verify) ) {
return false;
}
verify.update( content );
let retval = verify.verify( publickey.join(''), param["sign"], 'base64' );
return retval;
}
async submit_orderAction() {
let account = this.get('account'); // 账号
let item_name = this.get('item_name'); // 商品识别码(gamme server和网站必须相同)
let count = this.get('count');
let comment = this.get('comment');
if( think.isEmpty(account) ||
think.isEmpty(item_name) ||
think.isEmpty(count) )
{
return this.fail( 6000, "invalid argument" );
}
if( think.isEmpty(comment) ) {
comment = '';
}
// 查询得到商品的数据=
let item = await this.model('', 'mysql').find_item( item_name );
if( think.isEmpty(item) ) {
return this.fail( 6001, "invalid item" );
}
// 填充商品数据
let detail = item.name + " * " + count; // 比如xxx * 3
let sale_off = item.sale_off;
let original_price = item.price * count;
let actual_price = original_price * sale_off;
// 创建订单并插入到数据库中
let order_id = await this.model('', 'mysql').save(
account, item_name, detail, count, original_price, actual_price, sale_off, comment );
if( think.isEmpty(order_id) ) {
return this.fail( 6002, "create order failed" );
}
// 调用alipay,生成交易号
let formData = new AlipayFormData();
let notify = think.config('alipay').notify;
formData.setMethod ('get');
formData.addField ('notifyUrl', notify );
formData.addField ('bizContent',
{
outTradeNo : order_id.toString(), // 商户订单号,64个字符以内、可包含字母、数字、下划线,且不能重复
ProductCode : 'QUICK_MSECURITY_PAY',
totalAmount : (actual_price / 100).toString(), // 订单总金额,单位为元,精确到小数点后两位
subject : item.stencil, // 订单标题
body : item.name, // 订单描述
}
);
// result 为可以跳转到支付链接的 url
const result = await alipaySdk.exec(
'alipay.trade.app.pay', // 统一收单下单并支付接口
{}, // api 请求的参数(包含“公共请求参数”和“业务参数”)
{ formData: formData },
);
// 返回的url去除支付宝网关获取到orderString,可以直接给客户端请求。
return this.json(
{
order_id : order_id,
sale_off : sale_off,
original_price : original_price,
actual_price : actual_price,
detail : detail,
result : result.replace('https://openapi.alipay.com/gateway.do?',''),
}
);
}
async notifyAction() {
// should install think-payload first.
// detail: https://github.com/thinkjs/think-payload
let info = this.ctx.request.body;
if( think.isEmpty(info) ) {
return this.fail( 6000, 'invalid arguments');
}
let params = info.post;
if( params['trade_status'] != "TRADE_SUCCESS" )
{
return this.fail( 6010, 'payment failed.' );
}
// 检查参数是否合法
if( this.verify(params) == false )
{
return this.fail( 6003, "failed" );
}
let item = params['subject'];
let order_id = params['out_trade_no'];
let payment_sn = params['trade_no'];
let money = params['receipt_amount'];
// 更新订单(仅更新一个标志,反复读写不会有任何障碍)
let retval = await this.model('', 'mysql').
update_payment(order_id, payment_sn, item, parseInt(money * 100) );
if( retval == 1 ) {
return this.success( "success" );
}
// 错误的单号或则金额
return this.fail( 6012, "failed" );
}
}
最后说一句,那个密钥格式别弄错了。