很想吐槽现在支付,文档资料不全,写的东西有误,文档让我给value字段用URL Encodeing,按照文档做结果无法完成通知,后来删掉所有的URL Encoding才解决问题。
而且返回参数的方式奇葩,必须用payload的模式来获取返回值,还得装一个额外的组件
https://github.com/thinkjs/think-payload
可以用这个组建来获取数据,安装流程参考github的说明,需要在中间件内加一个解析器,在middlewares.js内,增加在 Think.js的payload 解析器后面。
{
handle: 'payload',
options: {
keepExtensions: true,
limit: '5mb'
}
},
// 《---------------加在这儿
{
handle: payload,
options: {
extendTypes: {
json: ['application/x-javascript'], // will parse application/x-javascript type body in the same way as JSON type
form: ['application/thinkjs-form'], // will parse application/thinkjs-form type body in the same way as form type
text: ['application/thinkjs-text'], // will parse application/thinkjs-text type body in the same way as text type
multipart: ['application/thinkjs-multipart'], // will parse application/thinkjs-multipart type body in the same way as multipart-form type
xml: ['application/thinkjs-xml'], // will parse application/thinkjs-xml type body in the same way as xml type
}
}
},
{
handle: 'router',
options: {}
},
'logic',
'controller'
好了,接下来就是实现代码(Controller):
const Base = require('./base.js');
const crypto = require('crypto');
const { ctx } = require('./base.js');
const ThinkSessionFile = require('think-session-file');
const { Console } = require('console');
const { request } = require('http');
const { version } = require('os');
module.exports = class extends Base {
sort(map)
{
let keys = new Array();
for( var key in map ) {
keys.push( key );
}
keys.sort();
let new_map = new Map();
keys.forEach(key => {
new_map[key] = map[key];
})
return new_map;
}
signature(map, key)
{
let result = '';
for( var mapkey in map )
{
if( mapkey == 'signature' ||
mapkey == 'mhtSignature' ||
map[mapkey] == '' ) {
continue;
}
result = result + mapkey + '=' + map[mapkey] + '&';
}
let hex = crypto.createHash('md5').update(key ).digest("hex");
result += hex;
console.log( result );
result = crypto.createHash('md5').update(result).digest("hex");
return result;
}
datetime_string()
{
let now = new Date();
let day = now.getDate();
let month = now.getMonth() + 1;
let year = now.getFullYear();
let hour = now.getHours();
let minuate = now.getMinutes();
let second = now.getSeconds();
String( month ).length < 2 ? ( month = "0" + month ) : month;
String( day ).length < 2 ? ( day = "0" + day ) : day;
String( hour ).length < 2 ? ( hour = "0" + hour ) : hour;
String( minuate ).length < 2 ? ( minuate = "0" + minuate ) : minuate;
String( second ).length < 2 ? ( second = "0" + second ) : second;
let result = year + "" + month + "" + day + "" + hour + "" + minuate + "" + second;
return result;
}
async submitAction()
{
// 客户端上传,get方式
let account = this.get('account');
let item_name = this.get('item_name'); // 商品识别码
let count = this.get('count');
let pay_type = this.get('pay_type');
let comment = this.get('comment');
if( think.isEmpty(account) ||
think.isEmpty(item_name) ||
think.isEmpty(count) ||
think.isEmpty(pay_type) )
{
return this.fail( 6000, "invalid argument" );
}
if( think.isEmpty(comment) ) {
comment = '';
}
// 查询得到商品的数据(这部分自己实现)
let item = await this.model('commodity', 'mysql_nova').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('order', 'mysql_nova').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" );
}
// 生成订单的签名字符串
let map = new Map();
map['appId'] = think.config('pay.appid');
map['funcode'] = 'WP001';
map['version'] = '1.0.3';
map['mhtOrderType'] = '05';
map['deviceType'] = '01';
map['mhtOrderNo'] = order_id.toString();
map['mhtOrderName'] = item_name;
map['mhtReserved'] = item_name;
map['mhtOrderDetail'] = detail;
map['mhtOrderAmt'] = actual_price;
map['mhtOrderStartTime'] = this.datetime_string();
map['payChannelType'] = pay_type;
map['mhtCurrencyType'] = think.config('pay.currency');
map['mhtOrderTimeOut'] = think.config('pay.timeout');
map['notifyUrl'] = think.config('pay.notify');
map['mhtCharset'] = think.config('pay.charset');
map['mhtSignType'] = think.config('pay.signtype');
map['mhtSubAppId'] = think.config('pay.subappid');
map['consumerCreateIp'] = this.ctx.ip;
map = this.sort( map );
let sign = this.signature( map, think.config('pay.key') );
map['mhtSignature'] = sign;
let launch = true;
let result = "";
for( var key in map )
{
if( launch != true ) {
result += '&';
}
result = result + key + '=' + map[key];
launch = false;
}
return this.json(result);
}
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.json('success=N, extract body post failed');
}
console.log( 'notify info', info );
// 拆分字符串
let array = info.split('&');
let map = new Map();
array.forEach(key => {
let pair = key.split('=');
map[pair[0]] = decodeURIComponent(pair[1]);
});
map = this.sort( map );
let secret = this.signature( map, think.config('pay.key') );
if( secret != map['signature'] &&
secret != map['mhtSignature'] )
{
return this.json('success=N, signature failed.');
}
// 检验校验码通过,接下来就需要判断是否支付成功,
// 订单号是否正确
let state = map['tradeStatus'];
let order_id = map['mhtOrderNo'];
let payment_sn = map['nowPayOrderNo'];
if( state == 'A001' )
{
// (数据库的更新自己实现)
let retval = await this.model('order', 'mysql_nova').update_payment(order_id, payment_sn);
if( retval == 1 ) {
return this.json('success=Y');
}
}
return this.json('success=N, invalid state or order id');
}
}
这个代码测试了可用,config需要自己去重新配。这儿就不累述了。