From 7d423c8ed214e163e030ba0894611af818b1a542 Mon Sep 17 00:00:00 2001 From: YunaiV <> Date: Wed, 13 Mar 2019 20:27:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E4=BB=98=E6=A8=A1=E5=9D=97=EF=BC=8C?= =?UTF-8?q?=E8=B0=83=E9=80=9A=E5=AF=B9=20ping++=20=E7=9A=84=E8=B0=83?= =?UTF-8?q?=E7=94=A8=EF=BC=8C=E4=BB=A5=E5=AE=9E=E7=8E=B0=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当然,整体代码还是有点乱,后面花点时间重构下~ 现在,缺一个异步 MQ 通知业务方订单支付成功 --- common/common-framework/pom.xml | 6 ++ .../common/framework/util/DateUtil.java | 20 ++++ .../common/framework/util/MathUtil.java | 33 +++++++ .../controller/users/PayDemoController.java | 53 +++++++++- .../users/PayTransactionController.java | 41 +++++++- .../test/java/DubboGenericInvokerTest.java | 32 ++++++ .../iocoder/mall/pay/api/PayDemoService.java | 7 ++ .../mall/pay/api/PayTransactionService.java | 12 +++ .../pay/api/constant/PayErrorCodeEnum.java | 2 + pay/pay-service-impl/pom.xml | 1 - .../mall/pay/client/AbstractPaySDK.java | 3 + .../iocoder/mall/pay/client/PingxxPaySDK.java | 15 ++- .../pay/client/TransactionPaySuccessBO.java | 52 ++++++++++ .../config/ServiceExceptionConfiguration.java | 4 +- .../dao/PayTransactionExtensionMapper.java | 8 +- .../mall/pay/dao/PayTransactionMapper.java | 5 + .../dao/PayTransactionNotifyTaskMapper.java | 13 +++ .../PayTransactionNotifyTaskDO.java | 15 ++- .../mall/pay/service/PayDemoServiceImpl.java | 15 +++ .../mall/pay/service/PayServiceImpl.java | 97 ++++++++++++++++++- .../main/resources/mapper/PayAppMapper.xml | 4 +- .../mapper/PayTransactionExtensionMapper.xml | 44 +++++++++ .../resources/mapper/PayTransactionMapper.xml | 69 +++++++++++++ .../mapper/PayTransactionNotifyTaskMapper.xml | 44 +++++++++ 24 files changed, 579 insertions(+), 16 deletions(-) create mode 100644 common/common-framework/src/main/java/cn/iocoder/common/framework/util/DateUtil.java create mode 100644 common/common-framework/src/main/java/cn/iocoder/common/framework/util/MathUtil.java create mode 100644 pay/pay-application/src/test/java/DubboGenericInvokerTest.java create mode 100644 pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/PayDemoService.java create mode 100644 pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/TransactionPaySuccessBO.java create mode 100644 pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionNotifyTaskMapper.java create mode 100644 pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/service/PayDemoServiceImpl.java create mode 100644 pay/pay-service-impl/src/main/resources/mapper/PayTransactionExtensionMapper.xml create mode 100644 pay/pay-service-impl/src/main/resources/mapper/PayTransactionMapper.xml create mode 100644 pay/pay-service-impl/src/main/resources/mapper/PayTransactionNotifyTaskMapper.xml diff --git a/common/common-framework/pom.xml b/common/common-framework/pom.xml index d8f9e472f..52d055f6b 100644 --- a/common/common-framework/pom.xml +++ b/common/common-framework/pom.xml @@ -55,6 +55,12 @@ + + com.alibaba + fastjson + 1.2.56 + + diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DateUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DateUtil.java new file mode 100644 index 000000000..d5736e45e --- /dev/null +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/DateUtil.java @@ -0,0 +1,20 @@ +package cn.iocoder.common.framework.util; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class DateUtil { + + /** + * @param date 时间。若为空,则返回空串 + * @param pattern 时间格式化 + * @return 格式化后的时间字符串. + */ + public static String format(Date date, String pattern) { + if (date == null) { + return ""; + } + return new SimpleDateFormat(pattern).format(date); + } + +} \ No newline at end of file diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MathUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MathUtil.java new file mode 100644 index 000000000..4aafa732e --- /dev/null +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MathUtil.java @@ -0,0 +1,33 @@ +package cn.iocoder.common.framework.util; + +import java.util.Random; + +public class MathUtil { + + /** + * 随机对象 + */ + private static final Random RANDOM = new Random(); // TODO 后续优化 + + /** + * 随机[min, max]范围内的数字 + * + * @param min 随机开始 + * @param max 随机结束 + * @return 数字 + */ + public static int random(int min, int max) { + if (min == max) { + return min; + } + if (min > max) { + int temp = min; + min = max; + max = temp; + } + // 随即开始 + int diff = max - min + 1; + return RANDOM.nextInt(diff) + min; + } + +} diff --git a/pay/pay-application/src/main/java/cn/iocoder/mall/pay/application/controller/users/PayDemoController.java b/pay/pay-application/src/main/java/cn/iocoder/mall/pay/application/controller/users/PayDemoController.java index 5af8b5736..71ae866d6 100644 --- a/pay/pay-application/src/main/java/cn/iocoder/mall/pay/application/controller/users/PayDemoController.java +++ b/pay/pay-application/src/main/java/cn/iocoder/mall/pay/application/controller/users/PayDemoController.java @@ -31,7 +31,7 @@ public class PayDemoController { // 调用【支付服务】,创建交易订单 PayTransactionCreateDTO payTransactionCreateDTO = new PayTransactionCreateDTO() - .setAppId("1024") + .setAppId("POd4RC6a") .setCreateIp(HttpUtil.getIp(request)) .setOrderId("1") .setOrderSubject("商品名" ) @@ -43,4 +43,55 @@ public class PayDemoController { Assert.isTrue(result.isSuccess(), "一定会成功的"); } + @PostMapping("/pingxx") + public String pingxx() { + return "{\n" + + " \"id\": \"ch_n5COmHGG8iX5TWf5qDynvTaP\",\n" + + " \"object\": \"charge\",\n" + + " \"created\": 1552445643,\n" + + " \"livemode\": false,\n" + + " \"paid\": false,\n" + + " \"refunded\": false,\n" + + " \"reversed\": false,\n" + + " \"app\": \"app_aTyfXDjrvzDSbLuz\",\n" + + " \"channel\": \"wx_pub\",\n" + + " \"order_no\": \"1552445643093\",\n" + + " \"client_ip\": \"127.0.0.1\",\n" + + " \"amount\": 1,\n" + + " \"amount_settle\": 1,\n" + + " \"currency\": \"cny\",\n" + + " \"subject\": \"测试商品\",\n" + + " \"body\": \"测试描述\",\n" + + " \"time_paid\": null,\n" + + " \"time_expire\": 1552452843,\n" + + " \"time_settle\": null,\n" + + " \"transaction_no\": null,\n" + + " \"refunds\": {\n" + + " \"object\": \"list\",\n" + + " \"url\": \"/v1/charges/ch_n5COmHGG8iX5TWf5qDynvTaP/refunds\",\n" + + " \"has_more\": false,\n" + + " \"data\": []\n" + + " },\n" + + " \"amount_refunded\": 0,\n" + + " \"failure_code\": null,\n" + + " \"failure_msg\": null,\n" + + " \"metadata\": {},\n" + + " \"credential\": {\n" + + " \"object\": \"credential\",\n" + + " \"wx_pub\": {\n" + + " \"appId\": \"wxytom5krtuf54qjff\",\n" + + " \"timeStamp\": \"1552445643\",\n" + + " \"nonceStr\": \"5cc0206f78d8bf931980f5132d5ce394\",\n" + + " \"package\": \"prepay_id=1101000000190313rx9y5oahkkcsm5gg\",\n" + + " \"signType\": \"MD5\",\n" + + " \"paySign\": \"9F6E80E89439575B8414FA56ADB07228\"\n" + + " }\n" + + " },\n" + + " \"extra\": {\n" + + " \"open_id\": \"just_for_test\"\n" + + " },\n" + + " \"description\": null\n" + + "}"; + } + } \ No newline at end of file diff --git a/pay/pay-application/src/main/java/cn/iocoder/mall/pay/application/controller/users/PayTransactionController.java b/pay/pay-application/src/main/java/cn/iocoder/mall/pay/application/controller/users/PayTransactionController.java index e1285f418..bd3b25e44 100644 --- a/pay/pay-application/src/main/java/cn/iocoder/mall/pay/application/controller/users/PayTransactionController.java +++ b/pay/pay-application/src/main/java/cn/iocoder/mall/pay/application/controller/users/PayTransactionController.java @@ -1,12 +1,22 @@ package cn.iocoder.mall.pay.application.controller.users; +import cn.iocoder.common.framework.util.HttpUtil; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.pay.api.PayTransactionService; +import cn.iocoder.mall.pay.api.bo.PayTransactionSubmitBO; +import cn.iocoder.mall.pay.api.constant.PayChannelEnum; +import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO; import com.alibaba.dubbo.config.annotation.Reference; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; 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.io.BufferedReader; +import java.io.IOException; + @RestController @RequestMapping("users/transaction") // TODO 芋艿,理论来说,是用户无关的。这里先酱紫先~ public class PayTransactionController { @@ -15,8 +25,35 @@ public class PayTransactionController { private PayTransactionService payService; @PostMapping("/submit") // TODO api 注释 - public CommonResult submit() { // TODO 1. params 2. result - return null; + // TODO result 后面改下 + public CommonResult submit(HttpServletRequest request, + @RequestParam("appId") String appId, + @RequestParam("orderId") String orderId, + @RequestParam("payChannel") Integer payChannel) { + PayTransactionSubmitDTO payTransactionSubmitDTO = new PayTransactionSubmitDTO() + .setAppId(appId).setOrderId(orderId).setPayChannel(payChannel) + .setCreateIp(HttpUtil.getIp(request)); + // 提交支付提交 + return payService.submitTransaction(payTransactionSubmitDTO); + } + + @PostMapping(value = "pingxx_pay_success", consumes = MediaType.APPLICATION_JSON_VALUE) +// @GetMapping(value = "pingxx_pay_success") + public String pingxxSuccess(HttpServletRequest request) throws IOException { + // 读取 webhook + StringBuilder sb = new StringBuilder(); + try (BufferedReader reader = request.getReader()) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } + +// JSONObject bodyObj = JSON.parseObject(sb.toString()); +// bodyObj.put("webhookId", bodyObj.remove("id")); +// String body = bodyObj.toString(); + payService.updateTransactionPaySuccess(PayChannelEnum.PINGXX.getId(), sb.toString()); + return ""; } } \ No newline at end of file diff --git a/pay/pay-application/src/test/java/DubboGenericInvokerTest.java b/pay/pay-application/src/test/java/DubboGenericInvokerTest.java new file mode 100644 index 000000000..b46a5f4d0 --- /dev/null +++ b/pay/pay-application/src/test/java/DubboGenericInvokerTest.java @@ -0,0 +1,32 @@ +import com.alibaba.dubbo.config.ApplicationConfig; +import com.alibaba.dubbo.config.ReferenceConfig; +import com.alibaba.dubbo.config.RegistryConfig; +import com.alibaba.dubbo.rpc.service.GenericService; + +public class DubboGenericInvokerTest { + + public static void main(String[] args) { + ApplicationConfig application = new ApplicationConfig(); + application.setName("api-generic-consumer"); + + RegistryConfig registry = new RegistryConfig(); + registry.setAddress("zookeeper://127.0.0.1:2181"); + + application.setRegistry(registry); + + ReferenceConfig reference = new ReferenceConfig<>(); + // 弱类型接口名 + reference.setInterface("cn.iocoder.mall.pay.api.PayDemoService"); + // 声明为泛化接口 + reference.setGeneric(true); + + reference.setApplication(application); + + // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用 + GenericService genericService = reference.get(); + + String name = (String) genericService.$invoke("updatePaySuccess", new String[]{String.class.getName()}, new Object[]{"1"}); + System.out.println(name); + } + +} \ No newline at end of file diff --git a/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/PayDemoService.java b/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/PayDemoService.java new file mode 100644 index 000000000..77a500cad --- /dev/null +++ b/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/PayDemoService.java @@ -0,0 +1,7 @@ +package cn.iocoder.mall.pay.api; + +public interface PayDemoService { + + String updatePaySuccess(String orderId); + +} \ No newline at end of file diff --git a/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/PayTransactionService.java b/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/PayTransactionService.java index 51700b443..0e68a4dfa 100644 --- a/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/PayTransactionService.java +++ b/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/PayTransactionService.java @@ -12,6 +12,18 @@ public interface PayTransactionService { CommonResult submitTransaction(PayTransactionSubmitDTO payTransactionSubmitDTO); + /** + * 更新交易支付成功 + * + * 该接口用于不同支付平台,支付成功后,回调该接口 + * + * @param payChannel 支付渠道 + * @param params 回调参数。 + * 因为不同平台,能够提供的参数不同,所以使用 String 类型统一接收,然后在使用不同的 AbstractPaySDK 进行处理。 + * @return 是否支付成功 + */ + CommonResult updateTransactionPaySuccess(Integer payChannel, String params); + CommonResult cancelTransaction(); // TODO 1. params 2. result } \ No newline at end of file diff --git a/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/constant/PayErrorCodeEnum.java b/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/constant/PayErrorCodeEnum.java index 1d60cd387..c012a0cf6 100644 --- a/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/constant/PayErrorCodeEnum.java +++ b/pay/pay-service-api/src/main/java/cn/iocoder/mall/pay/api/constant/PayErrorCodeEnum.java @@ -15,6 +15,8 @@ public enum PayErrorCodeEnum { PAY_TRANSACTION_NOT_FOUND(100401000, "支付交易单不存在"), PAY_TRANSACTION_STATUS_IS_NOT_WAITING(100401001, "支付交易单不处于待支付"), + PAY_TRANSACTION_EXTENSION_NOT_FOUND(100401002, "支付交易拓展单不存在"), + PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING(100401003, "支付交易拓展单不处于待支付"), ; private final int code; diff --git a/pay/pay-service-impl/pom.xml b/pay/pay-service-impl/pom.xml index 9513972f2..0cc2d3487 100644 --- a/pay/pay-service-impl/pom.xml +++ b/pay/pay-service-impl/pom.xml @@ -19,7 +19,6 @@ com.alibaba dubbo - compile cn.iocoder.mall diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/AbstractPaySDK.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/AbstractPaySDK.java index 8be86179b..ea2d25e35 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/AbstractPaySDK.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/AbstractPaySDK.java @@ -13,4 +13,7 @@ public abstract class AbstractPaySDK { PayTransactionExtensionDO transactionExtension, Map extra); + // TODO 芋艿,理论来说不会出现解析失败的情况,先返回这个参数列。等后面封装支付宝和微信支付的时候,在看看。 + public abstract CommonResult parseTransactionPaySuccessParams(String params); + } \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/PingxxPaySDK.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/PingxxPaySDK.java index 9c2e887b9..74d838a2e 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/PingxxPaySDK.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/PingxxPaySDK.java @@ -3,11 +3,14 @@ package cn.iocoder.mall.pay.client; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.pay.dataobject.PayTransactionDO; import cn.iocoder.mall.pay.dataobject.PayTransactionExtensionDO; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.google.common.collect.ImmutableMap; import com.pingplusplus.Pingpp; import com.pingplusplus.exception.*; import com.pingplusplus.model.Charge; +import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -25,7 +28,7 @@ public class PingxxPaySDK extends AbstractPaySDK { // 请求ping++ try { Charge charge = Charge.create(reqObj); -// System.out.println(charge.toString()); + System.out.println(charge.toString()); return CommonResult.success(charge.toString()); } catch (AuthenticationException e) { e.printStackTrace(); @@ -43,6 +46,16 @@ public class PingxxPaySDK extends AbstractPaySDK { return null; } + @Override + public CommonResult parseTransactionPaySuccessParams(String params) { + JSONObject paramsObj = JSON.parseObject(params); + JSONObject chargeObj = paramsObj.getJSONObject("data").getJSONObject("object"); + TransactionPaySuccessBO transactionPaySuccessBO = new TransactionPaySuccessBO() + .setTransactionCode(chargeObj.getString("order_no")) + .setPaymentTime(new Date(chargeObj.getLong("time_paid") * 1000)) + .setTradeNo(chargeObj.getString("transaction_no")); + return CommonResult.success(transactionPaySuccessBO); + } private Map createChargeRequest(PayTransactionDO transaction, PayTransactionExtensionDO transactionExtension, Map extra) { // 计算支付渠道和支付额外参数 diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/TransactionPaySuccessBO.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/TransactionPaySuccessBO.java new file mode 100644 index 000000000..97b420b57 --- /dev/null +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/client/TransactionPaySuccessBO.java @@ -0,0 +1,52 @@ +package cn.iocoder.mall.pay.client; + +import java.util.Date; + +/** + * 交易支付成功结果 + */ +public class TransactionPaySuccessBO { + + /** + * 生成传输给第三方的订单号 + * + * 唯一索引 + */ + private String transactionCode; + /** + * 第三方的流水号 + */ + private String tradeNo; + /** + * 第三方支付成功的时间 + */ + private Date paymentTime; + + public String getTransactionCode() { + return transactionCode; + } + + public TransactionPaySuccessBO setTransactionCode(String transactionCode) { + this.transactionCode = transactionCode; + return this; + } + + public String getTradeNo() { + return tradeNo; + } + + public TransactionPaySuccessBO setTradeNo(String tradeNo) { + this.tradeNo = tradeNo; + return this; + } + + public Date getPaymentTime() { + return paymentTime; + } + + public TransactionPaySuccessBO setPaymentTime(Date paymentTime) { + this.paymentTime = paymentTime; + return this; + } + +} \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/config/ServiceExceptionConfiguration.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/config/ServiceExceptionConfiguration.java index 28f5be277..f057ffbc7 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/config/ServiceExceptionConfiguration.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/config/ServiceExceptionConfiguration.java @@ -1,7 +1,7 @@ package cn.iocoder.mall.pay.config; import cn.iocoder.common.framework.util.ServiceExceptionUtil; -import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; +import cn.iocoder.mall.pay.api.constant.PayErrorCodeEnum; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; @@ -18,7 +18,7 @@ public class ServiceExceptionConfiguration { // } catch (IOException e) { // throw new RuntimeException(e); // } - for (AdminErrorCodeEnum item : AdminErrorCodeEnum.values()) { + for (PayErrorCodeEnum item : PayErrorCodeEnum.values()) { ServiceExceptionUtil.put(item.getCode(), item.getMessage()); } } diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionExtensionMapper.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionExtensionMapper.java index 118204e3f..128e8c042 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionExtensionMapper.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionExtensionMapper.java @@ -1,11 +1,17 @@ package cn.iocoder.mall.pay.dao; import cn.iocoder.mall.pay.dataobject.PayTransactionExtensionDO; +import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface PayTransactionExtensionMapper { - void insert(PayTransactionExtensionDO payTransactionExtensionDO); + void insert(PayTransactionExtensionDO entity); + + int update(@Param("entity") PayTransactionExtensionDO entity, + @Param("whereStatus") Integer whereStatus); + + PayTransactionExtensionDO selectByTransactionCode(@Param("transactionCode") String transactionCode); } \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionMapper.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionMapper.java index 0e59255ac..028daec61 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionMapper.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionMapper.java @@ -9,7 +9,12 @@ public interface PayTransactionMapper { void insert(PayTransactionDO entity); + int update(@Param("entity") PayTransactionDO entity, + @Param("whereStatus") Integer whereStatus); + PayTransactionDO selectByAppIdAndOrderId(@Param("appId") String appId, @Param("orderId") String orderId); + PayTransactionDO selectById(@Param("id") Integer appId); + } \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionNotifyTaskMapper.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionNotifyTaskMapper.java new file mode 100644 index 000000000..cb6097010 --- /dev/null +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dao/PayTransactionNotifyTaskMapper.java @@ -0,0 +1,13 @@ +package cn.iocoder.mall.pay.dao; + +import cn.iocoder.mall.pay.dataobject.PayTransactionNotifyTaskDO; +import org.springframework.stereotype.Repository; + +@Repository +public interface PayTransactionNotifyTaskMapper { + + void insert(PayTransactionNotifyTaskDO entity); + + int update(PayTransactionNotifyTaskDO entity); + +} \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dataobject/PayTransactionNotifyTaskDO.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dataobject/PayTransactionNotifyTaskDO.java index d4c4364eb..5ed6d2253 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dataobject/PayTransactionNotifyTaskDO.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/dataobject/PayTransactionNotifyTaskDO.java @@ -51,9 +51,10 @@ public class PayTransactionNotifyTaskDO extends BaseDO { * 最大可通知次数 */ private Integer maxNotifyTimes; - - // TODO notify url - + /** + * 通知地址 + */ + private String notifyUrl; public Integer getTransactionId() { return transactionId; @@ -136,4 +137,12 @@ public class PayTransactionNotifyTaskDO extends BaseDO { return this; } + public String getNotifyUrl() { + return notifyUrl; + } + + public PayTransactionNotifyTaskDO setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + return this; + } } \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/service/PayDemoServiceImpl.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/service/PayDemoServiceImpl.java new file mode 100644 index 000000000..b60fd451c --- /dev/null +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/service/PayDemoServiceImpl.java @@ -0,0 +1,15 @@ +package cn.iocoder.mall.pay.service; + +import cn.iocoder.mall.pay.api.PayDemoService; +import org.springframework.stereotype.Service; + +@Service +@com.alibaba.dubbo.config.annotation.Service +public class PayDemoServiceImpl implements PayDemoService { + + @Override + public String updatePaySuccess(String orderId) { + return "你好呀"; + } + +} \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/service/PayServiceImpl.java b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/service/PayServiceImpl.java index b265c97b1..5893010a9 100644 --- a/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/service/PayServiceImpl.java +++ b/pay/pay-service-impl/src/main/java/cn/iocoder/mall/pay/service/PayServiceImpl.java @@ -1,25 +1,33 @@ package cn.iocoder.mall.pay.service; +import cn.iocoder.common.framework.util.DateUtil; +import cn.iocoder.common.framework.util.MathUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.pay.api.PayTransactionService; import cn.iocoder.mall.pay.api.bo.PayTransactionBO; import cn.iocoder.mall.pay.api.bo.PayTransactionSubmitBO; import cn.iocoder.mall.pay.api.constant.PayErrorCodeEnum; +import cn.iocoder.mall.pay.api.constant.PayTransactionNotifyStatusEnum; import cn.iocoder.mall.pay.api.constant.PayTransactionStatusEnum; import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO; import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO; +import cn.iocoder.mall.pay.client.AbstractPaySDK; import cn.iocoder.mall.pay.client.PaySDKFactory; +import cn.iocoder.mall.pay.client.TransactionPaySuccessBO; import cn.iocoder.mall.pay.convert.PayTransactionConvert; import cn.iocoder.mall.pay.dao.PayTransactionExtensionMapper; import cn.iocoder.mall.pay.dao.PayTransactionMapper; +import cn.iocoder.mall.pay.dao.PayTransactionNotifyTaskMapper; import cn.iocoder.mall.pay.dataobject.PayAppDO; import cn.iocoder.mall.pay.dataobject.PayTransactionDO; import cn.iocoder.mall.pay.dataobject.PayTransactionExtensionDO; +import cn.iocoder.mall.pay.dataobject.PayTransactionNotifyTaskDO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Date; @@ -34,6 +42,8 @@ public class PayServiceImpl implements PayTransactionService { @Autowired private PayTransactionExtensionMapper payTransactionExtensionMapper; @Autowired + private PayTransactionNotifyTaskMapper payTransactionNotifyTaskMapper; + @Autowired private PayAppServiceImpl payAppService; @Override @@ -77,17 +87,18 @@ public class PayServiceImpl implements PayTransactionService { if (payTransaction == null) { // 是否存在 return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode()); } - if (PayTransactionStatusEnum.WAITTING.getValue().equals(payTransaction.getStatus())) { // 校验状态 + if (!PayTransactionStatusEnum.WAITTING.getValue().equals(payTransaction.getStatus())) { // 校验状态,必须是待支付 return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode()); } // 插入 PayTransactionExtensionDO PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(payTransactionSubmitDTO) .setTransactionId(payTransaction.getId()) - .setTransactionCode("TODO") + .setTransactionCode(generateTransactionCode()) .setStatus(PayTransactionStatusEnum.WAITTING.getValue()); payTransactionExtensionMapper.insert(payTransactionExtensionDO); // 调用三方接口 - CommonResult invokeResult = PaySDKFactory.getSDK(payTransactionSubmitDTO.getPayChannel()).submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null + AbstractPaySDK paySDK = PaySDKFactory.getSDK(payTransactionSubmitDTO.getPayChannel()); + CommonResult invokeResult = paySDK.submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null if (invokeResult.isError()) { return CommonResult.error(invokeResult); } @@ -99,8 +110,88 @@ public class PayServiceImpl implements PayTransactionService { } @Override + @Transactional + public CommonResult updateTransactionPaySuccess(Integer payChannel, String params) { + // TODO 芋艿,记录回调日志 + // 解析传入的参数,成 TransactionPaySuccessBO 对象 + AbstractPaySDK paySDK = PaySDKFactory.getSDK(payChannel); + CommonResult paySuccessResult = paySDK.parseTransactionPaySuccessParams(params); + if (paySuccessResult.isError()) { + return CommonResult.error(paySuccessResult); + } + // TODO 芋艿,先最严格的校验。即使调用方重复调用,实际哪个订单已经被重复回调的支付,也返回 false 。也没问题,因为实际已经回调成功了。 + // 1.1 查询 PayTransactionExtensionDO + PayTransactionExtensionDO payTransactionExtension = payTransactionExtensionMapper.selectByTransactionCode(paySuccessResult.getData().getTransactionCode()); + if (payTransactionExtension == null) { + return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_NOT_FOUND.getCode()); + } + if (!PayTransactionStatusEnum.WAITTING.getValue().equals(payTransactionExtension.getStatus())) { // 校验状态,必须是待支付 + return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING.getCode()); + } + // 1.2 更新 PayTransactionExtensionDO + PayTransactionExtensionDO updatePayTransactionExtension = new PayTransactionExtensionDO() + .setId(payTransactionExtension.getId()) + .setStatus(PayTransactionStatusEnum.SUCCESS.getValue()) + .setExtensionData(params); + int updateCounts = payTransactionExtensionMapper.update(updatePayTransactionExtension, PayTransactionStatusEnum.WAITTING.getValue()); + if (updateCounts == 0) { // 校验状态,必须是待支付 + throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_EXTENSION_STATUS_IS_NOT_WAITING.getCode()); + } + // 2.1 + PayTransactionDO payTransactionDO = payTransactionMapper.selectById(payTransactionExtension.getTransactionId()); + if (payTransactionDO == null) { + return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode()); + } + if (!PayTransactionStatusEnum.WAITTING.getValue().equals(payTransactionDO.getStatus())) { // 校验状态,必须是待支付 + throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode()); + } + // 2.2 更新 PayTransactionDO + PayTransactionDO updatePayTransaction = new PayTransactionDO() + .setId(payTransactionDO.getId()) + .setStatus(PayTransactionStatusEnum.SUCCESS.getValue()) + .setExtensionId(payTransactionExtension.getId()) + .setPayChannel(payChannel) + .setPaymentTime(paySuccessResult.getData().getPaymentTime()) + .setNotifyTime(new Date()) + .setTradeNo(paySuccessResult.getData().getTradeNo()); + updateCounts = payTransactionMapper.update(updatePayTransaction, PayTransactionStatusEnum.WAITTING.getValue()); + if (updateCounts == 0) { // 校验状态,必须是待支付 TODO 这种类型,需要思考下。需要返回错误,但是又要保证事务回滚 + throw ServiceExceptionUtil.exception(PayErrorCodeEnum.PAY_TRANSACTION_STATUS_IS_NOT_WAITING.getCode()); + } + // 3. 插入 + PayTransactionNotifyTaskDO payTransactionNotifyTask = new PayTransactionNotifyTaskDO() + .setTransactionId(payTransactionExtension.getTransactionId()).setTransactionExtensionId(payTransactionExtension.getId()) + .setAppId(payTransactionDO.getAppId()).setOrderId(payTransactionDO.getOrderId()) + .setStatus(PayTransactionNotifyStatusEnum.WAITING.getValue()) + .setNotifyTimes(0).setMaxNotifyTimes(5) + .setNotifyUrl(payTransactionDO.getNotifyUrl()); + payTransactionNotifyTaskMapper.insert(payTransactionNotifyTask); + // 返回结果 + return CommonResult.success(true); + } + + @Override // TODO 芋艿,后面去实现 public CommonResult cancelTransaction() { return null; } + private String generateTransactionCode() { +// wx +// 2014 +// 10 +// 27 +// 20 +// 09 +// 39 +// 5522657 +// a690389285100 + // 目前的算法 + // 时间序列,年月日时分秒 14 位 + // 纯随机,6 位 TODO 此处估计是会有问题的,后续在调整 + return DateUtil.format(new Date(), "yyyyMMddHHmmss") + // 时间序列 + MathUtil.random(100000, 999999) // 随机。为什么是这个范围,因为偷懒 + ; + } + + } \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/resources/mapper/PayAppMapper.xml b/pay/pay-service-impl/src/main/resources/mapper/PayAppMapper.xml index b581aae1f..8d219ee9f 100644 --- a/pay/pay-service-impl/src/main/resources/mapper/PayAppMapper.xml +++ b/pay/pay-service-impl/src/main/resources/mapper/PayAppMapper.xml @@ -3,7 +3,7 @@ - id, name, status, create_time + id, name, notify_url, status, create_time @@ -27,7 +27,7 @@ - SELECT FROM app diff --git a/pay/pay-service-impl/src/main/resources/mapper/PayTransactionExtensionMapper.xml b/pay/pay-service-impl/src/main/resources/mapper/PayTransactionExtensionMapper.xml new file mode 100644 index 000000000..99363bd22 --- /dev/null +++ b/pay/pay-service-impl/src/main/resources/mapper/PayTransactionExtensionMapper.xml @@ -0,0 +1,44 @@ + + + + + + id, transaction_id, pay_channel, transaction_code, extension_data, + create_ip, status, create_time + + + + INSERT INTO transaction_extension ( + transaction_id, pay_channel, transaction_code, extension_data, + create_ip, status + ) VALUES ( + #{transactionId}, #{payChannel}, #{transactionCode}, #{extensionData}, + #{createIp}, #{status} + ) + + + + UPDATE transaction_extension + + + , extension_data = #{entity.extensionData} + + + , status = #{entity.status} + + + WHERE id = #{entity.id} + + AND status = #{whereStatus} + + + + + + \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/resources/mapper/PayTransactionMapper.xml b/pay/pay-service-impl/src/main/resources/mapper/PayTransactionMapper.xml new file mode 100644 index 000000000..e05647900 --- /dev/null +++ b/pay/pay-service-impl/src/main/resources/mapper/PayTransactionMapper.xml @@ -0,0 +1,69 @@ + + + + + + id, app_id, create_ip, order_id, order_subject, + order_description, order_memo, price, status, expire_time, + finish_time, notify_url, extension_id, pay_channel, payment_time, + notify_time, trade_no, create_time + + + + INSERT INTO transaction ( + app_id, create_ip, order_id, order_subject, + order_description, order_memo, price, status, expire_time, + finish_time, notify_url, extension_id, pay_channel, payment_time, + notify_time, trade_no, create_time + ) VALUES ( + #{appId}, #{createIp}, #{orderId}, #{orderSubject}, + #{orderDescription}, #{orderMemo}, #{price}, #{status}, #{expireTime}, + #{finishTime}, #{notifyUrl}, #{extensionId}, #{payChannel}, #{paymentTime}, + #{notifyTime}, #{tradeNo}, #{createTime} + ) + + + + UPDATE transaction + + + , status = #{entity.status} + + + , extension_id = #{entity.extensionId} + + + , pay_channel = #{entity.payChannel} + + + , payment_time = #{entity.paymentTime} + + + , notify_time = #{entity.notifyTime} + + + , trade_no = #{entity.tradeNo} + + + WHERE id = #{entity.id} + + AND status = #{whereStatus} + + + + + + + + \ No newline at end of file diff --git a/pay/pay-service-impl/src/main/resources/mapper/PayTransactionNotifyTaskMapper.xml b/pay/pay-service-impl/src/main/resources/mapper/PayTransactionNotifyTaskMapper.xml new file mode 100644 index 000000000..3a5c8ecd7 --- /dev/null +++ b/pay/pay-service-impl/src/main/resources/mapper/PayTransactionNotifyTaskMapper.xml @@ -0,0 +1,44 @@ + + + + + + id, transaction_id, transaction_extension_id, app_id, order_id, + status, last_Notify_time, notify_times, max_notify_times, create_time + + + + INSERT INTO transaction_notify_task ( + transaction_id, transaction_extension_id, app_id, order_id, + status, last_notify_time, notify_times, max_notify_times + ) VALUES ( + #{transactionId}, #{transactionExtensionId}, #{appId}, #{orderId}, + #{status}, #{lastNotifyTime}, #{notifyTimes}, #{maxNotifyTimes} + ) + + + + UPDATE transaction_notify_task + + + , status = #{status} + + + , last_notify_time = #{lastNotifyTime} + + + , notify_times = #{notifyTimes} + + + WHERE id = #{id} + + + + + + + + + + + \ No newline at end of file