【微信支付】微信JSAPI支付流程 (2)

生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。

Application 开启计划任务,用于定时刷新

@EnableScheduling//开启计划任务 表达式参考

配置全局缓存

WxParament

/**
  * 获取ACCESS_TOKEN
  * 刷新规则:
  * 1、启动设置该值
  * 2、一小时刷新一次
  */
 public static String ACCESS_TOKEN;
 /**
  * 获取JSAPI_TICKET
  * 刷新规则:
  * 1、启动设置该值
  * 2、一小时刷新一次
  */
 public static String JSAPI_TICKET;

定时刷新

InitServiceImpl

 @Autowired
 private YmlParament ymlParament;

 @PostConstruct
 @Override
 public void init() {
  initAccessToken();
  initJsapiTicket();
 }

 /**
  * 初始化AccessToken 一小时执行一次
  *cron表达式可在线生成:https://cron.qqe2.com/
  */
 @Scheduled(cron = "0 0 0/1 * * ?")
 @Override
 public void initAccessToken() {
  try {
   WxParament.ACCESS_TOKEN = GetToken.getAccessToken(ymlParament.getH_app_id(), ymlParament.getH_app_secret());
  } catch (Exception e) {
   log.error("<====initAccessToken初始化失败!==>" + e);
   e.printStackTrace();
  }
  log.info("<====初始化initAccessToken成功,值为==>" + WxParament.ACCESS_TOKEN);
 }

 /**
  * 初始化JSAPI_TICKET 一小时执行一次
  */
 @Scheduled(cron = "0 0 0/1 * * ?")
 @Override
 public void initJsapiTicket() {
  try {
   log.info("<====正在刷新 JSAPI_TICKET ==>");
   WxParament.JSAPI_TICKET = GetToken.getTicket(ymlParament.getH_app_id(), ymlParament.getH_app_secret(),
     WxParament.ACCESS_TOKEN);
  } catch (Exception e) {
   log.error("<====initJsapiTicket初始化失败!==>" + e);
   e.printStackTrace();
  }
  log.info("<====刷新 JSAPI_TICKET 成功,值为 ==>" + WxParament.JSAPI_TICKET);
 }

GetToken

/**
  * ~~~~~~~~~ 第一步 ~~~~~~~~~
  * 有效期两个小时,注意缓存 获取access_token,记得配置ip白名单
  * 参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):
  * https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
  * @param appid
  * @param secret
  * @throws Exception 
  */
 public static String getAccessToken(String appid, String secret) throws Exception {
  String res=HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token",
    "grant_type=client_credential&appid=" + appid + "&secret=" + secret);
   String access_token=JSON.parseObject(res).getString("access_token");
   if(IsNull.isNull(access_token)) {
   throw new Exception(res);
  }
   return access_token;
 }

 /**
  * ~~~~~~~~~ 第二步 ~~~~~~~~~
  * 有效期两个小时,注意缓存 用第一步拿到的access_token 采用http
  * GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):
  * https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
  * @param appid
  * @param secret
  * @param accessToken 如果 accessToken 传null 则重新获取 access_token,如果accessToken有值,则直接获取Ticket
  * @throws Exception
  */
 public static String getTicket(String appid, String secret,String accessToken) throws Exception {
  String token=IsNull.isNull(accessToken)?getAccessToken(appid, secret):accessToken;
  String res=HttpUtil.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket",
    "access_token=" + token + "&type=jsapi");
  String ticket=JSON.parseObject(res).getString("ticket");
  if(IsNull.isNull(ticket)) {
   throw new Exception(res);
  }
  return ticket;
 }
 
 /**
  *
  * jsapi_ticket是公众号用于调用微信JS接口的临时票据
  * 前台统一js签名,参考说明如下
  * 1、业务说明:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#3
  * 2、签名算法:1-JS-SDK使用权限签名算法:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
  * @param jsapi_ticket,必须传入,每次有效时间两小时,如果频繁获取会报错
  * @param url url(当前支付页面的URL,不包含#及其后面部分)
  * @return
  */
 public static Map<String, String> getJsSignature(String jsapi_ticket, String url) {
  return sign(jsapi_ticket, url);
 }
 /*签名算法*/
 private static Map<String, String> sign(String jsapi_ticket, String url) {
  Map<String, String> ret = new HashMap<String, String>();
  String nonce_str = UUID.randomUUID().toString();;
  String timestamp = Long.toString(System.currentTimeMillis() / 1000);
  String signature = null;
  // 注意这里参数名必须全部小写,且必须有序
  String string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url=" + url;
  try {
   MessageDigest crypt = MessageDigest.getInstance("SHA-1");
   crypt.reset();
   crypt.update(string1.getBytes("UTF-8"));
   signature = byteToHex(crypt.digest());
  } catch (NoSuchAlgorithmException e) {
   e.printStackTrace();
  } catch (UnsupportedEncodingException e) {
   e.printStackTrace();
  }
  ret.put("url", url);
  ret.put("jsapi_ticket", jsapi_ticket);
  ret.put("nonceStr", nonce_str);
  ret.put("timestamp", timestamp);
  ret.put("signature", signature);
  return ret;
 }

 private static String byteToHex(final byte[] hash) {
  Formatter formatter = new Formatter();
  for (byte b : hash) {
   formatter.format("%02x", b);
  }
  String result = formatter.toString();
  formatter.close();
  return result;
 }

HttpUtil

public static String get(String urlStr, Map<String, String> parameters) throws IOException {
  URL url = new URL(urlStr);
  HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
  httpURLConnection.setDoInput(true);
  httpURLConnection.setDoOutput(true); // 设置该连接是可以输出的
  httpURLConnection.setRequestMethod("GET"); // 设置请求方式
  httpURLConnection.setRequestProperty("charset", "utf-8");
  PrintWriter pw = new PrintWriter(new BufferedOutputStream(httpURLConnection.getOutputStream()));

  StringBuffer parameter = new StringBuffer();
  parameter.append("1=1");
  for (Entry<String, String> entry : parameters.entrySet()) {
   parameter.append("&" + entry.getKey() + "=" + entry.getValue());
  }
  pw.write(parameter.toString());// 向连接中写数据(相当于发送数据给服务器)
  pw.flush();
  pw.close();
    
  BufferedReader br = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "utf-8"));
  String line = null;
  StringBuilder sb = new StringBuilder();
  while ((line = br.readLine()) != null) { // 读取数据
   sb.append(line + "\n");
 }
  br.close();
 return sb.toString();
 }

JacksonUtil

public class JacksonUtil {
    public static String parseString(String body, String field) {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode node;
        try {
            node = mapper.readTree(body);
            JsonNode leaf = node.get(field);
            if (leaf != null) {
                return leaf.asText();
            }
        } catch (IOException e) {
         e.printStackTrace();
        }
        return null;
    }
}

 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgwgyy.html