liuchengsen 2 месяцев назад
Родитель
Сommit
16a3508e35

+ 4 - 4
zhijiayun-gateway/src/main/resources/application-dev.yml

@@ -58,13 +58,13 @@ payment:
     height: 200
   # 支付宝(开发/测试环境):电脑网站支付,将沙箱证书文件复制到 certs/dev/alipay/ 目录下
   alipay:
-    app-id: ${ALIPAY_APP_ID:2021006169624278}
+    app-id: ${ALIPAY_APP_ID:9021000165614119}
     notify-url: ${ALIPAY_NOTIFY_URL:https://priceapi.kailin.com.cn/api/payment/callback/alipay}
     return-url: ${ALIPAY_RETURN_URL:http://localhost:8001/payment/success}
-    server-url: ${ALIPAY_SERVER_URL:https://openapi.alipay.com/gateway.do}
+    server-url: ${ALIPAY_SERVER_URL:https://openapi-sandbox.dl.alipaydev.com/gateway.do}
     # 公钥模式(优先):配置了 alipay-public-key 即走公钥模式
-    app-private-key: ${ALIPAY_APP_PRIVATE_KEY:MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPZKA2tBUzhVUOlQkoSaovA6Mg9hTyHuI/lK0eqx5C478MKi4Ve1LMDdatY7jKhY0HVT5vu8VQ+NYyzP6CXTlRhWRCzzWa6BYPzWvR3JAIj+mJN/7jRsWvMx8DFjzR+1VIaO2eA3FHU673pCJwuM8PTgM0t/dW0NuzoYus6/cEUXAKYoM7Bm8qewHjSVIB7bC9Dy1IUwMwbXLtBjb6IPquYfCkXqcgfLSXspxqQ5PTYbWkUxeI24kP+4IcoDo3C8LwOseXD8EHPo1J9N2IbiPD8mfEmTJPo4F+vvlI8oudFcLLVd46YP5gRcmkZkM3F6863vHumXwciFZgVsqqdOSVAgMBAAECggEAbcoxXoQ67Qlam9ud+2vz4eulAiADuqFK2KnWc/PWk0aJruDtgIXGsStqSGQQQXYt0IH6CaB3toPksQQAf/bmDxf0mcGj+RuAAmGXAIQo4797vB6emqaupC8F7dZp4CeTYD/b+tiLsVzFtWd4q//J7ao43EG/eIAcrp2ImcRZAjk+arRv+ZftP98bNwD5H3zPL+M66Y2mVqYSWq5N6ZNTMdrL8rU3s46piKhLQvcjwtiX3EoCju2ubQswDt0Z4K0gQ+TqL6HctIc4mltJlSzxqBSUb3WyifPzhbZWqFPKbuDU+aU5HSJFA6j0YJgFSV8Lg1NYh3BxCu/l1v2UNE0+CQKBgQDta+QAzNdh0P0je/lMPTcqW5X6s0uCA5hCcOGMhVbDxP9fI6sBOnUeNVp0H3wgG1vFRbeZeIqO6qSrdDOG+TfKuG+8lumQCLg0UxpkBvAS0zyTVsHg5N9+qtJQ0lf2jE2BWiP5x/7vqqlLb0x4tD8QIe2itCdRlZTZthhmbDQRXwKBgQDfnzI8uuT+CF3WNlhyvC8APmvKsplxeIlE2Ejn930hQOmSgomwbgSoSkanzZ3yxF9othOc7gDNyz833aWOQrhZ59t3s3QfrkI/ZHlAhfmQ+/+CuxEMvN+RPbkA9a2dLWNMzU9yPDKcVq0JNmlGvrrkpbG4V0Koaf1iMrYkARZKiwKBgAG6YE11MDghCkag116rr2QaiIvrlCgdGJWbNrkMG2aPX4B0xQDcSQ/wOXlQ5VlieZeE+2Idk3yYzpwE238m/zX1g7KusoJ+3cBbvnxt5H9ulClKX1zgrZPBcIhwD33W5ZWEb3CY+Q0Lg/HtnHPgc/kNFiqx/vtECHJRNfpA3VwnAoGBAKTFxtyc0dKlE4+d57pTE0bYt9BZnVrQ+ER0IfRalicEj5bW/OG1j1T5teo7ReD19V9Qmk8NyCXbVy+w8CZM303P3BTtc+lMG6BeRkHW9jdwl6sab8u09/8rPcCJRm4EXaDfv2RrGte7vjCFmTj5nPiRZsxv77vsrTnIKBGLg4NVAoGBAKei4bfOwPsh33KT+e0jMJFlDO3lJ3KvQ47j6kela2LyFvG8rAkwDfqQjdUp2HfrumSQ8+WeJ1fYKpfKEn5XMRAtsCBhCYr0wQyhevG3m1JcuJqioAqo1WljYfUL9PJUFskssAmF9FxrG14mthdbiIlo5H0hYLWTn8aKYv3uvS+e}
-    alipay-public-key: ${ALIPAY_PUBLIC_KEY:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ZB3OubP0KcNtCVLFsXdkvwMGXnzlLse6lFifFTjo5z3UJQQCRjRgFVgPO5idGiUT4TQaV5IzqDGoP/S4w9l2+oFYw/6Lc5QBAjO441NlD8hf05K8+HEc6qtRAhzimzVyvGPsPFjHxmLaQbjiF0Oqvhez5UKUoFiZcBrqnpy+bB9ym5rMoUUPaTKV/fgdAnS3h4NxIzb2kP/nWMIEWt320yQkkeyIbiWcKmdHwwzgPOIjJm6gDfoN4KQvjuc8CwNXb21YTn4FFqfXMhCSIW5Weh4uS+MN5YL8JeqBaMtit9x1crqnBKKYLxhbrV381O/O9P6qbsI3tPk6ePLFVGxWQIDAQAB}
+    app-private-key: ${ALIPAY_APP_PRIVATE_KEY:MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDlmkkSgxXgmdl8H2q+AFUtHxZ6yBNjJTHPSalYKnxVSbyL2YKqhJwdJ4UzRn1aXSB8Tgl1bZndg/XJfammNvBNHTjMUsXdhWbTPqN5SHuDgwboJ2/RmG4wJQg8bQ0BmnChfG1YSnpzLJ8cGf576jnSmg2SEb0KaAl220xH/a7nPfHM45q2PyxRvclThZBkd4eG1T14//VczQeJ4Qsaw/9XnADaFCR3tqNs/9cml70tqM3GkcjsYCxKi2cSwJKAcupZc0lt2gmlAr5GzHjWJTpyGVA1dl8LIPDNhXLhHczriJ3J8Jsojx6h2wcM+nxATeGMCQ7pn67zMIoTyrCDXLAFAgMBAAECggEBAI24QcpPZz2PORLIQZatleWX/sQiovTbjHcqPIDTGJN8RAj+ZQzjdYj5T9pqdA1G1jpj4Kq+z+5O+XM5QXH7DzBKDTjxDnjBUsqpKzY9ax+tzKGXVgnFUJ4e/YputUx8C3Fh4AxTTGmy7TLnSJEr7iz0MdQrZsf5l33ptpfpGgBYrECVp1goqUFsFYIxMylzRg3K6RcnO8et8T3bu96LQ488qyaOgWyTMQVsMnWi68CZ3Ft7cSABAlwf9huRjuW/UQKESo6ZJ+4BZ4eATh/9hReP5bkVhJWCPBLhJ0+0b5peU5IjPigUFJNfKjdSgUBWHmOc5OSk9gVHMGTJW65XHTECgYEA/b/4si7jOrX1FO1aGpYb5QT7w4OC8ezFNdDn1BwkBhYMwjpoAaO1FAs8AHBvlwT0vwekrZRFjat/+PBRrdH0MomNUM5eUbZKONhJp3/A8QjEQ6LUSnDlCDFEQL+tobEIyJ6ZiHYTeNTBHQehkBqRDo3gLeLNJTldo4xPoI3eDKsCgYEA56N/jWMVj+IscsvWuc7S0OakwSduQxvtO9cD1fOrYH7xxtfO6uPfwX/n/N+WSAG8JumBatvZf0aBzzdrqa3fmjVfrILN7BWlK/Dt9h3krBqMh3c/jEsCQj1yNw+4UWjWRH9DbXO3LpNwDCdW5MnTnWRoQw7PVOGpC8QlrOhJ1g8CgYEAzn1w0PcM3FMDzHLVtvreWkDy64hSf7JQTVnhvZ7kxbsdyNSknGjuyw/VVPoqJLfYvrdmkAYvj3i3HAncPdmzoYiD4fT9lfsEbTu8vDxe8wZSSEPyA13ZUxBcXWBd2E1rO26su/pyIArp10jFdbHYxwI3IsAEdr028QcWKS4pOx8CgYEAtMC8k9Oxy6kuDhZJ5IwjraeNALyIKg3svTk3YQjXvmDQwd09NcXDoPbyTD0cw+u2qpajyoLr4YlfWhdhB/MJ/QeRNMiNb6AkV3mV9uu0uSeuaTcAe7U8efwA2D6IEd7A9IpqT0pwswOR96nrAHUNkE/9m/Y+i3nbs7bHv3kzxLkCgYBH58zAeQLVKl7vCqtG/iBAuGjD8+Y+YH702+kK58ufr1zRFj9WiiLER0OxTpdzBqEu9FG12V2jCsVVH3m5E4RjAiBYPbXAmN1w4BjtM8zKRnl7PECSVcIlIFNV1foAhS+ubFwKLgeox8z2A19sZZ/HgdnLqBr9z+0b6KXRHDrUrA==}
+    alipay-public-key: ${ALIPAY_PUBLIC_KEY:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg7TfqIiv1ghpv1oF05HM8deLMT1Da8VYLkPIYTGcVa/+XNNaCivQCPiJQUe78TL2CPfuT49tNspQBtZXnwiZ4XJYSvzfOTko0sssFOgLW76TWNyr1cVL0bPOzemY84Dz1ezdRo1TiGlNVcJnQPq8abzYDtgPgh4pDuoXEyMKQGFnmXdPZdtZFBIlMTe3Smf0DMn98307cZxS+Kh3bWtA0WQt+RMuBjbugvms1xrmTWAFs5Q9cuUgDjxX4C1+CgIlTfJvB0XPotljHQ0kGytD9mDrWR29t7CKW+K6qjSWiq6SrM3llWO4wGp6Af34qzc05wegdgv73Ai0DjueAIQUtwIDAQAB}
     # 证书模式(备选):未配置 alipay-public-key 时回退到证书模式,从 certs/dev/alipay/ 读取
     app-private-key-path: ${ALIPAY_APP_PRIVATE_KEY_PATH:certs/dev/alipay/appPrivateKey.txt}
     app-cert-path: ${ALIPAY_APP_CERT_PATH:certs/dev/alipay/appCertPublicKey_2021005188648051.crt}

+ 25 - 0
zhijiayun-gateway/src/main/resources/db/migration-v3.sql

@@ -0,0 +1,25 @@
+-- ============================================
+-- Migration v3: 新增支付宝开放能力回调记录表
+-- Date: 2026-06-29
+-- Author: AI Assistant
+-- ============================================
+
+-- 支付宝开放能力回调记录表
+-- 用于持久化应用网关推送和授权回调信息,便于后续排查和审计
+CREATE TABLE IF NOT EXISTS `t_alipay_callback_record` (
+    `id`          BIGINT        NOT NULL COMMENT '主键ID(雪花算法)',
+    `type`        VARCHAR(50)   NOT NULL COMMENT '回调类型:gateway(应用网关) / auth_callback(授权回调)',
+    `app_id`      VARCHAR(64)   DEFAULT NULL COMMENT '支付宝应用ID',
+    `msg_method`  VARCHAR(128)  DEFAULT NULL COMMENT '网关消息方法名',
+    `auth_code`   VARCHAR(256)  DEFAULT NULL COMMENT '授权码(已脱敏)',
+    `source`      VARCHAR(64)   DEFAULT NULL COMMENT '来源',
+    `state`       VARCHAR(256)  DEFAULT NULL COMMENT '状态参数',
+    `params`      TEXT          DEFAULT NULL COMMENT '原始请求参数(JSON文本)',
+    `status`      VARCHAR(20)   DEFAULT 'RECEIVED' COMMENT '处理状态:RECEIVED/PROCESSED/IGNORED',
+    `error_msg`   VARCHAR(500)  DEFAULT NULL COMMENT '错误信息',
+    `create_time` DATETIME      DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (`id`),
+    KEY `idx_type_create_time` (`type`, `create_time`),
+    KEY `idx_app_id` (`app_id`),
+    KEY `idx_msg_method` (`msg_method`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支付宝开放能力回调记录表';

+ 22 - 0
zhijiayun-gateway/src/main/resources/db/schema.sql

@@ -559,3 +559,25 @@ ON DUPLICATE KEY UPDATE
     `reward_type` = 'MEMBERSHIP',
     `landing_desc` = '登录智价云(药店端)平台,聚合比价查低价',
     `max_total_invites` = VALUES(`max_total_invites`);
+
+-- ============================================
+-- DDL: 支付宝开放能力回调记录表
+-- 用于持久化应用网关推送和授权回调信息,便于后续排查和审计
+-- ============================================
+CREATE TABLE IF NOT EXISTS `t_alipay_callback_record` (
+    `id`          BIGINT        NOT NULL COMMENT '主键ID(雪花算法)',
+    `type`        VARCHAR(50)   NOT NULL COMMENT '回调类型:gateway(应用网关) / auth_callback(授权回调)',
+    `app_id`      VARCHAR(64)   DEFAULT NULL COMMENT '支付宝应用ID',
+    `msg_method`  VARCHAR(128)  DEFAULT NULL COMMENT '网关消息方法名',
+    `auth_code`   VARCHAR(256)  DEFAULT NULL COMMENT '授权码(已脱敏)',
+    `source`      VARCHAR(64)   DEFAULT NULL COMMENT '来源',
+    `state`       VARCHAR(256)  DEFAULT NULL COMMENT '状态参数',
+    `params`      TEXT          DEFAULT NULL COMMENT '原始请求参数(JSON文本)',
+    `status`      VARCHAR(20)   DEFAULT 'RECEIVED' COMMENT '处理状态:RECEIVED/PROCESSED/IGNORED',
+    `error_msg`   VARCHAR(500)  DEFAULT NULL COMMENT '错误信息',
+    `create_time` DATETIME      DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (`id`),
+    KEY `idx_type_create_time` (`type`, `create_time`),
+    KEY `idx_app_id` (`app_id`),
+    KEY `idx_msg_method` (`msg_method`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支付宝开放能力回调记录表';

+ 113 - 0
zhijiayun-payment/src/main/java/com/xuekairui/payment/controller/AlipayOpenController.java

@@ -0,0 +1,113 @@
+package com.xuekairui.payment.controller;
+
+import com.xuekairui.payment.service.AlipayCallbackRecordService;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 支付宝开放能力回调接口
+ *
+ * <p>与 {@link PaymentCallbackController} 不同,本控制器处理的是非支付类回调:
+ * <ol>
+ *   <li>应用网关:接收支付宝主动推送的消息(如退款、投诉、账单等)</li>
+ *   <li>授权回调:接收支付宝 OAuth 授权码回调</li>
+ * </ol>
+ *
+ * <p>这两个地址需在支付宝开放平台后台的“应用网关地址”和“授权回调地址”中配置,
+ * 不应与支付异步通知地址混用。
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/payment/alipay")
+@RequiredArgsConstructor
+public class AlipayOpenController {
+
+    private final AlipayCallbackRecordService callbackRecordService;
+
+    /**
+     * 支付宝应用网关入口
+     *
+     * <p>支付宝会把各类异步消息推送到此地址,例如:
+     * <ul>
+     *   <li>alipay.trade.refund.depositback.completed(退款的保证金解冻完成)</li>
+     *   <li>alipay.open.auth.userauth.cancelled(用户取消授权)</li>
+     *   <li>各种账单/发票/对账通知</li>
+     * </ul>
+     *
+     * <p>当前会持久化到 {@code t_alipay_callback_record} 表并返回 success,避免支付宝重试。
+     * 后续若需处理具体业务,可根据 {@code msg_method} 字段分发。
+     *
+     * @see <a href="https://opendocs.alipay.com/open/194/106292">支付宝异步通知说明</a>
+     */
+    @PostMapping("/gateway")
+    public String gateway(HttpServletRequest request) {
+        Map<String, String> params = extractAllParams(request);
+        String msgMethod = params.get("msg_method");
+        String appId = params.get("app_id");
+        log.info("收到支付宝应用网关推送: msgMethod={}, appId={}, params={}", msgMethod, appId, maskSensitiveParams(params));
+
+        callbackRecordService.saveGatewayRecord(appId, msgMethod, params);
+        return "success";
+    }
+
+    /**
+     * 支付宝授权回调入口
+     *
+     * <p>用户在支付宝完成 OAuth 授权后,支付宝会重定向到此地址,携带 {@code auth_code}、
+     * {@code app_id}、{@code source} 等参数。</p>
+     *
+     * <p>当前会持久化到 {@code t_alipay_callback_record} 表并返回简单成功页面。</p>
+     *
+     * @see <a href="https://opendocs.alipay.com/open/284/106035">支付宝用户授权文档</a>
+     */
+    @GetMapping("/auth-callback")
+    public String authCallback(
+            @RequestParam(value = "auth_code", required = false) String authCode,
+            @RequestParam(value = "app_id", required = false) String appId,
+            @RequestParam(value = "source", required = false) String source,
+            @RequestParam(value = "state", required = false) String state) {
+
+        log.info("收到支付宝授权回调: appId={}, source={}, state={}", appId, source, state);
+        callbackRecordService.saveAuthCallbackRecord(appId, source, state, authCode);
+
+        // 如需真正换取 access_token,可在此调用 alipay.system.oauth.token
+        return "<html><body><h1>授权回调已收到</h1><p>授权码已记录,可关闭此页面。</p></body></html>";
+    }
+
+    /**
+     * 提取所有请求参数
+     */
+    private Map<String, String> extractAllParams(HttpServletRequest request) {
+        Map<String, String> params = new HashMap<>();
+        request.getParameterMap().forEach((key, values) -> {
+            if (values != null && values.length > 0) {
+                params.put(key, values[0]);
+            }
+        });
+        return params;
+    }
+
+    /**
+     * 对网关参数做脱敏,避免日志泄露敏感内容
+     */
+    private Map<String, String> maskSensitiveParams(Map<String, String> params) {
+        Map<String, String> masked = new HashMap<>(params);
+        if (masked.containsKey("sign")) {
+            masked.put("sign", "***");
+        }
+        if (masked.containsKey("biz_content")) {
+            String biz = masked.get("biz_content");
+            masked.put("biz_content", biz.length() > 100 ? biz.substring(0, 100) + "..." : biz);
+        }
+        return masked;
+    }
+}

+ 57 - 0
zhijiayun-payment/src/main/java/com/xuekairui/payment/entity/AlipayCallbackRecord.java

@@ -0,0 +1,57 @@
+package com.xuekairui.payment.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 支付宝开放能力回调记录
+ *
+ * <p>用于持久化支付宝应用网关推送和授权回调的信息,便于后续排查和审计。
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@TableName("t_alipay_callback_record")
+public class AlipayCallbackRecord {
+
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /** 回调类型:gateway(应用网关) / auth_callback(授权回调) */
+    private String type;
+
+    /** 支付宝应用ID */
+    private String appId;
+
+    /** 网关消息方法名(如 alipay.trade.refund.depositback.completed) */
+    private String msgMethod;
+
+    /** 授权码(已脱敏存储,保留前4后4位) */
+    private String authCode;
+
+    /** 来源 */
+    private String source;
+
+    /** 状态参数 */
+    private String state;
+
+    /** 原始请求参数(JSON文本) */
+    private String params;
+
+    /** 处理状态:RECEIVED(已接收) / PROCESSED(已处理) / IGNORED(已忽略) */
+    @Builder.Default
+    private String status = "RECEIVED";
+
+    /** 错误信息 */
+    private String errorMsg;
+
+    /** 创建时间 */
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+}

+ 19 - 0
zhijiayun-payment/src/main/java/com/xuekairui/payment/mapper/AlipayCallbackRecordMapper.java

@@ -0,0 +1,19 @@
+package com.xuekairui.payment.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.xuekairui.payment.entity.AlipayCallbackRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * 支付宝开放能力回调记录 Mapper
+ */
+@Mapper
+public interface AlipayCallbackRecordMapper extends BaseMapper<AlipayCallbackRecord> {
+
+    @Select("SELECT * FROM t_alipay_callback_record WHERE type = #{type} ORDER BY create_time DESC LIMIT #{limit}")
+    List<AlipayCallbackRecord> selectRecentByType(@Param("type") String type, @Param("limit") int limit);
+}

+ 74 - 0
zhijiayun-payment/src/main/java/com/xuekairui/payment/service/AlipayCallbackRecordService.java

@@ -0,0 +1,74 @@
+package com.xuekairui.payment.service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.xuekairui.payment.entity.AlipayCallbackRecord;
+import com.xuekairui.payment.mapper.AlipayCallbackRecordMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+/**
+ * 支付宝开放能力回调记录 Service
+ */
+@Slf4j
+@Service
+public class AlipayCallbackRecordService extends ServiceImpl<AlipayCallbackRecordMapper, AlipayCallbackRecord> {
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    /**
+     * 保存应用网关推送记录
+     */
+    public void saveGatewayRecord(String appId, String msgMethod, Map<String, String> params) {
+        try {
+            AlipayCallbackRecord record = AlipayCallbackRecord.builder()
+                    .type("gateway")
+                    .appId(appId)
+                    .msgMethod(msgMethod)
+                    .params(toJson(params))
+                    .status("RECEIVED")
+                    .build();
+            save(record);
+        } catch (Exception e) {
+            log.error("保存支付宝网关回调记录失败: appId={}, msgMethod={}", appId, msgMethod, e);
+        }
+    }
+
+    /**
+     * 保存授权回调记录
+     */
+    public void saveAuthCallbackRecord(String appId, String source, String state, String authCode) {
+        try {
+            AlipayCallbackRecord record = AlipayCallbackRecord.builder()
+                    .type("auth_callback")
+                    .appId(appId)
+                    .source(source)
+                    .state(state)
+                    .authCode(maskAuthCode(authCode))
+                    .status("RECEIVED")
+                    .build();
+            save(record);
+        } catch (Exception e) {
+            log.error("保存支付宝授权回调记录失败: appId={}", appId, e);
+        }
+    }
+
+    private String toJson(Map<String, String> params) {
+        try {
+            return objectMapper.writeValueAsString(params);
+        } catch (JsonProcessingException e) {
+            log.warn("支付宝回调参数序列化失败: {}", e.getMessage());
+            return params.toString();
+        }
+    }
+
+    private String maskAuthCode(String authCode) {
+        if (authCode == null || authCode.length() <= 8) {
+            return authCode;
+        }
+        return authCode.substring(0, 4) + "****" + authCode.substring(authCode.length() - 4);
+    }
+}