由于公司自己的app,需要接入微信app支付,我使用的是微信支付V2版本,有V3版本
一丶在微信开放平台认证开发者资质认证(以公司的身份)。 交300元,有专门的给审核,第一次审核没过,补交资料申请公函,打款凭证(验证公司对公账户)
二、审核通过后,创建移动应用,(也要审核,人工审核、检查你的官网,应用类目(这里自己新增就是)) 得到 appId,
注意:应用签名(32位的MD5)和包名,只有正确才能拉起微信app的收银台,之前调试调起微信支付接口时 一直报-1
三、在商户平台成为商家得到商户号,在开放平台的app管理管理自己的商户号。同时设置商户平台api验签的秘钥
四。下载微信sdk
配置 appId,商户号,api验签密钥,
五。后台创建微信预付订单后将预付订单再次加密给前段。就可以了
1.调用微信sdk生成预付订单
Map<String, String> data = new HashMap<>();
WXPay wxPay = new WXPay(this, true); // 微信sdk this里面配置了appId,商户号,api密钥
// this 继承了微信提供了 WXPayConfig
data.put("appid", this.appId);
data.put("mch_id", this.mchId);
String firstNonce = WXPayUtil.generateNonceStr();
data.put("nonce_str", firstNonce);
data.put("body", body);
data.put("out_trade_no", String.valueOf(orderId));
String total_fee = String.valueOf(price.multiply(new BigDecimal(100)).intValue());
data.put("total_fee", total_fee); // 金额分
data.put("attach", num.toString()); // 附加数据
data.put("spbill_create_ip", InetAddress.getLocalHost().getHostAddress()); // 调用微信支付API的机器IP
data.put("notify_url", notifyUrl); // 回调地址
data.put("trade_type", "APP"); // 支付方式
data.put("sign", WXPayUtil.generateSignature(data, key)); // 签名微信的sdk,签名有问题,会多次覆盖这个sign,我将sdk的签名注释了
Map<String, String> response = wxPay.unifiedOrder(data);
// 自己的订单记录
/*
处理的自己的业务逻辑,生成自己的业务订单
*/
ConsumeHistoryEntity consumeHistoryEntity = new ConsumeHistoryEntity();
consumeHistoryEntity.setPrice(price); // 总价
consumeHistoryEntity.setStatus(0); // 未支付
consumeHistoryEntity.setTitle(body); // 标题
consumeHistoryEntity.setOrderId(orderId); // 订单号
consumeHistoryEntity.setId(orderId);
if (Objects.nonNull(userEntity))
consumeHistoryEntity.setUserId(userEntity.getId());
// consumeHistoryEntity.setUserId(64L); // 测试写死
consumeHistory.save(consumeHistoryEntity); // 插入数据库
微信的sdk会重新再次签名了。我将sdk注释了。
2.将微信生成的预支付订单,再次签名返回给前段
// 统一下单生成的预支付订单
Map<String, String> response = wxPay.unifiedOrder(data);
String appid = response.get("appid"); //appId
String partnerid = getMchID(); // 商户号
String prepayid = response.get("prepay_id"); // 预支付单号
String packageValue = "Sign=WXPay";
String noncestr = WXPayUtil.generateNonceStr(); // 随机字符串
String timestamp = String.valueOf(System.currentTimeMillis() / 1000); // 10的时间戳
Map<String, String> returnApp = new TreeMap<>();
returnApp.put("appid", appid);
returnApp.put("partnerid", partnerid);
returnApp.put("prepayid", prepayid);
returnApp.put("package", packageValue);
returnApp.put("noncestr", noncestr);
returnApp.put("timestamp", timestamp);
returnApp.put("sign", WXPayUtil.generateSignature(returnApp, this.key)); // 签名
// 查询业务支付订单,不参与签名,orderId 是前段实际查询支付状态
returnApp.put("oderId", String.valueOf(orderId));
return ResponseEntity.success(returnApp);
3.前端接收参数,调用sdk就能拉取微信收银台
4.拉取微信收银台
六丶 支付结果通知(在微信创建预付订单配置了回调地址。我用的ngrok将内网映射出去),由于微信返回来的是xml
@RequestMapping("weixinCallback")
public String weixinCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
//读取参数
int len = request.getContentLength();
ServletInputStream in = request.getInputStream();
byte[] buffer = new byte[len];
in.read(buffer, 0, len);
String data = new String(buffer, "utf-8");
in.close();
HashMap<String, String> map = new HashMap<>();
//解析xml成map
Map<String, String> result = WXPayUtil.xmlToMap(data);
if (!result.get("return_code").equals(WXPayConstants.SUCCESS)){
map.put("return_code","FAIL");
map.put("return_msg","");
return wxService.mapToXml(map);
}
if(wxService.isPayResultNotifySignatureValid(result)){
///----------------------/ 修改订单支付状态
String out_trade_no = result.get("out_trade_no"); // 商户订单编号
String attach = result.get("attach"); // 商户订单编号
String appId = result.get("appid"); // 商户订单编号
String transaction_id = result.get("transaction_id"); // 微信支付订单号
String total_fee = result.get("total_fee"); // 实际支付金额
ConsumeHistoryEntity consumeHistoryById = consumeHistory.getById(out_trade_no);
if(Objects.nonNull(consumeHistoryById)){
BigDecimal price = consumeHistoryById.getPrice();
BigDecimal wxPrice = new BigDecimal(total_fee).divide(new BigDecimal(100));
/** 支付宝验签
* 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
* 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
* 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
* 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
* 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
* 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
*
*/
// 我的判断逻辑是,该订单的支付状态为0,价格与数据相等, appId相等
if(consumeHistoryById.getStatus() == 0 && wxPrice.equals(price) && wxService.getAppID().equals(appId) ){
//订单支付成功
// 实际的业务逻辑
// TODO
}
map.put("return_code","SUCCESS");
map.put("return_msg","OK");
return wxService.mapToXml(map);
}
//-----/
}else {
log.error("非法回调,验签失败:"+ result);
map.put("return_code","FAIL");
map.put("return_msg","");
return wxService.mapToXml(map);
}
map.put("return_code","FAIL");
map.put("return_msg","");
return wxService.mapToXml(map);
} catch (Exception ex) {
HashMap<String, String> map = new HashMap<>();
map.put("return_code", "FAIL");
map.put("return_msg", ex.getMessage());
return wxService.mapToXml(map);
}
}
支付成功验签:
/** 支付宝验签
* 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
* 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
* 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
* 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
* 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
* 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
*
*/