Bootstrap

支付宝沙箱支付(保姆级教程)

一、支付环境准备

先提一嘴几个重要的参数

  • 支付宝的公钥和私钥
  • 支付的网关
  • 支付的APPID

1、配置沙箱应用环境

1)打开支付宝开放平台,官网:https://open.alipay.com/
image.png
2)登录个人账户,然后点击控制台找到里面的沙箱
image.png
3)这里能够找到APPID支付宝网关地址密钥
image.png

注意:我们接入的时候最好选择自定义密钥,自定义密钥需要我们下载密钥工具

2、下载密钥工具

下载地址:https://opendocs.alipay.com/common/02kipk?pathHash=0d20b438
1)选择自己的操作系统下载即可,我这里是 Windows
image.png

2)下载完成后,生成密钥
image.png
3)生成应用公钥和应用私钥
image.png

3、配置接口加签方式

1)按步骤 查看 -> 复制刚刚生成的应用公钥到下图 -> 保存
image.png
2)然后我们就接入了支付宝开放平台
image.png

二、设置内网穿透环境

NATAPP:https://natapp.cn/

1、创建隧道

1)官网右上角注册,先注册账户
image.png
2)登录后,购买一个免费隧道
image.png
3)购买成功后,会在下图的最下面出现一条刚刚购买的数据
image.png

记住这里有个 authtoken,等下有用

2、修改服务器端口号

1)点击右侧的配置
image.png

2)改为你后端要提供服务的端口
image.png

3、下载客户端

1)点击右上角客户端,下载对应的版本
image.png

2)下载完成之后为下图的 natapp.exe
image.png

4、运行 natapp.exe

注意:不要直接双击启动
1)我们需要在目录上 输入 cmd,如下图
image.png
2)在命令行窗口中输入如下命令

natapp.exe authtoken=你的authtoken

image.png
3)启动成功如下图
image.png

image.png
注:这里的地址会因为网络环境的不同,每次启动该地址都会变化
其实这里就是将项目运行的地址代理到了内网穿透的地址,但是因为每次启动都会变化,所以还是建议使用 localhost:8081

三、实际后端开发

1、引入依赖

<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-easysdk -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.9.28.ALL</version>
</dependency>

2、服务端代码配置

接口环境调试接口是,开发者需要调整如下代码配置

1)创建沙箱支付配置文件

alipay:
  appId: 你的APPID
  merchantPrivateKey: 你的应用私钥
  alipayPublicKey: 你的应用公钥
  notifyUrl: http://内网穿透地址/api/alipay/notify
  returnUrl: http://member.zyz.com/memberOrder.html
  signType: RSA2
  charset: utf-8
  # 我的支付宝网关地址
  gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do

2)订单实体类

package com.wei.weiapicommon.model.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * 接口次数订单表
 * @TableName order
 */
@TableName(value ="`order`")
@Data
public class Order implements Serializable {
    /**
     * 订单Id
     */
    @TableId(type = IdType.ASSIGN_UUID)
    private Long id;

    /**
     * 用户Id
     */
    private Long userId;

    /**
     * 接口Id
     */
    private Long interfaceInfoId;

    /**
     * 支付金额
     */
    private Double money;

    /**
     * 支付方式
     */
    private String paymentMethod;

    /**
     * 0 - 未支付 1 - 已支付
     */
    private Integer status;

    /**
     * 创建时间
     */
    private Date createTime;

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

    /**
     * 是否删除
     */
    private Integer isDelete;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

这里为了方便测试,就不去调用数据库了

3)创建支付配置类,使用@Value注解将配置文件中的属性值映射到对应的属性中

package com.wei.project.manager;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.wei.weiapicommon.model.entity.Order;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


@ConfigurationProperties(prefix = "alipay")
@Component
@Data
public class AlipayTemplate {
    // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
    @Value("{alipay.appId}")

    public String appId;

    // 应用私钥,就是工具生成的应用私钥
    @Value("{alipay.merchantPrivateKey}")
    public String merchantPrivateKey;
    // 支付宝公钥,对应APPID下的支付宝公钥。
    @Value("{alipay.alipayPublicKey}")
    public String alipayPublicKey;

    // 支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息
    @Value("{alipay.notifyUrl}")
    public String notifyUrl;
    //同步通知,支付成功,一般跳转到成功页
    @Value("{alipay.returnUrl}")
    public String returnUrl;

    // 签名方式
    @Value("{alipay.signType}")
    private String signType;

    // 字符编码格式
    @Value("{alipay.charset}")
    private String charset;

    //订单超时时间
    private String timeout = "1m";
    // 支付宝网关;https://openapi-sandbox.dl.alipaydev.com/gateway.do
    @Value("{alipay.gatewayUrl}")
    public String gatewayUrl;

    public String pay(Order order) throws
            AlipayApiException {

        //1、根据支付宝的配置生成一个支付客户端
         AlipayClient alipayClient = new
                DefaultAlipayClient(gatewayUrl, appId, merchantPrivateKey,
                "json", charset, alipayPublicKey, signType);

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

        Long id = order.getId();
        Long interfaceInfoId = order.getInterfaceInfoId();
        Double money = order.getMoney();
        String paymentMethod = order.getPaymentMethod();


        alipayRequest.setBizContent(" {\"out_trade_no\":\"" + id + "\","
                + "\"total_amount\":\"" + money + "\","
                + "\"subject\":\"" + interfaceInfoId
                + "\","
                + "\"body\":\"" + paymentMethod + "\","
                +
                "\"timeout_express\":\"" + timeout + "\","
                +
                "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        String result = alipayClient.pageExecute(alipayRequest).getBody();
        //会收到支付宝的响应,响应的是一个页面,只要浏览器显示这个页面,就会自动来到支付宝的收银台页面
        System.out.println("支付宝的响应:" + result);
        return result;
    }
}

4)调用支付接口

package com.wei.project.controller;


import cn.hutool.core.bean.BeanUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.easysdk.factory.Factory;
import com.wei.project.manager.AlipayTemplate;
import com.wei.project.mapper.OrderMapper;
import com.wei.project.model.dto.order.OrderRequest;
import com.wei.project.test.paymentbox.PayVO;
import com.wei.weiapicommon.model.entity.Order;
import org.springframework.web.bind.annotation.*;

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.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * 支付宝接口
 */
@RestController
@RequestMapping("/alipay")
public class AliPayController {

    @Resource
    AlipayTemplate alipayTemplate;

    @GetMapping(value = "/pay", produces = "text/html")
    @ResponseBody
    public String pay(@RequestParam long id) throws AlipayApiException {
        Order order = new Order();
        order.setId(120948748372234L);
        order.setUserId(129904058947L);
        order.setInterfaceInfoId(294389472934L);
        order.setMoney(10.0);
        order.setPaymentMethod("支付宝");
        return alipayTemplate.pay(order);
    }

    @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));
            }

            String tradeNo = params.get("out_trade_no");
            String gmtPayment = params.get("gmt_payment");
            String alipayTradeNo = params.get("trade_no");
            // 支付宝验签
            if (Factory.Payment.Common().verifyNotify(params)) {
                // 验签通过
                System.out.println("交易名称: " + params.get("subject"));
                System.out.println("交易状态: " + params.get("trade_status"));
                System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
                System.out.println("商户订单号: " + 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";
    }
}

3、前端请求

1)前端通过传入订单 id 的方式去支付订单
注意:订单号必须唯一

  const toPayment = async () => {
    window.open("http://localhost:8081/api/alipay/pay?id=1")
  };

访问地址:http://localhost:8081/api/alipay/pay?id=100

2)弹出支付宝登录页面
image.png

3)来到支付宝开放平台,找到买家信息,登录进行支付
image.png

4)确认支付
image.png

5)支付成功
image.png

6)支付成功后,对比商家信息和买家信息中的账户余额
image.png

;