|
@@ -1,5 +1,6 @@
|
|
|
package org.jeecg.modules.app.service;
|
|
|
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
@@ -7,6 +8,9 @@ import com.alibaba.fastjson2.JSONArray;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
import com.aliyun.oss.ServiceException;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.wechat.pay.java.core.notification.Notification;
|
|
|
+import com.wechat.pay.java.core.notification.RequestParam;
|
|
|
+import com.yomahub.liteflow.exception.IfTypeErrorException;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.RandomStringUtils;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
@@ -26,14 +30,12 @@ import javax.crypto.NoSuchPaddingException;
|
|
|
import javax.crypto.spec.GCMParameterSpec;
|
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.io.BufferedReader;
|
|
|
import java.io.IOException;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.math.RoundingMode;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
-import java.security.GeneralSecurityException;
|
|
|
-import java.security.InvalidAlgorithmParameterException;
|
|
|
-import java.security.InvalidKeyException;
|
|
|
-import java.security.NoSuchAlgorithmException;
|
|
|
+import java.security.*;
|
|
|
import java.text.DateFormat;
|
|
|
import java.text.ParseException;
|
|
|
import java.text.SimpleDateFormat;
|
|
@@ -214,7 +216,7 @@ public class WeChatPayService {
|
|
|
//查询订单,判断是否已修改为已支付状态
|
|
|
AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
|
|
|
if (ObjectUtil.isNotEmpty(appOrder)) {
|
|
|
- if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0)){
|
|
|
+ if (Objects.equals(appOrder.getOrderStatus(), CommonConstant.ORDER_STATUS_0)) {
|
|
|
appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_1);
|
|
|
appOrder.setPayStatus(CommonConstant.ORDER_STATUS_1);
|
|
|
appOrder.setPayTime(new Date());
|
|
@@ -223,7 +225,7 @@ public class WeChatPayService {
|
|
|
appOrder.setPayTime(dealDateFormat(res.getString("success_time")));
|
|
|
|
|
|
List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()));
|
|
|
- if (ObjectUtil.isNotEmpty(proInfoList)){
|
|
|
+ if (ObjectUtil.isNotEmpty(proInfoList)) {
|
|
|
for (AppOrderProInfo appOrderProInfo : proInfoList) {
|
|
|
appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_1);
|
|
|
appOrderProInfoMapper.updateById(appOrderProInfo);
|
|
@@ -239,7 +241,7 @@ public class WeChatPayService {
|
|
|
}
|
|
|
result.put("code", "SUCCESS");
|
|
|
result.put("message", "OK");
|
|
|
- result.put("orderCode",orderCode);
|
|
|
+ result.put("orderCode", orderCode);
|
|
|
return result;
|
|
|
} catch (Exception e) {
|
|
|
result.put("code", "FAIL");
|
|
@@ -249,6 +251,12 @@ public class WeChatPayService {
|
|
|
}
|
|
|
|
|
|
public void addProfitSharingInfos(AppOrder appOrder) {
|
|
|
+ List<AppOrderProInfo> orderProInfoList = appOrderProInfoMapper.selectList(Wrappers.lambdaQuery(AppOrderProInfo.class).eq(AppOrderProInfo::getOrderId, appOrder.getId()).eq(AppOrderProInfo::getType, CommonConstant.ORDER_PRO_INFO_TYPE_6));
|
|
|
+ BigDecimal insurePrice = BigDecimal.ZERO;
|
|
|
+ if (CollUtil.isNotEmpty(orderProInfoList)) {
|
|
|
+ BigDecimal reduce = orderProInfoList.stream().map(AppOrderProInfo::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+ insurePrice = insurePrice.add(reduce);
|
|
|
+ }
|
|
|
//创建预分账详情
|
|
|
String orgCode = appOrder.getOrgCode();
|
|
|
SysDepart depart = sysDepartMapper.selectOne(Wrappers.lambdaQuery(SysDepart.class).eq(SysDepart::getOrgCode, orgCode).last("limit 1"));
|
|
@@ -260,11 +268,13 @@ public class WeChatPayService {
|
|
|
BigDecimal PT = separateAccounts.getPtSeparateAccounts();
|
|
|
BigDecimal SH = separateAccounts.getShSeparateAccounts();
|
|
|
BigDecimal MD = separateAccounts.getMdSeparateAccounts();
|
|
|
+ //分账金额
|
|
|
+ BigDecimal price = appOrder.getPrice().subtract(insurePrice);
|
|
|
//微信手续费,不足1分按1分算
|
|
|
- Integer FEE = RatiosUtil.amount_fee(appOrder.getPrice().multiply(new BigDecimal("0.06")).setScale(0, RoundingMode.UP));
|
|
|
+ Integer FEE = RatiosUtil.amount_fee(price.multiply(new BigDecimal("0.06")).setScale(0, RoundingMode.UP));
|
|
|
//商户(分账给平台)
|
|
|
if (depart.getSystemType() == 1) {
|
|
|
- BigDecimal[] allocate = RatiosUtil.allocate(appOrder.getPrice(), new BigDecimal[]{SH, PT});
|
|
|
+ BigDecimal[] allocate = RatiosUtil.allocate(price, new BigDecimal[]{SH, PT});
|
|
|
//商户所得金额
|
|
|
AppProfitSharingInfo SHAppProfitSharingInfo = new AppProfitSharingInfo();
|
|
|
SHAppProfitSharingInfo.setOrderId(appOrder.getId());
|
|
@@ -283,7 +293,7 @@ public class WeChatPayService {
|
|
|
}
|
|
|
//门店(分账给平台及商户)
|
|
|
if (depart.getSystemType() == 2) {
|
|
|
- BigDecimal[] allocate = RatiosUtil.allocate(appOrder.getPrice(), MD, SH, PT);
|
|
|
+ BigDecimal[] allocate = RatiosUtil.allocate(price, MD, SH, PT);
|
|
|
//门店所得金额
|
|
|
AppProfitSharingInfo MDAppProfitSharingInfo = new AppProfitSharingInfo();
|
|
|
MDAppProfitSharingInfo.setOrderId(appOrder.getId());
|
|
@@ -331,98 +341,109 @@ public class WeChatPayService {
|
|
|
* @param out_trade_no 发起支付时创建的商户订单号
|
|
|
* @return null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
|
|
|
*/
|
|
|
- public JSONObject orderQueryByOutTradeNo(String out_trade_no) {
|
|
|
+ public JSONObject orderQueryByOutTradeNo(String out_trade_no,String subMchId) {
|
|
|
String url = WechatUrlConstants.PAY_V3_QUERY_OUT;
|
|
|
url = url.replace("{out_trade_no}", WXPayUtility.urlEncode(out_trade_no));
|
|
|
Map<String, Object> args = new HashMap<>();
|
|
|
args.put("sp_mchid", WechatConstants.WECHAT_SP_MCH_ID);
|
|
|
- args.put("sub_mchid", WechatConstants.WECHAT_SUB_MCH_ID);
|
|
|
+ args.put("sub_mchid", subMchId);
|
|
|
url = url + "?" + WXPayUtility.urlEncode(args);
|
|
|
JSONObject res = wechatPayV3Utils.sendGet(url);
|
|
|
- return res ;
|
|
|
+ return res;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 申请退款
|
|
|
- * @param orderCode 原订单号
|
|
|
+ *
|
|
|
+ * @param appOrder
|
|
|
+ * @param refundAmount
|
|
|
+ * @param reason
|
|
|
*/
|
|
|
- public void refundOrder(String orderCode,String reason){
|
|
|
+ public void refundOrder(AppOrder appOrder, List<AppOrderProInfo> appOrderProInfoList, BigDecimal refundAmount, String reason) {
|
|
|
|
|
|
log.info("进入退款接口------>");
|
|
|
- log.info("执行操作的 原支付交易对应的商户订单号:{}", orderCode);
|
|
|
-
|
|
|
- BigDecimal totalFee = BigDecimal.ZERO;
|
|
|
- BigDecimal total = BigDecimal.ZERO;
|
|
|
- AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, orderCode).last("limit 1"));
|
|
|
- if (ObjectUtil.isNotEmpty(appOrder)) {
|
|
|
- total = appOrder.getPrice();
|
|
|
- totalFee = total;
|
|
|
- List<AppOrderProInfo> proInfoList = appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()).eq(AppOrderProInfo::getType, CommonConstant.ORDER_PRO_INFO_TYPE_6));
|
|
|
- if (ObjectUtil.isNotEmpty(proInfoList)) {
|
|
|
- for (AppOrderProInfo appOrderProInfo : proInfoList) {
|
|
|
- totalFee = totalFee.subtract(appOrderProInfo.getPrice());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ log.info("执行操作的 原支付交易对应的商户订单号:{}", appOrder.getOrderCode());
|
|
|
+
|
|
|
+ SysDepart depart = sysDepartMapper.selectOne(Wrappers.lambdaQuery(SysDepart.class).eq(SysDepart::getOrgCode, appOrder.getOrgCode()).last("limit 1"));
|
|
|
+
|
|
|
//退款单号
|
|
|
String out_refund_no = generateOrderNumber(1);
|
|
|
-
|
|
|
// 创建退款订单
|
|
|
AppOrderRefundsInfo appOrderRefundsInfo = new AppOrderRefundsInfo();
|
|
|
-
|
|
|
appOrderRefundsInfo.setOrderId(appOrder.getId());
|
|
|
- appOrderRefundsInfo.setOrderCode(orderCode);
|
|
|
+ //项目只有单商品退款
|
|
|
+ appOrderRefundsInfo.setOrderProInfoId(appOrderProInfoList.get(0).getId());
|
|
|
+ appOrderRefundsInfo.setOrderCode(appOrder.getOrderCode());
|
|
|
appOrderRefundsInfo.setOutRefundNo(out_refund_no);
|
|
|
appOrderRefundsInfo.setTransactionId(appOrder.getTransactionId());
|
|
|
appOrderRefundsInfo.setReason(reason);
|
|
|
- appOrderRefundsInfo.setAmount(total);
|
|
|
+ appOrderRefundsInfo.setAmount(refundAmount);
|
|
|
appOrderRefundsInfo.setCreateTime(new Date());
|
|
|
|
|
|
- //异步发起分账回退
|
|
|
- weChatProfitSharingService.returnProfitSharing(orderCode);
|
|
|
+ JSONObject params = new JSONObject();
|
|
|
+ params.put("sub_mchid",depart.getMchId());
|
|
|
+ params.put("out_trade_no", appOrder.getOrderCode());//商户订单号
|
|
|
+ params.put("out_refund_no", out_refund_no);//商户退款单号
|
|
|
+ params.put("reason", reason);//退款原因
|
|
|
+ params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY);//退款通知
|
|
|
+
|
|
|
+ JSONObject amount = new JSONObject();
|
|
|
+ amount.put("refund", RatiosUtil.amount_fee(refundAmount));//退款金额
|
|
|
+ amount.put("currency", "CNY");
|
|
|
+ amount.put("total", RatiosUtil.amount_fee(appOrder.getPrice()));//原订单金额
|
|
|
+ params.put("amount", amount);
|
|
|
+ // 执行请求POST 请求发送到微信退款接口
|
|
|
+ JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, params);
|
|
|
|
|
|
- try {
|
|
|
- JSONObject params = new JSONObject();
|
|
|
- params.put("sub_mchid", WechatConstants.WECHAT_SUB_MCH_ID);
|
|
|
- params.put("out_trade_no", orderCode);//商户订单号
|
|
|
- params.put("out_refund_no", out_refund_no);//商户退款单号
|
|
|
- params.put("reason", reason);//退款原因
|
|
|
- params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY);//退款通知
|
|
|
-
|
|
|
- JSONObject amount = new JSONObject();
|
|
|
- amount.put("refund", (totalFee.multiply(BigDecimal.valueOf(100))).longValue());//退款金额
|
|
|
- amount.put("currency", "CNY");
|
|
|
- amount.put("total", (total.multiply(BigDecimal.valueOf(100))).longValue());//原订单金额
|
|
|
- params.put("amount", amount);
|
|
|
- // 执行请求POST 请求发送到微信退款接口
|
|
|
- JSONObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, params);
|
|
|
-
|
|
|
- log.info("最终拿到的微信支付通知数据:" + res);
|
|
|
-
|
|
|
- final String status = res.getString("status");
|
|
|
- switch (status) {
|
|
|
- case "SUCCESS":
|
|
|
- log.info("订单:{},退款成功!原因:{}",orderCode,reason);
|
|
|
- break;
|
|
|
- case "CLOSED":
|
|
|
- log.info("退款关闭");
|
|
|
- break;
|
|
|
- case "PROCESSING":
|
|
|
- log.info("退款处理中");
|
|
|
- break;
|
|
|
- case "ABNORMAL":
|
|
|
- log.info("订单:{},退款异常",orderCode);
|
|
|
- break;
|
|
|
- }
|
|
|
- appOrderRefundsInfo.setRefundId(res.getString("refund_id"));
|
|
|
- appOrderRefundsInfo.setSuccessTime(DateUtil.parse(res.getString("success_time"), "yyyy-MM-dd HH:mm:ss"));
|
|
|
- appOrderRefundsInfo.setNotifyRequest(res.toString());
|
|
|
- appOrderRefundsInfoMapper.insert(appOrderRefundsInfo);
|
|
|
- } catch (Exception e) {
|
|
|
- // TODO Auto-generated catch block
|
|
|
- log.info(e.toString());
|
|
|
- e.printStackTrace();
|
|
|
+ log.info("最终拿到的微信支付通知数据:" + res);
|
|
|
+
|
|
|
+ final String status = res.getString("status");
|
|
|
+ switch (status) {
|
|
|
+ case "SUCCESS":
|
|
|
+ log.info("订单:{},退款成功!原因:{}", appOrder.getOrderCode(), reason);
|
|
|
+ //修改订单状态
|
|
|
+ for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
|
|
|
+ appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_6);
|
|
|
+ appOrderProInfo.setAfterSaleStatus(CommonConstant.NUMBER_2);
|
|
|
+ appOrderProInfoMapper.updateById(appOrderProInfo);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case "CLOSED":
|
|
|
+ log.info("退款关闭");
|
|
|
+ break;
|
|
|
+ case "PROCESSING":
|
|
|
+ log.info("退款处理中");
|
|
|
+ //修改订单状态
|
|
|
+ for (AppOrderProInfo appOrderProInfo : appOrderProInfoList) {
|
|
|
+ appOrderProInfo.setOrderStatus(CommonConstant.ORDER_STATUS_5);
|
|
|
+ appOrderProInfo.setAfterSaleStatus(CommonConstant.NUMBER_1);
|
|
|
+ appOrderProInfoMapper.updateById(appOrderProInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<AppOrderProInfo> orderProInfoList =
|
|
|
+ appOrderProInfoMapper.selectList(Wrappers.<AppOrderProInfo>lambdaQuery().eq(AppOrderProInfo::getOrderId, appOrder.getId()));
|
|
|
+ boolean isAllRefund = true;
|
|
|
+ for (AppOrderProInfo appOrderProInfo : orderProInfoList) {
|
|
|
+ if (appOrderProInfo.getAfterSaleStatus() != 1) {
|
|
|
+ isAllRefund = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isAllRefund) {
|
|
|
+ log.info("修改订单:{},支付状态为退款中", appOrder.getOrderCode());
|
|
|
+ appOrder.setOrderStatus(CommonConstant.ORDER_STATUS_5);
|
|
|
+ appOrderMapper.updateById(appOrder);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case "ABNORMAL":
|
|
|
+ log.info("订单:{},退款异常", appOrder.getOrderCode());
|
|
|
+ break;
|
|
|
}
|
|
|
+ appOrderRefundsInfo.setRefundId(res.getString("refund_id"));
|
|
|
+ appOrderRefundsInfo.setNotifyRequest(res.toString());
|
|
|
+ appOrderRefundsInfo.setTransactionId(res.getString("transaction_id"));
|
|
|
+ appOrderRefundsInfo.setAcceptedTime(new Date());
|
|
|
+ appOrderRefundsInfoMapper.insert(appOrderRefundsInfo);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -444,14 +465,11 @@ public class WeChatPayService {
|
|
|
}
|
|
|
|
|
|
public Map<String, Object> refundOrderNotify(String jsonData) throws IOException, GeneralSecurityException {
|
|
|
-
|
|
|
//转为map格式
|
|
|
Map jsonMap = JSONObject.parseObject(jsonData, Map.class);
|
|
|
|
|
|
//退款成功后返回一个加密字段resource,以下为解密
|
|
|
- /**
|
|
|
- * 解密需要从resource参数中,获取到ciphertext,nonce,associated_data这三个参数进行解密
|
|
|
- */
|
|
|
+ //解密需要从resource参数中,获取到ciphertext,nonce,associated_data这三个参数进行解密
|
|
|
String resource = JSONObject.toJSONString(jsonMap.get("resource"));
|
|
|
JSONObject object = JSONObject.parseObject(resource);
|
|
|
|
|
@@ -462,13 +480,13 @@ public class WeChatPayService {
|
|
|
String resultStr = decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
|
|
|
Map<String, String> reqInfo = JSONObject.parseObject(resultStr, Map.class);
|
|
|
|
|
|
- log.info("微信返回的退款通知数据:" + reqInfo);
|
|
|
+ log.info("微信返回的退款通知数据:{}", reqInfo);
|
|
|
|
|
|
String refund_status = reqInfo.get("refund_status");//退款状态
|
|
|
String out_trade_no = reqInfo.get("out_trade_no"); //订单号
|
|
|
|
|
|
Map<String, Object> parm = new HashMap<>();
|
|
|
- if (!StringUtils.isEmpty(refund_status) && "SUCCESS".equals(refund_status)) {
|
|
|
+ if (!StringUtils.isEmpty(refund_status) && "SUCCESS".equals(refund_status)) {
|
|
|
|
|
|
//查询订单
|
|
|
AppOrder order = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getOrderCode, out_trade_no).last("limit 1"));
|
|
@@ -493,7 +511,7 @@ public class WeChatPayService {
|
|
|
// 创建退款订单
|
|
|
AppOrderRefundsInfo appOrderRefundsInfo = appOrderRefundsInfoMapper.selectOne(Wrappers.lambdaQuery(AppOrderRefundsInfo.class).eq(AppOrderRefundsInfo::getOrderId, order.getId()));
|
|
|
|
|
|
- if (ObjectUtil.isNotEmpty(appOrderRefundsInfo)){
|
|
|
+ if (ObjectUtil.isNotEmpty(appOrderRefundsInfo)) {
|
|
|
// appOrderRefundsInfo.setRefundId();
|
|
|
appOrderRefundsInfo.setSuccessTime(new Date());
|
|
|
// appOrderRefundsInfo.setNotifyRequest();
|
|
@@ -514,7 +532,8 @@ public class WeChatPayService {
|
|
|
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException {
|
|
|
try {
|
|
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
|
- SecretKeySpec key = new SecretKeySpec(WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(), "AES");// 这里的apiV3key是你的商户APIV3密钥
|
|
|
+ // 这里的apiV3key是你的商户ApiV3密钥
|
|
|
+ SecretKeySpec key = new SecretKeySpec(WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(), "AES");
|
|
|
GCMParameterSpec spec = new GCMParameterSpec(128, nonce);//规定为128
|
|
|
cipher.init(Cipher.DECRYPT_MODE, key, spec);
|
|
|
cipher.updateAAD(associatedData);
|