支付宝网页支付流程与实现


支付宝网站支付官方文档: https://docs.open.alipay.com/270

一、运行官方Demo

官方文档:https://opendocs.alipay.com/open/270/106291

首先要在控制台创建应用,创建完成后,可以得到 appId,后面需要在代码中配置:
创建应用流程官方文档:https://opendocs.alipay.com/open/200/105310

B站雷神的视频:https://www.bilibili.com/video/BV1np4y1C7Yf?p=301

二、时序图

在这里插入图片描述
这里没有使用第7步,只使用的第8步

三、请求与响应参数

全部的参数:

这里只介绍一下使用到的必填的参数:

1. 过程1.1的请求参数与响应参数

公共请求参数:

  • app_id:必填,支付宝分配给开发者的应用ID
  • method:接口名称
  • return_url:HTTP/HTTPS开头字符串,时序图中第六步回调的地址
  • charset:请求使用的编码格式,如utf-8,gbk,gb2312等
  • sign_type:商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2,这里使用的是RSA2
  • sign:商户请求参数的签名串
  • timestamp:时间戳,发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
  • version:调用的接口版本,固定为:1.0
  • notify_url:支付宝服务器主动通知商户服务器里指定的页面http/https路径。要求可以在公网上访问的地址

业务请求参数:

  • out_trade_no:商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复
  • product_code:仅支持"FAST_INSTANT_TRADE_PAY"
  • total_amount:订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]。
  • subject:订单标题,

响应参数(过程6的同步返回请求参数):

  • code:通信标识,10000表示通信成功
  • out_trade_no:商户订单号
  • total_amount:交易金额

2. 过程8中的请求参数与响应参数

公共请求参数同上,业务请求参数有:

  • out_trade_no:订单支付时传入的商户订单号,和支付宝交易号不能同时为空。
  • trade_no:支付宝交易号,和商户订单号不能同时为空

响应参数:

  • code:通信标识,10000表示通信成功
  • trade_status:交易状态,有四种情况:
    • WAIT_BUYER_PAY(交易创建,等待买家付款)
    • TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)
    • TRADE_SUCCESS(交易支付成功)
    • TRADE_FINISHED(交易结束,不可退款)

四、配置类

1. 引入依赖

<!--日志依赖-->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
</dependency>
<!--支付宝开发软件包依赖-->
<!--https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java-->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.16.44.ALL</version>
</dependency>

2. 配置一个配置类

配置类用来指定公共的请求参数

要改的参数:

  • 私钥与公钥的生成方法:https://docs.open.alipay.com/291/105971
  • appid:注册支付宝开放平台就有(https://opendocs.alipay.com/open/200/105310
  • notify_url:异步通知页面路径,也可以不指定
  • return_url:同步通知页面路径,也可以不指定,这里采用的方式是收到同步通知的请求后,调用支付宝的统一收单线下交易查询接口,确认订单是否完成
  • gatewayUrl :支付宝网关,开发的接口需要在alipay后面加上dev,即alipaydev
public class AlipayConfig {

//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
    public static String app_id = "111111111";

    // 商户私钥,您的PKCS8格式RSA2私钥
    public static String merchant_private_key = "私钥";

    // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
    public static String alipay_public_key = "公钥";

    // 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
	public static String notify_url = "http://localhost:9090/notify_url.jsp";

    // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    public static String return_url = "http://localhost:9090/p2p/loan/back";

    // 签名方式
    public static String sign_type = "RSA2";

    // 字符编码格式
    public static String charset = "utf-8";

    // 支付宝网关
    public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";

    // 支付宝网关
    public static String log_path = "C:\\";


//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    /**
     * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
     * @param sWord 要写入日志里的文本内容
     */
    public static void logResult(String sWord) {
        FileWriter writer = null;
        try {
            writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
            writer.write(sWord);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

五、实现

由于支付这个功能是公共的,所以把这个功能单独提出来做为一个pay模块,而页面是在web模块,从web访问pay的两种方式:

  • 直接在后台重定向到pay中的地址,缺点:只能发送get请求,在浏览器地址栏中中会看到参数一闪而过
  • 先请求转发到一个web中的页面,该页面在加载完成后,自动访问pay模块中的地址(使用form表单发送post请求,隐藏域存放参数,js实现页面加载完毕后自动访问

1. web模块

后台接收到充值请求后,访问pay工程的Alipay方法,Alipay方法中访问了支付宝的统一收单下单并支付页面接口

/**
* @param rechargeMoney:充值金额
* @return:跳转到一个页面
*/
@RequestMapping("/loan/toAlipayRecharge")
public String  toAlipayRecharge(HttpServletRequest request,
                               Double rechargeMoney,
                               Model model) {
	model.addAttribute("out_trade_no",rechargeNo);
    model.addAttribute("total_amount", rechargeMoney);
    model.addAttribute("subject", rechargeRecord.getRechargeDesc());
    return "p2pToPay";
}                           

p2pToPay.html:在页面加载完成后,自动调用pay模块的方法

<form method="post" action="http://localhost:9094/pay/api/alipay">
    <input type="hidden" name="out_trade_no" th:value="${out_trade_no}">
    <input type="hidden" name="total_amount" th:value="${total_amount}">
    <input type="hidden" name="subject" th:value="${subject}">

</form>
<script>document.forms[0].submit()</script>

2. pay模块

pay模块的alipay方法,该模块的负责向支付宝统一收单下单并支付页面接口发起请求,获取到一个类似上面 p2pToPay.html 页面的字符串,将该字符串放到一个页面中,即可跳转到支付宝的付款页面

@RequestMapping("/api/alipay")
public String alipay(Model model,
                     String out_trade_no,
                     String  total_amount,
                     String subject) throws AlipayApiException {

    //获得初始化的AlipayClient,从AlipayConfig中获取公共请求参数
    AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

    //设置同步响应请求参数
    AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
    alipayRequest.setReturnUrl(AlipayConfig.return_url);
    //alipayRequest.setNotifyUrl(AlipayConfig.notify_url); 有需要的设,这里就不设置了

	//拼接请求的参数
    alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
            + "\"total_amount\":\""+ total_amount +"\","
            + "\"subject\":\""+ subject +"\","
            + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

    //请求
    String result = alipayClient.pageExecute(alipayRequest).getBody();

    //输出
    model.addAttribute("result", result);

    return "/payToAlipay";
}

这里的使用的是jsp页面,payToAlipay.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
	${result}
</body>
</html>

3. 同步返回

@RequestMapping("/loan/back")
public String alipayBack(HttpServletRequest request,
                         Model model,
                         String out_trade_no) throws Exception {
	
	//转换后的请求参数
    Map<String,String> params = new HashMap<String,String>();

	//请求参数
    Map<String,String[]> requestParams = request.getParameterMap();

	//这段代码将请求参数的String[]转为使用逗号隔开的String字符串
    for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
        String name = (String) iter.next();
        String[] values = (String[]) requestParams.get(name);
        String valueStr = "";
        for (int i = 0; i < values.length; i++) {
            valueStr = (i == values.length - 1) ? valueStr + values[i]
                    : valueStr + values[i] + ",";
        }
        //乱码解决,这段代码在出现乱码时使用
        valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
        params.put(name, valueStr);
    }
	
	//验证签名
    boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名

	if(signVerified){	
		Map<String ,Object > map = new HashMap<>();
        map.put("out_trade_no", out_trade_no);
		/*
		在这里调用通过httpClint调用支付宝统一收单线下交易查询接口,查询交易是否成功
		*/
		 // 创建httpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();

        // 创建http对象
        HttpPost httpPost = new HttpPost(url);
        /**
         * setConnectTimeout:设置连接超时时间,单位毫秒。
         * setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection
         * 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
         * setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。
         */
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
        httpPost.setConfig(requestConfig);

        // 封装请求参数
        packageParam(params, map);

        // 创建httpResponse对象
        CloseableHttpResponse httpResponse = null;

        String result = "";
        try {

            // 执行请求
            httpResponse = httpClient.execute(httpPost);

            // 获取返回结果
            if (httpResponse != null && httpResponse.getStatusLine() != null) {
                if (httpResponse.getEntity() != null) {
                    result = EntityUtils.toString(httpResponse.getEntity(), ENCODING);
                }
            }

        } finally {
            // 释放资源
            release(httpResponse, httpClient);
        }
        
        //result为响应回来的json字符串,根据查询结果做出相应的业务处理
	}

六、沙箱环境

官方文档:使用沙箱环境

使用沙箱环境可以在支付宝开发环境下完成一些主要功能和主要逻辑,比如使用沙箱环境进行支付,收款等

七、收单

订单在支付页,不支付,一直刷新,订单过期了才支付,订单状态改为已支付了,但是库存解锁了

  • 使用支付宝自动收单功能解决。只要一段时间不支付,就不能支付了

由于时延等问题。订单解锁完成,正在解锁库存的时候,异步通知才到

  • 订单解锁,手动调用收单

网络阻塞问题,订单支付成功的异步通知一直不到达

  • 查询订单列表时,ajax 获取当前未支付的订单状态,查询订单状态时,再获取一下支付宝此订单的状态

其它各种问题

  • 每天晚上闲时下载支付宝对账单,一一进行对账。

八、封装的Template与Vo

1. AlipayTemplate

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.atguigu.gulimall.order.vo.PayVo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "alipay")
@Component
@Data
public class AlipayTemplate {

    // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
    public String app_id;

    // 商户私钥,您的PKCS8格式RSA2私钥
    public String merchant_private_key;

    // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
    public String alipay_public_key;

    // 服务器[异步通知]页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    // 支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息
    public String notify_url;

    // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    //同步通知,支付成功,一般跳转到成功页
    public String return_url;

    // 签名方式
    private  String sign_type;

    // 字符编码格式
    private  String charset;

    //订单超时时间
    private String timeout = "1m";

    // 支付宝网关; https://openapi.alipaydev.com/gateway.do
    public String gatewayUrl;

    public  String pay(PayVo vo) throws AlipayApiException {

        //AlipayClient alipayClient = new DefaultAlipayClient(AlipayTemplate.gatewayUrl, AlipayTemplate.app_id, AlipayTemplate.merchant_private_key, "json", AlipayTemplate.charset, AlipayTemplate.alipay_public_key, AlipayTemplate.sign_type);
        //1、根据支付宝的配置生成一个支付客户端
        AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,
                app_id, merchant_private_key, "json",
                charset, alipay_public_key, sign_type);

        //2、创建一个支付请求 //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(return_url);
        alipayRequest.setNotifyUrl(notify_url);

        //商户订单号,商户网站订单系统中唯一订单号,必填
        String out_trade_no = vo.getOut_trade_no();
        //付款金额,必填
        String total_amount = vo.getTotal_amount();
        //订单名称,必填
        String subject = vo.getSubject();
        //商品描述,可空
        String body = vo.getBody();

        alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
                + "\"total_amount\":\""+ total_amount +"\","
                + "\"subject\":\""+ subject +"\","
                + "\"body\":\""+ body +"\","
                + "\"timeout_express\":\""+timeout+"\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

        String result = alipayClient.pageExecute(alipayRequest).getBody();

        //会收到支付宝的响应,响应的是一个页面,只要浏览器显示这个页面,就会自动来到支付宝的收银台页面
        System.out.println("支付宝的响应:"+result);

        return result;

    }
}

AlipayTemplate 对象中的属性可以直接在 application 配置文件中进行配置。

2. PayVo

@Data
public class PayVo {

    private String out_trade_no; // 商户订单号 必填
    private String subject; // 订单名称 必填
    private String total_amount;  // 付款金额 必填
    private String body; // 商品描述 可空
}
  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
支付宝实现Spring Boot HTML网页支付功能主要有以下几个步骤: 1. 注册支付宝开发者账号并创建应用:首先,我们需要在支付宝开放平台注册一个开发者账号,然后登录后台创建一个应用,获取到应用的App ID、应用私钥、支付宝公钥等必要的信息。 2. 引入支付宝SDK:将支付宝提供的SDK导入到项目中,可以通过Maven等工具进行依赖管理。 3. 配置支付宝支付相关参数:在Spring Boot的配置文件中配置支付宝支付相关的参数,包括应用ID、应用私钥、支付宝公钥、回调URL等。 4. 创建支付接口:在项目中创建一个支付接口,接口中包含支付金额、订单号等支付信息作为参数,并调用支付宝SDK的统一下单接口,获取到支付宝的表单数据。 5. 渲染支付页面:将支付宝返回的表单数据填入一个HTML页面,并提交表单到支付宝支付页面。 6. 处理支付回调:支付宝支付完成后,会通过异步通知的方式回调商户的回调URL,商户需要在回调URL接口中处理支付结果,包括验签、更新订单状态等。 7. 查询支付状态:商户可以通过调用支付宝的订单查询接口来获取订单的支付状态,以保证订单的安全和准确性。 8. 完善支付流程:可以根据具体需求进行其他相关功能的完善,如支付前的校验、订单支付成功后的通知等。 以上就是使用支付宝实现Spring Boot HTML网页支付功能的步骤,通过配置支付宝支付相关参数、集成支付宝SDK,再结合Spring Boot的开发优势,可以方便快捷地实现网页支付功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值