SpringBoot开发Native⽀付(APIv3密钥版)
前⾔
 前段时间弄了的⽂章,有需要开发⽀付宝当⾯付的童鞋可以看⼀下我开发⽀付宝的⽂章,现在打算把开发的Native⽀付也写⼀下,做个参考。
准备
 ⽀付的开发⼀般很⿇烦,需要的东西特别多,现在就来说⼀下开发的Native⽀付都需要些什么。这⾥要说下,⽀付是分为
v3和v2两个版本的,这⾥选⽤的v3版本,但是⽹上关于v3版本的demo很少,v3和v2版本的差别很⼤,所以路途很艰难,作者是深有体会啊。
AppID:应⽤ID
mchid:商户号
AppSecret:和AppID绑定的Secret参数(设置时需保存,设置后不到)
PrivateKey:私钥(这个私钥是证书的私钥,私钥下载下来后在apiclient_key.pem⽂件中)
mchSerialNo:证书序列号(绑定证书后可以查看证书,证书序列号就在上⾯)
APIv3Key:APIv3密钥(⾃⼰设置)
代码转换以上的东西只需在开发时需要⽤到的,⾄于在哪⾥获取就不说了,获取的地⽅都不⼀样。
pom⽂件
<!-- 导⼊⽀付 -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
这⾥要说明⼀下,开发APIv3版本时,会提供⼀个SDK,需要把这个SDK下载下来,放到项⽬中去
开发Native⽀付
发送请求
 发送请求时需要注意⼏点
 1、的⾦额单位是按分算的,0.01的商品要转换为100发送给,做的时候要做好⾦额的转换准备(⽐⽀付宝坑的地⽅这算⼀个)。
 2、要注意传给对象的类型。例如:总⾦额(total)是int类型,作者就是传了⼀个String类型,这样也可以拿到⽀付的⼆维码,但是扫描⼆维码⽀付的时候出现了扫码不停地等待最后提⽰系统繁忙的信息。作者当初就在这⾥卡了好⼏天,最后才在⽹上了很久资料才到问题所在,是类型没有传对的原因。
 3、开发Native官⽅⽂档中SDK的代码⼀定要放到项⽬中去,因为官⽅提供SDK已经帮我们封装了请求时签名的创建和回调时签名验证认证等。
/**⽀付的AppId*/
private static final String AppId ="你的AppID";
/**⽀付的商户号(mchid)*/
private static final String MchId ="你的商户号";
/**⽀付的secret参数*/
private static final String Secret ="你的AppIdSecret";
/**私钥字符串*/
private static final String privateKey ="你的私钥(商户(证书)的私钥)"
/**证书序列号*/
private static final String mchSerialNo ="你的证书序列号";
/**APIv3密钥*/
private static final String apiV3Key ="你的APIv3密钥";
/
**
* Native⽀付
* @param paymentMethod:⽀付⽅式(0:购物卡⽀付 1:⽀付宝⽀付 2:⽀付 3:购物卡和⽀付宝 4:购物卡和)
* @param userId:⽤户id
* @param price:总价
* @param orderNos:订单编号
* @return
*/
@Override
public R wxNativePay(Integer paymentMethod, Integer userId, Integer price, String[] orderNos)throws Exception {
//new⼀个空串⽤来存储订单编号(订单编号可能会有多个,买多个不同商家的商品)
String newOrderNo =createOrderNo().toString();
//将传来的订单编号去重
Set orderSets =new HashSet();
//遍历订单编号
for(String orderNo : orderNos){
orderSets.add(orderNo);
}
if(orderSets.size()<1){
(-1,"没有订单编号");
}
/
/遍历去重后的订单编号
for(Object orderNo : orderSets){
//根据订单编号,⽤户id查询订单
List<TOrder> orderList = orderDao.String(), userId);
if(orderList.size()>0){
//循环查出来的订单,将⽀付总订单编号设置到订单数据中
for(TOrder order : orderList){
//给该订单设置⽀付总订单编号
order.setPayTotalOrderNo(newOrderNo);
Integer updateStatus = orderDao.updatePayTotalOrderNo(order);
}
}
}
/**********************上⾯是业务逻辑,按⾃⼰的写即可**********************/
/***************开发Native⽂档中有这些代码,配置⽤⾃⼰的即可 start*************/
// 加载商户私钥(privateKey:私钥字符串)
PrivateKey merchantPrivateKey = PemUtil
.loadPrivateKey(new Bytes("utf-8")));
// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3秘钥)
AutoUpdateCertificatesVerifier verifier =new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(MchId,new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),Bytes("utf-8"));
// 初始化httpClient(获得Http客户端)
CloseableHttpClient httpClient = ate()
.withMerchant(MchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier)).build();
/***************开发Native⽂档中有这些代码,配置⽤⾃⼰的即可 end*************/
HttpPost httpPost =new HttpPost("h.weixin.qq/v3/pay/transactions/native");
String reqdata ="{"
+"\"amount\": {"
+"\"total\":"+"100"+","
+"\"currency\":\"CNY\""
+"},"
+"},"
+"\"mchid\":\""+"你的商户号"+"\","
+"\"description\":\"商品描述\","
+"\"notify_url\":\"你的回调地址"+"\","
+"\"out_trade_no\":\""+"你的订单编号"+"\","
+"\"appid\":\""+"你的AppID"+"\""
+"}";
StringEntity entity =new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept","application/json");
//完成签名并执⾏请求
CloseableHttpResponse response = ute(httpPost);
//获取⽀付返回的⽀付状态码
Integer statusCode = StatusLine().getStatusCode();
//获取返回回来的codeUrl链接
String codeUrl = Entity());
//将返回的⼆维码链接进⾏截取,只获取链接本⾝,其它字符串不获取
String qrCode = codeUrl.substring(codeUrl.indexOf("w"),codeUrl.indexOf("}")-1);
try{
//处理成功
if(statusCode ==200){
log.info("statusCode = "+ statusCode +",success,return body = "+ codeUrl);
/
/处理成功,⽆返回Body
}else if(statusCode ==204){
log.info("statusCode = "+ statusCode +",请求发送成功,但⽆返回body");
}else{
log.info("failed,resp code = "+ statusCode +",return body = "+ codeUrl);
}
}finally{
//关闭资源
response.close();
httpClient.close();
}
/
/返回
if(statusCode.equals(200)){
return R.ok(1,"Native⽀付请求成功",qrCode);
}else if(statusCode.equals(204)){
(-1,"Native⽀付请求成功,但是没有⼆维码链接",codeUrl);
}else{
(-1,"Native⽀付请求失败",codeUrl);
}
}
开发回调
 接收到回调后,不管⽀付是否成功,都要给返回信息,因为会通过⼀些策略来定时给⽤户发送回调()
/**
* 回调
* @param request
* @param response
* @return
* @throws Exception
*/
@PostMapping("/wxCallBack")
public String wxCallBack(HttpServletRequest request, HttpServletResponse response)throws Exception{ //获取回调回来的数据流
ServletInputStream inputStream = InputStream();
String notifyXmlInfo = StreamUtils.inputStream2String(inputStream,"utf-8");
//将数据转为map
Map<String, Map<String,String>> strToMap = JSONUtil.jsonStrToMap(notifyXmlInfo);
//获取返回的resource信息
String associatedData = ("resource").get("associated_data");
String nonce = ("resource").get("nonce");
String ciphertext = ("resource").get("ciphertext");
//使⽤SDK提供的AesUtil⼯具类和APIv3密钥进⾏签名验证
AesUtil aesUtil =new Bytes(StandardCharsets.UTF_8));
//如果APIv3密钥和返回的resource中的信息不⼀致就会导致签名失败,⽤户是拿不到返回的⽀付信息的        String decryptToString = aesUtil.Bytes(StandardCharsets.UTF_8),
//验证成功后将获取的⽀付信息转为map
Map<String, Object> map = JSONUtil.strToMap(decryptToString);
//判断是否⽀付成功
if("SUCCESS".("trade_state"))){
//⽀付成功后进⼊业务处理逻辑中,这个代码就不贴出来了,每个公司的业务需求都不相同
wxPayService.wxCallBack(map);
//⽀付成功给发送我已接收通知的响应
//创建给响应的对象
Map<String,String> returnMap =new HashMap<>();
returnMap.put("code","SUCCESS");
returnMap.put("message","成功");
/
/将返回给的响应对象转为xml
String returnXml = WXPayUtil.mapToXml(returnMap);
log.info("⽀付成功,并且给返回响应数据");
return returnXml;
}
//⽀付失败给发送我已接收通知的响应
//创建给响应的对象
Map<String,String> returnMap =new HashMap<>();
returnMap.put("code","FAIL");
returnMap.put("message","");
//将返回给的响应对象转为xml
String returnXml = WXPayUtil.mapToXml(returnMap);
log.info("⽀付失败,给返回失败的响应数据");
return returnXml;
}
JSONUtil⼯具类
import com.alibaba.fastjson.JSON;
slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
/**
* @author lpx
* @date 2021/4/15
*/
@Slf4j
public class JSONUtil {
/**
* Json字符串转Map
* @param wxStr:返回的数据
* @return
*/
public static Map<String, Map<String,String>>jsonStrToMap(String wxStr){
Map<String,Map<String,String>> map =(Map) JSON.parse(wxStr);
log.info("Json字符串转Map:"+ map);
return map;
}
/**
* str转map
* @param str:字符串
* @return
*/
public static Map<String,Object>strToMap(String str){
Map<String,Object> map = JSON.parseObject(str, HashMap.class);
log.info("回调验证签名后⽀付信息:"+ map);
return map;
}
}
总结
 作者在开发⽀付时遇到的坎坷颇多,因为⽹上关于⽀付开发的⽂章很少,⽽且作者是使⽤的v3版本开发的,这个就更少了,这些是作者⼀点⼀点在bug中探索出来的,如果各位童鞋有更好的⽅法可以私信下作者,让我们共同学习,共同成长,希望作者分享的开发的经验可以帮到各位还在路上的童鞋⼀些帮助。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。