支付模块,调通对 ping++ 的调用,以实现模拟支付。
当然,整体代码还是有点乱,后面花点时间重构下~ 现在,缺一个异步 MQ 通知业务方订单支付成功
This commit is contained in:
parent
c772da6716
commit
7d423c8ed2
@ -55,6 +55,12 @@
|
||||
<!--<version>2.3</version>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.56</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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" +
|
||||
"}";
|
||||
}
|
||||
|
||||
}
|
@ -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<PayTransactionSubmitBO> 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 "";
|
||||
}
|
||||
|
||||
}
|
@ -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<GenericService> 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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package cn.iocoder.mall.pay.api;
|
||||
|
||||
public interface PayDemoService {
|
||||
|
||||
String updatePaySuccess(String orderId);
|
||||
|
||||
}
|
@ -12,6 +12,18 @@ public interface PayTransactionService {
|
||||
|
||||
CommonResult<PayTransactionSubmitBO> submitTransaction(PayTransactionSubmitDTO payTransactionSubmitDTO);
|
||||
|
||||
/**
|
||||
* 更新交易支付成功
|
||||
*
|
||||
* 该接口用于不同支付平台,支付成功后,回调该接口
|
||||
*
|
||||
* @param payChannel 支付渠道
|
||||
* @param params 回调参数。
|
||||
* 因为不同平台,能够提供的参数不同,所以使用 String 类型统一接收,然后在使用不同的 AbstractPaySDK 进行处理。
|
||||
* @return 是否支付成功
|
||||
*/
|
||||
CommonResult<Boolean> updateTransactionPaySuccess(Integer payChannel, String params);
|
||||
|
||||
CommonResult cancelTransaction(); // TODO 1. params 2. result
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -19,7 +19,6 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
|
@ -13,4 +13,7 @@ public abstract class AbstractPaySDK {
|
||||
PayTransactionExtensionDO transactionExtension,
|
||||
Map<String, Object> extra);
|
||||
|
||||
// TODO 芋艿,理论来说不会出现解析失败的情况,先返回这个参数列。等后面封装支付宝和微信支付的时候,在看看。
|
||||
public abstract CommonResult<TransactionPaySuccessBO> parseTransactionPaySuccessParams(String params);
|
||||
|
||||
}
|
@ -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<TransactionPaySuccessBO> 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<String, Object> createChargeRequest(PayTransactionDO transaction, PayTransactionExtensionDO transactionExtension, Map<String, Object> extra) {
|
||||
// 计算支付渠道和支付额外参数
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 "你好呀";
|
||||
}
|
||||
|
||||
}
|
@ -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<String> invokeResult = PaySDKFactory.getSDK(payTransactionSubmitDTO.getPayChannel()).submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null
|
||||
AbstractPaySDK paySDK = PaySDKFactory.getSDK(payTransactionSubmitDTO.getPayChannel());
|
||||
CommonResult<String> 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<Boolean> updateTransactionPaySuccess(Integer payChannel, String params) {
|
||||
// TODO 芋艿,记录回调日志
|
||||
// 解析传入的参数,成 TransactionPaySuccessBO 对象
|
||||
AbstractPaySDK paySDK = PaySDKFactory.getSDK(payChannel);
|
||||
CommonResult<TransactionPaySuccessBO> 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) // 随机。为什么是这个范围,因为偷懒
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<mapper namespace="cn.iocoder.mall.pay.dao.PayAppMapper">
|
||||
|
||||
<sql id="FIELDS">
|
||||
id, name, status, create_time
|
||||
id, name, notify_url, status, create_time
|
||||
</sql>
|
||||
|
||||
<!--<insert id="insert" parameterType="RoleDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">-->
|
||||
@ -27,7 +27,7 @@
|
||||
<!--WHERE id = #{id}-->
|
||||
<!--</update>-->
|
||||
|
||||
<select id="selectById" parameterType="Integer" resultType="PayAppDO">
|
||||
<select id="selectById" parameterType="String" resultType="PayAppDO">
|
||||
SELECT
|
||||
<include refid="FIELDS"/>
|
||||
FROM app
|
||||
|
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.mall.pay.dao.PayTransactionExtensionMapper">
|
||||
|
||||
<sql id="FIELDS">
|
||||
id, transaction_id, pay_channel, transaction_code, extension_data,
|
||||
create_ip, status, create_time
|
||||
</sql>
|
||||
|
||||
<insert id="insert" parameterType="PayTransactionExtensionDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||
INSERT INTO transaction_extension (
|
||||
transaction_id, pay_channel, transaction_code, extension_data,
|
||||
create_ip, status
|
||||
) VALUES (
|
||||
#{transactionId}, #{payChannel}, #{transactionCode}, #{extensionData},
|
||||
#{createIp}, #{status}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="update">
|
||||
UPDATE transaction_extension
|
||||
<set>
|
||||
<if test="entity.extensionData != null">
|
||||
, extension_data = #{entity.extensionData}
|
||||
</if>
|
||||
<if test="entity.status != null">
|
||||
, status = #{entity.status}
|
||||
</if>
|
||||
</set>
|
||||
WHERE id = #{entity.id}
|
||||
<if test="whereStatus != null">
|
||||
AND status = #{whereStatus}
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<select id="selectByTransactionCode" parameterType="String" resultType="PayTransactionExtensionDO">
|
||||
SELECT
|
||||
<include refid="FIELDS"/>
|
||||
FROM transaction_extension
|
||||
WHERE transaction_code = #{transactionCode}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.mall.pay.dao.PayTransactionMapper">
|
||||
|
||||
<sql id="FIELDS">
|
||||
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
|
||||
</sql>
|
||||
|
||||
<insert id="insert" parameterType="PayTransactionDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||
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}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="update">
|
||||
UPDATE transaction
|
||||
<set>
|
||||
<if test="entity.status != null">
|
||||
, status = #{entity.status}
|
||||
</if>
|
||||
<if test="entity.extensionId != null">
|
||||
, extension_id = #{entity.extensionId}
|
||||
</if>
|
||||
<if test="entity.payChannel != null">
|
||||
, pay_channel = #{entity.payChannel}
|
||||
</if>
|
||||
<if test="entity.paymentTime != null">
|
||||
, payment_time = #{entity.paymentTime}
|
||||
</if>
|
||||
<if test="entity.notifyTime != null">
|
||||
, notify_time = #{entity.notifyTime}
|
||||
</if>
|
||||
<if test="entity.tradeNo != null">
|
||||
, trade_no = #{entity.tradeNo}
|
||||
</if>
|
||||
</set>
|
||||
WHERE id = #{entity.id}
|
||||
<if test="whereStatus != null">
|
||||
AND status = #{whereStatus}
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<select id="selectByAppIdAndOrderId" resultType="PayTransactionDO">
|
||||
SELECT
|
||||
<include refid="FIELDS"/>
|
||||
FROM transaction
|
||||
WHERE app_id = #{appId}
|
||||
AND order_id = #{orderId}
|
||||
</select>
|
||||
|
||||
<select id="selectById" parameterType="Integer" resultType="PayTransactionDO">
|
||||
SELECT
|
||||
<include refid="FIELDS"/>
|
||||
FROM transaction
|
||||
WHERE id = #{id}
|
||||
</select>
|
||||
|
||||
</mapper>
|
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.mall.pay.dao.PayTransactionNotifyTaskMapper">
|
||||
|
||||
<sql id="FIELDS">
|
||||
id, transaction_id, transaction_extension_id, app_id, order_id,
|
||||
status, last_Notify_time, notify_times, max_notify_times, create_time
|
||||
</sql>
|
||||
|
||||
<insert id="insert" parameterType="PayTransactionNotifyTaskDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||
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}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="update" parameterType="PayTransactionNotifyTaskDO">
|
||||
UPDATE transaction_notify_task
|
||||
<set>
|
||||
<if test="status != null">
|
||||
, status = #{status}
|
||||
</if>
|
||||
<if test="lastNotifyTime != null">
|
||||
, last_notify_time = #{lastNotifyTime}
|
||||
</if>
|
||||
<if test="notifyTimes != null">
|
||||
, notify_times = #{notifyTimes}
|
||||
</if>
|
||||
</set>
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<!--<select id="selectByTransactionCode" parameterType="String" resultType="PayTransactionExtensionDO">-->
|
||||
<!--SELECT-->
|
||||
<!--<include refid="FIELDS"/>-->
|
||||
<!--FROM transaction_extension-->
|
||||
<!--WHERE transaction_code = #{transactionCode}-->
|
||||
<!--LIMIT 1-->
|
||||
<!--</select>-->
|
||||
|
||||
</mapper>
|
Loading…
Reference in New Issue
Block a user