编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

实用干货 | 一步一步教你在SpringBoot中集成支付宝的当面付

wxchong 2025-01-29 18:24:33 开源技术 26 ℃ 0 评论

一:简介

当面付分为三种,不同的方式有着不同的使用场景,当面付的字面含义是面对面的付也就是买家当着卖家的面来付款。其中条码支付和扫码支付使用的最多,本文主要介绍这两种。

  • 当面付条码支付(商家使用扫码设备扫描买家支付宝的付款码):条码支付是支付宝给到线下传统行业的一种收款方式。商户使用扫码枪等条码识别设备扫描用户支付宝钱包上的条码/二维码,完成收款。一般在超市、便利店、店铺等使用。扫码支付(买家使用支付宝扫一扫扫描卖家的二维码):指用户打开支付宝钱包中的“扫一扫”功能,扫描商户针对每个订单实时生成的订单二维码,并在手机端确认支付。一般在类似于无人售货机上使用,像地铁中的无人售货饮料机、医院中的自助挂号收费机等声波支付

在接入之前首先要熟悉一下当面付产品的业务文档:

  • 当面付文档
  • 沙箱当面付接入引导
  • 快速接入

当面付SDK&Demo 拿到demo我们熟悉一下demo,就可以依葫芦画瓢了,集成到自己工程中来。

通过下载Demo可以看到有两个工程,一个是TradePayDemo,一个是TradePaySDK,集成SDK必须要使用
alipay-sdk-java20161213173952.jar,而TradePaySDK是对alipay-sdk-java.jar的进一步封装,在TradePayDemo中可以看到已经对TradePaySDK打成了jar包依赖进来了,当然也可以将整个TradePaySDK的src目录拖入到自己工程中, 当然也可以不使用alipay-trade-sdk.jar直接使用alipay-sdk-java.jar来开发。

集成当面付即可以参考TradePayDemo中的Main.java也可以参考WebRoot下的JSP,这里参考JSP中的代码来集成。 这里注意一点:jsp中的最上面的注释,所有的jsp文件都是一样的,作者粘贴了也不知道改一下,吐槽一下:这Demo写的真不细心

  • 条码付参考trade_pay.jsp
  • 扫码付参考trade_precreate.jsp
  • 退款参考trade_refund.jsp
  • 交易查询参考trade_query.jsp

条码支付流程图:

扫码支付流程图:

二:集成步骤

0. 创建应用、配置密钥

集成前需要先创建应用、配置密钥、回调地址等,具体操作请查看Spring Boot入门教程(三十五):支付宝集成-准备工作

1. 将Demo中的alipay-sdk-java和alipay-trade-sdk两个jar安装到本地maven仓库中

注意:将jar打到本地maven仓库时命令行最好是一行,不好有回车换行, 这里使用2017版本的,不是2016版本的,Demo中的还是2016的版本。

mvn install:install-file -DgroupId=com.alipay -DartifactId=alipay-sdk-java -Dversion= 20170725114550 -Dpackaging=jar -Dfile=alipay-sdk-java-20170725114550.jar
mvn install:install-file -DgroupId=com.alipay -DartifactId=alipay-trade-sdk -Dversion=20161215 -Dpackaging=jar -Dfile=alipay-trade-sdk-20161215.jar

2. 引入依赖



    com.alipay
    alipay-sdk-java
    20170725114550


    com.alipay
    alipay-trade-sdk
    20161215


    com.google.code.gson
    gson


    commons-configuration
    commons-configuration
    1.10


    com.google.zxing
    core
    3.2.1




    org.projectlombok
    lombok

3. 配置application.yml

注意:配置appPrivateKey和alipayPublicKey时值不要换行,returnUrl在当面付中不是必须的 注意:这里的returnUrl和notifyUrl在测试的时候需要外网能访问,可以通过natapp来实现

# 沙箱配置
pay:
  alipay:
    gatewayUrl: https://openapi.alipaydev.com/gateway.do
    appid: 2016091400508498
    appPrivateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtXKWs+trRSuCxEUvlsEeSAuLWs3B/Dh74R8223BnfzoA29aGeoycAqfKlBMcbzU2G1KayESvZKGpwBLeejSbecRYjgZsQDyEaDimQQJtGFvZVV6u4XNnvIJ72eQzEaEIQfuiorjBTLm6DQuds4R0GxftqON6QFoIZkWB9ZrZKd02cuy16dW+UqtLVGGAHcCIAkB63zUiKSNfweMddneZ7MVs3lvu3xhMnD+5us/+n2Vp4qhfmpYLcdqIW6InU4GypeoOpyUTrfUGpgdR0l924vHy/GQJZEKFaRcK3cYK+ECyKpSIoqaJJFLHbkqsliuPpMUG+rM3jiqeIAH4psAznAgMBAAECggEBAJ5jyEbbxrJzrAh7GhHX1fwUQPYSadTbrPYAfHX2cHlnrQMJtsk+nTLhEv0r+VJwZ8WpYkfMong8kcqYtL7ajcmsHqMAFhE9EWxBxj2ymWsXLabZe93sj30IG9Rq0nxcGQgDO0RqKWLGSFgK93Al2KRInKT3InkY53K+vR61ihVLmGf7+GwPtIZteBy+tuAyvcj2RlkYvjiFi3ySyZ1wA3sJIlgrGiixd6fj20XFGNE3CnAwu0BJgXXbP/S9J4C0RRa3ZXj8fX7oONhVxz2xKxn7AT4e8OWjt7J41H2LRct8Fgl9pqgz2FJYv/WfbkG8x9fGiKYYvPXoxjjrk/tkewkCgYEA8f9Lcu5JPrE9rpw9zlwhm5cOO81xLxdwL5R5/1bRP48BZGIYuqlCbVvjJVqtO8eTnLhUwH7fG8B7cmoeO9bGr9GQrtfyCqz6FtVymTBieJlfgZDVhtzyv2qKOBMIFE8jsbSBK/NHHMvykJ+XdQ1riwCeQDdXICRuYTTFwGk2OsUCgYEAt2SoN95tVmVrvKG6ATLNEtge/ozeVywA4GjltrSw/G9vqp+DkkT2pY19uROuzMazoTzKWpPho2q/qzNlv/ANbOFM2GEmKamQ7CO88JgRxMsPTvc/HxCLU/ClMJU8LcOf9LfP2KYZpPwuheKJoF4vDGj8NsbFmccJyYSdpkNEk7sCgYBJlL2FMaz1sgC2Ue19DIhvfaunRV1P20mSPgwmNmijccETm7w3LXX0OIdFeV/JGHLqqSWj7i+6iXk/ncKZoUGCfi8G6sQ+uL/GJ5qTt6GJV+ExTS+PtSjeSO/EAw1m13Vb+C16hpstx1l23f+4aJ81gbecgPct38XsKpaiXZtOnQKBgQCMsN7QRYYxwoq9YoDUzIlAzKYyeBVWYL6najHYUZR5hG/xQIBqZRem9/4cTvpJxKInrwA6LrrqaEl0aHDFp75U6h7O3PCvA5PXZK9dD/yJsZIj7U/yX/nTQokn1UUegrYiwiTkusBvrrtuINWePsLvTVc4GpObHnPmsiNTWsWwYwKBgENaeTNOCHV2km/ysXQSEIhKbtlAMQPsgWHCt/bzHlF9m18izb1LrJyjzcSsd+Zy78R+pv4G50Q27c3e/DFPz/wYxN/yHWRbyLBA8ipJbCtMtPEdS9krpmN6cChIdLGbz4CVUqOPSRzNb9lhhgPCcCNRq6DG3HBceb1Se9VnO3zk
    alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApFQKccMq+wPoWh93DcX3XYrnT7FJ3gntJA/jEwgk6Jd3iEVS2CyUCCgFVcQn8xjXT81YbZHYvoC50IBuu+A+Ey+J8VIgsBm5g9uwbOLRf66GrZjuKOlalHm5gHXjcL2gZRMBJEStOxstcO2YQriqhQzdL3EKp+HQc9u14IOVFpZdR8qq1l7CzKHn1vQo/1fUPfUrTLQqSuQTU9Ospv/QHFqOJA7DPetUQ+jnaZ082f3clr4ITw4EE8A6IWqTXcETTx5j/udCGP84g2Y4j+8i9DqYGyD5ePVgt4G0ICBX1bi1qNlylfxRg8Y3c1DFrRGyr0NpKQxSVXkYaVNvrCoudQIDAQAB
    returnUrl: http://fuzsmc.natappfree.cc/alipay/return
    notifyUrl: http://fuzsmc.natappfree.cc/alipay/notify

4. AliPayProperties

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.annotation.PostConstruct;

/**
 * @author mengday zhang
 */
@Data
@Slf4j
@ConfigurationProperties(prefix = "pay.alipay")
public class AlipayProperties {

    /** 支付宝gatewayUrl */
    private String gatewayUrl;
    /** 商户应用id */
    private String appid;
    /** RSA私钥,用于对商户请求报文加签 */
    private String appPrivateKey;
    /** 支付宝RSA公钥,用于验签支付宝应答 */
    private String alipayPublicKey;
    /** 签名类型 */
    private String signType = "RSA2";

    /** 格式 */
    private String formate = "json";
    /** 编码 */
    private String charset = "UTF-8";

    /** 同步地址 */
    private String returnUrl;

    /** 异步地址 */
    private String notifyUrl;

    /** 最大查询次数 */
    private static int maxQueryRetry = 5;
    /** 查询间隔(毫秒) */
    private static long queryDuration = 5000;
    /** 最大撤销次数 */
    private static int maxCancelRetry = 3;
    /** 撤销间隔(毫秒) */
    private static long cancelDuration = 3000;

    private AlipayProperties() {}

    /**
     * PostContruct是spring框架的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。
     */
    @PostConstruct
    public void init() {
        log.info(description());
    }

    public String description() {
        StringBuilder sb = new StringBuilder("\nConfigs{");
        sb.append("支付宝网关: ").append(gatewayUrl).append("\n");
        sb.append(", appid: ").append(appid).append("\n");
        sb.append(", 商户RSA私钥: ").append(getKeyDescription(appPrivateKey)).append("\n");
        sb.append(", 支付宝RSA公钥: ").append(getKeyDescription(alipayPublicKey)).append("\n");
        sb.append(", 签名类型: ").append(signType).append("\n");

        sb.append(", 查询重试次数: ").append(maxQueryRetry).append("\n");
        sb.append(", 查询间隔(毫秒): ").append(queryDuration).append("\n");
        sb.append(", 撤销尝试次数: ").append(maxCancelRetry).append("\n");
        sb.append(", 撤销重试间隔(毫秒): ").append(cancelDuration).append("\n");
        sb.append("}");
        return sb.toString();
    }

    private String getKeyDescription(String key) {
        int showLength = 6;
        if (StringUtils.isNotEmpty(key) && key.length() > showLength) {
            return new StringBuilder(key.substring(0, showLength)).append("******")
                    .append(key.substring(key.length() - showLength)).toString();
        }
        return null;
    }
}

5. AliPayConfiguration

@Configuration
@EnableConfigurationProperties(AlipayProperties.class)
public class AlipayConfiguration {

    private AlipayProperties properties;

    public AlipayConfiguration(AlipayProperties properties) {
        this.properties = properties;
    }

    @Bean
    public AlipayTradeService alipayTradeService() {
        return new AlipayTradeServiceImpl.ClientBuilder()
                .setGatewayUrl(properties.getGatewayUrl())
                .setAppid(properties.getAppid())
                .setPrivateKey(properties.getAppPrivateKey())
                .setAlipayPublicKey(properties.getAlipayPublicKey())
                .setSignType(properties.getSignType())
                .build();
    }
}

6. PayUtil

public class PayUtil {

    /**
     * 根据url生成二位图片对象
     *
     * @param codeUrl
     * @return
     * @throws WriterException
     */
    public static BufferedImage getQRCodeImge(String codeUrl) throws WriterException {
        Map hints = new Hashtable();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        hints.put(EncodeHintType.CHARACTER_SET, "UTF8");
        int width = 256;
        BitMatrix bitMatrix = (new MultiFormatWriter()).encode(codeUrl, BarcodeFormat.QR_CODE, width, width, hints);
        BufferedImage image = new BufferedImage(width, width, 1);
        for(int x = 0; x < width; ++x) {
            for(int y = 0; y < width; ++y) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? -16777216 : -1);
            }
        }

        return image;
    }
}

7. AlipayF2FController

package com.example.paydemo.controller;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.demo.trade.model.GoodsDetail;
import com.alipay.demo.trade.model.builder.AlipayTradePayRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradeQueryRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradeRefundRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPayResult;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.model.result.AlipayF2FQueryResult;
import com.alipay.demo.trade.model.result.AlipayF2FRefundResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.utils.ZxingUtils;
import com.example.paydemo.configuration.AlipayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.ResourceUtils;
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.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.*;

/**
 * 支付宝-当面付 控制器.
 * 

* https://openclub.alipay.com/read.php?tid=1720&fid=40 * * https://docs.open.alipay.com/203/105910 * * @author Mengday Zhang * @version 1.0 * @since 2018/6/4 */ @Slf4j @RestController @RequestMapping("/alipay") public class AlipayF2FController { @Autowired private AlipayTradeService alipayTradeService; @Autowired private AlipayProperties aliPayProperties; /** * 当面付-条码付 * * 商家使用扫码工具(扫码枪等)扫描用户支付宝的付款码 * * @param authCode */ @PostMapping("/f2fpay/barCodePay") public String barCodePay(String authCode){ // 实际使用时需要根据商品id查询商品的基本信息并计算价格(可能还有什么优惠),这里只是写死值来测试 // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线, String outTradeNo = UUID.randomUUID().toString(); // (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费” String subject = "测试订单"; // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元" String body = "购买商品2件共20.05元"; // (必填) 订单总金额,单位为元,不能超过1亿元 // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】 String totalAmount = "0.01"; // (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】 String undiscountableAmount = ""; // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持 String storeId = "test_store_id"; // 商户操作员编号,添加此参数可以为商户操作员做销售统计 String operatorId = "test_operator_id"; // 商品明细列表,需填写购买商品详细信息, List goodsDetailList = new ArrayList<>(); GoodsDetail goods1 = GoodsDetail.newInstance("goods_id001", "全麦小面包", 1, 1); goodsDetailList.add(goods1); GoodsDetail goods2 = GoodsDetail.newInstance("goods_id002", "黑人牙刷", 1, 2); goodsDetailList.add(goods2); // 支付超时,线下扫码交易定义为5分钟 String timeoutExpress = "5m"; AlipayTradePayRequestBuilder builder = new AlipayTradePayRequestBuilder() .setOutTradeNo(outTradeNo) .setSubject(subject) .setBody(body) .setTotalAmount(totalAmount) .setAuthCode(authCode) .setTotalAmount(totalAmount) .setStoreId(storeId) .setOperatorId(operatorId) .setGoodsDetailList(goodsDetailList) .setTimeoutExpress(timeoutExpress); // 当面付,面对面付,face to face pay -> face 2 face pay -> f2f pay // 同步返回支付结果 AlipayF2FPayResult f2FPayResult = alipayTradeService.tradePay(builder); // 注意:一定要处理支付的结果,因为不是每次支付都一定会成功,可能会失败 switch (f2FPayResult.getTradeStatus()) { case SUCCESS: log.info("支付宝支付成功: )"); break; case FAILED: log.error("支付宝支付失败!!!"); break; case UNKNOWN: log.error("系统异常,订单状态未知!!!"); break; default: log.error("不支持的交易状态,交易返回异常!!!"); break; } /** * { * "alipay_trade_pay_response": { * "code": "10000", * "msg": "Success", * "buyer_logon_id": "ekf***@sandbox.com", * "buyer_pay_amount": "0.01", * "buyer_user_id": "2088102176027680", * "buyer_user_type": "PRIVATE", * "fund_bill_list": [ * { * "amount": "0.01", * "fund_channel": "ALIPAYACCOUNT" * } * ], * "gmt_payment": "2018-06-10 14:54:16", * "invoice_amount": "0.01", * "out_trade_no": "91fbd3fa-8558-443a-82c2-bd8e941d7e71", * "point_amount": "0.00", * "receipt_amount": "0.01", * "total_amount": "0.01", * "trade_no": "2018061021001004680200326495" * }, * "sign": "BNgMmA2t8fwFZNSa39kyEKgL6hV45DVOKOsdyyzTzsQnX8HEkKOzVevQEDyK083dNYewip1KK/K92BTDY2KMAsrOEqcCNxsk9NLAvK9ZQVxQzFbAFKqs5EBAEzncSWnChJcb7VMhDakUxHZFmclHg38dLJiHE2bEcF8ar9R1zj0p4V0Jr+BXO10kLtaSTc9NeaCwJZ89sPHKitNnUWRroU7t0xPHc1hWpstObwixKmAWnsFyb9eyGwPQnqNBsUVNSNWCJ7Pg3rb03Tx6J3zNsqH5f0YhWilMi09npPe33URFc6zG1HJSfhEm4Gq1zwQrPoA/anW8BbdmEUUmNo1dEw==" * } */ String result = f2FPayResult.getResponse().getBody(); return result; } /** * 当面付-扫码付 * * 扫码支付,指用户打开支付宝钱包中的“扫一扫”功能,扫描商户针对每个订单实时生成的订单二维码,并在手机端确认支付。 * * 发起预下单请求,同步返回订单二维码 * * 适用场景:商家获取二维码展示在屏幕上,然后用户去扫描屏幕上的二维码 * @return * @throws AlipayApiException */ @PostMapping("/precreate") public void precreate(HttpServletRequest request, HttpServletResponse response) throws Exception { // 实际使用时需要根据商品id查询商品的基本信息并计算价格(可能还有什么优惠),这里只是写死值来测试 // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线, String outTradeNo = UUID.randomUUID().toString(); // (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费” String subject = "测试订单"; // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元" String body = "购买商品2件共20.05元"; // (必填) 订单总金额,单位为元,不能超过1亿元 // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】 String totalAmount = "0.01"; // (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】 String undiscountableAmount = ""; // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号) // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID String sellerId = ""; // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持 String storeId = "test_store_id"; // 商户操作员编号,添加此参数可以为商户操作员做销售统计 String operatorId = "test_operator_id"; // 商品明细列表,需填写购买商品详细信息, List goodsDetailList = new ArrayList<>(); GoodsDetail goods1 = GoodsDetail.newInstance("goods_id001", "全麦小面包", 1, 1); goodsDetailList.add(goods1); GoodsDetail goods2 = GoodsDetail.newInstance("goods_id002", "黑人牙刷", 1, 2); goodsDetailList.add(goods2); // 支付超时,线下扫码交易定义为5分钟 String timeoutExpress = "5m"; AlipayTradePrecreateRequestBuilder builder =new AlipayTradePrecreateRequestBuilder() .setSubject(subject) .setTotalAmount(totalAmount) .setOutTradeNo(outTradeNo) .setUndiscountableAmount(undiscountableAmount) .setSellerId(sellerId) .setBody(body) .setOperatorId(operatorId) .setStoreId(storeId) .setTimeoutExpress(timeoutExpress) //支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置 .setNotifyUrl(aliPayProperties.getNotifyUrl()) .setGoodsDetailList(goodsDetailList); AlipayF2FPrecreateResult result = alipayTradeService.tradePrecreate(builder); String qrCodeUrl = null; switch (result.getTradeStatus()) { case SUCCESS: log.info("支付宝预下单成功: )"); AlipayTradePrecreateResponse res = result.getResponse(); BufferedImage image = PayUtil.getQRCodeImge(res.getQrCode()); response.setContentType("image/jpeg"); response.setHeader("Pragma","no-cache"); response.setHeader("Cache-Control","no-cache"); response.setIntHeader("Expires",-1); ImageIO.write(image, "JPEG", response.getOutputStream()); break; case FAILED: log.error("支付宝预下单失败!!!"); break; case UNKNOWN: log.error("系统异常,预下单状态未知!!!"); break; default: log.error("不支持的交易状态,交易返回异常!!!"); break; } } /** * 订单查询(最主要用于查询订单的支付状态) * @param orderNo 商户订单号 * @return */ @GetMapping("/query") public String query(String orderNo){ AlipayTradeQueryRequestBuilder builder = new AlipayTradeQueryRequestBuilder() .setOutTradeNo(orderNo); AlipayF2FQueryResult result = alipayTradeService.queryTradeResult(builder); switch (result.getTradeStatus()) { case SUCCESS: log.info("查询返回该订单支付成功: )"); AlipayTradeQueryResponse resp = result.getResponse(); log.info(resp.getTradeStatus()); // log.info(resp.getFundBillList()); break; case FAILED: log.error("查询返回该订单支付失败!!!"); break; case UNKNOWN: log.error("系统异常,订单支付状态未知!!!"); break; default: log.error("不支持的交易状态,交易返回异常!!!"); break; } return result.getResponse().getBody(); } /** * 退款 * @param orderNo 商户订单号 * @return */ @PostMapping("/refund") public String refund(String orderNo){ AlipayTradeRefundRequestBuilder builder = new AlipayTradeRefundRequestBuilder() .setOutTradeNo(orderNo) .setRefundAmount("0.01") .setRefundReason("当面付退款测试") .setOutRequestNo(String.valueOf(System.nanoTime())) .setStoreId("A1"); AlipayF2FRefundResult result = alipayTradeService.tradeRefund(builder); switch (result.getTradeStatus()) { case SUCCESS: log.info("支付宝退款成功: )"); break; case FAILED: log.error("支付宝退款失败!!!"); break; case UNKNOWN: log.error("系统异常,订单退款状态未知!!!"); break; default: log.error("不支持的交易状态,交易返回异常!!!"); break; } return result.getResponse().getBody(); } /** * 扫码付异步结果通知 * https://docs.open.alipay.com/194/103296 * @param request */ @RequestMapping("/notify") public String notify(HttpServletRequest request) throws AlipayApiException { // 一定要验签,防止黑客篡改参数 Map parameterMap = request.getParameterMap(); parameterMap.forEach((key, value) -> System.out.println(key+"="+value[0])); // https://docs.open.alipay.com/54/106370 // 获取支付宝POST过来反馈信息 Map params = new HashMap<>(); Map requestParams = request.getParameterMap(); for (Iterator 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] + ","; } params.put(name, valueStr); } boolean flag = AlipaySignature.rsaCheckV1(params, aliPayProperties.getAlipayPublicKey(), aliPayProperties.getCharset(), aliPayProperties.getSignType()); if (flag) { /** * TODO 需要严格按照如下描述校验通知数据的正确性 * * 商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, * 并判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), * 同时需要校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email), * * 上述有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。 * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。 * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。 */ return "success"; } return null; } }

总结

以上代码都实际测试过,保证所有代码都是可用的,Spring Boot集成支付宝只需把代码复制过去即可,关注并私信“当面付”获取支付宝集成源码!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表