7.13 支付宝支付沙箱申请、对接alipay

This commit is contained in:
zhangsan 2025-07-13 14:28:15 +08:00
parent cf575464ea
commit 267e250820
23 changed files with 462 additions and 126 deletions

1
.gitignore vendored
View File

@ -38,3 +38,4 @@ build/
.DS_Store
/data/
/.idea/
/pay-mall-app/src/main/resources/application-local.yml

View File

@ -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', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=Af8UHbR23xcDThup0mv3NVZLlkMAcQ3tjmqGNBpwYqJ1Xn1ytqt42NWBsbDxKHT3rEo3h3dmd60wJ9N0LTmjElq%2B9HGFukp7KtxOiZRlbxip9x9yl4mCjQTUunf6l7iMsUAAtBUh%2FiVJOsI7Uy4Jy8Fg7fqjRAjc06fKcsynU5wb3xtOhr9f8uQIpe7ykxLg3Uc6YyR9GYdTcceVRu15BoSur6xz4S%2FMhRTOqc8XbWrqBGhvWMk%2BV6FHbx04AHYg7HHc0%2FTndoIHFSX9c1h7Jysxbyxp3q40GMfrnpX53VkqcPpQBbLA8f%2BWO8MnzbnxobeKcXAR11gxSbl4FcEWng%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top&notify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2&timestamp=2025-07-13+14%3A21%3A33&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{&quot;out_trade_no&quot;:&quot;51403944017404&quot;,&quot;total_amount&quot;:&quot;1.68&quot;,&quot;subject&quot;:&quot;测试商品&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', 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', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=lQ1WTro6vDXTQet%2Bd%2BJ7TWIIWXF%2B5SWg0r53joEBUesQL12UtgHrWa%2BafAwjkJWxH9zPqwAvFuXr1ZFfmMnqz1PTsRXo4ACTAJC0Se2uFSYAU1HK8Cyk4jHh9OwEjW1MQXdWT1l52KjLuZOBeJAlUvVPyAfxQnBx0HF2XKHEfFLFBgeRufAp%2BEzCM%2BoX8l0G%2FkQdskCRhICLMh2tFj2T4i4sgsSwuJQDV%2Fs0EOwpDpDKV3Z9xPEzkI2zZM6ZB8jCfTa734H8%2BC9IWZmKX2njnNj89g4h9RVuLd92l0veNn%2FJmrxTz296eCWJl6yU5PLSB0pX0IiSnUoHiySFnrswaA%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top&notify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2&timestamp=2025-07-13+14%3A19%3A11&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{&quot;out_trade_no&quot;:&quot;66665553128265&quot;,&quot;total_amount&quot;:&quot;1.68&quot;,&quot;subject&quot;:&quot;测试商品&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', NULL, '2025-07-13 14:19:11', '2025-07-13 14:21:21');
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -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;

View File

@ -20,6 +20,10 @@
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,10 @@
package edu.whut.api;
import edu.whut.api.dto.CreatePayRequestDTO;
import edu.whut.api.response.Response;
public interface IPayService {
Response<String> createPayOrder(CreatePayRequestDTO createPayRequestDTO);
}

View File

@ -0,0 +1,13 @@
package edu.whut.api.dto;
import lombok.Data;
@Data
public class CreatePayRequestDTO {
// 用户ID 实际产生中会通过登录模块获取不需要透彻
private String userId;
// 产品编号
private String productId;
}

View File

@ -1,4 +0,0 @@
/**
* 数据传输对象 xxxRequestDTO xxxResponseDTO
*/
package edu.whut.api.dto;

View File

@ -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());
}
}

View File

@ -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";
}

View File

@ -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:

View File

@ -1,5 +1,3 @@
spring:
config:
name: pay-mall-app
profiles:
active: dev
active: dev,local

View File

@ -32,4 +32,9 @@
limit 1
</select>
<update id="updateOrderPayInfo" parameterType="edu.whut.infrastructure.dao.po.PayOrder">
update pay_order set pay_url = #{payUrl}, status = #{status}, update_time = now()
where order_id = #{orderId}
</update>
</mapper>

View File

@ -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表单
<form name="punchout_form" method="post" action="https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=vl9G%2FJHoVvn5cW87mYYK2PhiISCw4pFdB9kYLEqx645S6Q%2BzN9OBw1QbfkmvNB5EOApbNOsz2UyvKeWQtUVswyHJhadIIEvbbP925VIvWveqYvPY6rnoFfA3OXXZR%2FxFrDLxij%2BhCtz7N6T9Iyxraf4UGuiTmez8LJZu60Exr2ej%2FSeZIwRXOflB6u7xFXfcJqVHE0sz1%2B2vkDaMjYBJ77dZoLLUs2smavzN%2BZIRs7%2FT8w39w7ymab23uMSM%2FXLMi%2B%2BrL9rSzvdWgk9NwxLIKslc2IWg2%2Bk5%2BVOc1KyY2K%2F35XzRzaeYGWuMCCLYCwDVE2Lvp8ul99A5%2FebYQaaV9g%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top&notify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2&timestamp=2025-07-12+16%3A29%3A37&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json">
<input type="hidden" name="biz_content" value="{&quot;out_trade_no&quot;:&quot;smile000091004001&quot;,&quot;total_amount&quot;:&quot;0.01&quot;,&quot;subject&quot;:&quot;测试商品&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}">
<input type="submit" value="立即支付" style="display:none" >
</form>
<script>document.forms[0].submit();</script>
*/
}
/**
* 查询订单
*/
@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());
}
}

View File

@ -38,6 +38,11 @@
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- 支付宝支付 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
</dependency>
<!-- 系统模块 -->
<dependency>
<groupId>edu.whut</groupId>

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -9,4 +9,6 @@ public interface IOrderDao {
PayOrder queryUnPayOrder(PayOrder payOrder);
void updateOrderPayInfo(PayOrder payOrder);
}

View File

@ -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<String> 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.<String>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.<String>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<String, String> params = new HashMap<>();
Map<String, String[]> 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";
}
}

View File

@ -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, "登录成功");
}

View File

@ -21,4 +21,5 @@ public class Constants {
private String info;
}
}

View File

@ -130,7 +130,12 @@
<artifactId>adapter-rxjava2</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 支付宝沙箱支付对接文档https://opendocs.alipay.com/common/02kkv7 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.38.157.ALL</version>
</dependency>
<!-- 工程模块 -->
<dependency>
<groupId>edu.whut</groupId>