支付成功异步通知接口
订单支付成功后,平台会发送异步通知到订单携带的notify_url
,推送方式为 HTTP POST
特别提醒:接收异步通知的notify_url
必须为可直接访问的URL,不能携带参数,不能有session、csrf等各种登录验证
请求参数:
字段名称 | 字段类型 | 必填参数 | 说明 |
---|---|---|---|
return_code | int(1) | Y | 1:支付成功 |
total_fee | int(16) | Y | 金额。单位:分 |
out_trade_no | string(32) | Y | 用户端自主生成的订单号 |
payjs_order_id | string(32) | Y | PAYJS 订单号 |
transaction_id | string(32) | Y | 微信用户手机显示订单号 |
time_end | string(32) | Y | 支付成功时间 |
openid | string(32) | Y | 用户OPENID标示,本参数没有实际意义,旨在方便用户端区分不同用户 |
attach | string(127) | N | 用户自定义数据 |
mchid | string(16) | Y | 商户号 |
type | string(16) | N | 支付类型。微信订单不返回该字段;支付宝订单返回:alipay |
sign | string(32) | Y | 数据签名 详见签名算法 |
提示:
- 异步通知以原始表单方式 application/x-www-form-urlencoded POST 推送,不是以json方式推送
- 接收通知的服务器请在3秒钟内响应(http状态码200则标记通知成功,其它状态码则标记通知失败,推送结束)
- 为保障推送到达率,系统可能多次进行通知推送,请做好去重逻辑
- 推送可能增加参数,请做好冗余处理
- 商户系统对于支付结果通知的内容要验签,避免异步通知被伪造
- 验签时,所有接收参数都参与验签
- 通知频率(秒):0,15,30,180,1800,3600。超过1小时后如需推送,可以在后台手动补发
- 异步通知过程中,如果该订单接收到check接口的成功查询,则异步通知在执行完当前任务后终止,不再继续通知
接收异步通知流程示例:
<?php
$data = $_POST;
if($data['return_code'] == 1){
// 1.验签逻辑
// 2.验重逻辑
// 3.自身业务逻辑
// 4.返回 success 字符串(http状态码为200)
echo 'success';
}
?>
# 以flask为例
from flask import Flask
from flask import request
import logging
app = Flask(__name__)
app.logger.setLevel(logging.DEBUG)
@app.route('/api[您的接口]/',methods=['GET','POST'])
def api():
if request.method == 'POST':
data = request.form
if data.get('return_code') == '1':
# 1.验签逻辑
# 2.验重逻辑
# 3.自身业务逻辑
# 4.返回 success 字符串(http状态码为200)
return 'success!'
return 'fail!'
if __name__ == '__main__':
app.run()
//基于expressjs的回调示例
//异步通知,/notifyCheck为你的自定义回调地址
router.post('/notifyCheck', function(req, res, next) {
var params=req.body;
if(pay.notifyCheck(params)==true){ //签名校验成功
if(params.return_code==1){
//业务逻辑
res.send('success');
}else{
res.status(404);
}
}else{
//校验失败
res.status(404);
}
});
// 完整代码:https://github.com/payjs-cn/demo-java
public Object payjsNotify(NotifyDTO notifyDTO){
Map<String,String> notifyData = new HashMap<>();
notifyData.put("return_code",notifyDTO.getReturn_code());
notifyData.put("total_fee",notifyDTO.getTotal_fee());
notifyData.put("out_trade_no",notifyDTO.getOut_trade_no());
notifyData.put("payjs_order_id",notifyDTO.getPayjs_order_id());
notifyData.put("transaction_id",notifyDTO.getTransaction_id());
notifyData.put("time_end",notifyDTO.getTime_end());
notifyData.put("openid",notifyDTO.getOpenid());
notifyData.put("attach",notifyDTO.getAttach());
notifyData.put("mchid",notifyDTO.getMchid());
String sign = sign(notifyData, PayjsConfig.key);
if(sign.equals(notifyDTO.getSign())){
// 验签通过,这里修改订单状态
return "success";
}
return "failure";
}
// 完整项目地址:https://github.com/payjs-cn/sdk-csharp
Payjs payjs = new Payjs("your mchid","your key");
//获取post参数
var param = Request.Form.ToDictionary(s => s.Key, s => s.Value);
//示例参数
// string orderid = param.ContainsKey("out_trade_no")?param["out_trade_no"].ToString():"";
// string total_fee = param.ContainsKey("total_fee") ? param["total_fee"].ToString() : "";
// string attach = param.ContainsKey("attach") ? param["attach"].ToString() : "";
//这里需要对订单数据做基本校验,可以检查当前订单号是否存在,金额和自定义数据是否匹配等
//post 参数字典转为<string,string>
var dic = new Dictionary<string, string>();
foreach (var keyPair in param)
{
dic.Add(keyPair.Key,keyPair.Value.ToString());
}
//对签名校验
string sign = param["sign"];
if (!payjs.notifyCheck(dic))
{
return "sign error";
}
//校验成功,进入自身业务逻辑(需在3s内响应
//若超过3s,可以把自身业务放后端(比如通过tcp或者udp通知后端服务),然后这里就直接return success
return "success";