From e75fc8e32d1255066d1b96f6b8954cc7fcf4748e Mon Sep 17 00:00:00 2001 From: Cauchy Date: Mon, 9 Oct 2023 15:37:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/weChatPay/config/WechatPayConfig.java | 142 +++------- .../controller/WeChatPayController.java | 257 ++++-------------- .../modules/pay/weChatPay/dto/WechatDto.java | 40 +-- .../modules/pay/weChatPay/util/WxPayUtil.java | 217 +++++++-------- .../mapper/book/ShopProudictBookDao.xml | 2 +- src/main/resources/weChatConfig.properties | 67 ++--- 6 files changed, 232 insertions(+), 493 deletions(-) diff --git a/src/main/java/com/peanut/modules/pay/weChatPay/config/WechatPayConfig.java b/src/main/java/com/peanut/modules/pay/weChatPay/config/WechatPayConfig.java index 72636938..ea8e8352 100644 --- a/src/main/java/com/peanut/modules/pay/weChatPay/config/WechatPayConfig.java +++ b/src/main/java/com/peanut/modules/pay/weChatPay/config/WechatPayConfig.java @@ -1,39 +1,30 @@ package com.peanut.modules.pay.weChatPay.config; -import cn.hutool.core.io.resource.ClassPathResource; -import cn.hutool.core.io.resource.Resource; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; -import com.wechat.pay.contrib.apache.httpclient.auth.*; +import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; +import com.wechat.pay.contrib.apache.httpclient.auth.Verifier; +import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials; +import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator; import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager; import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException; import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException; import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; -import io.swagger.annotations.ApiOperation; import lombok.Data; -import lombok.Value; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FileUtils; import org.apache.http.impl.client.CloseableHttpClient; -import org.junit.After; -import org.junit.Before; -import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; -import org.springframework.stereotype.Component; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.util.ResourceUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import javax.servlet.http.HttpServletResponse; -import java.io.*; -import java.net.URI; -import java.net.URL; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.PrivateKey; -import java.util.Enumeration; /** @@ -43,104 +34,71 @@ import java.util.Enumeration; @Configuration @Data @Slf4j -@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点 -@PropertySource(value = "classpath:weChatConfig.properties" ) //读取配置文件 -//@Component +@PropertySource(value = "classpath:weChatConfig.properties") //读取配置文件 public class WechatPayConfig implements Serializable { /** * APPID */ - + @Value("${wxpay.appId}") private String appId; /** * 商户号 */ - + @Value("${wxpay.mchId}") private String mchId; /** - * 服务商商户号 + * pay url */ - - private String slMchId; + @Value("${wxpay.payUrl}") + private String payUrl; /** - * APIv2密钥 - */ - - private String apiKey; - /** - * APIv3密钥 - */ - - private String apiV3Key; - /** - * 支付通知回调地址 + * 回调地址 */ + @Value("${wxpay.notifyUrl}") private String notifyUrl; + /** + * key + */ + @Value("${wxpay.apiV3Key}") + private String apiV3Key; /** * 退款回调地址 */ - + @Value("${wxpay.refundNotifyUrl}") private String refundNotifyUrl; - /** * API 证书中的 key.pem 商户私钥 */ - + @Value("${wxpay.keyPemPath}") private String keyPemPath; - /** * 商户序列号 */ - + @Value("${wxpay.serialNo}") private String serialNo; + @Value("${wxpay.wechatPayCertificateUrl}") + private String wechatPayCertificateUrl; + + @Value("${wxpay.privateKeyUrl}") + private String privateKeyUrl; + + @Bean + public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); + } + /** - * 微信支付V3-url前缀 + * 获取私钥 + * + * @param keyPemPath + * @return */ - - private String baseUrl; - - private String domain; - - - - - - - - - - - //获取私钥工具 public PrivateKey getPrivateKey(String keyPemPath) { try { - /* - - 解决方案 - 1. 将私钥文件放在应用的外部,然后通过文件路径来读取。例如: - privateKeyPath = "/etc/myapp/apiclient_key.pem"; - File file = new File(privateKeyPath); - return PemUtil.loadPrivateKey(new FileInputStream(file)); - 这样你就可以将 privateKeyPath 设置为任意位置的文件路径。 - - - 2.如果你确实需要将私钥文件打包到应用中,你可以使用 ResourceUtils.getURL(filename).openStream() 来获取文件内容,例如: - privateKeyPath = "classpath:cert/apiclient_key.pem"; - return PemUtil.loadPrivateKey(ResourceUtils.getURL(privateKeyPath).openStream()); - 这样你就可以从应用的 classpath 中读取文件内容。 - - ResourceUtils.getURL(filename).openStream() 来获取资源,这是与平台无关的,所以在Windows、Linux以及其他任何支持Java的平台上都能正常使用。只要你的资源文件(在这个例子中是私钥文件)被正确的包含在了你的应用的classpath中,那么你就可以在任何平台上使用这个方法来读取资源文件的内容。 - 这里需要注意的是,classpath: 是一个特殊的协议前缀,它表示资源是从classpath中获取的。当你的资源文件被打包进jar或war时,它们就位于应用的classpath中,因此你可以使用 classpath: 前缀来获取这些文件。 - 因此,无论你的应用运行在Windows还是Linux,或者是其他任何操作系统上,使用方法2都不会有问题。只要你的资源文件被正确地打包进了应用,那么就可以使用这种方法来读取文件内容。 - */ - - - // 修改后的方法 - return PemUtil.loadPrivateKey(ResourceUtils.getURL(keyPemPath).openStream()); - } catch (FileNotFoundException e) { throw new RuntimeException("私钥文件不存在", e); } catch (IOException e) { @@ -149,7 +107,6 @@ public class WechatPayConfig implements Serializable { } - /** * 获取证书管理器实例 签名验证器 * @@ -157,27 +114,16 @@ public class WechatPayConfig implements Serializable { */ @Bean public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException { - log.info("获取证书管理器实例"); - //获取商户私钥 - - PrivateKey privateKey = getPrivateKey(keyPemPath); - //私钥签名对象 PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey); - //身份认证对象 WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner); - // 使用定时更新的签名验证器,不需要传入证书 - - CertificatesManager certificatesManager = CertificatesManager.getInstance(); - certificatesManager.putMerchant( - mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8)); - + certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8)); return certificatesManager.getVerifier(mchId); } @@ -193,20 +139,16 @@ public class WechatPayConfig implements Serializable { log.info("获取HttpClient"); //获取商户私钥 - PrivateKey privateKey = getPrivateKey(keyPemPath); - WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() .withMerchant(mchId, serialNo, privateKey) .withValidator(new WechatPay2Validator(verifier)); - CloseableHttpClient httpClient =builder.build(); + CloseableHttpClient httpClient = builder.build(); // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 return httpClient; } - - /** * 获取HttpClient,无需进行应答签名验证,跳过验签的流程 */ diff --git a/src/main/java/com/peanut/modules/pay/weChatPay/controller/WeChatPayController.java b/src/main/java/com/peanut/modules/pay/weChatPay/controller/WeChatPayController.java index e3877c81..54884e10 100644 --- a/src/main/java/com/peanut/modules/pay/weChatPay/controller/WeChatPayController.java +++ b/src/main/java/com/peanut/modules/pay/weChatPay/controller/WeChatPayController.java @@ -29,13 +29,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.math.BigDecimal; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; @Slf4j @@ -76,70 +69,49 @@ public class WeChatPayController { //无需应答签名 @Autowired private CloseableHttpClient wxPayClient; + @Autowired private WxPayUtil wxPayUtil; - // 由微信生成的应用ID,全局唯一。 - public static final String appId = "wx47134a8f15083734"; - // 直连商户的商户号,由微信支付生成并下发。 - public static final String mchId = "1612860909"; - // 商户证书序列号 7B5676E3CDF56680D0414A009CE501C844DBE2D6 679AECB2F7AC4183033F713828892BA640E4EEE3 - public static final String mchSerialNo = "679AECB2F7AC4183033F713828892BA640E4EEE3"; - // 微信下单Url - public static final String payUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/app"; /** - * App 微信下单 + * 生成预订单 * - * @param + * @param dto * @return - * @PathVariable + * @throws Exception */ - - @RequestMapping(value = "/placeAnOrder/app") + @RequestMapping(value = "/placeAnOrder/shoppingPay") @Transactional(rollbackFor = Exception.class) - public R pay(@RequestBody WechatDto dto) throws Exception { - log.info("微信生成订单"); - List one = this.buyOrderService.getBaseMapper().selectList(new QueryWrapper().eq("order_sn", dto.getOrderSn())); - BuyOrderEntity order = one.get(0); - // 获取订单 + public R shoppingPay(@RequestBody WechatDto dto) throws Exception { + log.info("生成预订单"); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("order_sn", dto.getOrderSn()); + BuyOrderEntity order = buyOrderService.getOne(queryWrapper); + Map paramMap = new HashMap<>(); - paramMap.put("appid", appId); - paramMap.put("mchid", mchId); - paramMap.put("description", "微信充值"); + paramMap.put("appid", wechatPayConfig.getAppId()); + paramMap.put("mchid", wechatPayConfig.getMchId()); + paramMap.put("description", "微信支付"); // 订单编号 paramMap.put("out_trade_no", order.getOrderSn()); - // paramMap.put("attach",""); //自定义数据 支付完成后才能显示 在查询API和支付通知中原样返回,可作为自定义参数使用 - //paramMap.put("notify_url", wechatPayConfig.getNotifyUrl()); - paramMap.put("notify_url","https://testapi.nuttyreading.com/pay/payNotify"); - - // paramMap.put("time_expire",afterString); - // 实收金额0.38乘100=38 - BigDecimal realsmoney = order.getRealMoney(); + // 微信回调地址 + paramMap.put("notify_url", wechatPayConfig.getNotifyUrl()); + BigDecimal totalAmount = dto.getTotalAmount(); + // 这里 * 100 单位为 ‘分’ BigDecimal hand = new BigDecimal("100"); - realsmoney = realsmoney.multiply(hand); + totalAmount = totalAmount.multiply(hand); + Map amountMap = new HashMap<>(); - amountMap.put("total", realsmoney); + amountMap.put("total", totalAmount); amountMap.put("currency", "CNY"); - - - BigDecimal money = order.getRealMoney(); - BigDecimal han = new BigDecimal("100"); - realsmoney = realsmoney.multiply(han); - Map amount = new HashMap<>(); - amount.put("total", realsmoney); - amount.put("currency", "CNY"); - - if (dto.getTotalAmount() != order.getRealMoney()) { - - } paramMap.put("amount", amountMap); JSONObject json = JSONObject.parseObject(JSON.toJSONString(paramMap)); - log.info("请求参数" + paramMap); - com.alibaba.fastjson.JSONObject jsonObject1 = wxPayUtil.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/app", json.toJSONString()); - String prepayid = jsonObject1.getString("prepay_id"); + log.info("请求参数:{}", paramMap); + JSONObject responseJson = wxPayUtil.doPostWexinV3(wechatPayConfig.getPayUrl(), json.toJSONString()); + String prepayId = responseJson.getString("prepay_id"); // 传入参数 payUrl 发送post请求 - HttpPost httpPost = new HttpPost(payUrl); + HttpPost httpPost = new HttpPost(wechatPayConfig.getPayUrl()); // 将json数据转换成字符串 StringEntity entity = new StringEntity(json.toString(), "utf-8"); // 设置该请求的Content-Type为application/json 都是json格式 @@ -150,77 +122,48 @@ public class WeChatPayController { httpPost.setHeader("Accept", "application/json"); CloseableHttpResponse response = wxPayClient.execute(httpPost); // 向微信支付平台发送请求,处理响应结果,并将订单信息保存到数据库中。 - String bodyAsString = EntityUtils.toString(response.getEntity());//响应体 + String bodyAsString = EntityUtils.toString(response.getEntity()); // 时间戳 - Long timestamp = System.currentTimeMillis() / 1000; + long timestamp = System.currentTimeMillis() / 1000; // 随机串 String nonceStr = UUID.randomUUID().toString().replace("-", ""); - - String sign = wxPayUtil.getSign(WxPayUtil.appId, timestamp, nonceStr, prepayid); - log.info("签名:" + sign); - - Map Map = new HashMap(); - Map.put("prepayid", prepayid); - Map.put("timestamp", timestamp + ""); - Map.put("noncestr", nonceStr); - Map.put("sign", sign); - Map.put("appid", appId); - Map.put("package", "Sign=WXPay"); - Map.put("extData", "sign"); - Map.put("partnerid", mchId); + String sign = wxPayUtil.getSign(wechatPayConfig.getAppId(), timestamp, nonceStr, prepayId); + log.info("签名:{}", sign); + Map map = new HashMap<>(); + map.put("prepayid", prepayId); + map.put("timestamp", timestamp); + map.put("noncestr", nonceStr); + map.put("sign", sign); + map.put("appid", wechatPayConfig.getAppId()); + map.put("package", "Sign=WXPay"); + map.put("extData", "sign"); + map.put("partnerid", wechatPayConfig.getMchId()); try { - int statusCode = response.getStatusLine().getStatusCode(); //响应状态码 - if (statusCode == 200) { //处理成功 - System.out.println("成功,返回结果 = " + bodyAsString); //返回响应体 EntityUtils.toString(response.getEntity()) + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { + log.info("返回结果:{}", bodyAsString); // 添加微信支付订单信息 PayWechatOrderEntity wechat = new PayWechatOrderEntity(); - wechat.setCustomerId(order.getUserId()); //用户id - wechat.setCreateTime(new Date()); //创建订单时间 - wechat.setOrderSn(order.getOrderSn()); //订单编号 - wechat.setPrepayId(prepayid); //预支付回话标识 标识为响应体EntityUtils.toString(response.getEntity()) - wechat.setTotalAmount(dto.getTotalAmount()); //支付实收金额 - wechat.setSystemLog(response.toString()); //日志 - wechat.setPayType(order.getOrderType()); //交易类型 - wechat.setOrderId(order.getOrderSn()); //订单号 - wechat.setBuyOrderId(dto.getBuyOrderId()); //购买配置id -// wechat.setEndtime(after); - this.payWechatOrderService.save(wechat); //微信订单表拿到数据保存数据库 - + wechat.setCustomerId(order.getUserId()); + wechat.setCreateTime(new Date()); + wechat.setOrderSn(order.getOrderSn()); + wechat.setPrepayId(prepayId); + wechat.setTotalAmount(order.getRealMoney()); + wechat.setSystemLog(response.toString()); + wechat.setPayType(order.getOrderType()); + wechat.setOrderId(order.getOrderSn()); + wechat.setBuyOrderId(dto.getBuyOrderId()); + this.payWechatOrderService.save(wechat); } else if (statusCode == 204) { //处理成功,无返回Body - System.out.println("成功"); + log.info("支付成功"); } else { - System.out.println("下单失败 = " + statusCode + ",返回结果 = " + bodyAsString); + log.error("下单失败,响应码为:{},返回结果:{}", statusCode, bodyAsString); throw new IOException("request failed"); } } finally { response.close(); } -// 返回url和订单号 - return R.ok().put("paramMap", paramMap).put("Map", Map); - - } - - /** - * 获取私钥。 - * - * @param filename 私钥文件路径 (required) - * @return 私钥对象 - */ - public static PrivateKey getPrivateKey(String filename) throws Exception { - String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8"); - try { - String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") - .replace("-----END PRIVATE KEY-----", "") - .replaceAll("\\s+", ""); - - KeyFactory kf = KeyFactory.getInstance("RSA"); - return kf.generatePrivate( - new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("当前Java环境不支持RSA", e); - } catch (InvalidKeySpecException e) { - throw new RuntimeException("无效的密钥格式"); - } + return R.ok().put("paramMap", paramMap).put("Map", map); } /** @@ -256,21 +199,16 @@ public class WeChatPayController { if ("order".equals(order.getOrderType())) { BuyOrderEntity orderEntity = buyOrderService.getBaseMapper().selectOne(new QueryWrapper().eq("order_sn", wechatEntity.getOrderSn())); BigDecimal realMoney = orderEntity.getRealMoney(); - System.out.println("=========== 获取到 orderSn==================" + order.getOrderSn()); // 查询订单的所有 book_id List orderBookIdList = shopProudictBookService.getOrderBookId(order.getOrderSn()); - System.out.println("=========== 获取到 orderBookIdList==========" + orderBookIdList); // 去重 Set set = new HashSet<>(orderBookIdList); orderBookIdList.clear(); orderBookIdList.addAll(set); // 查询用户的所有 book_id - System.out.println("==============去重后orderBookIdList=========" + orderBookIdList); List userBookIdList = userEbookBuyService.getUserBookId(order.getUserId()); - System.out.println("==============userBookIdList===============" + userBookIdList); // 取差集 orderBookIdList.removeAll(userBookIdList); - System.out.println("============== 差集========================" + orderBookIdList); // 为用户添加书籍 List userEbookBuyEntities = new ArrayList<>(); for (Integer bookId : orderBookIdList) { @@ -279,12 +217,9 @@ public class WeChatPayController { entity.setBookId(bookId); userEbookBuyEntities.add(entity); } - System.out.println("============userEbookBuyEntities===========" + userEbookBuyEntities); boolean b = userEbookBuyService.saveBatch(userEbookBuyEntities); - System.out.println("============result========================" + b); if (wechatEntity.getTotalAmount().compareTo(realMoney) == 0) { buyOrderService.updateOrderStatus(order.getUserId(), order.getOrderSn(), "0"); - } } if ("point".equals(order.getOrderType())) { @@ -348,88 +283,4 @@ public class WeChatPayController { } return bodyMap; } - - - @RequestMapping(value = "/placeAnOrder/shoppingpay") - @Transactional(rollbackFor = Exception.class) - public R shoppingpay(@RequestBody WechatDto dto) throws Exception { - log.info("生成订单"); - List one = this.buyOrderService.getBaseMapper().selectList(new QueryWrapper().eq("order_sn", dto.getOrderSn())); - BuyOrderEntity order = one.get(0); - - // 获取订单 - Map paramMap = new HashMap<>(); - paramMap.put("appid", appId); - paramMap.put("mchid", mchId); - paramMap.put("description", "微信充值"); - // 订单编号 - paramMap.put("out_trade_no", order.getOrderSn()); - paramMap.put("notify_url", wechatPayConfig.getNotifyUrl()); - BigDecimal realsmoney = dto.getTotalAmount(); - BigDecimal hand = new BigDecimal("100"); - realsmoney = realsmoney.multiply(hand); - Map amountMap = new HashMap<>(); - amountMap.put("total", realsmoney); - amountMap.put("currency", "CNY"); - paramMap.put("amount", amountMap); - JSONObject json = JSONObject.parseObject(JSON.toJSONString(paramMap)); - log.info("请求参数" + paramMap); - com.alibaba.fastjson.JSONObject jsonObject1 = wxPayUtil.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/app", json.toJSONString()); - String prepayid = jsonObject1.getString("prepay_id"); - // 传入参数 payUrl 发送post请求 - HttpPost httpPost = new HttpPost(payUrl); - // 将json数据转换成字符串 - StringEntity entity = new StringEntity(json.toString(), "utf-8"); - // 设置该请求的Content-Type为application/json 都是json格式 - entity.setContentType("application/json"); - // 将实体对象设置到HttpPost表示要传递该数据到服务器端。 - httpPost.setEntity(entity); - // 设置请求头部的Accept属性为"application/json"表示客户端希望接收的为json。 - httpPost.setHeader("Accept", "application/json"); - CloseableHttpResponse response = wxPayClient.execute(httpPost); - // 向微信支付平台发送请求,处理响应结果,并将订单信息保存到数据库中。 - String bodyAsString = EntityUtils.toString(response.getEntity());//响应体 - // 时间戳 - long timestamp = System.currentTimeMillis() / 1000; - // 随机串 - String nonceStr = UUID.randomUUID().toString().replace("-", ""); - String sign = wxPayUtil.getSign(WxPayUtil.appId, timestamp, nonceStr, prepayid); - log.info("签名:" + sign); - Map map = new HashMap<>(); - map.put("prepayid", prepayid); - map.put("timestamp", timestamp + ""); - map.put("noncestr", nonceStr); - map.put("sign", sign); - map.put("appid", appId); - map.put("package", "Sign=WXPay"); - map.put("extData", "sign"); - map.put("partnerid", mchId); - try { - int statusCode = response.getStatusLine().getStatusCode(); //响应状态码 - if (statusCode == 200) { //处理成功 - System.out.println("成功,返回结果 = " + bodyAsString); //返回响应体 EntityUtils.toString(response.getEntity()) - // 添加微信支付订单信息 - PayWechatOrderEntity wechat = new PayWechatOrderEntity(); - wechat.setCustomerId(order.getUserId()); //用户id - wechat.setCreateTime(new Date()); //创建订单时间 - wechat.setOrderSn(order.getOrderSn()); //订单编号 - wechat.setPrepayId(prepayid); //预支付回话标识 标识为响应体EntityUtils.toString(response.getEntity()) - wechat.setTotalAmount(order.getRealMoney()); //支付实收金额 - wechat.setSystemLog(response.toString()); //日志 - wechat.setPayType(order.getOrderType()); //交易类型 - wechat.setOrderId(order.getOrderSn()); //订单号 - wechat.setBuyOrderId(dto.getBuyOrderId()); //购买配置id - this.payWechatOrderService.save(wechat); //微信订单表拿到数据保存数据库 - } else if (statusCode == 204) { //处理成功,无返回Body - System.out.println("成功"); - } else { - System.out.println("下单失败 = " + statusCode + ",返回结果 = " + bodyAsString); - throw new IOException("request failed"); - } - } finally { - response.close(); - } - // 返回url和订单号 - return R.ok().put("paramMap", paramMap).put("Map", map); - } } diff --git a/src/main/java/com/peanut/modules/pay/weChatPay/dto/WechatDto.java b/src/main/java/com/peanut/modules/pay/weChatPay/dto/WechatDto.java index ca543a65..47b808a4 100644 --- a/src/main/java/com/peanut/modules/pay/weChatPay/dto/WechatDto.java +++ b/src/main/java/com/peanut/modules/pay/weChatPay/dto/WechatDto.java @@ -1,43 +1,23 @@ package com.peanut.modules.pay.weChatPay.dto; import lombok.Data; + import java.io.Serializable; import java.math.BigDecimal; - @Data public class WechatDto implements Serializable { - - //totalAmount: payItem.realMoney, - // customerId: - + /** + * 订单号 + */ private String orderSn; - + /** + * 订单 ID + */ private Integer buyOrderId; + /** + * 总金额 + */ private BigDecimal totalAmount; - - public BigDecimal getTotalAmount() { - return totalAmount; - } - - public void setTotalAmount(BigDecimal totalAmount) { - this.totalAmount = totalAmount; - } - - public String getOrderSn() { - return orderSn; - } - - public void setOrderSn(String orderSn) { - this.orderSn = orderSn; - } - - public Integer getBuyOrderId() { - return buyOrderId; - } - - public void setBuyOrderId(Integer buyOrderId) { - this.buyOrderId = buyOrderId; - } } diff --git a/src/main/java/com/peanut/modules/pay/weChatPay/util/WxPayUtil.java b/src/main/java/com/peanut/modules/pay/weChatPay/util/WxPayUtil.java index f7beebde..8650bb52 100644 --- a/src/main/java/com/peanut/modules/pay/weChatPay/util/WxPayUtil.java +++ b/src/main/java/com/peanut/modules/pay/weChatPay/util/WxPayUtil.java @@ -1,6 +1,7 @@ package com.peanut.modules.pay.weChatPay.util; import com.alibaba.fastjson.JSONObject; +import com.peanut.modules.pay.weChatPay.config.WechatPayConfig; import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; import lombok.Data; @@ -11,6 +12,7 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.FileInputStream; @@ -21,127 +23,116 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Base64; - @Component - @Data - public class WxPayUtil { +@Component +@Data +public class WxPayUtil { - public static final String mchId = "1612860909"; // 商户号 + @Autowired + WechatPayConfig wechatPayConfig; - public static final String appId = "wx47134a8f15083734"; // appId + private CloseableHttpClient httpClient; - public static final String apiV3Key = "4aYFklzaULeGlr7oJPZ6rHWKcxjihZUF"; // apiV3秘钥 - //商户私钥路径 - //public static final String privateKeyUrl = "/usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem"; - public static final String privateKeyUrl = "C:/Users/Cauchy/IdeaProjects/nuttyreading-java/src/main/resources/cent/apiclient_key.pem"; + public void setup() { + PrivateKey merchantPrivateKey = null; + X509Certificate wechatPayCertificate = null; - //平台证书路径 - //public static final String wechatPayCertificateUrl = "/usr/local/hs/peanut_book/target/classes/cent/wechatpay_7B5676E3CDF56680D0414A009CE501C844DBE2D6.pem"; - public static final String wechatPayCertificateUrl = "C:/Users/Cauchy/IdeaProjects/nuttyreading-java/src/main/resources/cent/wechatpay_7B5676E3CDF56680D0414A009CE501C844DBE2D6.pem"; - //第一步申请完证书后,在API证书哪里点击管理证书就能看到 - public static final String mchSerialNo = "679AECB2F7AC4183033F713828892BA640E4EEE3"; // 商户证书序列号 + try { + merchantPrivateKey = PemUtil.loadPrivateKey( + new FileInputStream(wechatPayConfig.getPrivateKeyUrl())); + wechatPayCertificate = PemUtil.loadCertificate( + new FileInputStream(wechatPayConfig.getWechatPayCertificateUrl())); - private CloseableHttpClient httpClient; - - public void setup() { - PrivateKey merchantPrivateKey = null; - X509Certificate wechatPayCertificate = null; - - try { - merchantPrivateKey = PemUtil.loadPrivateKey( - new FileInputStream(privateKeyUrl)); - wechatPayCertificate = PemUtil.loadCertificate( - new FileInputStream(wechatPayCertificateUrl)); - - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - - ArrayList listCertificates = new ArrayList<>(); - listCertificates.add(wechatPayCertificate); - - WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() - .withMerchant(mchId, mchSerialNo, merchantPrivateKey) - .withWechatPay(listCertificates); - httpClient = builder.build(); + } catch (FileNotFoundException e) { + e.printStackTrace(); } - /** - * wxMchid商户号 - * wxCertno证书编号 - * wxCertPath证书地址 - * wxPaternerKey v3秘钥 - * url 下单地址 - * body 构造好的消息体 - */ - public JSONObject doPostWexinV3(String url, String body) { - if (httpClient == null) { - setup(); - } + ArrayList listCertificates = new ArrayList<>(); + listCertificates.add(wechatPayCertificate); - HttpPost httpPost = new HttpPost(url); - httpPost.addHeader("Content-Type", "application/json;chartset=utf-8"); - httpPost.addHeader("Accept", "application/json"); - try { - if (body == null) { - throw new IllegalArgumentException("data参数不能为空"); - } - StringEntity stringEntity = new StringEntity(body, "utf-8"); - httpPost.setEntity(stringEntity); - // 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新 - HttpResponse httpResponse = httpClient.execute(httpPost); - HttpEntity httpEntity = httpResponse.getEntity(); - - if (httpResponse.getStatusLine().getStatusCode() == 200) { - String jsonResult = EntityUtils.toString(httpEntity); - - return JSONObject.parseObject(jsonResult); - } else { - System.err.println("微信支付错误信息" + EntityUtils.toString(httpEntity)); - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - - } - - //获取签名 - public String getSign(String appId, long timestamp, String nonceStr, String pack){ - String message = buildMessage(appId, timestamp, nonceStr, pack); - String paySign= null; - try { - paySign = sign(message.getBytes("utf-8")); - } catch (Exception e) { - e.printStackTrace(); - } - return paySign; - } - - private String buildMessage(String appId, long timestamp, String nonceStr, String pack) { - return appId + "\n" - + timestamp + "\n" - + nonceStr + "\n" - + pack + "\n"; - } - private String sign(byte[] message) throws Exception{ - PrivateKey merchantPrivateKey = null; - X509Certificate wechatPayCertificate = null; - - try { - merchantPrivateKey = PemUtil.loadPrivateKey( - new FileInputStream(privateKeyUrl)); - wechatPayCertificate = PemUtil.loadCertificate( - new FileInputStream(wechatPayCertificateUrl)); - - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - Signature sign = Signature.getInstance("SHA256withRSA"); - - sign.initSign(merchantPrivateKey); - sign.update(message); - return Base64.getEncoder().encodeToString(sign.sign()); - } + WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() + .withMerchant(wechatPayConfig.getMchId(), wechatPayConfig.getSerialNo(), merchantPrivateKey) + .withWechatPay(listCertificates); + httpClient = builder.build(); } + /** + * wxMchid商户号 + * wxCertno证书编号 + * wxCertPath证书地址 + * wxPaternerKey v3秘钥 + * url 下单地址 + * body 构造好的消息体 + */ + public JSONObject doPostWexinV3(String url, String body) { + if (httpClient == null) { + setup(); + } + + HttpPost httpPost = new HttpPost(url); + httpPost.addHeader("Content-Type", "application/json;chartset=utf-8"); + httpPost.addHeader("Accept", "application/json"); + try { + if (body == null) { + throw new IllegalArgumentException("data参数不能为空"); + } + StringEntity stringEntity = new StringEntity(body, "utf-8"); + httpPost.setEntity(stringEntity); + // 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新 + HttpResponse httpResponse = httpClient.execute(httpPost); + HttpEntity httpEntity = httpResponse.getEntity(); + + if (httpResponse.getStatusLine().getStatusCode() == 200) { + String jsonResult = EntityUtils.toString(httpEntity); + + return JSONObject.parseObject(jsonResult); + } else { + System.err.println("微信支付错误信息" + EntityUtils.toString(httpEntity)); + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + + } + + //获取签名 + public String getSign(String appId, long timestamp, String nonceStr, String pack) { + String message = buildMessage(appId, timestamp, nonceStr, pack); + String paySign = null; + try { + paySign = sign(message.getBytes("utf-8")); + } catch (Exception e) { + e.printStackTrace(); + } + return paySign; + } + + private String buildMessage(String appId, long timestamp, String nonceStr, String pack) { + return appId + "\n" + + timestamp + "\n" + + nonceStr + "\n" + + pack + "\n"; + } + + private String sign(byte[] message) throws Exception { + PrivateKey merchantPrivateKey = null; + X509Certificate wechatPayCertificate = null; + + try { + merchantPrivateKey = PemUtil.loadPrivateKey( + new FileInputStream(wechatPayConfig.getPrivateKeyUrl())); + wechatPayCertificate = PemUtil.loadCertificate( + new FileInputStream(wechatPayConfig.getWechatPayCertificateUrl())); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + Signature sign = Signature.getInstance("SHA256withRSA"); + + sign.initSign(merchantPrivateKey); + sign.update(message); + return Base64.getEncoder().encodeToString(sign.sign()); + } +} + diff --git a/src/main/resources/mapper/book/ShopProudictBookDao.xml b/src/main/resources/mapper/book/ShopProudictBookDao.xml index 4b6dfc80..63237def 100644 --- a/src/main/resources/mapper/book/ShopProudictBookDao.xml +++ b/src/main/resources/mapper/book/ShopProudictBookDao.xml @@ -18,7 +18,7 @@ FROM shop_proudict_book spb LEFT JOIN buy_order_detail bod ON spb.proudict_id = bod.product_id LEFT JOIN buy_order bo ON bo.order_id = bod.order_id - WHERE bo.order_sn = #{orderSn} AND del_flag != -1 + WHERE bo.order_sn = #{orderSn} AND spb.del_flag != -1 diff --git a/src/main/resources/weChatConfig.properties b/src/main/resources/weChatConfig.properties index 806c29c0..70ec1e92 100644 --- a/src/main/resources/weChatConfig.properties +++ b/src/main/resources/weChatConfig.properties @@ -1,48 +1,23 @@ -wxpay: +# APP ID +wxpay.appId:wx47134a8f15083734 +# ?? ID +wxpay.mchId:1612860909 +# ?? URL +wxpay.payUrl:https://api.mch.weixin.qq.com/v3/pay/transactions/app # ???? -wxpay.appId: wx47134a8f15083734 +wxpay.notifyUrl:https://testapi.nuttyreading.com/pay/payNotify +# ?? url +wxpay.refundNotifyUrl:http://pjm6m9.natappfree.cc/pay/refundNotify +# key pem +wxpay.keyPemPath:/usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem +#wxpay.keyPemPath:C:/Users/Cauchy/IdeaProjects/nuttyreading-java/src/main/resources/cent/apiclient_key.pem # ??? -wxpay.mchId: 1612860909 -# API v3 ?? -wxpay.apiV3Key: 4aYFklzaULeGlr7oJPZ6rHWKcxjihZUF -# v3 - url -wxpay.baseUrl: https://api.mch.weixin.qq.com/v3 -#po9k1ezoyexk.ngrok.xiaomiqiu123.top # ???????? -#wxpay.notifyUrl: http://101.201.146.165:9100 - -wxpay.notifyUrl: http://59.110.212.44:9200/pb/pay/payNotify - -# ?????? -wxpay.refundNotifyUrl: http://pjm6m9.natappfree.cc/pay/refundNotify -# ???? /usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem - -wxpay.keyPemPath:C:/Users/Cauchy/IdeaProjects/nuttyreading-java/src/main/resources/cent/apiclient_key.pem -#wxpay.keyPemPath:/usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem -#??????? -wxpay.serialNo: 679AECB2F7AC4183033F713828892BA640E4EEE3 -#??????? -wxpay.domain: https://api.mch.weixin.qq.com -#wxpay: -##???? -#wxpay.appId: wx47134a8f15083734 -##??? -#wxpay.mchId: 1612860909 -## APIv3?? -#wxpay.apiV3Key: 4aYFklzaULeGlr7oJPZ6rHWKcxjihZUF -## ????V3-url?? -#wxpay.baseUrl: https://api.mch.weixin.qq.com/v3 -##po9k1ezoyexk.ngrok.xiaomiqiu123.top ?????? ??ok ???????????? -# -##wxpay.notifyUrl: http://101.201.146.165:9100 -# -#wxpay.notifyUrl: http://59.110.212.44:9100/pb/pay/payNotify -# -## ??????, pjm6m9.natappfree.cc ??????? -#wxpay.refundNotifyUrl: http://pjm6m9.natappfree.cc/pay/refundNotify -## ????,resources? /usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem -# -#wxpay.keyPemPath:/usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem -##??????? -#wxpay.serialNo: 679AECB2F7AC4183033F713828892BA640E4EEE3 -##??????? -#wxpay.domain: https://api.mch.weixin.qq.com \ No newline at end of file +wxpay.serialNo:679AECB2F7AC4183033F713828892BA640E4EEE3 +# API v3 key +wxpay.apiV3Key:4aYFklzaULeGlr7oJPZ6rHWKcxjihZUF +# ???? +wxpay.wechatPayCertificateUrl:/usr/local/hs/peanut_book/target/classes/cent/wechatpay_7B5676E3CDF56680D0414A009CE501C844DBE2D6.pem +# wxpay.wechatPayCertificateUrl:C:/Users/Cauchy/IdeaProjects/nuttyreading-java/src/main/resources/cent/wechatpay_7B5676E3CDF56680D0414A009CE501C844DBE2D6.pem +# ?? url +wxpay.privateKeyUrl:/usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem +#wxpay.privateKeyUrl:C:/Users/Cauchy/IdeaProjects/nuttyreading-java/src/main/resources/cent/apiclient_key.pem \ No newline at end of file