单接口实现多种支付功能:包括不限于微信、支付宝、银联

前言

        在项目中我们负责支付接口的开发,但是随着时代的发展,支付方式也日新月异,由一开始的网银支付,到支付宝绑定的快捷支付,现在的扫码支付APP支付,形式多种多样,在一般开发的眼中,项目需要什么支付就开发什么接口,需要接入新的支付方式时,就再开发一个新的接口,为了统一这些接口,本文介绍如何将多种支付合并接口。

1,开发工具

        IDEA、JDK8、mybatis-plus、ngrok

        介绍一下   “ngrok”   这是内网穿透工具,在我们开发中帮助我们接收回调,将我们的端口暴露在公网能被访问,可以自行学习,后续我可能会写。

2,准备相关配置

        2.1 数据库

        必要:交易数据表、商户证书表;可按业务自行配置

        可选:日志记录表(用来存交易数据,可以将调用方给我们的数据、我们请求银行(支付宝、微信)、银行响应给我们和我们响应给调用方的数据存在日志表中,方便我们联调排查。

                PS:我为了方便演示,所有要存日志表的地方我会使用 //TODO 说明

        为方便演示,本文全程使用  “支付宝密钥模式” ,要改为其他支付方式,只需要稍作修改。

        2.2 maven

        依赖,建议新版本

<!--  支付宝支付-->
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.39.48.ALL</version>
    </dependency>
<!-- mybatis-plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.1</version>
    </dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version> <!-- 建议使用最新版本 -->
</dependency>

3,代码实现

        3.1实体类

商户证书类,

PS:他们的Dao、Service按照正常的走,我就不写了

package com.example.pay.entity;

import lombok.Data;

import java.io.Serializable;

@Data
public class BankMerchant implements Serializable {

    private static final long serialVersionUID=1l;
    //    主键
    private long merId;
    //    商户号
    private String merNo;
    //    商户名称
    private String merDesc;
    //    商户绑定的appId
    private String merBindAppid;

    //    银行代码
    private String bankId;
    //    商户说明
    private String desc;

    //    渠道
    private String channelId;

    //私钥
    private byte[] dealPrivatekey;
    //公钥
    private byte[] dealCert;

}

     交易类,

package com.example.pay.entity;


import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

@Data

public class PayTrans {

    //    给每个业务系统分配的唯一id,用来区分他们,并查询商户
    private String appId;    
    //    平台流水号
    private String transId;
    //    业务流水号
    private String appTransId;
    //    原平台流水号
    private String srcTransId;
    //    原业务流水号
    private String appSrcTransId;
    //    交易类型
    private String transType;
    //    交易金额
    private BigDecimal transAmount;
    //    原交易金额
    private BigDecimal srcTransAmount;
    //    平台交易时间
    private Date transTime;
    //    原平台交易时间
    private Date srcTransTime;
    //    交易状态
    private String transStatus;
    //    渠道
    private String channelId;
    //    交易银行id
    private String bankId;
    //    银行交易时间
    private Date bankTransTime;
    //    银行流水号
    private String bankTransId;
    //    银行返回码
    private String bankResponseCode;
    //    银行返回描述
    private String bankResponseDesc;


    //    通知业务地址
    private String returnNotifyUrl;


    //交易完成银行通知时间
    private Date updateTime;
    //    业务交易时间
    private Date appTransTime;
    //    超时时间
    private Date orderTimeoutDate;
    //    用户id
    private String userId;


    //商品名称
    private String goodsName;
    //备注
    private String orderRemark;


    //    支付时请求银行报文
    @TableField(exist = false)
    private Object payRequestData;
    //    支付时请求银行报文
    @TableField(exist = false)
    private Object refundRequestData;
    //    银行返回报文
    @TableField(exist = false)
    private Object refundBackData;
    //    银行返回报文
    @TableField(exist = false)
    private Object payBackData;
}

        3.2配置类

用于获取支付宝支付时的配置类

ppackage com.example.pay.config;

import com.alipay.api.AlipayConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.pay.entity.BankMerchant;
import com.example.pay.mapper.BankMerchantMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
@Component
@RequiredArgsConstructor
public class AliPayManager {


    private final BankMerchantMapper bankMerchantMapper;


    /**
     * 获取支付宝配置类 -- 密钥模式
     *
     * @param  appId 业务系统编号
     * @param  bankId 银行编号
     * @param  channelId 支付渠道
     * @return AlipayConfig 支付宝的配置类
     */
    public AlipayConfig getAlipayConfig(String appId, String bankId, String channelId) throws NullPointerException {

        //获取银行商户
        LambdaQueryWrapper<BankMerchant> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BankMerchant::getAppId, appId)
                .eq(BankMerchant::getBankId, bankId)
                .eq(BankMerchant::getChannelId, channelId);
        BankMerchant bankMerchant = bankMerchantMapper.selectOne(wrapper);
        if (bankMerchant == null) {
            log.error("支付宝商户获取  null-->{}", appId);
            throw new NullPointerException("支付宝商户是空的--> " + appId);
        }
        log.info("获取到支付宝商户");

        String alipayPublicKey = new String(bankMerchant.getDealCert(), StandardCharsets.UTF_8);//支付宝公钥
        log.info("获取到支付宝公钥");
        String privateKey = new String(bankMerchant.getDealPrivatekey(), StandardCharsets.UTF_8);//应用私钥
        log.info("获取到支付宝应用私钥");

        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
        alipayConfig.setAppId(bankMerchant.getMerBindAppid());
        alipayConfig.setPrivateKey(privateKey);
        alipayConfig.setFormat("json");
        alipayConfig.setAlipayPublicKey(alipayPublicKey);
        alipayConfig.setCharset("UTF-8");
        alipayConfig.setSignType("RSA2");
        return alipayConfig;
    }

    //支付宝异步处理参数
    public Map<String, String> checkAsynNotify(Map bankResponseObject){
        //获取支付宝POST过来反馈信息,将异步通知中收到的待验证所有参数都存放到map中
        Map<String, String> params = new LinkedHashMap<>();
        for (Iterator iter = bankResponseObject.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            List<String> values = (ArrayList)bankResponseObject.get(name);
            String valueStr = "";
            for (int i = 0; i < values.size(); i++) {
                valueStr = (i == values.size() - 1) ? valueStr + values.get(i) : valueStr + values.get(i) + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。
//            valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name.trim(), valueStr.trim());
        }
        params.forEach((key,value)->{
            System.out.println(key + "=" + value);
        });
        return params;
    }


}

处理时间的工具类,处理支付宝回调参数中的时间,可以根据格式自行添加

package com.example.pay.config;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 时间工具类
 *
 * @author MrBird
 */
public abstract class DateUtil {

    private static Log log = LogFactory.getLog(DateUtil.class);

    /**
     * This method generates a string representation of a date/time
     * in the format you specify on input
     *
     * @param aMask   the date pattern the string is in
     * @param strDate a string representation of a date
     * @return a converted Date object
     * @throws ParseException
     * @see SimpleDateFormat
     */
    public static final Date convertStringToDate(String aMask, String strDate)
            throws ParseException {
        SimpleDateFormat df = new SimpleDateFormat(aMask);
        Date date = null;
        

        if (log.isDebugEnabled()) {
            log.debug("converting '" + strDate + "' to date with mask '"
                    + aMask + "'");
        }

        try {
            date = df.parse(strDate);
        } catch (ParseException pe) {
            //log.debug("ParseException: " + pe);
            throw new ParseException(pe.getMessage(), pe.getErrorOffset());
        }

        return (date);
    }

    public static Date convertStringToDateTimeSplit(String strDate)
            throws ParseException {
        Date aDate = null;

        try {
            if (log.isDebugEnabled()) {
                log.debug("converting date with pattern: " + "yyyy-MM-dd HH:mm:ss");
            }

            aDate = convertStringToDate("yyyy-MM-dd HH:mm:ss", strDate);
        } catch (ParseException pe) {
            log.debug("Could not convert '" + strDate
                    + "' to a date, throwing exception");
            //pe.printStackTrace();
            throw new ParseException(pe.getMessage(),
                    pe.getErrorOffset());

        }

        return aDate;
    }

}

        这个key是用 交易类 中的 bankId(银行编码)和 channelId(渠道),,我们这边就 1 表示用户出示付款码支付

        initializeHandlers在启动时会自动寻找 BankMessageHandler 的实现类

package com.example.pay.domsg;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class PaymentHandlerFactory {

    private final Map<String, BankMessageHandler> handlerMap = new HashMap<>();
    private final ApplicationContext applicationContext;
    

    // 通过构造器注入确保线程安全
    public PaymentHandlerFactory(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        initializeHandlers();
    }

    private void initializeHandlers() {
        // 自动发现所有 BankMessageHandler 实现类
        Map<String, BankMessageHandler> beans =
                applicationContext.getBeansOfType(BankMessageHandler.class);

        beans.forEach((beanName, handler) -> {
            String key = handler.getHandlerKey();
            handlerMap.put(key, handler);
            log.info("Registered handler [{}] for key: {}",
                    handler.getClass().getSimpleName(), key);
        });
    }
    public BankMessageHandler getHandler(String handlerKey) {
        BankMessageHandler handler = handlerMap.get(handlerKey);
        if (handler == null) {
            throw new NullPointerException();
        }
        return handler;
    }
    

}

        3.3 具体实现

                3.3.1 controller

这个就看自己要不要了,可以使用feign,dubbo等远程调用工具,也可以直接发http请求

package com.example.pay.controller;

import com.example.pay.Result.Result;
import com.example.pay.service.PayService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;


@Slf4j
@RestController
@RequestMapping("/zf")
@RequiredArgsConstructor
public class PayController {

    private final PayService payService;


    @PostMapping("/payRequest")
    public Result payRequest(@RequestBody Map<String, String> params) {
        log.info("payRequest result: {}", params.toString());
        Result result = this.payService.payRequest(params);
        log.info("payRequest result: {}", result);
        return result;
    }


    @PostMapping("/zfbPayNotify")
    public String zfbPayNotify(HttpServletRequest request) {
        log.info("payRequest result: {}", request.toString());
        // TODO  自己的回调处理逻辑
        String str = this.payService.zfbPayNotify(request);
        log.info("payRequest result: ");
        return str;
    }



}

                3.3.2 service

        这里只写了支付和支付的回调,

        在看支付逻辑之前,请先看完这段话,

        我们的支付逻辑步骤

                1. 校验重复支付

                2. 解析参数生成对应交易类(PayTrans)

                3. 寻找对应的接口实现类的Bean

                4. 调用对应方法完成支付

                        在对应实现类中将方法拆分了四个,

                        获取支付参数、请求支付宝(银行)、解析响应、支付的回调

                        每次调完一个方法我都会存一下日志,如果你们觉的麻烦,可以在寻找到对应实现类之后直接在里面把所有的流程走完,我比较习惯将数据库的操作放在主流程中,

                5. 返回

package com.example.pay.service;

import com.example.pay.Result.Result;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public interface PayService {

    /**
     * 业务系统的支付请求
     * @param params 业务系统调用的参数
     * @return Result 支付模块的统一返回类
     */
    Result payRequest(Map<String, String> params);

    String zfbPayNotify(HttpServletRequest request);

    //   退款查询 请参考支付功能
}


————————————————————————————————————————————————————————————————————————————————————
package com.example.pay.service.impl;

import com.alipay.api.internal.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.pay.Result.Result;
import com.example.pay.Result.ResultUtil;
import com.example.pay.config.SpringUtil;
import com.example.pay.domsg.BankMessageHandler;
import com.example.pay.entity.PayTrans;
import com.example.pay.service.PayService;
import com.example.pay.service.PayTransService;
import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


@Slf4j
@Service
@RequiredArgsConstructor
public class PayServiceImpl implements PayService {

    private final PayTransService payTransService;
    private final PaymentHandlerFactory paymentHandlerFactory;


    private final Gson gson = new Gson();


    @Override
    public Result payRequest(Map<String, String> params) {

        //检查是否重复退款
        String srcTransId = params.get("srcTransId");
        LambdaQueryWrapper<PayTrans> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PayTrans::getSrcTransId, srcTransId);
        PayTrans one = this.payTransService.getOne(wrapper);
        if (one != null) {
            return ResultUtil.error("1", "重复支付");
            //预防重复支付的方法多种多样,这个是最简单的一种,就这样演示一下
        }

        //TODO  将业务系统请求我们的参数保存到日志表  我这里没写
        //解析业务系统的参数
        //这里可以一个一个get() set(),也可以用框架
        String json = this.gson.toJson(params);
        PayTrans payTrans = this.gson.fromJson(json, PayTrans.class);

        payTrans.setTransId(UUID.randomUUID().toString());
        payTrans.setTransStatus("接收数据");

        boolean save = this.payTransService.save(payTrans);
        if (!save) {
            return ResultUtil.error("1", "未知异常,请联系管理员");
        }

        //开始解析获取实现类
        String key = payTrans.getBankId() + "_" + payTrans.getChannelId();
        log.info("key: {}", key);

        BankMessageHandler handler = paymentHandlerFactory.getHandler(key);

        // 执行业务方法
        //获取请求银行用的参数
        Object requestBankParam = handler.payRequest(payTrans);

        //TODO  将我们请求银行方面的数据保存到日志表  就是这个 requestBankParam  我这里没写

        payTrans.setPayRequestData(requestBankParam);
        //请求银行方面,并获取到银行的回调
        Object responseBankParam = handler.paySend(payTrans);

        //TODO  将我们获取的银行方面的响应数据保存到日志表  就是这个 responseBankParam  我这里没写

        /*
        在这里将银行的响应数据设置给这笔交易,在 paySynResponse 方法中更新 payTrans 但是不操作数据库
        返回值我们要返给业务系统的数据,业务系统方面根据这个数据来给用户响应
        * */
        payTrans.setPayBackData(responseBankParam);
        Object responseParam = handler.paySynResponse(payTrans);

        //TODO  将我们响应给业务系统的数据保存到日志表  就是这个 responseParam  我这里没写
        //更新数据库
        this.payTransService.updateById(payTrans);

        //返回
        return ResultUtil.success(this.gson.fromJson(this.gson.toJson(responseParam), Map.class));
    }

    @Override
    public String zfbPayNotify(HttpServletRequest request) {
        log.info("接收到支付宝的异步回调 ,:{}", request.toString());
        //获取订单号
        //request  的key是支付宝定的
        String transId = request.getParameter("out_trade_no");
        log.info("支付宝的异步回调参数流水号:{}", transId);
        //获取支付宝POST过来反馈信息,将异步通知中收到的待验证所有参数都存放到map中
        Map requestParams = request.getParameterMap();

        PayTrans payTransById = this.payTransService.getById(transId);

        //TODO  将我们银行回调的数据保存到日志表  就是这个 requestParams 我这里没写

        String key = payTransById .getBankId() + "_" + payTransById .getChannelId();
        log.info("key: {}", key);

        BankMessageHandler handler = paymentHandlerFactory.getHandler(key);

        // 执行业务方法
        //异步处理
        payTransById.setPayBackData(requestParams);
        handler.payAsyResponse(payTransById);

        //直接对  payTransById   进行更新操作
        this.payTransService.updateById(payTransById);

        if ("交易成功".equals(payTransById.getTransStatus())) {
            return "success";
        } else {
            return "fail";//返回 fail 支付宝会再次发起回调
        }

    }
}

                3.3.3 多实现支付接口

        这个要被多实现的接口,要实现几种支付方式,就写多少个这个类,别忘了在之前写的配置类Map里面写名称,

package com.example.pay.domsg;


public interface BankMessageHandler<R, T> {

    String getHandlerKey();

    /**
     * 获取银行支付时需要用到的参数
     *
     * @param t
     * @return
     */
    R payRequest(T t);

    /**
     * 支付请求发送
     *
     * @param t
     * @return
     */
    R paySend(T t);

    /**
     * 银行方面支付同步响应
     *
     * @param t
     * @return
     */
    R paySynResponse(T t);

    /**
     * 支付异步通知
     *
     * @param t
     * @return
     */
    R payAsyResponse(T t);

}



_________________________________________________________________________________________

package com.example.pay.domsg.bank;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.pay.config.AliPayManager;
import com.example.pay.config.DateUtil;
import com.example.pay.domsg.BankMessageHandler;
import com.example.pay.entity.BankMerchant;
import com.example.pay.entity.PayTrans;
import com.example.pay.mapper.BankMerchantMapper;
import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;


@Slf4j
@Component
@RequiredArgsConstructor
public class ZfbQrcodeMessageHandlerImpl implements BankMessageHandler<Map, PayTrans> {


    public static final String HANDLER_KEY = "zfb_1";

    private final AliPayManager aliPayManager;
    private final BankMerchantMapper bankMerchantMapper;

    private final Gson gson = new Gson();

    @Override
    public String getHandlerKey() {
        return HANDLER_KEY;
    }

    /**
     * 获取银行支付时需要用到的参数
     *
     * @param payTrans PayTrans
     * @return 银行支付时需要用到的参数  Map
     */
    @Override
    public Map<String, String> payRequest(PayTrans payTrans) {
        String transId = payTrans.getTransId();
        Map<String, String> data = new HashMap<>();

        //商品名称
        data.put("subject", payTrans.getGoodsName());
        //商品订单号
        data.put("transId", transId);
        //标价金额
        data.put("transAmount", String.valueOf(payTrans.getTransAmount()));
        //回调地址
        data.put("notify_url", "https://793e-223-87-151-54.ngrok-free.app/zt/zfbPayNotify");
        //这个  “https://793e-223-87-151-54.ngrok-free.app”  就是使用 ngrok 得到的

        //TODO   如果你们项目有什么需要带给银行的,可以在这里弄

        //银行支付时需要用到的参数-----data
        return data;
    }

    @Override
    public Map<String, Object> paySend(PayTrans payTrans) {
        //请求银行方面的数据
        Map<String, String> payRequestData = this.gson.fromJson(this.gson.toJson(payTrans.getPayRequestData()), Map.class);

        // 初始化SDK
        AlipayConfig alipayConfig = aliPayManager.getAlipayConfig(payTrans.getAppId(), payTrans.getBankId(), payTrans.getChannelId());
        if (alipayConfig == null) {
            throw new RuntimeException("获取支付宝配置类异常");//最好是统一异常处理一下,
        }
        AlipayClient alipayClient = null;
        AlipayTradePrecreateResponse response = null;
        try {
            alipayClient = new DefaultAlipayClient(alipayConfig);

            AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
            AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();

            model.setOutTradeNo(payRequestData.get("transId"));// 设置商户订单号
            model.setTotalAmount(payRequestData.get("transAmount")); // 设置订单总金额
            model.setSubject(payRequestData.get("subject"));// 设置订单标题

            model.setProductCode("QR_CODE_OFFLINE");// 设置产品码
            model.setDiscountableAmount("0.00");// 设置可打折金额

            request.setNotifyUrl(payRequestData.get("notifyUrl"));
            request.setBizModel(model);

            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        log.info("支付请求返回参数{}", response.getBody());
        if (response.isSuccess()) {
            log.info("支付请求调用成功");
        } else {
            log.info("支付请求调用失败");
        }
        return this.gson.fromJson(this.gson.toJson(response), Map.class);
    }

    @Override
    public Map<String, Object> paySynResponse(PayTrans payTrans) {

        log.info("支付宝 订单码 支付同步响应:{}", payTrans.getTransId());

        Map<String, String> payBackData = this.gson.fromJson(this.gson.toJson(payTrans.getPayBackData()), Map.class);

        payTrans.setUpdateTime(new Date());

        if (!Objects.equals("10000", payBackData.get("code"))) {

            payTrans.setTransStatus("支付失败");
            payTrans.setBankResponseCode(payBackData.get("subCode"));
            payTrans.setBankResponseDesc(payBackData.get("subMsg"));
        } else {

            payTrans.setTransStatus("等待回调");
            String body = payBackData.get("body").trim();
            // TODO 请注意,把这个串返给业务系统的时候,很有可能出现转译错误,这样处理一下
            payTrans.setCodeUrl(body.replace("&", "&amp;"));
        }

        Map<String, Object> responseMap = new HashMap<>();
        //存要返给业务系统的数据


        log.info("支付宝 订单码 支付同步响应结束: {}", payTrans.getTransId());
        return responseMap;
    }

    @Override
    public Map<String, Object> payAsyResponse(PayTrans payTrans) {

        log.info("进行支付宝的异步通知处理 :{}", payTrans.getTransId());

        Map<String, Object> requestParams = this.gson.fromJson(gson.toJson(payTrans.getPayBackData()), Map.class);

        Map<String, String> params = aliPayManager.checkAsynNotify(requestParams);

        //获取银行商户
        LambdaQueryWrapper<BankMerchant> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BankMerchant::getAppId, payTrans.getAppId())
                .eq(BankMerchant::getBankId, payTrans.getBankId())
                .eq(BankMerchant::getChannelId, payTrans.getChannelId());
        BankMerchant bankMerchant = bankMerchantMapper.selectOne(wrapper);
        if (bankMerchant == null) {
            log.error("支付宝商户获取  null-->{}", payTrans.getAppId());
            throw new NullPointerException("支付宝商户是空的--> " + payTrans.getAppId());
        }

        String publicKey = new String(bankMerchant.getDealCert(), StandardCharsets.UTF_8);

        boolean signVerified = false;
        try {
            signVerified = AlipaySignature.rsaCheckV1(params, publicKey, "UTF-8", "RSA2");
            log.info("支付宝异步回调证书校验 = " + signVerified);
            if (!signVerified) {
                log.error("支付宝异步回调证书校验失败!");
                throw new RuntimeException("支付宝异步通知失败");
            }

            log.info("支付宝异步回调证书校验成功!");
            String app_id = params.get("app_id");
            if (!app_id.equals(bankMerchant.getMerBindAppid())) {
                log.info("商户号核对失败!");
                throw new RuntimeException("支付宝异步通知失败");
            }

            String total_amount = params.get("total_amount");
            if (!total_amount.equals(payTrans.getTransAmount().toString())) {
                log.info("交易金额核对失败!");
                throw new RuntimeException("支付宝异步通知失败");
            } else {
                payTrans.setBankTransId(params.get("trade_no"));
                String tradeStatus = params.get("trade_status");
                if (tradeStatus.equals("WAIT_BUYER_PAY")) {
                    payTrans.setTransStatus("交易中");
                } else if (tradeStatus.equals("TRADE_SUCCESS") || tradeStatus.equals("TRADE_FINISHED")) {
                    payTrans.setTransStatus("交易成功");
                    payTrans.setBankTransTime(DateUtil.convertStringToDateTimeSplit(params.get("gmt_create")));//这个时间处理配置类直接复制我的就行
                    payTrans.setBankResponseDesc(tradeStatus);
                } else if (tradeStatus.equals("TRADE_CLOSED")) {
                    payTrans.setTransStatus("交易失败");
                }
                return null;
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

最后附上项目结构图

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值