支付宝支付功能接入(SprinBoot-网站支付)

目录

 

概述

接入过程

一、前期准备

二、创建应用并配置加签方式

 三、应用提交审核

四、签约/开通支付产品

五、支付API代码查看/编写

 六、实际开发

测试

支付测试

回调测试

订单查询测试

演示代码地址(Gitee)


 

概述

  • 本文章介绍如何接入支付宝支付功能

  • 使用SpringBoot作为后端代码提供示例,前端使用Vue3做表单提交测试。

  • 本文仅演示“电脑网站支付”这一种支付方式,但是其他支付方式如“JSAPI支付”,“手机网站支付”等等接入过程基本一样,看完本文应当可以一通百通。

接入过程

一、前期准备

首先需要注册/登录支付宝的“开放平台”和“商家平台”。

1.开放平台(open.alipay.com):用于创建应用并获取APPID和密钥等信息。

2.商家平台(b.alipay.com):用于设置收款信息和支付产品的签约等操作。

 

二、创建应用并配置加签方式

1. 我们需要到“开放平台”创建一个应用,点击右上角“控制台

 2. 创建自己需求类型的应用,本文使用网站支付测试,因此选择“网页/移动应用”。

 3. 填写应用基本信息

值得注意的是应用名称不要带有"测试"或"test"之类的字样,否则应用提交审核通不过的。

 4. 设置“接口加签方式”

进入刚创建的应用中,在“开发设置”里找到“接口加签方式”进行设置。

完成加签方式的设置之后,我们会拿到调用支付接口时的参数“应用私钥”,“支付宝公钥”...

点击设置后,按照表单提示一步步来: 

 

 生成应用密钥后自己妥善保管,将“应用公钥”复制回传到刚才的位置。

 上传后我们就可以得到参数“支付宝公钥

至此,我们就已经拿到了调用支付接口时的一些参数“应用的公私钥”,“支付宝公钥”。 

 三、应用提交审核

在我们设置了接口加签方式之后,就可以先提交审核了。

如果不提交审核可能会导致支付接口调用失败,提示错误"isv.invalid-app-id"。

 

四、签约/开通支付产品

在应用审核期间,我们可以到“商家平台(b.alipay.com)”去开通我们对应的支付产品。

例如本文演示使用的网站支付,所以我们就应该点击“电脑网站支付”这个产品进行签约开通。

开通信息按要求填写提交申请等待一下基本就审核好了。

如果没有签约开通产品,会提示错误:ISV权限不足....

五、支付API代码查看/编写

在我们的应用审核成功并且支付产品签约开通成功之后,我们就可以进行代码的编写了。

在编写之前,我们需要先了解支付宝支付API的介绍。

我们进入支付产品最下面找到开放文档

在开发文档中会看到官方提供的开发SDK和API列表

以Java为例,我们能找到Maven依赖包的描述。

  点击“API列表”能看到SDK使用的代码示例和请求和响应参数的说明 

 六、实际开发

在此仅演示“下单支付”,“订单查询”和“支付成功回调”的代码。

其他功能代码请参考官网“API列表”进行编写。

 如下仅贴出关键性代码,完整代码调用链请到最后的"Gitee演示Demo代码"处获取查看。

@RestController
@RequestMapping("/alipay")
@Api("支付")
@CrossOrigin
public class AliPayController {
    private static final String GATEWAY_URL = "https://openapi.alipay.com/gateway.do";
    @Autowired
    private AliPayConfig aliPayConfig;

    /**
     *
     * @param aliPay 支付请求参数
     * @param httpResponse ·
     * @throws Exception ·
     */
    @GetMapping("/pay") // &subject=xxx&traceNo=xxx&totalAmount=xxx
    public void pay(AliPay aliPay, HttpServletResponse httpResponse) throws Exception {
        AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(), FORMAT_JSON, CHARSET_UTF8, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE_RSA2);
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl(aliPayConfig.getNotifyUrl());
        request.setBizContent("{\"out_trade_no\":\"" + aliPay.getTraceNo() + "\","
                + "\"total_amount\":\"" + aliPay.getTotalAmount() + "\","
                + "\"subject\":\"" + aliPay.getSubject() + "\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        String form = "";
        try {
            form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        httpResponse.setContentType("text/html;charset=" + CHARSET_UTF8);
        httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
    }

    /**
     * 订单查询
     * @param out_trade_no 商户订单号
     * @param trade_no 支付宝交易订单号
     * @throws AlipayApiException ·
     */
    @GetMapping("/queryOrder/{out_trade_no}/{trade_no}")
    public void queryOrder(@PathVariable("out_trade_no") String out_trade_no, @PathVariable("trade_no") String trade_no) throws AlipayApiException {
        // 初始化SDK
        AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(), FORMAT_JSON, CHARSET_UTF8, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE_RSA2);
        // 构造请求参数以调用接口
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();

        // 设置订单支付时传入的商户订单号
        model.setOutTradeNo(out_trade_no);

        // 设置支付宝交易号
        model.setTradeNo(trade_no);

        // 设置查询选项
        List<String> queryOptions = new ArrayList<String>();
        /*
         * 交易结算信息: trade_settle_info
         * 交易支付使用的资金渠道: fund_bill_list
         * 交易支付时使用的所有优惠券信息: voucher_detail_list
         * 交易支付使用单品券优惠的商品优惠信息: discount_goods_detail
         * 商家优惠金额: mdiscount_amount
         * 医保信息: medical_insurance_info
         */
        queryOptions.add("trade_settle_info"); // 交易结算信息
        model.setQueryOptions(queryOptions);

        request.setBizModel(model);

        AlipayTradeQueryResponse response = alipayClient.execute(request);
        System.out.println("===>订单信息:\n" + response.getBody());

        if (response.isSuccess()) {
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
            // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
            // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
            // System.out.println(diagnosisUrl);
        }

    }

    @PostMapping("/notify")  // 注意这里必须是POST接口
    public String payNotify(HttpServletRequest request) throws Exception {
        if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("=========支付成功回调========");

            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                params.put(name, request.getParameter(name));
                // System.out.println(name + " = " + request.getParameter(name));
            }
            // 支付宝验签
            if (Factory.Payment.Common().verifyNotify(params)) {
                // 验签通过
                System.out.println("交易名称: " + params.get("subject"));
                System.out.println("交易状态: " + params.get("trade_status"));
                System.out.println("支付宝交易凭证号(trade_no): " + params.get("trade_no"));
                System.out.println("商户订单号(out_trade_no): " + params.get("out_trade_no"));
                System.out.println("交易金额: " + params.get("total_amount"));
                System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
                System.out.println("买家付款时间: " + params.get("gmt_payment"));
                System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));
            }
        }
        return "success";
    }
}

 实际上,如上的所有流程描述皆在官方的文档中,但是为什么还是有许多人“博客文章”优先,以至于出现“收费博文浏览”的博文设定,也许是当下人们丢失了沉稳的耐心。

测试

支付测试

前端测试表单代码: 

<template>
  <div id="alipay_container">
    <h1>支付宝支付测试</h1>
    <form @submit.prevent="submitPayment">
      <div class="form-group">
        <label for="alipayTraceNo">支付宝交易号:</label>
        <input type="text" id="alipayTraceNo" v-model="form.alipayTraceNo" required />
      </div>
      <div class="form-group">
        <label for="subject">支付说明:</label>
        <input type="text" id="subject" v-model="form.subject" required />
      </div>
      <div class="form-group">
        <label for="totalAmount">支付金额:</label>
        <input type="number" id="totalAmount" v-model="form.totalAmount" step="0.01" required />
      </div>
      <div class="form-group">
        <label for="traceNo">订单号:</label>
        <input type="text" id="traceNo" v-model="form.traceNo" required />
      </div>
      <button type="submit" class="submit-btn">提交支付</button>
    </form>
  </div>
</template>

<script setup>
import axios from 'axios';

// 表单数据
const form = {
  alipayTraceNo: 'alipayTraceNo20241215ss1a1',
  subject: '支付测试',
  totalAmount: 0.1,
  traceNo: 'traceNo20241215122100000',
};

// 提交支付请求
const submitPayment = async () => {
  const url = `http://IP:9075/alipay/pay?alipayTraceNo=${form.alipayTraceNo}&subject=${encodeURIComponent(form.subject)}&totalAmount=${form.totalAmount}&traceNo=${form.traceNo}`;
  try {
    const response = await axios.get(url, { responseType: 'text' }); // 接收 HTML 表单作为字符串
    const tempDiv = document.createElement('div');
    tempDiv.innerHTML = response.data; // 插入返回的 HTML 表单
    document.body.appendChild(tempDiv);
    tempDiv.querySelector('form').submit(); // 自动提交表单
  } catch (error) {
    alert(`支付请求失败:${error.message}`);
  }
};

</script>

<style scoped>
...已省略
<style>

1. 点击“提交支付”

 

手机扫码支付,或点击右侧的“登录支付宝”进行支付。

回调测试

在各种支付的回调测试中,老生常谈的提示:把回调地址设置为公网可访问的地址

否则支付成功后没有办法通知到程序做订单后续处理。

在支付成功后查看日志打印:

发现支付成功后,回调地址成功被调用,并且返回了支付宝交易订单号。

 

订单查询测试

在支付成功之后,我们一般在回调中将“支付宝交易订单号”存储起来,可用于后续的订单查询。

关于开发文档对于查询订单接口的参数描述:

 

调用接口,查看日志:

结果复合预期,测试完毕。

演示代码地址(Gitee)

Gitee地址:gitee.com/maohe101/alipay_test/tree/master/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mao.O

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值