偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

別再踩坑了!SpringBoot 集成支付寶支付最全教程

開(kāi)發(fā) 項(xiàng)目管理
支付寶支付集成并不復(fù)雜,但細(xì)節(jié)環(huán)環(huán)相扣,稍有遺漏就可能導(dǎo)致異常。希望本文能幫你在項(xiàng)目中快速構(gòu)建一套可靠的支付體系,少走彎路!

在電商、在線服務(wù)以及會(huì)員系統(tǒng)中,支付是繞不過(guò)去的核心環(huán)節(jié)。支付寶作為國(guó)內(nèi)主流的支付方式,早已開(kāi)放了完善的 API 接口供開(kāi)發(fā)者使用。然而,許多初學(xué)者在 Spring Boot 項(xiàng)目中集成支付寶時(shí),經(jīng)常會(huì)遇到密鑰配置不當(dāng)、回調(diào)驗(yàn)證失敗、訂單狀態(tài)未及時(shí)更新等“坑”。

本文將帶你從 支付寶開(kāi)放平臺(tái)沙箱環(huán)境配置 → Spring Boot 項(xiàng)目接入 → 支付/回調(diào)/退款流程 → 消息隊(duì)列處理超時(shí)訂單 全流程,構(gòu)建一個(gè)健壯的支付模塊。為了便于理解,我們會(huì)逐步展開(kāi)代碼示例,幫助你快速落地。

支付寶開(kāi)放平臺(tái)沙箱環(huán)境配置

在本地調(diào)試之前,我們需要先完成支付寶開(kāi)放平臺(tái) 沙箱環(huán)境 的配置。操作步驟如下:

登錄沙箱環(huán)境

進(jìn)入支付寶開(kāi)發(fā)者平臺(tái),使用開(kāi)發(fā)者賬號(hào)登錄,選擇 沙箱環(huán)境。 在 沙箱控制臺(tái) → 沙箱應(yīng)用 → 產(chǎn)品列表 中,可以看到當(dāng)前沙箱所支持的產(chǎn)品。

配置接口加簽方式

支付寶提供了兩種方式:

  • 系統(tǒng)默認(rèn)密鑰/證書(shū)(推薦)
  • 自定義密鑰

這里選擇系統(tǒng)默認(rèn)密鑰,因?yàn)楹罄m(xù)使用 API 在線調(diào)試工具時(shí)必須依賴默認(rèn)密鑰。

應(yīng)用網(wǎng)關(guān)配置

應(yīng)用網(wǎng)關(guān)主要用于接收支付寶的異步通知(如交易完成回調(diào))。

  • HTTP 訂閱模式下必須配置應(yīng)用網(wǎng)關(guān);
  • WebSocket 訂閱模式下則不需要。

生成密鑰

完成應(yīng)用信息配置后,生成一套屬于自己的應(yīng)用密鑰,確保項(xiàng)目后續(xù)能與支付寶沙箱環(huán)境正常交互。

至此,網(wǎng)頁(yè)端配置環(huán)節(jié)結(jié)束。

Spring Boot 項(xiàng)目配置(IDEA 操作部分)

引入依賴

在 pom.xml 中添加支付寶 SDK:

<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.22.110.ALL</version>
</dependency>

在 application.yml 中配置

alipay:
  appId: your-app-id
  appPrivateKey: your-private-key
  alipayPublicKey: your-alipay-public-key
  notifyUrl: http://your-domain.com/alipay/notify

封裝配置類

//src/main/java/com/icoderoad/config/AliPayConfig.java
package com.icoderoad.config;


import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
    private String appId;
    private String appPrivateKey;
    private String alipayPublicKey;
    private String notifyUrl;
}

支付接口開(kāi)發(fā)

//src/main/java/com/icoderoad/controller/AliPayController.java
package com.icoderoad.controller;


import com.alibaba.fastjson.JSONObject;
import com.alipay.api.*;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.icoderoad.config.AliPayConfig;
import com.icoderoad.entity.AliPay;
import com.icoderoad.mapper.OrdersMapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;


@RestController
public class AliPayController {


    private static final String GATEWAY_URL = "https://openapi.alipaydev.com/gateway.do";
    private static final String FORMAT = "JSON";
    private static final String CHARSET = "UTF-8";
    private static final String SIGN_TYPE = "RSA2";


    @Resource
    private AliPayConfig aliPayConfig;


    @Resource
    private OrdersMapper ordersMapper;


    @GetMapping("/pay")
    public void pay(AliPay aliPay, HttpServletResponse httpResponse) throws Exception {
        // 1. 創(chuàng)建客戶端
        AlipayClient alipayClient = new DefaultAlipayClient(
                GATEWAY_URL,
                aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(),
                FORMAT,
                CHARSET,
                aliPayConfig.getAlipayPublicKey(),
                SIGN_TYPE
        );


        // 2. 創(chuàng)建支付請(qǐng)求
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl(aliPayConfig.getNotifyUrl());


        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", aliPay.getTraceNo());
        bizContent.put("total_amount", aliPay.getTotalAmount());
        bizContent.put("subject", aliPay.getSubject());
        bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
        request.setBizContent(bizContent.toString());


        // 3. 執(zhí)行請(qǐng)求并返回表單
        String form = alipayClient.pageExecute(request).getBody();
        httpResponse.setContentType("text/html;charset=" + CHARSET);
        httpResponse.getWriter().write(form);
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
    }
}

異步回調(diào)處理

支付寶異步回調(diào)必須使用 公網(wǎng)地址,本地調(diào)試可借助 natapp 內(nèi)網(wǎng)穿透。

@PostMapping("/notify")
public String payNotify(HttpServletRequest request) throws Exception {
    if ("TRADE_SUCCESS".equals(request.getParameter("trade_status"))) {
        Map<String, String> params = new HashMap<>();
        request.getParameterMap().forEach((name, values) -> params.put(name, values[0]));


        String sign = params.get("sign");
        String content = AlipaySignature.getSignCheckContentV1(params);


        boolean checkSignature = AlipaySignature.rsa256CheckContent(
                content,
                sign,
                aliPayConfig.getAlipayPublicKey(),
                "UTF-8"
        );


        if (checkSignature) {
            ordersMapper.updateState(params.get("out_trade_no"),
                    "已支付",
                    params.get("gmt_payment"),
                    params.get("trade_no"));
        }
    }
    return "success";
}

退款流程

退款流程與支付類似:

  1. 創(chuàng)建 AlipayClient;
  2. 創(chuàng)建 AlipayTradeRefundRequest 并設(shè)置參數(shù);
  3. 執(zhí)行請(qǐng)求,根據(jù) isSuccess 判斷結(jié)果;
  4. 成功則更新數(shù)據(jù)庫(kù)狀態(tài)。

退款接口完整代碼

package com.icoderoad.controller;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.DateUnit;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.*;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.icoderoad.config.AliPayConfig;
import com.icoderoad.entity.AliPay;
import com.icoderoad.entity.Orders;
import com.icoderoad.mapper.OrdersMapper;
import com.icoderoad.result.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


import javax.annotation.Resource;


@RestController
public class RefundController {


    private static final String GATEWAY_URL = "https://openapi.alipaydev.com/gateway.do";
    private static final String FORMAT = "JSON";
    private static final String CHARSET = "UTF-8";
    private static final String SIGN_TYPE = "RSA2";


    @Resource
    private AliPayConfig aliPayConfig;


    @Resource
    private OrdersMapper ordersMapper;


    @GetMapping("/return")
    public Result returnPay(AliPay aliPay) throws AlipayApiException {
        String now = DateUtil.now();
        Orders orders = ordersMapper.getByNo(aliPay.getTraceNo());
        if (orders != null) {
            long between = DateUtil.between(DateUtil.parseDateTime(orders.getPaymentTime()),
                    DateUtil.parseDateTime(now), DateUnit.DAY);
            if (between > 7) {
                return Result.error("-1", "該訂單已超過(guò)7天,不支持退款");
            }
        }


        AlipayClient alipayClient = new DefaultAlipayClient(
                GATEWAY_URL,
                aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(),
                FORMAT,
                CHARSET,
                aliPayConfig.getAlipayPublicKey(),
                SIGN_TYPE
        );


        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("trade_no", aliPay.getAlipayTraceNo());
        bizContent.put("refund_amount", aliPay.getTotalAmount());
        bizContent.put("out_request_no", aliPay.getTraceNo());
        request.setBizContent(bizContent.toString());


        AlipayTradeRefundResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            ordersMapper.updatePayState(aliPay.getTraceNo(), "已退款", now);
            return Result.success();
        } else {
            return Result.error(response.getCode(), response.getBody());
        }
    }
}

核心邏輯就是 使用支付寶回調(diào)訂單號(hào)與金額完成退款。

未支付訂單的自動(dòng)取消(消息隊(duì)列)

為了避免用戶下單后長(zhǎng)時(shí)間不支付,系統(tǒng)需要自動(dòng)取消訂單。

我們使用 RabbitMQ 延遲隊(duì)列 來(lái)實(shí)現(xiàn):

  1. 下單時(shí)投遞一條延時(shí)消息(30 分鐘);
  2. 若超時(shí)未消費(fèi),該消息進(jìn)入死信隊(duì)列;
  3. 消費(fèi)死信消息時(shí),查詢訂單支付狀態(tài);
  • 若仍未支付,則更新訂單為“已超時(shí)”;
  • 若已支付,則丟棄消息。

這種方式具有 高效、可擴(kuò)展 的優(yōu)勢(shì),但依賴 RabbitMQ 運(yùn)維,增加了一定的系統(tǒng)復(fù)雜度。

RabbitMQ 配置類

//src/main/java/com/icoderoad/config/RabbitConfig.java
package com.icoderoad.config;


import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class RabbitConfig {


    public static final String ORDER_DELAY_QUEUE = "order.delay.queue";
    public static final String ORDER_DELAY_EXCHANGE = "order.delay.exchange";
    public static final String ORDER_ROUTING_KEY = "order.delay.routingkey";


    public static final String ORDER_DEAD_QUEUE = "order.dead.queue";
    public static final String ORDER_DEAD_EXCHANGE = "order.dead.exchange";
    public static final String ORDER_DEAD_ROUTING_KEY = "order.dead.routingkey";


    @Bean
    public DirectExchange orderDelayExchange() {
        return new DirectExchange(ORDER_DELAY_EXCHANGE);
    }


    @Bean
    public DirectExchange orderDeadExchange() {
        return new DirectExchange(ORDER_DEAD_EXCHANGE);
    }


    @Bean
    public Queue orderDelayQueue() {
        return QueueBuilder.durable(ORDER_DELAY_QUEUE)
                .withArgument("x-dead-letter-exchange", ORDER_DEAD_EXCHANGE)
                .withArgument("x-dead-letter-routing-key", ORDER_DEAD_ROUTING_KEY)
                .withArgument("x-message-ttl", 1800000) // 30分鐘
                .build();
    }


    @Bean
    public Queue orderDeadQueue() {
        return QueueBuilder.durable(ORDER_DEAD_QUEUE).build();
    }


    @Bean
    public Binding orderDelayBinding() {
        return BindingBuilder.bind(orderDelayQueue()).to(orderDelayExchange()).with(ORDER_ROUTING_KEY);
    }


    @Bean
    public Binding orderDeadBinding() {
        return BindingBuilder.bind(orderDeadQueue()).to(orderDeadExchange()).with(ORDER_DEAD_ROUTING_KEY);
    }
}

死信隊(duì)列消費(fèi)者

//src/main/java/com/icoderoad/consumer/OrderDeadConsumer.java
package com.icoderoad.consumer;


import com.icoderoad.mapper.OrdersMapper;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;


import javax.annotation.Resource;


import static com.icoderoad.config.RabbitConfig.ORDER_DEAD_QUEUE;


@Component
public class OrderDeadConsumer {


    @Resource
    private OrdersMapper ordersMapper;


    @RabbitListener(queues = ORDER_DEAD_QUEUE)
    public void handleDeadMessage(String orderNo) {
        String state = ordersMapper.getStateByNo(orderNo);
        if ("待支付".equals(state)) {
            ordersMapper.updateState(orderNo, "已超時(shí)", null, null);
            System.out.println("訂單超時(shí)未支付,已自動(dòng)取消: " + orderNo);
        }
    }
}

結(jié)論

通過(guò)以上完整的流程,我們實(shí)現(xiàn)了一個(gè) 可落地、穩(wěn)定、支持支付/回調(diào)/退款/超時(shí)取消 的支付寶支付模塊:

  • 前端沙箱環(huán)境幫助我們快速調(diào)試;
  • Spring Boot 配合支付寶 SDK 打通了支付鏈路;
  • 異步回調(diào)保障交易狀態(tài)的準(zhǔn)確性;
  • RabbitMQ 延遲隊(duì)列確保未支付訂單能夠自動(dòng)取消。

支付寶支付集成并不復(fù)雜,但細(xì)節(jié)環(huán)環(huán)相扣,稍有遺漏就可能導(dǎo)致異常。希望本文能幫你在項(xiàng)目中快速構(gòu)建一套可靠的支付體系,少走彎路! 

責(zé)任編輯:武曉燕 來(lái)源: 路條編程
相關(guān)推薦

2025-09-25 09:31:53

2021-09-09 15:30:28

鴻蒙HarmonyOS應(yīng)用

2025-02-17 00:00:45

接口支付寶沙箱

2021-01-25 14:13:26

iOS支付寶支付

2025-02-18 16:00:00

SpringBoot支付Java

2014-11-17 10:52:56

支付寶去阿里化

2024-02-28 08:59:47

2011-04-21 11:27:42

Firefox支付寶

2009-09-17 12:15:28

互聯(lián)網(wǎng)

2013-10-11 09:41:01

Windows 8.1支付寶

2009-08-26 16:07:09

支付寶網(wǎng)上購(gòu)物安全邁克菲

2023-11-28 08:53:15

2018-03-27 12:02:31

央行支付寶紅包

2011-10-15 23:24:48

Java

2009-11-23 10:02:22

PHP支付寶接口

2009-12-14 16:31:00

Linux安裝支付寶

2013-10-31 11:24:53

支付寶漏洞支付寶漏洞

2017-12-18 18:23:09

支付寶掃碼賺錢支付寶套路

2020-11-06 07:35:09

微信支付支付寶

2013-11-28 11:15:43

微信支付寶支付戰(zhàn)爭(zhēng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)