From 267e2508208f3ac1a8f7b76635b05892ca227875 Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Sun, 13 Jul 2025 14:28:15 +0800 Subject: [PATCH] =?UTF-8?q?7.13=20=E6=94=AF=E4=BB=98=E5=AE=9D=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E6=B2=99=E7=AE=B1=E7=94=B3=E8=AF=B7=E3=80=81=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5alipay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + docs/dev-ops/mysql/sql/0713paymall.sql | 48 ++++++++ .../dev-ops/mysql/sql/xfg-frame-archetype.sql | 108 ----------------- pay-mall-api/pom.xml | 4 + .../main/java/edu/whut/api/IPayService.java | 10 ++ .../edu/whut/api/dto/CreatePayRequestDTO.java | 13 ++ .../java/edu/whut/api/dto/package-info.java | 4 - .../java/edu/whut/config/AliPayConfig.java | 23 ++++ .../whut/config/AliPayConfigProperties.java | 31 +++++ .../src/main/resources/application-dev.yml | 20 +++- .../src/main/resources/application.yml | 4 +- .../mybatis/mapper/pay_order_mapper.xml | 5 + .../test/java/edu/whut/test/AliPayTest.java | 111 ++++++++++++++++++ pay-mall-domain/pom.xml | 5 + .../adapter/repository/IOrderRepository.java | 2 + .../order/service/AbstractOrderService.java | 19 ++- .../domain/order/service/OrderService.java | 47 ++++++++ .../adapter/repository/OrderRepository.java | 14 ++- .../whut/infrastructure/dao/IOrderDao.java | 2 + .../whut/trigger/http/AliPayController.java | 107 +++++++++++++++++ .../trigger/http/WeixinPortalController.java | 2 + .../java/edu/whut/types/common/Constants.java | 1 + pom.xml | 7 +- 23 files changed, 462 insertions(+), 126 deletions(-) create mode 100644 docs/dev-ops/mysql/sql/0713paymall.sql delete mode 100644 docs/dev-ops/mysql/sql/xfg-frame-archetype.sql create mode 100644 pay-mall-api/src/main/java/edu/whut/api/IPayService.java create mode 100644 pay-mall-api/src/main/java/edu/whut/api/dto/CreatePayRequestDTO.java delete mode 100644 pay-mall-api/src/main/java/edu/whut/api/dto/package-info.java create mode 100644 pay-mall-app/src/main/java/edu/whut/config/AliPayConfig.java create mode 100644 pay-mall-app/src/main/java/edu/whut/config/AliPayConfigProperties.java create mode 100644 pay-mall-app/src/test/java/edu/whut/test/AliPayTest.java create mode 100644 pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java diff --git a/.gitignore b/.gitignore index 613ee2d..3127462 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ build/ .DS_Store /data/ /.idea/ +/pay-mall-app/src/main/resources/application-local.yml diff --git a/docs/dev-ops/mysql/sql/0713paymall.sql b/docs/dev-ops/mysql/sql/0713paymall.sql new file mode 100644 index 0000000..4ef4bc0 --- /dev/null +++ b/docs/dev-ops/mysql/sql/0713paymall.sql @@ -0,0 +1,48 @@ +/* + Navicat Premium Data Transfer + + Source Server : group_buy_local + Source Server Type : MySQL + Source Server Version : 80042 + Source Host : localhost:13306 + Source Schema : pay-mall + + Target Server Type : MySQL + Target Server Version : 80042 + File Encoding : 65001 + + Date: 13/07/2025 14:21:55 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for pay_order +-- ---------------------------- +DROP TABLE IF EXISTS `pay_order`; +CREATE TABLE `pay_order` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID', + `product_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品ID', + `product_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品名称', + `order_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单ID', + `order_time` datetime NOT NULL COMMENT '下单时间', + `total_amount` decimal(8, 2) UNSIGNED NULL DEFAULT NULL COMMENT '订单金额', + `status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单状态;create-创建完成、pay_wait-等待支付、pay_success-支付成功、deal_done-交易完成、close-订单关单', + `pay_url` varchar(2014) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '支付信息', + `pay_time` datetime NULL DEFAULT NULL COMMENT '支付时间', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uq_order_id`(`order_id` ASC) USING BTREE, + INDEX `idx_user_id_product_id`(`user_id` ASC, `product_id` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of pay_order +-- ---------------------------- +INSERT INTO `pay_order` VALUES (5, 'smile01', '10001', '测试商品', '51403944017404', '2025-07-12 05:49:16', 1.68, 'PAY_WAIT', '
\n\n\n
\n', NULL, '2025-07-12 13:49:15', '2025-07-13 14:21:33'); +INSERT INTO `pay_order` VALUES (6, '10001', '10001', '测试商品', '66665553128265', '2025-07-13 06:19:11', 1.68, 'PAY_WAIT', '
\n\n\n
\n', NULL, '2025-07-13 14:19:11', '2025-07-13 14:21:21'); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/docs/dev-ops/mysql/sql/xfg-frame-archetype.sql b/docs/dev-ops/mysql/sql/xfg-frame-archetype.sql deleted file mode 100644 index 8b598d3..0000000 --- a/docs/dev-ops/mysql/sql/xfg-frame-archetype.sql +++ /dev/null @@ -1,108 +0,0 @@ -/* - Navicat Premium Data Transfer - - Source Server : 127.0.0.1 - Source Server Type : MySQL - Source Server Version : 50639 - Source Host : localhost:3306 - Source Schema : road-map - - Target Server Type : MySQL - Target Server Version : 50639 - File Encoding : 65001 - - Date: 15/07/2023 09:26:39 -*/ - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - -CREATE database if NOT EXISTS `xfg_frame_archetype` default character set utf8mb4 collate utf8mb4_0900_ai_ci; -use `xfg_frame_archetype`; - --- ---------------------------- --- Table structure for employee --- ---------------------------- -DROP TABLE IF EXISTS `employee`; -CREATE TABLE `employee` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `employee_number` varchar(16) NOT NULL DEFAULT '' COMMENT '雇员ID', - `employee_name` varchar(32) NOT NULL DEFAULT '' COMMENT '雇员姓名', - `employee_level` varchar(8) NOT NULL DEFAULT '' COMMENT '雇员级别', - `employee_title` varchar(16) NOT NULL DEFAULT '' COMMENT '雇员岗位Title', - `create_time` datetime NOT NULL COMMENT '创建时间', - `update_time` datetime NOT NULL COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `idx_employee_number` (`employee_number`) -) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8; - --- ---------------------------- --- Records of employee --- ---------------------------- -BEGIN; -INSERT INTO `employee` VALUES (1, '10000001', 'sXvfDpsWnJdLsCVk64tJgw==', 'T-3', '中级工程师', '2023-07-14 15:26:26', '2023-07-14 15:26:26'); -INSERT INTO `employee` VALUES (2, '10000010', 'sXvfDpsWnJdLsCVk64tJgw==', 'T2', '见习工程师', '2023-07-14 15:34:40', '2023-07-14 15:34:40'); -INSERT INTO `employee` VALUES (3, '10000011', 'sXvfDpsWnJdLsCVk64tJgw==', 'T2', '见习工程师', '2023-07-14 15:34:40', '2023-07-14 15:34:40'); -INSERT INTO `employee` VALUES (4, '10000012', 'sXvfDpsWnJdLsCVk64tJgw==', 'T2', '见习工程师', '2023-07-14 15:34:40', '2023-07-14 15:34:40'); -INSERT INTO `employee` VALUES (5, '10000013', 'sXvfDpsWnJdLsCVk64tJgw==', 'T2', '见习工程师', '2023-07-14 15:34:40', '2023-07-14 15:34:40'); -INSERT INTO `employee` VALUES (6, '10000014', 'sXvfDpsWnJdLsCVk64tJgw==', 'T2', '见习工程师', '2023-07-14 15:34:40', '2023-07-14 15:34:40'); -INSERT INTO `employee` VALUES (9, '10000002', 'sXvfDpsWnJdLsCVk64tJgw==', 'T2', '见习工程师', '2023-07-15 07:42:52', '2023-07-15 07:42:52'); -INSERT INTO `employee` VALUES (22, '10000015', 'hMCgLG6WV3CsNBQ1UD6PEQ==', 'T2', '见习工程师', '2023-07-15 08:02:31', '2023-07-15 08:02:31'); -INSERT INTO `employee` VALUES (23, '10000016', 'hMCgLG6WV3CsNBQ1UD6PEQ==', 'T2', '见习工程师', '2023-07-15 08:02:31', '2023-07-15 08:02:31'); -INSERT INTO `employee` VALUES (24, '10000017', 'hMCgLG6WV3CsNBQ1UD6PEQ==', 'T2', '见习工程师', '2023-07-15 08:02:31', '2023-07-15 08:02:31'); -INSERT INTO `employee` VALUES (39, '10000022', 'GyG+V0r6mBCNsdusuKl03g==', 'T1', '实习工程师', '2023-07-15 09:17:49', '2023-07-15 09:17:49'); -COMMIT; - --- ---------------------------- --- Table structure for employee_salary --- ---------------------------- -DROP TABLE IF EXISTS `employee_salary`; -CREATE TABLE `employee_salary` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `employee_number` varchar(16) NOT NULL DEFAULT '' COMMENT '雇员编号', - `salary_total_amount` decimal(8,2) NOT NULL COMMENT '薪资总额', - `salary_merit_amount` decimal(8,2) NOT NULL COMMENT '绩效工资', - `salary_base_amount` decimal(8,2) NOT NULL COMMENT '基础工资', - `create_time` datetime NOT NULL COMMENT '创建时间', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `idx_employee_number` (`employee_number`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; - --- ---------------------------- --- Records of employee_salary --- ---------------------------- -BEGIN; -INSERT INTO `employee_salary` VALUES (1, '10000001', 5100.00, 1020.00, 4080.00, '2023-07-14 16:09:06', '2023-07-14 16:09:06'); -INSERT INTO `employee_salary` VALUES (2, '10000010', 5000.00, 1000.00, 4000.00, '2023-07-14 16:17:10', '2023-07-14 16:17:10'); -INSERT INTO `employee_salary` VALUES (3, '10000011', 5000.00, 1000.00, 4000.00, '2023-07-14 16:17:10', '2023-07-14 16:17:10'); -INSERT INTO `employee_salary` VALUES (4, '10000012', 5000.00, 1000.00, 4000.00, '2023-07-14 16:17:10', '2023-07-14 16:17:10'); -INSERT INTO `employee_salary` VALUES (5, '10000013', 5000.00, 1000.00, 4000.00, '2023-07-14 16:17:10', '2023-07-14 16:17:10'); -INSERT INTO `employee_salary` VALUES (6, '10000014', 5000.00, 1000.00, 4000.00, '2023-07-14 16:17:10', '2023-07-14 16:17:10'); -INSERT INTO `employee_salary` VALUES (8, '10000022', 100.00, 10.00, 90.00, '2023-07-15 09:17:49', '2023-07-15 09:17:49'); -COMMIT; - --- ---------------------------- --- Table structure for employee_salary_adjust --- ---------------------------- -DROP TABLE IF EXISTS `employee_salary_adjust`; -CREATE TABLE `employee_salary_adjust` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `employee_number` varchar(16) NOT NULL DEFAULT '' COMMENT '雇员编号', - `adjust_order_id` varchar(32) NOT NULL DEFAULT '' COMMENT '调薪单号', - `adjust_total_amount` decimal(8,2) NOT NULL COMMENT '总额调薪', - `adjust_base_amount` decimal(8,2) NOT NULL COMMENT '基础调薪', - `adjust_merit_amount` decimal(8,2) NOT NULL COMMENT '绩效调薪', - `create_time` datetime NOT NULL COMMENT '创建时间', - `update_time` datetime NOT NULL COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `idx_order_id` (`adjust_order_id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; - --- ---------------------------- --- Records of employee_salary_adjust --- ---------------------------- -BEGIN; -INSERT INTO `employee_salary_adjust` VALUES (1, '10000001', '109089990198888811', 1000.00, 800.00, 200.00, '2023-07-14 16:55:53', '2023-07-14 16:55:53'); -INSERT INTO `employee_salary_adjust` VALUES (2, '10000001', '100908977676001', 100.00, 20.00, 80.00, '2023-07-14 21:57:39', '2023-07-14 21:57:39'); -COMMIT; \ No newline at end of file diff --git a/pay-mall-api/pom.xml b/pay-mall-api/pom.xml index cb6db6b..68a2b1a 100644 --- a/pay-mall-api/pom.xml +++ b/pay-mall-api/pom.xml @@ -20,6 +20,10 @@ jakarta.validation-api 3.0.2 + + com.squareup.okhttp3 + okhttp + diff --git a/pay-mall-api/src/main/java/edu/whut/api/IPayService.java b/pay-mall-api/src/main/java/edu/whut/api/IPayService.java new file mode 100644 index 0000000..a7d285e --- /dev/null +++ b/pay-mall-api/src/main/java/edu/whut/api/IPayService.java @@ -0,0 +1,10 @@ +package edu.whut.api; + +import edu.whut.api.dto.CreatePayRequestDTO; +import edu.whut.api.response.Response; + +public interface IPayService { + + Response createPayOrder(CreatePayRequestDTO createPayRequestDTO); + +} diff --git a/pay-mall-api/src/main/java/edu/whut/api/dto/CreatePayRequestDTO.java b/pay-mall-api/src/main/java/edu/whut/api/dto/CreatePayRequestDTO.java new file mode 100644 index 0000000..3473ab6 --- /dev/null +++ b/pay-mall-api/src/main/java/edu/whut/api/dto/CreatePayRequestDTO.java @@ -0,0 +1,13 @@ +package edu.whut.api.dto; + +import lombok.Data; + +@Data +public class CreatePayRequestDTO { + + // 用户ID 【实际产生中会通过登录模块获取,不需要透彻】 + private String userId; + // 产品编号 + private String productId; + +} diff --git a/pay-mall-api/src/main/java/edu/whut/api/dto/package-info.java b/pay-mall-api/src/main/java/edu/whut/api/dto/package-info.java deleted file mode 100644 index e19888a..0000000 --- a/pay-mall-api/src/main/java/edu/whut/api/dto/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 数据传输对象 xxxRequestDTO xxxResponseDTO - */ -package edu.whut.api.dto; \ No newline at end of file diff --git a/pay-mall-app/src/main/java/edu/whut/config/AliPayConfig.java b/pay-mall-app/src/main/java/edu/whut/config/AliPayConfig.java new file mode 100644 index 0000000..294ba42 --- /dev/null +++ b/pay-mall-app/src/main/java/edu/whut/config/AliPayConfig.java @@ -0,0 +1,23 @@ +package edu.whut.config; + +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AliPayConfig { + + @Bean("alipayClient") + public AlipayClient alipayClient(AliPayConfigProperties properties) { + return new DefaultAlipayClient(properties.getGatewayUrl(), + properties.getApp_id(), + properties.getMerchant_private_key(), + properties.getFormat(), + properties.getCharset(), + properties.getAlipay_public_key(), + properties.getSign_type()); + } + +} diff --git a/pay-mall-app/src/main/java/edu/whut/config/AliPayConfigProperties.java b/pay-mall-app/src/main/java/edu/whut/config/AliPayConfigProperties.java new file mode 100644 index 0000000..cda1520 --- /dev/null +++ b/pay-mall-app/src/main/java/edu/whut/config/AliPayConfigProperties.java @@ -0,0 +1,31 @@ +package edu.whut.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "alipay", ignoreInvalidFields = true) +public class AliPayConfigProperties { + + // 「沙箱环境」应用ID - 您的APPID,收款账号既是你的APPID对应支付宝账号。获取地址;https://open.alipay.com/develop/sandbox/app + private String app_id; + // 「沙箱环境」商户私钥,你的PKCS8格式RSA2私钥 + private String merchant_private_key; + // 「沙箱环境」支付宝公钥 + private String alipay_public_key; + // 「沙箱环境」服务器异步通知页面路径 + private String notify_url; + // 「沙箱环境」页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 + private String return_url; + // 「沙箱环境」 + private String gatewayUrl; + // 签名方式 + private String sign_type = "RSA2"; + // 字符编码格式 + private String charset = "utf-8"; + // 传输格式 + private String format = "json"; + +} diff --git a/pay-mall-app/src/main/resources/application-dev.yml b/pay-mall-app/src/main/resources/application-dev.yml index 8ef3c6c..dae3ecb 100644 --- a/pay-mall-app/src/main/resources/application-dev.yml +++ b/pay-mall-app/src/main/resources/application-dev.yml @@ -38,11 +38,21 @@ mybatis: # 微信公众号对接 weixin: config: - originalid: gh_b748269e1f4c - token: asdf - app-id: wx7cc74be9b340b26e - app-secret: d4e73551512c6dc7a2e8f746c26b7f2c - template_id: ARDqdKXuGvASjsDqXzeunq0P8chMQ7tXk_4-BPULJ6U + original-id: ${paymall.wechat.original-id} + token: ${paymall.wechat.token} + app-id: ${paymall.wechat.app-id} + app-secret: ${paymall.wechat.app-secret} + template_id: ${paymall.wechat.template-id} + +# 支付宝支付 - 沙箱 https://opendocs.alipay.com/common/02kkv7 +alipay: + enabled: true + app_id: ${paymall.alipay.app-id} + merchant_private_key: ${paymall.alipay.merchant-private-key} + alipay_public_key: ${paymall.alipay.alipay-public-key} + notify_url: ${paymall.alipay.notify-url} + return_url: ${paymall.alipay.return-url} + gateway-url: ${paymall.alipay.gateway-url} # 日志 logging: diff --git a/pay-mall-app/src/main/resources/application.yml b/pay-mall-app/src/main/resources/application.yml index da00f44..7f350e7 100644 --- a/pay-mall-app/src/main/resources/application.yml +++ b/pay-mall-app/src/main/resources/application.yml @@ -1,5 +1,3 @@ spring: - config: - name: pay-mall-app profiles: - active: dev + active: dev,local diff --git a/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml b/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml index b0eaf7f..3f7f78c 100644 --- a/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml +++ b/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml @@ -32,4 +32,9 @@ limit 1 + + update pay_order set pay_url = #{payUrl}, status = #{status}, update_time = now() + where order_id = #{orderId} + + diff --git a/pay-mall-app/src/test/java/edu/whut/test/AliPayTest.java b/pay-mall-app/src/test/java/edu/whut/test/AliPayTest.java new file mode 100644 index 0000000..fab6c8b --- /dev/null +++ b/pay-mall-app/src/test/java/edu/whut/test/AliPayTest.java @@ -0,0 +1,111 @@ +package edu.whut.test; + +import com.alibaba.fastjson.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; +import com.alipay.api.domain.AlipayTradeQueryModel; +import com.alipay.api.domain.AlipayTradeRefundModel; +import com.alipay.api.request.AlipayTradePagePayRequest; +import com.alipay.api.request.AlipayTradeQueryRequest; +import com.alipay.api.request.AlipayTradeRefundRequest; +import com.alipay.api.response.AlipayTradeRefundResponse; +import edu.whut.config.AliPayConfigProperties; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.math.BigDecimal; + +@Slf4j +@SpringBootTest +@RunWith(SpringRunner.class) +public class AliPayTest { + @Autowired + private AliPayConfigProperties config; // 字段注入 + + private AlipayClient alipayClient; + + + @Before + public void init() { + log.info(config.getNotify_url()); + // ④ 用属性里的值来构造 AlipayClient + this.alipayClient = new DefaultAlipayClient( + config.getGatewayUrl(), + config.getApp_id(), + config.getMerchant_private_key(), + config.getFormat(), + config.getCharset(), + config.getAlipay_public_key(), + config.getSign_type() + ); + } + + @Test + public void test_aliPay_pageExecute() throws AlipayApiException { + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); // 发送请求的 Request类 + request.setNotifyUrl(config.getNotify_url()); + request.setReturnUrl(config.getReturn_url()); + + JSONObject bizContent = new JSONObject(); + bizContent.put("out_trade_no", "smile000091004001"); // 我们自己生成的订单编号 + bizContent.put("total_amount", "0.01"); // 订单的总金额 + bizContent.put("subject", "测试商品"); // 支付的名称 + bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 固定配置 + request.setBizContent(bizContent.toString()); + + String form = alipayClient.pageExecute(request).getBody(); + log.info("测试结果:{}", form); + + /** + * 会生成一个form表单; +
+ + +
+ + */ + } + + /** + * 查询订单 + */ + @Test + public void test_alipay_certificateExecute() throws AlipayApiException { + + AlipayTradeQueryModel bizModel = new AlipayTradeQueryModel(); + bizModel.setOutTradeNo("daniel82AAAA000032333361Y001"); + + AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); + request.setBizModel(bizModel); + + String body = alipayClient.execute(request).getBody(); + log.info("测试结果:{}", body); + } + + /** + * 退款接口 + */ + @Test + public void test_alipay_refund() throws AlipayApiException { + AlipayTradeRefundRequest request =new AlipayTradeRefundRequest(); + AlipayTradeRefundModel refundModel =new AlipayTradeRefundModel(); + refundModel.setOutTradeNo("daniel82AAAA000032333361X03"); + refundModel.setRefundAmount("1.00"); + refundModel.setRefundReason("退款说明"); + request.setBizModel(refundModel); + + AlipayTradeRefundResponse execute = alipayClient.execute(request); + log.info("测试结果:{}", execute.isSuccess()); + } + + public static void main(String[] args) { + System.out.println(new BigDecimal("9.99").doubleValue()); + } + +} \ No newline at end of file diff --git a/pay-mall-domain/pom.xml b/pay-mall-domain/pom.xml index e321001..bf1f15d 100644 --- a/pay-mall-domain/pom.xml +++ b/pay-mall-domain/pom.xml @@ -38,6 +38,11 @@ commons-codec commons-codec + + + com.alipay.sdk + alipay-sdk-java + edu.whut diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/repository/IOrderRepository.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/repository/IOrderRepository.java index 86a2512..70e2f5d 100644 --- a/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/repository/IOrderRepository.java +++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/repository/IOrderRepository.java @@ -2,6 +2,7 @@ package edu.whut.domain.order.adapter.repository; import edu.whut.domain.order.model.aggregate.CreateOrderAggregate; import edu.whut.domain.order.model.entity.OrderEntity; +import edu.whut.domain.order.model.entity.PayOrderEntity; import edu.whut.domain.order.model.entity.ShopCartEntity; public interface IOrderRepository { @@ -9,4 +10,5 @@ public interface IOrderRepository { OrderEntity queryUnPayOrder(ShopCartEntity shopCartEntity); + void updateOrderPayInfo(PayOrderEntity payOrderEntity); } diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/AbstractOrderService.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/AbstractOrderService.java index f6cdbfb..78ae7ec 100644 --- a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/AbstractOrderService.java +++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/AbstractOrderService.java @@ -1,5 +1,6 @@ package edu.whut.domain.order.service; +import com.alipay.api.AlipayApiException; import edu.whut.domain.order.adapter.port.IProductPort; import edu.whut.domain.order.adapter.repository.IOrderRepository; import edu.whut.domain.order.model.aggregate.CreateOrderAggregate; @@ -10,6 +11,8 @@ import edu.whut.domain.order.model.entity.ShopCartEntity; import edu.whut.domain.order.model.valobj.OrderStatusVO; import lombok.extern.slf4j.Slf4j; +import java.math.BigDecimal; + @Slf4j public abstract class AbstractOrderService implements IOrderService { @@ -36,9 +39,14 @@ public abstract class AbstractOrderService implements IOrderService { .orderId(unpaidOrderEntity.getOrderId()) .payUrl(unpaidOrderEntity.getPayUrl()) .build(); - // TODO: 如果存在“已创建未支付”状态 - } else if (null != unpaidOrderEntity && OrderStatusVO.CREATE.equals(unpaidOrderEntity.getOrderStatusVO())) { + } else if (null != unpaidOrderEntity && OrderStatusVO.CREATE.equals(unpaidOrderEntity.getOrderStatusVO())) { + log.info("创建订单-存在,存在未创建'支付单'订单,创建支付单开始 userId:{} productId:{} orderId:{}", shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId()); + PayOrderEntity payOrderEntity = doPrepayOrder(shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(), unpaidOrderEntity.getTotalAmount()); + return PayOrderEntity.builder() + .orderId(payOrderEntity.getOrderId()) + .payUrl(payOrderEntity.getPayUrl()) + .build(); } // 2. 调用产品服务,查询商品详细信息 @@ -57,10 +65,14 @@ public abstract class AbstractOrderService implements IOrderService { // 5. 交由子类实现,保存订单聚合 this.doSaveOrder(orderAggregate); + PayOrderEntity payOrderEntity = doPrepayOrder(shopCartEntity.getUserId(), productEntity.getProductId(), productEntity.getProductName(), orderEntity.getOrderId(), productEntity.getPrice()); + log.info("创建订单-完成,生成支付单。userId: {} orderId: {} payUrl: {}", shopCartEntity.getUserId(), orderEntity.getOrderId(), payOrderEntity.getPayUrl()); + + // 6. 返回支付实体 return PayOrderEntity.builder() .orderId(orderEntity.getOrderId()) - .payUrl("暂无") + .payUrl(payOrderEntity.getPayUrl()) .build(); } @@ -69,4 +81,5 @@ public abstract class AbstractOrderService implements IOrderService { */ protected abstract void doSaveOrder(CreateOrderAggregate orderAggregate); + protected abstract PayOrderEntity doPrepayOrder(String userId, String productId, String productName, String orderId, BigDecimal totalAmount) throws AlipayApiException; } diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java index 2d0ee41..c376a77 100644 --- a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java +++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java @@ -1,14 +1,32 @@ package edu.whut.domain.order.service; +import com.alibaba.fastjson.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayClient; +import com.alipay.api.request.AlipayTradePagePayRequest; import edu.whut.domain.order.adapter.port.IProductPort; import edu.whut.domain.order.adapter.repository.IOrderRepository; import edu.whut.domain.order.model.aggregate.CreateOrderAggregate; +import edu.whut.domain.order.model.entity.PayOrderEntity; +import edu.whut.domain.order.model.valobj.OrderStatusVO; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import java.math.BigDecimal; + @Slf4j @Service public class OrderService extends AbstractOrderService{ + @Value("${alipay.notify_url}") + private String notifyUrl; + @Value("${alipay.return_url}") + private String returnUrl; + + @Resource + private AlipayClient alipayClient; + public OrderService(IOrderRepository repository, IProductPort port) { super(repository, port); } @@ -18,4 +36,33 @@ public class OrderService extends AbstractOrderService{ repository.doSaveOrder(orderAggregate); } + /** + * 预支付订单 + */ + @Override + protected PayOrderEntity doPrepayOrder(String userId, String productId, String productName, String orderId, BigDecimal totalAmount) throws AlipayApiException { + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + request.setNotifyUrl(notifyUrl); + request.setReturnUrl(returnUrl); + + JSONObject bizContent = new JSONObject(); + bizContent.put("out_trade_no", orderId); + bizContent.put("total_amount", totalAmount.toString()); + bizContent.put("subject", productName); + bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); + request.setBizContent(bizContent.toString()); + + String form = alipayClient.pageExecute(request).getBody(); + + PayOrderEntity payOrderEntity = new PayOrderEntity(); + payOrderEntity.setOrderId(orderId); + payOrderEntity.setPayUrl(form); + //等待支付 + payOrderEntity.setOrderStatus(OrderStatusVO.PAY_WAIT); + + repository.updateOrderPayInfo(payOrderEntity); + + return payOrderEntity; + } + } diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java index 2cdf6d7..24b5b30 100644 --- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java +++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java @@ -3,6 +3,7 @@ package edu.whut.infrastructure.adapter.repository; import edu.whut.domain.order.adapter.repository.IOrderRepository; import edu.whut.domain.order.model.aggregate.CreateOrderAggregate; import edu.whut.domain.order.model.entity.OrderEntity; +import edu.whut.domain.order.model.entity.PayOrderEntity; import edu.whut.domain.order.model.entity.ProductEntity; import edu.whut.domain.order.model.entity.ShopCartEntity; import edu.whut.domain.order.model.valobj.OrderStatusVO; @@ -11,8 +12,6 @@ import edu.whut.infrastructure.dao.po.PayOrder; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; -import javax.annotation.Resource; - @Repository @RequiredArgsConstructor public class OrderRepository implements IOrderRepository { @@ -59,4 +58,15 @@ public class OrderRepository implements IOrderRepository { .payUrl(order.getPayUrl()) .build(); } + + @Override + public void updateOrderPayInfo(PayOrderEntity payOrderEntity) { + PayOrder payOrderReq = PayOrder.builder() + .userId(payOrderEntity.getUserId()) + .orderId(payOrderEntity.getOrderId()) + .status(payOrderEntity.getOrderStatus().getCode()) + .payUrl(payOrderEntity.getPayUrl()) + .build(); + orderDao.updateOrderPayInfo(payOrderReq); + } } diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/IOrderDao.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/IOrderDao.java index ea24e9b..17ff527 100644 --- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/IOrderDao.java +++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/IOrderDao.java @@ -9,4 +9,6 @@ public interface IOrderDao { PayOrder queryUnPayOrder(PayOrder payOrder); + void updateOrderPayInfo(PayOrder payOrder); + } diff --git a/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java b/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java new file mode 100644 index 0000000..3aba484 --- /dev/null +++ b/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java @@ -0,0 +1,107 @@ +package edu.whut.trigger.http; +import com.alipay.api.AlipayApiException; +import com.alipay.api.internal.util.AlipaySignature; +import edu.whut.api.IPayService; +import edu.whut.api.dto.CreatePayRequestDTO; +import edu.whut.api.response.Response; +import edu.whut.domain.order.model.entity.PayOrderEntity; +import edu.whut.domain.order.model.entity.ShopCartEntity; +import edu.whut.domain.order.service.IOrderService; +import edu.whut.types.common.Constants; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController() +@CrossOrigin("*") +@RequiredArgsConstructor +@RequestMapping("/api/v1/alipay") +public class AliPayController implements IPayService { + + @Value("${alipay.alipay_public_key}") + private String alipayPublicKey; + + private final IOrderService orderService; + + /** + * { + * "userId": "10001", + * "productId": "10001" + * } + */ + @PostMapping("/create_pay_order") + @Override + public Response createPayOrder(@RequestBody CreatePayRequestDTO createPayRequestDTO) { + try { + log.info("商品下单,根据商品ID创建支付单开始 userId:{} productId:{}", createPayRequestDTO.getUserId(), createPayRequestDTO.getUserId()); + String userId = createPayRequestDTO.getUserId(); + String productId = createPayRequestDTO.getProductId(); + // 下单 + PayOrderEntity payOrderEntity = orderService.createOrder(ShopCartEntity.builder() + .userId(userId) + .productId(productId) + .build()); + + log.info("商品下单,根据商品ID创建支付单完成 userId:{} productId:{} orderId:{}", userId, productId, payOrderEntity.getOrderId()); + return Response.builder() + .code(Constants.ResponseCode.SUCCESS.getCode()) + .info(Constants.ResponseCode.SUCCESS.getInfo()) + .data(payOrderEntity.getPayUrl()) + .build(); + } catch (Exception e) { + log.error("商品下单,根据商品ID创建支付单失败 userId:{} productId:{}", createPayRequestDTO.getUserId(), createPayRequestDTO.getUserId(), e); + return Response.builder() + .code(Constants.ResponseCode.UN_ERROR.getCode()) + .info(Constants.ResponseCode.UN_ERROR.getInfo()) + .build(); + } + } + + @PostMapping("/alipay_notify_url") + public String payNotify(HttpServletRequest request) throws AlipayApiException { + log.info("支付回调,消息接收 {}", request.getParameter("trade_status")); + + if (!request.getParameter("trade_status").equals("TRADE_SUCCESS")) { + return "false"; + } + + Map params = new HashMap<>(); + Map requestParams = request.getParameterMap(); + for (String name : requestParams.keySet()) { + params.put(name, request.getParameter(name)); + } + + String tradeNo = params.get("out_trade_no"); + String gmtPayment = params.get("gmt_payment"); + String alipayTradeNo = params.get("trade_no"); + + String sign = params.get("sign"); + String content = AlipaySignature.getSignCheckContentV1(params); + boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, alipayPublicKey, "UTF-8"); // 验证签名 + // 支付宝验签 + if (!checkSignature) { + return "false"; + } + + // 验签通过 + log.info("支付回调,交易名称: {}", params.get("subject")); + log.info("支付回调,交易状态: {}", params.get("trade_status")); + log.info("支付回调,支付宝交易凭证号: {}", params.get("trade_no")); + log.info("支付回调,商户订单号: {}", params.get("out_trade_no")); + log.info("支付回调,交易金额: {}", params.get("total_amount")); + log.info("支付回调,买家在支付宝唯一id: {}", params.get("buyer_id")); + log.info("支付回调,买家付款时间: {}", params.get("gmt_payment")); + log.info("支付回调,买家付款金额: {}", params.get("buyer_pay_amount")); + log.info("支付回调,支付回调,更新订单 {}", tradeNo); + + return "success"; + } + +} diff --git a/pay-mall-trigger/src/main/java/edu/whut/trigger/http/WeixinPortalController.java b/pay-mall-trigger/src/main/java/edu/whut/trigger/http/WeixinPortalController.java index be9498e..4c4b06a 100644 --- a/pay-mall-trigger/src/main/java/edu/whut/trigger/http/WeixinPortalController.java +++ b/pay-mall-trigger/src/main/java/edu/whut/trigger/http/WeixinPortalController.java @@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; /** + * 微信会回调该接口 * https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index 平台地址 * https://pay.bitday.top/api/v1/weixin/portal/receive 内网穿透 */ @@ -73,6 +74,7 @@ public class WeixinPortalController { MessageTextEntity message = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class); if ("event".equals(message.getMsgType()) && "SCAN".equals(message.getEvent())) { + //存储用户登录状态 loginService.saveLoginState(message.getTicket(), openid); return buildMessageTextEntity(openid, "登录成功"); } diff --git a/pay-mall-types/src/main/java/edu/whut/types/common/Constants.java b/pay-mall-types/src/main/java/edu/whut/types/common/Constants.java index a0c2b99..b36cfdd 100644 --- a/pay-mall-types/src/main/java/edu/whut/types/common/Constants.java +++ b/pay-mall-types/src/main/java/edu/whut/types/common/Constants.java @@ -21,4 +21,5 @@ public class Constants { private String info; } + } diff --git a/pom.xml b/pom.xml index ad612de..732d994 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,12 @@ adapter-rxjava2 2.9.0 - + + + com.alipay.sdk + alipay-sdk-java + 4.38.157.ALL + edu.whut