JSAPI支付V3版本更V2版本不同的是,V3需要使用自身的私钥对API URL、消息体等关键数据的组合进行SHA-256 with RSA签名。请求的签名信息通过HTTP头Authorization 传递。 说实话,微信官方文档有时候真的觉得是个憨批,东一块西一块的,说话不说全。找起来真的是麻烦。不多说,先开始第一种用法。
//先将参数放入Object ObjectMapper objectMapper = new ObjectMapper(); ObjectNode jsonObject = objectMapper.createObjectNode(); jsonObject.put("mchid",""); jsonObject.put("appid",""); jsonObject.put("description","Image形象店-深圳腾大-QQ公仔"); jsonObject.put("notify_url","http:/wechat/backll"); jsonObject.put("out_trade_no", "DO202111231042500002"); //订单号 jsonObject.putObject("amount") .put("total", 1); //金额 jsonObject.putObject("payer") .put("openid",""); //支付人信息 System.out.println(jsonObject.toString()); V3Parpy v3 = new V3Parpy(); //这里是我自己定义的类 KeyPair keyPair = v3.createPKCS12("apiclient_cert.p12","Tenpay Certificate","1483413552"); //获取私钥,这里我用的是apiclient_cert.p12证书,直接放在resources下 String url ="https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; //接口 String nonceStr = CommonUtils.generateUUID(); //随机数; long timestamp = System.currentTimeMillis() / 1000; //时间戳 String url2 ="/v3/pay/transactions/jsapi"; //加密时用到的请求地址,注意,千万不要带域名部分,带了就会报错。 String sign = v3.sign("POST",url2,timestamp,nonceStr,jsonObject.toString(),keyPair); //获取sign签名 System.out.println("sign :" +sign); String token = v3.token("商户号mchid",nonceStr,timestamp,"证书序列号",sign); System.out.println(token); String surrecs = HttpUtils.doPost2(url,jsonObject.toString(),4000,token); //发起请求,将token带过去 微信要求请求头中必须要带Authorization 格式为 WECHATPAY2-SHA256-RSA2048 token //请求完成后,我们会得到预支付交易会话标识 prepay_id。这个后面下单需要用到。 //先将下单接口需要用到的参数放入集合,然后加密得到sign签名 Listlist = new ArrayList<>(); list.add("wx0e3c90e9f84c1c05"); list.add(String.valueOf(timestamp)); list.add(nonceStr); list.add("prepay_id="+surrecs); String paySign= GetPaySign(list,keyPair); System.out.println(paySign); //得到签名后,将需要用到的接口返回给前端页面,这里前端页面和上篇v2文档里差不多,就不列出来了。唯一不同的就是需要改一下签名的方式 从MD5该为RSA model.addAttribute("appId","wx0e3c90e9f84c1c05"); model.addAttribute("timeStamp",timestamp); model.addAttribute("nonceStr",nonceStr); model.addAttribute("prepay_id","prepay_id="+surrecs); model.addAttribute("paySign",paySign); return "pay/JSAPIPay";
//使用私钥对数据进行SHA256withRSA加密,并且进行base64编码。 public static String GetPaySign(Listlist, KeyPair keyPair){ try { StringBuffer buffer = new StringBuffer(); for (String str: list) { buffer.append(str).append("n"); } System.out.println(buffer.toString()); Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(keyPair.getPrivate()); sign.update(buffer.toString().getBytes(StandardCharsets.UTF_8)); return base64Utils.encodeToString(sign.sign()); }catch (Exception e){ } return ""; } public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) { ClassPathResource resource = new ClassPathResource(keyPath); char[] pem = keyPass.toCharArray(); try { synchronized (lock) { if (store == null) { synchronized (lock) { store = KeyStore.getInstance("PKCS12"); store.load(resource.getInputStream(), pem); } } } X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias); certificate.checkValidity(); // 证书的序列号 也有用 String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(); System.out.println(serialNumber); // 证书的 公钥 PublicKey publicKey = certificate.getPublicKey(); // 证书的私钥 PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem); return new KeyPair(publicKey, storeKey); } catch (Exception e) { throw new IllegalStateException("Cannot load keys from store: " + resource, e); } } public String sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair) { try { String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body) .collect(Collectors.joining("n", "", "n")); Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(keyPair.getPrivate()); sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return base64Utils.encodeToString(sign.sign()); }catch (Exception e){ } return ""; } public String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) { final String TOKEN_PATTERN = "mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s""; // 生成token return String.format(TOKEN_PATTERN, mchId, nonceStr, timestamp, serialNo, signature); }
我在其中碰到的坑就是这个方法中的一些参数要保持一致,比如:随机数、时间戳。
在sign签名加密时,用的url不要有域名。
WECHATPAY2-SHA256-RSA2048和 token值连起来的时候,记得有个空格。
有时候签名一直错误,那么建议去微信官方下载一个验签的工具,自己在上面验证一下,一般证书密码就是商户号。
接着说第二种方法。来源自微信的: 自动更新证书
首先maven先导入依赖
com.github.wechatpay-apiv3 wechatpay-apache-httpclient0.2.3
实现方法如下:
//根据证书地址获取证书私钥 PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey( new FileInputStream("E:\WXCertUtil\cert\apiclient_key.pem")); //apiv3密钥 自己在商户平台上,上传证书的时候设置的 String apiv3 =""; //自动更新证书功能 AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( new WechatPay2Credentials("商户号mchid", new PrivateKeySigner("证书序列号", merchantPrivateKey)), apiv3.getBytes()); //WechatPayHttpClientBuilder构造的HttpClient,后续的HttpClient都可以在这个上取,不必每次生成,这里我就都放一起了。自己根据自己业务逻辑可以另外放 WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() .withMerchant("商户号mchid", "证书序列号", merchantPrivateKey) .withValidator(new WechatPay2Validator(verifier)); HttpClient httpClient = builder.build(); //JSAPI下单 ObjectMapper objectMapper = new ObjectMapper(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectNode jsonObject = objectMapper.createObjectNode(); jsonObject.put("mchid",""); jsonObject.put("appid",""); jsonObject.put("description","Image形象店-深圳腾大-QQ公仔"); jsonObject.put("notify_url","http:/wechat/backll"); jsonObject.put("out_trade_no", "DO202111231042500002"); jsonObject.putObject("amount") .put("total", 1); jsonObject.putObject("payer") .put("openid",""); System.out.println(jsonObject.toString()); //请求地址 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"); //设置请求头 httpPost.addHeader("Accept", "application/json"); httpPost.addHeader("Content-type","application/json; charset=utf-8"); objectMapper.writevalue(bos, jsonObject); httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8")); CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost); //获取预下单的单据id String bodyAsString = EntityUtils.toString(response.getEntity()); System.out.println(bodyAsString); //后面的下单内容和加密大致跟前面一样了,这里我就不重复了
这个里面可能会碰到这个问题,就是apiv3密钥哪里长度的错误。
使用Java8 加载密钥时,抛出异常InvalidKeyException: Illegal key size
这个问题在jdk9及以上不会出现,问题在于API V3密钥问题
解决方法是在 Javajdk1.8.0_45jrelibsecurity下,将local_policy.jar 和 US_export_policy.jar 替换。替换包下载路径 下载补丁包
大致就这样吧,希望能帮到你们。 有错误和改进的地方希望可以指出来。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)