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
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>sfbx-cloud</artifactId>
<groupId>com.itheima.sfbx</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<!--支付模块-->
<artifactId>sfbx-trade</artifactId>
<name>sfbx-trade</name>
<packaging>pom</packaging>
<modules>
<!--数据埋点接口端模块-->
<module>trade-interface</module>
<!--数据埋点web端模块-->
<module>trade-web</module>
</modules>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
</project>
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.itheima.sfbx</groupId>
<artifactId>sfbx-trade</artifactId>
<version>2.0-SNAPSHOT</version>
</parent>
<!--支付接口模块-->
<artifactId>trade-interface</artifactId>
<name>trade-interface</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<dependencies>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-feign</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,13 @@
package com.itheima.sfbx.trade.config;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
/**
* @ClassName DictFenginConfig.java
* @Description feign的最优化配置
*/
@EnableFeignClients(basePackages = "com.itheima.sfbx.trade.feign")
@Configuration
public class TradeFeignConfig {
}
@@ -0,0 +1,23 @@
package com.itheima.sfbx.trade.feign;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.trade.hystrix.AppPayHystrix;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @ClassName AppPayFeign.java
* @Description App方式支付feign接口
*/
@FeignClient(value = "trade-web",fallback = AppPayHystrix.class )
public interface AppPayFeign {
/***
* @description 外部商户APP唤起快捷SDK创建订单并支付
* @param tradeVO 订单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
@PostMapping("trade-app-feign/app-trade")
TradeVO appTrade(@RequestBody TradeVO tradeVO);
}
@@ -0,0 +1,71 @@
package com.itheima.sfbx.trade.feign;
import com.itheima.sfbx.trade.hystrix.CommonPayHystrix;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @ClassName CommonPayFeign.java
* @Description 基础支付处理能力feign接口
*/
@FeignClient(value = "trade-web",fallback = CommonPayHystrix.class )
public interface CommonPayFeign {
/***
* @description 查看二维码信息
* 收银员通过收银台或商户后台调用此接口,生成二维码后,展示给用户,商户可以多次展示二维码
* @param tradeVO 交易单
* @return 二维码路径
*/
@PostMapping("trade-common-feign/query-qr-code")
String queryQrCode(@RequestBody TradeVO tradeVO);
/***
* @description 统一收单线下交易查询
* 该接口提供所有支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。
* @param tradeVO 交易单
* @return
*/
@PostMapping("trade-common-feign/query")
TradeVO queryTrade(@RequestBody TradeVO tradeVO);
/***
* @description 统一收单交易退款接口
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
* 将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* @param tradeVO 交易单
* @return
*/
@PostMapping("trade-common-feign/refund")
TradeVO refundTrade(@RequestBody TradeVO tradeVO);
/***
* @description 统一收单交易退款查询接口
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
* 将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* @param refundRecordVO 退款交易单
* @return
*/
@PostMapping("trade-common-feign/query-refund")
void queryRefundTrade(@RequestBody RefundRecordVO refundRecordVO);
/***
* @description 统一关闭订单
* 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
* 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
* @param tradeVO 退款交易单
* @return
*/
@PostMapping("trade-common-feign/close-refund")
TradeVO closeTrade(@RequestBody TradeVO tradeVO);
/***
* @description 计划任务:同步交易单结果
* @return
*/
@PostMapping("trade-common-feign/sync-payment-job")
Boolean syncPaymentJob();
}
@@ -0,0 +1,32 @@
package com.itheima.sfbx.trade.feign;
import com.itheima.sfbx.trade.hystrix.FaceToFacePayHystrix;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @ClassName FaceToFacePayFeign.java
* @Description 面对面支付接口
*/
@FeignClient(value = "trade-web",fallback = FaceToFacePayHystrix.class )
public interface FaceToFacePayFeign {
/***
* @description 扫用户出示的付款码,完成付款
* @param tradeVO 订单
* @return 支付结果
*/
@PostMapping("trade-face-to-face-feign/pay")
TradeVO pay(@RequestBody TradeVO tradeVO);
/***
* @description 生成交易付款码,待用户扫码付款
* @param tradeVO 订单
* @return 二维码路径
*/
@PostMapping("trade-face-to-face-feign/precreate")
TradeVO precreate(@RequestBody TradeVO tradeVO);
}
@@ -0,0 +1,24 @@
package com.itheima.sfbx.trade.feign;
import com.itheima.sfbx.trade.hystrix.PagePayHystrix;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @ClassName PagePayFeign.java
* @Description PC网页支付feign接口
*/
@FeignClient(value = "trade-web",fallback = PagePayHystrix.class )
public interface PagePayFeign {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
@PostMapping("trade-page-feign/page-trade")
TradeVO pageTrade(@RequestBody TradeVO tradeVO);
}
@@ -0,0 +1,62 @@
package com.itheima.sfbx.trade.feign;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import com.itheima.sfbx.trade.hystrix.WapPayHystrix;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @ClassName WapPayFeign.java
* @Description 手机网页支付feign接口
*/
@FeignClient(value = "trade-web",fallback = WapPayHystrix.class )
public interface PeriodicPayFeign {
/***
* @description app签约并支付
* @param tradeVO 交易单
* @return 交易单
*/
@PostMapping("trade-periodic-feign/app-pay-sign")
TradeVO appPaySign(@RequestBody TradeVO tradeVO);
/***
* @description h5签约
* @param tradeVO 交易单
* @return 交易单
*/
@PostMapping("trade-periodic-feign/h5-sign-contract")
TradeVO h5SignContract(@RequestBody TradeVO tradeVO);
/***
* @description h5签约代扣
* @param tradeVO 交易单
* @return 交易单
*/
@PostMapping("trade-periodic-feign/h5-periodic-pay")
TradeVO h5PeriodicPay(@RequestBody TradeVO tradeVO);
/***
* @description 签约同步
* @param warrantyNo 合同编号
* @param tradingChannel 支付渠道
* @param agreementNo 支付签约号
* @return
*/
@PostMapping("trade-periodic-feign/sign-contract-sync/{warrantyNo}/{tradingChannel}/{agreementNo}")
Boolean signContractSync(@PathVariable("warrantyNo") String warrantyNo,
@PathVariable("tradingChannel")String tradingChannel,
@PathVariable("agreementNo")String agreementNo);
/***
* @description 关闭h5签约
* @param warrantyNo 合同号
* @param tradingChannel 渠道号
* @return 交易单
*/
@PostMapping("trade-periodic-feign/h5-close-sign-contract/{warrantyNo}/{tradingChannel}")
Boolean h5CloseSignContract(@PathVariable("warrantyNo") String warrantyNo,
@PathVariable("tradingChannel")String tradingChannel);
}
@@ -0,0 +1,31 @@
package com.itheima.sfbx.trade.feign;
import com.itheima.sfbx.trade.hystrix.WapPayHystrix;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @ClassName WapPayFeign.java
* @Description 手机网页支付feign接口
*/
@FeignClient(value = "trade-web",fallback = WapPayHystrix.class )
public interface WapPayFeign {
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
@PostMapping("trade-wap-feign/wap-trade")
TradeVO wapTrade(@RequestBody TradeVO tradeVO);
/***
* @description 生成交易表单,渲染后自动跳转支付宝或者微信引导用户完成支付
* @param tradeVO 订单单
* @return TradeVO对象中的placeOrderJson:阿里云form表单字符串、微信prepay_id标识
*/
@PostMapping("trade-common-feign/close")
TradeVO closeTrade(@RequestBody TradeVO tradeVO);
}
@@ -0,0 +1,19 @@
package com.itheima.sfbx.trade.hystrix;
import com.itheima.sfbx.trade.feign.AppPayFeign;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.stereotype.Component;
/**
* @ClassName AppPayHystrix.java
* @Description TODO
*/
@Component
public class AppPayHystrix implements AppPayFeign {
@Override
public TradeVO appTrade(TradeVO tradeVO) {
return null;
}
}
@@ -0,0 +1,45 @@
package com.itheima.sfbx.trade.hystrix;
import com.itheima.sfbx.trade.feign.CommonPayFeign;
import com.itheima.sfbx.framework.commons.dto.trade.RefundRecordVO;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.stereotype.Component;
/**
* @ClassName CommonPayHystrix.java
* @Description TODO
*/
@Component
public class CommonPayHystrix implements CommonPayFeign {
@Override
public String queryQrCode(TradeVO tradeVO) {
return null;
}
@Override
public TradeVO queryTrade(TradeVO tradeVO) {
return null;
}
@Override
public TradeVO refundTrade(TradeVO tradeVO) {
return null;
}
@Override
public void queryRefundTrade(RefundRecordVO refundRecordVO) {
}
@Override
public TradeVO closeTrade(TradeVO tradeVO) {
return null;
}
@Override
public Boolean syncPaymentJob() {
return null;
}
}
@@ -0,0 +1,24 @@
package com.itheima.sfbx.trade.hystrix;
import com.itheima.sfbx.trade.feign.FaceToFacePayFeign;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.stereotype.Component;
/**
* @ClassName FaceToFacePayHystrix.java
* @Description FaceToFacePayFeign的Hystrix i
*/
@Component
public class FaceToFacePayHystrix implements FaceToFacePayFeign {
@Override
public TradeVO pay(TradeVO tradeVO) {
return null;
}
@Override
public TradeVO precreate(TradeVO tradeVO) {
return null;
}
}
@@ -0,0 +1,18 @@
package com.itheima.sfbx.trade.hystrix;
import com.itheima.sfbx.trade.feign.PagePayFeign;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.stereotype.Component;
/**
* @ClassName PagePayHystrix.java
* @Description TODO
*/
@Component
public class PagePayHystrix implements PagePayFeign {
@Override
public TradeVO pageTrade(TradeVO tradeVO) {
return null;
}
}
@@ -0,0 +1,24 @@
package com.itheima.sfbx.trade.hystrix;
import com.itheima.sfbx.trade.feign.WapPayFeign;
import com.itheima.sfbx.framework.commons.dto.trade.TradeVO;
import org.springframework.stereotype.Component;
/**
* @ClassName WapPayHystrix.java
* @Description TODO
*/
@Component
public class WapPayHystrix implements WapPayFeign {
@Override
public TradeVO wapTrade(TradeVO tradeVO) {
return null;
}
@Override
public TradeVO closeTrade(TradeVO tradeVO) {
return null;
}
}
+21
View File
@@ -0,0 +1,21 @@
FROM openjdk:11-jdk
LABEL maintainer="研究院研发组 <research@itcast.cn>"
# 时区修改为东八区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /trade-web
ARG PACKAGE_PATH=./target/trade-web.jar
ADD ${PACKAGE_PATH:-./} trade-web.jar
EXPOSE 8080
ENV JAVA_OPTS="\
-server \
-Xms256m \
-Xmx512m \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m\
-Dspring.profiles.active=test"
ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom -jar $JAVA_OPTS trade-web.jar"]
+100
View File
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.itheima.sfbx</groupId>
<artifactId>sfbx-trade</artifactId>
<version>2.0-SNAPSHOT</version>
</parent>
<!--支付web模块-->
<artifactId>trade-web</artifactId>
<name>trade-web</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-mybatis-plus</artifactId>
</dependency>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-task-executor</artifactId>
</dependency>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-redis</artifactId>
</dependency>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-seata</artifactId>
</dependency>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-knife4j-web</artifactId>
</dependency>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-rabbitmq</artifactId>
</dependency>
<!-- 阿里easy版本支付- -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
</dependency>
<!-- 阿里通用版本支付- -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.yaml</include>
<include>**/*.txt</include>
</includes>
</resource>
</resources>
<finalName>trade-web</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -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> {
}

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