背景 :网上支付这块资料比较多也比较杂,有些是基于老版本接口的文章,虽然说明很详细写的很好阅读量很大, 但是不是那么适用。现在最新版本apiv3,基于此基础上做的支付开发。
官方文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_2.shtml
业务流程图如下:多看几遍,脑袋里得知道整体流转过程。
1、2两步,服务端业务逻辑,主要目的是服务端生成自己的订单记录和相关数据,后面支付使用。
3、4、5步 小程序下单,将1、2步服务端订单相关参数,按照接口要求格式组装好,请求微信支付系统,生成预支付交易订单;
说明:
(1)、毕竟是涉及交易请求,httpclient比平常需要做些安全初始化操作,创建加载商户私钥、加载平台证书、初始化httpClient的通用方法,官方说明2.1.搭建和配置开发环境有对应说明。
(2)、具体参数说明见官方说明
public JSONObject createOrder(BigDecimal total, String orderNo) throws Exception{
SUser sysUser = (SUser) SecurityUtils.getSubject().getPrincipal();
String openid=sysUser.getOpenid();
File directory = new File("..");// 参数为空
String courseFile = directory.getCanonicalPath()+"/apiclient_key.pem";
PrivateKey privateKey=getPrivateKey(courseFile);
JSONObject map = new JSONObject();
// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3秘钥)
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, privateKey)),apiV3Key.getBytes("utf-8"));
// 初始化httpClient
// *********************** 此处有坑 HttpClient 与文档有出入
HttpClient httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, privateKey)
.withValidator(new WechatPay2Validator(verifier)).build();
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
//********************参数去参考文档,自定义
//随机字符串 根据自己工具类写
String nonceStr = RandomStringUtils.randomAlphanumeric(5);
//时间戳 根据自己工具类写
String timeStamp = String.valueOf(System.currentTimeMillis());
//微信金额是按照分来的,所以要乘以100
total=total.multiply(new BigDecimal(100));
// 请求body参数
String reqdata = "{"
+ "\"amount\": {"
+ "\"total\": " + total + ","
+ "\"currency\": \"CNY\""
+ "},"
+ "\"mchid\": \""+mchId+ "\","
+ "\"attach\": \""+orderNo + "\","
+ "\"description\": \"" + orderNo+ "\","
+ "\"notify_url\": \""+notifyUrl+"\","
+ "\"payer\": {"
+ "\"openid\": \"" + openid +"\"" + "},"
+ "\"out_trade_no\": \"" + orderNo + "\","
+ "\"goods_tag\": \"WXG\","
+ "\"appid\": \""+ appId+"\"" + "}";
StringEntity entity = new StringEntity(reqdata);
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
String result = "";
CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
JSONObject jsonObject = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
result = "prepay_id=" + jsonObject.getString("prepay_id");;
} else if (statusCode == 204) {
System.out.println("success");
result = "success";
} else {
result = "failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity());
System.out.println(result);
throw new IOException("request failed");
}
} finally {
response.close();
}
//************************签名,自己拼接,然后进行两次加密,为什么加密,下面解释
String pendingPaySign = appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + result + "\n";
String paySign = SignUtil.encodeBase64(SignUtil.sign256(pendingPaySign, privateKey));
map.put("appId", appId);
map.put("timeStamp", timeStamp);
map.put("nonceStr", nonceStr);
map.put("package", result);
map.put("signType", "RSA");
map.put("paySign", paySign);
return map;
}
到此为止,预支付成功,获得了prepay_id,获得了prepay_id是为了第6步:生成带签名支付信息。
因为第7步,小程序前端需要第6步生成的带签名支付信息,发起支付请求。
7-14步是微信前端发起支付、用户确认支付的整个过程,后端不需要做什么
15-18步用户支付成功了,后台需要被通知到(异步的,告诉微信支付接口被通知回调的接口,支付成功后平台会按照这个接口地址调用,回调接口地址就是生成预支付订单参数:notify_url,关于服务端接收支付结果通知详细说明,参考官方说明3.2.3),然后才好做后续操作,例如:记录成功交易记录、发货等后续操作。详细说明地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_5.shtml
接收支付通知处理逻辑代码:
@Override
public String notifyResult(HttpServletRequest request, HttpServletResponse response) throws Exception {
JSONObject map = new JSONObject();
String result = readData(request);
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = verifyNotify(result, apiV3Key);
if (StringUtils.isNotEmpty(plainText)) {
response.setStatus(200);
map.put("code", "SUCCESS");
map.put("message", "SUCCESS");
JSONObject jsonObject=JSONObject.parseObject(plainText);
String out_trade_no = jsonObject.getString("out_trade_no");
String transaction_id = jsonObject.getString("transaction_id");
String trade_state = jsonObject.getString("trade_state");
String trade_state_desc = jsonObject.getString("trade_state_desc");
String attach = jsonObject.getString("attach");
String success_time = jsonObject.getString("success_time");
String amount = jsonObject.getString("amount");
String openid=jsonObject.getJSONObject("payer").getString("openid");
updateOrder(attach,trade_state,openid);
} else {
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "签名错误");
}
//通知应答
//支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx
response.setCharacterEncoding("utf-8");
response.setContentType("application/json; charset=utf-8");
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(plainText.getBytes());
out.flush();
out.close();
return null;
}
19-21步是主动查询支付结果。
暂没考虑主动查询支付结果。