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

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.具体代码

依赖:

<dependency>
  <groupId>com.github.binarywang</groupId>
  <artifactId>weixin-java-miniapp</artifactId>
  <version>3.4.0</version>
</dependency>
<dependency>
  <groupId>com.github.wxpay</groupId>
  <artifactId>wxpay-sdk</artifactId>
  <version>0.0.3</version>
</dependency>

<dependency>
  <groupId>com.github.binarywang</groupId>
  <artifactId>weixin-java-mp</artifactId>
  <version>3.4.0</version>
</dependency>
<dependency>
  <groupId>com.github.binarywang</groupId>
  <artifactId>weixin-java-pay</artifactId>
  <version>3.4.0</version>
</dependency>

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);
  //获取签名
    //获取token
  String accessToken = getAccessToken(payProperties.getAppId(),properties.getConfigs().get(0).getSecret()).getToken();
    //获取jsapi_ticket
  String jsapi_ticket = getJsApiTicket(accessToken);
  log.info("accessToken = " + accessToken);
  log.info("jsapi_ticket = " + jsapi_ticket);

  String signature = createAutoSign("jsapi_ticket",timestamp+"",noncestr,url);

  //创建Map用于创建签名串
  Map<String, Object> params = new HashMap<>(16);
  params.put("timestamp", timestamp);
  params.put("noncestr", noncestr);
  params.put("url", url);

  //得到签名再组装到Map里
  params.put("signature", signature);
  //传入对应的appId
  params.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&timestamp=%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&timestamp=%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<String, String> 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是微信配置,拿appid
  String prepay_id =  res.getPrepayId();//统一下单返回的

  String st = String.valueOf(System.currentTimeMillis() / 1000);;
  String str = WXPayUtil.generateNonceStr();
  String packageRes = "prepay_id=" + prepay_id;

  Map<String, String> 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是微信支付配置,拿MchKey

  Map<String, String> 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)) {
      // 根据网卡取本机配置的IP
      InetAddress inet = null;
      try {
        inet = InetAddress.getLocalHost();
        ipAddress = inet.getHostAddress();
      } catch (UnknownHostException e) {
        log.info("[StringUtil.getIpAddr]", e);
      }
    }
  }
  // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
  // "***.***.***.***".length() = 15
  if (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<String, String> 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调起微信支付

 

 

 

 

 

    

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值