1.引入maven依赖
<!--wechat支付--> <dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-apache-httpclient</artifactId> <version>0.4.7</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.5</version> </dependency>
2.微信appid,证书配置信息类(根据自身情况放于yaml或数据库中)
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class CWechat implements Serializable { private static final long serialVersionUID = -4851521781L; /** * id */ private Long id; /** * 状态 0:关闭 1:开启 */ private Integer status; /** * 直连微信商户号 */ private String mchId; /** * 微信appid */ private String appId; /** * 微信密钥 */ private String appSecret; /** * 证书序列号 */ private String serialNo; /** * v3证书密钥 */ private String v3Secret; /** * 私钥证书路径 */ private String privateKeyPath; /** * 商户申请H5时提交的授权域名 例如 http://www.baidu.com */ private String referer; /** * 支付回调地址 */ private String notifyUrl; /** * 退款回调地址 */ private String refundServerUrl; /** * 创建时间 */ private LocalDateTime createTime; /** * 创建人 */ private String createBy; /** * 最后修改时间 */ private LocalDateTime updateTime; /** * 最后修改人 */ private String updateBy; }
3.发起支付代码(标注部分为关键,也是0.4.7版本简化了对微信平台证书的代码设置。若系统只有一个微信商户号,提出去做成配置类,把生成的httpClient对象注入容器,使用的地方直接注入即可)
/** * 微信官方JSAPI下单接口 * * @param orderNo * @param amount * @param ip * @param goodsName * @param wechatConfig * @return * @throws IOException */ public Map<String, String> createJsApiOrder(String orderNo, Integer amount, String ip, String goodsName, String openid, CWechat wechatConfig) throws Exception { //订单金额 单位分 JSONObject amountObject = new JSONObject(); amountObject.put("total", amount); amountObject.put("currency", "CNY"); //场景信息 JSONObject sceneInfoObject = new JSONObject(); sceneInfoObject.put("payer_client_ip", ip); //支付者 JSONObject payerObject = new JSONObject(); payerObject.put("openid", openid); //参数 JSONObject paramObject = new JSONObject(); paramObject.put("appid", wechatConfig.getAppId()); paramObject.put("mchid", wechatConfig.getMchId()); paramObject.put("description", goodsName); paramObject.put("out_trade_no", orderNo); paramObject.put("notify_url", wechatConfig.getNotifyUrl()); paramObject.put("amount", amountObject); paramObject.put("payer", payerObject); paramObject.put("scene_info", sceneInfoObject); HttpPost httpPost = new HttpPost(JSAPI_REQ_URL); httpPost.setHeader("Accept", "application/json"); StringEntity entity = new StringEntity(paramObject.toJSONString(), "utf-8"); entity.setContentType("application/json"); httpPost.setEntity(entity); //完成签名并执行请求 PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(wechatConfig.getPrivateKeyPath())); CertificatesManager certificatesManager = CertificatesManager.getInstance(); certificatesManager.putMerchant(wechatConfig.getMchId(), new WechatPay2Credentials(wechatConfig.getMchId(), new PrivateKeySigner(wechatConfig.getSerialNo(), merchantPrivateKey)), wechatConfig.getV3Secret().getBytes(StandardCharsets.UTF_8)); CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create() .withMerchant(wechatConfig.getMchId(), wechatConfig.getSerialNo(), merchantPrivateKey) .withValidator(new WechatPay2Validator(certificatesManager.getVerifier(wechatConfig.getMchId()))) .build(); CloseableHttpResponse response = httpClient.execute(httpPost); Map<String, String> retMap = new HashMap<>(2); try { int statusCode = response.getStatusLine().getStatusCode(); JSONObject jsonObject = JSON.parseObject(EntityUtils.toString(response.getEntity())); Assert.isTrue(statusCode == 200, "微信JSAPI下单请求失败 msg:" + JSON.toJSONString(jsonObject)); String prepayId = jsonObject.getString("prepay_id"); Assert.isTrue(StringUtils.isNotBlank(prepayId), "微信JSAPI下单失败"); Map<String, Object> params = new LinkedHashMap<>(); params.put("appId", wechatConfig.getAppId()); params.put("timeStamp", (int) (System.currentTimeMillis() / 1000)); params.put("nonceStr", RandomUtil.randomString(32)); prepayId = "prepay_id=" + prepayId; params.put("package", prepayId); params.put("signType", "RSA"); String signText = StringUtils.joining("\n", new String[]{ String.valueOf(params.get("appId")), String.valueOf(params.get("timeStamp")), String.valueOf(params.get("nonceStr")), String.valueOf(params.get("package"))}); String paySign = RSA2.sign(signText, merchantPrivateKey, "utf-8"); params.put("paySign", paySign); retMap.put("payInfo", JSON.toJSONString(params)); retMap.put("param", JSON.toJSONString(paramObject)); log.info("[微信官方JSAPI] 参数:{}, 响应:{}", JSON.toJSONString(paramObject), JSON.toJSONString(jsonObject)); } catch (Exception e) { throw new BusinessException(e.getMessage()); } finally { response.close(); } return retMap; }
3.1 StringUtils工具类
import java.io.UnsupportedEncodingException; public class StringUtils { public StringUtils() { } public static boolean equals(String str1, String str2) { if (str1 == null) { return str2 == null; } else { return str1.equals(str2); } } public static boolean isEmpty(CharSequence cs) { return cs == null || cs.length() == 0; } public static boolean isNotEmpty(CharSequence cs) { return !isEmpty(cs); } public static boolean isBlank(CharSequence cs) { int strLen; if (cs != null && (strLen = cs.length()) != 0) { for (int i = 0; i < strLen; ++i) { if (!Character.isWhitespace(cs.charAt(i))) { return false; } } return true; } else { return true; } } public static boolean isNotBlank(CharSequence cs) { return !isBlank(cs); } public static byte[] getContentBytes(String content, String charset) { if (isEmpty(charset)) { return content.getBytes(); } else { try { return content.getBytes(charset); } catch (UnsupportedEncodingException var3) { throw new RuntimeException("转码过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset); } } } public static String tryTrim(String str) { return str == null ? null : str.trim(); } public static String joining(String separator, String... str) { StringBuilder builder = new StringBuilder(); String[] var3 = str; int var4 = str.length; for (int var5 = 0; var5 < var4; ++var5) { String s = var3[var5]; if (null != s) { builder.append(s).append(separator); } } return builder.toString(); } }
3.3 RSA2工具类
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; /** * @author White * @date 2022/10/9 15:21 */ public class RSA2 { private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA"; public static String sign(String content, String privateKey, String characterEncoding) { return RSA.sign(content, privateKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); } /** * RSA签名 * * @param content 待签名数据 * @param privateKey 私钥 * @param characterEncoding 编码格式 * @return 签名值 */ public static String sign(String content, PrivateKey privateKey, String characterEncoding) { return RSA.sign(content, privateKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); } /** * RSA验签名检查 * * @param content 待签名数据 * @param sign 签名值 * @param publicKey 公钥 * @param characterEncoding 编码格式 * @return 布尔值 */ public static boolean verify(String content, String sign, String publicKey, String characterEncoding) { return RSA.verify(content, sign, publicKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); } /** * RSA验签名检查 * * @param content 待签名数据 * @param sign 签名值 * @param publicKey 公钥 * @param characterEncoding 编码格式 * @return 布尔值 */ public static boolean verify(String content, String sign, PublicKey publicKey, String characterEncoding) { return RSA.verify(content, sign, publicKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); } /** * RSA验签名检查 * * @param content 待签名数据 * @param sign 签名值 * @param publicKey 公钥 * @param characterEncoding 编码格式 * @return 布尔值 */ public static boolean verify(String content, String sign, Certificate publicKey, String characterEncoding) { PublicKey pubKey = publicKey.getPublicKey(); return verify(content, sign, pubKey, characterEncoding); } /** * 解密 * * @param content 密文 * @param privateKey 商户私钥 * @param characterEncoding 编码格式 * @return 解密后的字符串 * @throws GeneralSecurityException 解密异常 * @throws IOException 解密异常 */ public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { return RSA.decrypt(content, privateKey, characterEncoding); } /** * 得到私钥 * * @param key 密钥字符串(经过base64编码) * @return 私钥 * @throws GeneralSecurityException 加密异常 */ public static PrivateKey getPrivateKey(String key) throws GeneralSecurityException { return RSA.getPrivateKey(key); } /** * @param content 加密文本 * @param publicKey 公钥 * @param cipherAlgorithm 算法 * @param characterEncoding 编码类型 * @return 加密后文本 * @throws GeneralSecurityException 加密异常 * @throws IOException IOException */ public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding) throws GeneralSecurityException, IOException { return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey), 2048, 11, cipherAlgorithm)); }
4.1 回调数据封装为类
@Data public class WechatPayOrderNoticeDTO implements Serializable { private String id; private String create_time; private String resource_type; private String event_type; private String summary; private WechatPayOrderNoticeResourceDTO resource; } @Data public class WechatPayOrderNoticeResourceDTO implements Serializable { private String original_type; private String algorithm; private String ciphertext; private String associated_data; private String nonce; }
4.2 回调验签
@PostMapping("/wechatH5PayBack") public ResponseEntity<Map<String, Object>> wechatH5PayBack(HttpServletRequest request) throws Exception { BufferedReader br = request.getReader(); String str; StringBuilder body = new StringBuilder(); while ((str = br.readLine()) != null) { body.append(str); } WechatPayOrderNoticeDTO wechatPayOrderNoticeDTO = JSON.parseObject(body.toString(), WechatPayOrderNoticeDTO.class); Map<String, Object> map = new LinkedHashMap<>(); map.put("code", "SUCCESS"); map.put("message", "成功"); ResponseEntity<Map<String, Object>> entity = new ResponseEntity<>(map, HttpStatus.OK); if (!"TRANSACTION.SUCCESS".equals(wechatPayOrderNoticeDTO.getEvent_type())) { log.info("[微信H5支付] 回调结果为交易失败"); return entity; } // 解析支付数据 String payText = WechatApiV3RSAUtils.decrypt( wxMchIdsProperties.getV3Key().getBytes("UTF-8") , wechatPayOrderNoticeDTO.getResource().getAssociated_data().getBytes("UTF-8") , wechatPayOrderNoticeDTO.getResource().getNonce().getBytes("UTF-8") , wechatPayOrderNoticeDTO.getResource().getCiphertext()); JSONObject payNotice = JSON.parseObject(payText); // log.info("[微信H5支付] 支付回调解密数据:{}", JSON.toJSONString(payText)); String privateSerialNo = "xxx"; String privateKeyPath = "xxx"; String v3Key = "xxx"; PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath)); // 获取证书管理器实例 CertificatesManager certificatesManager = CertificatesManager.getInstance(); // 向证书管理器增加需要自动更新平台证书的商户信息 certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId, new PrivateKeySigner(privateSerialNo, merchantPrivateKey)), v3Key.getBytes(StandardCharsets.UTF_8)); // 从证书管理器中获取verifier Verifier verifier = certificatesManager.getVerifier(mchId); // 构建req,传入必要参数 NotificationRequest req = new NotificationRequest.Builder() .withSerialNumber(request.getHeader("Wechatpay-Serial")) .withNonce(request.getHeader("Wechatpay-Nonce")) .withTimestamp(request.getHeader("Wechatpay-Timestamp")) .withSignature(request.getHeader("Wechatpay-Signature")) .withBody(body.toString()) .build(); NotificationHandler handler = new NotificationHandler(verifier, v3Key.getBytes(StandardCharsets.UTF_8)); // 验签和解析请求体 Notification notification = handler.parse(req); if (Objects.isNull(notification)) { log.info("[微信JSAPI支付] notification为空 验签失败"); throw new BusinessException("微信JSAPI支付回调-验签失败"); } log.info("[微信JSAPI支付] 验签成功 解密后param : " + JSON.toJSONString(payText)); //业务代码 return entity; }
标签:return,String,微信,0.4,param,private,new,public,SpringBoot From: https://www.cnblogs.com/runwithraining/p/16848779.html