diff --git a/pom.xml b/pom.xml index 7dfdf99..4dc1cdb 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ wxapp-server 1.8 + 2.6 @@ -25,6 +26,11 @@ org.springframework.boot spring-boot-starter-test + + org.springframework.boot + spring-boot-configuration-processor + true + com.auth0 java-jwt @@ -56,13 +62,17 @@ fastjson 1.2.83 - + - com.github.wxpay - wxpay-sdk - 0.0.3 + com.github.wechatpay-apiv3 + wechatpay-apache-httpclient + 0.4.8 + + + commons-lang + commons-lang + ${commons.lang} - diff --git a/src/main/java/com/bigdata/wxappserver/config/WxPayConfig.java b/src/main/java/com/bigdata/wxappserver/config/WxPayConfig.java deleted file mode 100644 index c405ae8..0000000 --- a/src/main/java/com/bigdata/wxappserver/config/WxPayConfig.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.bigdata.wxappserver.config; - -import com.github.wxpay.sdk.WXPayConfig; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -/** - * Created with IntelliJ IDEA. - * - * @Author: Cool - * @Date: 2024/08/31/20:53 - * @Description: - */ -public class WxPayConfig implements WXPayConfig { - - private byte[] certData; - - public void MyConfig() throws Exception { - //此处暂时用不到,这里是读取证书的地方 - } - - @Override - public String getAppID() { - return "wx865aefa5a7115ae0"; - } - - @Override - public String getMchID() { - //申请普通商户时分配给你的商户号 - return "这里是你的商户号"; - } - - @Override - public String getKey() { - //这里的key 就是你在支付平台设置的API密钥 - return "这是就是你的Key了"; - } - - @Override - public InputStream getCertStream() { - ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData); - return certBis; - } - @Override - public int getHttpConnectTimeoutMs() { - return 8000; - } - @Override - public int getHttpReadTimeoutMs() { - return 10000; - } -} - diff --git a/src/main/java/com/bigdata/wxappserver/controller/OrderController.java b/src/main/java/com/bigdata/wxappserver/controller/OrderController.java index 15eaa02..d9b596c 100644 --- a/src/main/java/com/bigdata/wxappserver/controller/OrderController.java +++ b/src/main/java/com/bigdata/wxappserver/controller/OrderController.java @@ -2,12 +2,10 @@ package com.bigdata.wxappserver.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.bigdata.wxappserver.result.Result; import com.bigdata.wxappserver.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; /** * Created with IntelliJ IDEA. @@ -43,4 +41,15 @@ public class OrderController { return orderService.delete(id); } + /** + * 订单支付 + * + * @param + * @return + */ + @PutMapping("/payment") + public Result payment(@RequestBody JSONObject jsonObject) throws Exception { + return orderService.payment(jsonObject); + } + } diff --git a/src/main/java/com/bigdata/wxappserver/controller/PayController.java b/src/main/java/com/bigdata/wxappserver/controller/PayController.java deleted file mode 100644 index 8d4132b..0000000 --- a/src/main/java/com/bigdata/wxappserver/controller/PayController.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.bigdata.wxappserver.controller; - -import com.bigdata.wxappserver.config.WxPayConfig; -import com.github.wxpay.sdk.WXPay; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; - -@RestController -@RequestMapping("pay") -public class PayController { - - @RequestMapping("payment") - public Object getPayment(HttpServletRequest request, @RequestParam("totalFee") String totalFee, String tradeno) throws Exception { -// 获取到当前登录用户,因为这里我保存了openid , 方法大家可以自己处理,这里就不展示了 -// User user = User.getCurrentUserInfo().getUser(); - //当前就是我们自己配置的支付配置。appid 商户号 key 什么的; - WxPayConfig config = new WxPayConfig(); - //当前类是官方为我们封装的一些使用的方法 - WXPay wxpay = new WXPay(config); - //获取到 IP - String clientIp = getIpAddress(request); - System.err.println(clientIp); - //封装请求参数 参数说明看API文档,当前就不进行讲解了 - Map data = new HashMap<>(); - data.put("body", "腾讯充值中心-QQ会员充值"); - data.put("out_trade_no", "2016090910595900000012"); - data.put("device_info", "12345679"); //此处设备或商品编号 - data.put("fee_type", "CNY"); // 货币类型 人民币 - - // 支付中没有小数点,起步以分做为单们,当前为1 分钱,所以自行调整金额 ,这里可以做为传参, - //选取商品金额传到后端来 - data.put("total_fee", "1"); - - data.put("spbill_create_ip", "123.12.12.123"); - data.put("notify_url", "http://www.example.com/wxpay/notify"); - data.put("trade_type", "JSAPI"); // 此处指定JSAPI - data.put("product_id", "12"); - data.put("openid", "这是是登录获取到的openId 必传"); - //调用统一下单方法 - Map order = wxpay.unifiedOrder(data); - //获取到需要的参数返回小程序 - return order; - - - } - - // 获取 IP - public static String getIpAddress(HttpServletRequest request) { - String ip = request.getHeader("x-forwarded-for"); - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("WL-Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("HTTP_CLIENT_IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("HTTP_X_FORWARDED_FOR"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getRemoteAddr(); - } - return ip; - } - -} diff --git a/src/main/java/com/bigdata/wxappserver/controller/PayNotifyController.java b/src/main/java/com/bigdata/wxappserver/controller/PayNotifyController.java new file mode 100644 index 0000000..c26fa3c --- /dev/null +++ b/src/main/java/com/bigdata/wxappserver/controller/PayNotifyController.java @@ -0,0 +1,118 @@ +package com.bigdata.wxappserver.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.bigdata.wxappserver.properties.WeChatProperties; +import com.bigdata.wxappserver.service.OrderService; +import com.wechat.pay.contrib.apache.httpclient.util.AesUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.entity.ContentType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; + +/** + * 支付回调相关接口 + */ +@RestController +@RequestMapping("/notify") +@Slf4j +public class PayNotifyController { + @Autowired + private OrderService orderService; + @Autowired + private WeChatProperties weChatProperties; + + /** + * 支付成功回调 + * + * @param request + */ + @RequestMapping("/paySuccess") + public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception { + //读取数据 + String body = readData(request); + log.info("支付成功回调:{}", body); + + //数据解密 + String plainText = decryptData(body); + log.info("解密后的文本:{}", plainText); + + JSONObject jsonObject = JSON.parseObject(plainText); + String outTradeNo = jsonObject.getString("out_trade_no");//商户平台订单号 + String transactionId = jsonObject.getString("transaction_id");//微信支付交易号 + + log.info("商户平台订单号:{}", outTradeNo); + log.info("微信支付交易号:{}", transactionId); + + //业务处理,修改订单状态、来单提醒 + orderService.paySuccess(outTradeNo); + + //给微信响应 + responseToWeixin(response); + } + + /** + * 读取数据 + * + * @param request + * @return + * @throws Exception + */ + private String readData(HttpServletRequest request) throws Exception { + BufferedReader reader = request.getReader(); + StringBuilder result = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + if (result.length() > 0) { + result.append("\n"); + } + result.append(line); + } + return result.toString(); + } + + /** + * 数据解密 + * + * @param body + * @return + * @throws Exception + */ + private String decryptData(String body) throws Exception { + JSONObject resultObject = JSON.parseObject(body); + JSONObject resource = resultObject.getJSONObject("resource"); + String ciphertext = resource.getString("ciphertext"); + String nonce = resource.getString("nonce"); + String associatedData = resource.getString("associated_data"); + + AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8)); + //密文解密 + String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), + nonce.getBytes(StandardCharsets.UTF_8), + ciphertext); + + return plainText; + } + + /** + * 给微信响应 + * + * @param response + */ + private void responseToWeixin(HttpServletResponse response) throws Exception { + response.setStatus(200); + HashMap map = new HashMap<>(); + map.put("code", "SUCCESS"); + map.put("message", "SUCCESS"); + response.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); + response.getOutputStream().write(JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8)); + response.flushBuffer(); + } +} diff --git a/src/main/java/com/bigdata/wxappserver/properties/WeChatProperties.java b/src/main/java/com/bigdata/wxappserver/properties/WeChatProperties.java new file mode 100644 index 0000000..482ee59 --- /dev/null +++ b/src/main/java/com/bigdata/wxappserver/properties/WeChatProperties.java @@ -0,0 +1,22 @@ +package com.bigdata.wxappserver.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "dx.wechat") +@Data +public class WeChatProperties { + + private String appid; //小程序的appid + private String secret; //小程序的秘钥 + private String mchid; //商户号 + private String mchSerialNo; //商户API证书的证书序列号 + private String privateKeyFilePath; //商户私钥文件 + private String apiV3Key; //证书解密的密钥 + private String weChatPayCertFilePath; //平台证书 + private String notifyUrl; //支付成功的回调地址 + private String refundNotifyUrl; //退款成功的回调地址 + +} diff --git a/src/main/java/com/bigdata/wxappserver/result/PageResult.java b/src/main/java/com/bigdata/wxappserver/result/PageResult.java new file mode 100644 index 0000000..3871ada --- /dev/null +++ b/src/main/java/com/bigdata/wxappserver/result/PageResult.java @@ -0,0 +1,22 @@ +package com.bigdata.wxappserver.result; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 封装分页查询结果 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PageResult implements Serializable { + + private long total; //总记录数 + + private List records; //当前页数据集合 + +} diff --git a/src/main/java/com/bigdata/wxappserver/result/Result.java b/src/main/java/com/bigdata/wxappserver/result/Result.java new file mode 100644 index 0000000..4394dff --- /dev/null +++ b/src/main/java/com/bigdata/wxappserver/result/Result.java @@ -0,0 +1,38 @@ +package com.bigdata.wxappserver.result; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 后端统一返回结果 + * @param + */ +@Data +public class Result implements Serializable { + + private Integer code; //编码:1成功,0和其它数字为失败 + private String msg; //错误信息 + private T data; //数据 + + public static Result success() { + Result result = new Result(); + result.code = 1; + return result; + } + + public static Result success(T object) { + Result result = new Result(); + result.data = object; + result.code = 1; + return result; + } + + public static Result error(String msg) { + Result result = new Result(); + result.msg = msg; + result.code = 0; + return result; + } + +} diff --git a/src/main/java/com/bigdata/wxappserver/service/OrderService.java b/src/main/java/com/bigdata/wxappserver/service/OrderService.java index 3e8e765..12a1534 100644 --- a/src/main/java/com/bigdata/wxappserver/service/OrderService.java +++ b/src/main/java/com/bigdata/wxappserver/service/OrderService.java @@ -9,6 +9,8 @@ import com.bigdata.wxappserver.entity.Order; import com.bigdata.wxappserver.entity.User; import com.bigdata.wxappserver.enums.OrderStatusEnum; import com.bigdata.wxappserver.mapper.OrderMapper; +import com.bigdata.wxappserver.result.Result; +import com.bigdata.wxappserver.utils.WeChatPayUtil; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -16,6 +18,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; @@ -28,12 +32,14 @@ import java.util.stream.Collectors; */ @Service public class OrderService extends ServiceImpl { - + @Autowired + private WeChatPayUtil weChatPayUtil; @Autowired UserService userService; @Autowired GoodsService goodsService; + @Transactional(rollbackFor = Exception.class) public JSONObject addOrUpdate(JSONObject jsonObject) { Order order = jsonObject.toJavaObject(Order.class); @@ -50,14 +56,16 @@ public class OrderService extends ServiceImpl { } public JSONObject list(JSONObject jsonObject) { - Integer userId = jsonObject.getInteger("userId"); + String openId = jsonObject.getString("openId"); + List userList = userService.list(); + User user = userList.stream().filter(e -> Objects.equals(e.getOpenId(), openId)).findFirst().orElse(null); Integer status = jsonObject.getInteger("status"); // Integer currentPage = jsonObject.getInteger("currentPage"); // Integer pageSize = jsonObject.getInteger("pageSize"); // IPage page = new Page<>(currentPage, pageSize); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(userId != null, Order::getUserId, userId) - .eq(status!=null,Order::getStatus,status); + wrapper.eq(user != null, Order::getUserId, user.getId()) + .eq(status != null, Order::getStatus, status); // IPage orderIPage = page(page, wrapper); List list = list(wrapper); List orderDtoList = dealList(list); @@ -71,15 +79,15 @@ public class OrderService extends ServiceImpl { return new ArrayList<>(); } List goodsList = goodsService.listByIds(records.stream().map(Order::getGoodsId).collect(Collectors.toSet())); - Set goodsSet =new HashSet<>(goodsList); - List list =new ArrayList<>(); + Set goodsSet = new HashSet<>(goodsList); + List list = new ArrayList<>(); for (Order record : records) { - OrderDto orderDto=new OrderDto(); + OrderDto orderDto = new OrderDto(); record.setStatusDescription(OrderStatusEnum.describe(record.getStatus())); - BeanUtils.copyProperties(record,orderDto); + BeanUtils.copyProperties(record, orderDto); Goods goods = goodsSet.stream().filter(item -> Objects.equals(item.getId(), record.getGoodsId())) .findFirst().orElse(null); - if(goods!=null){ + if (goods != null) { orderDto.setGoodDetail(goods.getGoodDetail()); orderDto.setGoodPrice(goods.getGoodPrice()); orderDto.setGoodImage(goods.getGoodImage()); @@ -114,4 +122,48 @@ public class OrderService extends ServiceImpl { } } + public void paySuccess(String outTradeNo) { + List list = list(); + Order order = list.stream().filter(e -> Objects.equals(String.valueOf(e.getId()), outTradeNo)).findFirst().orElse(null); + if (order != null) { + order.setStatus(2); + } + saveOrUpdate(order); + } + + /** + * 订单支付 + * + * @param ordersPaymentDTO + * @return + */ + public Result payment(JSONObject ordersPaymentDTO) throws Exception { + // 当前登录用户id + String openId = ordersPaymentDTO.getString("openId"); + String orderCode = ordersPaymentDTO.getString("orderCode"); + List userList = userService.list(); + User user = userList.stream().filter(e -> Objects.equals(e.getOpenId(), openId)).findFirst().orElse(null); + if (user == null) { + return Result.error("查询不到该用户"); + } + //调用微信支付接口,生成预支付交易单 + JSONObject jsonObject = weChatPayUtil.pay( + orderCode, //商户订单号 + new BigDecimal(0.01), //支付金额,单位 元 + "Test123", //商品描述 + user.getOpenId() //微信用户的openid + ); + + if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) { + return Result.error("该订单已支付"); + } + +// OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class); +// vo.setPackageStr(jsonObject.getString("package")); +// +// return vo; + return Result.success(); + } + + } diff --git a/src/main/java/com/bigdata/wxappserver/utils/WeChatPayUtil.java b/src/main/java/com/bigdata/wxappserver/utils/WeChatPayUtil.java new file mode 100644 index 0000000..9e90ce5 --- /dev/null +++ b/src/main/java/com/bigdata/wxappserver/utils/WeChatPayUtil.java @@ -0,0 +1,235 @@ +package com.bigdata.wxappserver.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.bigdata.wxappserver.properties.WeChatProperties; +import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; +import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.http.HttpHeaders; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +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.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.math.BigDecimal; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; + +/** + * 微信支付工具类 + */ +@Component +public class WeChatPayUtil { + + //微信支付下单接口地址 + public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; + + //申请退款接口地址 + public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; + + @Autowired + private WeChatProperties weChatProperties; + + /** + * 获取调用微信接口的客户端工具对象 + * + * @return + */ + private CloseableHttpClient getClient() { + PrivateKey merchantPrivateKey = null; + try { + //merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题 + merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath()))); + //加载平台证书文件 + X509Certificate x509Certificate = PemUtil.loadCertificate(new FileInputStream(new File(weChatProperties.getWeChatPayCertFilePath()))); + //wechatPayCertificates微信支付平台证书列表。你也可以使用后面章节提到的“定时更新平台证书功能”,而不需要关心平台证书的来龙去脉 + List wechatPayCertificates = Arrays.asList(x509Certificate); + + WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() + .withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey) + .withWechatPay(wechatPayCertificates); + + // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签 + CloseableHttpClient httpClient = builder.build(); + return httpClient; + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 发送post方式请求 + * + * @param url + * @param body + * @return + */ + private String post(String url, String body) throws Exception { + CloseableHttpClient httpClient = getClient(); + + HttpPost httpPost = new HttpPost(url); + httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString()); + httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); + httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo()); + httpPost.setEntity(new StringEntity(body, "UTF-8")); + + CloseableHttpResponse response = httpClient.execute(httpPost); + try { + String bodyAsString = EntityUtils.toString(response.getEntity()); + return bodyAsString; + } finally { + httpClient.close(); + response.close(); + } + } + + /** + * 发送get方式请求 + * + * @param url + * @return + */ + private String get(String url) throws Exception { + CloseableHttpClient httpClient = getClient(); + + HttpGet httpGet = new HttpGet(url); + httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString()); + httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); + httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo()); + + CloseableHttpResponse response = httpClient.execute(httpGet); + try { + String bodyAsString = EntityUtils.toString(response.getEntity()); + return bodyAsString; + } finally { + httpClient.close(); + response.close(); + } + } + + /** + * jsapi下单 + * + * @param orderNum 商户订单号 + * @param total 总金额 + * @param description 商品描述 + * @param openid 微信用户的openid + * @return + */ + private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("appid", weChatProperties.getAppid()); + jsonObject.put("mchid", weChatProperties.getMchid()); + jsonObject.put("description", description); + jsonObject.put("out_trade_no", orderNum); + jsonObject.put("notify_url", weChatProperties.getNotifyUrl()); + + JSONObject amount = new JSONObject(); + amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue()); + amount.put("currency", "CNY"); + + jsonObject.put("amount", amount); + + JSONObject payer = new JSONObject(); + payer.put("openid", openid); + + jsonObject.put("payer", payer); + + String body = jsonObject.toJSONString(); + return post(JSAPI, body); + } + + /** + * 小程序支付 + * + * @param orderNum 商户订单号 + * @param total 金额,单位 元 + * @param description 商品描述 + * @param openid 微信用户的openid + * @return + */ + public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception { + //统一下单,生成预支付交易单 + String bodyAsString = jsapi(orderNum, total, description, openid); + //解析返回结果 + JSONObject jsonObject = JSON.parseObject(bodyAsString); + System.out.println(jsonObject); + + String prepayId = jsonObject.getString("prepay_id"); + if (prepayId != null) { + String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = RandomStringUtils.randomNumeric(32); + ArrayList list = new ArrayList<>(); + list.add(weChatProperties.getAppid()); + list.add(timeStamp); + list.add(nonceStr); + list.add("prepay_id=" + prepayId); + //二次签名,调起支付需要重新签名 + StringBuilder stringBuilder = new StringBuilder(); + for (Object o : list) { + stringBuilder.append(o).append("\n"); + } + String signMessage = stringBuilder.toString(); + byte[] message = signMessage.getBytes(); + + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())))); + signature.update(message); + String packageSign = Base64.getEncoder().encodeToString(signature.sign()); + + //构造数据给微信小程序,用于调起微信支付 + JSONObject jo = new JSONObject(); + jo.put("timeStamp", timeStamp); + jo.put("nonceStr", nonceStr); + jo.put("package", "prepay_id=" + prepayId); + jo.put("signType", "RSA"); + jo.put("paySign", packageSign); + + return jo; + } + return jsonObject; + } + + /** + * 申请退款 + * + * @param outTradeNo 商户订单号 + * @param outRefundNo 商户退款单号 + * @param refund 退款金额 + * @param total 原订单金额 + * @return + */ + public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) throws Exception { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("out_trade_no", outTradeNo); + jsonObject.put("out_refund_no", outRefundNo); + + JSONObject amount = new JSONObject(); + amount.put("refund", refund.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue()); + amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue()); + amount.put("currency", "CNY"); + + jsonObject.put("amount", amount); + jsonObject.put("notify_url", weChatProperties.getRefundNotifyUrl()); + + String body = jsonObject.toJSONString(); + + //调用申请退款接口 + return post(REFUNDS, body); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6377e90..594b27d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,3 +14,15 @@ mybatis-plus: mapper-locations: classpath*:/mapper/*Mapper.xml configuration: map-underscore-to-camel-case: true +dx: + wechat: + appid: wx865aefa5a7115ae0 + secret: 3f9849429894435abc935eea88178dfd + mchid: 1684540409 + mchSerialNo: 250414966CEADA52CF0A989445FB3190C5A82F40 + privateKeyFilePath: template/bddf2dc508484b6bb086fe748e813260.pem + apiV3Key: d5a58d44588b42cbbe01daa5cfa4e792 + #未填写及以下 + weChatPayCertFilePath: template/wechatpay_429733475DFDCEBE2A6135485F984EE337297F4F.pem + notifyUrl: https://www.weixin.qq.com/wxpay/pay.php + refundNotifyUrl: https://www.weixin.qq.com/wxpay/pay.php diff --git a/src/main/resources/template/bddf2dc508484b6bb086fe748e813260.pem b/src/main/resources/template/bddf2dc508484b6bb086fe748e813260.pem new file mode 100644 index 0000000..b96a751 --- /dev/null +++ b/src/main/resources/template/bddf2dc508484b6bb086fe748e813260.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDE4z0uWRbfuMPP +m19ZOd3CoKmm20Rdp0EJGsYsItxWk1veEUrtUDGUevrBpAWxSW6ka6YRcs3G6nzK +sAkrOzoFOU1xTH4I0qrspIelXwLZMYKZrdR+G+LNS9gmITrvkBKlZsrWTNOxStv1 +0CTeWPr3mBUt3SrUOHPX+eY5JR1XgBau8Y/9hd5fiYuPTCoWQsM+IUIXBrWsaEqI +oDrymtFH8Nupgz55g8qgUX0jxb1yjcuenO7jlonqMySIpZuNrAxla4GnfERG7TAL +36VYJ/f4QWiHpa8lg4azq/FgwegL7nVo2wcDoCcqkIjDvyYxTXtwwn7llFSa44B9 +abXQ4Ip3AgMBAAECggEBAMCtwcoB1ajLoJUjcIRZZPR7Vou8OYONks/eK+I1m3v+ +agFA2xqzSFIOqCeo5QlC28x8rOCWgVsRT17sN4zfQUCre2ZNfWoVAMDlovnrqVX0 +ZeMPgsyHbcWLW9S04IBhdl334rkmJmVy5SACupH+clyrsM5Zivh05qmOHgf+kmB1 +dpG8oJ+4gaAg5f8YQ0S3q0jJy/Tr2tZRaTU5S1Gaa3xmfDHBvte6bmmSrjF7V56M +hibEOqvjrgK04P20FtFH7Laug4tq5IMyi6L9XVN2tE06c0epN7+Z7oBEzeTuQ+zU +EwEnNwH7GG+JYjUIrxlQMr6XXNEWv/iDhbXKJd5zvoECgYEA/9AFOlQthPdR0dY3 +9N9q41jmrxvF0yep+MFw4+X7sEV+gnLLRATQCnsBF+Br3DHdUU2H8DwtOEYD8V9L +Ks8j7B9ivVcbs4KT6iu3ehrQgX2DvnGov+ZlRpVwWV90CppLzubuX++0L8K/dYfF +OPpm128k18BgwWY+4UvD1BClrU8CgYEAxQgqsEysa9RUcKFcPMMkMUG3SDvB0+q3 +vkn4EDZVgUoojQnk54vQ4U6pbF4KTPPHvNF4vrmu5DdhSWBBnHAchdLHO7NXVl7n +cIPOHnakzauiS+jZlokpf4bMIECsQMW+n6c2WfT4mhbciCTo4+oLBu/aZ/tS/fUu +EJyW2+/VllkCgYByexSr2toYJFpgbt7R3l5GahWqjeJFMf32DhPIWbb+Vstb86WV +xhWYax29IrkCbOpfpTPNxhBym5rOHVhVSygHVrBojaAfALPTW0ccBP4ExTf9NX5Q +ivoN0Xja9kXHLO/6IwzQsERSD3SBU4ZmjmERznKNf1GNm4VObAqyT9TlLwKBgQCT +R1OBjQ6lW4Xy2urzkHqRRyoVmHV+TskiOHBwzeyEREajhm3QlraAdCg4lOLmOqNn +BL1Y87tDZBIYoxboNPVdIv1CJwB102L0u9Fq/AycoFskmt1qpQScCsqyoSUhFa8z +7+20uGTL6aLXMZ/UHbI4cTm02CxlIMxaJnKt3EyvoQKBgQCeZwTjaqIQmR3fBrNm +MG0qO652ONVLirzEsfn7DpxPC6+ON3MGuPZxaiSaYwAhQq3dqQ/dpJoxrInPMScH +bebJz33OLTH4+1t4NvNDJkauWwuDI46uZhX+MVfIelNC5j5V+qrd1n614nQe9pJJ +UMlUt8FS3+aqnzvtYMOs6MNO9g== +-----END PRIVATE KEY----- diff --git a/src/main/resources/template/wechatpay_429733475DFDCEBE2A6135485F984EE337297F4F.pem b/src/main/resources/template/wechatpay_429733475DFDCEBE2A6135485F984EE337297F4F.pem new file mode 100644 index 0000000..66fa223 --- /dev/null +++ b/src/main/resources/template/wechatpay_429733475DFDCEBE2A6135485F984EE337297F4F.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEFDCCAvygAwIBAgIUQpczR139zr4qYTVIX5hO4zcpf08wDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjQwOTAyMDkxNjQ1WhcNMjkwOTAxMDkxNjQ1WjBuMRgwFgYDVQQDDA9U +ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl +bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGEwJDTjERMA8GA1UEBwwIU2hlblpo +ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjlZH/RfODPFpUJoQ7 +VtfxW+65CSEPwOAlLQvdCELE5Irw8e0kCpmVUw8jKJKr/7yMfOgt4jxHkgZYcXp6 +YejRNAXOQi5CKnW1FuJi+gU9CNE+wneG1Yq/9P2LGRpO+uWzYOUKe5G6qja7/y5A +37mgRy1b6EpRjN3cPQxOCwqlU6+ynojM+1ZKBbVmXETwJL3Cet03fb1IR50N/lLt +vRHGEpjLUR991aPBgHCQF+2BmlSlrZyQcfqzvVbRzCLnZtFWVa+yESYF1o3mf7+W +MSKPwrGgyfWw2NpY5Sz3smLUOgqJOooqoYbCf4iW+QceBAdXTixY14MBInS7jHPr +T8MLAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB +kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv +aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4 +RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0 +MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAdhtjpoyuFq7mMOAqWFmdgA+Z9ko8Yu31 +iehWhcFaKbhGZ0rvbXUquzQYS8/DYTobCyM1I9+2skKks+bbOC56BrZtduzSqzII +IRlGr714v8WoudsIwuT7U6t4rws6mekrPrh9tTtDr6xpEuuLaYe0gJrpFM0UbryO +H20I1g2mxzmXl3MuGq8zB+fGQUy+MIO4EoUGIIEM6q7HzcCAPIVn+Qb/MCto56Ne +YPr5a+w7g6u4UZ+sVix2AddlOnqJPBIv/t91sQtFSzew9yztW/r2b1AyUHAsYa0n +76QTS6cjmV/Ee726l9d6tYGhF/NR9lUcXK7hr4JzA9e0zKr0JnAy8Q== +-----END CERTIFICATE----- \ No newline at end of file