https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=1_1
Let’s discuss WeChat payment with everyone~ (To be honest, the official website of WeChat is really a mess, and any similarities in personal opinions are purely coincidental)
The various descriptions of WeChat, the documents are not concise and clear enough, and I feel that they are very messy. This is probably also a problem with the personnel of the product team.
Wechat payment now has 2 versions, one is v2 v3, v3 is relatively new
WeChat does not have a sandbox environment, it needs to be an enterprise.
First, the merchant platform opens WeChat payment
The merchant system first calls this interface to generate a prepayment transaction order in the WeChat payment service background, returns the correct prepayment transaction session ID, and then generates transaction strings according to different scenarios such as Native, JSAPI, and APP to initiate payment.
parameter name | variable | type [length limit] | required | describe | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
application ID | appid | string[1,32] | yes | body App ID generated by WeChat, globally unique. Please pay attention to the APPID application attribute when requesting the basic order interface. For example, in the official account scenario, you need to use the service account APPID with the application attribute as the official account Example value: wxd678efh567hg6787 |
||||||||||||||||||||
Directly connected to the merchant account | mchid | string[1,32] | yes | body The merchant account of the directly connected merchant, which is generated and delivered by WeChat Pay. Example value: 1230000109 |
||||||||||||||||||||
product description | description | string[1,127] | yes | body Product description Example value: Image store-Shenzhen Tengda-QQ doll |
||||||||||||||||||||
Merchant order number | out_trade_no | string[6,32] | yes | body The internal order number of the merchant system, which can only be numbers, uppercase and lowercase letters_-* and is unique under the same merchant number. Example value: 1217752501201407033233368018 |
||||||||||||||||||||
Trading end time | time_expire | string[1,64] | no | body The order expiration time follows the rfc3339 standard format, the format is yyyy-MM-DDTHH:mm:ss+TIMEZONE, yyyy-MM-DD represents the year, month and day, T appears in the string, representing the beginning of the time element, HH:mm:ss represents the hour, minute and second, and TIMEZONE represents the time zone (+08:00 represents the time of the East Eighth District, which is 8 hours ahead of UTC, which is Beijing time). For example: 2015-05-20T13:29:35+08:00 means May 20, 2015 13:29:35 Beijing time. Example value: 2018-06-08T10:34:56+08:00 |
||||||||||||||||||||
additional data | attach | string[1,128] | no | body Additional data, which is returned as it is in the query API and payment notification, and can be used as a custom parameter. In actual cases, this field will only be returned when the payment is completed. Example value: custom data |
||||||||||||||||||||
notification address | notify_url | string[1,256] | yes | body Asynchronously receives the callback address of WeChat payment result notification, the notification url must be an url accessible from the external network, and cannot carry parameters. The domain name of the public network must be https. If it is accessed through a dedicated line, use the private line NAT IP or a private callback domain name to use http example value: https://www.weixin.qq.com/wxpay/pay.php |
||||||||||||||||||||
Order discount mark | goods_tag | string[1,32] | no | body Order discount token Example value: WXG |
||||||||||||||||||||
Electronic invoice entry opening sign | support_fapiao | boolean | no | When the body is passed true, the payment success message and the payment details page will display the billing entry. The electronic invoice function needs to be activated on the WeChat Pay Merchant Platform or WeChat Official Platform before passing this field to take effect. true: yes false: no example value: true |
||||||||||||||||||||
-order amount | amount | object | yes | body order amount information | ||||||||||||||||||||
|
||||||||||||||||||||||||
- payer | payer | object | yes | body payer information | ||||||||||||||||||||
|
||||||||||||||||||||||||
-Preferential function | detail | object | no | body discount function | ||||||||||||||||||||
|
||||||||||||||||||||||||
-场景信息 | scene_info | object | 否 | body 支付场景描述 | ||||||||||||||||||||
|
||||||||||||||||||||||||
-结算信息 | settle_info | object | 否 | body 结算信息 | ||||||||||||||||||||
|
请求示例
{ "mchid": "1900006XXX", "out_trade_no": "1217752501201407033233368318", "appid": "wxdace645e0bc2cXXX", "description": "Image形象店-深圳腾大-QQ公仔", "notify_url": "https://www.weixin.qq.com/wxpay/pay.php", "amount": { "total": 1, "currency": "CNY" }, "payer": { "openid": "o4GgauInH_RCEdvrrNGrntXDuXXX" } }
返回参数
参数名 | 变量 | 类型[长度限制] | 必填 | 描述 |
---|---|---|---|---|
预支付交易会话标识 | prepay_id | string[1,64] | 是 | 预支付交易会话标识。用于后续接口调用中使用,该值有效期为2小时 示例值:wx201410272009395522657a690389285100 |
{ "prepay_id": "wx26112221580621e9b071c00d9e093b0000" }
错误码公共错误码
状态码 | 错误码 | 描述 | 解决方案 |
---|---|---|---|
403 | TRADE_ERROR | 交易错误 | 因业务原因交易失败,请查看接口返回的详细信息 |
500 | SYSTEM_ERROR | 系统错误 | 系统异常,请用相同参数重新调用 |
401 | SIGN_ERROR | 签名错误 | 请检查签名参数和方法是否都符合签名算法要求 |
403 | RULE_LIMIT | 业务规则限制 | 因业务规则限制请求频率,请查看接口返回的详细信息 |
400 | PARAM_ERROR | 参数错误 | 请根据接口返回的详细信息检查请求参数 |
403 | OUT_TRADE_NO_USED | 商户订单号重复 | 请核实商户订单号是否重复提交 |
404 | ORDER_NOT_EXIST | 订单不存在 | 请检查订单是否发起过交易 |
400 | ORDER_CLOSED | 订单已关闭 | 当前订单已关闭,请重新下单 |
500 | OPENID_MISMATCH | openid和appid不匹配 | 请确认openid和appid是否匹配 |
403 | NO_AUTH | 商户无权限 | 请商户前往申请此接口相关权限 |
400 | MCH_NOT_EXISTS | 商户号不存在 | 请检查商户号是否正确 |
500 | INVALID_TRANSACTIONID | 订单号非法 | 请检查微信支付订单号是否正确 |
400 | INVALID_REQUEST | 无效请求 | 请根据接口返回的详细信息检查 |
429 | FREQUENCY_LIMITED | 频率超限 | 请降低请求接口频率 |
500 | BANK_ERROR | 银行系统异常 | 银行系统异常,请用相同参数重新调用 |
400 | APPID_MCHID_NOT_MATCH | appid和mch_id不匹配 | 请确认appid和mch_id是否匹配 |
403 | ACCOUNT_ERROR | 账号异常 | 用户账号异常,无需更多操作 |
微信支付现在分为v2版和v3版
2014年9月10号之前申请的为v2版(旧版本),之后申请的为v3版。
V2版中的参数有
AppID
AppSecret
支付专用签名串PaySignKey
商户号PartnerID
初始密钥PartnerKey
并且包含一个证书文件: 安全证书
V3版中的参数有
AppID
AppSecret
商户号PartnerID
初始密钥PartnerKey
商户号MCHID
申请编号
商户平台登录帐号
商户平台登录密码
包含5个证书文件(证书pkcs12格式、证书pem格式、证书密钥pem格式、CA证书, 安全证书)
如果收到的邮件中没有【支付专用签名串PaySignKey】,表示已经是V3版的微信支付了。
WeChat payment
The entrance of the WeChat payment interface is the same as the products of the WeChat public platform (official account, small program, and enterprise WeChat). They are all unified in the resource center of the WeChat open platform. In fact, we only need to visit the official website of the WeChat open platform. Through the resource center, we can access the interface documents of all WeChat products we need to connect.
We need to apply for many certificates
Get prepaid tracking number payment callback
@RestController
@Api(tags = "PayController")
@Tag(name = "PayController", description = "微信支付")
@RequestMapping("/v3")
public class PayController {
@PostMapping("/")
public Map createOrder(Long amount, String desc,String openId) throws Exception{
return PayUtil.createOrder(desc,amount,openId);
}
@PostMapping("/")
public Map callBackOrder(HttpServletRequest request) throws Exception{
System.out.println("微信回调开始返回");
System.out.println("Wechatpay-Timestamp:"+request.getHeader("Wechatpay-Timestamp"));
System.out.println("Wechatpay-Nonce:" + request.getHeader("Wechatpay-Nonce"));
System.out.println("Wechatpay-Signature:" + request.getHeader("Wechatpay-Signature"));
System.out.println("Wechatpay-Serial:" + request.getHeader("Wechatpay-Serial"));
Map result = new HashMap();
result.put("code","FAIL");
try {
StringBuilder signStr = new StringBuilder();
// 请求时间戳\n
signStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
// 请求随机串\n
signStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");
BufferedReader br = request.getReader();
String str =null;
StringBuilder builder =new StringBuilder();
while((str = br.readLine()) !=null){
builder.append(str);
}
System.out.println(builder);
signStr.append(builder.toString()).append("\n");
//验证签名
if(!PayUtil.signVerify(request.getHeader("Wechatpay-Serial"), signStr.toString(), request.getHeader("Wechatpay-Signature"))){
result.put("message","sign error");
return result;
}
//解密密文
String decryptOrder = PayUtil.decryptOrder(builder.toString());
System.out.println(decryptOrder);
//验证订单
result.put("message",decryptOrder);
result.put("code","SUCCESS");
}catch (IIOException e){
e.printStackTrace();
}
return result;
}
@PostMapping("/")
public Map<String, Object> wxPayNotify(HttpServletRequest request) {
System.out.println("微信回调开始返回");
System.out.println("Wechatpay-Timestamp:"+request.getHeader("Wechatpay-Timestamp"));
System.out.println("Wechatpay-Nonce:" + request.getHeader("Wechatpay-Nonce"));
System.out.println("Wechatpay-Signature:" + request.getHeader("Wechatpay-Signature"));
System.out.println("Wechatpay-Serial:" + request.getHeader("Wechatpay-Serial"));
Map result = new HashMap();
result.put("code", "FAIL");
BufferedReader reader = null;
try {
StringBuilder signStr = new StringBuilder();
// 请求时间戳\n
signStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
// 请求随机串\n
signStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");
// 请求报文主体\n
reader = request.getReader();
String str = null;
StringBuilder builder = new StringBuilder();
while ((str = reader.readLine()) != null) {
builder.append(str);
}
System.out.println(builder);
signStr.append(builder.toString()).append("\n");
// 1.验签
if (!V3WXPayUtil.signVerify(request.getHeader("Wechatpay-Serial"), signStr.toString(), request.getHeader("Wechatpay-Signature"))) {
result.put("message", "sign error");
return result;
}
// 2.解密
String decryptOrder = V3WXPayUtil.decryptOrder(builder.toString());
System.out.println(decryptOrder);
if (StringUtils.isEmpty(decryptOrder)) {
result.put("message", "verification error");
return result;
} else {
result.put("code", decryptOrder);
}
return result;
} catch (IOException e) {
e.printStackTrace();
result.put("code", "FAIL");
result.put("message", e.getMessage());
return result;
}
}
}