first commit

This commit is contained in:
abcv7
2026-02-25 15:08:40 +08:00
commit 7f1d83ada7
2003 changed files with 144362 additions and 0 deletions
@@ -0,0 +1,15 @@
package com.itheima.sfbx.trade;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 负责订单明细、支付渠道管理、支付回调、其它与支付相关的功能对应的启动引导类
*/
@SpringBootApplication(scanBasePackages = "com.itheima.sfbx")
public class TradeWebStart {
public static void main(String[] args) {
SpringApplication.run(TradeWebStart.class, args);
}
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.adapter;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName WapPayAdapter.java
* @Description 原生APP支付适配
*/
public interface AppPayAdapter {
/***
* @description 外部商户APP唤起快捷SDK创建订单并支付
* @param tradeVO 订单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO appTrade(TradeVO tradeVO);
}
@@ -0,0 +1,62 @@
package com.itheima.sfbx.trade.adapter;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
/**
* @ClassName CommonPayAdapter.java
* @Description 基础支付适配
*/
public interface CommonPayAdapter {
/***
* @description 统一收单线下交易查询
* 该接口提供所有支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。
* @param tradeVO 交易单
* @return
*/
TradeVO queryTrade(TradeVO tradeVO) throws ProjectException;
/***
* @description 统一收单交易退款接口
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
* 将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* @param tradeVO 交易单
* @return
*/
TradeVO refundTrade(TradeVO tradeVO) throws ProjectException;
/***
* @description 统一收单交易退款查询接口
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
* 将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* @param refundRecordVO 退款交易单
* @return
*/
RefundRecordVO queryRefundTrade(RefundRecordVO refundRecordVO) throws ProjectException;
/***
* @description 统一关闭订单
* 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
* 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
* @param tradeVO 交易单
* @return
*/
TradeVO closeTrade(TradeVO tradeVO);
/***
* @description 查看二维码信息
* 收银员通过收银台或商户后台调用此接口,生成二维码后,展示给用户,商户可以多次展示二维码
* @param tradeVO 交易单
* @return 二维码路径
*/
String queryQrCode(TradeVO tradeVO);
/***
* @description 为方便商户快速查账,支持商户通过本接口获取商户离线账单下载地址
* @param tradeVO 退款交易单
* @return
*/
TradeVO downLoadBill(TradeVO tradeVO);
}
@@ -0,0 +1,24 @@
package com.itheima.sfbx.trade.adapter;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName FaceToFacePayAdapter.java
* @Description 面对面支付适配实现
*/
public interface FaceToFacePayAdapter {
/***
* @description 扫用户出示的付款码,完成付款
* @param tradeVO 订单
* @return 支付结果
*/
TradeVO payTrade(TradeVO tradeVO);
/***
* @description 生成交易付款码,待用户扫码付款
* @param tradeVO 订单
* @return 二维码路径
*/
TradeVO precreateTrade(TradeVO tradeVO);
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.adapter;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName PagePayAdapter.java
* @Description PC网页支付适配
*/
public interface PagePayAdapter {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO pageTrade(TradeVO tradeVO);
}
@@ -0,0 +1,31 @@
package com.itheima.sfbx.trade.adapter;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName PeriodicPayAdepter.java
* @Description 周期性扣款接口
*/
public interface PeriodicPayAdepter {
/***
* @description App支付并签约
* @param tradeVO 交易单信息
* @return
*/
TradeVO appPaySign(TradeVO tradeVO);
/***
* @description H5先签约
* @param tradeVO 交易单信息
* @return
*/
TradeVO h5SignContract(TradeVO tradeVO);
/***
* @description H5签约后代扣
* @param tradeVO 交易单信息
* @return
*/
TradeVO h5PeriodicPay(TradeVO tradeVO);
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.adapter;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName WapPayAdapter.java
* @Description 手机网页支付适配
*/
public interface WapPayAdapter {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO wapTrade(TradeVO tradeVO);
}
@@ -0,0 +1,41 @@
package com.itheima.sfbx.trade.adapter.impl;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
import com.itheima.sfbx.trade.adapter.AppPayAdapter;
import com.itheima.sfbx.trade.handler.AppPayHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName AppPayAdapter.java
* @Description 原生APP支付适配支付适配实现
*/
@Slf4j
@Component
public class AppPayAdapterImpl implements AppPayAdapter {
@Autowired
RegisterBeanHandler registerBeanHandler;
private static Map<String,String> appPayHandlers =new HashMap<>();
static {
appPayHandlers.put(TradeConstant.TRADE_CHANNEL_ALI_PAY,"aliAppPayHandler");
appPayHandlers.put(TradeConstant.TRADE_CHANNEL_WECHAT_PAY,"wechatAppPayHandler");
}
@Override
public TradeVO appTrade(TradeVO tradeVO) {
//从IOC容器中找到AppPayHandler实现
String appPayHandlerString = appPayHandlers.get(tradeVO.getTradeChannel());
AppPayHandler appPayHandler = registerBeanHandler.getBean(appPayHandlerString, AppPayHandler.class);
//appTrade支付交易处理
return appPayHandler.appTrade(tradeVO);
}
}
@@ -0,0 +1,110 @@
package com.itheima.sfbx.trade.adapter.impl;
import cn.hutool.core.codec.Base64;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
import com.itheima.sfbx.trade.adapter.CommonPayAdapter;
import com.itheima.sfbx.trade.handler.CommonPayHandler;
import com.itheima.sfbx.trade.service.ITradeService;
import com.itheima.sfbx.trade.pojo.Trade;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName CommonPayAdapterImpl.java
* @Description 基础交易适配
*/
@Slf4j
@Component
public class CommonPayAdapterImpl implements CommonPayAdapter {
@Autowired
RegisterBeanHandler registerBeanHandler;
@Autowired
RedissonClient redissonClient;
@Autowired
ITradeService tradeService;
private static Map<String,String> commonPayHandlers =new HashMap<>();
static {
commonPayHandlers.put(TradeConstant.TRADE_CHANNEL_ALI_PAY,"aliCommonPayHandler");
commonPayHandlers.put(TradeConstant.TRADE_CHANNEL_WECHAT_PAY,"wechatCommonPayHandler");
}
@Override
public TradeVO queryTrade(TradeVO tradeVO) throws ProjectException {
//从IOC容器中找到AppPayHandler实现
String commonPayHandlerString = commonPayHandlers.get(tradeVO.getTradeChannel());
CommonPayHandler commonPayHandler = registerBeanHandler
.getBean(commonPayHandlerString, CommonPayHandler.class);
//queryTrade查询订单结果
return commonPayHandler.queryTrade(tradeVO);
}
@Override
public TradeVO refundTrade(TradeVO tradeVO) throws ProjectException {
//从IOC容器中找到AppPayHandler实现
String commonPayHandlerString = commonPayHandlers.get(tradeVO.getTradeChannel());
CommonPayHandler commonPayHandler = registerBeanHandler
.getBean(commonPayHandlerString, CommonPayHandler.class);
//refundTrade处理退款
return commonPayHandler.refundTrade(tradeVO);
}
@Override
public RefundRecordVO queryRefundTrade(RefundRecordVO refundRecordVO) throws ProjectException {
String commonPayHandlerString = commonPayHandlers.get(refundRecordVO.getTradeChannel());
CommonPayHandler commonPayHandler = registerBeanHandler
.getBean(commonPayHandlerString, CommonPayHandler.class);
return commonPayHandler.queryRefundTrade(refundRecordVO);
}
@Override
public TradeVO closeTrade(TradeVO tradeVO) {
String commonPayHandlerString = commonPayHandlers.get(tradeVO.getTradeChannel());
CommonPayHandler commonPayHandler = registerBeanHandler
.getBean(commonPayHandlerString, CommonPayHandler.class);
return commonPayHandler.closeTrade(tradeVO);
}
@Override
public String queryQrCode(TradeVO tradeVO) {
Trade trade = tradeService.findTradByProductOrderNo(tradeVO.getProductOrderNo());
//4、构建支付二维码
BufferedImage image = QrCodeUtil.generate(trade.getPlaceOrderMsg(), 480, 480);
//输出流
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", stream);
} catch (IOException e) {
throw new ProjectException(TradeEnum.TRAD_QRCODE_FAIL);
}
return Base64.encode(stream.toByteArray());
}
@Override
public TradeVO downLoadBill(TradeVO tradeVO) {
String commonPayHandlerString = commonPayHandlers.get(tradeVO.getTradeChannel());
CommonPayHandler commonPayHandler = registerBeanHandler
.getBean(commonPayHandlerString, CommonPayHandler.class);
return commonPayHandler.downLoadBill(tradeVO);
}
}
@@ -0,0 +1,81 @@
package com.itheima.sfbx.trade.adapter.impl;
import cn.hutool.core.codec.Base64;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.itheima.sfbx.trade.handler.FaceToFacePayHandler;
import com.itheima.sfbx.trade.adapter.FaceToFacePayAdapter;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName FaceToFacePayAdapterImpl.java
* @Description 面对面适配实现
*/
@Slf4j
@Component
public class FaceToFacePayAdapterImpl implements FaceToFacePayAdapter {
@Autowired
RegisterBeanHandler registerBeanHandler;
@Autowired
RedissonClient redissonClient;
private static Map<String,String> faceToFacePayHandlers =new HashMap<>();
static {
faceToFacePayHandlers.put(TradeConstant.TRADE_CHANNEL_ALI_PAY,"aliFaceToFacePayHandler");
faceToFacePayHandlers.put(TradeConstant.TRADE_CHANNEL_WECHAT_PAY,"wechatFaceToFacePayHandler");
}
@Override
public TradeVO payTrade(TradeVO tradeVO) {
//2、从IOC容器中找到FaceToFacePayHandler实现
String faceToFacePayHandlerString = faceToFacePayHandlers.get(tradeVO.getTradeChannel());
FaceToFacePayHandler faceToFacePayHandler = registerBeanHandler
.getBean(faceToFacePayHandlerString, FaceToFacePayHandler.class);
//3、payTrade支付交易处理
TradeVO tradeVOResult = faceToFacePayHandler.payTrade(tradeVO);
return tradeVOResult;
}
@Override
public TradeVO precreateTrade(TradeVO tradeVO) throws ProjectException {
//2、从IOC容器中找到FaceToFacePayHandler实现
String faceToFacePayHandlerString = faceToFacePayHandlers.get(tradeVO.getTradeChannel());
FaceToFacePayHandler faceToFacePayHandler = registerBeanHandler
.getBean(faceToFacePayHandlerString, FaceToFacePayHandler.class);
//3、precreateTrade支付交易处理
TradeVO tradeVOResult = faceToFacePayHandler.precreateTrade(tradeVO);
if (EmptyUtil.isNullOrEmpty(tradeVOResult.getPlaceOrderMsg())) {
throw new ProjectException(TradeEnum.TRAD_QRCODE_FAIL);
}
//4、构建支付二维码
BufferedImage image = QrCodeUtil.generate(tradeVOResult.getPlaceOrderMsg(), 480, 480);
//输出流
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", stream);
} catch (IOException e) {
throw new ProjectException(TradeEnum.TRAD_QRCODE_FAIL);
}
String base64Image = Base64.encode(stream.toByteArray());
tradeVOResult.setQrCodeImageBase64(base64Image);
return tradeVOResult;
}
}
@@ -0,0 +1,46 @@
package com.itheima.sfbx.trade.adapter.impl;
import com.itheima.sfbx.trade.adapter.PagePayAdapter;
import com.itheima.sfbx.trade.handler.PagePayHandler;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName PagePayAdapter.java
* @Description PC网页支付适配实现
*/
@Slf4j
@Component
public class PagePayAdapterImpl implements PagePayAdapter {
@Autowired
RegisterBeanHandler registerBeanHandler;
@Autowired
RedissonClient redissonClient;
private static Map<String,String> pagePayHandlers =new HashMap<>();
static {
pagePayHandlers.put(TradeConstant.TRADE_CHANNEL_ALI_PAY,"aliPagePayHandler");
pagePayHandlers.put(TradeConstant.TRADE_CHANNEL_WECHAT_PAY,"wechatPagePayHandler");
}
@Override
public TradeVO pageTrade(TradeVO tradeVO) {
//2、从IOC容器中找到PagePayHandler实现
String pagePayHandlerString = pagePayHandlers.get(tradeVO.getTradeChannel());
PagePayHandler pagePayHandler = registerBeanHandler.getBean(pagePayHandlerString, PagePayHandler.class);
//3、payTrade支付交易处理
TradeVO tradeVOResult = pagePayHandler.pageTrade(tradeVO);
return tradeVOResult;
}
}
@@ -0,0 +1,60 @@
package com.itheima.sfbx.trade.adapter.impl;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
import com.itheima.sfbx.trade.adapter.PeriodicPayAdepter;
import com.itheima.sfbx.trade.handler.PeriodicPayHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName PeriodicPayAdepterImpl.java
* @Description 周期性扣款接口
*/
@Component
public class PeriodicPayAdepterImpl implements PeriodicPayAdepter {
@Autowired
RegisterBeanHandler registerBeanHandler;
private static Map<String,String> periodicPayHandlers =new HashMap<>();
static {
periodicPayHandlers.put(TradeConstant.TRADE_CHANNEL_ALI_PAY,"aliPeriodicPayHandler");
periodicPayHandlers.put(TradeConstant.TRADE_CHANNEL_WECHAT_PAY,"wechatPayPeriodicPayHandler");
}
@Override
public TradeVO appPaySign(TradeVO tradeVO) {
//1、从IOC容器中找到PeriodicPayHandler实现
String periodicPayHandlerString = periodicPayHandlers.get(tradeVO.getTradeChannel());
PeriodicPayHandler periodicPayHandler = registerBeanHandler
.getBean(periodicPayHandlerString, PeriodicPayHandler.class);
//2、contractDeduction支付交易处理
return periodicPayHandler.appPaySign(tradeVO);
}
@Override
public TradeVO h5SignContract(TradeVO tradeVO) {
//1、从IOC容器中找到PeriodicPayHandler实现
String periodicPayHandlerString = periodicPayHandlers.get(tradeVO.getTradeChannel());
PeriodicPayHandler periodicPayHandler = registerBeanHandler
.getBean(periodicPayHandlerString, PeriodicPayHandler.class);
//2、contractDeduction支付交易处理
return periodicPayHandler.h5SignContract(tradeVO);
}
@Override
public TradeVO h5PeriodicPay(TradeVO tradeVO) {
//1、从IOC容器中找到PeriodicPayHandler实现
String periodicPayHandlerString = periodicPayHandlers.get(tradeVO.getTradeChannel());
PeriodicPayHandler periodicPayHandler = registerBeanHandler
.getBean(periodicPayHandlerString, PeriodicPayHandler.class);
//2、contractDeduction支付交易处理
return periodicPayHandler.h5PeriodicPay(tradeVO);
}
}
@@ -0,0 +1,46 @@
package com.itheima.sfbx.trade.adapter.impl;
import com.itheima.sfbx.trade.adapter.WapPayAdapter;
import com.itheima.sfbx.trade.handler.WapPayHandler;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName WapPayAdapter.java
* @Description 手机网页支付适配实现
*/
@Slf4j
@Component
public class WapPayAdapterImpl implements WapPayAdapter {
@Autowired
RegisterBeanHandler registerBeanHandler;
@Autowired
RedissonClient redissonClient;
private static Map<String,String> wapPayHandlers =new HashMap<>();
static {
wapPayHandlers.put(TradeConstant.TRADE_CHANNEL_ALI_PAY,"aliWapPayHandler");
wapPayHandlers.put(TradeConstant.TRADE_CHANNEL_WECHAT_PAY,"wechatWapPayHandler");
}
@Override
public TradeVO wapTrade(TradeVO tradeVO) {
//2、从IOC容器中找到WapPayHandler实现
String wapPayHandlerString = wapPayHandlers.get(tradeVO.getTradeChannel());
WapPayHandler wapPayHandler = registerBeanHandler.getBean(wapPayHandlerString, WapPayHandler.class);
//3、wapTrade支付交易处理
TradeVO tradeVOResult = wapPayHandler.wapTrade(tradeVO);
return tradeVOResult;
}
}
@@ -0,0 +1,12 @@
package com.itheima.sfbx.trade.binding;
import com.itheima.sfbx.framework.rabbitmq.source.TradeSource;
import org.springframework.cloud.stream.annotation.EnableBinding;
/**
* @ClassName Binding.java
* @Description 绑定支付发送者声明
*/
@EnableBinding({TradeSource.class})
public class SourceBinding {
}
@@ -0,0 +1,115 @@
package com.itheima.sfbx.trade.client.alipay;
import com.alipay.easysdk.kernel.AlipayConstants;
import com.alipay.easysdk.kernel.Client;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.Context;
import com.alipay.easysdk.kms.aliyun.AliyunKMSClient;
import com.alipay.easysdk.kms.aliyun.AliyunKMSSigner;
import com.aliyun.tea.TeaModel;
import java.lang.reflect.Constructor;
/**
* @ClassName Factory.java
* @Description 基于ali工厂改造:在多租户下,用户上下文对象并发问题
*/
public class Factory {
public final String SDK_VERSION = "alipay-easysdk-java-2.1.2";
/**
* 将一些初始化耗时较多的信息缓存在上下文中
*/
private Context context;
/**
* 设置客户端参数,只需设置一次,即可反复使用各种场景下的API Client
*
* @param options 客户端参数对象
*/
public void setOptions(Config options) {
try {
context = new Context(options, SDK_VERSION);
if (AlipayConstants.AliyunKMS.equals(context.getConfig(AlipayConstants.SIGN_PROVIDER_CONFIG_KEY))) {
context.setSigner(new AliyunKMSSigner(new AliyunKMSClient(TeaModel.buildMap(options))));
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* 获取调用OpenAPI所需的客户端实例
* 本方法用于调用SDK扩展包中的API Client下的方法
*
* 注:返回的实例不可重复使用,只可用于单次调用
*
* @param client API Client的类型对象
* @return client实例,用于发起单次调用
*/
public <T> T getClient(Class<T> client) {
try {
Constructor<T> constructor = client.getConstructor(Client.class);
context.setSdkVersion(getSdkVersion(client));
return constructor.newInstance(new Client(context));
} catch (Exception e) {
throw new RuntimeException("" + e.getMessage(), e);
}
}
private <T> String getSdkVersion(Class<T> client) {
return context.getSdkVersion() + "-" + client.getCanonicalName()
.replace("com.alipay.easysdk.", "")
.replace(".Client", "")
.replace(".", "-");
}
/**
* 获取支付通用API Client
*
* @return 支付通用API Client
*/
public com.alipay.easysdk.payment.common.Client Common() throws Exception {
return new com.alipay.easysdk.payment.common.Client(new Client(context));
}
/**
* 获取当面付相关API Client
*
* @return 当面付相关API Client
*/
public com.alipay.easysdk.payment.facetoface.Client FaceToFace() throws Exception {
return new com.alipay.easysdk.payment.facetoface.Client(new Client(context));
}
/**
* 获取电脑网站支付相关API Client
*
* @return 电脑网站支付相关API Client
*/
public com.alipay.easysdk.payment.page.Client Page() throws Exception {
return new com.alipay.easysdk.payment.page.Client(new Client(context));
}
/**
* 获取手机网站支付相关API Client
*
* @return 手机网站支付相关API Client
*/
public com.alipay.easysdk.payment.wap.Client Wap() throws Exception {
return new com.alipay.easysdk.payment.wap.Client(new Client(context));
}
/**
* 获取手机APP支付相关API Client
*
* @return 手机APP支付相关API Client
*/
public com.alipay.easysdk.payment.app.Client App() throws Exception {
return new com.alipay.easysdk.payment.app.Client(new Client(context));
}
}
@@ -0,0 +1,62 @@
package com.itheima.sfbx.trade.client.wechat;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* @ClassName Config.java
* @Description 微信的配置文件
*/
@Slf4j
@Data
public class Config {
//appId
String appid;
//商户号
String mchId;
//私钥字符串
String privateKey;
//商户证书序列号
String mchSerialNo;
//V3密钥
String apiV3Key;
//请求地址
String domain;
//回调地址
String notifyUrl;
/***
* @description 构建客户端
* @param mchId 商户号
* @param privateKey 私钥字符串
* @param mchSerialNo 商户证书序列号
* @param apiV3Key V3密钥
* @return
*/
@Builder
public Config(String appid,
String mchId,
String privateKey,
String mchSerialNo,
String apiV3Key,
String domain,String notifyUrl) {
this.appid = appid;
this.mchId = mchId;
this.privateKey = privateKey;
this.mchSerialNo = mchSerialNo;
this.apiV3Key = apiV3Key;
this.domain=domain;
this.notifyUrl = notifyUrl;
}
public Config() {
}
}
@@ -0,0 +1,50 @@
package com.itheima.sfbx.trade.client.wechat;
import com.itheima.sfbx.trade.client.wechat.operators.*;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
/**
* @ClassName Factory.java
* @Description 封装对于微信支付工程类
*/
@Data
@Log4j2
public class Factory {
Config config;
public void setOptions(Config config) {
this.config = config;
}
//基础服务
public Common Common(){
return new Common(config);
}
//面对面支付
public FaceToFace FaceToFace(){
return new FaceToFace(config);
}
//PC网页支付
public Page Page(){
return new Page(config);
}
//手机网页支付
public Wap Wap(){
return new Wap(config);
}
//APP网页支付
public App App(){
return new App(config);
}
//H5
public H5 H5(){
return new H5(config);
}
}
@@ -0,0 +1,144 @@
package com.itheima.sfbx.trade.client.wechat;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
/**
* @ClassName WechatPayHttpClient.java
* @Description 支付宝支付远程调用对象
*/
@Slf4j
@Data
@NoArgsConstructor
public class WechatPayHttpClient {
//商户号
String mchId;
//私钥字符串
String privateKey;
//商户证书序列号
String mchSerialNo;
//V3密钥
String apiV3Key;
//请求地址
String domain;
@Builder
public WechatPayHttpClient(String mchId,
String privateKey,
String mchSerialNo,
String apiV3Key,
String domain) {
this.mchId = mchId;
this.privateKey = privateKey;
this.mchSerialNo = mchSerialNo;
this.apiV3Key = apiV3Key;
this.domain = domain;
}
/***
* @description 构建CloseableHttpClient远程请求对象
* @return: org.apache.http.impl.client.CloseableHttpClient
*/
private CloseableHttpClient createHttpClient() throws UnsupportedEncodingException {
// 加载商户私钥(privateKey:私钥字符串)
PrivateKey merchantPrivateKey = PemUtil
.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
try {
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
log.error("流处理异常:{}", ExceptionsUtil.getStackTraceAsString(e));
return null;
} catch (GeneralSecurityException e) {
log.error("微信支付权限异常:{}", ExceptionsUtil.getStackTraceAsString(e));
return null;
} catch (HttpCodeException e) {
log.error("微信支付请求异常:{}", ExceptionsUtil.getStackTraceAsString(e));
return null;
}
Verifier verifier = null;
try {
verifier = certificatesManager.getVerifier(mchId);
} catch (NotFoundException e) {
log.error("微信支付验证者创建异常:{}", ExceptionsUtil.getStackTraceAsString(e));
return null;
}
// 初始化httpClient
return com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
}
/***
* @description 支持post请求的远程调用
* @param params 携带请求参数
* @return 返回字符串
*/
public String doPost(ObjectNode params) throws IOException {
HttpPost httpPost = new HttpPost("https://"+domain);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type","application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(bos, params);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = this.createHttpClient().execute(httpPost);
return EntityUtils.toString(response.getEntity());
}
/***
* @description 支持get请求的远程调用
* @param param 在路径中请求的参数
* @return
* @return: 返回字符串
*/
public String doGet(String param) throws URISyntaxException, IOException {
URIBuilder uriBuilder = new URIBuilder("https://"+domain+param);
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = this.createHttpClient().execute(httpGet);
return EntityUtils.toString(response.getEntity());
}
}
@@ -0,0 +1,67 @@
package com.itheima.sfbx.trade.client.wechat.operators;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itheima.sfbx.trade.client.wechat.WechatPayHttpClient;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.response.AppPayResponse;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
* @ClassName Appprecreate.java
* @Description app的支付
*/
@Slf4j
public class App {
private Config config;
public App(Config config) {
this.config=config;
}
/***
* @description 统一收单线下交易预创建
* @param outTradeNo 发起支付传递的交易单号
* @param amount 交易金额【元】
* @param description 商品描述
* @return
*/
public AppPayResponse pay(String outTradeNo, String amount, String description) throws Exception {
//请求地址
String uri ="/v3/pay/transactions/app";
//系统参数封装
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
//业务数据的封装
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode bodyParams = objectMapper.createObjectNode();
BigDecimal bigDecimal = new BigDecimal(amount);
BigDecimal multiply = bigDecimal.multiply(new BigDecimal(100));
bodyParams.put("mchid",config.getMchId())
.put("appid",config.getAppid() )
.put("description", description)
.put("notify_url", config.getNotifyUrl())
.put("out_trade_no", outTradeNo);
bodyParams.putObject("amount")
.put("total", multiply.intValue());
String body = httpClient.doPost(bodyParams);
AppPayResponse appPayResponse = JSONObject.parseObject(body, AppPayResponse.class);
if (!EmptyUtil.isNullOrEmpty(appPayResponse.getPrepayId())){
appPayResponse.setCode("200");
appPayResponse.setMessage("网关请求成功");
appPayResponse.setSubCode("10000");
appPayResponse.setSubMessage("App支付PrepayId生成成功");
}
return appPayResponse;
}
}
@@ -0,0 +1,145 @@
package com.itheima.sfbx.trade.client.wechat.operators;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.WechatPayHttpClient;
import com.itheima.sfbx.trade.client.wechat.response.CloseResponse;
import com.itheima.sfbx.trade.client.wechat.response.QueryResponse;
import com.itheima.sfbx.trade.client.wechat.response.RefundResponse;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
* @ClassName Common.java
* @Description 基础交易API封装
*/
@Slf4j
public class Common {
private Config config;
public Common(Config config) {
this.config=config;
}
/***
* @description 查询交易结果
* @param outTradeNo 发起支付传递的交易单号
* @return
*/
public QueryResponse query(String outTradeNo) throws Exception {
//请求地址
String uri ="/v3/pay/transactions/out-trade-no";
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
String uriParams ="/"+outTradeNo+"?mchid="+config.getMchId();
String body = httpClient.doGet(uriParams);
QueryResponse queryResponse = JSONObject.parseObject(body, QueryResponse.class);
if (!EmptyUtil.isNullOrEmpty(queryResponse.getTradeState())){
queryResponse.setCode("200");
queryResponse.setMessage("网关请求成功");
}
return queryResponse;
}
/***
* @description 统一收单交易退款接口查询
* @param outRefundNo 商户系统内部的退款单号
* @return
*/
public RefundResponse queryRefund(String outRefundNo) throws Exception {
//请求地址
String uri ="/v3/refund/domestic/refunds";
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
String uriParams ="/"+outRefundNo;
String body = httpClient.doGet(uriParams);
RefundResponse refundResponse = JSONObject.parseObject(body, RefundResponse.class);
if (!EmptyUtil.isNullOrEmpty(refundResponse.getStatus())){
refundResponse.setCode("200");
refundResponse.setMessage("网关请求成功");
}
return refundResponse;
}
/***
* @description 统一收单交易退款接口
* @param outTradeNo 发起支付传递的交易单号
* @param amount 退款金额
* @param outRefundNo 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔
* @param total 原订单金额
* @return
*/
public RefundResponse refund(String outTradeNo, String amount,
String outRefundNo, String total) throws Exception {
//请求地址
String uri ="/v3/refund/domestic/refunds";
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode bodyParams = objectMapper.createObjectNode();
BigDecimal bigDecimal = new BigDecimal(amount);
BigDecimal multiply = bigDecimal.multiply(new BigDecimal(100));
BigDecimal bigDecimalTotal = new BigDecimal(total);
BigDecimal multiplyTotal = bigDecimalTotal.multiply(new BigDecimal(100));
bodyParams.put("out_refund_no", outRefundNo)
.put("out_trade_no", outTradeNo);
bodyParams.putObject("amount")
.put("refund", multiply.intValue())
.put("total", multiplyTotal.intValue())
.put("currency", "CNY");
String body = httpClient.doPost(bodyParams);
RefundResponse refundResponse = JSONObject.parseObject(body, RefundResponse.class);
if (!EmptyUtil.isNullOrEmpty(refundResponse.getStatus())){
refundResponse.setCode("200");
refundResponse.setMessage("网关请求成功");
}
return refundResponse;
}
/***
* @description 统一关闭订单
* 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
* 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
* @param outTradeNo 外部交易单号
* @return
*/
public CloseResponse close(String outTradeNo) throws Exception {
//请求地址
String uri ="/v3/pay/transactions/out-trade-no/"+outTradeNo+"/close";
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode bodyParams = objectMapper.createObjectNode();
bodyParams.put("mchid", config.getMchId());
String body = httpClient.doPost(bodyParams);
CloseResponse closeResponse = new CloseResponse();
closeResponse.setCode("200");
closeResponse.setMessage("网关请求成功");
return closeResponse;
}
}
@@ -0,0 +1,69 @@
package com.itheima.sfbx.trade.client.wechat.operators;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.WechatPayHttpClient;
import com.itheima.sfbx.trade.client.wechat.response.PreCreateResponse;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
* @ClassName FaceToFace.java
* @Description 面對面支付
*/
@Slf4j
public class FaceToFace {
private Config config;
public FaceToFace(Config config) {
this.config=config;
}
/**
* @description 扫用户出示的付款码,完成付款
* @param outTradeNo 交易单号
* @param amount 交易金额
* @param description 描述
* @return 支付结果
*/
public PreCreateResponse precreate(String outTradeNo, String amount, String description) throws Exception {
//请求地址
String uri ="/v3/pay/transactions/native";
//系统参数封装
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
//业务数据的封装
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode bodyParams = objectMapper.createObjectNode();
BigDecimal bigDecimal = new BigDecimal(amount);
BigDecimal multiply = bigDecimal.multiply(new BigDecimal(100));
bodyParams.put("mchid",config.getMchId())
.put("appid",config.getAppid() )
.put("description", description)
.put("notify_url", config.getNotifyUrl())
.put("out_trade_no", outTradeNo);
bodyParams.putObject("amount")
.put("total", multiply.intValue());
String body = httpClient.doPost(bodyParams);
PreCreateResponse preCreateResponse = JSONObject.parseObject(body, PreCreateResponse.class);
if (!EmptyUtil.isNullOrEmpty(preCreateResponse.getCodeUrl())){
preCreateResponse.setCode("200");
preCreateResponse.setMessage("网关请求成功");
preCreateResponse.setSubCode("10000");
preCreateResponse.setSubMessage("FaceToFace支付CodeUrl生成成功");
}
return preCreateResponse;
}
}
@@ -0,0 +1,72 @@
package com.itheima.sfbx.trade.client.wechat.operators;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itheima.sfbx.trade.client.wechat.WechatPayHttpClient;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.response.H5PayResponse;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
* @ClassName H5.java
* @Description H5的支付SDK封装
*/
@Slf4j
public class H5 {
private Config config;
public H5(Config config) {
this.config=config;
}
/***
* @description H5下单API
* @param outTradeNo 发起支付传递的交易单号
* @param amount 交易金额【元】
* @param description 商品描述
* @param payerClientIp 客户端IP
* @param type 场景类型 示例值:iOS, Android, Wap
* @return
*/
public H5PayResponse pay(String outTradeNo, String amount,
String description,String payerClientIp,String type) throws Exception {
//请求地址
String uri ="/v3/pay/transactions/h5";
//系统参数封装
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
//业务数据的封装
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode bodyParams = objectMapper.createObjectNode();
BigDecimal bigDecimal = new BigDecimal(amount);
BigDecimal multiply = bigDecimal.multiply(new BigDecimal(100));
bodyParams.put("mchid",config.getMchId())
.put("appid",config.getAppid() )
.put("description", description)
.put("notify_url", config.getNotifyUrl())
.put("out_trade_no", outTradeNo);
bodyParams.putObject("amount")
.put("total", multiply.intValue());
bodyParams.putObject("scene_info").put("payer_client_ip", payerClientIp)
.putObject("h5_info").put("type",type);
String body = httpClient.doPost(bodyParams);
H5PayResponse h5PayResponse = JSONObject.parseObject(body, H5PayResponse.class);
if (!EmptyUtil.isNullOrEmpty(h5PayResponse.getH5Url())){
h5PayResponse.setCode("200");
h5PayResponse.setMessage("网关请求成功");
h5PayResponse.setSubCode("10000");
h5PayResponse.setSubMessage("微信Page支付PrepayId生成成功");
}
return h5PayResponse;
}
}
@@ -0,0 +1,73 @@
package com.itheima.sfbx.trade.client.wechat.operators;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itheima.sfbx.trade.client.wechat.WechatPayHttpClient;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.response.PagePayResponse;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.math.BigDecimal;
/**
* @ClassName Page.java
* @Description TODO
*/
@Slf4j
public class Page {
private Config config;
public Page(Config config) {
this.config=config;
}
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param outTradeNo 外部订单【业务系统中间的交易单号】
* @param amount 交易的金额【单位:分】
* @param description 商品的描述
* @param openId 用户在当前公众号下的OpenID
* @return TradeVo对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
public PagePayResponse pay(String outTradeNo, String amount,
String description, String openId) throws IOException {
//请求地址
String uri ="/v3/pay/transactions/jsapi";
//发起对应的请求
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode bodyParams = objectMapper.createObjectNode();
BigDecimal bigDecimal = new BigDecimal(amount);
BigDecimal multiply = bigDecimal.multiply(new BigDecimal(100));
bodyParams.put("mchid",config.getMchId())
.put("appid",config.getAppid() )
.put("description", description)
.put("notify_url", config.getNotifyUrl())
.put("out_trade_no", outTradeNo);
//交易金额
bodyParams.putObject("amount").put("total", multiply.intValue());
//付款者OpenId信息
bodyParams.putObject("payer").put("openid",openId);
String body = httpClient.doPost(bodyParams);
PagePayResponse pagePayResponse = JSONObject.parseObject(body, PagePayResponse.class);
if (!EmptyUtil.isNullOrEmpty(pagePayResponse.getPrepayId())){
pagePayResponse.setCode("200");
pagePayResponse.setMessage("网关请求成功");
pagePayResponse.setSubCode("10000");
pagePayResponse.setSubMessage("微信Page支付PrepayId生成成功");
}
return pagePayResponse;
}
}
@@ -0,0 +1,72 @@
package com.itheima.sfbx.trade.client.wechat.operators;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.WechatPayHttpClient;
import com.itheima.sfbx.trade.client.wechat.response.WapPayResponse;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
* @ClassName Wap.java
* @Description 手机网页支付
*/
@Slf4j
public class Wap {
private Config config;
public Wap(Config config) {
this.config=config;
}
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param outTradeNo 外部订单【业务系统中间的交易单号】
* @param amount 交易的金额【单位:分】
* @param description 商品的描述
* @param openId 用户在当前公众号下的OpenID
* @return TradeVo对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
public WapPayResponse pay(String outTradeNo, String amount,
String description, String openId) throws Exception {
//请求地址
String uri ="/v3/pay/transactions/jsapi";
//发起对应的请求
WechatPayHttpClient httpClient = WechatPayHttpClient.builder()
.mchId(config.getMchId())
.mchSerialNo(config.getMchSerialNo())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.domain(config.getDomain()+uri)
.build();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode bodyParams = objectMapper.createObjectNode();
BigDecimal bigDecimal = new BigDecimal(amount);
BigDecimal multiply = bigDecimal.multiply(new BigDecimal(100));
bodyParams.put("mchid",config.getMchId())
.put("appid",config.getAppid() )
.put("description", description)
.put("notify_url", config.getNotifyUrl())
.put("out_trade_no", outTradeNo);
//交易金额
bodyParams.putObject("amount").put("total", multiply.intValue());
//付款者OpenId信息
bodyParams.putObject("payer").put("openid",openId);
String body = httpClient.doPost(bodyParams);
WapPayResponse wapPayResponse = JSONObject.parseObject(body, WapPayResponse.class);
if (!EmptyUtil.isNullOrEmpty(wapPayResponse.getPrepayId())){
wapPayResponse.setCode("200");
wapPayResponse.setMessage("网关请求成功");
wapPayResponse.setSubCode("10000");
wapPayResponse.setSubMessage("微信Page支付PrepayId生成成功");
}
return wapPayResponse;
}
}
@@ -0,0 +1,19 @@
package com.itheima.sfbx.trade.client.wechat.response;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName AmountResponse.java
* @Description 金额信息
*/
@Data
@NoArgsConstructor
public class AmountResponse {
//订单总金额【分】
private Integer total;
//退款总金额【分】
private Integer refund;
}
@@ -0,0 +1,18 @@
package com.itheima.sfbx.trade.client.wechat.response;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName PagePayResponse.java
* @Description TODO
*/
@Data
@NoArgsConstructor
public class AppPayResponse extends BasicResponse{
@JSONField(name="prepay_id")
private String prepayId;
}
@@ -0,0 +1,20 @@
package com.itheima.sfbx.trade.client.wechat.response;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName AppPreCreateResponse.java
* @Description TODO
*/
@Data
@NoArgsConstructor
public class AppPreCreateResponse {
//请求返回编码
private String code;
@JSONField(name="prepay_id")
private String prepayId;
}
@@ -0,0 +1,23 @@
package com.itheima.sfbx.trade.client.wechat.response;
import lombok.Data;
/**
* @ClassName BasicResponse.java
* @Description 返回基础对象
*/
@Data
public class BasicResponse {
//网关请求返回编码
public String code;
//网关请求返回信息
public String message;
//业务请求返回编码
public String subCode;
//业务请求返回信息
public String subMessage;
}
@@ -0,0 +1,14 @@
package com.itheima.sfbx.trade.client.wechat.response;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName CloseResponse.java
* @Description 关闭订单返回结果
*/
@Data
@NoArgsConstructor
public class CloseResponse extends BasicResponse {
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.client.wechat.response;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName H5PayResponse.java
* @Description TODO
*/
@Data
@NoArgsConstructor
public class H5PayResponse extends BasicResponse{
@JSONField(name="h5_url")
private String h5Url;
}
@@ -0,0 +1,18 @@
package com.itheima.sfbx.trade.client.wechat.response;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName PagePayResponse.java
* @Description TODO
*/
@Data
@NoArgsConstructor
public class PagePayResponse extends BasicResponse {
@JSONField(name="prepay_id")
private String prepayId;
}
@@ -0,0 +1,19 @@
package com.itheima.sfbx.trade.client.wechat.response;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName PreCreateResponse.java
* @Description TODO
*/
@Data
@NoArgsConstructor
public class PreCreateResponse extends BasicResponse{
//二维码请求地址
@JSONField(name="code_url")
private String codeUrl;
}
@@ -0,0 +1,27 @@
package com.itheima.sfbx.trade.client.wechat.response;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName QueryResponse.java
* @Description 统一收单线下交易查询返回
*/
@Data
@NoArgsConstructor
public class QueryResponse extends BasicResponse{
//商户订单号
@JSONField(name="out_trade_no")
private String outTradeNo;
//交易状态:SUCCESS:支付成功,REFUND:转入退款,NOTPAY:未支付,CLOSED:已关闭,REVOKED:已撤销(仅付款码支付会返回)
//USERPAYING:用户支付中(仅付款码支付会返回),PAYERROR:支付失败(仅付款码支付会返回)
@JSONField(name="trade_state")
private String tradeState;
//交易状态描述
@JSONField(name="trade_state_desc")
private String tradeStateDesc;
}
@@ -0,0 +1,32 @@
package com.itheima.sfbx.trade.client.wechat.response;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName RefundResponse.java
* @Description 退款返回
*/
@Data
@NoArgsConstructor
public class RefundResponse extends BasicResponse {
//商户退款单号
@JSONField(name = "out_refund_no")
private String outRefundNo;
//商户订单号
@JSONField(name = "out_trade_no")
private String outTradeNo;
//退款状态
//SUCCESS:退款成功
//CLOSED:退款关闭
//PROCESSING:退款处理中
//ABNORMAL:退款异常
private String status;
//金额信息
private AmountResponse amount;
}
@@ -0,0 +1,18 @@
package com.itheima.sfbx.trade.client.wechat.response;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName PagePayResponse.java
* @Description TODO
*/
@Data
@NoArgsConstructor
public class WapPayResponse extends BasicResponse {
@JSONField(name="prepay_id")
private String prepayId;
}
@@ -0,0 +1,84 @@
package com.itheima.sfbx.trade.config;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.easysdk.kernel.Config;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.PayChannelVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.face.PayChannelFace;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.stream.Collectors;
/**
* @ClassName AlipayConfig.java
* @Description 支付宝配置类
*/
@Slf4j
@Configuration
public class AliPayConfig {
@Autowired
PayChannelFace payChannelFace;
/***
* @description easy-sdk创建配置
* @return
*/
public Config config(String companyNo ){
//1、缓存配置
PayChannelVO payChannelVO = payChannelFace.findPayChannelVlid(TradeConstant.TRADE_CHANNEL_ALI_PAY);
//2、配置信息校验
if (EmptyUtil.isNullOrEmpty(payChannelVO)){
throw new ProjectException(TradeEnum.CONFIG_EMPT);
}
//2、创建配置
Config config = new Config();
config.protocol = "https";
config.gatewayHost =payChannelVO.getDomain();
config.signType = "RSA2";
config.appId = payChannelVO.getAppId();
//2.1、配置应用私钥
config.merchantPrivateKey = payChannelVO.getMerchantPrivateKey();
//2.2、配置支付宝公钥
config.alipayPublicKey = payChannelVO.getPublicKey();
//2.3、可设置异步通知接收服务地址(可选)
config.notifyUrl = payChannelVO.getNotifyUrl()+"/"+payChannelVO.getCompanyNo();
//2.4、设置AES密钥,调用AES加解密相关接口时需要(可选)
config.encryptKey = payChannelVO.getEncryptKey();
//2.5、配置信息放入configHashMap中
return config;
}
/***
* @description 通用-sdk创建配置
* @return
*/
public DefaultAlipayClient createAlipayClient(String companyNo ){
//1、缓存配置
PayChannelVO payChannelVO = payChannelFace.findPayChannelVlid(TradeConstant.TRADE_CHANNEL_ALI_PAY);
//2、配置信息校验
if (EmptyUtil.isNullOrEmpty(payChannelVO)){
throw new ProjectException(TradeEnum.CONFIG_EMPT);
}
return new DefaultAlipayClient("https://"+payChannelVO.getDomain()+"/gateway.do",
payChannelVO.getAppId(),
payChannelVO.getMerchantPrivateKey(),
"json",
"GBK",
payChannelVO.getPublicKey(),
"RSA2");
}
}
@@ -0,0 +1,56 @@
package com.itheima.sfbx.trade.config;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.basic.OtherConfigVO;
import com.itheima.sfbx.framework.commons.dto.trade.PayChannelVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.face.PayChannelFace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @ClassName WeChatPayConfig.java
* @Description 微信配置类
*/
@Configuration
public class WechatPayConfig {
@Autowired
PayChannelFace payChannelFace;
/***
* @description 获得配置
* @return
*/
public Config config(String companyNo){
//1、缓存配置
PayChannelVO payChannelVO = payChannelFace.findPayChannelVlid(TradeConstant.TRADE_CHANNEL_WECHAT_PAY);
//2、配置信息校验
if (EmptyUtil.isNullOrEmpty(payChannelVO)){
throw new ProjectException(TradeEnum.CONFIG_EMPT);
}
//2、转换其他属性为map结构
List<OtherConfigVO> otherConfigs = payChannelVO.getOtherConfigs();
Map<String,String> otherConfigMap = new HashMap<>();
otherConfigs.forEach(n->{
otherConfigMap.put(n.getConfigKey(),n.getConfigValue());
});
return Config.builder()
.appid(payChannelVO.getAppId())
.domain(payChannelVO.getDomain())
.mchId(otherConfigMap.get("mchId"))
.mchSerialNo(otherConfigMap.get("mchSerialNo"))
.apiV3Key(otherConfigMap.get("apiV3Key"))
.privateKey(payChannelVO.getMerchantPrivateKey())
.notifyUrl(payChannelVO.getNotifyUrl()+"/"+payChannelVO.getCompanyNo())
.build();
}
}
@@ -0,0 +1,18 @@
package com.itheima.sfbx.trade.face;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName AppPayFace.java
* @Description 原生APP支付Face接口
*/
public interface AppPayFace {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO appTrade(TradeVO tradeVO);
}
@@ -0,0 +1,69 @@
package com.itheima.sfbx.trade.face;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName CommonPayFace.java
* @Description 基础交易face接口
*/
public interface CommonPayFace {
/***
* @description 查看二维码信息
* 收银员通过收银台或商户后台调用此接口,生成二维码后,展示给用户,商户可以多次展示二维码
* @param tradeVO 交易单
* @return 二维码路径
*/
String queryQrCode(TradeVO tradeVO);
/***
* @description 统一收单线下交易查询
* 该接口提供所有支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。
* @param tradeVO 交易单
* @return
*/
TradeVO queryTrade(TradeVO tradeVO) throws ProjectException;
/***
* @description 统一收单交易退款接口
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
* 将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* @param tradeVO 交易单
* @return
*/
TradeVO refundTrade(TradeVO tradeVO) throws ProjectException;
/***
* @description 统一收单交易退款查询接口
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
* 将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* @param refundRecordVO 退款交易单
* @return
*/
RefundRecordVO queryRefundTrade(RefundRecordVO refundRecordVO) throws ProjectException;
/***
* @description 统一关闭订单
* 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
* 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
* @param tradeVO 退款交易单
* @return
*/
TradeVO closeTrade(TradeVO tradeVO);
/***
* @description 为方便商户快速查账,支持商户通过本接口获取商户离线账单下载地址
* @param tradeVO 退款交易单
* @return
*/
TradeVO downLoadBill(TradeVO tradeVO);
/**
* @description 计划任务:同步交易单结果
* @return
*/
Boolean syncPaymentJob();
}
@@ -0,0 +1,24 @@
package com.itheima.sfbx.trade.face;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName FaceToFacePayFace.java
* @Description 面对面支付Face接口
*/
public interface FaceToFacePayFace {
/***
* @description 扫用户出示的付款码,完成付款
* @param tradeVO 订单
* @return 支付结果
*/
TradeVO payTrade(TradeVO tradeVO);
/***
* @description 生成交易付款码,待用户扫码付款
* @param tradeVO 订单
* @return 二维码路径
*/
TradeVO precreateTrade(TradeVO tradeVO);
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.face;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName PagePayFace.java
* @Description PC网页支付Face接口
*/
public interface PagePayFace {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO pageTrade(TradeVO tradeVO);
}
@@ -0,0 +1,53 @@
package com.itheima.sfbx.trade.face;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.dto.trade.PayChannelVO;
import java.util.List;
/**
* @ClassName PayChannelFace.java
* @Description 渠道Face接口
*/
public interface PayChannelFace {
/**
* @Description 渠道列表
* @param smsChannelVO 查询条件
* @param pageNum 页码
* @param pageSize 每页条数
* @return Page<PayChannelVO>
*/
Page<PayChannelVO> findPayChannelVOPage(PayChannelVO smsChannelVO,
int pageNum,
int pageSize)throws ProjectException;
/**
* @Description 创建渠道
* @param smsChannelVO 对象信息
* @return PayChannelVO
*/
PayChannelVO createPayChannel(PayChannelVO smsChannelVO)throws ProjectException;
/**
* @Description 修改渠道
* @param smsChannelVO 对象信息
* @return Boolean
*/
Boolean updatePayChannel(PayChannelVO smsChannelVO)throws ProjectException;
/**
* @Description 删除渠道
* @param checkedIds 选择中对象Ids
* @return Boolean
*/
Boolean deletePayChannel(String[] checkedIds)throws ProjectException;
/**
* @Description 查询唯一有效支付渠道
* @param channelLabel 渠道标识
* @return Boolean
*/
PayChannelVO findPayChannelVlid(String channelLabel);
}
@@ -0,0 +1,32 @@
package com.itheima.sfbx.trade.face;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName PeriodicPayFace.java
* @Description 周期性扣款接口
*/
public interface PeriodicPayFace {
/***
* @description App支付并签约
* @param tradeVO 交易单信息
* @return
*/
TradeVO appPaySign(TradeVO tradeVO);
/***
* @description H5先签约
* @param tradeVO 交易单信息
* @return
*/
TradeVO h5SignContract(TradeVO tradeVO);
/***
* @description H5签约后代扣
* @param tradeVO 交易单信息
* @return
*/
TradeVO h5PeriodicPay(TradeVO tradeVO);
}
@@ -0,0 +1,20 @@
package com.itheima.sfbx.trade.face;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import java.util.List;
/**
* @ClassName TradeFace.java
* @Description 交易Face接口
*/
public interface RefundRecordFace {
/***
* @description 按退款状态查询退款记录
* @param refundStatus
* @return
*/
List<RefundRecordVO> findRefundRecordByRefundStatus(String refundStatus);
}
@@ -0,0 +1,52 @@
package com.itheima.sfbx.trade.face;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.sfbx.framework.commons.dto.trade.SignContractVO;
import com.itheima.sfbx.trade.pojo.SignContract;
import java.util.List;
/**
* @Description 签约合同服务类
*/
public interface SignContractFace {
/**
* @Description 签约合同分页
* @param payChannelVO 查询条件
* @param pageNum 当前页
* @param pageSize 当前页
* @return Page<SignContract>
*/
Page<SignContractVO> findSignContractPage(SignContractVO payChannelVO, int pageNum, int pageSize);
/**
* @Description 创建签约合同
* @param payChannelVO 对象信息
* @return SignContract
*/
SignContractVO createSignContract(SignContractVO payChannelVO);
/**
* @Description 修改签约合同
* @param payChannelVO 对象信息
* @return Boolean
*/
Boolean updateSignContract(SignContractVO payChannelVO);
/**
* @Description 删除签约合同
* @param checkedIds 选择的签约合同ID
* @return Boolean
*/
Boolean deleteSignContract(String[] checkedIds);
/***
* @description 多条件查询
* @param signContractVO
* @return
*/
List<SignContractVO> findSignContractList(SignContractVO signContractVO);
}
@@ -0,0 +1,31 @@
package com.itheima.sfbx.trade.face;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import java.util.List;
/**
* @ClassName TradeFace.java
* @Description 交易Face接口
*/
public interface TradeFace {
/***
* @description 按交易状态查询最近10分钟内交易单
* @param tradeState
* @return
*/
List<TradeVO> findTradeByTradeState(String tradeState);
/***
* @description 交易记录分页查询
*
* @param tradeVO
* @param pageNum
* @param pageSize
* @return Page<TradeVO>
*/
Page<TradeVO> findTradeVOPage(TradeVO tradeVO, int pageNum, int pageSize);
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.face;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName WapPayFace.java
* @Description 手机网页支付Face接口
*/
public interface WapPayFace {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO wapTrade(TradeVO tradeVO);
}
@@ -0,0 +1,52 @@
package com.itheima.sfbx.trade.face.impl;
import com.itheima.sfbx.trade.adapter.AppPayAdapter;
import com.itheima.sfbx.framework.commons.constant.trade.TradeCacheConstant;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.trade.face.AppPayFace;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @ClassName AppPayFaceImpl.java
* @Description 手机网页支付Face接口实现
*/
@Slf4j
@Component
public class AppPayFaceImpl implements AppPayFace {
@Autowired
RedissonClient redissonClient;
@Autowired
AppPayAdapter appPayAdapter;
@Override
public TradeVO appTrade(TradeVO tradeVO) {
//防止并发:使用订单号,对本次交易加锁
String key = TradeCacheConstant.CREATE_PAY + tradeVO.getProductOrderNo();
RLock lock = redissonClient.getFairLock(key);
try {
if (lock.tryLock(TradeCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS)){
return appPayAdapter.appTrade(tradeVO);
}else {
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
} catch (Exception e) {
log.error("统一收单线下交易预创建异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}finally {
//释放锁
lock.unlock();
}
}
}
@@ -0,0 +1,118 @@
package com.itheima.sfbx.trade.face.impl;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.adapter.CommonPayAdapter;
import com.itheima.sfbx.trade.face.CommonPayFace;
import com.itheima.sfbx.framework.commons.constant.trade.TradeCacheConstant;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.trade.service.ITradeService;
import com.itheima.sfbx.trade.service.impl.TradeServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.validation.constraints.Email;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
* @ClassName CommonPayFaceImpl.java
* @Description 基础交易face接口实现
*/
@Component
@Slf4j
public class CommonPayFaceImpl implements CommonPayFace {
@Autowired
CommonPayAdapter commonPayAdapter;
@Autowired
RedissonClient redissonClient;
@Autowired
ITradeService tradeService;
@Resource(name = "syncPaymentExecutor")
Executor periodicPayExecutor;
@Override
public String queryQrCode(TradeVO tradeVO) {
return commonPayAdapter.queryQrCode(tradeVO);
}
@Override
@Transactional
public TradeVO queryTrade(TradeVO tradeVO) throws ProjectException {
return commonPayAdapter.queryTrade(tradeVO);
}
@Override
@Transactional
public TradeVO refundTrade(TradeVO tradeVO) throws ProjectException {
//1、对交易订单加锁
Long productOrderNo = tradeVO.getProductOrderNo();
String key = TradeCacheConstant.REFUND_PAY + productOrderNo;
RLock lock = redissonClient.getFairLock(key);
try {
boolean flag = lock.tryLock(TradeCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS);
if (flag){
return commonPayAdapter.refundTrade(tradeVO);
}else {
throw new ProjectException(TradeEnum.TRAD_REFUND_FAIL);
}
} catch (Exception e) {
log.error("统一收单交易退款接口异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_REFUND_FAIL);
}finally {
lock.unlock();
}
}
@Override
@Transactional
public RefundRecordVO queryRefundTrade(RefundRecordVO refundRecordVO) throws ProjectException {
return commonPayAdapter.queryRefundTrade(refundRecordVO);
}
@Override
public TradeVO closeTrade(TradeVO tradeVO) {
return commonPayAdapter.closeTrade(tradeVO);
}
@Override
public TradeVO downLoadBill(TradeVO tradeVO) {
return commonPayAdapter.downLoadBill(tradeVO);
}
@Override
public Boolean syncPaymentJob() {
//查询最近10分钟内待支付订单
List<Trade> trades = tradeService.findTradeByTradeState(TradeConstant.TRADE_WAIT);
if (EmptyUtil.isNullOrEmpty(trades)){
return true;
}
//线程处周期性扣款
for (Trade trade: trades) {
periodicPayExecutor.execute(new Runnable() {
@Override
public void run() {
queryTrade(BeanConv.toBean(trade,TradeVO.class));
}
});
}
return true;
}
}
@@ -0,0 +1,77 @@
package com.itheima.sfbx.trade.face.impl;
import com.itheima.sfbx.trade.adapter.FaceToFacePayAdapter;
import com.itheima.sfbx.trade.face.FaceToFacePayFace;
import com.itheima.sfbx.framework.commons.constant.trade.TradeCacheConstant;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @ClassName FaceToFacePayFaceImpl.java
* @Description 面对面支付Face接口实现
*/
@Slf4j
@Component
public class FaceToFacePayFaceImpl implements FaceToFacePayFace {
@Autowired
FaceToFacePayAdapter faceToFacePayAdapter;
@Autowired
RedissonClient redissonClient;
@Override
public TradeVO payTrade(TradeVO tradeVO) {
//1、对交易订单加锁
Long productOrderNo = tradeVO.getProductOrderNo();
String key = TradeCacheConstant.CREATE_PAY + productOrderNo;
RLock lock = redissonClient.getFairLock(key);
try {
if (lock.tryLock(TradeCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS)){
return faceToFacePayAdapter.payTrade(tradeVO);
}else {
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
} catch (Exception e) {
log.error("统一收单线下交易预创建异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}finally {
//释放锁
lock.unlock();
}
}
@Override
@GlobalTransactional
public TradeVO precreateTrade(TradeVO tradeVO) {
//1、对交易订单加锁
Long productOrderNo = tradeVO.getProductOrderNo();
String key = TradeCacheConstant.CREATE_PAY + productOrderNo;
RLock lock = redissonClient.getFairLock(key);
try {
if (lock.tryLock(TradeCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS)){
return faceToFacePayAdapter.precreateTrade(tradeVO);
}else {
throw new ProjectException(TradeEnum.TRAD_QRCODE_FAIL);
}
} catch (Exception e) {
log.error("统一收单线下交易预创建异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_QRCODE_FAIL);
}finally {
//释放锁
lock.unlock();
}
}
}
@@ -0,0 +1,53 @@
package com.itheima.sfbx.trade.face.impl;
import com.itheima.sfbx.trade.adapter.PagePayAdapter;
import com.itheima.sfbx.framework.commons.constant.trade.TradeCacheConstant;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.trade.face.PagePayFace;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @ClassName PagePayFaceImpl.java
* @Description PC网页支付Face接口实现
*/
@Slf4j
@Component
public class PagePayFaceImpl implements PagePayFace {
@Autowired
PagePayAdapter pagePayAdapter;
@Autowired
RedissonClient redissonClient;
@Override
public TradeVO pageTrade(TradeVO tradeVO) {
//1、对交易订单加锁
Long productOrderNo = tradeVO.getProductOrderNo();
String key = TradeCacheConstant.CREATE_PAY + productOrderNo;
RLock lock = redissonClient.getFairLock(key);
try {
if (lock.tryLock(TradeCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS)){
return pagePayAdapter.pageTrade(tradeVO);
}else {
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
} catch (Exception e) {
log.error("统一收单线下交易预创建异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}finally {
//释放锁
lock.unlock();
}
}
}
@@ -0,0 +1,114 @@
package com.itheima.sfbx.trade.face.impl;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.sfbx.framework.commons.constant.trade.TradeCacheConstant;
import com.itheima.sfbx.framework.commons.dto.basic.OtherConfigVO;
import com.itheima.sfbx.framework.commons.dto.trade.PayChannelVO;
import com.itheima.sfbx.framework.commons.enums.trade.PayChannelEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.face.PayChannelFace;
import com.itheima.sfbx.trade.pojo.PayChannel;
import com.itheima.sfbx.trade.service.IPayChannelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @ClassName PayChannelFaceImpl.java
* @Description 支付渠道Face接口接口实现
*/
@Slf4j
@Component
public class PayChannelFaceImpl implements PayChannelFace {
@Autowired
IPayChannelService payChannelService;
@Override
@Cacheable(value = TradeCacheConstant.PAGE,key ="#pageNum+'-'+#pageSize+'-'+#payChannelVO.hashCode()")
public Page<PayChannelVO> findPayChannelVOPage(PayChannelVO payChannelVO,
int pageNum,
int pageSize) throws ProjectException{
try {
Page<PayChannel> page = payChannelService.findPayChannelVOPage(payChannelVO, pageNum, pageSize);
Page<PayChannelVO> pageVo = BeanConv.toPage(page,PayChannelVO.class);
//结果集转换
List<PayChannelVO> payChannelVOList = pageVo.getRecords();
if (!EmptyUtil.isNullOrEmpty(payChannelVOList)){
payChannelVOList.forEach(n->{
if (!EmptyUtil.isNullOrEmpty(n.getOtherConfig())){
List<OtherConfigVO> list = JSONArray.parseArray(n.getOtherConfig(),OtherConfigVO.class);
n.setOtherConfigs(list);
}
});
}
pageVo.setRecords(payChannelVOList);
return pageVo;
} catch (Exception e) {
log.error("查询支付通道列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(PayChannelEnum.PAGE_FAIL);
}
}
@Override
@Transactional
@Caching(evict = {@CacheEvict(value = TradeCacheConstant.PAGE,allEntries = true),
@CacheEvict(value = TradeCacheConstant.PAY_CHANNEL_VLID,allEntries = true)})
public PayChannelVO createPayChannel(PayChannelVO payChannelVO) throws ProjectException{
try {
return BeanConv.toBean( payChannelService.createPayChannel(payChannelVO), PayChannelVO.class);
} catch (Exception e) {
log.error("保存支付通道异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(PayChannelEnum.CREATE_FAIL);
}
}
@Override
@Transactional
@Caching(evict = {@CacheEvict(value = TradeCacheConstant.PAGE,allEntries = true),
@CacheEvict(value = TradeCacheConstant.PAY_CHANNEL_VLID,allEntries = true)})
public Boolean updatePayChannel(PayChannelVO payChannelVO) throws ProjectException{
try {
return payChannelService.updatePayChannel(payChannelVO);
} catch (Exception e) {
log.error("保存支付通道异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(PayChannelEnum.UPDATE_FAIL);
}
}
@Override
@Transactional
@Caching(evict = {@CacheEvict(value = TradeCacheConstant.PAGE,allEntries = true),
@CacheEvict(value = TradeCacheConstant.PAY_CHANNEL_VLID,allEntries = true)})
public Boolean deletePayChannel(String[] checkedIds) throws ProjectException{
try {
return payChannelService.deletePayChannel(checkedIds);
} catch (Exception e) {
log.error("删除支付通道异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(PayChannelEnum.DELETE_FAIL);
}
}
@Override
@Cacheable(value = TradeCacheConstant.PAY_CHANNEL_VLID,key = "#channelLabel")
public PayChannelVO findPayChannelVlid(String channelLabel) {
PayChannelVO payChannelVO = BeanConv.toBean(payChannelService.findPayChannelVlid(channelLabel), PayChannelVO.class);
if (!EmptyUtil.isNullOrEmpty(payChannelVO)&&!EmptyUtil.isNullOrEmpty(payChannelVO.getOtherConfig())){
List<OtherConfigVO> list = JSONArray.parseArray(payChannelVO.getOtherConfig(),OtherConfigVO.class);
payChannelVO.setOtherConfigs(list);
}
return payChannelVO;
}
}
@@ -0,0 +1,73 @@
package com.itheima.sfbx.trade.face.impl;
import com.itheima.sfbx.framework.commons.constant.trade.TradeCacheConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.adapter.PeriodicPayAdepter;
import com.itheima.sfbx.trade.adapter.WapPayAdapter;
import com.itheima.sfbx.trade.face.PeriodicPayFace;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @ClassName PeriodicPayFaceImpl.java
* @Description 周期性扣款
*/
@Slf4j
@Component
public class PeriodicPayFaceImpl implements PeriodicPayFace {
@Autowired
PeriodicPayAdepter periodicPayAdepter;
@Autowired
RedissonClient redissonClient;
@Override
public TradeVO appPaySign(TradeVO tradeVO) {
try {
return periodicPayAdepter.appPaySign(tradeVO);
} catch (Exception e) {
log.error("签约预创建异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_SIGN_CONTRACT_FAIL);
}
}
@Override
public TradeVO h5SignContract(TradeVO tradeVO) {
try {
return periodicPayAdepter.h5SignContract(tradeVO);
} catch (Exception e) {
log.error("签约预创建异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_SIGN_CONTRACT_FAIL);
}
}
@Override
public TradeVO h5PeriodicPay(TradeVO tradeVO) {
//1、对交易订单加锁
Long productOrderNo = tradeVO.getProductOrderNo();
String key = TradeCacheConstant.CREATE_PAY + productOrderNo;
RLock lock = redissonClient.getFairLock(key);
try {
if (lock.tryLock(TradeCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS)){
return periodicPayAdepter.h5PeriodicPay(tradeVO);
}else {
throw new ProjectException(TradeEnum.TRAD_DEDUCTION_FAIL);
}
} catch (Exception e) {
log.error("周期支付异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_DEDUCTION_FAIL);
}finally {
//释放锁
lock.unlock();
}
}
}
@@ -0,0 +1,42 @@
package com.itheima.sfbx.trade.face.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.trade.face.RefundRecordFace;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import com.itheima.sfbx.trade.service.IRefundRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @ClassName TradeFace.java
* @Description 交易Face接口
*/
@Slf4j
@Component
public class ReFundRecordFaceImpl implements RefundRecordFace {
@Autowired
IRefundRecordService refundRecordService;
/***
* @description 按退款状态查询退款记录
* @param refundStatus
* @return
*/
@Override
public List<RefundRecordVO> findRefundRecordByRefundStatus(String refundStatus) {
LambdaQueryWrapper<RefundRecord> wrapper = Wrappers.<RefundRecord>lambdaQuery()
.eq(RefundRecord::getRefundStatus, refundStatus)
.eq(RefundRecord::getDataState, SuperConstant.DATA_STATE_0);
List<RefundRecord> refundRecordList = refundRecordService.list(wrapper);
return BeanConv.toBeanList(refundRecordList,RefundRecordVO.class);
}
}
@@ -0,0 +1,80 @@
package com.itheima.sfbx.trade.face.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.sfbx.framework.commons.dto.trade.SignContractVO;
import com.itheima.sfbx.framework.commons.enums.trade.SignContractEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.face.SignContractFace;
import com.itheima.sfbx.trade.mapper.SignContractMapper;
import com.itheima.sfbx.trade.pojo.SignContract;
import com.itheima.sfbx.trade.service.ISignContractService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/**
* @Description 签约合成服务实现类
*/
@Component
public class SignContractFaceImpl implements SignContractFace {
@Autowired
ISignContractService signContractService;
@Override
public Page<SignContractVO> findSignContractPage(SignContractVO signContractVO, int pageNum, int pageSize) {
try {
Page<SignContract> signContractPage = signContractService
.findSignContractPage(signContractVO, pageNum, pageSize);
return BeanConv.toPage(signContractPage,SignContractVO.class);
}catch (Exception e){
throw new ProjectException(SignContractEnum.PAGE_FAIL);
}
}
@Override
public SignContractVO createSignContract(SignContractVO signContractVO) {
try {
return BeanConv.toBean(signContractService.createSignContract(signContractVO),SignContractVO.class);
}catch (Exception e){
throw new ProjectException(SignContractEnum.SAVE_FAIL);
}
}
@Override
public Boolean updateSignContract(SignContractVO signContractVO) {
try {
return signContractService.updateSignContract(signContractVO);
}catch (Exception e){
throw new ProjectException(SignContractEnum.UPDATE_FAIL);
}
}
@Override
public Boolean deleteSignContract(String[] checkedIds) {
try {
return signContractService.deleteSignContract(checkedIds);
}catch (Exception e){
throw new ProjectException(SignContractEnum.DELETE_FAIL);
}
}
@Override
public List<SignContractVO> findSignContractList(SignContractVO signContractVO) {
try {
return BeanConv.toBeanList(signContractService
.findSignContractList(signContractVO),SignContractVO.class);
}catch (Exception e){
throw new ProjectException(SignContractEnum.LIST_FAIL);
}
}
}
@@ -0,0 +1,71 @@
package com.itheima.sfbx.trade.face.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.enums.trade.PayChannelEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.face.TradeFace;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.trade.service.IRefundRecordService;
import com.itheima.sfbx.trade.service.ITradeService;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/**
* @ClassName TradeFace.java
* @Description 交易Face接口
*/
@Slf4j
@Component
public class TradeFaceImpl implements TradeFace {
@Autowired
ITradeService tradeService;
@Autowired
IRefundRecordService refundRecordService;
@Override
public List<TradeVO> findTradeByTradeState(String tradeState) {
List<Trade> trades = tradeService.findTradeByTradeState(tradeState);
return BeanConv.toBeanList(trades,TradeVO.class);
}
@Override
public Page<TradeVO> findTradeVOPage(TradeVO tradeVO, int pageNum, int pageSize) {
try {
Page<Trade> page = tradeService.findPayChannelVOPage(tradeVO, pageNum, pageSize);
Page<TradeVO> pageVO = BeanConv.toPage(page,TradeVO.class);
if (!EmptyUtil.isNullOrEmpty(pageVO.getRecords())){
//对应退款单
List<Long> productOrderNos = pageVO.getRecords().stream()
.map(TradeVO::getProductOrderNo).collect(Collectors.toList());
List<RefundRecordVO> refundRecordVOList = refundRecordService
.findRefundRecordInProductOrderNo(productOrderNos);
pageVO.getRecords().forEach(n->{
//装备退款单
List<RefundRecordVO> refundRecordVOListHandler = Lists.newArrayList();
refundRecordVOList.forEach(d->{
if (n.getProductOrderNo().equals(d.getProductOrderNo())){
refundRecordVOListHandler.add(d);
}
});
n.setRefundRecordVOList(refundRecordVOListHandler);
});
}
return pageVO;
} catch (Exception e) {
log.error("查询支付通道列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(PayChannelEnum.PAGE_FAIL);
}
}
}
@@ -0,0 +1,53 @@
package com.itheima.sfbx.trade.face.impl;
import com.itheima.sfbx.trade.adapter.WapPayAdapter;
import com.itheima.sfbx.framework.commons.constant.trade.TradeCacheConstant;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.trade.face.WapPayFace;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @ClassName WapPayFaceImpl.java
* @Description 手机网页支付Face接口实现
*/
@Slf4j
@Component
public class WapPayFaceImpl implements WapPayFace {
@Autowired
WapPayAdapter wapPayAdapter;
@Autowired
RedissonClient redissonClient;
@Override
public TradeVO wapTrade(TradeVO tradeVO) {
//1、对交易订单加锁
Long productOrderNo = tradeVO.getProductOrderNo();
String key = TradeCacheConstant.CREATE_PAY + productOrderNo;
RLock lock = redissonClient.getFairLock(key);
try {
if (lock.tryLock(TradeCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS)){
return wapPayAdapter.wapTrade(tradeVO);
}else {
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
} catch (Exception e) {
log.error("统一收单线下交易预创建异常:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}finally {
//释放锁
lock.unlock();
}
}
}
@@ -0,0 +1,215 @@
package com.itheima.sfbx.trade.generator;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import java.util.*;
/**
* @Description:代码生成器
*/
public class CodeGenerator {
public static void autoGenerator(){
//用来获取generrator.properties文件的配置信息
final ResourceBundle rb = ResourceBundle.getBundle("generrator");
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String serviceProjectPath =rb.getString("serviceProjectPath");
gc.setOutputDir(serviceProjectPath + "/src/main/java");
gc.setAuthor(rb.getString("author"));
gc.setOpen(false);
gc.setFileOverride(true);
//指定时间处理类型
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true); //实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
//数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl(rb.getString("url"));
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername(rb.getString("userName"));
dsc.setPassword(rb.getString("password"));
mpg.setDataSource(dsc);
//包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(rb.getString("moduleName"));
pc.setParent(rb.getString("parent"));
pc.setController("web");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setEntity("pojo");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
//管理需要附件功能的代码生成
List<String> tablesFile = Arrays.asList(rb.getString("tablesFile").split(","));
Map<String, Object> map = new HashMap<>();
map.put("tablesFile",tablesFile);
this.setMap(map);
}
};
String voProjectPath =rb.getString("voProjectPath");
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
if ("true".equals(rb.getString("face"))){
String faceTemplatePath = rb.getString("face.ftl.path")+".ftl";
// 自定义face配置会被优先输出
focList.add(new FileOutConfig(faceTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名
return serviceProjectPath + "/src/main/java/" +pc.getParent().replace(".","/")+"/face/"
+ tableInfo.getEntityName() + "Face" + StringPool.DOT_JAVA;
}
});
}
if ("true".equals(rb.getString("faceImpl"))){
String faceImplTemplatePath = rb.getString("faceImpl.ftl.path")+".ftl";
// 自定义faceImpl配置会被优先输出
focList.add(new FileOutConfig(faceImplTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名
return serviceProjectPath + "/src/main/java/" +pc.getParent().replace(".","/")+"/face/impl/"
+ tableInfo.getEntityName() + "FaceImpl" + StringPool.DOT_JAVA;
}
});
}
if ("true".equals(rb.getString("constant"))){
String constantTemplatePath = rb.getString("constant.ftl.path")+".ftl";
// 自定义constant配置会被优先输出
focList.add(new FileOutConfig(constantTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名
return voProjectPath + "/src/main/java/" +pc.getParent().replace(".","/")+"/constant/"
+ tableInfo.getEntityName() + "CacheConstant" + StringPool.DOT_JAVA;
}
});
}
if ("true".equals(rb.getString("enums"))){
String enumsTemplatePath = rb.getString("enums.ftl.path")+".ftl";
// 自定义enums配置会被优先输出
focList.add(new FileOutConfig(enumsTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名
return voProjectPath + "/src/main/java/" +pc.getParent().replace(".","/")+"/enums/"
+ tableInfo.getEntityName() + "Enum" + StringPool.DOT_JAVA;
}
});
}
if ("true".equals(rb.getString("vo"))){
String voTemplatePath = rb.getString("vo.ftl.path")+".ftl";
// 自定义Vo配置会被优先输出
focList.add(new FileOutConfig(voTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名
return voProjectPath + "/src/main/java/" +pc.getParent().replace(".","/")+"/vo/"
+ tableInfo.getEntityName() + "Vo" + StringPool.DOT_JAVA;
}
});
}
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
if ("true".equals(rb.getString("entity"))){
String entityFtlPath = rb.getString("entity.ftl.path");
if (!EmptyUtil.isNullOrEmpty(entityFtlPath)){
templateConfig.setEntity(entityFtlPath);
}
}else {
templateConfig.setEntity(null);
}
if ("true".equals(rb.getString("mapper"))){
String mapperFtlPath = rb.getString("mapper.ftl.path");
if (!EmptyUtil.isNullOrEmpty(mapperFtlPath)){
templateConfig.setMapper(mapperFtlPath);
}
}else {
templateConfig.setMapper(null);
}
if ("true".equals(rb.getString("service"))){
String serviceFtlPath = rb.getString("service.ftl.path");
if (!EmptyUtil.isNullOrEmpty(serviceFtlPath)){
templateConfig.setService(serviceFtlPath);
}
}else {
templateConfig.setService(null);
}
if ("true".equals(rb.getString("serviceImpl"))){
String serviceImpFtlPath = rb.getString("serviceImpl.ftl.path");
if (!EmptyUtil.isNullOrEmpty(serviceImpFtlPath)){
templateConfig.setServiceImpl(serviceImpFtlPath);
}
}else {
templateConfig.setServiceImpl(null);
}
if ("true".equals(rb.getString("controller"))){
String controllerFtlPath = rb.getString("controller.ftl.path");
if (!EmptyUtil.isNullOrEmpty(controllerFtlPath)){
templateConfig.setController(controllerFtlPath);
}
}else {
templateConfig.setController(null);
}
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setSuperEntityClass(rb.getString("SuperEntityClass"));
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 写于父类中的公共字段
String[] SuperEntityColumns = rb.getString("superEntityColumns").split(",");
strategy.setSuperEntityColumns(SuperEntityColumns);
strategy.setInclude(rb.getString("tableName").split(","));
strategy.setControllerMappingHyphenStyle(true);
String tablePrefix = rb.getString("tablePrefix");
if (tablePrefix!=null){
strategy.setTablePrefix(tablePrefix);
}
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.handler;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName AppPayHandler.java
* @Description 原生APP支付处理
*/
public interface AppPayHandler extends CommonPayHandler {
/***
* @description 外部商户APP唤起快捷SDK创建订单并支付
* @param tradeVO 订单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO appTrade(TradeVO tradeVO);
}
@@ -0,0 +1,100 @@
package com.itheima.sfbx.trade.handler;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName IdempotentHandler.java
* @Description 交易前置处理接口
*/
public interface BeforePayHandler {
/***
* @description CreateTrade交易幂等性
* @param tradeVO 交易订单
* @return
*/
void idempotentCreateTrade(TradeVO tradeVO) throws ProjectException;
/***
* @description AppPaySign交易单参数校验
* @param tradeVO 交易订单
* @return
*/
Boolean checkeAppPaySign(TradeVO tradeVO);
/***
* @description H5Sign交易单参数校验
* @param tradeVO 交易订单
* @return
*/
Boolean checkeH5Sign(TradeVO tradeVO);
/***
* @description H5Sign周期扣款参数教育
* @param tradeVO 交易订单
* @return
*/
Boolean checkeH5PeriodicPay(TradeVO tradeVO);
/***
* @description CreateTrade交易单参数校验
* @param tradeVO 交易订单
* @return
*/
Boolean checkeCreateTrade(TradeVO tradeVO);
/***
* @description QueryTrade交易单参数校验
* @param tradeVO 交易订单
* @return
*/
Boolean checkeQueryTrade(TradeVO tradeVO);
/***
* @description RefundTrade退款交易幂等性
* @param tradeVO 交易订单
* @return
*/
void idempotentRefundTrade(TradeVO tradeVO);
/***
* @description RefundTrade退款交易单参数校验
* @param tradeVO 交易订单
* @return
*/
Boolean checkeRefundTrade(TradeVO tradeVO);
/***
* @description QueryRefundTrade交易单参数校验
* @param refundRecordVO 退款记录
* @return
*/
Boolean checkeQueryRefundTrade(RefundRecordVO refundRecordVO);
/***
* @description closeTradin交易单参数校验c
* @param tradeVO 交易订单
* @return
*/
Boolean checkeCloseTrade(TradeVO tradeVO);
/***
* @description DownLoadBill下载订单交易
* @param tradeVO 交易订单
* @return
*/
Boolean checkeDownLoadBill(TradeVO tradeVO);
/***
* @description SignContract交易幂等性
* @param tradeVO 交易订单
* @return
*/
void idempotentSignContract(TradeVO tradeVO);
}
@@ -0,0 +1,53 @@
package com.itheima.sfbx.trade.handler;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName CommonPayHandler.java
* @Description 基础交易处理接口
*/
public interface CommonPayHandler {
/***
* @description 统一收单线下交易查询
* 该接口提供所有支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。
* @param tradeVO 交易单
* @return
*/
TradeVO queryTrade(TradeVO tradeVO);
/***
* @description 统一收单交易退款接口
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
* 将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* @param tradeVO 交易单
* @return
*/
TradeVO refundTrade(TradeVO tradeVO);
/***
* @description 统一收单交易退款查询接口
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
* 将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* @param refundRecordVO 退款交易单
* @return
*/
RefundRecordVO queryRefundTrade(RefundRecordVO refundRecordVO) ;
/***
* @description 统一关闭订单
* 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
* 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
* @param tradeVO 交易单
* @return
*/
TradeVO closeTrade(TradeVO tradeVO);
/***
* @description 为方便商户快速查账,支持商户通过本接口获取商户离线账单下载地址
* @param tradeVO 退款交易单
* @return
*/
TradeVO downLoadBill(TradeVO tradeVO);
}
@@ -0,0 +1,24 @@
package com.itheima.sfbx.trade.handler;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName FaceToFacePayHandler.java
* @Description 面对面支付Handler接口
*/
public interface FaceToFacePayHandler extends CommonPayHandler {
/***
* @description 扫用户出示的付款码,完成付款
* @param tradeVO 订单
* @return 支付结果
*/
TradeVO payTrade(TradeVO tradeVO);
/***
* @description 生成交易付款码,待用户扫码付款
* @param tradeVO 订单
* @return 二维码路径
*/
TradeVO precreateTrade(TradeVO tradeVO);
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.handler;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName PagePayHandler.java
* @Description PC网页支付处理
*/
public interface PagePayHandler extends CommonPayHandler {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO pageTrade(TradeVO tradeVO);
}
@@ -0,0 +1,21 @@
package com.itheima.sfbx.trade.handler;
import org.springframework.http.HttpEntity;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName NotifyHandler.java
* @Description 支付回调接口
*/
public interface PayNotifyHandler {
/***
* @description 支付回调
* @param request http请求对象:支付宝
* @param httpEntity http请求对象:微信
* @param companyNo 企业号
*/
String notify(HttpServletRequest request, HttpEntity<String> httpEntity,String companyNo);
}
@@ -0,0 +1,31 @@
package com.itheima.sfbx.trade.handler;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName PeriodicPayHandler.java
* @Description 周期性扣款接口
*/
public interface PeriodicPayHandler {
/***
* @description App支付并签约
* @param tradeVO 交易单信息
* @return
*/
TradeVO appPaySign(TradeVO tradeVO);
/***
* @description H5先签约
* @param tradeVO 交易单信息
* @return
*/
TradeVO h5SignContract(TradeVO tradeVO);
/***
* @description H5签约后代扣
* @param tradeVO 交易单信息
* @return
*/
TradeVO h5PeriodicPay(TradeVO tradeVO);
}
@@ -0,0 +1,17 @@
package com.itheima.sfbx.trade.handler;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
/**
* @ClassName WapPayHandler.java
* @Description 手机网页支付处理
*/
public interface WapPayHandler extends CommonPayHandler {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
TradeVO wapTrade(TradeVO tradeVO);
}
@@ -0,0 +1,73 @@
package com.itheima.sfbx.trade.handler.aliyun;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.handler.AppPayHandler;
import com.itheima.sfbx.trade.client.alipay.Factory;
import com.itheima.sfbx.trade.pojo.Trade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName AliAppPayHandler.java
* @Description 原生APP支付处理
*/
@Slf4j
@Component
public class AliAppPayHandler extends AliCommonPayHandler implements AppPayHandler {
@Override
public TradeVO appTrade(TradeVO tradeVO) {
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//5、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
AlipayTradeAppPayResponse appPayResponse = factory
.App().pay(tradeVO.getMemo(),
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getTradeAmount()));
//6、检查网关响应结果
boolean isSuccess = ResponseChecker.success(appPayResponse);
//7、网关响应成功且appPayResponse有返回结果
if (isSuccess&&!EmptyUtil.isNullOrEmpty(appPayResponse.getBody())){
tradeVO.setPlaceOrderCode(TradeConstant.ALI_SUCCESS_CODE);
tradeVO.setPlaceOrderMsg(TradeConstant.ALI_SUCCESS_MSG);
tradeVO.setPlaceOrderJson(appPayResponse.getBody());
tradeVO.setTradeState(TradeConstant.TRADE_WAIT);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
return BeanConv.toBean(trade, TradeVO.class);
}else {
log.error("网关:支付宝APP统一下单:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(appPayResponse));
throw new RuntimeException("网关:支付宝APP统一下单创建失败!");
}
} catch (Exception e) {
log.error("支付宝APP统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.PAYING_TRADE_FAIL);
}
}
}
@@ -0,0 +1,321 @@
package com.itheima.sfbx.trade.handler.aliyun;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.*;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.framework.rabbitmq.pojo.MqMessage;
import com.itheima.sfbx.framework.rabbitmq.source.TradeSource;
import com.itheima.sfbx.trade.handler.BeforePayHandler;
import com.itheima.sfbx.trade.handler.CommonPayHandler;
import com.itheima.sfbx.trade.service.IRefundRecordService;
import com.itheima.sfbx.trade.service.ISignContractService;
import com.itheima.sfbx.trade.service.ITradeService;
import com.itheima.sfbx.trade.client.alipay.Factory;
import com.itheima.sfbx.trade.config.AliPayConfig;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import com.itheima.sfbx.trade.pojo.Trade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
* @ClassName AliCommonPayHandler.java
* @Description 阿里支付基础支付
*/
@Slf4j
@Service
public class AliCommonPayHandler implements CommonPayHandler {
@Autowired
AliPayConfig aliPayConfig;
@Autowired
ITradeService tradeService;
@Autowired
ISignContractService signContractService;
@Autowired
IRefundRecordService refundRecordService;
@Autowired
IdentifierGenerator identifierGenerator;
@Autowired
BeforePayHandler beforePayHandler;
@Autowired
TradeSource tradeSource;
@Override
public TradeVO queryTrade(TradeVO tradeVO) {
//1、查询前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeQueryTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
Trade tradeHandler = tradeService.findTradByTradeOrderNo(tradeVO.getTradeOrderNo());
//2、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//3、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//4、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
//5、调用支付宝API:通用查询支付情况
AlipayTradeQueryResponse queryResponse = factory.Common()
.query(String.valueOf(tradeVO.getTradeOrderNo()));
//6、判断响应是否成功
boolean success = ResponseChecker.success(queryResponse);
//7、响应成功,分析交易状态
if (success&&!EmptyUtil.isNullOrEmpty(queryResponse.getTradeStatus())){
switch (queryResponse.getTradeStatus()){
//支付取消:TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)
case TradeConstant.ALI_TRADE_CLOSED:
tradeHandler.setTradeState(TradeConstant.TRADE_CLOSED);break;
//支付成功:TRADE_SUCCESS(交易支付成功)
case TradeConstant.ALI_TRADE_SUCCESS:
tradeHandler.setTradeState(TradeConstant.TRADE_SUCCESS);break;
//支付成功:TRADE_FINISHED(交易结束,不可退款)
case TradeConstant.ALI_TRADE_FINISHED:
tradeHandler.setTradeState(TradeConstant.TRADE_SUCCESS);break;
//非最终状态不处理,当前交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)不处理
default:
flag = false;break;
}
//8.1、修改交易单状态
if (flag){
tradeHandler.setResultCode(queryResponse.getSubCode());
tradeHandler.setResultMsg(queryResponse.getSubMsg());
tradeHandler.setResultJson(JSONObject.toJSONString(queryResponse));
tradeService.updateById(tradeHandler);
//8.2 发送同步业务信息的MQ信息
Long messageId = (Long) identifierGenerator.nextId(tradeHandler);
MqMessage mqMessage = MqMessage.builder()
.id(messageId)
.title("trade-message")
.content(JSONObject.toJSONString(tradeHandler))
.messageType("trade-project-sync")
.produceTime(Timestamp.valueOf(LocalDateTime.now()))
.sender("system")
.build();
//指定通知的企业
Message<MqMessage> message = MessageBuilder.withPayload(mqMessage)
.setHeader("type", "trade-key").build();
tradeSource.tradeOutput().send(message);
return BeanConv.toBean(tradeHandler,TradeVO.class);
}else {
log.info("查询支付宝交易单:{},结果:{}", tradeVO.getTradeOrderNo(),queryResponse.getTradeStatus());
}
}else {
throw new RuntimeException("网关:查询支付宝统一下单失败!");
}
} catch (Exception e) {
log.warn("查询支付宝统一下单失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("查询支付宝统一下单失败!");
}
//7、返回结果
return tradeVO;
}
@Override
public TradeVO refundTrade(TradeVO tradeVO) {
//1、生成退款请求编号
tradeVO.setOutRequestNo(String.valueOf(identifierGenerator.nextId(tradeVO)));
Trade tradeHandler = tradeService.findTradByTradeOrderNo(tradeVO.getTradeOrderNo());
tradeVO.setTradeAmount(tradeHandler.getTradeAmount());
tradeVO.setRefund(tradeHandler.getRefund());
//2、退款前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeRefundTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
//3、退款前置处理:退款幂等性校验
beforePayHandler.idempotentRefundTrade(tradeVO);
//4、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//5、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//6、使用配置
Factory factory = new Factory();
factory.setOptions(config);
//7、调用退款接口
try {
AlipayTradeRefundResponse refundResponse = factory.Common()
.optional("out_request_no", tradeVO.getOutRequestNo())
.refund(String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getOperTionRefund()));
//8、判断响应是否成功
boolean success = ResponseChecker.success(refundResponse);
if (success&&String.valueOf(tradeVO.getTradeOrderNo()).equals(refundResponse.getOutTradeNo())){
//9、保存交易单信息
tradeHandler.setIsRefund(SuperConstant.YES);
tradeHandler.setRefund(tradeHandler.getRefund().add(tradeVO.getOperTionRefund()));
tradeService.updateById(tradeHandler);
//10、保存退款单信息
RefundRecord refundRecord = RefundRecord.builder()
.companyNo(tradeHandler.getCompanyNo())
.refundNo(tradeVO.getOutRequestNo())
.refundAmount(tradeVO.getOperTionRefund())
.refundCode(refundResponse.getSubCode())
.refundMsg(refundResponse.getSubMsg())
.productOrderNo(tradeHandler.getProductOrderNo())
.tradeChannel(tradeHandler.getTradeChannel())
.tradeOrderNo(tradeHandler.getTradeOrderNo())
.refundStatus(success?TradeConstant.REFUND_STATUS_SENDING:TradeConstant.REFUND_STATUS_FAIL)
.memo("退款:"+tradeHandler.getMemo())
.build();
refundRecordService.save(refundRecord);
}else {
log.error("网关:支付宝统一下单退款:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(refundResponse));
throw new RuntimeException("网关:支付宝统一下单退款失败");
}
} catch (Exception e) {
log.error("支付宝统一下单退款失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_REFUND_FAIL);
}
return BeanConv.toBean(tradeHandler,TradeVO.class);
}
@Override
public RefundRecordVO queryRefundTrade(RefundRecordVO refundRecordVO) {
//1、退款前置处理:检测退款单参数
Boolean flag = beforePayHandler.checkeQueryRefundTrade(refundRecordVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
RefundRecord refundRecordHandler = refundRecordService.findRefundRecordByRefundNo(refundRecordVO.getRefundNo());
//2、获得支付宝配置文件
Config config = aliPayConfig.config(refundRecordVO.getCompanyNo());
//3、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//4、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
AlipayTradeFastpayRefundQueryResponse refundQueryResponse =
factory.Common().queryRefund(
String.valueOf(refundRecordVO.getTradeOrderNo()),
refundRecordVO.getRefundNo());
//5、判断响应是否成功
boolean success = ResponseChecker.success(refundQueryResponse);
//6、查询出的退款状态
String refundStatus = refundQueryResponse.getRefundStatus();
if (success&&TradeConstant.REFUND_SUCCESS.equals(refundStatus)){
refundRecordHandler.setRefundStatus(TradeConstant.REFUND_STATUS_SUCCESS);
refundRecordHandler.setRefundCode(refundQueryResponse.getSubCode());
refundRecordHandler.setRefundMsg(refundQueryResponse.getSubMsg());
refundRecordService.updateById(refundRecordHandler);
}else {
log.error("网关:查询支付宝统一下单退款:{},结果:{}", refundRecordVO.getTradeOrderNo(),
JSONObject.toJSONString(refundQueryResponse));
throw new RuntimeException("网关:查询支付宝统一下单退款失败!");
}
} catch (Exception e) {
log.warn("查询支付宝统一下单退款失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("查询支付宝统一下单退款失败!");
}
return refundRecordVO;
}
@Override
public TradeVO closeTrade(TradeVO tradeVO) {
//1、退款前置处理:检测关闭参数tradeVO
Boolean flag = beforePayHandler.checkeCloseTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
Trade tradeHandler = tradeService.findTradByTradeOrderNo(tradeVO.getTradeOrderNo());
//2、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//3、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//4、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
//5、调用三方API关闭订单
AlipayTradeCloseResponse closeResponse = factory.Common().close(
String.valueOf(tradeVO.getTradeOrderNo()));
//6、关闭订单受理情况
boolean success = ResponseChecker.success(closeResponse);
if (closeResponse.getCode().equals("40004")||
(success&&tradeVO.getTradeOrderNo().equals(closeResponse.getOutTradeNo()))){
tradeHandler.setTradeState(TradeConstant.TRADE_CLOSED);
tradeService.updateById(tradeHandler);
return BeanConv.toBean(tradeHandler,TradeVO.class);
}else {
log.error("网关:支付宝关闭订单:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(closeResponse));
throw new RuntimeException("网关:支付宝关闭订单失败!");
}
} catch (Exception e) {
log.warn("关闭订单失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_CLOSE_FAIL);
}
}
@Override
public TradeVO downLoadBill(TradeVO tradeVO) {
//1、下载账单前置处理:检测关闭参数tradeVO
Boolean flag = beforePayHandler.checkeDownLoadBill(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
//2、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//3、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//4、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
AlipayDataDataserviceBillDownloadurlQueryResponse billDownloadurlQueryResponse = factory.Common()
.downloadBill(tradeVO.getBillType(), DateUtil.formatDate(tradeVO.getBillDate()));
//5、请求下载受理情况
boolean success = ResponseChecker.success(billDownloadurlQueryResponse);
if (success){
String billDownloadUrl = billDownloadurlQueryResponse.getBillDownloadUrl();
tradeVO.setBillDownloadUrl(billDownloadUrl);
}else {
log.warn("网关:支付宝下载订单类型:{},时间:{},结果:{}", tradeVO.getBillType(),tradeVO.getBillDate(),
JSONObject.toJSONString(billDownloadurlQueryResponse));
throw new RuntimeException("网关:支付宝下载订单失败!");
}
} catch (Exception e) {
log.warn("支付宝下载订单失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("支付宝下载订单失败!");
}
return tradeVO;
}
}
@@ -0,0 +1,124 @@
package com.itheima.sfbx.trade.handler.aliyun;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePayResponse;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.client.alipay.Factory;
import com.itheima.sfbx.trade.handler.FaceToFacePayHandler;
import com.itheima.sfbx.trade.pojo.Trade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName AliFaceToFacePayHandler.java
* @Description 支付宝扫码付支付实现
*/
@Slf4j
@Component
public class AliFaceToFacePayHandler extends AliCommonPayHandler implements FaceToFacePayHandler {
@Override
public TradeVO payTrade(TradeVO tradeVO) {
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//5、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
//6、调用支付宝API
AlipayTradePayResponse payResponse = factory.FaceToFace().pay(tradeVO.getMemo(),
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getTradeAmount()),
tradeVO.getAuthCode()
);
//7、受理结果【只表示请求是否成功,而不是支付是否成功】
boolean isSuccess = ResponseChecker.success(payResponse);
//8、受理成功:修改交易单
if (isSuccess&&String.valueOf(tradeVO.getTradeOrderNo()).equals(payResponse.getOutTradeNo())){
tradeVO.setResultCode(payResponse.getCode());
tradeVO.setResultMsg(payResponse.getMsg());
tradeVO.setResultJson(JSONObject.toJSONString(payResponse));
tradeVO.setTradeState(TradeConstant.TRADE_SUCCESS);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
}else {
log.error("网关:支付宝商家扫用户统一下单创建:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(payResponse));
throw new RuntimeException("网关:支付宝商家扫用户统一下单创建失败!");
}
} catch (Exception e) {
log.error("支付宝商家扫用户统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.PAYING_TRADE_FAIL);
}
return tradeVO;
}
@Override
public TradeVO precreateTrade(TradeVO tradeVO) {
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//5、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
//6、调用支付宝API面对面支付
AlipayTradePrecreateResponse precreateResponse = factory.FaceToFace()
.preCreate(
tradeVO.getMemo(),
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getTradeAmount()));
//7、受理结果【只表示请求是否成功,而不是支付是否成功】
boolean isSuccess = ResponseChecker.success(precreateResponse);
//8、受理成功:修改交易单
if (isSuccess&&!EmptyUtil.isNullOrEmpty(precreateResponse.getQrCode())){
tradeVO.setPlaceOrderCode(precreateResponse.getCode());
tradeVO.setPlaceOrderMsg(precreateResponse.getMsg());
tradeVO.setPlaceOrderJson(precreateResponse.getQrCode());
tradeVO.setTradeState(TradeConstant.TRADE_WAIT);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
}else {
log.error("网关:支付宝商家扫用户统一下单创建:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(precreateResponse));
throw new RuntimeException("网关:支付宝商家扫用户统一下单创建失败!");
}
} catch (Exception e) {
log.error("支付宝用户扫商家统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.PAYING_TRADE_FAIL);
}
return tradeVO;
}
}
@@ -0,0 +1,74 @@
package com.itheima.sfbx.trade.handler.aliyun;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.itheima.sfbx.trade.handler.PagePayHandler;
import com.itheima.sfbx.trade.client.alipay.Factory;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName AliPagePayHandler.java
* @Description PC网页支付处理实现
*/
@Slf4j
@Component
public class AliPagePayHandler extends AliCommonPayHandler implements PagePayHandler {
@Override
public TradeVO pageTrade(TradeVO tradeVO) {
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//5、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
//6、调用支付宝API
AlipayTradePagePayResponse pagePayResponse = factory.Page().pay(tradeVO.getMemo(),
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getTradeAmount()),
tradeVO.getReturnUrl());
//7、受理结果【只表示请求是否成功,而不是支付是否成功】
boolean isSuccess = ResponseChecker.success(pagePayResponse);
if (isSuccess&&!EmptyUtil.isNullOrEmpty(pagePayResponse.getBody())){
tradeVO.setPlaceOrderCode(TradeConstant.ALI_SUCCESS_CODE);
tradeVO.setPlaceOrderMsg(TradeConstant.ALI_SUCCESS_MSG);
tradeVO.setPlaceOrderJson(pagePayResponse.getBody());
tradeVO.setTradeState(TradeConstant.TRADE_WAIT);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
return BeanConv.toBean(trade, TradeVO.class);
}else {
log.error("网关:支付宝网页支付统一下单创建:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(pagePayResponse));
throw new RuntimeException("网关:支付宝网页支付统一下单创建失败!");
}
} catch (Exception e) {
log.error("支付宝网页支付统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("支付宝网页支付统一下单创建失败!");
}
}
}
@@ -0,0 +1,126 @@
package com.itheima.sfbx.trade.handler.aliyun;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.kernel.Config;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.rabbitmq.pojo.MqMessage;
import com.itheima.sfbx.framework.rabbitmq.source.TradeSource;
import com.itheima.sfbx.trade.service.ITradeService;
import com.itheima.sfbx.trade.client.alipay.Factory;
import com.itheima.sfbx.trade.config.AliPayConfig;
import com.itheima.sfbx.trade.handler.PayNotifyHandler;
import com.itheima.sfbx.trade.pojo.Trade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName AliNotifyHandler.java
* @Description 支付通知接口
*/
@Component
public class AliPayNotifyHandler implements PayNotifyHandler {
@Autowired
AliPayConfig aliPayConfig;
@Autowired
ITradeService tradeService;
@Autowired
TradeSource tradeSource;
@Autowired
IdentifierGenerator identifierGenerator;
@Override
public String notify(HttpServletRequest request, HttpEntity<String> httpEntity,String companyNo) {
//获取支付结果参数
Map<String, String[]> parameterMap = request.getParameterMap();
Map<String, String> paramMap = new HashMap<>();
for (String key : parameterMap.keySet()) {
paramMap.put(key, parameterMap.get(key)[0]);
}
try {
//1、获得支付宝配置文件
Config config = aliPayConfig.config(companyNo);
//2、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//3、使用配置
Factory factory = new Factory();
factory.setOptions(config);
//4、验证签名
boolean verifyResult = factory.Common().verifyNotify(paramMap);
if (!verifyResult) {
return "verify sign fail";
}
//5、校验支付状态
if (TradeConstant.ALI_TRADE_SUCCESS.equals(paramMap.get("trade_status"))) {
Trade trade = tradeService.findTradByTradeOrderNo(Long.valueOf(paramMap.get("out_trade_no")));
trade.setResultCode(paramMap.get("trade_status"));
trade.setResultMsg(TradeConstant.ALI_SUCCESS_MSG);
trade.setResultJson(JSONObject.toJSONString(paramMap));
trade.setTradeState(TradeConstant.TRADE_SUCCESS);
tradeService.updateById(trade);
//发送同步业务信息的MQ信息
Long messageId = (Long) identifierGenerator.nextId(trade);
MqMessage mqMessage = MqMessage.builder()
.id(messageId)
.title("trade-message")
.content(JSONObject.toJSONString(BeanConv.toBean(trade, TradeVO.class)))
.messageType("trade-project-sync")
.produceTime(Timestamp.valueOf(LocalDateTime.now()))
.sender("system")
.build();
//指定通知的企业
Message<MqMessage> message = MessageBuilder.withPayload(mqMessage)
.setHeader("type", "trade-key").build();
boolean flag = tradeSource.tradeOutput().send(message);
return "success";
}else if(TradeConstant.ALI_TRADE_CLOSED.equals(paramMap.get("trade_status"))){
Trade trade = tradeService.findTradByTradeOrderNo(Long.valueOf(paramMap.get("out_trade_no")));
trade.setResultCode(paramMap.get("trade_status"));
trade.setResultMsg(TradeConstant.ALI_SUCCESS_MSG);
trade.setResultJson(JSONObject.toJSONString(paramMap));
trade.setTradeState(TradeConstant.TRADE_CLOSED);
tradeService.updateById(trade);
//发送同步业务信息的MQ信息
Long messageId = (Long) identifierGenerator.nextId(trade);
MqMessage mqMessage = MqMessage.builder()
.id(messageId)
.title("trade-message")
.content(JSONObject.toJSONString(BeanConv.toBean(trade, TradeVO.class)))
.messageType("trade-project-sync")
.produceTime(Timestamp.valueOf(LocalDateTime.now()))
.sender("system")
.build();
//指定通知的企业
Message<MqMessage> message = MessageBuilder.withPayload(mqMessage)
.setHeader("type", "trade-key").build();
tradeSource.tradeOutput().send(message);
return "success";
}else {
return "fail";
}
} catch (Exception e) {
//6、异常返回 fail 给支付宝
return "fail";
}
}
}
@@ -0,0 +1,245 @@
package com.itheima.sfbx.trade.handler.aliyun;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradePayRequest;
import com.alipay.api.request.AlipayUserAgreementPageSignRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradePayResponse;
import com.alipay.api.response.AlipayUserAgreementPageSignResponse;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.itheima.sfbx.framework.commons.constant.trade.SignContractConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.config.AliPayConfig;
import com.itheima.sfbx.trade.handler.PeriodicPayHandler;
import com.itheima.sfbx.trade.pojo.SignContract;
import com.itheima.sfbx.trade.pojo.Trade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.net.URLEncoder;
/**
* @ClassName AliPeriodicPayHandler.java
* @Description 周期性扣款实现
*/
@Slf4j
@Component
public class AliPeriodicPayHandler extends AliCommonPayHandler implements PeriodicPayHandler {
@Autowired
AliPayConfig aliPayConfig;
@Autowired
IdentifierGenerator identifierGenerator;
@Override
public TradeVO appPaySign(TradeVO tradeVO) {
//交易前置处理:参数校验
Boolean flag = beforePayHandler.checkeAppPaySign(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_DEDUCTION_FAIL);
}
//签约前置处理:幂等性处理
beforePayHandler.idempotentSignContract(tradeVO);
//交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl(tradeVO.getNotifyUrl());
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", tradeVO.getTradeOrderNo());
//订单总金额。首次支付金额,不算在周期扣总金额里面。
bizContent.put("total_amount", tradeVO.getTradeAmount());
bizContent.put("subject", tradeVO.getMemo());
bizContent.put("product_code", "QUICK_MSECURITY_PAY");
//签约参数
JSONObject agreement_sign_params = new JSONObject();
agreement_sign_params.put("product_code", "GENERAL_WITHHOLDING");
agreement_sign_params.put("personal_product_code", "CYCLE_PAY_AUTH_P");
//扣款场景
agreement_sign_params.put("sign_scene", tradeVO.getAliPeriodicVO().getSignScene());
//商家签约号,代扣协议中标示用户的唯一签约号(确保在商家系统中唯一)。用户支持一对多
agreement_sign_params.put("external_agreement_no", identifierGenerator.nextId(tradeVO));
//签约成功异步通知地址。支付后签约场景中,用户支付成功及签约成功都会触发异步通知
agreement_sign_params.put("sign_notify_url", tradeVO.getAliPeriodicVO().getSignNotifyUrl());
//周期规则参数,必填
JSONObject period_rule_params = new JSONObject();
//周期类型 ,枚举值为 DAY 和 MONTH。周期类型使用MONTH的时候,计划扣款时间 execute_time不允许传 28 日之后的日期(可以传 28 日),以此避免有些月份可能不存在对应日期的情况。
period_rule_params.put("period_type", tradeVO.getAliPeriodicVO().getRulePeriodType());
//必填,周期数,与 period_type 组合使用确定扣款周期,例如 period_type 为 DAYperiod = 90,则扣款周期为 90 天。
period_rule_params.put("period", tradeVO.getAliPeriodicVO().getRulePeriod());
//下次扣款的时间。非支付并签约的成功时间,必填。精确到日,格式为 yyyy-MM-dd。
period_rule_params.put("execute_time", tradeVO.getAliPeriodicVO().getRuleExecuteTime());
//单次扣款最大金额,必填,即每次发起扣款时限制的最大金额,单位为元。商家每次发起扣款都不允许大于此金额。
period_rule_params.put("single_amount", tradeVO.getAliPeriodicVO().getRuleSingleAmount());
//周期内允许扣款的总金额。
period_rule_params.put("total_amount", tradeVO.getAliPeriodicVO().getRuleTotalAmount());
//总扣款次数
period_rule_params.put("total_payments", tradeVO.getAliPeriodicVO().getRuleTotalPayments());
agreement_sign_params.put("period_rule_params", period_rule_params);
JSONObject access_params = new JSONObject();
//channel:目前支持以下值:ALIPAYAPP :支付宝客户端 H5 页面签约。QRCODE:扫码签约。QRCODEORSMS:扫码签约或者短信签约。
access_params.put("channel", tradeVO.getAliPeriodicVO().getAccessChannel());
agreement_sign_params.put("access_params", access_params);
bizContent.put("agreement_sign_params", agreement_sign_params);
request.setBizContent(bizContent.toString());
AlipayClient alipayClient = aliPayConfig.createAlipayClient(tradeVO.getCompanyNo());
try {
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
boolean success = response.isSuccess();
if (success){
//保存签约合同记录
SignContract signContract = SignContract.builder()
.contractNo(tradeVO.getAliPeriodicVO().getContractNo())
.externalAgreementNo(tradeVO.getAliPeriodicVO().getExternalAgreementNo())
.signState(SignContractConstant.SIGNSTATE_TEMP)
.tradeChannel(tradeVO.getTradeChannel())
.rulePeriodType(tradeVO.getAliPeriodicVO().getRulePeriodType())
.rulePeriod(Long.valueOf(tradeVO.getAliPeriodicVO().getRulePeriod()))
.ruleTotalAmount(new BigDecimal(tradeVO.getAliPeriodicVO().getRuleTotalAmount()))
.ruleSingleAmount(new BigDecimal(tradeVO.getAliPeriodicVO().getRuleSingleAmount()))
.ruleTotalPayments(Long.valueOf(tradeVO.getAliPeriodicVO().getRuleTotalPayments()))
.build();
signContractService.save(signContract);
//保存支付记录
tradeVO.setPlaceOrderCode(TradeConstant.ALI_SUCCESS_CODE);
tradeVO.setPlaceOrderMsg(TradeConstant.ALI_SUCCESS_MSG);
tradeVO.setPlaceOrderJson(response.getBody());
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
return BeanConv.toBean(trade, TradeVO.class);
}else {
log.error("网关:App支付并签约:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(response));
throw new RuntimeException("网关:支付宝App支付并签约创建失败!");
}
}catch (Exception e){
log.error("支付宝App支付并签约创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("支付宝App支付并签约创建失败!");
}
}
@Override
public TradeVO h5SignContract(TradeVO tradeVO) {
//交易前置处理:参数校验
Boolean flag = beforePayHandler.checkeH5Sign(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_DEDUCTION_FAIL);
}
//签约前置处理:幂等性处理
beforePayHandler.idempotentSignContract(tradeVO);
//构建请求对象
AlipayUserAgreementPageSignRequest request = new AlipayUserAgreementPageSignRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("product_code", "GENERAL_WITHHOLDING");
//商家扣款场景固定为 GENERAL_WITHHOLDING
bizContent.put("personal_product_code", "CYCLE_PAY_AUTH_P");
//商户签约号
bizContent.put("external_agreement_no", tradeVO.getAliPeriodicVO().getExternalAgreementNo());
//签约场景
bizContent.put("sign_scene", tradeVO.getAliPeriodicVO().getSignScene());
//周期规则参数,必填
JSONObject period_rule_params = new JSONObject();
//周期类型 ,枚举值为 DAY 和 MONTH。周期类型使用MONTH的时候,计划扣款时间 execute_time不允许传 28 日之后的日期(可以传 28 日),以此避免有些月份可能不存在对应日期的情况。
period_rule_params.put("period_type", tradeVO.getAliPeriodicVO().getRulePeriodType());
//必填,周期数,与 period_type 组合使用确定扣款周期,例如 period_type 为 DAYperiod = 90,则扣款周期为 90 天。
period_rule_params.put("period", tradeVO.getAliPeriodicVO().getRulePeriod());
//首次扣款的时间。非支付并签约的成功时间,必填。精确到日,格式为 yyyy-MM-dd。
period_rule_params.put("execute_time", tradeVO.getAliPeriodicVO().getRuleExecuteTime());
//单次扣款最大金额,必填,即每次发起扣款时限制的最大金额,单位为元。商家每次发起扣款都不允许大于此金额。
period_rule_params.put("single_amount", tradeVO.getAliPeriodicVO().getRuleSingleAmount());
//周期内允许扣款的总金额。
period_rule_params.put("total_amount", tradeVO.getAliPeriodicVO().getRuleTotalAmount());
//总扣款次数
period_rule_params.put("total_payments", tradeVO.getAliPeriodicVO().getRuleTotalPayments());
JSONObject access_params = new JSONObject();
//channel:目前支持以下值:ALIPAYAPP :支付宝客户端 H5 页面签约。QRCODE:扫码签约。QRCODEORSMS:扫码签约或者短信签约。
access_params.put("channel", tradeVO.getAliPeriodicVO().getAccessChannel());
bizContent.put("access_params", access_params);
bizContent.put("period_rule_params", period_rule_params);
String bizContentJsonString = bizContent.toJSONString();
request.setBizContent(bizContentJsonString);
request.setNotifyUrl(tradeVO.getNotifyUrl());//设置异步通知地址
request.setReturnUrl(tradeVO.getReturnUrl());//跳转商家处理地址
AlipayClient alipayClient = aliPayConfig.createAlipayClient(tradeVO.getCompanyNo());
try {
// 商家扣款场景请求,生成form表单
AlipayUserAgreementPageSignResponse response = alipayClient.pageExecute(request);
boolean success = response.isSuccess();
if (success){
//保存签约合同记录
SignContract signContract = SignContract.builder()
.contractNo(tradeVO.getAliPeriodicVO().getContractNo())
.externalAgreementNo(tradeVO.getAliPeriodicVO().getExternalAgreementNo())
.signState(SignContractConstant.SIGNSTATE_TEMP)
.tradeChannel(tradeVO.getTradeChannel())
.rulePeriodType(tradeVO.getAliPeriodicVO().getRulePeriodType())
.rulePeriod(Long.valueOf(tradeVO.getAliPeriodicVO().getRulePeriod()))
.ruleTotalAmount(new BigDecimal(tradeVO.getAliPeriodicVO().getRuleTotalAmount()))
.ruleSingleAmount(new BigDecimal(tradeVO.getAliPeriodicVO().getRuleSingleAmount()))
.ruleTotalPayments(Long.valueOf(tradeVO.getAliPeriodicVO().getRuleTotalPayments()))
.build();
signContractService.save(signContract);
//返回签约唤醒
tradeVO.setPlaceOrderCode(TradeConstant.ALI_SUCCESS_CODE);
tradeVO.setPlaceOrderMsg(TradeConstant.ALI_SUCCESS_MSG);
tradeVO.setPlaceOrderJson(response.getBody());
return tradeVO;
}else {
log.error("网关:H5先签约:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(response));
throw new RuntimeException("网关:支付宝H5先签约创建失败!");
}
} catch (Exception e) {
log.error("支付宝H5先签约创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("支付宝H5先签约创建失败!");
}
}
@Override
public TradeVO h5PeriodicPay(TradeVO tradeVO) {
//交易前置处理:参数校验
Boolean flag = beforePayHandler.checkeH5PeriodicPay(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_DEDUCTION_FAIL);
}
//签约前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//周期扣款
AlipayTradePayRequest request = new AlipayTradePayRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", tradeVO.getTradeOrderNo());
bizContent.put("product_code", "GENERAL_WITHHOLDING");//商家扣款产品码固定为GENERAL_WITHHOLDING
bizContent.put("total_amount", tradeVO.getTradeAmount());
bizContent.put("subject", tradeVO.getMemo());
JSONObject agreement_params = new JSONObject();
agreement_params.put("agreement_no", tradeVO.getAliPeriodicVO().getAgreementNo());
bizContent.put("agreement_params", agreement_params);
request.setBizContent(bizContent.toJSONString());
AlipayClient alipayClient = aliPayConfig.createAlipayClient(tradeVO.getCompanyNo());
try {
AlipayTradePayResponse response = alipayClient.execute(request);
boolean success = response.isSuccess();
tradeVO.setResultCode("10000");//因无资质,所以此处为模拟结果
tradeVO.setResultMsg("SUCCESS");//因无资质,所以此处为模拟结果
tradeVO.setResultJson(JSONObject.toJSONString(response));//因无资质,所以此处为模拟结果
tradeVO.setTradeState(TradeConstant.TRADE_SUCCESS);//因无资质,所以此处为模拟结果
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
return BeanConv.toBean(trade, TradeVO.class);
} catch (AlipayApiException e) {
log.error("支付宝H5代扣失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("支付宝H5代扣失败!");
}
}
}
@@ -0,0 +1,74 @@
package com.itheima.sfbx.trade.handler.aliyun;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.handler.WapPayHandler;
import com.itheima.sfbx.trade.client.alipay.Factory;
import com.itheima.sfbx.trade.pojo.Trade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
/**
* @ClassName AliWapPayHandler.java
* @Description 手机网页支付
*/
@Slf4j
@Component
public class AliWapPayHandler extends AliCommonPayHandler implements WapPayHandler {
@Override
public TradeVO wapTrade(TradeVO tradeVO) {
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得支付宝配置文件
Config config = aliPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//5、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
//6、调用接口
AlipayTradeWapPayResponse wapPayResponse = factory.Wap().pay(tradeVO.getMemo(),
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(new BigDecimal("0.01")), tradeVO.getQuitUrl(), tradeVO.getReturnUrl());
//7、检查网关响应结果
boolean isSuccess = ResponseChecker.success(wapPayResponse);
if (isSuccess&&!EmptyUtil.isNullOrEmpty(wapPayResponse.getBody())){
tradeVO.setPlaceOrderCode(TradeConstant.ALI_SUCCESS_CODE);
tradeVO.setPlaceOrderMsg(TradeConstant.ALI_SUCCESS_MSG);
tradeVO.setPlaceOrderJson(wapPayResponse.getBody());
tradeVO.setTradeState(TradeConstant.TRADE_WAIT);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
return BeanConv.toBean(trade, TradeVO.class);
}else {
log.error("网关:支付宝手机网页支付统一下单创建:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(wapPayResponse));
throw new RuntimeException("网关:支付宝手机网页支付统一下单创建失败!");
}
} catch (Exception e) {
log.error("支付宝手机网页支付统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("支付宝手机网页支付统一下单创建失败");
}
}
}
@@ -0,0 +1,361 @@
package com.itheima.sfbx.trade.handler.basic;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.itheima.sfbx.framework.commons.constant.trade.SignContractConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.trade.handler.BeforePayHandler;
import com.itheima.sfbx.trade.pojo.SignContract;
import com.itheima.sfbx.trade.service.IRefundRecordService;
import com.itheima.sfbx.trade.service.ISignContractService;
import com.itheima.sfbx.trade.service.ITradeService;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @ClassName BeforePayHandlerImpl.java
* @Description 阿里交易前置处理接口实现
*/
@Component("beforePayHandler")
public class BasicBeforePayHandler implements BeforePayHandler {
@Autowired
ITradeService tradeService;
@Autowired
IRefundRecordService refundRecordService;
@Autowired
IdentifierGenerator identifierGenerator;
@Override
public void idempotentCreateTrade(TradeVO tradeVO) throws ProjectException {
//查询当前订单对应的最近交易单
Trade trade = tradeService.findTradByProductOrderNo(tradeVO.getProductOrderNo());
//交易单为空:之前未对此订单做过支付
if (EmptyUtil.isNullOrEmpty(trade)) {
tradeVO.setTradeOrderNo((Long)identifierGenerator.nextId(tradeVO));
return;
}
//交易单不为空:判定交易单状态
switch (trade.getTradeState()){
//待支付:交易创建,等待买家付款
case TradeConstant.TRADE_WAIT:
throw new ProjectException(TradeEnum.TRADE_STATE_PAYING);
//已结算:直接抛出重复支付异常
case TradeConstant.TRADE_SUCCESS:
throw new ProjectException(TradeEnum.TRADE_STATE_SUCCEED);
//已关闭:付款交易超时关闭或支付失败,重新发起交易
case TradeConstant.TRADE_CLOSED:
tradeVO.setTradeOrderNo((Long)identifierGenerator.nextId(tradeVO));break;
default:
throw new ProjectException(TradeEnum.PAYING_TRADE_FAIL);
}
}
@Override
public Boolean checkeAppPaySign(TradeVO tradeVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(tradeVO)) {
flag = false;
//订单号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getProductOrderNo())){
flag = false;
//订单总金额为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeAmount())){
flag = false;
//规则为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO())){
flag = false;
//签约场景为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getSignScene())){
flag = false;
//步通知地址为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getSignNotifyUrl())){
flag = false;
//周期类型为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRulePeriodType())){
flag = false;
//周期数为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRulePeriod())){
flag = false;
//下次扣款的时间为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRuleExecuteTime())){
flag = false;
//channel为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getAccessChannel())){
flag = false;
//周期内允许扣款的总金额为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRuleTotalAmount())){
flag = false;
//总扣款次数
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRuleTotalPayments())){
flag = false;
//支付渠道为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeChannel())){
flag=false;
}else {
flag = true;
}
return flag;
}
@Override
public Boolean checkeH5Sign(TradeVO tradeVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(tradeVO)) {
flag = false;
//订单号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getProductOrderNo())){
flag = false;
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO())){
flag = false;
//签约场景为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getSignScene())){
flag = false;
//步通知地址为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getNotifyUrl())){
flag = false;
//步通知地址为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getReturnUrl())){
flag = false;
//周期类型为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRulePeriodType())){
flag = false;
//周期数为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRulePeriod())){
flag = false;
//扣款的时间为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRuleExecuteTime())){
flag = false;
//channel为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getAccessChannel())){
flag = false;
//周期内允许扣款的总金额为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRuleTotalAmount())){
flag = false;
//签约业务编号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getContractNo())){
flag = false;
//总扣款次数
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getRuleTotalPayments())){
flag = false;
//支付渠道为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeChannel())){
flag=false;
}else {
flag = true;
}
return flag;
}
@Override
public Boolean checkeH5PeriodicPay(TradeVO tradeVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(tradeVO)) {
flag = false;
//订单号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getProductOrderNo())){
flag = false;
//订单交易金额
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeAmount())){
flag = false;
//订单签约号
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getAliPeriodicVO().getAgreementNo())){
flag=false;
//订单交易渠道
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeChannel())){
flag=false;
}else {
flag = true;
}
return flag;
}
@Override
public void idempotentRefundTrade(TradeVO tradeVO) {
//查询交易单是否为以结算订单
Trade trade = tradeService.findTradByTradeOrderNo(tradeVO.getTradeOrderNo());
//交易单不存在,或者不为已结算状态:抛出退款失败异常
if (EmptyUtil.isNullOrEmpty(trade)|| !TradeConstant.TRADE_SUCCESS.equals(trade.getTradeState())){
throw new ProjectException(TradeEnum.TRAD_REFUND_FAIL);
}else {
tradeVO.setTradeOrderNo(trade.getTradeOrderNo());
tradeVO.setId(trade.getId());
tradeVO.setTradeAmount(tradeVO.getOperTionRefund());
}
//查询是否有退款中的退款记录
RefundRecord refundRecord = refundRecordService
.findRefundRecordByProductOrderNoAndSending(tradeVO.getProductOrderNo());
if (!EmptyUtil.isNullOrEmpty(refundRecord)){
throw new ProjectException(TradeEnum.TRAD_REFUND_FAIL);
}
}
@Override
public Boolean checkeCreateTrade(TradeVO tradeVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(tradeVO)) {
flag = false;
}else if (TradeConstant.TRADE_CHANNEL_WECHAT_PAY.equals(tradeVO.getTradeChannel())
&&EmptyUtil.isNullOrEmpty(tradeVO.getOpenId())){
flag = false;
//订单号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getProductOrderNo())){
flag = false;
//企业号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getCompanyNo())){
flag = false;
//交易金额为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeAmount())){
flag = false;
//支付渠道为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeChannel())){
flag=false;
}else {
flag = true;
}
return flag;
}
@Override
public Boolean checkeQueryTrade(TradeVO tradeVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(tradeVO)) {
flag = false;
//企业号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getCompanyNo())){
flag = false;
//交易号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeOrderNo())){
flag = false;
}else {
flag = true;
}
return flag;
}
@Override
public Boolean checkeRefundTrade(TradeVO tradeVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(tradeVO)) {
flag = false;
//企业号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getCompanyNo())){
flag = false;
//交易号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeOrderNo())){
flag = false;
//退款请求号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getOutRequestNo())){
flag = false;
//当前退款金额为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getOperTionRefund())){
flag = false;
//退款总金额不可超剩余实付总金额
}else if (tradeVO.getRefund()
.add(tradeVO.getOperTionRefund()).compareTo(tradeVO.getTradeAmount()) > 0){
flag = false;
}else {
flag = true;
}
return flag;
}
@Override
public Boolean checkeQueryRefundTrade(RefundRecordVO refundRecordVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(refundRecordVO)) {
flag = false;
//企业号为空
}else if (EmptyUtil.isNullOrEmpty(refundRecordVO.getCompanyNo())){
flag = false;
//交易号为空
}else if (EmptyUtil.isNullOrEmpty(refundRecordVO.getTradeOrderNo())){
flag = false;
//退款请求号为空
}else if (EmptyUtil.isNullOrEmpty(refundRecordVO.getRefundNo())){
flag = false;
}else {
flag = true;
}
return flag;
}
@Override
public Boolean checkeCloseTrade(TradeVO tradeVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(tradeVO)) {
flag = false;
//交易号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getTradeOrderNo())){
flag = false;
}else {
flag = true;
}
return flag;
}
@Override
public Boolean checkeDownLoadBill(TradeVO tradeVO) {
Boolean flag =null;
//订单为空
if (EmptyUtil.isNullOrEmpty(tradeVO)) {
flag = false;
//企业号为空
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getCompanyNo())){
flag = false;
//账单日期
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getBillDate())){
flag = false;
//账单账单类型
}else if (EmptyUtil.isNullOrEmpty(tradeVO.getBillType())){
flag = false;
}else {
flag = true;
}
return flag;
}
@Autowired
ISignContractService signContractService;
@Override
public void idempotentSignContract(TradeVO tradeVO) {
//查询当前业务对应的签约信息
SignContract signContract = signContractService.findSignContractByContract_no(tradeVO.getAliPeriodicVO().getContractNo());
//签约合同为空:当前业务签约无有效签约号
if (EmptyUtil.isNullOrEmpty(signContract)) {
tradeVO.getAliPeriodicVO().setExternalAgreementNo(String.valueOf(identifierGenerator.nextId(tradeVO)));
return;
}
//签约合同单不为空:判定签约状态
switch (signContract.getSignState()){
//TEMP:暂存,协议未生效过
case SignContractConstant.SIGNSTATE_TEMP:
throw new RuntimeException("暂存");
//NORMAL:正常
case SignContractConstant.SIGNSTATE_NORMAL:
throw new RuntimeException("正常");
//已关闭:付款交易超时关闭或支付失败,重新发起交易
default:
throw new RuntimeException("签约合同异常");
}
}
}
@@ -0,0 +1,74 @@
package com.itheima.sfbx.trade.handler.wechat;
import com.alibaba.fastjson.JSONObject;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.Factory;
import com.itheima.sfbx.trade.client.wechat.response.AppPayResponse;
import com.itheima.sfbx.trade.handler.AppPayHandler;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.trade.utils.ResponseChecker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName WechatAppPayHandler.java
* @Description 原生APP支付处理
*/
@Component
@Slf4j
public class WechatAppPayHandler extends WechatCommonPayHandler implements AppPayHandler {
@Override
public TradeVO appTrade(TradeVO tradeVO){
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得微信客户端
Config config = wechatPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//5、指定配置文件
Factory factory = new Factory();
factory.setOptions(config);
try {
AppPayResponse appPayResponse = factory.App().pay(
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getTradeAmount()),
tradeVO.getMemo());
//6、检查网关响应结果
boolean success = ResponseChecker.success(appPayResponse);
//7、网关响应成功且appPayResponse有返回结果
if (success&&!EmptyUtil.isNullOrEmpty(appPayResponse.getPrepayId())){
tradeVO.setPlaceOrderCode(appPayResponse.getCode());
tradeVO.setPlaceOrderMsg(appPayResponse.getMessage());
tradeVO.setPlaceOrderJson(appPayResponse.getPrepayId());
tradeVO.setTradeState(TradeConstant.TRADE_WAIT);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
return BeanConv.toBean(trade, TradeVO.class);
}else {
log.error("网关:微信APP统一下单:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(appPayResponse));
throw new RuntimeException("网关:微信APP统一下单创建失败!");
}
} catch (Exception e) {
log.error("微信APP统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("微信APP统一下单创建失败!");
}
}
}
@@ -0,0 +1,267 @@
package com.itheima.sfbx.trade.handler.wechat;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.Factory;
import com.itheima.sfbx.trade.client.wechat.response.CloseResponse;
import com.itheima.sfbx.trade.client.wechat.response.QueryResponse;
import com.itheima.sfbx.trade.client.wechat.response.RefundResponse;
import com.itheima.sfbx.trade.config.WechatPayConfig;
import com.itheima.sfbx.trade.handler.BeforePayHandler;
import com.itheima.sfbx.trade.handler.CommonPayHandler;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.trade.service.IRefundRecordService;
import com.itheima.sfbx.trade.service.ITradeService;
import com.itheima.sfbx.trade.utils.ResponseChecker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @ClassName BasicPayHandlerImpl.java
* @Description 微信交易基础类
*/
@Service
@Slf4j
public class WechatCommonPayHandler implements CommonPayHandler {
@Autowired
ITradeService tradeService;
@Autowired
IRefundRecordService refundRecordService;
@Autowired
IdentifierGenerator identifierGenerator;
@Autowired
BeforePayHandler beforePayHandler;
@Autowired
WechatPayConfig wechatPayConfig;
@Override
public TradeVO queryTrade(TradeVO tradeVO) {
//1、查询前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeQueryTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
Trade tradeHandler = tradeService.findTradByTradeOrderNo(tradeVO.getTradeOrderNo());
//2、获得微信客户端
Config config = wechatPayConfig.config(tradeVO.getCompanyNo());
//3、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//4、指定配置文件
Factory factory = new Factory();
factory.setOptions(config);
//5、调用接口
try {
QueryResponse queryResponse = factory.Common()
.query(String.valueOf(tradeVO.getTradeOrderNo()));
//6、判断响应是否成功
boolean success = ResponseChecker.success(queryResponse);
//7、响应成功,分析交易状态
if (success&&!EmptyUtil.isNullOrEmpty(queryResponse.getTradeState())){
//SUCCESS:支付成功,REFUND:转入退款,NOTPAY:未支付,CLOSED:已关闭,REVOKED:已撤销(仅付款码支付会返回)
//USERPAYING:用户支付中(仅付款码支付会返回),PAYERROR:支付失败(仅付款码支付会返回)
switch (queryResponse.getTradeState()){
case TradeConstant.WECHAT_TRADE_CLOSED:
tradeHandler.setTradeState(TradeConstant.TRADE_CLOSED);break;
case TradeConstant.WECHAT_TRADE_REVOKED:
tradeHandler.setTradeState(TradeConstant.TRADE_CLOSED);break;
case TradeConstant.WECHAT_TRADE_SUCCESS:
tradeHandler.setTradeState(TradeConstant.TRADE_SUCCESS);break;
case TradeConstant.WECHAT_TRADE_REFUND:
tradeHandler.setTradeState(TradeConstant.TRADE_SUCCESS);break;
default:
flag = false;break;
}
//8、修改交易单状态
if (flag){
tradeHandler.setResultCode(queryResponse.getTradeState());
tradeHandler.setResultMsg(queryResponse.getTradeStateDesc());
tradeHandler.setResultJson(JSONObject.toJSONString(queryResponse));
tradeService.updateById(tradeHandler);
return BeanConv.toBean(tradeHandler,TradeVO.class);
}else {
log.info("查询微信交易单:{},结果:{}", tradeVO.getTradeOrderNo(),queryResponse.getTradeState());
}
}else {
throw new RuntimeException("查询微信统一下单失败!");
}
} catch (Exception e) {
log.warn("查询微信统一下单失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("查询微信统一下单失败!");
}
return tradeVO;
}
@Override
public TradeVO refundTrade(TradeVO tradeVO) {
//1、生成退款请求编号
tradeVO.setOutRequestNo(String.valueOf(identifierGenerator.nextId(tradeVO)));
Trade tradeHandler = tradeService.findTradByTradeOrderNo(tradeVO.getTradeOrderNo());
tradeVO.setTradeAmount(tradeHandler.getTradeAmount());
//2、退款前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeRefundTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_REFUND_FAIL);
}
//3、退款前置处理:退款幂等性校验
beforePayHandler.idempotentRefundTrade(tradeVO);
//4、获得微信客户端
Config config = wechatPayConfig.config(tradeVO.getCompanyNo());
//5、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//6、指定配置文件
Factory factory = new Factory();
factory.setOptions(config);
//7、调用接口
try {
RefundResponse refundResponse = factory.Common().refund(
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getOperTionRefund()),
tradeVO.getOutRequestNo(),String.valueOf(tradeVO.getTradeAmount()));
boolean success = ResponseChecker.success(refundResponse);
if (success&&String.valueOf(tradeVO.getTradeOrderNo()).equals(refundResponse.getOutTradeNo())){
//8、指定此交易单是否有退款:YES
tradeHandler.setIsRefund(SuperConstant.YES);
tradeHandler.setRefund(tradeVO.getRefund().add(tradeVO.getOperTionRefund()));
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.updateById(trade);
//6、保存退款单信息
RefundRecord refundRecord = RefundRecord.builder()
.companyNo(tradeHandler.getCompanyNo())
.refundNo(tradeVO.getOutRequestNo())
.refundAmount(tradeVO.getOperTionRefund())
.refundCode(refundResponse.getCode())
.refundMsg(refundResponse.getMessage())
.productOrderNo(tradeHandler.getProductOrderNo())
.tradeChannel(tradeHandler.getTradeChannel())
.tradeOrderNo(tradeHandler.getTradeOrderNo())
.memo("退款:"+tradeHandler.getMemo())
.build();
switch (refundResponse.getStatus()){
case TradeConstant.WECHAT_REFUND_SUCCESS:
refundRecord.setRefundStatus(TradeConstant.REFUND_STATUS_SUCCESS);break;
case TradeConstant.WECHAT_REFUND_CLOSED:
refundRecord.setRefundStatus(TradeConstant.REFUND_STATUS_CLOSED);break;
case TradeConstant.WECHAT_REFUND_PROCESSING:
refundRecord.setRefundStatus(TradeConstant.REFUND_STATUS_SENDING);break;
default:
refundRecord.setRefundStatus(TradeConstant.REFUND_STATUS_FAIL);break;
}
refundRecordService.save(refundRecord);
}else {
log.error("网关:微信统一下单退款失败:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(refundResponse));
throw new RuntimeException("网关:微信统一下单退款失败!");
}
} catch (Exception e) {
log.error("微信统一下单退款失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_REFUND_FAIL);
}
return BeanConv.toBean(tradeHandler,TradeVO.class);
}
@Override
public RefundRecordVO queryRefundTrade(RefundRecordVO refundRecordVO) {
//1、退款前置处理:检测退款单参数
Boolean flag = beforePayHandler.checkeQueryRefundTrade(refundRecordVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_QUERY_REFUND_FAIL);
}
RefundRecord refundRecordHandler = refundRecordService.findRefundRecordByRefundNo(refundRecordVO.getRefundNo());
//2、获得微信客户端
Config config = wechatPayConfig.config(refundRecordVO.getCompanyNo());
//3、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//4、指定配置文件
Factory factory = new Factory();
factory.setOptions(config);
try {
RefundResponse refundResponse = factory.Common().queryRefund(refundRecordVO.getRefundNo());
//5、判断响应是否成功
boolean success = ResponseChecker.success(refundResponse);
//6、查询出的退款状态
if (success&&TradeConstant.WECHAT_REFUND_SUCCESS.equals(refundResponse.getStatus())){
refundRecordHandler.setRefundStatus(TradeConstant.REFUND_STATUS_SUCCESS);
refundRecordHandler.setRefundCode(refundResponse.getCode());
refundRecordHandler.setRefundMsg(refundResponse.getMessage());
refundRecordService.updateById(BeanConv.toBean(refundRecordHandler,RefundRecord.class));
}else {
log.error("网关:查询微信统一下单退款失败:{},结果:{}", refundRecordVO.getTradeOrderNo(),
JSONObject.toJSONString(refundResponse));
throw new RuntimeException("网关:查询微信统一下单退款失败!");
}
} catch (Exception e) {
log.warn("查询微信统一下单退款失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("查询微信统一下单退款失败!");
}
return refundRecordVO;
}
@Override
public TradeVO closeTrade(TradeVO tradeVO) {
//1、退款前置处理:检测关闭参数tradeVO
Boolean flag = beforePayHandler.checkeCloseTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_CLOSE_FAIL);
}
Trade tradeHandler = tradeService.findTradByTradeOrderNo(tradeVO.getTradeOrderNo());
//2、获得微信客户端
Config config = wechatPayConfig.config(tradeVO.getCompanyNo());
//3、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new ProjectException(TradeEnum.CONFIG_ERROR);
}
//4、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
//5、调用三方API关闭订单
CloseResponse closeResponse = factory.Common()
.close(String.valueOf(tradeVO.getTradeOrderNo()));
//6、关闭订单受理情况
boolean success = ResponseChecker.success(closeResponse);
if (success){
tradeHandler.setTradeState(TradeConstant.TRADE_CLOSED);
tradeService.updateById(tradeHandler);
return BeanConv.toBean(tradeHandler,TradeVO.class);
}else {
log.error("网关:微信关闭订单失败:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(closeResponse));
throw new RuntimeException("网关:微信关闭订单失败!");
}
} catch (Exception e) {
log.warn("微信关闭订单失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new ProjectException(TradeEnum.TRAD_CLOSE_FAIL);
}
}
@Override
public TradeVO downLoadBill(TradeVO tradeVO) {
//toDo 后面补齐
throw new RuntimeException("未支持:微信关下载账单!");
}
}
@@ -0,0 +1,81 @@
package com.itheima.sfbx.trade.handler.wechat;
import com.alibaba.fastjson.JSONObject;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.Factory;
import com.itheima.sfbx.trade.client.wechat.response.PreCreateResponse;
import com.itheima.sfbx.trade.handler.FaceToFacePayHandler;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.trade.utils.ResponseChecker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName WechatNativePayHandler.java
* @Description Native支付方式:商户生成二维码,用户扫描支付
*/
@Slf4j
@Component
public class WechatFaceToFacePayHandler extends WechatCommonPayHandler implements FaceToFacePayHandler {
@Override
public TradeVO payTrade(TradeVO tradeVO) {
//因微信商户扫用户模式新版本未出,还是V2版本,因此未接入,等待更新
throw new RuntimeException("因微信商户扫用户模式新版本未出,还是V2版本,因此未接入,等待更新");
}
@Override
public TradeVO precreateTrade(TradeVO tradeVO) {
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得微信客户端
Config config = wechatPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new RuntimeException("微信支付配置为空!");
}
//5、使用配置
Factory factory = new Factory();
factory.setOptions(config);
try {
//4、调用接口
PreCreateResponse preCreateResponse =factory.FaceToFace().precreate(
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getTradeAmount()),
tradeVO.getMemo());
//7、受理结果【只表示请求是否成功,而不是支付是否成功】
boolean success = ResponseChecker.success(preCreateResponse);
//8、受理成功:修改交易单
if (success&&!EmptyUtil.isNullOrEmpty(preCreateResponse.getCodeUrl())){
tradeVO.setPlaceOrderCode(preCreateResponse.getCode());
tradeVO.setPlaceOrderMsg(preCreateResponse.getMessage());
tradeVO.setPlaceOrderJson(preCreateResponse.getCodeUrl());
tradeVO.setTradeState(TradeConstant.TRADE_WAIT);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
}else {
log.error("网关:precreateResponse{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(preCreateResponse));
throw new RuntimeException("网关:微信商家扫用户统一下单创建失败!");
}
} catch (Exception e) {
log.error("微信用户扫商家统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("微信商家扫用户统一下单创建失败!");
}
return tradeVO;
}
}
@@ -0,0 +1,75 @@
package com.itheima.sfbx.trade.handler.wechat;
import com.alibaba.fastjson.JSONObject;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.Factory;
import com.itheima.sfbx.trade.client.wechat.response.PagePayResponse;
import com.itheima.sfbx.trade.handler.PagePayHandler;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.trade.utils.ResponseChecker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName WechatPagePayHandler.java
* @Description PC网页支付处理实现
*/
@Slf4j
@Component
public class WechatPagePayHandler extends WechatCommonPayHandler implements PagePayHandler {
@Override
public TradeVO pageTrade(TradeVO tradeVO){
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.TRAD_PAY_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得微信客户端
Config config = wechatPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new RuntimeException("微信支付配置为空!");
}
//5、指定配置文件
Factory factory = new Factory();
factory.setOptions(config);
try {
//6、调用接口
PagePayResponse pagePayResponse = factory.Page().pay(
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getTradeAmount()),
tradeVO.getMemo(),
tradeVO.getOpenId());
//7、受理结果【只表示请求是否成功,而不是支付是否成功】
boolean success = ResponseChecker.success(pagePayResponse);
if (success&&!EmptyUtil.isNullOrEmpty(pagePayResponse.getPrepayId())){
tradeVO.setPlaceOrderCode(pagePayResponse.getCode());
tradeVO.setPlaceOrderMsg(pagePayResponse.getMessage());
tradeVO.setPlaceOrderJson(pagePayResponse.getPrepayId());
tradeVO.setTradeState(TradeConstant.TRADE_WAIT);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
}else {
log.error("网关:微信Page支付preCreate{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(pagePayResponse));
throw new RuntimeException("网关:微信Page支付preCreate发生异常!");
}
} catch (Exception e) {
log.error("微信用户扫商家统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("微信商家扫用户统一下单创建失败!");
}
return tradeVO;
}
}
@@ -0,0 +1,79 @@
package com.itheima.sfbx.trade.handler.wechat;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.trade.service.ITradeService;
import com.itheima.sfbx.trade.config.WechatPayConfig;
import com.itheima.sfbx.trade.handler.PayNotifyHandler;
import com.itheima.sfbx.trade.pojo.Trade;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
/**
* @ClassName AliNotifyHandler.java
* @Description 支付通知接口
*/
@Component
public class WechatPayNotifyHandler implements PayNotifyHandler {
@Autowired
WechatPayConfig wechatPayConfig;
@Autowired
ITradeService tradeService;
@Override
public String notify(HttpServletRequest request, HttpEntity<String> httpEntity,String companyNo) {
//获取请求头
HttpHeaders headers = httpEntity.getHeaders();
//构建微信请求数据对象
NotificationRequest notificationRequest = new NotificationRequest.Builder()
.withSerialNumber(headers.getFirst("Wechatpay-Serial")) //证书序列号(微信平台)
.withNonce(headers.getFirst("Wechatpay-Nonce")) //随机串
.withTimestamp(headers.getFirst("Wechatpay-Timestamp")) //时间戳
.withSignature(headers.getFirst("Wechatpay-Signature")) //签名字符串
.withBody(httpEntity.getBody())
.build();
//验证签名,确保请求来自微信
JSONObject jsonData;
try {
//确保在管理器中存在自动更新的商户证书
CertificatesManager certificatesManager = CertificatesManager.getInstance();
Verifier verifier = certificatesManager.getVerifier(wechatPayConfig.config(companyNo).getMchId());
//验签和解析请求数据
NotificationHandler notificationHandler = new NotificationHandler(verifier, wechatPayConfig.config(companyNo)
.getApiV3Key().getBytes(StandardCharsets.UTF_8));
Notification notification = notificationHandler.parse(notificationRequest);
if (!StrUtil.equals("TRANSACTION.SUCCESS", notification.getEventType())) {
return "verify sign fail";
}
//获取解密后的数据
jsonData = JSONUtil.parseObj(notification.getDecryptData());
} catch (Exception e) {
throw new RuntimeException("微信通知:验签失败");
}
if (!StrUtil.equals(jsonData.getStr("trade_state"), TradeConstant.WECHAT_TRADE_SUCCESS)) {
Trade trade = tradeService.findTradByTradeOrderNo(jsonData.getLong("out_trade_no"));
trade.setResultCode(jsonData.getStr("trade_state"));
trade.setResultMsg(TradeConstant.ALI_SUCCESS_MSG);
trade.setResultJson(com.alibaba.fastjson.JSONObject.toJSONString(jsonData));
trade.setTradeState(TradeConstant.TRADE_WAIT);
tradeService.updateById(trade);
return "SUCCESS";
}else {
return "FAIL";
}
}
}
@@ -0,0 +1,75 @@
package com.itheima.sfbx.trade.handler.wechat;
import com.alibaba.fastjson.JSONObject;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.trade.TradeEnum;
import com.itheima.sfbx.framework.commons.exception.ProjectException;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
import com.itheima.sfbx.trade.client.wechat.Config;
import com.itheima.sfbx.trade.client.wechat.Factory;
import com.itheima.sfbx.trade.client.wechat.response.WapPayResponse;
import com.itheima.sfbx.trade.handler.WapPayHandler;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.trade.utils.ResponseChecker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName WechatWapPayHandler.java
* @Description Jsapi支付处理
*/
@Slf4j
@Component
public class WechatWapPayHandler extends WechatCommonPayHandler implements WapPayHandler {
@Override
public TradeVO wapTrade(TradeVO tradeVO){
//1、交易前置处理:检测交易单参数
Boolean flag = beforePayHandler.checkeCreateTrade(tradeVO);
if (!flag){
throw new ProjectException(TradeEnum.CHECK_TRADE_FAIL);
}
//2、交易前置处理:幂等性处理
beforePayHandler.idempotentCreateTrade(tradeVO);
//3、获得微信客户端
Config config = wechatPayConfig.config(tradeVO.getCompanyNo());
//4、配置如果为空,抛出异常
if (EmptyUtil.isNullOrEmpty(config)){
throw new RuntimeException("微信支付配置为空!");
}
//5、指定配置文件
Factory factory = new Factory();
factory.setOptions(config);
try {
//6、调用接口
WapPayResponse wapPayResponse = factory.Wap().pay(
String.valueOf(tradeVO.getTradeOrderNo()),
String.valueOf(tradeVO.getTradeAmount()),
tradeVO.getMemo(),
tradeVO.getOpenId());
//7、检查网关响应结果
boolean success = ResponseChecker.success(wapPayResponse);
if (success&&!EmptyUtil.isNullOrEmpty(wapPayResponse.getPrepayId())){
tradeVO.setPlaceOrderCode(wapPayResponse.getCode());
tradeVO.setPlaceOrderMsg(wapPayResponse.getMessage());
tradeVO.setPlaceOrderJson(wapPayResponse.getPrepayId());
tradeVO.setTradeState(TradeConstant.TRADE_WAIT);
Trade trade = BeanConv.toBean(tradeVO, Trade.class);
tradeService.save(trade);
return BeanConv.toBean(trade, TradeVO.class);
}else {
log.error("网关:支付宝手机网页支付统一下单创建:{},结果:{}", tradeVO.getTradeOrderNo(),
JSONObject.toJSONString(wapPayResponse));
throw new RuntimeException("网关:微信手机网页支付统一下单创建失败!");
}
} catch (Exception e) {
log.error("微信手机网页支付统一下单创建失败:{}", ExceptionsUtil.getStackTraceAsString(e));
throw new RuntimeException("微信手机网页支付统一下单创建失败");
}
}
}
@@ -0,0 +1,13 @@
package com.itheima.sfbx.trade.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.sfbx.trade.pojo.PayChannel;
import org.apache.ibatis.annotations.Mapper;
/**
* @Description:交易渠道表Mapper接口
*/
@Mapper
public interface PayChannelMapper extends BaseMapper<PayChannel> {
}
@@ -0,0 +1,13 @@
package com.itheima.sfbx.trade.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import org.apache.ibatis.annotations.Mapper;
/**
* @Description:退款记录表Mapper接口
*/
@Mapper
public interface RefundRecordMapper extends BaseMapper<RefundRecord> {
}
@@ -0,0 +1,14 @@
package com.itheima.sfbx.trade.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import com.itheima.sfbx.trade.pojo.SignContract;
import org.apache.ibatis.annotations.Mapper;
/**
* @Description:签约合同Mapper接口
*/
@Mapper
public interface SignContractMapper extends BaseMapper<SignContract> {
}
@@ -0,0 +1,13 @@
package com.itheima.sfbx.trade.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.sfbx.trade.pojo.Trade;
import org.apache.ibatis.annotations.Mapper;
/**
* @Description:交易订单表Mapper接口
*/
@Mapper
public interface TradeMapper extends BaseMapper<Trade> {
}
@@ -0,0 +1,73 @@
package com.itheima.sfbx.trade.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.itheima.sfbx.framework.mybatisplus.basic.BasePojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* @Description:交易渠道表
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("tab_pay_channel")
@ApiModel(value="PayChannel对象", description="交易渠道表")
public class PayChannel extends BasePojo {
private static final long serialVersionUID = 1L;
@Builder
public PayChannel(Long id, String dataState, String channelName, String channelLabel, String domain, String appId, String publicKey, String merchantPrivateKey, String otherConfig, String encryptKey, String remark, String companyNo, String notifyUrl) {
super(id, dataState);
this.channelName = channelName;
this.channelLabel = channelLabel;
this.domain = domain;
this.appId = appId;
this.publicKey = publicKey;
this.merchantPrivateKey = merchantPrivateKey;
this.otherConfig = otherConfig;
this.encryptKey = encryptKey;
this.remark = remark;
this.companyNo = companyNo;
this.notifyUrl = notifyUrl;
}
@ApiModelProperty(value = "通道名称")
private String channelName;
@ApiModelProperty(value = "通道唯一标记")
private String channelLabel;
@ApiModelProperty(value = "域名")
private String domain;
@ApiModelProperty(value = "商户appid")
private String appId;
@ApiModelProperty(value = "支付公钥")
private String publicKey;
@ApiModelProperty(value = "商户私钥")
private String merchantPrivateKey;
@ApiModelProperty(value = "其他配置")
private String otherConfig;
@ApiModelProperty(value = "AES混淆密钥")
private String encryptKey;
@ApiModelProperty(value = "说明")
private String remark;
@ApiModelProperty(value = "商户ID【系统内部识别使用】")
private String companyNo;
@ApiModelProperty(value = "回调地址")
private String notifyUrl;
}
@@ -0,0 +1,74 @@
package com.itheima.sfbx.trade.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.itheima.sfbx.framework.mybatisplus.basic.BasePojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @Description:退款记录表
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("tab_refund_record")
@ApiModel(value="RefundRecord对象", description="退款记录表")
public class RefundRecord extends BasePojo {
private static final long serialVersionUID = 1L;
@Builder
public RefundRecord(Long id, String dataState, Long tradeOrderNo, Long productOrderNo, String refundNo,
String companyNo, String tradeChannel, String refundStatus, String refundCode,
String refundMsg, String memo, BigDecimal refundAmount) {
super(id, dataState);
this.tradeOrderNo = tradeOrderNo;
this.productOrderNo = productOrderNo;
this.refundNo = refundNo;
this.companyNo = companyNo;
this.tradeChannel = tradeChannel;
this.refundStatus = refundStatus;
this.refundCode = refundCode;
this.refundMsg = refundMsg;
this.memo = memo;
this.refundAmount = refundAmount;
}
@ApiModelProperty(value = "交易系统订单号【对于三方来说:商户订单】")
private Long tradeOrderNo;
@ApiModelProperty(value = "业务系统订单号")
private Long productOrderNo;
@ApiModelProperty(value = "本次退款订单号")
private String refundNo;
@ApiModelProperty(value = "商户号")
private String companyNo;
@ApiModelProperty(value = "退款渠道【支付宝、微信、现金】")
private String tradeChannel;
@ApiModelProperty(value = "退款状态【成功:SUCCESS,进行中:SENDING】")
private String refundStatus;
@ApiModelProperty(value = "返回编码")
private String refundCode;
@ApiModelProperty(value = "返回信息")
private String refundMsg;
@ApiModelProperty(value = "备注【订单门店,桌台信息】")
private String memo;
@ApiModelProperty(value = "本次退款金额")
private BigDecimal refundAmount;
}
@@ -0,0 +1,71 @@
package com.itheima.sfbx.trade.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.itheima.sfbx.framework.mybatisplus.basic.BasePojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @ClassName SignContract.java
* @Description 签约合同
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("tab_sign_contract")
@ApiModel(value="SignContract对象", description="签约合同")
public class SignContract extends BasePojo {
private static final long serialVersionUID = 1L;
@Builder
public SignContract(String agreementNo, Long id, String dataState, String contractNo, String externalAgreementNo, String signState, String tradeChannel, String rulePeriodType, Long rulePeriod, BigDecimal ruleTotalAmount, BigDecimal ruleSingleAmount, Long ruleTotalPayments) {
super(id, dataState);
this.contractNo = contractNo;
this.externalAgreementNo = externalAgreementNo;
this.signState = signState;
this.tradeChannel = tradeChannel;
this.rulePeriodType = rulePeriodType;
this.rulePeriod = rulePeriod;
this.ruleTotalAmount = ruleTotalAmount;
this.ruleSingleAmount = ruleSingleAmount;
this.ruleTotalPayments = ruleTotalPayments;
this.agreementNo = agreementNo;
}
@ApiModelProperty(value = "合同编号:关联业务")
private String contractNo;
@ApiModelProperty(value = "商户签约号")
private String externalAgreementNo;
@ApiModelProperty(value = "支付宝签约号:关联支付")
private String agreementNo;
@ApiModelProperty(value = "签约状态:1. TEMP:暂存,协议未生效过;2. NORMAL:正常;3. STOP:暂停")
private String signState;
@ApiModelProperty(value = "支付渠道【支付宝、微信】")
private String tradeChannel;
@ApiModelProperty(value = "周期类型")
private String rulePeriodType;
@ApiModelProperty(value = "周期数")
private Long rulePeriod;
@ApiModelProperty(value = "总金额")
private BigDecimal ruleTotalAmount;
@ApiModelProperty(value = "总金额")
private BigDecimal ruleSingleAmount;
@ApiModelProperty(value = "扣款总次数")
private Long ruleTotalPayments;
}
@@ -0,0 +1,112 @@
package com.itheima.sfbx.trade.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.itheima.sfbx.framework.mybatisplus.basic.BasePojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @Description:交易订单表
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("tab_trade")
@ApiModel(value="Trade对象", description="交易订单表")
public class Trade extends BasePojo {
private static final long serialVersionUID = 1L;
@Builder
public Trade(Long id, String dataState, String openId, Long productOrderNo, Long tradeOrderNo, String tradeChannel, String tradeState, String payeeName, Long payeeId, String payerName, Long payerId, BigDecimal tradeAmount, BigDecimal refund, String isRefund, String resultCode, String resultMsg, String resultJson, String placeOrderCode, String placeOrderMsg, String placeOrderJson, String companyNo, String memo) {
super(id, dataState);
this.openId = openId;
this.productOrderNo = productOrderNo;
this.tradeOrderNo = tradeOrderNo;
this.tradeChannel = tradeChannel;
this.tradeState = tradeState;
this.payeeName = payeeName;
this.payeeId = payeeId;
this.payerName = payerName;
this.payerId = payerId;
this.tradeAmount = tradeAmount;
this.refund = refund;
this.isRefund = isRefund;
this.resultCode = resultCode;
this.resultMsg = resultMsg;
this.resultJson = resultJson;
this.placeOrderCode = placeOrderCode;
this.placeOrderMsg = placeOrderMsg;
this.placeOrderJson = placeOrderJson;
this.companyNo = companyNo;
this.memo = memo;
}
@ApiModelProperty(value = "openId标识")
private String openId;
@ApiModelProperty(value = "业务系统订单号")
private Long productOrderNo;
@ApiModelProperty(value = "交易系统订单号【对于三方来说:商户订单】")
private Long tradeOrderNo;
@ApiModelProperty(value = "支付渠道【支付宝、微信】")
private String tradeChannel;
@ApiModelProperty(value = "交易单状态 0待付款 1已支付 2已关闭)")
private String tradeState;
@ApiModelProperty(value = "收款人姓名")
private String payeeName;
@ApiModelProperty(value = "收款人账户ID")
private Long payeeId;
@ApiModelProperty(value = "付款人姓名")
private String payerName;
@ApiModelProperty(value = "付款人Id")
private Long payerId;
@ApiModelProperty(value = "交易金额")
private BigDecimal tradeAmount;
@ApiModelProperty(value = "退款金额【付款后】")
private BigDecimal refund;
@ApiModelProperty(value = "是否有退款:YESNO")
private String isRefund;
@ApiModelProperty(value = "第三方交易返回编码【最终确认交易结果】")
private String resultCode;
@ApiModelProperty(value = "第三方交易返回提示消息【最终确认交易信息】")
private String resultMsg;
@ApiModelProperty(value = "第三方交易返回信息json【分析交易最终信息】")
private String resultJson;
@ApiModelProperty(value = "统一下单返回编码")
private String placeOrderCode;
@ApiModelProperty(value = "统一下单返回信息")
private String placeOrderMsg;
@ApiModelProperty(value = "统一下单返回信息json【用于生产二维码、Android ios唤醒支付等】")
private String placeOrderJson;
@ApiModelProperty(value = "商户ID【系统内部识别使用】")
private String companyNo;
@ApiModelProperty(value = "备注【订单门店,桌台信息】")
private String memo;
}
@@ -0,0 +1,51 @@
package com.itheima.sfbx.trade.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.sfbx.trade.pojo.PayChannel;
import com.itheima.sfbx.framework.commons.dto.trade.PayChannelVO;
import java.util.List;
/**
* @Description 支付通道服务类
*/
public interface IPayChannelService extends IService<PayChannel> {
/**
* @Description 支付通道列表
* @param payChannelVO 查询条件
* @param pageNum 当前页
* @param pageSize 当前页
* @return Page<PayChannel>
*/
Page<PayChannel> findPayChannelVOPage(PayChannelVO payChannelVO, int pageNum, int pageSize);
/**
* @Description 创建支付通道
* @param payChannelVO 对象信息
* @return PayChannel
*/
PayChannel createPayChannel(PayChannelVO payChannelVO);
/**
* @Description 修改支付通道
* @param payChannelVO 对象信息
* @return Boolean
*/
Boolean updatePayChannel(PayChannelVO payChannelVO);
/**
* @Description 删除支付通道
* @param checkedIds 选择的支付通道ID
* @return Boolean
*/
Boolean deletePayChannel(String[] checkedIds);
/**
* @Description 查找有效渠道标识
* @param channelLabel 支付通道标识
* @return Boolean
*/
PayChannel findPayChannelVlid(String channelLabel);
}
@@ -0,0 +1,37 @@
package com.itheima.sfbx.trade.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import java.util.List;
/**
* @Description 退款记录表服务类
*/
public interface IRefundRecordService extends IService<RefundRecord> {
/***
* @description 查询当前订单是否有退款中的记录
*
* @param productOrderNo
* @return
*/
RefundRecord findRefundRecordByProductOrderNoAndSending(Long productOrderNo);
/***
* @description 按RefundNo查询退款单
*
* @param refundNo
* @return
*/
RefundRecord findRefundRecordByRefundNo(String refundNo);
/***
* @description 订单对应的退款单
*
* @param productOrderNos
* @return
*/
List<RefundRecordVO> findRefundRecordInProductOrderNo(List<Long> productOrderNos);
}
@@ -0,0 +1,79 @@
package com.itheima.sfbx.trade.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.sfbx.framework.commons.dto.trade.SignContractVO;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.SignContractVO;
import com.itheima.sfbx.trade.pojo.SignContract;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import com.itheima.sfbx.trade.pojo.SignContract;
import java.util.List;
/**
* @Description 签约合同服务类
*/
public interface ISignContractService extends IService<SignContract> {
/**
* @Description 签约合同分页
* @param payChannelVO 查询条件
* @param pageNum 当前页
* @param pageSize 当前页
* @return Page<SignContract>
*/
Page<SignContract> findSignContractPage(SignContractVO payChannelVO, int pageNum, int pageSize);
/**
* @Description 创建签约合同
* @param payChannelVO 对象信息
* @return SignContract
*/
SignContract createSignContract(SignContractVO payChannelVO);
/**
* @Description 修改签约合同
* @param payChannelVO 对象信息
* @return Boolean
*/
Boolean updateSignContract(SignContractVO payChannelVO);
/**
* @Description 删除签约合同
* @param checkedIds 选择的签约合同ID
* @return Boolean
*/
Boolean deleteSignContract(String[] checkedIds);
/***
* @description 多条件查询
* @param signContractVO
* @return
*/
List<SignContract> findSignContractList(SignContractVO signContractVO);
/***
* @description 查询当前业务对应的有效签约信息
* @param contractNo
* @return
*/
SignContract findSignContractByContract_no(String contractNo);
/***
* @description 关闭h5签约
* @param warrantyNo 合同号
* @param tradingChannel 渠道
* @return
*/
Boolean h5CloseSignContract(String warrantyNo, String tradingChannel);
/***
* @description 签约同步
* @param warrantyNo 合同号
* @param tradingChannel 交易渠道
* @param agreementNo 签约号
* @return
*/
Boolean signContractSync(String warrantyNo, String tradingChannel, String agreementNo);
}
@@ -0,0 +1,45 @@
package com.itheima.sfbx.trade.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.trade.pojo.Trade;
import java.util.List;
/**
* @Description:交易订单表 服务类
*/
public interface ITradeService extends IService<Trade> {
/***
* @description 按交易单号查询交易单
* @param tradeOrderNo 交易单号
* @return
*/
Trade findTradByTradeOrderNo(Long tradeOrderNo);
/***
* @description 按订单单号查询交易单
* @param productOrderNo 交易单号
* @return
*/
Trade findTradByProductOrderNo(Long productOrderNo);
/***
* @description 按交易状态查询最近10分钟交易单
* @param tradeState
* @return
*/
List<Trade> findTradeByTradeState(String tradeState);
/***
* @description 分页查询
*
* @param tradeVO
* @param pageNum
* @param pageSize
* @return
*/
Page<Trade> findPayChannelVOPage(TradeVO tradeVO, int pageNum, int pageSize);
}
@@ -0,0 +1,77 @@
package com.itheima.sfbx.trade.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.dto.trade.PayChannelVO;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.mapper.PayChannelMapper;
import com.itheima.sfbx.trade.pojo.PayChannel;
import com.itheima.sfbx.trade.service.IPayChannelService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/**
* @Description 服务实现类
*/
@Service
public class PayChannelServiceImpl extends ServiceImpl<PayChannelMapper, PayChannel> implements IPayChannelService {
@Override
public Page<PayChannel> findPayChannelVOPage(PayChannelVO payChannelVO, int pageNum, int pageSize) {
Page<PayChannel> page = new Page<>(pageNum,pageSize);
QueryWrapper<PayChannel> queryWrapper = new QueryWrapper<>();
if (!EmptyUtil.isNullOrEmpty(payChannelVO.getId())) {
queryWrapper.lambda().like(PayChannel::getId,payChannelVO.getId());
}
if (!EmptyUtil.isNullOrEmpty(payChannelVO.getChannelLabel())) {
queryWrapper.lambda().eq(PayChannel::getChannelLabel,payChannelVO.getChannelLabel());
}
if (!EmptyUtil.isNullOrEmpty(payChannelVO.getChannelName())) {
queryWrapper.lambda().likeRight(PayChannel::getChannelName,payChannelVO.getChannelName());
}
if (!EmptyUtil.isNullOrEmpty(payChannelVO.getDataState())) {
queryWrapper.lambda().eq(PayChannel::getDataState,payChannelVO.getDataState());
}
queryWrapper.lambda().orderByAsc(PayChannel::getCreateTime);
return page(page, queryWrapper);
}
@Override
public PayChannel createPayChannel(PayChannelVO payChannelVO) {
PayChannel payChannel = BeanConv.toBean(payChannelVO, PayChannel.class);
payChannel.setOtherConfig(JSONObject.toJSONString(payChannelVO.getOtherConfigs()));
boolean flag = save(payChannel);
if (flag){
return payChannel;
}
return null;
}
@Override
public Boolean updatePayChannel(PayChannelVO payChannelVO) {
PayChannel payChannel = BeanConv.toBean(payChannelVO, PayChannel.class);
payChannel.setOtherConfig(JSONObject.toJSONString(payChannelVO.getOtherConfigs()));
return updateById(payChannel);
}
@Override
public Boolean deletePayChannel(String[] checkedIds) {
List<String> ids = Arrays.asList(checkedIds);
return removeByIds(ids);
}
@Override
public PayChannel findPayChannelVlid(String channelLabel) {
QueryWrapper<PayChannel> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(PayChannel::getChannelLabel,channelLabel)
.eq(PayChannel::getDataState, SuperConstant.DATA_STATE_0);
return getOne(queryWrapper);
}
}
@@ -0,0 +1,42 @@
package com.itheima.sfbx.trade.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.sfbx.framework.commons.constant.trade.TradeConstant;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.trade.mapper.RefundRecordMapper;
import com.itheima.sfbx.trade.pojo.RefundRecord;
import com.itheima.sfbx.trade.service.IRefundRecordService;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Description 退款记录服务实现类
*/
@Service
public class RefundRecordServiceImpl extends ServiceImpl<RefundRecordMapper, RefundRecord> implements IRefundRecordService {
@Override
public RefundRecord findRefundRecordByProductOrderNoAndSending(Long productOrderNo) {
QueryWrapper<RefundRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(RefundRecord::getProductOrderNo, productOrderNo)
.eq(RefundRecord::getRefundStatus, TradeConstant.REFUND_STATUS_SENDING);
return getOne(queryWrapper);
}
@Override
public RefundRecord findRefundRecordByRefundNo(String refundNo) {
QueryWrapper<RefundRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(RefundRecord::getRefundNo, refundNo);
return getOne(queryWrapper);
}
@Override
public List<RefundRecordVO> findRefundRecordInProductOrderNo(List<Long> productOrderNos) {
QueryWrapper<RefundRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().in(RefundRecord::getProductOrderNo, productOrderNos);
return BeanConv.toBeanList(list(queryWrapper),RefundRecordVO.class);
}
}
@@ -0,0 +1,129 @@
package com.itheima.sfbx.trade.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.sfbx.framework.commons.constant.trade.SignContractConstant;
import com.itheima.sfbx.framework.commons.dto.trade.SignContractVO;
import com.itheima.sfbx.framework.commons.utils.BeanConv;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.mapper.SignContractMapper;
import com.itheima.sfbx.trade.pojo.SignContract;
import com.itheima.sfbx.trade.service.ISignContractService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
/**
* @Description 签约合成服务实现类
*/
@Service
public class SignContractServiceImpl extends ServiceImpl<SignContractMapper, SignContract> implements ISignContractService {
/***
* @description 签约合成多条件组合
* @param signContract 签约合成
* @return QueryWrapper查询条件
*/
private QueryWrapper<SignContract> queryWrapper(SignContractVO signContract){
QueryWrapper<SignContract> queryWrapper = new QueryWrapper<>();
//合同编号:关联业务
if (!EmptyUtil.isNullOrEmpty(signContract.getContractNo())) {
queryWrapper.lambda().eq(SignContract::getContractNo,signContract.getContractNo());
}
//商户签约号:关联支付
if (!EmptyUtil.isNullOrEmpty(signContract.getExternalAgreementNo())) {
queryWrapper.lambda().eq(SignContract::getExternalAgreementNo,signContract.getExternalAgreementNo());
}
//理赔项值查询
if (!EmptyUtil.isNullOrEmpty(signContract.getSignState())) {
queryWrapper.lambda().eq(SignContract::getSignState,signContract.getSignState());
}
//支付渠道【支付宝、微信】
if (!EmptyUtil.isNullOrEmpty(signContract.getTradeChannel())) {
queryWrapper.lambda().eq(SignContract::getTradeChannel,signContract.getTradeChannel());
}
//周期类型
if (!EmptyUtil.isNullOrEmpty(signContract.getRulePeriodType())) {
queryWrapper.lambda().eq(SignContract::getRulePeriodType,signContract.getRulePeriodType());
}
//按创建时间降序
queryWrapper.lambda().orderByDesc(SignContract::getCreateTime);
return queryWrapper;
}
@Override
public Page<SignContract> findSignContractPage(SignContractVO signContractVO, int pageNum, int pageSize) {
Page<SignContract> page = new Page<>(pageNum,pageSize);
QueryWrapper<SignContract> queryWrapper =queryWrapper(signContractVO);
return page(page, queryWrapper);
}
@Override
public SignContract createSignContract(SignContractVO signContractVO) {
SignContract signContract = BeanConv.toBean(signContractVO, SignContract.class);
boolean flag = save(signContract);
if (flag){
return signContract;
}
return null;
}
@Override
public Boolean updateSignContract(SignContractVO signContractVO) {
SignContract signContract = BeanConv.toBean(signContractVO, SignContract.class);
return updateById(signContract);
}
@Override
public Boolean deleteSignContract(String[] checkedIds) {
List<String> ids = Arrays.asList(checkedIds);
return removeByIds(ids);
}
@Override
public List<SignContract> findSignContractList(SignContractVO signContractVO) {
QueryWrapper<SignContract> queryWrapper =queryWrapper(signContractVO);
return list(queryWrapper);
}
@Override
public SignContract findSignContractByContract_no(String contractNo) {
QueryWrapper<SignContract> queryWrapper= new QueryWrapper<>();
queryWrapper.lambda()
.eq(SignContract::getContractNo,contractNo);
queryWrapper.lambda().and(QueryWrapper->{
QueryWrapper
.eq(SignContract::getSignState,SignContractConstant.SIGNSTATE_NORMAL)
.or()
.eq(SignContract::getSignState,SignContractConstant.SIGNSTATE_TEMP);
});
return getOne(queryWrapper);
}
@Override
public Boolean h5CloseSignContract(String warrantyNo, String tradingChannel) {
UpdateWrapper<SignContract> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda()
.set(SignContract::getSignState,SignContractConstant.SIGNSTATE_STOP)
.eq(SignContract::getContractNo,warrantyNo)
.eq(SignContract::getTradeChannel,tradingChannel);
return update(updateWrapper);
}
@Override
@Transactional
public Boolean signContractSync(String warrantyNo, String tradingChannel, String agreementNo) {
UpdateWrapper<SignContract> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda()
.set(SignContract::getSignState,SignContractConstant.SIGNSTATE_NORMAL)
.set(SignContract::getAgreementNo,agreementNo)
.eq(SignContract::getContractNo,warrantyNo)
.eq(SignContract::getTradeChannel,tradingChannel);
return update(updateWrapper);
}
}
@@ -0,0 +1,70 @@
package com.itheima.sfbx.trade.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.trade.mapper.TradeMapper;
import com.itheima.sfbx.trade.pojo.Trade;
import com.itheima.sfbx.trade.service.ITradeService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
/**
* @Description:交易订单表 服务实现类
*/
@Service
public class TradeServiceImpl extends ServiceImpl<TradeMapper, Trade> implements ITradeService {
@Override
public Trade findTradByTradeOrderNo(Long tradeOrderNo) {
QueryWrapper<Trade> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(Trade::getTradeOrderNo,tradeOrderNo);
return getOne(queryWrapper);
}
@Override
public Trade findTradByProductOrderNo(Long productOrderNo) {
LambdaQueryWrapper<Trade> queryWrapper = Wrappers.<Trade>lambdaQuery();
queryWrapper.eq(Trade::getProductOrderNo,productOrderNo).orderByDesc(Trade::getCreateTime);
List<Trade> list = list(queryWrapper);
if (!EmptyUtil.isNullOrEmpty(list)){
return list.get(0);
}
return null;
}
@Override
public List<Trade> findTradeByTradeState(String tradeState) {
QueryWrapper<Trade> queryWrapper = new QueryWrapper<>();
LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(600);
queryWrapper.lambda().eq(Trade::getTradeState,tradeState)
.gt(Trade::getCreateTime, localDateTime)
.eq(Trade::getDataState, SuperConstant.DATA_STATE_0);
return list(queryWrapper);
}
@Override
public Page<Trade> findPayChannelVOPage(TradeVO tradeVO, int pageNum, int pageSize) {
Page<Trade> page = new Page<>(pageNum,pageSize);
QueryWrapper<Trade> queryWrapper = new QueryWrapper<>();
if (!EmptyUtil.isNullOrEmpty(tradeVO.getTradeOrderNo())) {
queryWrapper.lambda().eq(Trade::getTradeOrderNo,tradeVO.getTradeOrderNo());
}
if (!EmptyUtil.isNullOrEmpty(tradeVO.getProductOrderNo())) {
queryWrapper.lambda().likeRight(Trade::getProductOrderNo,tradeVO.getProductOrderNo());
}
if (!EmptyUtil.isNullOrEmpty(tradeVO.getTradeState())) {
queryWrapper.lambda().eq(Trade::getTradeState,tradeVO.getTradeState());
}
queryWrapper.lambda().orderByAsc(Trade::getCreateTime);
return page(page, queryWrapper);
}
}
@@ -0,0 +1,16 @@
package com.itheima.sfbx.trade.utils;
import com.itheima.sfbx.trade.client.wechat.response.BasicResponse;
/**
* @ClassName ResponseChecker.java
* @Description 微信结果检查
*/
public class ResponseChecker {
public static boolean success(BasicResponse response) {
String code = response.getCode();
return code.equals("200");
}
}
@@ -0,0 +1,84 @@
package com.itheima.sfbx.trade.web;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.sfbx.framework.commons.basic.ResponseResult;
import com.itheima.sfbx.framework.commons.enums.basic.BaseEnum;
import com.itheima.sfbx.framework.commons.utils.ResponseResultBuild;
import com.itheima.sfbx.trade.face.PayChannelFace;
import com.itheima.sfbx.framework.commons.dto.trade.PayChannelVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName PayChannelController.java
* @Description 支付通道
*/
@RestController
@RequestMapping("pay-channel")
@Slf4j
@Api(tags = "支付通道")
public class PayChannelController {
@Autowired
PayChannelFace payChannelFace;
/**
* @Description 支付通道列表
* @param payChannelVO 查询条件
* @return
*/
@PostMapping("page/{pageNum}/{pageSize}")
@ApiOperation(value = "查询支付通道分页",notes = "查询支付通道分页")
@ApiImplicitParams({
@ApiImplicitParam(name = "payChannelVO",value = "支付通道查询对象",required = true,dataType = "PayChannelVO"),
@ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",dataType = "Integer"),
@ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",dataType = "Integer")
})
public ResponseResult<Page<PayChannelVO>> findPayChannelVOPage(
@RequestBody PayChannelVO payChannelVO,
@PathVariable("pageNum") int pageNum,
@PathVariable("pageSize") int pageSize) {
Page<PayChannelVO> payChannelVOPage = payChannelFace.findPayChannelVOPage(payChannelVO, pageNum, pageSize);
return ResponseResultBuild.build(BaseEnum.SUCCEED,payChannelVOPage);
}
/**
* @Description 添加支付通道
* @param payChannelVO 对象信息
* @return
*/
@PostMapping
@ApiOperation(value = "添加支付通道",notes = "添加支付通道")
@ApiImplicitParam(name = "payChannelVO",value = "支付通道对象",required = true,dataType = "PayChannelVO")
ResponseResult<PayChannelVO> createPayChannel(@RequestBody PayChannelVO payChannelVO) {
PayChannelVO payChannelVOResult = payChannelFace.createPayChannel(payChannelVO);
return ResponseResultBuild.build(BaseEnum.SUCCEED,payChannelVOResult);
}
/**
* @Description 修改支付通道
* @param payChannelVO 对象信息
* @return
*/
@PatchMapping
@ApiOperation(value = "修改支付通道",notes = "修改支付通道")
@ApiImplicitParam(name = "payChannelVO",value = "支付通道对象",required = true,dataType = "PayChannelVO")
ResponseResult<Boolean> updatePayChannel(@RequestBody PayChannelVO payChannelVO) {
Boolean flag = payChannelFace.updatePayChannel(payChannelVO);
return ResponseResultBuild.build(BaseEnum.SUCCEED,flag);
}
@PatchMapping("dataState")
@ApiOperation(value = "修改支付通道状态",notes = "修改支付通道状态")
@ApiImplicitParam(name = "payChannelVO",value = "支付通道查询对象",required = true,dataType = "PayChannelVO")
ResponseResult<Boolean> updatePayChannelEnableFlag(@RequestBody PayChannelVO payChannelVO) {
Boolean flag = payChannelFace.updatePayChannel(payChannelVO);
return ResponseResultBuild.build(BaseEnum.SUCCEED,flag);
}
}
@@ -0,0 +1,54 @@
package com.itheima.sfbx.trade.web;
import com.itheima.sfbx.trade.handler.PayNotifyHandler;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName AliPayNotifyController.java
* @Description TODO
*/
@RestController
@RequestMapping("pay-notify")
@Slf4j
@Api(tags = "支付通道回调")
public class PayNotifyController {
@Autowired
PayNotifyHandler aliPayNotifyHandler;
@Autowired
PayNotifyHandler wechatPayNotifyHandler;
/***
* @description 阿里支付回调
* @param request http请求对象:支付宝
* @param httpEntity http请求对象:微信
*/
@PostMapping("ali-pay-notify/{companyNo}")
String aliPayNotify(HttpServletRequest request, HttpEntity<String> httpEntity,
@PathVariable("companyNo") String companyNo){
log.info("==========支付宝通知接口companyNo{}",companyNo);
return aliPayNotifyHandler.notify(request,httpEntity,companyNo);
}
/***
* @description 微信支付回调
* @param request http请求对象:支付宝
* @param httpEntity http请求对象:微信
*/
@PostMapping("wechat-pay-notify/{companyNo}")
String wechatPayNotify(HttpServletRequest request, HttpEntity<String> httpEntity,
@PathVariable("companyNo") String companyNo){
log.info("===============微信通知接口companyNo{}",companyNo);
return wechatPayNotifyHandler.notify(request,httpEntity,companyNo);
}
}
@@ -0,0 +1,47 @@
package com.itheima.sfbx.trade.web;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.sfbx.framework.commons.basic.ResponseResult;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.framework.commons.enums.basic.BaseEnum;
import com.itheima.sfbx.framework.commons.utils.ResponseResultBuild;
import com.itheima.sfbx.trade.face.TradeFace;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName TradeFace.java
* @Description 交易Face接口
*/
@RequestMapping("trade")
@RestController
@Api(tags = "账单明细")
public class TradeController {
@Autowired
TradeFace tradeFace;
/**
* @Description 账单明细
* @param tradeVO 查询条件
* @return
*/
@PostMapping("page/{pageNum}/{pageSize}")
@ApiOperation(value = "账单明细",notes = "账单明细")
@ApiImplicitParams({
@ApiImplicitParam(name = "tradeVO",value = "支付通道查询对象",required = true,dataType = "TradeVO"),
@ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",dataType = "Integer"),
@ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",dataType = "Integer")
})
public ResponseResult<Page<TradeVO>> findTradeVOPage(
@RequestBody TradeVO tradeVO,
@PathVariable("pageNum") int pageNum,
@PathVariable("pageSize") int pageSize) {
Page<TradeVO> page = tradeFace.findTradeVOPage(tradeVO, pageNum, pageSize);
return ResponseResultBuild.build(BaseEnum.SUCCEED,page);
}
}

Some files were not shown because too many files have changed in this diff Show More