diff --git a/smart-switch-ruoyi/smart-switch-common/pom.xml b/smart-switch-ruoyi/smart-switch-common/pom.xml index 2cdd7815..d3590b6c 100644 --- a/smart-switch-ruoyi/smart-switch-common/pom.xml +++ b/smart-switch-ruoyi/smart-switch-common/pom.xml @@ -29,12 +29,18 @@ 4.5.7 - + com.alipay.sdk alipay-sdk-java 4.40.0.ALL + + + + + + diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/auth/ali/AliConfig.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/auth/ali/AliConfig.java index 7d5bb9fe..d626e014 100644 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/auth/ali/AliConfig.java +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/auth/ali/AliConfig.java @@ -36,6 +36,9 @@ public class AliConfig { @Value("${ali.aesPrivateKey}") private String aesPrivateKey; + @Value("${ali.notifyUrl}") + private String notifyUrl; + private AlipayConfig getAlipayConfig() { AlipayConfig alipayConfig = new AlipayConfig(); alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do"); diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/PayApi.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/PayApi.java new file mode 100644 index 00000000..50c42da1 --- /dev/null +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/PayApi.java @@ -0,0 +1,31 @@ +package com.ruoyi.common.pay; + +import java.time.LocalDateTime; + +/** + * @author wjh + * 2024/12/10 + */ +public interface PayApi { + + // 发起支付 + Object pay(Payable payable); + + // 关闭支付单 + void closeByOutTradeNo(String outTradeNo); + + // 查询支付单 + Object queryByOutTradeNo(String outTradeNo); + + // 退款 + Object refund(Refundable refundAble); + + // 是否支付成功 + boolean isPaySuccessByOutTradeNo(String outTradeNo); + + // 是否支付成功 + boolean isPaySuccess(Object result); + + // 获取支付成功的时间 + LocalDateTime getPayTime(Object result); +} diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/domain/Payable.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/Payable.java similarity index 58% rename from smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/domain/Payable.java rename to smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/Payable.java index 390f4b1b..04cf839d 100644 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/domain/Payable.java +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/Payable.java @@ -1,6 +1,7 @@ -package com.ruoyi.common.pay.wx.domain; +package com.ruoyi.common.pay; import java.math.BigDecimal; +import java.math.RoundingMode; /** * @author wjh @@ -11,7 +12,14 @@ public interface Payable { /** * 获取价格(分) */ - BigDecimal payableMoney(); + BigDecimal payableFen(); + + /** + * 获取价格(元) + */ + default BigDecimal payableYuan() { + return payableFen().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + } /** * 获取描述 @@ -29,7 +37,7 @@ public interface Payable { String payableOutTradeNo(); /** - * 获取微信openId + * 获取openId */ String payableOpenId(); } diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/domain/RefundAble.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/Refundable.java similarity index 50% rename from smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/domain/RefundAble.java rename to smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/Refundable.java index 21ceb3a3..e6582c83 100644 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/domain/RefundAble.java +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/Refundable.java @@ -1,12 +1,15 @@ -package com.ruoyi.common.pay.wx.domain; +package com.ruoyi.common.pay; import com.wechat.pay.java.service.refund.model.AmountReq; +import java.math.BigDecimal; +import java.math.RoundingMode; + /** * @author wjh * 2024/7/10 */ -public interface RefundAble { +public interface Refundable { // 原商户订单号 String refundOutTradeNo(); @@ -20,4 +23,9 @@ public interface RefundAble { // 退款金额(分) AmountReq refundAmount(); + default BigDecimal refundAmountYuan() { + return BigDecimal.valueOf(refundAmount().getRefund()) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + } + } diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/ali/enums/AliTradeStatusEnum.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/ali/enums/AliTradeStatusEnum.java new file mode 100644 index 00000000..212237ec --- /dev/null +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/ali/enums/AliTradeStatusEnum.java @@ -0,0 +1,23 @@ +package com.ruoyi.common.pay.ali.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author wjh + * 2024/12/10 + */ +@Getter +@AllArgsConstructor +public enum AliTradeStatusEnum { + + WAIT_BUYER_PAY("WAIT_BUYER_PAY", "交易创建,等待买家付款"), + TRADE_CLOSED("TRADE_CLOSED", "未付款交易超时关闭,或支付完成后全额退款"), + TRADE_SUCCESS("TRADE_SUCCESS", "交易支付成功"), + TRADE_FINISHED("TRADE_FINISHED", "交易结束,不可退款"); + + private String status; + private String msg; + +} diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/ali/service/AliPayService.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/ali/service/AliPayService.java new file mode 100644 index 00000000..9a919834 --- /dev/null +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/ali/service/AliPayService.java @@ -0,0 +1,154 @@ +package com.ruoyi.common.pay.ali.service; + +import cn.hutool.json.JSONObject; +import com.alipay.api.AlipayClient; +import com.alipay.api.domain.AlipayTradeCreateModel; +import com.alipay.api.domain.AlipayTradeQueryModel; +import com.alipay.api.domain.AlipayTradeRefundModel; +import com.alipay.api.request.AlipayTradeCloseRequest; +import com.alipay.api.request.AlipayTradeCreateRequest; +import com.alipay.api.request.AlipayTradeQueryRequest; +import com.alipay.api.request.AlipayTradeRefundRequest; +import com.alipay.api.response.AlipayTradeCloseResponse; +import com.alipay.api.response.AlipayTradeCreateResponse; +import com.alipay.api.response.AlipayTradeQueryResponse; +import com.alipay.api.response.AlipayTradeRefundResponse; +import com.ruoyi.common.auth.ali.AliConfig; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.pay.PayApi; +import com.ruoyi.common.pay.Payable; +import com.ruoyi.common.pay.Refundable; +import com.ruoyi.common.pay.ali.enums.AliTradeStatusEnum; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.ServiceUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +/** + * @author wjh + * 2024/12/10 + */ +@Service +public class AliPayService implements PayApi { + + @Autowired + private AlipayClient alipayClient; + + @Autowired + private AliConfig aliConfig; + + public String pay(Payable payable) { + // 构造请求参数以调用接口 + AlipayTradeCreateRequest request = new AlipayTradeCreateRequest(); + AlipayTradeCreateModel data = new AlipayTradeCreateModel(); + // 设置商户订单号 + data.setOutTradeNo(payable.payableOutTradeNo()); + // 设置产品码 + data.setProductCode("JSAPI_PAY"); + // 设置小程序appId + data.setOpAppId(aliConfig.getAppId()); + // 设置订单总金额(元) + data.setTotalAmount(payable.payableYuan().toString()); + // 设置订单标题 + data.setSubject(payable.payableDescription()); + // 设置订单附加信息 + data.setBody(payable.payableAttach()); + // 设置买家支付宝用户唯一标识 + data.setBuyerOpenId(payable.payableOpenId()); + request.setBizModel(data); + request.setNotifyUrl(aliConfig.getNotifyUrl()); + + try { + AlipayTradeCreateResponse response = alipayClient.execute(request); + ServiceUtil.assertion(!response.isSuccess(), response.getMsg()); + return response.getTradeNo(); + } catch (Exception e) { + throw new ServiceException("调用支付宝下单失败:" + e.getMessage()); + } + } + + /** + * 关闭支付订单 + */ + public void closeByOutTradeNo(String outTradeNo) { + AlipayTradeCloseRequest request = new AlipayTradeCloseRequest(); + JSONObject bizContent = new JSONObject(); + bizContent.put("out_trade_no", outTradeNo); + request.setBizContent(bizContent.toString()); + try { + AlipayTradeCloseResponse response = alipayClient.execute(request); + ServiceUtil.assertion(!response.isSuccess(), response.getMsg()); + } catch (Exception e) { + throw new ServiceException("调用支付宝关闭订单失败:" + e.getMessage()); + } + } + + + public AlipayTradeQueryResponse queryByOutTradeNo(String outTradeNo) { + // 构造请求参数以调用接口 + AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); + AlipayTradeQueryModel model = new AlipayTradeQueryModel(); + + // 设置订单支付时传入的商户订单号 + model.setOutTradeNo(outTradeNo); + request.setBizModel(model); + + try { + AlipayTradeQueryResponse response = alipayClient.execute(request); + ServiceUtil.assertion(!response.isSuccess(), response.getMsg()); + return response; + } catch (Exception e) { + throw new ServiceException("调用支付宝查询订单失败:" + e.getMessage()); + } + } + + + /** + * 发起退款 + * + * @param refund + */ + public AlipayTradeRefundResponse refund(Refundable refund) { + // 构造请求参数以调用接口 + AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); + AlipayTradeRefundModel model = new AlipayTradeRefundModel(); + + // 设置商户订单号 + model.setOutTradeNo(refund.refundOutTradeNo()); + // 设置退款金额 + model.setRefundAmount(refund.refundAmountYuan().toString()); + // 设置退款原因说明 + model.setRefundReason(refund.refundReason()); + // 设置退款请求号 + model.setOutRequestNo(refund.refundOutRefundNo()); + request.setBizModel(model); + + try { + AlipayTradeRefundResponse response = alipayClient.execute(request); + ServiceUtil.assertion(!response.isSuccess(), response.getMsg()); + return response; + } catch (Exception e) { + throw new ServiceException("调用支付宝退款失败:" + e.getMessage()); + } + } + + @Override + public boolean isPaySuccessByOutTradeNo(String outTradeNo) { + AlipayTradeQueryResponse res = this.queryByOutTradeNo(outTradeNo); + return isPaySuccess(res); + } + + @Override + public boolean isPaySuccess(Object result) { + AlipayTradeQueryResponse res = (AlipayTradeQueryResponse) result; + return res != null && AliTradeStatusEnum.TRADE_SUCCESS.getStatus().equals(res.getTradeStatus()); + } + + @Override + public LocalDateTime getPayTime(Object result) { + AlipayTradeQueryResponse res = (AlipayTradeQueryResponse) result; + return DateUtils.toLocalDateTime(res.getSendPayDate()); + } +} diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayService.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayService.java index 5a8014c2..e2b2a235 100644 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayService.java +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayService.java @@ -2,18 +2,20 @@ package com.ruoyi.common.pay.syb.service; import com.alibaba.fastjson2.JSON; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.pay.PayApi; import com.ruoyi.common.pay.syb.config.SybConfig; import com.ruoyi.common.pay.syb.enums.SybTrxStatus; import com.ruoyi.common.pay.syb.util.SybUtil; -import com.ruoyi.common.pay.wx.domain.Payable; -import com.ruoyi.common.pay.wx.domain.RefundAble; +import com.ruoyi.common.pay.Payable; +import com.ruoyi.common.pay.Refundable; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.ServiceUtil; import com.ruoyi.common.utils.StringUtils; import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse; -import com.wechat.pay.java.service.payments.model.Transaction; -import com.wechat.pay.java.service.refund.model.Refund; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; import java.util.Map; import java.util.TreeMap; @@ -22,7 +24,7 @@ import java.util.TreeMap; * 2024/9/9 */ @Service -public class SybPayService { +public class SybPayService implements PayApi { @Autowired private SybPayClient sybPayClient; @@ -33,10 +35,10 @@ public class SybPayService { /** * 微信小程序预下单 */ - public PrepayWithRequestPaymentResponse prepayWxApp(Payable payable) { + public PrepayWithRequestPaymentResponse pay(Payable payable) { try { Map result = sybPayClient.pay( - payable.payableMoney().longValue(), + payable.payableFen().longValue(), payable.payableOutTradeNo(), "W06", payable.payableDescription(), @@ -95,7 +97,7 @@ public class SybPayService { /** * 使用商户自定义订单编号查询支付订单 */ - public Map queryOrderByOutTradeNo(String outTradeNo) { + public Map queryByOutTradeNo(String outTradeNo) { try { return sybPayClient.query(outTradeNo, ""); } catch (Exception e) { @@ -106,23 +108,43 @@ public class SybPayService { /** * 微信退款 */ - public Map refundWx(RefundAble refundAble) { + public Map refund(Refundable refundAble) { try { - return sybPayClient.refund( - refundAble.refundAmount().getRefund().longValue(), + Map result = sybPayClient.refund( + refundAble.refundAmount().getRefund(), refundAble.refundOutRefundNo(), "", refundAble.refundOutTradeNo() ); + ServiceUtil.assertion(result == null, "退款失败,响应数据为空"); + String trxStatus = result.get("trxstatus"); + ServiceUtil.assertion(!SybTrxStatus.isSuccess(trxStatus), "发起退款失败:" + result.get("errmsg")); + return result; } catch (Exception e) { throw new ServiceException("发起退款异常:" + e.getMessage()); } } + @Override + public boolean isPaySuccessByOutTradeNo(String outTradeNo) { + return isPaySuccess(this.queryByOutTradeNo(outTradeNo)); + } + + @Override + public boolean isPaySuccess(Object result) { + return SybTrxStatus.isSuccess((Map)result); + } + + @Override + public LocalDateTime getPayTime(Object result) { + Map res = (Map)result; + return DateUtils.toLocalDate(res.get("fintime"), "yyyyMMddHHmmss"); + } + /** * 关闭微信支付订单 */ - public void closeOrderWx(String outTradeNo) { + public void closeByOutTradeNo(String outTradeNo) { try { sybPayClient.close("", outTradeNo); } catch (Exception e) { diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/tm/Application.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/tm/Application.java deleted file mode 100644 index 141ac76a..00000000 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/tm/Application.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.ruoyi.common.pay.tm; - -import com.alibaba.fastjson2.JSON; -import com.ruoyi.common.utils.SnowFlakeUtil; -import com.ruoyi.common.utils.http.HttpUtils; -import com.ruoyi.common.utils.uuid.IdUtils; - -import java.util.HashMap; - -public class Application { - - private final static String HTTP = "https://pos.weixincore.com"; - private final static String SIGNKEY = "ac6d97e67b444b7a43edfc9182634786"; - - public static void main(String[] args) { - orderQuery("tmwx1727172604248"); -// miniPay(); - } - - /** - * 订单查询 - */ - public static void orderQuery(String outTradeId) { - HashMap body = new HashMap<>(); - body.put("outTradeId", outTradeId); - body.put("terminalType", "1"); - body.put("shopId", "488"); - doPost("/open/Pay/orderQuery", body); - } - - /** - * 退款 - */ - public static void refund() { - HashMap body = new HashMap<>(); - body.put("refundFee", "0.01"); - body.put("terminalType", "1"); - body.put("tradeId", "1"); - body.put("shopId", "488"); - doPost("/open/Pay/refund", body); - } - - -// /** -// * jsapi支付 -// */ -// public static void pay() { -// HashMap body = new HashMap(); -// body.put("payAmount", "1"); -// body.put("terminalType", "1"); -// body.put("shopId", "488"); -// // 填充必填字段 -// body.put("payType", "wx_pay"); // 支付方式,可以是 wx.pay, ali.pay, union.online -// body.put("outTradeId", "tradeId123"); // 商户订单号 -// body.put("body", "商品描述"); // 商品描述 -// body.put("notifyUrl", "https://yourdomain.com/notify"); // 异步回调URL -// body.put("frontUrl", "https://yourdomain.com/front"); // 前端页面跳转URL -// doPost("/open/Pay/unifiedOrder", body); -// } - - - /** - * 小程序支付 - */ - public static void miniPay() { - HashMap body = new HashMap<>(); - body.put("payAmount", "1"); - body.put("terminalType", "1"); - body.put("shopId", "488"); - // 填充必填字段 - body.put("payType", "wx_pay"); // 支付方式,可以是 wx.pay, ali.pay, union.online - body.put("outTradeId", SnowFlakeUtil.newId()); // 商户订单号 - System.out.println("----------------------------------:"+body.get("outTradeId")); - body.put("openid", "o6yEK7Z0OdWM2N_d8ehItn-5NBH8"); // openid - body.put("body", "商品描述"); // 商品描述 - body.put("notifyUrl", "https://yourdomain.com/notify"); // 异步回调URL - body.put("frontUrl", "https://yourdomain.com/front"); // 前端页面跳转URL - doPost("/open/Pay/miniPay", body); - } - - private static void doPost(String url, HashMap body) { - body.put("developerId", "100001"); - body.put("version", "1.0"); - body.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000)); - body.put("nonceStr", TmPayUtil.getRandomString(16)); - String bodyStr = TmPayUtil.getAsciiSort(body); - String sign = TmPayUtil.getMD5Code(bodyStr+"&key="+SIGNKEY).toUpperCase(); - body.put("sign", sign); - - HashMap headerData = new HashMap(); - headerData.put("Content-Type", "application/json"); - - String response = HttpUtils.sendPostWithHeaders(HTTP + url, headerData,JSON.toJSONString(body)); - System.out.println("API Response: " + response); - } - -} diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/tm/TmPayService.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/tm/TmPayService.java index c087585c..0d3dd05f 100644 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/tm/TmPayService.java +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/tm/TmPayService.java @@ -3,16 +3,20 @@ package com.ruoyi.common.pay.tm; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.pay.PayApi; import com.ruoyi.common.pay.tm.config.TmPayConfig; +import com.ruoyi.common.pay.tm.enums.TmPayStatus; import com.ruoyi.common.pay.tm.vo.RefundInfo; import com.ruoyi.common.pay.tm.vo.TmTradeInfo; -import com.ruoyi.common.pay.wx.domain.Payable; -import com.ruoyi.common.pay.wx.domain.RefundAble; +import com.ruoyi.common.pay.Payable; +import com.ruoyi.common.pay.Refundable; +import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.http.HttpUtils; import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @@ -20,7 +24,7 @@ import java.util.Map; * 太米支付 */ @Service -public class TmPayService { +public class TmPayService implements PayApi { @Autowired private TmPayConfig config; @@ -28,7 +32,7 @@ public class TmPayService { /** * 订单查询 */ - public TmTradeInfo orderQuery(String outTradeNo) { + public TmTradeInfo queryByOutTradeNo(String outTradeNo) { HashMap body = new HashMap<>(); body.put("outTradeId", outTradeNo); body.put("terminalType", "1"); @@ -50,7 +54,7 @@ public class TmPayService { /** * 退款 */ - public RefundInfo refund(RefundAble refundAble) { + public RefundInfo refund(Refundable refundAble) { HashMap body = new HashMap<>(); body.put("refundFee", String.valueOf(refundAble.refundAmount().getRefund())); body.put("terminalType", "1"); @@ -61,8 +65,7 @@ public class TmPayService { JSONObject jsonResponse = JSON.parseObject(response); if (jsonResponse.getInteger("errCode") == 0 && "退款成功".equals(jsonResponse.getString("errMsg"))) { JSONObject tradeInfo = jsonResponse.getJSONObject("refundInfo"); - RefundInfo refundInfo = tradeInfo.toJavaObject(RefundInfo.class); - return refundInfo; + return tradeInfo.toJavaObject(RefundInfo.class); } else { throw new ServiceException("退款失败: " + jsonResponse.getString("errMsg")); } @@ -70,23 +73,49 @@ public class TmPayService { throw new ServiceException("退款数据为空"); } + @Override + public boolean isPaySuccessByOutTradeNo(String outTradeNo) { + TmTradeInfo result = queryByOutTradeNo(outTradeNo); + return isPaySuccess(result); + } + + @Override + public boolean isPaySuccess(Object result) { + TmTradeInfo res = (TmTradeInfo) result; + return res != null && TmPayStatus.isSuccess(res.getPayStatus()); + } + + @Override + public LocalDateTime getPayTime(Object result) { + TmTradeInfo res = (TmTradeInfo) result; + return DateUtils.toLocalDateTime(res.getPayTime(), "yyyy-MM-dd HH:mm:ss"); + } + /** * 关闭订单 */ - public void closeOrder(String outTradeNo) { + public void closeByOutTradeNo(String outTradeNo) { HashMap body = new HashMap<>(); body.put("outTradeId", outTradeNo); body.put("terminalType", "1"); body.put("shopId", config.getShopId()); - doPost(config.getHttpUrl() + "/open/Pay/orderClose", body, config); + String response = doPost(config.getHttpUrl() + "/open/Pay/orderClose", body, config); + if (com.ruoyi.common.utils.StringUtils.hasText(response)) { + JSONObject jsonResponse = JSON.parseObject(response); + if (jsonResponse.getInteger("errCode") != 0) { + throw new ServiceException("关闭订单失败: " + jsonResponse.getString("errMsg")); + } + }else{ + throw new ServiceException("关闭订单数据为空"); + } } /** * 微信小程序支付 */ - public PrepayWithRequestPaymentResponse wxPay(Payable payable) { + public PrepayWithRequestPaymentResponse pay(Payable payable) { HashMap body = new HashMap<>(); - body.put("payAmount", String.valueOf(payable.payableMoney())); + body.put("payAmount", String.valueOf(payable.payableFen())); body.put("terminalType", "1"); body.put("shopId", config.getShopId()); // 从渠道获取shopId body.put("sn", null); diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/service/WxPayService.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/service/WxPayService.java index 95c3876c..efbd131e 100644 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/service/WxPayService.java +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/wx/service/WxPayService.java @@ -1,8 +1,12 @@ package com.ruoyi.common.pay.wx.service; import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.pay.PayApi; +import com.ruoyi.common.pay.Payable; +import com.ruoyi.common.pay.Refundable; import com.ruoyi.common.pay.wx.config.WxPayConfig; -import com.ruoyi.common.pay.wx.domain.*; +import com.ruoyi.common.pay.wx.util.WxPayUtil; +import com.ruoyi.common.utils.DateUtils; import com.wechat.pay.java.service.payments.jsapi.JsapiService; import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension; import com.wechat.pay.java.service.payments.jsapi.model.*; @@ -15,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.time.LocalDateTime; /** * 微信支付服务 @@ -23,7 +28,7 @@ import java.math.BigDecimal; */ @Service @Slf4j -public class WxPayService { +public class WxPayService implements PayApi { @Autowired private JsapiService jsapiService; @@ -39,8 +44,8 @@ public class WxPayService { private static final String CNY = "CNY"; - public PrepayWithRequestPaymentResponse prepayWithRequestPayment(Payable payable) { - return this.prepayWithRequestPayment(payable.payableOutTradeNo(), payable.payableMoney(), payable.payableDescription(), payable.payableOpenId(), payable.payableAttach()); + public PrepayWithRequestPaymentResponse pay(Payable payable) { + return this.prepayWithRequestPayment(payable.payableOutTradeNo(), payable.payableFen(), payable.payableDescription(), payable.payableOpenId(), payable.payableAttach()); } public PrepayWithRequestPaymentResponse prepayWithRequestPayment(String billNo, BigDecimal money, String description, String wxOpenId, String attach) { @@ -59,9 +64,10 @@ public class WxPayService { /** * 关闭支付订单 + * * @param billNo 平台订单编号 */ - public void closeOrder(String billNo) { + public void closeByOutTradeNo(String billNo) { CloseOrderRequest request = new CloseOrderRequest(); request.setMchid(wxPayConfig.getMerchantId()); request.setOutTradeNo(billNo); @@ -75,7 +81,7 @@ public class WxPayService { return jsapiService.queryOrderById(request); } - public Transaction queryOrderByOutTradeNo(String billNo) { + public Transaction queryByOutTradeNo(String billNo) { QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest(); request.setMchid(wxPayConfig.getMerchantId()); request.setOutTradeNo(billNo); @@ -87,7 +93,7 @@ public class WxPayService { * * @param refund */ - public Refund refund(RefundAble refund) { + public Refund refund(Refundable refund) { CreateRequest request = new CreateRequest(); request.setOutTradeNo(refund.refundOutTradeNo()); request.setOutRefundNo(refund.refundOutRefundNo()); @@ -100,6 +106,23 @@ public class WxPayService { return res; } + @Override + public boolean isPaySuccessByOutTradeNo(String outTradeNo) { + return isPaySuccess(this.queryByOutTradeNo(outTradeNo)); + } + + @Override + public boolean isPaySuccess(Object result) { + Transaction transaction = (Transaction) result; + return WxPayUtil.isSuccess(transaction); + } + + @Override + public LocalDateTime getPayTime(Object result) { + Transaction transaction = (Transaction) result; + return DateUtils.toLocalDateTimeByISO(transaction.getSuccessTime()); + } + private Payer getPayer(String openId) { Payer payer = new Payer(); payer.setOpenid(openId); diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/yst/service/YstPayService.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/yst/service/YstPayService.java index 73d23469..72d2360d 100644 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/yst/service/YstPayService.java +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/yst/service/YstPayService.java @@ -1,6 +1,6 @@ package com.ruoyi.common.pay.yst.service; -import com.ruoyi.common.pay.wx.domain.Payable; +import com.ruoyi.common.pay.Payable; import com.ruoyi.common.pay.yst.domain.bizRes.comsumptionApply.ChannelParamInfo; /** diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/yst/service/impl/YstPayServiceImpl.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/yst/service/impl/YstPayServiceImpl.java index eb836057..a7707273 100644 --- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/yst/service/impl/YstPayServiceImpl.java +++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/pay/yst/service/impl/YstPayServiceImpl.java @@ -1,7 +1,7 @@ package com.ruoyi.common.pay.yst.service.impl; import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.pay.wx.domain.Payable; +import com.ruoyi.common.pay.Payable; import com.ruoyi.common.pay.yst.config.YstConfig; import com.ruoyi.common.pay.yst.constants.YstApi; import com.ruoyi.common.pay.yst.constants.YstTransCode; diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/Channel.java b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/Channel.java index 9982fada..495eb285 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/Channel.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/Channel.java @@ -53,4 +53,7 @@ public class Channel extends BaseEntity @JsonView(JsonViewProfile.App.class) private String picture; + @Excel(name = "适用平台(1微信 2支付宝)") + @ApiModelProperty("适用平台(1微信 2支付宝)") + private String platform; } diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/enums/ChannelEnum.java b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/enums/ChannelEnum.java new file mode 100644 index 00000000..91b19160 --- /dev/null +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/enums/ChannelEnum.java @@ -0,0 +1,63 @@ +package com.ruoyi.ss.channel.domain.enums; + +import com.ruoyi.common.pay.PayApi; +import com.ruoyi.common.pay.ali.service.AliPayService; +import com.ruoyi.common.pay.syb.service.SybPayService; +import com.ruoyi.common.pay.tm.TmPayService; +import com.ruoyi.common.pay.wx.service.WxPayService; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.ss.account.domain.enums.AccountType; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 渠道信息 + * @author 辉 + * 2024/3/12 + */ +@Getter +@AllArgsConstructor +public enum ChannelEnum { + + WECHAT(1L, "微信支付", AccountType.WECHAT, WxPayService.class, false), + ALI(2L, "支付宝", AccountType.ALIPAY, AliPayService.class, true), + BANK(3L, "银行卡", AccountType.BANK_CARD, null, false), + BALANCE(4L, "余额支付", null, null, true), + TL_WX(5L, "通联微信支付", null, SybPayService.class, true), + TM_WX(6L, "太米微信支付", null, TmPayService.class, true); + + private final Long type; + private final String name; + private final AccountType accountType; + private final Class payApi; + private final Boolean isRefundSync; // 退款是否同步通知 + + public static ChannelEnum parse(Long type) { + for (ChannelEnum value : ChannelEnum.values()) { + if (Objects.equals(value.getType(), type)) { + return value; + } + } + return null; + } + + public static List asList(ChannelEnum...types) { + return Arrays.stream(types).map(ChannelEnum::getType).collect(Collectors.toList()); + } + + /** + * 获取支付API + */ + public static PayApi getPayApi(Long type) { + ChannelEnum parse = parse(type); + if (parse == null || parse.getPayApi() == null) { + return null; + } + return SpringUtils.getBean(parse.getPayApi()); + } +} diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/enums/ChannelPlatform.java b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/enums/ChannelPlatform.java new file mode 100644 index 00000000..00032df2 --- /dev/null +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/domain/enums/ChannelPlatform.java @@ -0,0 +1,20 @@ +package com.ruoyi.ss.channel.domain.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author wjh + * 2024/12/10 + */ +@Getter +@AllArgsConstructor +public enum ChannelPlatform { + + WX("1", "微信"), + ALI("2", "支付宝"); + + private final String code; + private final String msg; + +} diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/mapper/ChannelMapper.xml b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/mapper/ChannelMapper.xml index d1bde873..e03a6da8 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/mapper/ChannelMapper.xml +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/mapper/ChannelMapper.xml @@ -17,7 +17,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" sc.service_rate, sc.cost_rate, sc.service_type, - sc.picture + sc.picture, + sc.platform from sm_channel sc @@ -25,6 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and sc.name like concat('%', #{query.name}, '%') and sc.enabled = #{query.enabled} and sc.service_type = #{query.serviceType} + and platform = #{query.platform} and sc.channel_id in @@ -55,6 +57,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" cost_rate, service_type, picture, + platform, #{channelId}, @@ -64,6 +67,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{costRate}, #{serviceType}, #{picture}, + #{platform}, @@ -76,6 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" cost_rate = #{data.costRate}, service_type = #{data.serviceType}, picture = #{data.picture}, + platform = #{data.platform}, where channel_id = #{data.channelId} diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/service/ChannelService.java b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/service/ChannelService.java index 4acd5a50..3d4b2207 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/service/ChannelService.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/service/ChannelService.java @@ -69,7 +69,7 @@ public interface ChannelService * * @return */ - List selectEnabledRechargeList(); + List selectEnabledRechargeList(ChannelQuery query); /** * 查询充值渠道映射表 diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/service/impl/ChannelServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/service/impl/ChannelServiceImpl.java index 4efa6445..7db58edd 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/channel/service/impl/ChannelServiceImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/channel/service/impl/ChannelServiceImpl.java @@ -104,10 +104,9 @@ public class ChannelServiceImpl implements ChannelService } @Override - public List selectEnabledRechargeList() { - ChannelQuery dto = new ChannelQuery(); - dto.setEnabled(true); - return this.selectSmChannelList(dto); + public List selectEnabledRechargeList(ChannelQuery query) { + query.setEnabled(true); + return this.selectSmChannelList(query); } /** diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/domain/PayBill.java b/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/domain/PayBill.java index 36f63157..00eb910c 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/domain/PayBill.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/domain/PayBill.java @@ -2,7 +2,7 @@ package com.ruoyi.ss.payBill.domain; import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.core.domain.BaseEntity; -import com.ruoyi.common.pay.wx.domain.Payable; +import com.ruoyi.common.pay.Payable; import com.ruoyi.common.pay.wx.domain.enums.AttachEnums; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -71,7 +71,7 @@ public class PayBill extends BaseEntity implements Payable * 获取价格(分) */ @Override - public BigDecimal payableMoney() { + public BigDecimal payableFen() { return amount.multiply(BigDecimal.valueOf(100)); } diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillConverterImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillConverterImpl.java index e84377e7..9f14293f 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillConverterImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillConverterImpl.java @@ -1,6 +1,7 @@ package com.ruoyi.ss.payBill.service.impl; import com.ruoyi.ss.channel.domain.ChannelVO; +import com.ruoyi.ss.channel.domain.enums.ChannelPlatform; import com.ruoyi.ss.channel.service.ChannelService; import com.ruoyi.ss.payBill.domain.PayBill; import com.ruoyi.ss.payBill.domain.PayBillVO; @@ -10,7 +11,7 @@ import com.ruoyi.ss.payBill.service.PayBillConverter; import com.ruoyi.ss.timeBill.domain.TimeBill; import com.ruoyi.ss.transactionBill.domain.bo.RechargePayDepositBO; import com.ruoyi.ss.transactionBill.domain.dto.RechargePayBO; -import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillPayType; +import com.ruoyi.ss.channel.domain.enums.ChannelEnum; import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO; import com.ruoyi.ss.user.domain.SmUserVO; import com.ruoyi.ss.user.service.ISmUserService; @@ -63,7 +64,7 @@ public class PayBillConverterImpl implements PayBillConverter { data.setChannelCost(this.calcChannelCost(channel, bill.getAmount())); // 支付人 - if (TransactionBillPayType.WECHAT.getType().equals(data.getChannelId())) { + if (ChannelEnum.WECHAT.getType().equals(data.getChannelId())) { data.setAccount(user.getWxOpenId()); } return po; @@ -110,8 +111,10 @@ public class PayBillConverterImpl implements PayBillConverter { po.setChannelCost(this.calcChannelCost(channel, order.getMoney())); // 支付人 - if (TransactionBillPayType.wxList().contains(channel.getChannelId())) { + if (ChannelPlatform.WX.getCode().equals(channel.getPlatform())) { po.setAccount(user.getWxOpenId()); + } else if (ChannelPlatform.ALI.getCode().equals(channel.getPlatform())) { + po.setAccount(user.getAliOpenId()); } return po; @@ -149,8 +152,10 @@ public class PayBillConverterImpl implements PayBillConverter { po.setChannelCost(this.calcChannelCost(channel, order.getSuitPrice())); // 支付人 - if (TransactionBillPayType.wxList().contains(channel.getChannelId())) { + if (ChannelPlatform.WX.getCode().equals(channel.getPlatform())) { po.setAccount(user.getWxOpenId()); + } else if (ChannelPlatform.ALI.getCode().equals(channel.getPlatform())) { + po.setAccount(user.getAliOpenId()); } return po; diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillServiceImpl.java index 735c25c4..d221fd77 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillServiceImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillServiceImpl.java @@ -4,6 +4,8 @@ import com.ruoyi.common.core.redis.RedisLock; import com.ruoyi.common.core.redis.enums.RedisLockKey; import com.ruoyi.common.domain.vo.LocalDateDecimalVO; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.pay.PayApi; +import com.ruoyi.common.pay.ali.service.AliPayService; import com.ruoyi.common.pay.syb.enums.SybTrxStatus; import com.ruoyi.common.pay.syb.service.SybPayService; import com.ruoyi.common.pay.tm.TmPayService; @@ -14,6 +16,7 @@ import com.ruoyi.common.pay.wx.util.WxPayUtil; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.ServiceUtil; import com.ruoyi.common.utils.SnowFlakeUtil; +import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.collection.CollectionUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.ss.payBill.domain.PayBill; @@ -29,12 +32,11 @@ import com.ruoyi.ss.payBill.domain.vo.PayResultVO; import com.ruoyi.ss.payBill.interfaces.AfterPay; import com.ruoyi.ss.payBill.interfaces.AfterRefund; import com.ruoyi.ss.payBill.mapper.PayBillMapper; -import com.ruoyi.ss.payBill.service.PayBillConverter; import com.ruoyi.ss.payBill.service.PayBillService; import com.ruoyi.ss.refund.domain.RefundVO; import com.ruoyi.ss.refund.service.RefundConverter; import com.ruoyi.ss.refund.service.RefundService; -import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillPayType; +import com.ruoyi.ss.channel.domain.enums.ChannelEnum; import com.wechat.pay.java.service.payments.model.Transaction; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -80,15 +82,15 @@ public class PayBillServiceImpl implements PayBillService @Autowired private RefundService refundService; - @Autowired - private PayBillConverter payBillConverter; - @Autowired private SybPayService sybPayService; @Autowired private TmPayService tmPayService; + @Autowired + private AliPayService aliPayService; + /** * 查询支付订单 * @@ -166,6 +168,7 @@ public class PayBillServiceImpl implements PayBillService @Override public DoPayVO createPayBill(PayBill bill) { ServiceUtil.assertion(bill == null, "待新增的支付订单不存在"); + ServiceUtil.assertion(StringUtils.isBlank(bill.getAccount()), "用户支付账号不存在"); // 锁 String lockKey = bill.getBstType() + bill.getBstId(); if (!redisLock.lock(RedisLockKey.PAY_BILL, lockKey)) { @@ -229,27 +232,22 @@ public class PayBillServiceImpl implements PayBillService for (PayBillVO bill : payingList) { // 关闭支付中的订单 if (PayBillStatus.PAYING.getStatus().equals(bill.getStatus())) { - // 微信 - if (TransactionBillPayType.WECHAT.getType().equals(bill.getChannelId())) { - wxPayService.closeOrder(bill.getPayNo()); + // 获取支付API + PayApi payApi = ChannelEnum.getPayApi(bill.getChannelId()); + if (payApi == null) { + continue; } - // 通联微信 - else if (TransactionBillPayType.TL_WX.getType().equals(bill.getChannelId())) { - try { - sybPayService.closeOrderWx(bill.getPayNo()); - } catch (Exception e) { - Map result = sybPayService.queryOrderByOutTradeNo(bill.getPayNo()); - if (SybTrxStatus.isSuccess(result)) { - throw new ServiceException("交易已成功,无法关闭"); - } else { - log.error("关闭通联支付订单出错:{}", e.getMessage()); - } + // 调用关闭API + try { + payApi.closeByOutTradeNo(bill.getPayNo()); + } catch (Exception e) { + // 关闭失败,尝试查询订单信息,判断是否已经支付成功 + if (payApi.isPaySuccessByOutTradeNo(bill.getPayNo())) { + throw new ServiceException("当前交易已成功,无法关闭"); + } else { + log.error("关闭支付订单失败: payNo = {} 原因:{}", bill.getPayNo(), e.getMessage()); } } - // 太米微信 - else if (TransactionBillPayType.TM_WX.getType().equals(bill.getChannelId())) { - tmPayService.closeOrder(bill.getPayNo()); - } } } @@ -341,15 +339,9 @@ public class PayBillServiceImpl implements PayBillService this.handleSuccess(bill.getPayNo(), LocalDateTime.now()); }, 1L, TimeUnit.SECONDS); } else { - if (TransactionBillPayType.WECHAT.getType().equals(bill.getChannelId())) { - vo.setPayParams(wxPayService.prepayWithRequestPayment(bill)); - } else if (TransactionBillPayType.TL_WX.getType().equals(bill.getChannelId())) { - vo.setPayParams(sybPayService.prepayWxApp(bill)); - } else if (TransactionBillPayType.TM_WX.getType().equals(bill.getChannelId())) { - vo.setPayParams(tmPayService.wxPay(bill)); - } else { - throw new ServiceException("暂不支持该支付方式"); - } + PayApi payApi = ChannelEnum.getPayApi(bill.getChannelId()); + ServiceUtil.assertion(payApi == null, "暂不支持该支付方式"); + vo.setPayParams(payApi.pay(bill)); // 异步刷新支付结果 scheduledExecutorService.schedule(() -> { this.refreshPayResultMaxCount(bill, 20L, TimeUnit.SECONDS, 10); @@ -524,33 +516,18 @@ public class PayBillServiceImpl implements PayBillService return PayResultVO.success(LocalDateTime.now()); } - // 查询第三方支付渠道 - if (TransactionBillPayType.WECHAT.getType().equals(bill.getChannelId())) { - // 微信支付 - Transaction transaction = wxPayService.queryOrderByOutTradeNo(bill.getPayNo()); - boolean success = WxPayUtil.isSuccess(transaction); - if (success) { - return PayResultVO.success(DateUtils.toLocalDateTimeByISO(transaction.getSuccessTime()), transaction); - } else { - return PayResultVO.fail("暂未支付成功"); - } - } else if (TransactionBillPayType.TL_WX.getType().equals(bill.getChannelId())) { - // 通联微信支付 - Map result = sybPayService.queryOrderByOutTradeNo(bill.getPayNo()); - if (SybTrxStatus.isSuccess(result)) { - return PayResultVO.success(DateUtils.toLocalDate(result.get("fintime"), "yyyyMMddHHmmss"), result); - } else { - return PayResultVO.fail("暂未支付成功"); - } - } else if (TransactionBillPayType.TM_WX.getType().equals(bill.getChannelId())) { - // 太米微信支付 - TmTradeInfo result = tmPayService.orderQuery(bill.getPayNo()); - if (result != null && TmPayStatus.isSuccess(result.getPayStatus())) { - return PayResultVO.success(DateUtils.toLocalDateTime(result.getPayTime(), "yyyy-MM-dd HH:mm:ss"), result); // TODO 支付时间 - } else { - return PayResultVO.fail("暂未支付成功"); - } + // 获取支付API + PayApi payApi = ChannelEnum.getPayApi(bill.getChannelId()); + if (payApi == null) { + return PayResultVO.fail("暂不支持该支付方式"); + } + + // 获取支付结果 + Object result = payApi.queryByOutTradeNo(bill.getPayNo()); + if (payApi.isPaySuccess(result)) { + return PayResultVO.success(payApi.getPayTime(result), result); + } else { + return PayResultVO.fail("暂未支付成功"); } - return PayResultVO.fail("暂不支持该支付方式"); } } diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/refund/domain/RefundVO.java b/smart-switch-service/src/main/java/com/ruoyi/ss/refund/domain/RefundVO.java index 930c4578..73d3d956 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/refund/domain/RefundVO.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/refund/domain/RefundVO.java @@ -1,6 +1,6 @@ package com.ruoyi.ss.refund.domain; -import com.ruoyi.common.pay.wx.domain.RefundAble; +import com.ruoyi.common.pay.Refundable; import com.wechat.pay.java.service.refund.model.AmountReq; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -12,7 +12,7 @@ import java.math.BigDecimal; * 2024/7/9 */ @Data -public class RefundVO extends Refund implements RefundAble { +public class RefundVO extends Refund implements Refundable { @ApiModelProperty("原订单编号") private String payNo; diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/refund/service/impl/RefundServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/refund/service/impl/RefundServiceImpl.java index 7241f94e..29c5b593 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/refund/service/impl/RefundServiceImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/refund/service/impl/RefundServiceImpl.java @@ -2,6 +2,7 @@ package com.ruoyi.ss.refund.service.impl; import com.ruoyi.common.domain.vo.LocalDateDecimalVO; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.pay.PayApi; import com.ruoyi.common.pay.syb.enums.SybTrxStatus; import com.ruoyi.common.pay.syb.service.SybPayService; import com.ruoyi.common.pay.tm.TmPayService; @@ -18,14 +19,13 @@ import com.ruoyi.ss.refund.domain.RefundVO; import com.ruoyi.ss.refund.domain.enums.RefundStatus; import com.ruoyi.ss.refund.mapper.RefundMapper; import com.ruoyi.ss.refund.service.RefundService; -import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillPayType; +import com.ruoyi.ss.channel.domain.enums.ChannelEnum; import com.ruoyi.ss.transactionBill.service.TransactionBillService; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; @@ -193,38 +193,22 @@ public class RefundServiceImpl implements RefundService int insertRefund = this.insertRefund(refund); ServiceUtil.assertion(insertRefund != 1, "创建退款订单失败"); - RefundVO refundVO = this.selectRefundByRefundNo(refund.getRefundNo()); + RefundVO vo = this.selectRefundByRefundNo(refund.getRefundNo()); // 发起退款 - // 微信 - if (TransactionBillPayType.WECHAT.getType().equals(refund.getChannelId())) { - wxPayService.refund(refundVO); - } - // 通联微信 - else if (TransactionBillPayType.TL_WX.getType().equals(refund.getChannelId())) { - Map refundResult = sybPayService.refundWx(refundVO); - String trxStatus = refundResult.get("trxstatus"); - // 当状态不为空,则判断是否处理退款成功 - if (trxStatus != null) { - ServiceUtil.assertion(!SybTrxStatus.isSuccess(trxStatus), "发起退款失败:" + refundResult.get("errmsg")); - // 通联退款是同步通知,直接处理退款成功 - scheduledExecutorService.schedule(() -> { - this.handleRefundSuccess(refund.getRefundNo()); - }, 0, TimeUnit.SECONDS); - } - } - // 太米支付 - else if (TransactionBillPayType.TM_WX.getType().equals(refund.getChannelId())) { - RefundInfo refundResult = tmPayService.refund(refundVO); - ServiceUtil.assertion(!TmRefundStatus.isSuccess(refundResult.getRefundStatus().getCode()), "发起退款失败:" + refundResult.getRefundMessage()); - // 太米退款是同步通知,直接处理退款成功 - scheduledExecutorService.schedule(() -> { - handleRefundSuccess(refund.getRefundNo()); - }, 10, TimeUnit.SECONDS); - } - else { + PayApi payApi = ChannelEnum.getPayApi(refund.getChannelId()); + if (payApi == null) { throw new ServiceException("当前支付方式不支持退款"); } + payApi.refund(vo); + + // 判断是否同步通知,若是则直接处理支付成功 + ChannelEnum channelEnum = ChannelEnum.parse(refund.getChannelId()); + if (channelEnum != null && channelEnum.getIsRefundSync() != null && channelEnum.getIsRefundSync()) { + scheduledExecutorService.schedule(() -> { + this.handleRefundSuccess(refund.getRefundNo()); + }, 10, TimeUnit.SECONDS); + } return insertRefund; }); @@ -248,16 +232,16 @@ public class RefundServiceImpl implements RefundService } // 通联微信 - if (TransactionBillPayType.TL_WX.getType().equals(refund.getChannelId())) { - Map params = sybPayService.queryOrderByOutTradeNo(refund.getRefundNo()); - String trxStatus = params.get("trxstatus"); - if (SybTrxStatus.isSuccess(trxStatus)) { - // 退款成功 - scheduledExecutorService.schedule(() -> { - this.handleRefundSuccess(refund.getRefundNo()); - }, 0, TimeUnit.SECONDS); - } - } +// if (ChannelEnum.TL_WX.getType().equals(refund.getChannelId())) { +// Map params = sybPayService.queryByOutTradeNo(refund.getRefundNo()); +// String trxStatus = params.get("trxstatus"); +// if (SybTrxStatus.isSuccess(trxStatus)) { +// // 退款成功 +// scheduledExecutorService.schedule(() -> { +// this.handleRefundSuccess(refund.getRefundNo()); +// }, 0, TimeUnit.SECONDS); +// } +// } return 1; } diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/TransactionBill.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/TransactionBill.java index d302ef10..792164c3 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/TransactionBill.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/TransactionBill.java @@ -8,7 +8,7 @@ import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.JsonViewProfile; import com.ruoyi.common.core.domain.ValidGroup; import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillType; -import com.ruoyi.common.pay.wx.domain.Payable; +import com.ruoyi.common.pay.Payable; import com.ruoyi.common.pay.wx.domain.enums.AttachEnums; import com.ruoyi.system.valid.DictValid; import io.swagger.annotations.ApiModelProperty; @@ -336,7 +336,7 @@ public class TransactionBill extends BaseEntity implements Payable * 获取价格(分) */ @Override - public BigDecimal payableMoney() { + public BigDecimal payableFen() { return money.multiply(BigDecimal.valueOf(100)); } diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/enums/TransactionBillPayType.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/enums/TransactionBillPayType.java deleted file mode 100644 index d634632c..00000000 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/enums/TransactionBillPayType.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.ruoyi.ss.transactionBill.domain.enums; - -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.ss.account.domain.enums.AccountType; -import lombok.AllArgsConstructor; -import lombok.Getter; -import nonapi.io.github.classgraph.utils.LogNode; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -/** - * 支付方式(打款方式) - * @author 辉 - * 2024/3/12 - */ -@Getter -@AllArgsConstructor -public enum TransactionBillPayType { - - WECHAT(1L, "微信支付", AccountType.WECHAT), - ALI(2L, "支付宝", AccountType.ALIPAY), - BANK(3L, "银行卡", AccountType.BANK_CARD), - BALANCE(4L, "余额支付", null), - TL_WX(5L, "通联微信支付", null), - TM_WX(6L, "太米微信支付", null); - - private final Long type; - private final String name; - private final AccountType accountType; - - public static TransactionBillPayType parse(Long type) { - for (TransactionBillPayType value : TransactionBillPayType.values()) { - if (Objects.equals(value.getType(), type)) { - return value; - } - } - throw new ServiceException("不存在值为" + type + "的支付方式(打款方式)"); - } - - public static List asList(TransactionBillPayType ...types) { - return Arrays.stream(types).map(TransactionBillPayType::getType).collect(Collectors.toList()); - } - - /** - * 微信支付列表 - */ - public static List wxList() { - return asList(WECHAT, TL_WX, TM_WX); - } -} diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/vo/TransactionBillVO.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/vo/TransactionBillVO.java index 393dbb7a..a8ac0c30 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/vo/TransactionBillVO.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/vo/TransactionBillVO.java @@ -42,7 +42,7 @@ public class TransactionBillVO extends TransactionBill implements IotDevice { private String channelName; @ApiModelProperty("商户手机号") - @JsonView(JsonViewProfile.AppMch.class) + @JsonView(JsonViewProfile.App.class) private String mchMobile; @ApiModelProperty("设备总用电量") diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillConverterImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillConverterImpl.java index 044e45b9..144b49dc 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillConverterImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillConverterImpl.java @@ -100,7 +100,6 @@ public class TransactionBillConverterImpl implements TransactionBillConverter { SmUserVO user = userService.selectSmUserByUserId(SecurityUtils.getUserId()); ServiceUtil.assertion(user == null, "用户不存在"); - ServiceUtil.assertion(user.getWxOpenId() == null, "用户微信openId为空"); RechargeBO bo = new RechargeBO(); bo.setDevice(device); diff --git a/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppChannelController.java b/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppChannelController.java index 0132475e..aabe3d92 100644 --- a/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppChannelController.java +++ b/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppChannelController.java @@ -4,12 +4,13 @@ import com.fasterxml.jackson.annotation.JsonView; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.JsonViewProfile; +import com.ruoyi.ss.channel.domain.ChannelQuery; import com.ruoyi.ss.channel.service.ChannelService; -import com.ruoyi.ss.transactionBill.service.TransactionBillService; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** @@ -26,8 +27,10 @@ public class AppChannelController extends BaseController { @ApiOperation("获取充值渠道列表") @JsonView(JsonViewProfile.App.class) @GetMapping("/enabledList") - public AjaxResult getRechargeEnabledList() { - return success(channelService.selectEnabledRechargeList()); + public AjaxResult getRechargeEnabledList(@RequestParam(required = false, defaultValue = "1") String platform) { + ChannelQuery query = new ChannelQuery(); + query.setPlatform(platform); + return success(channelService.selectEnabledRechargeList(query)); } } diff --git a/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppPayController.java b/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppPayController.java index 241f3dee..3c0d017d 100644 --- a/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppPayController.java +++ b/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppPayController.java @@ -20,7 +20,7 @@ import com.ruoyi.common.utils.http.HttpUtils; import com.ruoyi.ss.payBill.service.PayBillService; import com.ruoyi.ss.refund.service.RefundService; import com.ruoyi.ss.transactionBill.domain.dto.RechargePayBO; -import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillPayType; +import com.ruoyi.ss.channel.domain.enums.ChannelEnum; import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO; import com.ruoyi.ss.transactionBill.service.TransactionBillConverter; import com.ruoyi.ss.transactionBill.service.TransactionBillService; @@ -41,7 +41,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; -import java.time.LocalDateTime; import java.util.Map; import java.util.TreeMap; @@ -80,7 +79,7 @@ public class AppPayController extends BaseController { if (bill == null || bill.getUserId() == null || !bill.getUserId().equals(getUserId())) { return error("这不是您的订单"); } - RechargePayBO bo = transactionBillConverter.toRechargePayBO(bill, TransactionBillPayType.WECHAT.getType()); + RechargePayBO bo = transactionBillConverter.toRechargePayBO(bill, ChannelEnum.WECHAT.getType()); return success(transactionBillService.pay(bo)); } diff --git a/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppTimeBillController.java b/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppTimeBillController.java index 010659a1..b9b01556 100644 --- a/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppTimeBillController.java +++ b/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppTimeBillController.java @@ -4,10 +4,8 @@ import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.ss.timeBill.domain.TimeBillVO; import com.ruoyi.ss.timeBill.domain.dto.TimeBillAddOrderDTO; -import com.ruoyi.ss.timeBill.domain.dto.TimeBillPayDTO; import com.ruoyi.ss.timeBill.service.TimeBillService; import com.ruoyi.ss.timeBill.service.TimeBillValidator; -import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillPayType; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; diff --git a/smart-switch-web/src/main/resources/application-dev.yml b/smart-switch-web/src/main/resources/application-dev.yml index 3550c385..bd28c426 100644 --- a/smart-switch-web/src/main/resources/application-dev.yml +++ b/smart-switch-web/src/main/resources/application-dev.yml @@ -148,3 +148,5 @@ ali: alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi1vuaEyUkCgagVodfOJp/rk1gwVzs8f/QzEXAEUwZZne+VE8be0rUv9SLY0uOjixanw0yhG9LinHJlePCvuK0Y31Cxx0BXgOt27nGTSqm4oINFYd5WL1vNMPzPE2gat+7ohO3h6FRlmsxsq9W5ZRkko+04Be4lEGZ+Ter/b2v3m4I3CzX6kr42e39QlDRUpD9l9ixpkmfEatdDf01Xp++Tvr/3EZcYoG3oPGztI7B8Kw8KV/6he3ZBlGROWz8ywZSBtR294Y1PRDv+3QXC3nr7J6OrTbnvj+MAtKmwjdkFHiFVr3gfenzeI87LnXrDPahda7Mn6ToQ1NU9tsWCcJgQIDAQAB # AES秘钥 aesPrivateKey: FGhCgOURrXkhGs36PUTHfg== + # 通知地址 + notifyUrl: http://124.221.246.124:2290/app/pay/notify/ali diff --git a/smart-switch-web/src/main/resources/application-prod.yml b/smart-switch-web/src/main/resources/application-prod.yml index fe958697..b9b5f225 100644 --- a/smart-switch-web/src/main/resources/application-prod.yml +++ b/smart-switch-web/src/main/resources/application-prod.yml @@ -148,3 +148,5 @@ ali: alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi1vuaEyUkCgagVodfOJp/rk1gwVzs8f/QzEXAEUwZZne+VE8be0rUv9SLY0uOjixanw0yhG9LinHJlePCvuK0Y31Cxx0BXgOt27nGTSqm4oINFYd5WL1vNMPzPE2gat+7ohO3h6FRlmsxsq9W5ZRkko+04Be4lEGZ+Ter/b2v3m4I3CzX6kr42e39QlDRUpD9l9ixpkmfEatdDf01Xp++Tvr/3EZcYoG3oPGztI7B8Kw8KV/6he3ZBlGROWz8ywZSBtR294Y1PRDv+3QXC3nr7J6OrTbnvj+MAtKmwjdkFHiFVr3gfenzeI87LnXrDPahda7Mn6ToQ1NU9tsWCcJgQIDAQAB # AES秘钥 aesPrivateKey: FGhCgOURrXkhGs36PUTHfg== + # 通知地址 + notifyUrl: https://kg.chuangtewl.com/prod-api/app/pay/notify/ali