Springboot+MyBatisPlus+Mysql+vue实现支付宝支付


前言

本篇通过使用Springboot+MybatisPlus+Mysql+vue实现支付宝网页支付,下单,下单通知,查询订单,关闭订单,退款等功能,前端使用单页面的Vue+elementui+axiso发送请求后台。对账估计是沙箱环境的原因提示账单不存在.


一、支付宝沙箱环境准备

1)注册支付宝开发者账号。
2)注册完成登录之后进入控制台->沙箱-》创建应用。
3)配置应用环境(支付宝公钥,应用私钥,应用公钥,支付宝网关地址,配置接口内容加密方式,查看APPID)。详细的配置过程这里就不多介绍。

二、开发环境搭建

1.数据库脚本执行。

-- test.`order` definition

CREATE TABLE `order` (
  `order_id` int NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_no` varchar(50) DEFAULT NULL COMMENT '订单号',
  `product_id` int DEFAULT NULL COMMENT '产品id',
  `user_id` int DEFAULT NULL COMMENT '用户ID',
  `order_num` int DEFAULT NULL COMMENT '订单产品数量',
  `order_amt` decimal(10,2) DEFAULT NULL COMMENT '订单金额',
  `order_status` varchar(10) DEFAULT NULL COMMENT '订单状态',
  `pay_status` varchar(10) DEFAULT NULL COMMENT '支付状态',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  `create_user` varchar(10) DEFAULT NULL COMMENT '创建人',
  `update_user` varchar(10) DEFAULT NULL COMMENT '更新人',
  `code_url` varchar(100) DEFAULT NULL COMMENT '订单二维码连接',
  `pay_type` varchar(10) DEFAULT NULL COMMENT '支付类别',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='订单表';


-- test.payment_info definition

CREATE TABLE `payment_info` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '支付记录id',
  `order_no` varchar(50) DEFAULT NULL COMMENT '商户订单编号',
  `transaction_id` varchar(50) DEFAULT NULL COMMENT '支付系统交易编号',
  `payment_type` varchar(20) DEFAULT NULL COMMENT '支付类型',
  `trade_type` varchar(20) DEFAULT NULL COMMENT '交易类型',
  `trade_state` varchar(50) DEFAULT NULL COMMENT '交易状态',
  `payer_total` decimal(10,0) DEFAULT NULL COMMENT '支付金额()',
  `content` text COMMENT '通知参数',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


-- test.product definition

CREATE TABLE `product` (
  `product_id` int NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `product_name` varchar(100) DEFAULT NULL COMMENT '商品名称',
  `product_type` varchar(10) DEFAULT NULL,
  `product_price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
  `create_by` varchar(100) DEFAULT NULL COMMENT '创建人',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  `update_by` varchar(100) DEFAULT NULL COMMENT '更新人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`product_id`),
  KEY `product_product_type_IDX` (`product_type`,`product_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表';


-- test.refund_info definition

CREATE TABLE `refund_info` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '退款单id',
  `order_no` varchar(50) DEFAULT NULL COMMENT '商户订单编号',
  `refund_no` varchar(50) DEFAULT NULL COMMENT '商户退款单编号',
  `refund_id` varchar(50) DEFAULT NULL COMMENT '支付系统退款单号',
  `total_fee` decimal(10,2) DEFAULT NULL COMMENT '原订单金额()',
  `refund` decimal(10,2) DEFAULT NULL COMMENT '退款金额()',
  `reason` varchar(50) DEFAULT NULL COMMENT '退款原因',
  `refund_status` varchar(10) DEFAULT NULL COMMENT '退款状态',
  `content_return` text COMMENT '申请退款返回参数',
  `content_notify` text COMMENT '退款结果通知参数',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


-- test.stock definition

CREATE TABLE `stock` (
  `stock_id` int NOT NULL AUTO_INCREMENT COMMENT '库存主键',
  `product_id` int NOT NULL COMMENT '商品ID',
  `stock_num` int DEFAULT NULL COMMENT '库存数量',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `create_by` varchar(100) DEFAULT NULL COMMENT '创建人',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  `update_by` varchar(100) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`stock_id`),
  KEY `stock_product_id_IDX` (`product_id`) USING BTREE,
  CONSTRAINT `stock_FK` FOREIGN KEY (`product_id`) REFERENCES `product` (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存表';

2 依赖添加

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.elite.springboot</groupId>
    <artifactId>Springboot16WechatAliPay</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--一引入alipay sdk-->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.22.0.ALL</version>
        </dependency>
        <!--引入微信支付api-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.3.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!--druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.4</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3 配置支付宝支付相关参数

##支付宝支付配置信息
alipayconfig:
  # 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号,开发时使用沙箱提供的APPID,生产环境改成自己的APPID
  app_id: APPID
  # 商户私钥,您的PKCS8格式RSA2私钥
  privateKey: 商户私钥
  # 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
  publicKey: 支付宝公钥
  # 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
  notify_url:  https://38bc-183-131-162-121.jp.ngrok.io/springboot/alipay/notify
  # 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问(其实就是支付成功后返回的页面)
  return_url:页面跳转同步通知页面路
  # 返回格式
  format: json
  #  签名方式
  sign_type: RSA2
  # 字符编码格式
  charset: utf-8
  # 支付宝网关,这是沙箱的网关
  gatewayUrl: https://openapi.alipaydev.com/gateway.do
  # 支付宝网关
  logPath: C:\\

4. 配置支付宝客户端类

package com.elite.springboot.config;

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * 支付宝支付配置类
 */
@Configuration
@ConfigurationProperties(prefix = "alipayconfig")
@Data
public class AlipayConfig {

    // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号,开发时使用沙箱提供的APPID,生产环境改成自己的APPID
    private String app_id;
    // 商户私钥,您的PKCS8格式RSA2私钥
    private String privateKey;
    // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
    private String publicKey;
    // 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    private String notify_url;
    // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问(其实就是支付成功后返回的页面)
    private String return_url;
    //返回格式
    private String format;
    // 签名方式
    private String sign_type;
    // 字符编码格式
    private String charset;
    // 支付宝网关,这是沙箱的网关
    private String gatewayUrl;
    // 支付宝网关
    private String logPath;

    /**
     * 获取alipayclient
     */
    @Bean(name="alipayClient")
    public AlipayClient getAlipayClient() {
        //调用RSA签名方式
        AlipayClient client = new DefaultAlipayClient(gatewayUrl, app_id,
                privateKey,format, charset, publicKey, sign_type);
//        AlipayTradeWapPayRequest alipay_request = new AlipayTradeWapPayRequest();
//
//        // 封装请求支付信息
//        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
//        model.setOutTradeNo(out_trade_no);
//        model.setSubject(subject);
//        model.setTotalAmount(total_amount);
//        model.setBody(body);
//        model.setTimeoutExpress(timeout_express);
//        model.setProductCode(product_code);
//        alipay_request.setBizModel(model);
//        // 设置异步通知地址
//        alipay_request.setNotifyUrl(AlipayConfig.notify_url);
//        // 设置同步地址
//        alipay_request.setReturnUrl(AlipayConfig.return_url);
        return client;

    }
}

三、支付宝支付api接口

1.下单接口

支付时序图

支付时序图
下单请求代码示例:

@Transactional(rollbackFor = Exception.class)
    @Override
    public R createOrder(Integer product_id) {

        try{
            log.info("创建订单开始......");
            //创建订单
            Order orderInfo = orderService.createOrderByProductId(product_id, PayTypeEnum.ALIPAY.getPayType());
            log.info("创建订单结束:"+orderInfo.toString());
            log.info("调用支付开始.....");
            //调用支付宝接口
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            //配置需要的公共请求参数
            request.setNotifyUrl(alipayConfig.getNotify_url());
            //支付完成后配置returnUrl
            request.setReturnUrl(alipayConfig.getReturn_url());
            /**
             * out_trade_no	String	必选	64	商户订单号。由商家自定义,64个字符以内,仅支持字母、数字、下划线且需保证在商户端不重复。	20150320010101001
             * total_amount	Price	必选	11	订单总金额,单位为元,精确到小数点后两位,取值范围为 [0.01,100000000]。金额不能为0。	88.88
             * subject	String	必选	256	订单标题。注意:不可使用特殊字符,如 /,=,& 等。	Iphone6 16G
             * product_code	String	必选	64	销售产品码,与支付宝签约的产品码名称。注:目前电脑支付场景下仅支持FAST_INSTANT_TRADE_PAY	FAST_INSTANT_TRADE_PAY
             */
            //组装当前业务方法的请求参数
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderInfo.getOrderNo());
            bizContent.put("total_amount", orderInfo.getOrderAmt());
            bizContent.put("subject", productService.getById(product_id).getProductName());
            bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
            request.setBizContent(bizContent.toString());
            //执行请求,调用支付宝接口
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            log.info("调用支付结束:"+response.getBody());
            if(response.isSuccess()){
                log.info("调用成功,返回结果 ===> " + response.getBody());
                return R.ok(response.getBody());
            } else {
                log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
                throw new RuntimeException("创建支付交易失败");
                //return R.fail(400,"创建支付交易失败:"+response.getMsg());
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("创建支付交易失败");
            //return R.fail(400,"创建支付交易失败:"+e.getErrMsg());
        }
    }

2 支付通知回调处理订单

  /**
     * 支付通知
     */
    @PostMapping("/notify")
    public String notify(@RequestParam Map<String, String> params){
        String result = "failure";
        try {
            //异步通知验签
            boolean signVerified = AlipaySignature.rsaCheckV1(
                    params,
                    alipayConfig.getPublicKey(),
                    AlipayConstants.CHARSET_UTF8,
                    AlipayConstants.SIGN_TYPE_RSA2); //调用SDK验证签名
            if(!signVerified){
               //验签失败则记录异常日志,并在response中返回failure.
                log.error("支付成功异步通知验签失败!");
                return result;
            }
            // 验签成功后
            log.info("支付成功异步通知验签成功!");
            //按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,
            //1 商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号
            String outTradeNo = params.get("out_trade_no");
            Order order = orderService.getOrderByOrderNo(outTradeNo);
            if(order == null){
                log.error("订单不存在");
                return result;
            }
            //2 判断 total_amount 是否确实为该订单的实际金额(即商户订单创建时的金额)
            String totalAmount = params.get("total_amount");
            int totalAmountInt = new BigDecimal(totalAmount).intValue();
            int totalFeeInt = order.getOrderAmt().intValue();
            if(totalAmountInt != totalFeeInt){
                log.error("金额校验失败");
                return result;
            }
            //3 校验通知中的 seller_id(或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方
            /*String sellerId = params.get("seller_id");
            String sellerIdProperty = config.getProperty("alipay.seller-id");
            if(!sellerId.equals(sellerIdProperty)){
                log.error("商家pid校验失败");
                return result;
            }*/
            //4 验证 app_id 是否为该商户本身
            String appId = params.get("app_id");
            String appIdProperty = alipayConfig.getApp_id();
            if(!appId.equals(appIdProperty)){
                log.error("appid校验失败");
                return result;
            }
            //在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS时,
            // 支付宝才会认定为买家付款成功。
            String tradeStatus = params.get("trade_status");
            if(!"TRADE_SUCCESS".equals(tradeStatus)){
                log.error("支付未成功");
                return result;
            }
            //处理业务 修改订单状态 记录支付日志
            aliPayService.processOrder(params);
            //校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure
            result = "success";
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return result;
    }

3.支付失败关闭订单


    /**
     * 取消订单业务
     * @param orderNo
     */
    private boolean closeOrder(String orderNo) {
        /**
         * trade_no	String	特殊可选	64	该交易在支付宝系统中的交易流水号。最短 16 位,最长 64 位。和out_trade_no不能同时为空,如果同时传了 out_trade_no和 trade_no,则以 trade_no为准。	2013112611001004680073956707
         * out_trade_no	String	特殊可选	64	订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 trade_no,out_trade_no如果同时存在优先取trade_no	HZ0120131127001
         * operator_id	String	可选	28	商家操作员编号 id,由商家自定义。	YX01
         */
        try {
            log.info("关单接口的调用,订单号 ===> {}", orderNo);
            AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);
            log.info("调用取消订单的的入参:"+bizContent.toString());
            request.setBizContent(bizContent.toString());
            AlipayTradeCloseResponse response = alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功,返回结果 ===> " + response.getBody());
                return true;
            } else {
                log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
                //throw new RuntimeException("关单接口的调用失败");
                return false;
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("关单接口的调用失败");
        }
    }

4.订单退款

  @Transactional(rollbackFor = Exception.class)
    @Override
    public void refund(String orderNo, String reason) {
        try {
            log.info("调用退款API");
            //创建退款单
            RefundInfo refundInfo = refundsInfoService.createRefundByOrderNoForAliPay(orderNo, reason);
            //调用统一收单交易退款接口
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest ();
            //组装当前业务方法的请求参数
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);//订单编号
            BigDecimal refund = new
                    BigDecimal(refundInfo.getRefund().toString());
            //BigDecimal refund = new BigDecimal("2").divide(new BigDecimal("100"));
            bizContent.put("refund_amount", refund);//退款金额:不能大于支付金额
            bizContent.put("refund_reason", reason);//退款原因(可选)
            request.setBizContent(bizContent.toString());
            log.info("退款API的入参:"+bizContent.toString());
            //执行请求,调用支付宝接口
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功,返回结果 ===> " + response.getBody());
                //更新订单状态
                orderService.updateStatusByOrderNo(orderNo,OrderStatus.REFUND_SUCCESS);
                //更新退款单
                refundsInfoService.updateRefundForAliPay(
                        refundInfo.getRefundNo(),
                        response.getBody(),
                        OrderStatus.REFUND_SUCCESS.getType()); //退款成功
            } else {
                 log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> "
                        + response.getMsg());
                //更新订单状态
                orderService.updateStatusByOrderNo(orderNo,
                        OrderStatus.REFUND_ABNORMAL);
                //更新退款单
                refundsInfoService.updateRefundForAliPay(
                        refundInfo.getRefundNo(),
                        response.getBody(),
                        OrderStatus.REFUND_ABNORMAL.getType()); //退款失败
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("创建退款申请失败");
        }
    }

5、查询订单

 /**
     * 查询订单
     * @param orderNo
     * @return 返回订单查询结果,如果返回null则表示支付宝端尚未创建订单
     */
    @Override
    public String queryOrder(String orderNo) {
        /**
         * out_trade_no	String	特殊可选	64	订单支付时传入的商户订单号,和支付宝交易号不能同时为空。
         * trade_no,out_trade_no如果同时存在优先取trade_no	20150320010101001
         * trade_no	String	特殊可选	64	支付宝交易号,和商户订单号不能同时为空	2014112611001004680 073956707
         * org_pid	String	可选	16	银行间联模式下有用,其它场景请不要使用;
         * 双联通过该参数指定需要查询的交易所属收单机构的pid;	2088101117952222
         * query_options	String[]	可选	1024	查询选项,商户传入该参数可定制本接口同步响应额外返回的信息字段,数组格式。支持枚举如下:trade_settle_info:返回的交易结算信息,包含分账、补差等信息;
         * fund_bill_list:交易支付使用的资金渠道;
         * voucher_detail_list:交易支付时使用的所有优惠券信息;
         * discount_goods_detail:交易支付所使用的单品券优惠的商品优惠信息;
         * mdiscount_amount:商家优惠金额;	trade_settle_info
         */
        try {
            log.info("查单接口调用 ===> {}", orderNo);
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderNo);
            request.setBizContent(bizContent.toString());
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功,返回结果 ===> " + response.getBody());
                return response.getBody();
            } else {
                log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
                //throw new RuntimeException("查单接口的调用失败");
                return null;//订单不存在
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("查单接口的调用失败");
        }
    }

6.对账

 @Override
    public String queryBill(String billDate, String type) {
        /**
         * bill_type	String	必选	20	账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型,支持:
         * trade:商户基于支付宝交易收单的业务账单;
         * signcustomer:基于商户支付宝余额收入及支出等资金变动的账务账单。	trade
         * bill_date	String	必选	15	账单时间:
         * * 日账单格式为yyyy-MM-dd,最早可下载2016年1月1日开始的日账单。不支持下载当日账单,只能下载前一日24点前的账单数据(T+1),当日数据一般于次日 9 点前生成,特殊情况可能延迟。
         * * 月账单格式为yyyy-MM,最早可下载2016年1月开始的月账单。不支持下载当月账单,只能下载上一月账单数据,当月账单一般在次月 3 日生成,特殊情况可能延迟。	2016-04-05
         * smid	String	可选	20	二级商户smid	2088123412341234
         * AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2");
         * AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();
         * request.setBizContent("{" +
         * "  \"bill_type\":\"trade\"," +
         * "  \"bill_date\":\"2016-04-05\"," +
         * "  \"smid\":\"2088123412341234\"" +
         * "}");
         * AlipayDataDataserviceBillDownloadurlQueryResponse response = alipayClient.execute(request);
         * if(response.isSuccess()){
         * System.out.println("调用成功");
         * } else {
         * System.out.println("调用失败");
         * }
         */
        try {
            log.info("账单类型:"+type+",日期:"+billDate);
            AlipayDataDataserviceBillDownloadurlQueryRequest request = new
                    AlipayDataDataserviceBillDownloadurlQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("bill_type", type);
            bizContent.put("bill_date", billDate);
            bizContent.put("smid","2088123412341234");
            request.setBizContent(bizContent.toString());

            AlipayDataDataserviceBillDownloadurlQueryResponse response = alipayClient.execute(request);
            if(response.isSuccess()){
                log.info("调用成功,返回结果 ===> " + response.getBody());
                //获取账单下载地址
                Gson gson = new Gson();
                HashMap<String, LinkedTreeMap> resultMap =
                        gson.fromJson(response.getBody(), HashMap.class);
                LinkedTreeMap billDownloadurlResponse =
                        resultMap.get("alipay_data_dataservice_bill_downloadurl_query_response");
                String billDownloadUrl =
                        (String)billDownloadurlResponse.get("bill_download_url");
                return billDownloadUrl;
            } else {
                log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> "
                        + response.getMsg());
                throw new RuntimeException("申请账单失败");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new RuntimeException("申请账单失败");
        }
    }

四.前端

1.下单页面

商品列表

2.订单列表

订单列表可以操作退款。
订单列表

3.对账页面

账单列表

总结

本篇通过一个简单简单的前端页面+springboot+mybatisplus实现支付宝支付,了解支付宝api接口的调用以及简单的下单业务逻辑。
注意:支付宝的回调地址必须外网可以访问,需要使用ngrok进行外网映射,这个工具比较简单。

  • 5
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
实现批量删除,可以按照以下步骤进行: 1. 前端页面中,勾选需要删除的数据,点击“删除”按钮,将所选数据的id封装成一个数组,通过axios发送到后端。 2. 后端接收前端传来的id数组,根据id数组中的id,从数据库中删除对应数据。 3. 在SpringBoot中,可以使用@DeleteMapping注解来接收前端发送的DELETE请求,并在方法体中实现删除操作。在MyBatisPlus中,可以使用wrapper进行批量删除操作。 4. 最后,后端返回删除结果给前端。 下面是一个简单的代码示例: 前端代码: ``` <template> <div> <el-table :data="tableData" style="width: 100%"> <el-table-column type="selection"></el-table-column> <el-table-column prop="name" label="姓名"></el-table-column> <el-table-column prop="age" label="年龄"></el-table-column> <el-table-column prop="gender" label="性别"></el-table-column> <el-table-column fixed="right" label="操作"> <template slot-scope="scope"> <el-button type="danger" size="mini" @click="handleDelete(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> <el-button type="danger" @click="batchDelete">批量删除</el-button> </div> </template> <script> import axios from 'axios'; export default { data() { return { tableData: [], // 表格数据 multipleSelection: [], // 多选数组 }; }, methods: { // 删除单条数据 handleDelete(id) { axios.delete('/api/delete/' + id).then((res) => { this.getTableData(); }); }, // 批量删除 batchDelete() { const ids = this.multipleSelection.map((item) => item.id); axios.delete('/api/batchDelete', { data: ids, }).then((res) => { this.getTableData(); this.multipleSelection = []; }); }, // 获取表格数据 getTableData() { axios.get('/api/getTableData').then((res) => { this.tableData = res.data; }); }, }, created() { this.getTableData(); }, }; </script> ``` 后端代码: ``` @RestController @RequestMapping("/api") public class UserController { @Autowired private UserService userService; // 删除单条数据 @DeleteMapping("/delete/{id}") public boolean delete(@PathVariable("id") Long id) { return userService.removeById(id); } // 批量删除 @DeleteMapping("/batchDelete") public boolean batchDelete(@RequestBody List<Long> ids) { return userService.removeByIds(ids); } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小刘同学要加油呀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值