【開發技巧】-- 一篇女朋友也能看懂的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整合完畢,如果文章對你有幫助的話請給我點個贊哦,也可以關注博主我將持續更新一些組件使用教程❤️

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章