前言:
网上有很多大佬们集成的综合支付包,做相应配置也很方便,我这个是基于微信官方文档流程做的直连模式,实现公众号和小程序支付(需要代码中正确使用appid),不得不吐槽微信的文档还是一如既往的。。。
微信官方支付文档链接:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_0.shtml,各种证书配置就按照文档上说的来就行了,下边直接上代码。
1.引入maven依赖
<dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-apache-httpclient</artifactId> <version>0.3.0</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.5</version> </dependency>
2.yaml配置
wx: pay: mchId: 16126xxxx #微信支付商户号 mchSerialNo: 60B04XXXXXXXXX #证书序列号 v3Secret: 5ovxxxxxxxxxxxxxxxx #密钥 p12Path:/usr/local/apiclient_cert.p12 #证书绝对路径地址 keyPass:16126xxxx #证书密码 默认微信商户号 privateKey: | #这个符号一定注意(回车键旁边) 是yaml中保持文本换行读取的写法 防止证书读出来加载失败 -----BEGIN PRIVATE KEY----- xxxx -----END PRIVATE KEY----- #私钥 certificate: | -----BEGIN CERTIFICATE----- xxxx -----END CERTIFICATE----- #平台证书
特别提示:
yaml配置中的certificate这个证书,需要自己生成,https://developers.weixin.qq.com/community/pay/doc/000e4a0d5dc1486acc19c6fd15bc00,这个链接中网友果果璐璐和萧的回答给出了答案
3.读取yaml内容的Properties类
@Data @Component @ConfigurationProperties(prefix = "wx.pay") public class WeChatPayProperties { /** * 商户id */ private String mchId; /** * 商户证书序列号 */ private String mchSerialNo; /** * apiV3密钥 */ private String v3Secret; /** * p12证书文件位置 */ private String p12Path; /** * 证书密码 */ private String keyPass; /** * 商户私钥 */ private String privateKey; /** * 微信支付平台证书 jar包生产的 */ private String certificate; }
4.启动配置类
@Configuration public class WxPayV3Config { @Autowired WeChatPayProperties weChatPayProperties; @Bean public CloseableHttpClient wxV3Init() { PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(weChatPayProperties.getPrivateKey()); X509Certificate wechatPayCertificate = PemUtil.loadCertificate( new ByteArrayInputStream(weChatPayProperties.getCertificate().getBytes(StandardCharsets.UTF_8))); ArrayList<X509Certificate> listCertificates = new ArrayList<>(); listCertificates.add(wechatPayCertificate); return WechatPayHttpClientBuilder.create() .withMerchant(weChatPayProperties.getMchId(), weChatPayProperties.getMchSerialNo(), merchantPrivateKey) .withWechatPay(listCertificates) .build(); } }
5.下单api
@Slf4j @Component public class WeChatPayApi { @Autowired WeChatPayProperties weChatPayProperties; @Autowired WeChatAppletProperties weChatAppletProperties; @Autowired CloseableHttpClient httpClient; /** * 微信小程序下单的请求地址 */ private static final String applet_req_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";/** * 创建微信小程序订单 * * @param orderNo 订单号 * @param amount 单位 分 * @param openid 小程序的openid * @param goodsName 订单名称 * @param notify 通知地址 * @throws IOException */ public String createWxAppletOrder(String orderNo, Integer amount, String openid, String goodsName, String notify) throws IOException {// 请求body参数 JSONObject paramObject = new JSONObject(); JSONObject amountObject = new JSONObject(); amountObject.put("total", amount); amountObject.put("currency", "CNY"); paramObject.put("amount", amountObject); paramObject.put("mchid", weChatPayProperties.getMchId()); paramObject.put("description", goodsName); paramObject.put("notify_url", notify); JSONObject payerObject = new JSONObject(); payerObject.put("openid", openid); paramObject.put("payer", payerObject); paramObject.put("out_trade_no", orderNo); paramObject.put("appid", "小程序appid"); HttpPost httpPost = new HttpPost(applet_req_url); httpPost.setHeader("Accept", "application/json"); StringEntity entity = new StringEntity(paramObject.toJSONString(), "utf-8"); entity.setContentType("application/json"); log.info("[微信下单请求参数列表]=" + paramObject); httpPost.setEntity(entity); //完成签名并执行请求 CloseableHttpResponse response = httpClient.execute(httpPost); Map<String, Object> resMap = new HashMap<>(); try { int statusCode = response.getStatusLine().getStatusCode(); Assert.isTrue(statusCode == 200, "微信下单请求失败"); JSONObject jsonObject = JSON.parseObject(EntityUtils.toString(response.getEntity())); String prepayId = jsonObject.getString("prepay_id"); Assert.isTrue(StringUtils.isNotBlank(prepayId), "下单获取参数失败"); String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); String nonceStr = RandomUtil.randomString(32).toUpperCase(); String packagep = "prepay_id=" + prepayId; SortedMap<Object, Object> params = new TreeMap<>(); params.put("appId", weChatAppletProperties.getAppId()); params.put("timeStamp", timeStamp); params.put("nonceStr", nonceStr); params.put("package", packagep); params.put("signType", "RSA"); resMap.put("appId", "小程序appid"); resMap.put("timeStamp", timeStamp); resMap.put("nonceStr", nonceStr); resMap.put("package", packagep); resMap.put("signType", "RSA"); resMap.put("paySign", WechatPayUtils.createSign(params, weChatPayProperties.getP12Path(), weChatPayProperties.getKeyPass())); log.info("[微信支付] 支付参数:" + JSON.toJSONString(resMap)); } catch (Exception e) { throw new BusinessException(e.getMessage()); } finally { response.close(); } return JSON.toJSONString(resMap); }
6.微信paySign签名生成 WechatPayUtils类,KeyPairFactory类,WechatRSAUtils类
public class WechatPayUtils { /** * sign签名 * * @param map * @return */ public static String createSign(SortedMap<Object, Object> map, String certPath, String keyPass) throws Exception { String signatureStr = Stream.of( String.valueOf(map.get("appId")) , String.valueOf(map.get("timeStamp")) , String.valueOf(map.get("nonceStr")) , String.valueOf(map.get("package")) ).collect(Collectors.joining("\n", "", "\n")); KeyPair keyPair = KeyPairFactory.createPKCS12(certPath, "Tenpay Certificate", keyPass); return WechatRSAUtils.payRequestSign(signatureStr, keyPair); }
public class KeyPairFactory { private static KeyStore store; private static final Object lock = new Object(); /** * 获取公私钥. * * @param keyPath the key path * @param keyAlias the key alias * @param keyPass password * @return the key pair */ public static KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) throws Exception { // ClassPathResource resource = new ClassPathResource(keyPath); //喜欢用相对路径的同学使用这一行 PathResource resource = new PathResource(keyPath); char[] pem = keyPass.toCharArray(); 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(); // 证书的 公钥 PublicKey publicKey = certificate.getPublicKey(); // 证书的私钥 PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem); return new KeyPair(publicKey, storeKey); } }
public class WechatRSAUtils { /** * 生成支付签名 * * @param signStr * @param keyPair * @return **/ @SneakyThrows public static String payRequestSign(String signStr, KeyPair keyPair) { Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(keyPair.getPrivate()); sign.update(signStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(sign.sign()); } }