【开发技巧】-- 一篇女朋友也能看懂的Spring整合第三方支付(支付宝篇)

1.1 业务背景

当今互联网,涌现了许许多多的电子商务平台,比如淘宝网、京东、还有一些不知名的电商平台,但是并不是所有的公司都有与银行合作开发金融业务的资质,但是像一些互联网大厂就有这样的资质,比如蚂蚁金服的支付宝、腾讯的财付通以及京东金融,其他没有资质的公司那么要实现支付业务那么必须调用其他平台的一个交易接口。

1.2 为什么要使用支付宝支付?

支付宝是蚂蚁金服旗下的一个产品,它稳定、安全并且可靠,并且接口、开发文档提供地非常全面。

1.3 如何使用支付宝支付?(以沙箱环境为例)

准备步骤
  1. 登录支付宝的官方网站,找到沙箱环境点击链接即可
    在这里插入图片描述2. 然后下载一个支付宝开放平台开发助手。

【点击链接下载】

  • WINDOWS(windows版本工具请不要安装在含有空格的目录路径下,否则会导致公私钥乱码的问题)

  • MAC_OSX
    在这里插入图片描述3. 生成支付宝接口验签所需密钥
    点击工具界面下方的 点击获取,生成应用公钥证书 CSR 申请文件。
    在这里插入图片描述4. 设置沙箱环境中的应用公钥
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200327110933144.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMTk5MDE2,size_16,color_FFFFFF,t_7在这里插入图片描述在这里插入图片描述5. 设置支付宝异步回调地址
    在这里插入图片描述

至此准备工作完毕

1.3.1 首先构建一个maven工程,然后引入alipay-sdk依赖包。
<dependency>
     <groupId>com.alipay.sdk</groupId>
     <artifactId>alipay-sdk-java</artifactId>
     <version>4.9.71.ALL</version>
 </dependency>
1.3.2 写一个alipay.propeties用于保存项目中需要使用到的支付宝支付配置信息。

在这里插入图片描述

  • 内容
//这是支付宝沙箱环境网关  如果是生产环境换成正式地址即可
alipay_url=https://openapi.alipaydev.com/gateway.do

app_id=沙箱环境的appid

app_private_key=应用私钥

alipay_public_key=支付宝公钥

# 同步回调地址 重定向地址本地浏览器
return_payment_url=同步回调地址
return_order_url=同步回调地址跳转目标地址

# 异步通知地址 公网接口(webService)
notify_payment_url=异步通知地址

  • 沙箱环境appid
    在这里插入图片描述
  • 应用私钥
    在这里插入图片描述* 支付宝公钥
    在这里插入图片描述
1.3.3 编写一个支付宝支付配置类。
package com.qingyun.gmall.payment.config;

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * Created with IntelliJ IDEA.
 * User: 李敷斌.
 * Date: 2020-03-14
 * Time: 18:15
 * Explain: 支付宝支付配置类
 */
@Configuration
@PropertySource("classpath:alipay.properties")
public class AlipayConfig {

    @Value("${alipay_url}")
    private String alipay_url;

    @Value("${app_private_key}")
    private String app_private_key;

    @Value("${app_id}")
    private String app_id;


    public final static String format="json";
    public final static String charset="utf-8";
    public final static String sign_type="RSA2";


    public static String return_payment_url;

    public static  String notify_payment_url;

    public static  String return_order_url;

    public static  String alipay_public_key;

    @Value("${alipay_public_key}")
    public   void setAlipay_public_key(String alipay_public_key) {
        AlipayConfig.alipay_public_key = alipay_public_key;
    }

    @Value("${return_payment_url}")
    public   void setReturn_url(String return_payment_url) {
        AlipayConfig.return_payment_url = return_payment_url;
    }

    @Value("${notify_payment_url}")
    public   void setNotify_url(String notify_payment_url) {
        AlipayConfig.notify_payment_url = notify_payment_url;
    }

    @Value("${return_order_url}")
    public   void setReturn_order_url(String return_order_url) {
        AlipayConfig.return_order_url = return_order_url;
    }

    @Bean
    public AlipayClient alipayClient(){
        AlipayClient alipayClient=new DefaultAlipayClient(alipay_url,app_id,app_private_key,format,charset, alipay_public_key,sign_type );

        return alipayClient;
    }
}
1.3.4 请求处理控制器实现
@Controller
@LoginRequired(mustLogin = true)
@Slf4j
public class PaymentController {

    @Reference
    private OrderService orderService;

    @Reference
    private PaymentService paymentService;

	/**
	  *支付方式选择页面
	  */
    @RequestMapping(value = "/index")
    public String index(String orderSN, HttpServletRequest request, ModelMap modelMap){

        //获取当前登录用户
        UmsMember umsMember =(UmsMember) request.getAttribute(CommonConstant.REQ_ATTR_USERINFO);

        if (umsMember == null) {
            throw new RuntimeException(ResultEnum.MEMBER_NOT_LOGIN.getMsg());
        }

        //通过orderSN与用户编号获取订单信息
        OmsOrder omsOrder=orderService.getOrderByOrderSN(umsMember.getId(),orderSN);

        modelMap.addAttribute("orderId",omsOrder.getOrderSn());
        modelMap.addAttribute("totalAmount",omsOrder.getTotalAmount());

        return "index";
    }

 /**
  *发起支付宝支付
  */
    @RequestMapping(value = "/alipay/submit")
    @ResponseBody
    public String alipaySubmit(String orderSN,HttpServletRequest request){

        //获取当前登录用户
        UmsMember umsMember =(UmsMember) request.getAttribute(CommonConstant.REQ_ATTR_USERINFO);

        if (umsMember == null) {
            throw new RuntimeException(ResultEnum.MEMBER_NOT_LOGIN.getMsg());
        }

        String resultHTML=paymentService.luanchAlipay(orderSN,umsMember);

        return resultHTML;
    }

	/**
	  *同步回调
	  */
    @RequestMapping(value = "/alipay/callback/return")
    public String alipayReturn(@RequestParam HashMap<String,String> notifyReqParam){

        if (StringUtils.isBlank(notifyReqParam.get("sign"))){
            return "failed";
        }

        return "finish";
    }
	 /**
	  *支付宝异步通知请求处理器
	  */
    @PostMapping(value = "/alipay/callback/notify")
    @ResponseBody
    public String alipayNotify(@RequestParam HashMap<String,String> notifyReqParam){
        return paymentService.checkAlipay(notifyReqParam);
    }
}
1.3.5 支付宝支付,业务逻辑层实现。
package com.qingyun.gmall.payment.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.qingyun.gmall.bean.OmsOrder;
import com.qingyun.gmall.bean.PaymentInfo;
import com.qingyun.gmall.bean.UmsMember;
import com.qingyun.gmall.constants.CommonConstant;
import com.qingyun.gmall.enums.PayTypeEnum;
import com.qingyun.gmall.enums.ResultEnum;
import com.qingyun.gmall.payment.config.AlipayConfig;
import com.qingyun.gmall.payment.enums.PaymentStatusEnum;
import com.qingyun.gmall.payment.mapper.PaymentInfoMapper;
import com.qingyun.gmall.payment.params.AlipayReqParam;
import com.qingyun.gmall.service.OrderService;
import com.qingyun.gmall.service.PaymentService;
import com.qingyun.gmall.utils.ActiveMQUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.jms.JMSException;
import java.util.Date;
import java.util.HashMap;

/**
 * Created with IntelliJ IDEA.
 * User: 李敷斌.
 * Date: 2020-03-14
 * Time: 18:36
 * Explain: 支付业务实现类
 */
@Service
@Slf4j
public class PaymentServiceImpl implements PaymentService {

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private PaymentInfoMapper paymentInfoMapper;

    @Reference
    private OrderService orderService;

    @Autowired
    private ActiveMQUtil activeMQUtil;

    /**
     * 发起支付宝支付
     *
     * @param orderSN
     * @param umsMember
     * @return 支付宝接口返回html页面信息
     */
    @Override
    public String luanchAlipay(String orderSN, UmsMember umsMember) {

        //根据订单号与会员id获取当前订单信息
        OmsOrder omsOrder = orderService.getOrderByOrderSN(umsMember.getId(), orderSN);

        if (omsOrder==null){
            throw new RuntimeException("不存在当前订单信息!orderSN="+orderSN);
        }

        String resultHTML=null;

        AlipayReqParam alipayReqParam=new AlipayReqParam();
        alipayReqParam.setOut_trade_no(orderSN);
        alipayReqParam.setSubject(omsOrder.getSubject());
        alipayReqParam.setTotal_amount(omsOrder.getTotalAmount());

        //利用支付宝客户端生成表单页面
        AlipayTradePagePayRequest alipayTradePayRequest=new AlipayTradePagePayRequest();

        alipayTradePayRequest.setReturnUrl(AlipayConfig.return_order_url);
        alipayTradePayRequest.setNotifyUrl(AlipayConfig.notify_payment_url);
        System.out.println(JSON.toJSONString(alipayReqParam));
        alipayTradePayRequest.setBizContent(JSON.toJSONString(alipayReqParam));


        try {
            resultHTML=alipayClient.pageExecute(alipayTradePayRequest).getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        //查询是否已经创建过当前支付信息
        Example example=new Example(PaymentInfo.class);
        example.createCriteria().andEqualTo("orderSn",orderSN);

        PaymentInfo info = paymentInfoMapper.selectOneByExample(example);

        if (info==null){
            PaymentInfo paymentInfo=new PaymentInfo();

            paymentInfo.setOrderSn(orderSN);
            paymentInfo.setCreateTime(new Date());
            paymentInfo.setSubject(omsOrder.getSubject());
            paymentInfo.setPaymentStatus(PaymentStatusEnum.NOT_PAY.getDecr());
            paymentInfo.setTotalAmount(omsOrder.getTotalAmount());
            paymentInfo.setOrderId(omsOrder.getId());

            boolean flag = paymentInfoMapper.insertSelective(paymentInfo)>0;

            if (!flag) {
                throw new RuntimeException("保存支付信息失败!");
            }
        }

        //发送一条消息到延时队列中 用于后期检查订单支付状态
        HashMap<String,Object> msgMap=new HashMap<>();
        msgMap.put("orderSN",orderSN);
        msgMap.put("sendCount",1);

        try {
            activeMQUtil.sendDelayMapMessage(CommonConstant.PAYMENT_STATUS_CHECK_QUEUE_NAME,msgMap,1,activeMQUtil.getConnectionFactory().createConnection());
        } catch (JMSException e) {
            log.error("【支付宝支付】发送延时消息失败");
            e.printStackTrace();
        }

        return resultHTML;
    }

    /**
     * 检查支付宝支付异步通知信息
     *
     * @param notifyReqParam
     * @return 返回处理结果信息
     */
    @Override
    @Transactional
    public String checkAlipay(HashMap<String, String> notifyReqParam) {

        log.info("------callbackstart 支付宝开始回调"+notifyReqParam.toString());

         boolean isCheckPass=false;

        try {
            isCheckPass= AlipaySignature.rsaCheckV1(notifyReqParam,AlipayConfig.alipay_public_key,AlipayConfig.charset,AlipayConfig.sign_type);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        if (!isCheckPass) {
            log.warn("-------验签不通过");

            return "failure";
        }

        log.warn("-------验签通过");

        //验签成功标志
        String trade_status=notifyReqParam.get("trade_status");

        if (trade_status.equals("TRADE_SUCCESS")){
            //检查当前支付状态
            String outTradeNo=notifyReqParam.get("out_trade_no");

            PaymentInfo paymentInfoQueryParams=new PaymentInfo();
            paymentInfoQueryParams.setOrderSn(outTradeNo);

            PaymentInfo paymentInfo=paymentInfoMapper.selectOne(paymentInfoQueryParams);

            if (paymentInfo==null){
                return "error:not exists out_trade_no:"+paymentInfoQueryParams.getOrderSn();
            }

            log.info("检查是否已处理="+paymentInfo.getOrderSn());

            if (paymentInfo.getPaymentStatus().equals(PaymentStatusEnum.PAID.getDecr())){
                log.info("---已处理="+paymentInfo.getOrderSn());

                return "success";
            }else{
                //更新支付信息状态
                log.info("---未处理,更新支付状态="+paymentInfo.getOrderSn());

                paymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getDecr());
                paymentInfo.setConfirmTime(new Date());
                paymentInfo.setCallbackContent(notifyReqParam.toString());
                paymentInfo.setAlipayTradeNo(notifyReqParam.get("trade_no"));

                sendPaymentSuccessMsg(paymentInfo);

                return "success";
            }
        }

        return "";
    }

    /**
     * 检查支付宝支付状态
     *
     * @param orderSN
     * @return 是否支付成功
     */
    @Override
    public boolean checkAlipayStatus(String orderSN) {

        boolean flag=false;

        AlipayTradeQueryRequest tradeQueryRequest=new AlipayTradeQueryRequest();

        AlipayReqParam alipayReqParam=new AlipayReqParam();
        alipayReqParam.setOut_trade_no(orderSN);

        tradeQueryRequest.setBizContent(JSON.toJSONString(alipayReqParam));

        try {
            AlipayTradeQueryResponse response = alipayClient.execute(tradeQueryRequest);

            if (response.isSuccess()) {
                String tradeStatus = response.getTradeStatus();

                if ("TRADE_SUCCESS".equals(tradeStatus)){
                    log.info("订单:{}支付成功完成!",orderSN);

                    flag=true;
                    //更新当前支付信息状态 这里需要进行幂等性检查  幂等性指的就是在多次请求中返回的结果都是一致的

                    //查询数据库中当前支付状态
                    PaymentInfo queryParam=new PaymentInfo();
                    queryParam.setOrderSn(orderSN);

                    PaymentInfo paymentInfo=paymentInfoMapper.selectOne(queryParam);

                    if (paymentInfo!=null){

                        if (paymentInfo.getPaymentStatus().equals(PaymentStatusEnum.NOT_PAY.getDecr())) {
                            //如果当前订单状态为未支付则修改当前订单状态
                            paymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getDecr());
                            paymentInfo.setConfirmTime(new Date());
                            paymentInfo.setCallbackContent(response.getBody());
                            paymentInfo.setAlipayTradeNo(response.getTradeNo());

                            boolean result=paymentInfoMapper.updateByPrimaryKeySelective(paymentInfo)>0;

                            if (result){
                                sendPaymentSuccessMsg(paymentInfo);
                                flag=true;
                                log.info("当前订单支付状态修改成功!");
                            }else{
                                log.error("当前订单支付状态修改失败!");
                            }
                        }else{
                            flag=true;
                            log.info("当前订单已被处理过了!");
                        }
                    }else{
                        log.info("当前订单支付信息不存在!orderSN={}",orderSN);
                        throw new RuntimeException("当前订单支付信息不存在!orderSN="+orderSN);
                    }
                }else{
                    log.info("订单:{}尚未完成!",orderSN);
                }
            }else{
                log.info("订单:{}尚未完成!",orderSN);
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        return flag;
    }

    private void sendPaymentSuccessMsg(PaymentInfo paymentInfo) {
        paymentInfoMapper.updateByPrimaryKeySelective(paymentInfo);

        //发送异步通知给订单
        HashMap<String, Object> msgMap = new HashMap<>();

        msgMap.put("orderSN", paymentInfo.getOrderSn());
        msgMap.put("result", String.valueOf(ResultEnum.SUCCESS.getCode()));
        msgMap.put("payType", String.valueOf(PayTypeEnum.ALI_PAY.getCode()));

        try {
            activeMQUtil.sendMapMessage(CommonConstant.PAYMENT_SUCCESS_QUEUE_NAME, msgMap, activeMQUtil.getConnectionFactory().createConnection());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}
  • 注意上面使用到了一个支付宝请求参数类AlipayReqParam
package com.qingyun.gmall.payment.params;

import lombok.Data;

import java.math.BigDecimal;
import java.util.HashMap;

/**
 * Created with IntelliJ IDEA.
 * User: 李敷斌.
 * Date: 2020-03-14
 * Time: 18:20
 * Explain: 支付宝支付请求参数
 */
@Data
public class AlipayReqParam {

    private String out_trade_no;

    private String product_code="FAST_INSTANT_TRADE_PAY";

    private BigDecimal total_amount=new BigDecimal("0.01");

    private String subject;

    private String body;

    private String passback_params;

    private HashMap<String,String> extend_params;
}

至此支付宝支付与spring整合完毕,如果文章对你有帮助的话请给我点个赞哦,也可以关注博主我将持续更新一些组件使用教程❤️

发布了132 篇原创文章 · 获赞 55 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43199016/article/details/105136106