nodejs+koa2微信app支付,小程序支付

企业付款到零钱文档;https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2

1,搞微信支付,先看流程图

https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3,先看流程图,看懂,再看微信支付api

2.调用统一下单接口

const router = new Router()

const uuid = require(\'uuid\')

router.post(\'wxpay\', async (ctx, next) => {

//1.根据orderCode查询订单状态和付款金额,此处省略

const order = { //参数一定要按照acil码(也就是a,b,c,d)顺序来写,或者你需要按照acil码自己排序,否则会在支付时报签名错误

appid: \'在微信中的应用appid,也在商户平台中\',

body: \'迪士尼\',

mch_id: \'微信平台中的商户编号\',

nonce_str: (uuid.v4()).replace(/-/g, \'\'),

notify_url: \'要回调的ulr,一定要是外网可访问的\',

out_trade_no: orderInfo[0].orderCode,

spbill_create_ip: ctx.request.ip.replace(/::ffff:/g, \'\'),

total_fee: 1, //先1分钱

trade_type: \'APP\'

}

const objStr = objTostring(order)

const preSign = objStr + \'key=微信商户平台的key\'

order.sign = endeurl.md5(preSign).toUpperCase()

const xml = objToXml(order)

//调用统一下单接口

const data = await request.postAsync({

url: \'https://api.mch.weixin.qq.com/pay/unifiedorder\',

body: xml

})

const result = await parseStringAsync(data.body)

if (result.xml.result_code[0] == \'FAIL\') {

throw {

message: \'orderStatus wrong\'

}

}

//字符串必需按照顺序来

const paysign2 = {

appid: result.xml.appid[0],

noncestr: result.xml.nonce_str[0],

package: \'Sign=WXPay\',

partnerid: result.xml.mch_id[0],

prepayid: result.xml.prepay_id[0],

timestamp: parseInt(Date.now() / 1000).toString() //注意:时间必需为秒

}

const payPrestr = objTostring(paysign2) + \'key=微信商户平台的key\' //不知道的话,可以问老板

paysign2.sign = endeurl.md5(payPrestr).toUpperCase()

//二次签名,返回给app即可,由app端进行微信支付吊起

ctx.body = {

paysign2

}

})

3.工具类

const xml2js = require(\'xml2js\')

const Parser = new xml2js.Parser()

exports.parseStringAsync = xml => {

return new Promise((resolve, reject) => {

Parser.parseString(xml, (err, result) => {

if (err) {

reject(err)

} else {

resolve(result)

}

})

})

}

exports.objTostring = obj => {

var preSign = \'\';

for (let key in obj) {

preSign += `${key}=${obj[key]}&`

}

return preSign

}

exports.objToXml = obj => {

let xml = \'<xml>\'

for (let key in obj) {

xml += `<${key}>${obj[key]}</${key}>`

}

xml += \'</xml>\'

return xml

}

3.微信回调你在第二步是的notify_url值

router.post(\'wxNotify\', async (ctx, next) => {

//获取微信返回的参数值,查询订单状态

const data = ctx.params

var payQuery = {

appid: data.xml.appid[0],

mch_id: data.xml.mch_id[0],

nonce_str: data.xml.nonce_str[0],

out_trade_no: data.xml.out_trade_no[0],

transaction_id: data.xml.transaction_id[0]

}

let payQueryString = objTostring(payQuery) + \'key=微信商户平台的key\'

payQuery.sign = endeurl.md5(payQueryString).toUpperCase()

//查询订单是否支付成功

const data1 = await request.postAsync({

url: \'https://api.mch.weixin.qq.com/pay/orderquery\',

body: services.pay.objToXml(payQuery)

})

const result = await parseStringAsync(data1.body)

if (result.xml.return_code[0] && result.xml.return_code[0] == \'SUCCESS\' && result.xml.trade_state[0] == \'SUCCESS\' ) {

//告诉微信,你收到支付结果通知了

const resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" +

"<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "

ctx.body = {

resXml

}

//从result中比较价格和你订单中的金额是否一致,,进行后台业务处理,此处省略

})

总结:

1.参数一定要按照acil码(也就是a,b,c,d)顺序来写,或者你需要按照acil码自己排序(Object.keys(json)),否则会在支付时报签名错误

2.时间戳单位为秒,北京时间,别玩坏了,国外时间不能在国内不能玩

3.如果在支付时,报签名错误,先检查参数是否排序/参数值正确,如果还不行,请重置商户平台的key,再者不行,给微信支付技术支持发邮件,人家会在2小时内回复的

4.node微信app支付,就是以上代码两部分,剩下的就是app的事了

二、补充微信小程序支付统一下单,回调和app支付一致,不再重复

router.post(\'xcxPay\', async (ctx, next) => {

//1.根据orderCode查询订单状态和付款金额

const userId = ctx.user.userid

const orderInfo = await model.order.find({

\'orderStatus.status\': {

$in: [1, 9]

}

}, {

_id: 0,

orderCode: 1,

transCode: 1,

orderProducts: 1,

siteId: 1,

virtualProducts: 1,

CNYCharge: 1

})

if (!orderInfo || orderInfo.length < 1) {

throw {

status: 20001,

message: \'paying orderInfo not exists\',

router: ctx._url

}

log(222, \'订单状态错误\', orderInfo)

return

}

const userOpenId = await model.user.findOne({

_id: userId

}, {

_id: 0,

openIds: 1

})

if (!userOpenId || (userOpenId && !userOpenId.openIds && !userOpenId.openIds.wcx)) {

throw {

status: 20001,

message: \'wcx openid is not find !\',

router: ctx._url

}

return

}

let amount = orderInfo[0].CNYCharge

//字符串必需按照顺序来,比app支付参数稍有不同

const order = {

appid: \'小程序appid\',

body: \'糖葫芦\',

mch_id: \'商户号\',

nonce_str: (uuid.v4()).replace(/-/g, \'\'),

notify_url: `${config.apiUrl}/notify_url`,

openid: userOpenId && userOpenId.openIds && userOpenId.openIds.wcx || \'\', //小程序支付必须

out_trade_no: orderInfo[0].orderCode,

spbill_create_ip: ctx.request.ip.replace(/::ffff:/g, \'\'),

total_fee: Number(amount) * 100,

trade_type: \'JSAPI\' //小程序支付必须

}

const objStr = services.pay.objTostring(order)

const preSign = objStr + \'key=商户平台key\'

order.sign = endeurl.md5(preSign).toUpperCase()

const xml = services.pay.objToXml(order)

const data = await request.postAsync({

url: \'https://api.mch.weixin.qq.com/pay/unifiedorder\',

body: xml

})

const result = await services.pay.parseStringAsync(data.body)

if (result.xml.result_code[0] == \'FAIL\') {

throw {

message: \'orderStatus wrong\'

}

}

const paysign2 = {

appId: result.xml.appid[0],

nonceStr: result.xml.nonce_str[0],

package: `prepay_id=${result.xml.prepay_id[0]}`,

signType: \'MD5\',

timeStamp: parseInt(Date.now() / 1000).toString()

}

const payPrestr = services.pay.objTostring(paysign2) + \'key=商户平台key,同app支付\'

paysign2.paySign = md5(payPrestr).toUpperCase()

ctx.body = {

paysign2

}

})