一两大白菜

导航

java springboot实现微信公众号支付

支付模块终于告一段落,由于之前没做过支付,踩过一些坑,在此我想分享自己的一些心得,希望对同行们有所帮助,话不多说,直接进入正题。

首先,我们要完成微信公众号支付功能(此处是java springboot版),那么我们需要用到公众号和商户号其中的一些配置信息,微信公众号选择服务号,

#微信支付信息配置
weixin:
#appid
appid: *********** #这是appid,在微信公众号的基本配置中可以找到
#商户id
partner: ********** #商户号id,在商户号中可以找到
#api秘钥
partnerkey: *********** #商户秘钥,也是在商户号中找到,不会请百度
#支付回调地址
notifyurlBuyCar: ********* #支付回调地址,支付完成之后会自动调用这个地址

  以上是在springboot项目中yml文件的基本配置信息,此外,请注意:

1:商户号需要绑定微信公众号
2:商户号需要开通JSAPI支付
3:微信公众号里面的功能设置需要绑定JS安全域名和网页授权域名
4:如果需要使用微信开发者工具进行调试的话,需要在公众号中绑定当前开发者的微信

  因为我的支付功能和购物车支付用的是同一套接口,所以数据格式上会有所差异,直接上代码:

/**
* 购物车支付
* @param parameterMap
* @param request
* @return
*/
@PostMapping("buyCarPay")
public ResponseResult buyCarPay(@RequestParam Map<String,Object> parameterMap,HttpServletRequest request){
//参数:需要一个goods_list的json格式的集合,以及用户的openid,openid是用户在本公众号中的唯一标识,不会的请百度或者查看微信官方文档获取
    //这个request没啥用,只是为了获取当前终端的ip地址
    //parameterMap:
  // goods_list:[
  // {"goods_id":10015,"goods_num":2,"goods_price":0.01},
  // {"goods_id":10016,"goods_num":3,"goods_price":0.01}
  // ],
  // openid: ********
Map<String,Object> resultMap = buyCarService.buyCarPay(parameterMap,request);
if(resultMap!=null){
return Result.SUCCESS(1,"success",resultMap);
}
return new ResponseResult(500,"error",null);
}

  这篇帖子只关心支付的功能,不关心其中的业务逻辑的处理,所以我会删掉其中的一些逻辑,嘿嘿嘿....... 统一下单可以去微信官方文档中调用api接口,

@Value("${weixin.appid}")
private String appid;
@Value("${weixin.partner}")
private String partner;
@Value("${weixin.partnerkey}")
private String partnerkey;
@Value("${weixin.notifyurlBuyCar}")
private String notifyurlBuyCar
/**
* 购物车支付
 * @param parameterMap
* @param request
* @return
*/
public Map<String, Object> buyCarPay(Map<String, Object> parameterMap,HttpServletRequest request) {
// "goods_list":[
// {"goods_id":10015,"goods_num":2,"goods_price":0.01},
// {"goods_id":10016,"goods_num":3,"goods_price":0.01}
// ],
// "openid": ajshdasnoasdasdas
String goods_list = (String)parameterMap.get("goods_list"); //获取goods_list
String openid = (String)parameterMap.get("openid"); //获取openid
List list = JSONArray.parseArray(goods_list); //将字符串格式的goods_list转为list集合
double totalPrice=0.00; //总价格
Map<String,String> paramMap = new HashMap<>();

//以下paramMap设置的参数都是为了获取签名
paramMap.put("appid",appid); //appid,配置文件中获取
paramMap.put("mch_id",partner); //商户id,配置文件中获取
paramMap.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串,这个WXPayUtil工具类可以在微信官方文档中获取,这个相当有用,针对支付这一块
paramMap.put("body","body"); //商品的body信息,相当于详情描述吧
paramMap.put("sign_type","MD5"); //签名类型,一般就写MD5
//获取时间,获取随机数,拼接在一起生成订单号
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
String newDate=sdf.format(new Date());
String result1="";
Random random=new Random();
for(int i=0;i<4;i++){
result1+=random.nextInt(10);
}
String str=newDate+result1;
paramMap.put("out_trade_no",str); //订单号

for (int i = 0; i < list.size(); i++) {
Map<String, Object> m = (Map<String, Object>) list.get(i);
double goods_price = Double.parseDouble(String.valueOf(m.get("goods_price"))); //商品单价
totalPrice=totalPrice+goods_num*goods_price;
}
int d = (totalPrice*100).intValue();
paramMap.put("total_fee",d+""); //总价格,单位是分
paramMap.put("spbill_create_ip", IPUtils.getIpAddr(request)); //IPUtils是获取当前ip的工具类,在网上可以找到
paramMap.put("notify_url",notifyurlBuyCar); //支付回调地址,提交支付后,会走这个网址,这个网址是可以通过外网直接访问到的
paramMap.put("trade_type","JSAPI"); //交易类型,目前是微信公众号支付,需要些JSAPI,商户号中一定要开通JSAPI支付

paramMap.put("openid",openid); //openid,用户在此公众号的唯一标识
String xmlParameters= null;
try {
//这一步操作就是将一个map集合加上秘钥通过MD5加密的方式生成一个签名,签名可验证
xmlParameters = WXPayUtil.generateSignedXml(paramMap,partnerkey); //partnerkey秘钥,在配置文件中获取
//URL地址
String url="https://api.mch.weixin.qq.com/pay/unifiedorder";
//URL地址
HttpClient httpClient=new HttpClient(url);
//提交方式
httpClient.setHttps(true);
//提交参数
httpClient.setXmlParam(xmlParameters);
//执行请求
httpClient.post();
//获取返回的参数
String result = httpClient.getContent();
//返回数据转为map
Map<String, String> resultMap = WXPayUtil.xmlToMap(result); //Map格式转为xml格式,xmlToMap方法在WXPayUtil中可以找到

System.out.println("调用统一下单result=========="+resultMap);

Map response=new HashMap();
response.put("appId",appid);
response.put("timeStamp",paramMap.get("out_trade_no")); //订单号
response.put("nonceStr",resultMap.get("nonce_str")); //随机字符串
response.put("package","prepay_id="+resultMap.get("prepay_id")); //拼接预支付id
response.put("signType","MD5"); //加密方式
String paySign1 = WXPayUtil.generateSignedXml(response, partnerkey); //加密获取签名
Map<String, String> map11 = WXPayUtil.xmlToMap(paySign1); //将xml格式转为map集合
String paySign = map11.get("sign"); //获取最后的支付签名,这个很重要
     Map<String,Object> payMap=new HashMap<>();    //最终响应的参数map
        payMap.put("appId",appid);   
payMap.put("timeStamp",paramMap.get("out_trade_no"));
payMap.put("nonceStr",resultMap.get("nonce_str"));
payMap.put("package","prepay_id="+resultMap.get("prepay_id"));
payMap.put("signType","MD5");
payMap.put("paySign",paySign);
payMap.put("out_trade_no",paramMap.get("out_trade_no"));
payMap.put("openid",openid);

return payMap;

} catch (Exception e) {
e.printStackTrace();
}
return null;
}

(支付完成后,微信会把相关支付结果及用户信息通过数据流的形式发送给商户,商户需要接收处理,并按文档规范返回应答。)

注意:

1、同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。

2、后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起多次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。

3、在订单状态不明或者没有收到微信支付结果通知的情况下,建议商户主动调用微信支付【查询订单API】确认订单状态。

上面这一块可以完成支付的功能,但是支付成功之后需要进行一些业务逻辑的处理,之前配置的支付回调地址派上了用场,请看配置中的notifyurlBuyCar路径,这个notifyurl接口如果报错,获取超时,微信方会循环调用此接口,

/**
* 支付结果通知回调方法
* @param request
* @return
*/
@RequestMapping("notifyurl")
public String notifyurl(HttpServletRequest request) throws Exception{
//以流的方式获取
ServletInputStream is = request.getInputStream();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
byte[] buffer=new byte[1024];
int len=0;
while ((len=is.read(buffer))!=-1){
baos.write(buffer,0,len);}
byte[] bytes = baos.toByteArray();
String xmlresult=new String(bytes,"UTF-8");
Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlresult);
System.out.println("支付响应参数:"+resultMap); //可以查询一下目前支付响应的参数,
String out_trade_no=resultMap.get("out_trade_no"); //平台生成的订单号
String time_end=resultMap.get("time_end"); //获取在微信支付的最终时间
String transaction_id=resultMap.get("transaction_id"); //获取微信交易号

Map map = buyCarService.queryStatus(out_trade_no);
String trade_state = (String)map.get("trade_state");

if(trade_state.contains("SUCCESS")){
    当交易状态是SUCCESS的时候,代表支付成功,接下来就是业务逻辑的处理
String result="SUCCESS";
return result;
}
String result="SUCCESS"; //目前返回的是SUCCESS,
return result;
}

 

/**
* 查询支付状态
* @param outtradeno
* @return
*/
public Map queryStatus(String outtradeno){
Map<String,String> paramMap= null;
try {
//参数
paramMap = new HashMap<>();
paramMap.put("appid",appid);
paramMap.put("mch_id",partner);
paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
paramMap.put("out_trade_no",outtradeno);
//Map转出xml字符串,可以携带签名
String xmlParameters= WXPayUtil.generateSignedXml(paramMap,partnerkey);
//URL地址
String url="https://api.mch.weixin.qq.com/pay/orderquery";
HttpClient httpClient=new HttpClient(url);
//提交方式
httpClient.setHttps(true);
//提交参数
httpClient.setXmlParam(xmlParameters);
//执行请求
httpClient.post();
//获取返回的参数
String result = httpClient.getContent();
//返回数据转为map
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
return resultMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

以上代码执行的是Controller和Service层,可以查看一下https://pay.weixin.qq.com/wiki/doc/api/external/jsapi.php?chapter=9_1


后续:敬请期待 !!!

       

 

posted on 2020-05-22 15:21  一两大白菜  阅读(738)  评论(0)    收藏  举报