微信公众号发起支付记录(后端)

1.流程

首先准备微信支付统一配置,进入可能发起支付的页面时,将要支付的手机端网页url传入后台,返回四个参数,写进函数wx.config进行授权

注:这是前端调用微信的统一配置,对真正调起手机微信支付没有影响。如果使用的是小程序云开发静态网站托管的域名的网页,可以免鉴权直接跳任意合法合规小程序,调用 wx.config 时 appId 需填入非个人主体的已认证小程序,不需计算签名,timestamp、nonceStr、signature 填入非空任意值即可。

然后在真正要在手机端拉起支付页面时,先将openid传入后台,后台根据openid和其他参数向微信端统一下单接口发送请求,返回prepay_id,再根据结合其他参数传给手机前端,前端调用wx.chooseWxPay函数拉起微信支付界面

注:流程中有三个签名(图中的signature和paySign),授权的时候后台生成一个签名传给前台(第一个),后台向微信统一下单的时候生成一个传给微信(第二个),结合微信统一下单返回的信息,生成一个给手机端用来拉起微信支付(第三个,也叫微信支付的二次签名)

统一下单接口:https://api.mch.weixin.qq.com/pay/unifiedorder    调用weixin-java-pay里的  unifiedOrder  可以直接发起

 

2.具体代码

依赖:

com.github.binarywangweixin-java-miniapp3.4.0

com.github.wxpaywxpay-sdk0.0.3
com.github.binarywangweixin-java-mp3.4.0
com.github.binarywangweixin-java-pay3.4.0

2.1wx.config参数获取

前端传过参数:url

    appid:通过微信公众号配置取得

    timestamp:意思是当前毫秒数,可以通过代码获取:String.valueOf(System.currentTimeMillis() / 1000)

    nonceStr:32位长度随机字符串,可以通过代码获取:WXPayUtil.generateNonceStr();  (wxpay-sdk里的方法)

    signature:签名,首先通过access_token获取jsapi_ticket,然后结合jsapi_ticket,timestamp,nonceStr和传入的url,通过排序和加密生成的字符串

    注:下面两部分是拿access_token和jsapi_ticket的代码,网上也有很多,属于微信统一配置,对拉起微信支付没有影响,不感兴趣的可以跳到2.2

    得到access_token的代码:

    向微信接口发送请求拿access_token:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

==================================================================================================================================

private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
public static AccessToken getAccessToken(String appid,String appsecret) {AccessToken token = new AccessToken();String url = ACCESS_TOKEN_URL.replace("APPID", appid).replace("APPSECRET", appsecret);JSONObject jsonObject = null;try {jsonObject = doGetStr(url);} catch (ParseException e) {log.info(e.toString());} catch (IOException e) {log.info(e.toString());e.printStackTrace();}if(jsonObject!=null){token.setToken(jsonObject.getString("access_token"));token.setExpiresIn(jsonObject.getInt("expires_in"));}return token;
}

==================================

public static JSONObject doGetStr(String url) throws ParseException, IOException{CloseableHttpClient client = HttpClients.createDefault();HttpGet httpGet = new HttpGet(url);JSONObject jsonObject = null;HttpResponse httpResponse = client.execute(httpGet);HttpEntity entity = httpResponse.getEntity();if(entity != null){String result = EntityUtils.toString(entity,"UTF-8");jsonObject = JSONObject.fromObject(result);}return jsonObject;
}

==================================================================================================================================

    拿到access_token之后,向微信接口发送请求:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi";  拿jsapi_ticket

public static String getJsApiTicket(String accessToken) {String jsapi_ticket = null;try {String urlticket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi";String responseText = httpGet(urlticket);jsapi_ticket = null;JSONObject object = JSONObject.fromObject(responseText);if (object.containsKey("ticket")) {jsapi_ticket = object.getString("ticket");}} catch (Exception e) {log.info(e.toString());}return jsapi_ticket;}

==================================================================================================================================

    根据jsapi_ticket生成wx.config所需参数的代码:

public R payConfig(ServletRequest request) {String url = request.getParameter("url");String noncestr = WXPayUtil.generateNonceStr();String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//获取签名//获取tokenString accessToken = getAccessToken(payProperties.getAppId(),properties.getConfigs().get(0).getSecret()).getToken();//获取jsapi_ticketString jsapi_ticket = getJsApiTicket(accessToken);log.info("accessToken = " + accessToken);log.info("jsapi_ticket = " + jsapi_ticket);String signature = createAutoSign("jsapi_ticket",timestamp+"",noncestr,url);//创建Map用于创建签名串Map params = new HashMap<>(16);params.put("timestamp", timestamp);params.put("noncestr", noncestr);params.put("url", url);//得到签名再组装到Map里params.put("signature", signature);//传入对应的appIdparams.put("appId", payProperties.getAppId());System.out.println(params);return R.ok().put("res",params);}

==================================================================================================================================

    根据传入的map生成授权签名的代码(第一个签名):

/*** JSTicketURL模板*/
private static final String JSAPISIGN = "jsapi_ticket=%s&noncestr=%s×tamp=%s&url=%s";
public static String createAutoSign(String jsapi_ticket,String timestamp,String noncestr,String url){try{String string1 = String.format(JSAPISIGN,jsapi_ticket,timestamp,noncestr,url);// SHA1签名生成MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(string1.getBytes(Charset.forName("UTF-8")));byte[] digest = md.digest();return new String(Hex.encodeHex(digest));}catch(Exception e){log.info(e.toString());return null;}
}

首先拼接成 “jsapi_ticket=%s&noncestr=%s×tamp=%s&url=%s”的字符串,然后进行SHA1加密

==================================================================================================================================

2.2wx.chooseWxPay参数生成

统一下单:

wx.chooseWxPay直接调起微信支付接口,要获得所需参数分为两步,首先后台向微信统一下单,根据微信统一下单的返回信息,返回给手机端

前端传过参数:openid

微信统一下单需要参数:

    body: 商品信息

    openid: 前端传入的参数

    out_trade_no: 交易单号,自己随机生成的字符串,可以通过下面的    generateOrderSN()  方法生成

    total_fee:交易金额

    sign_type:统一下单需求签名方式("MD5" 就行)

    spbill_create_ip:手机端发起支付的ip地址,可以通过下面的   getIpAddress()    方法获取

    nonce_str:随机生成的字符串(生成一个新的,别用上面的)

    trade_type:JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付

    notify_url:支付成功之后的回调访问地址,微信会向这个地址发送支付成功消息

    sign:将上面所有参数放进一个map里,用 WXPayUtil.generateSignature 生成的字符串  //wxpay-sdk里封装的方法

统一下单代码:

==================================================================================================================================

String openid = request.getParameter("openid");String body = "商品";
String out_trade_no = generateOrderSN();
int total_fee = 1;
String spbill_create_ip = getIpAddress(request);
String notify_url = payProperties.getCallback();
String trade_type = "JSAPI"; //JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支
String noncestr = WXPayUtil.generateNonceStr();
String type = "MD5";try{Map data = new HashMap<>();data.put("body", body);data.put("openid", openid);data.put("out_trade_no", out_trade_no);data.put("total_fee", "1");data.put("sign_type", type);data.put("spbill_create_ip", spbill_create_ip);data.put("nonce_str", noncestr);data.put("trade_type", trade_type);data.put("notify_url", notify_url);String sign = WXPayUtil.generateSignature(data, payProperties.getMchKey()); //wxpay-sdk里封装的方法,payProperties是微信配置文件//用weixin-java-pay里封装的方法直接发起统一下单WxPayUnifiedOrderRequest unif = new WxPayUnifiedOrderRequest();unif.setBody(body);unif.setOpenid(openid);unif.setOutTradeNo(out_trade_no);unif.setTotalFee(total_fee);unif.setSignType(type);unif.setSpbillCreateIp(spbill_create_ip);unif.setNonceStr(noncestr);unif.setTradeType(trade_type);unif.setNotifyUrl(notify_url);unif.setSign(sign);//统一下单返回结果WxPayUnifiedOrderResult res = this.wxService.unifiedOrder(unif);String appidRes = payProperties.getAppId();//payProperties是微信配置,拿appidString prepay_id =  res.getPrepayId();//统一下单返回的String st = String.valueOf(System.currentTimeMillis() / 1000);;String str = WXPayUtil.generateNonceStr();String packageRes = "prepay_id=" + prepay_id;Map dataRes = new HashMap<>();dataRes.put("appId", appidRes);dataRes.put("timeStamp", st);// “timeStamp”参与验签dataRes.put("nonceStr", str);dataRes.put("package", packageRes);dataRes.put("signType", type);String signRes = WXPayUtil.generateSignature(dataRes, payProperties.getMchKey());//payProperties是微信支付配置,拿MchKeyMap resfin= new HashMap<>();resfin.put("res","success");resfin.put("timestamp",st);resfin.put("nonceStr",str);resfin.put("package",packageRes);resfin.put("signType",type);resfin.put("signature",signRes);return resfin;
}catch(WxPayException payex){log.info(payex.toString());return R.ok().put("res","failed");
}catch(Exception e){log.info(e.toString());return R.ok().put("res","failed");
}

================

生成随机字符串的代码(可以用来当out_trade_no):

private final static String AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_*|";
/*** 生成唯一识别码*/
public static String generateOrderSN() {StringBuffer orderSNBuffer = new StringBuffer();orderSNBuffer.append(System.currentTimeMillis());orderSNBuffer.append(getRandomString());return orderSNBuffer.toString();
}/*** 获取随机字符串*/
private static String getRandomString() {int len = 7;StringBuilder sb = new StringBuilder(len);Random rnd = new Random();for (int i = 0; i < len; i++) {sb.append(AB.charAt(rnd.nextInt(AB.length())));}return sb.toString();
}

===========================

根据 HttpServletRequest 获取请求端ip的代码

/*** 获取ip*/
public static String getIpAddress(HttpServletRequest request) {if (null == request) {return "UNKNOWN";}String ipAddress = request.getHeader("x-forwarded-for");String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}if ("unknown".equals(ipAddress) || ip == null) {ipAddress = request.getRemoteAddr();if (StringUtils.equalsIgnoreCase("127.0.0.1", ipAddress)|| StringUtils.equalsIgnoreCase("0:0:0:0:0:0:0:1", ipAddress)) {// 根据网卡取本机配置的IPInetAddress inet = null;try {inet = InetAddress.getLocalHost();ipAddress = inet.getHostAddress();} catch (UnknownHostException e) {log.info("[StringUtil.getIpAddr]", e);}}}// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割// "***.***.***.***".length() = 15if (StringUtils.isNotBlank(ipAddress) && 15 < ipAddress.length()) {int idx = ipAddress.indexOf(",");if (-1 < idx) {ipAddress = ipAddress.substring(0, idx);}}return ipAddress;
}

===========================

最后一步:回调,微信支付成功之后,微信会向这个网址发送消息,可以进行插入数据库的操作

public String  wxPayCallBack(HttpServletRequest request) throws Exception {log.info("微信支付回调");// 1.接收微信官方返回的支付结果InputStream inputStream = request.getInputStream();BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));String temp;StringBuilder stringBuilder = new StringBuilder();while ((temp = in.readLine()) != null) {stringBuilder.append(temp);}in.close();inputStream.close();log.info("[AccServiceOrderServiceImpl.accOrderNotify]微信支付-统一下单结果通知(验签前),resp = {}", stringBuilder.toString());// 2.微信结果验证Map resultMap = PayCommonUtil.accOrderNotifyVerify(stringBuilder.toString(),payProperties.getMchKey());//resultMap里面包含了transaction_id等所有信息//to do 插入数据库的逻辑,根据resultMap里的信息插入数据库log.info("微信支付回调结束");return "SUCCESS";
}

==================================================================================================================================

总结:

统一配置,前端发送url给后端,后端通过access_token拿到jsapi_ticket,再根据其他参数生成签名和其他数值,返给前端,前端调用wx.config实现微信统一配置

拉起支付,前端发送appid给后端,后端向微信的unifiedorder统一下单接口发送请求,根据微信的返回,将参数发送回前端,前端调用wx.chooseWxpay调起微信支付

 

 

 

 

 

    


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部