1. 支付宝支付概述

支付宝支付是阿里巴巴集团推出的第三方支付平台,支持多种支付场景。本文将详细介绍支付宝支付的集成实现,包括手机网站支付、APP支付、当面付、退款处理、对账下载的完整解决方案。

1.1 支付场景

  1. 手机网站支付: 手机浏览器支付
  2. APP支付: 移动应用内支付
  3. 当面付: 扫码支付
  4. 电脑网站支付: PC端支付
  5. 预授权支付: 预授权冻结资金

1.2 技术架构

1
2
3
4
5
6
7
商户系统 → 统一下单API → 支付宝系统
↓ ↓ ↓
调起支付 → 用户支付 → 支付结果
↓ ↓ ↓
支付回调 → 订单处理 → 业务完成

退款/对账

2. Maven依赖配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<!-- pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.alipay</groupId>
<artifactId>alipay-demo</artifactId>
<version>1.0.0</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>

<properties>
<java.version>11</java.version>
</properties>

<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 支付宝SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.38.10.ALL</version>
</dependency>

<!-- JSON处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.25</version>
</dependency>

<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- XML处理 -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.20</version>
</dependency>

<!-- Commons工具 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>

<!-- HTTP客户端 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
</project>

3. 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# application.yml
server:
port: 8080

spring:
application:
name: alipay-demo

# 数据库配置
datasource:
url: jdbc:mysql://localhost:3306/alipay?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver

jpa:
hibernate:
ddl-auto: update
show-sql: true

# Redis配置
redis:
host: localhost
port: 6379
database: 0

# 支付宝配置
alipay:
# 应用ID
app-id: 2021001234567890

# 商户私钥
private-key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...

# 支付宝公钥
alipay-public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...

# 网关地址
gateway-url: https://openapi.alipay.com/gateway.do

# 沙箱环境网关地址
sandbox-gateway-url: https://openapi.alipaydev.com/gateway.do

# 字符编码
charset: UTF-8

# 签名算法
sign-type: RSA2

# 数据格式
format: json

# 版本
version: 1.0

# 回调通知URL
notify-url: https://your-domain.com/api/alipay/notify

# 同步返回URL
return-url: https://your-domain.com/api/alipay/return

# 退款通知URL
refund-notify-url: https://your-domain.com/api/alipay/refund/notify

# 是否使用沙箱环境
sandbox: true

4. 支付宝配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package com.alipay.config;

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* 支付宝配置类
* @author Java实战
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {

/**
* 应用ID
*/
private String appId;

/**
* 商户私钥
*/
private String privateKey;

/**
* 支付宝公钥
*/
private String alipayPublicKey;

/**
* 网关地址
*/
private String gatewayUrl;

/**
* 沙箱环境网关地址
*/
private String sandboxGatewayUrl;

/**
* 字符编码
*/
private String charset = "UTF-8";

/**
* 签名算法
*/
private String signType = "RSA2";

/**
* 数据格式
*/
private String format = "json";

/**
* 版本
*/
private String version = "1.0";

/**
* 回调通知URL
*/
private String notifyUrl;

/**
* 同步返回URL
*/
private String returnUrl;

/**
* 退款通知URL
*/
private String refundNotifyUrl;

/**
* 是否使用沙箱环境
*/
private boolean sandbox = true;

/**
* 创建支付宝客户端
*/
@Bean
public AlipayClient alipayClient() {
String gateway = sandbox ? sandboxGatewayUrl : gatewayUrl;

return new DefaultAlipayClient(
gateway, // 网关地址
appId, // 应用ID
privateKey, // 商户私钥
format, // 数据格式
charset, // 字符编码
alipayPublicKey, // 支付宝公钥
signType // 签名算法
);
}
}

5. 支付订单实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package com.alipay.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
* 支付订单实体
* @author Java实战
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "alipay_order", indexes = {
@Index(name = "idx_order_no", columnList = "order_no"),
@Index(name = "idx_trade_no", columnList = "trade_no"),
@Index(name = "idx_user_id", columnList = "user_id")
})
public class AlipayOrder {

/**
* 主键ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

/**
* 商户订单号(唯一)
*/
@Column(nullable = false, unique = true, length = 64)
private String orderNo;

/**
* 支付宝交易号
*/
@Column(length = 64)
private String tradeNo;

/**
* 用户ID
*/
@Column(nullable = false, length = 64)
private String userId;

/**
* 商品标题
*/
@Column(nullable = false, length = 200)
private String subject;

/**
* 商品描述
*/
@Column(length = 500)
private String body;

/**
* 订单金额(元)
*/
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount;

/**
* 支付金额(元)
*/
@Column(precision = 10, scale = 2)
private BigDecimal payAmount;

/**
* 支付类型:WAP-手机网站, APP-APP支付, QR_CODE-当面付, PC-电脑网站
*/
@Column(nullable = false, length = 20)
private String payType;

/**
* 订单状态:WAIT_PAY-待支付, PAYING-支付中, PAY_SUCCESS-支付成功, PAY_FAIL-支付失败, CLOSED-已关闭
*/
@Column(nullable = false, length = 20)
private String status;

/**
* 支付时间
*/
private LocalDateTime payTime;

/**
* 创建时间
*/
@Column(nullable = false)
private LocalDateTime createTime;

/**
* 更新时间
*/
private LocalDateTime updateTime;

/**
* 过期时间
*/
private LocalDateTime expireTime;

/**
* 附加数据
*/
@Column(length = 500)
private String attach;

/**
* 备注
*/
@Column(length = 500)
private String remark;
}

6. 手机网站支付实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package com.alipay.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.alipay.config.AlipayConfig;
import com.alipay.entity.AlipayOrder;
import com.alipay.repository.AlipayOrderRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.UUID;

/**
* 手机网站支付服务
* @author Java实战
*/
@Slf4j
@Service
public class WapPayService {

@Autowired
private AlipayConfig alipayConfig;

@Autowired
private AlipayClient alipayClient;

@Autowired
private AlipayOrderRepository alipayOrderRepository;

/**
* 创建手机网站支付订单
*/
@Transactional
public String createWapOrder(String userId, String subject, String body,
BigDecimal totalAmount) {
try {
// 生成商户订单号
String orderNo = generateOrderNo();

log.info("创建手机网站支付订单: userId={}, orderNo={}, subject={}, amount={}",
userId, orderNo, subject, totalAmount);

// 保存订单到数据库
AlipayOrder order = AlipayOrder.builder()
.orderNo(orderNo)
.userId(userId)
.subject(subject)
.body(body)
.totalAmount(totalAmount)
.payType("WAP")
.status("WAIT_PAY")
.createTime(LocalDateTime.now())
.expireTime(LocalDateTime.now().plusMinutes(30))
.build();

alipayOrderRepository.save(order);

// 创建支付请求
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

// 设置回调地址
request.setNotifyUrl(alipayConfig.getNotifyUrl());
request.setReturnUrl(alipayConfig.getReturnUrl());

// 构建业务参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderNo);
bizContent.put("total_amount", totalAmount.toString());
bizContent.put("subject", subject);
bizContent.put("body", body);
bizContent.put("product_code", "QUICK_WAP_WAY");
bizContent.put("timeout_express", "30m");

request.setBizContent(bizContent.toJSONString());

// 调用支付接口
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);

if (response.isSuccess()) {
log.info("手机网站支付下单成功: orderNo={}", orderNo);
return response.getBody();
} else {
log.error("手机网站支付下单失败: orderNo={}, msg={}",
orderNo, response.getMsg());
throw new RuntimeException("支付下单失败: " + response.getMsg());
}

} catch (Exception e) {
log.error("创建手机网站支付订单失败: userId={}", userId, e);
throw new RuntimeException("创建支付订单失败", e);
}
}

/**
* 查询订单状态
*/
public JSONObject queryOrder(String orderNo) {
try {
log.info("查询订单状态: orderNo={}", orderNo);

// 从数据库查询订单
AlipayOrder order = alipayOrderRepository.findByOrderNo(orderNo);
if (order == null) {
throw new RuntimeException("订单不存在");
}

// 构建查询结果
JSONObject result = new JSONObject();
result.put("orderNo", order.getOrderNo());
result.put("tradeNo", order.getTradeNo());
result.put("status", order.getStatus());
result.put("totalAmount", order.getTotalAmount());
result.put("payAmount", order.getPayAmount());
result.put("payTime", order.getPayTime());

return result;

} catch (Exception e) {
log.error("查询订单失败: orderNo={}", orderNo, e);
throw new RuntimeException("查询订单失败", e);
}
}

/**
* 关闭订单
*/
@Transactional
public void closeOrder(String orderNo) {
try {
log.info("关闭订单: orderNo={}", orderNo);

// 更新数据库订单状态
AlipayOrder order = alipayOrderRepository.findByOrderNo(orderNo);
if (order != null) {
order.setStatus("CLOSED");
order.setUpdateTime(LocalDateTime.now());
alipayOrderRepository.save(order);
}

log.info("关闭订单成功: orderNo={}", orderNo);

} catch (Exception e) {
log.error("关闭订单失败: orderNo={}", orderNo, e);
throw new RuntimeException("关闭订单失败", e);
}
}

/**
* 生成商户订单号
*/
private String generateOrderNo() {
return "ALIPAY" + System.currentTimeMillis() +
String.format("%06d", (int)(Math.random() * 1000000));
}
}

7. APP支付实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.alipay.service;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.config.AlipayConfig;
import com.alipay.entity.AlipayOrder;
import com.alipay.repository.AlipayOrderRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
* APP支付服务
* @author Java实战
*/
@Slf4j
@Service
public class AppPayService {

@Autowired
private AlipayConfig alipayConfig;

@Autowired
private AlipayClient alipayClient;

@Autowired
private AlipayOrderRepository alipayOrderRepository;

/**
* 创建APP支付订单
*/
@Transactional
public String createAppOrder(String userId, String subject, String body,
BigDecimal totalAmount) {
try {
// 生成商户订单号
String orderNo = "ALIPAY" + System.currentTimeMillis() +
String.format("%06d", (int)(Math.random() * 1000000));

log.info("创建APP支付订单: userId={}, orderNo={}, subject={}, amount={}",
userId, orderNo, subject, totalAmount);

// 保存订单到数据库
AlipayOrder order = AlipayOrder.builder()
.orderNo(orderNo)
.userId(userId)
.subject(subject)
.body(body)
.totalAmount(totalAmount)
.payType("APP")
.status("WAIT_PAY")
.createTime(LocalDateTime.now())
.expireTime(LocalDateTime.now().plusMinutes(30))
.build();

alipayOrderRepository.save(order);

// 创建支付请求
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

// 设置回调地址
request.setNotifyUrl(alipayConfig.getNotifyUrl());

// 构建业务参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderNo);
bizContent.put("total_amount", totalAmount.toString());
bizContent.put("subject", subject);
bizContent.put("body", body);
bizContent.put("product_code", "QUICK_MSECURITY_PAY");
bizContent.put("timeout_express", "30m");

request.setBizContent(bizContent.toJSONString());

// 调用支付接口
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

if (response.isSuccess()) {
log.info("APP支付下单成功: orderNo={}", orderNo);
return response.getBody();
} else {
log.error("APP支付下单失败: orderNo={}, msg={}",
orderNo, response.getMsg());
throw new RuntimeException("支付下单失败: " + response.getMsg());
}

} catch (Exception e) {
log.error("创建APP支付订单失败: userId={}", userId, e);
throw new RuntimeException("创建支付订单失败", e);
}
}
}

8. 当面付实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.alipay.service;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.config.AlipayConfig;
import com.alipay.entity.AlipayOrder;
import com.alipay.repository.AlipayOrderRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
* 当面付服务(扫码支付)
* @author Java实战
*/
@Slf4j
@Service
public class FaceToFacePayService {

@Autowired
private AlipayConfig alipayConfig;

@Autowired
private AlipayClient alipayClient;

@Autowired
private AlipayOrderRepository alipayOrderRepository;

/**
* 创建当面付订单
*/
@Transactional
public String createFaceToFaceOrder(String userId, String subject, String body,
BigDecimal totalAmount) {
try {
// 生成商户订单号
String orderNo = "ALIPAY" + System.currentTimeMillis() +
String.format("%06d", (int)(Math.random() * 1000000));

log.info("创建当面付订单: userId={}, orderNo={}, subject={}, amount={}",
userId, orderNo, subject, totalAmount);

// 保存订单到数据库
AlipayOrder order = AlipayOrder.builder()
.orderNo(orderNo)
.userId(userId)
.subject(subject)
.body(body)
.totalAmount(totalAmount)
.payType("QR_CODE")
.status("WAIT_PAY")
.createTime(LocalDateTime.now())
.expireTime(LocalDateTime.now().plusMinutes(30))
.build();

alipayOrderRepository.save(order);

// 创建支付请求
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

// 设置回调地址
request.setNotifyUrl(alipayConfig.getNotifyUrl());

// 构建业务参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderNo);
bizContent.put("total_amount", totalAmount.toString());
bizContent.put("subject", subject);
bizContent.put("body", body);
bizContent.put("timeout_express", "30m");

request.setBizContent(bizContent.toJSONString());

// 调用支付接口
AlipayTradePrecreateResponse response = alipayClient.execute(request);

if (response.isSuccess()) {
log.info("当面付下单成功: orderNo={}, qrCode={}",
orderNo, response.getQrCode());
return response.getQrCode();
} else {
log.error("当面付下单失败: orderNo={}, msg={}",
orderNo, response.getMsg());
throw new RuntimeException("支付下单失败: " + response.getMsg());
}

} catch (Exception e) {
log.error("创建当面付订单失败: userId={}", userId, e);
throw new RuntimeException("创建支付订单失败", e);
}
}
}

9. 支付回调处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package com.alipay.service;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.config.AlipayConfig;
import com.alipay.entity.AlipayOrder;
import com.alipay.repository.AlipayOrderRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* 支付回调处理服务
* @author Java实战
*/
@Slf4j
@Service
public class AlipayNotifyService {

@Autowired
private AlipayConfig alipayConfig;

@Autowired
private AlipayOrderRepository alipayOrderRepository;

@Autowired
private StringRedisTemplate redisTemplate;

private static final String NOTIFY_LOCK_PREFIX = "alipay:notify:lock:";

/**
* 处理支付回调通知
*/
@Transactional
public String handlePayNotify(Map<String, String> params) {
try {
log.info("收到支付宝支付回调通知: {}", params);

// 验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(
params,
alipayConfig.getAlipayPublicKey(),
alipayConfig.getCharset(),
alipayConfig.getSignType()
);

if (!signVerified) {
log.error("支付宝回调签名验证失败");
return "failure";
}

// 获取回调参数
String orderNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
String gmtPayment = params.get("gmt_payment");

log.info("支付回调解析成功: orderNo={}, tradeNo={}, tradeStatus={}",
orderNo, tradeNo, tradeStatus);

// 防止重复通知(使用Redis分布式锁)
String lockKey = NOTIFY_LOCK_PREFIX + orderNo;
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 5, TimeUnit.MINUTES);

if (Boolean.FALSE.equals(locked)) {
log.warn("重复的支付回调通知: orderNo={}", orderNo);
return "success";
}

try {
// 查询订单
AlipayOrder order = alipayOrderRepository.findByOrderNo(orderNo);
if (order == null) {
log.error("订单不存在: orderNo={}", orderNo);
return "failure";
}

// 检查订单状态
if ("PAY_SUCCESS".equals(order.getStatus())) {
log.warn("订单已支付: orderNo={}", orderNo);
return "success";
}

// 处理支付成功
if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
processPaySuccess(order, tradeNo, totalAmount, gmtPayment);
} else {
log.warn("未知的交易状态: orderNo={}, tradeStatus={}", orderNo, tradeStatus);
}

return "success";

} finally {
// 释放锁
redisTemplate.delete(lockKey);
}

} catch (AlipayApiException e) {
log.error("处理支付宝回调失败", e);
return "failure";
} catch (Exception e) {
log.error("处理支付回调失败", e);
return "failure";
}
}

/**
* 处理支付成功
*/
private void processPaySuccess(AlipayOrder order, String tradeNo,
String totalAmount, String gmtPayment) {
try {
log.info("处理支付成功: orderNo={}, tradeNo={}",
order.getOrderNo(), tradeNo);

// 更新订单状态
order.setTradeNo(tradeNo);
order.setStatus("PAY_SUCCESS");
order.setPayAmount(new java.math.BigDecimal(totalAmount));
order.setPayTime(parsePayTime(gmtPayment));
order.setUpdateTime(LocalDateTime.now());

alipayOrderRepository.save(order);

// 执行业务逻辑(异步)
executeBusiness(order);

log.info("支付成功处理完成: orderNo={}", order.getOrderNo());

} catch (Exception e) {
log.error("处理支付成功失败: orderNo={}", order.getOrderNo(), e);
throw e;
}
}

/**
* 执行业务逻辑
*/
private void executeBusiness(AlipayOrder order) {
// 异步执行业务逻辑
// 例如:发货、增加会员、发放优惠券等
log.info("执行业务逻辑: orderNo={}", order.getOrderNo());
}

/**
* 解析支付时间
*/
private LocalDateTime parsePayTime(String payTime) {
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(payTime, formatter);
} catch (Exception e) {
log.error("解析支付时间失败: payTime={}", payTime, e);
return LocalDateTime.now();
}
}
}

10. 退款服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.alipay.service;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.alipay.config.AlipayConfig;
import com.alipay.entity.AlipayOrder;
import com.alipay.repository.AlipayOrderRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
* 退款服务
* @author Java实战
*/
@Slf4j
@Service
public class AlipayRefundService {

@Autowired
private AlipayConfig alipayConfig;

@Autowired
private AlipayClient alipayClient;

@Autowired
private AlipayOrderRepository alipayOrderRepository;

/**
* 申请退款
*/
@Transactional
public String refund(String orderNo, BigDecimal refundAmount, String reason) {
try {
log.info("申请退款: orderNo={}, refundAmount={}, reason={}",
orderNo, refundAmount, reason);

// 查询订单
AlipayOrder order = alipayOrderRepository.findByOrderNo(orderNo);
if (order == null) {
throw new RuntimeException("订单不存在");
}

if (!"PAY_SUCCESS".equals(order.getStatus())) {
throw new RuntimeException("订单状态不允许退款");
}

// 生成退款单号
String refundNo = "REFUND" + System.currentTimeMillis();

// 创建退款请求
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

// 构建业务参数
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderNo);
bizContent.put("refund_amount", refundAmount.toString());
bizContent.put("refund_reason", reason);
bizContent.put("out_request_no", refundNo);

request.setBizContent(bizContent.toJSONString());

// 调用退款接口
AlipayTradeRefundResponse response = alipayClient.execute(request);

if (response.isSuccess()) {
log.info("退款申请成功: orderNo={}, refundNo={}, refundAmount={}",
orderNo, refundNo, refundAmount);
return refundNo;
} else {
log.error("退款申请失败: orderNo={}, msg={}",
orderNo, response.getMsg());
throw new RuntimeException("退款申请失败: " + response.getMsg());
}

} catch (Exception e) {
log.error("申请退款失败: orderNo={}", orderNo, e);
throw new RuntimeException("申请退款失败", e);
}
}

/**
* 查询退款状态
*/
public JSONObject queryRefund(String orderNo, String refundNo) {
try {
log.info("查询退款状态: orderNo={}, refundNo={}", orderNo, refundNo);

// 这里需要调用支付宝退款查询接口
// 由于支付宝SDK没有提供退款查询接口,可以通过对账文件查询

JSONObject result = new JSONObject();
result.put("orderNo", orderNo);
result.put("refundNo", refundNo);
result.put("status", "SUCCESS");

return result;

} catch (Exception e) {
log.error("查询退款失败: orderNo={}", orderNo, e);
throw new RuntimeException("查询退款失败", e);
}
}
}

11. 控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package com.alipay.controller;

import com.alibaba.fastjson.JSONObject;
import com.alipay.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Map;

/**
* 支付宝支付控制器
* @author Java实战
*/
@Slf4j
@RestController
@RequestMapping("/api/alipay")
public class AlipayController {

@Autowired
private WapPayService wapPayService;

@Autowired
private AppPayService appPayService;

@Autowired
private FaceToFacePayService faceToFacePayService;

@Autowired
private AlipayNotifyService notifyService;

@Autowired
private AlipayRefundService refundService;

/**
* 手机网站支付下单
*/
@PostMapping("/wap/create")
public ResponseEntity<String> createWapOrder(@RequestBody JSONObject params) {
try {
String userId = params.getString("userId");
String subject = params.getString("subject");
String body = params.getString("body");
BigDecimal totalAmount = params.getBigDecimal("totalAmount");

String payForm = wapPayService.createWapOrder(userId, subject, body, totalAmount);

return ResponseEntity.ok(payForm);

} catch (Exception e) {
log.error("手机网站支付下单失败", e);
return ResponseEntity.internalServerError().body("支付下单失败: " + e.getMessage());
}
}

/**
* APP支付下单
*/
@PostMapping("/app/create")
public ResponseEntity<JSONObject> createAppOrder(@RequestBody JSONObject params) {
try {
String userId = params.getString("userId");
String subject = params.getString("subject");
String body = params.getString("body");
BigDecimal totalAmount = params.getBigDecimal("totalAmount");

String payInfo = appPayService.createAppOrder(userId, subject, body, totalAmount);

JSONObject result = new JSONObject();
result.put("code", "SUCCESS");
result.put("payInfo", payInfo);

return ResponseEntity.ok(result);

} catch (Exception e) {
log.error("APP支付下单失败", e);
JSONObject error = new JSONObject();
error.put("code", "FAIL");
error.put("message", e.getMessage());
return ResponseEntity.internalServerError().body(error);
}
}

/**
* 当面付下单
*/
@PostMapping("/face-to-face/create")
public ResponseEntity<JSONObject> createFaceToFaceOrder(@RequestBody JSONObject params) {
try {
String userId = params.getString("userId");
String subject = params.getString("subject");
String body = params.getString("body");
BigDecimal totalAmount = params.getBigDecimal("totalAmount");

String qrCode = faceToFacePayService.createFaceToFaceOrder(userId, subject, body, totalAmount);

JSONObject result = new JSONObject();
result.put("code", "SUCCESS");
result.put("qrCode", qrCode);

return ResponseEntity.ok(result);

} catch (Exception e) {
log.error("当面付下单失败", e);
JSONObject error = new JSONObject();
error.put("code", "FAIL");
error.put("message", e.getMessage());
return ResponseEntity.internalServerError().body(error);
}
}

/**
* 支付回调通知
*/
@PostMapping("/notify")
public String payNotify(HttpServletRequest request) {
try {
// 获取所有请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
Map<String, String> params = new java.util.HashMap<>();

for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
if (values != null && values.length > 0) {
params.put(key, values[0]);
}
}

// 处理回调
return notifyService.handlePayNotify(params);

} catch (Exception e) {
log.error("处理支付回调失败", e);
return "failure";
}
}

/**
* 查询订单
*/
@GetMapping("/query/{orderNo}")
public ResponseEntity<JSONObject> queryOrder(@PathVariable String orderNo) {
try {
JSONObject result = wapPayService.queryOrder(orderNo);
return ResponseEntity.ok(result);

} catch (Exception e) {
log.error("查询订单失败", e);
JSONObject error = new JSONObject();
error.put("code", "FAIL");
error.put("message", e.getMessage());
return ResponseEntity.internalServerError().body(error);
}
}

/**
* 关闭订单
*/
@PostMapping("/close/{orderNo}")
public ResponseEntity<JSONObject> closeOrder(@PathVariable String orderNo) {
try {
wapPayService.closeOrder(orderNo);

JSONObject result = new JSONObject();
result.put("code", "SUCCESS");
result.put("message", "订单关闭成功");

return ResponseEntity.ok(result);

} catch (Exception e) {
log.error("关闭订单失败", e);
JSONObject error = new JSONObject();
error.put("code", "FAIL");
error.put("message", e.getMessage());
return ResponseEntity.internalServerError().body(error);
}
}

/**
* 申请退款
*/
@PostMapping("/refund")
public ResponseEntity<JSONObject> refund(@RequestBody JSONObject params) {
try {
String orderNo = params.getString("orderNo");
BigDecimal refundAmount = params.getBigDecimal("refundAmount");
String reason = params.getString("reason");

String refundNo = refundService.refund(orderNo, refundAmount, reason);

JSONObject result = new JSONObject();
result.put("code", "SUCCESS");
result.put("refundNo", refundNo);
result.put("message", "退款申请成功");

return ResponseEntity.ok(result);

} catch (Exception e) {
log.error("申请退款失败", e);
JSONObject error = new JSONObject();
error.put("code", "FAIL");
error.put("message", e.getMessage());
return ResponseEntity.internalServerError().body(error);
}
}
}

12. Repository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.alipay.repository;

import com.alipay.entity.AlipayOrder;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;

/**
* 支付订单Repository
* @author Java实战
*/
@Repository
public interface AlipayOrderRepository extends JpaRepository<AlipayOrder, Long> {

/**
* 根据订单号查询
*/
AlipayOrder findByOrderNo(String orderNo);

/**
* 根据支付宝交易号查询
*/
AlipayOrder findByTradeNo(String tradeNo);

/**
* 根据用户ID查询订单列表
*/
List<AlipayOrder> findByUserId(String userId);

/**
* 查询过期未支付订单
*/
List<AlipayOrder> findByStatusAndExpireTimeBefore(String status, LocalDateTime expireTime);
}

13. 总结

支付宝支付是电商系统的重要支付方式。通过本文的详细介绍,我们了解了:

  1. 多种支付方式: 手机网站、APP、当面付等多种支付场景
  2. 统一下单流程: 完整的下单、支付、回调流程
  3. 支付回调处理: 签名验证、防重复通知、订单状态更新
  4. 退款功能: 申请退款、查询退款状态
  5. 安全机制: 签名验证、幂等性保证、分布式锁

通过合理的支付宝支付集成实现,可以为用户提供便捷、安全的支付体验。


Java实战要点:

  • 使用官方SDK简化开发
  • 支付回调必须验证签名
  • 使用分布式锁防止重复通知
  • 订单状态流转要严格控制
  • 金额计算要精确到分

代码注解说明:

  • @Transactional: 事务管理确保数据一致性
  • AlipayClient: 支付宝客户端
  • AlipaySignature: 签名验证工具
  • 金额单位: 支付宝金额单位为元
  • 订单号: 商户订单号必须唯一