Merge remote-tracking branch 'origin/master'
commit
c772da6716
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.mall.pay.scheduler;
|
||||
package cn.iocoder.mall.pay.application.scheduler;
|
||||
|
||||
/**
|
||||
* TODO
|
|
@ -2,13 +2,15 @@ package cn.iocoder.mall.pay.api;
|
|||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.pay.api.bo.PayTransactionBO;
|
||||
import cn.iocoder.mall.pay.api.bo.PayTransactionSubmitBO;
|
||||
import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO;
|
||||
import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO;
|
||||
|
||||
public interface PayTransactionService {
|
||||
|
||||
CommonResult<PayTransactionBO> createTransaction(PayTransactionCreateDTO payTransactionCreateDTO);
|
||||
|
||||
CommonResult submitTransaction(); // TODO 1. params 2. result
|
||||
CommonResult<PayTransactionSubmitBO> submitTransaction(PayTransactionSubmitDTO payTransactionSubmitDTO);
|
||||
|
||||
CommonResult cancelTransaction(); // TODO 1. params 2. result
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package cn.iocoder.mall.pay.api.bo;
|
||||
|
||||
/**
|
||||
* 支付交易提交结果 BO
|
||||
*/
|
||||
public class PayTransactionSubmitBO {
|
||||
|
||||
/**
|
||||
* 支付交易拓展单编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 调用三方平台的响应结果
|
||||
*/
|
||||
private String invokeResponse;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public PayTransactionSubmitBO setId(Integer id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getInvokeResponse() {
|
||||
return invokeResponse;
|
||||
}
|
||||
|
||||
public PayTransactionSubmitBO setInvokeResponse(String invokeResponse) {
|
||||
this.invokeResponse = invokeResponse;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,10 @@ public enum PayChannelEnum {
|
|||
WEIXIN_APP(100, "wx", "微信 App 支付"),
|
||||
WEIXIN_PUB(100, "wx", "微信 JS API 支付"),
|
||||
|
||||
ALIPAY(200, "alipay", "微信支付");
|
||||
ALIPAY(200, "alipay", "微信支付"),
|
||||
|
||||
PINGXX(9999, "ping++", "ping++ 支付"),
|
||||
;
|
||||
|
||||
/**
|
||||
* 渠道编号
|
||||
|
|
|
@ -12,7 +12,8 @@ public enum PayErrorCodeEnum {
|
|||
PAY_APP_IS_DISABLE(1004000001, "App 已经被禁用"),
|
||||
|
||||
// ========== TRANSACTION 模块 ==========
|
||||
|
||||
PAY_TRANSACTION_NOT_FOUND(100401000, "支付交易单不存在"),
|
||||
PAY_TRANSACTION_STATUS_IS_NOT_WAITING(100401001, "支付交易单不处于待支付"),
|
||||
|
||||
;
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package cn.iocoder.mall.pay.api.dto;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 支付交易提交 DTO
|
||||
*/
|
||||
public class PayTransactionSubmitDTO {
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
*/
|
||||
@NotEmpty(message = "应用编号不能为空")
|
||||
private String appId;
|
||||
/**
|
||||
* 发起交易的 IP
|
||||
*/
|
||||
@NotEmpty(message = "IP 不能为空")
|
||||
private String createIp;
|
||||
/**
|
||||
* 业务线的订单编号
|
||||
*/
|
||||
@NotEmpty(message = "订单号不能为空")
|
||||
private String orderId;
|
||||
/**
|
||||
* 支付渠道
|
||||
*/
|
||||
@NotNull(message = "支付渠道")
|
||||
private Integer payChannel;
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public PayTransactionSubmitDTO setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCreateIp() {
|
||||
return createIp;
|
||||
}
|
||||
|
||||
public PayTransactionSubmitDTO setCreateIp(String createIp) {
|
||||
this.createIp = createIp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getOrderId() {
|
||||
return orderId;
|
||||
}
|
||||
|
||||
public PayTransactionSubmitDTO setOrderId(String orderId) {
|
||||
this.orderId = orderId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getPayChannel() {
|
||||
return payChannel;
|
||||
}
|
||||
|
||||
public PayTransactionSubmitDTO setPayChannel(Integer payChannel) {
|
||||
this.payChannel = payChannel;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -60,6 +60,13 @@
|
|||
<version>27.0.1-jre</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>Pingplusplus</groupId>
|
||||
<artifactId>pingpp-java</artifactId>
|
||||
<version>2.2.4</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -85,4 +92,15 @@
|
|||
</build>
|
||||
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>central</id>
|
||||
<name>bintray</name>
|
||||
<url>http://jcenter.bintray.com</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,16 @@
|
|||
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 java.util.Map;
|
||||
|
||||
public abstract class AbstractPaySDK {
|
||||
|
||||
// extra 属性,用于支持不同支付平台的拓展字段。例如说,微信公众号支付,需要多传递一个 openid
|
||||
public abstract CommonResult<String> submitTransaction(PayTransactionDO transaction,
|
||||
PayTransactionExtensionDO transactionExtension,
|
||||
Map<String, Object> extra);
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cn.iocoder.mall.pay.client;
|
||||
|
||||
import cn.iocoder.mall.pay.api.constant.PayChannelEnum;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PaySDKFactory {
|
||||
|
||||
private static Map<Integer, AbstractPaySDK> SDKS = new HashMap<>();
|
||||
|
||||
static {
|
||||
SDKS.put(PayChannelEnum.PINGXX.getId(), new PingxxPaySDK());
|
||||
}
|
||||
|
||||
public static AbstractPaySDK getSDK(Integer payChannel) {
|
||||
AbstractPaySDK sdk = SDKS.get(payChannel);
|
||||
if (sdk == null) {
|
||||
throw new NullPointerException("找不到合适的 PaySDK :" + payChannel);
|
||||
}
|
||||
return sdk;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
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.google.common.collect.ImmutableMap;
|
||||
import com.pingplusplus.Pingpp;
|
||||
import com.pingplusplus.exception.*;
|
||||
import com.pingplusplus.model.Charge;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
// TODO 代码略乱,后面重构下
|
||||
public class PingxxPaySDK extends AbstractPaySDK {
|
||||
|
||||
static {
|
||||
Pingpp.privateKeyPath = "/Users/yunai/Downloads/pingxx.pem";
|
||||
Pingpp.apiKey = "sk_test_8a9SGSXLKqX1ennjX9DenvbT";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<String> submitTransaction(PayTransactionDO transaction, PayTransactionExtensionDO transactionExtension, Map<String, Object> extra) {
|
||||
Map<String, Object> reqObj = createChargeRequest(transaction, transactionExtension, extra);
|
||||
// 请求ping++
|
||||
try {
|
||||
Charge charge = Charge.create(reqObj);
|
||||
// System.out.println(charge.toString());
|
||||
return CommonResult.success(charge.toString());
|
||||
} catch (AuthenticationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidRequestException e) {
|
||||
e.printStackTrace();
|
||||
} catch (APIConnectionException e) {
|
||||
e.printStackTrace();
|
||||
} catch (APIException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ChannelException e) {
|
||||
e.printStackTrace();
|
||||
} catch (RateLimitException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private Map<String, Object> createChargeRequest(PayTransactionDO transaction, PayTransactionExtensionDO transactionExtension, Map<String, Object> extra) {
|
||||
// 计算支付渠道和支付额外参数
|
||||
String channel = "wx_pub"; // 因为 ping++ 是用来做模拟支付的渠道,所以这里强制就选择了 wx_pub 微信公众号支付
|
||||
extra = new HashMap<>(); // TODO 临时
|
||||
extra.put("open_id", "just_for_test");
|
||||
// 生成支付对象
|
||||
Map<String, Object> reqObj = new HashMap<>();
|
||||
reqObj.put("subject", transaction.getOrderSubject());
|
||||
reqObj.put("body", transaction.getOrderDescription());
|
||||
reqObj.put("description", transaction.getOrderMemo());
|
||||
reqObj.put("amount", transaction.getPrice());
|
||||
reqObj.put("order_no", transactionExtension.getTransactionCode());
|
||||
reqObj.put("channel", channel);
|
||||
reqObj.put("currency", "cny");
|
||||
reqObj.put("client_ip", transactionExtension.getCreateIp());
|
||||
reqObj.put("app", ImmutableMap.of("id", "app_aTyfXDjrvzDSbLuz")); // TODO 写死先
|
||||
reqObj.put("extra", extra);
|
||||
return reqObj;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
PayTransactionDO transaction = new PayTransactionDO();
|
||||
transaction.setOrderSubject("测试商品");
|
||||
transaction.setOrderDescription("测试描述");
|
||||
transaction.setPrice(1);
|
||||
|
||||
PayTransactionExtensionDO extension = new PayTransactionExtensionDO();
|
||||
extension.setTransactionCode(System.currentTimeMillis() + "");
|
||||
extension.setCreateIp("127.0.0.1");
|
||||
|
||||
new PingxxPaySDK().submitTransaction(transaction, extension, null);
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,9 @@ package cn.iocoder.mall.pay.convert;
|
|||
|
||||
import cn.iocoder.mall.pay.api.bo.PayTransactionBO;
|
||||
import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO;
|
||||
import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO;
|
||||
import cn.iocoder.mall.pay.dataobject.PayTransactionDO;
|
||||
import cn.iocoder.mall.pay.dataobject.PayTransactionExtensionDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
@ -18,4 +20,7 @@ public interface PayTransactionConvert {
|
|||
@Mappings({})
|
||||
PayTransactionBO convert(PayTransactionDO payTransactionDO);
|
||||
|
||||
@Mappings({})
|
||||
PayTransactionExtensionDO convert(PayTransactionSubmitDTO payTransactionSubmitDTO);
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package cn.iocoder.mall.pay.dao;
|
||||
|
||||
import cn.iocoder.mall.pay.dataobject.PayTransactionExtensionDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface PayTransactionExtensionMapper {
|
||||
|
||||
void insert(PayTransactionExtensionDO payTransactionExtensionDO);
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.mall.pay.dao;
|
||||
|
||||
import cn.iocoder.mall.pay.dataobject.PayTransactionDO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
|
@ -8,4 +9,7 @@ public interface PayTransactionMapper {
|
|||
|
||||
void insert(PayTransactionDO entity);
|
||||
|
||||
PayTransactionDO selectByAppIdAndOrderId(@Param("appId") String appId,
|
||||
@Param("orderId") String orderId);
|
||||
|
||||
}
|
|
@ -1,24 +1,43 @@
|
|||
package cn.iocoder.mall.pay.service;
|
||||
|
||||
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.PayTransactionStatusEnum;
|
||||
import cn.iocoder.mall.pay.api.dto.PayTransactionCreateDTO;
|
||||
import cn.iocoder.mall.pay.api.dto.PayTransactionSubmitDTO;
|
||||
import cn.iocoder.mall.pay.client.PaySDKFactory;
|
||||
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.dataobject.PayAppDO;
|
||||
import cn.iocoder.mall.pay.dataobject.PayTransactionDO;
|
||||
import cn.iocoder.mall.pay.dataobject.PayTransactionExtensionDO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Service
|
||||
@com.alibaba.dubbo.config.annotation.Service(validation = "true")
|
||||
public class PayServiceImpl implements PayTransactionService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private PayTransactionMapper payTransactionMapper;
|
||||
@Autowired
|
||||
private PayTransactionExtensionMapper payTransactionExtensionMapper;
|
||||
@Autowired
|
||||
private PayAppServiceImpl payAppService;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("Duplicates")
|
||||
public CommonResult<PayTransactionBO> createTransaction(PayTransactionCreateDTO payTransactionCreateDTO) {
|
||||
// 校验 App
|
||||
CommonResult<PayAppDO> appResult = payAppService.validPayApp(payTransactionCreateDTO.getAppId());
|
||||
|
@ -26,12 +45,57 @@ public class PayServiceImpl implements PayTransactionService {
|
|||
return CommonResult.error(appResult);
|
||||
}
|
||||
// 插入 PayTransactionDO
|
||||
return null;
|
||||
PayTransactionDO payTransaction = payTransactionMapper.selectByAppIdAndOrderId(
|
||||
payTransactionCreateDTO.getAppId(), payTransactionCreateDTO.getOrderId());
|
||||
if (payTransaction != null) {
|
||||
logger.warn("[createTransaction][appId({}) orderId({}) exists]", payTransactionCreateDTO.getAppId(),
|
||||
payTransactionCreateDTO.getOrderId()); // 理论来说,不会出现这个情况
|
||||
// TODO 芋艿 可能要考虑,更新订单。例如说,业务线订单可以修改价格
|
||||
} else {
|
||||
payTransaction = PayTransactionConvert.INSTANCE.convert(payTransactionCreateDTO);
|
||||
payTransaction.setStatus(PayTransactionStatusEnum.WAITTING.getValue())
|
||||
.setNotifyUrl(appResult.getData().getNotifyUrl());
|
||||
payTransaction.setCreateTime(new Date());
|
||||
payTransactionMapper.insert(payTransaction);
|
||||
}
|
||||
// 返回成功
|
||||
return CommonResult.success(PayTransactionConvert.INSTANCE.convert(payTransaction));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult submitTransaction() {
|
||||
return null;
|
||||
@SuppressWarnings("Duplicates")
|
||||
public CommonResult<PayTransactionSubmitBO> submitTransaction(PayTransactionSubmitDTO payTransactionSubmitDTO) {
|
||||
// TODO 校验支付渠道是否有效
|
||||
// 校验 App 是否有效
|
||||
CommonResult<PayAppDO> appResult = payAppService.validPayApp(payTransactionSubmitDTO.getAppId());
|
||||
if (appResult.isError()) {
|
||||
return CommonResult.error(appResult);
|
||||
}
|
||||
// 获得 PayTransactionDO ,并校验其是否存在
|
||||
PayTransactionDO payTransaction = payTransactionMapper.selectByAppIdAndOrderId(
|
||||
payTransactionSubmitDTO.getAppId(), payTransactionSubmitDTO.getOrderId());
|
||||
if (payTransaction == null) { // 是否存在
|
||||
return ServiceExceptionUtil.error(PayErrorCodeEnum.PAY_TRANSACTION_NOT_FOUND.getCode());
|
||||
}
|
||||
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")
|
||||
.setStatus(PayTransactionStatusEnum.WAITTING.getValue());
|
||||
payTransactionExtensionMapper.insert(payTransactionExtensionDO);
|
||||
// 调用三方接口
|
||||
CommonResult<String> invokeResult = PaySDKFactory.getSDK(payTransactionSubmitDTO.getPayChannel()).submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null
|
||||
if (invokeResult.isError()) {
|
||||
return CommonResult.error(invokeResult);
|
||||
}
|
||||
// TODO 轮询三方接口,是否已经支付的任务
|
||||
// 返回成功
|
||||
PayTransactionSubmitBO payTransactionSubmitBO = new PayTransactionSubmitBO()
|
||||
.setId(payTransactionExtensionDO.getId()).setInvokeResponse(invokeResult.getData());
|
||||
return CommonResult.success(payTransactionSubmitBO);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,6 +103,4 @@ public class PayServiceImpl implements PayTransactionService {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue