java實現支付寶支付二

前期配置準備可參考該鏈接文檔:https://blog.csdn.net/weixin_42739916/article/details/104320830
 

1、設置支付寶環境參數,並導入支付SDK依賴

//支付寶環境參數設置
ali.pay.appId=AppID
ali.pay.appPrivateKey=應用私鑰
ali.pay.publicKey=支付寶公鑰
ali.pay.returnUrl=回調地址
ali.pay.notifyUrl=異步地址
ali.pay.gateway=網關(測試和正式環境的網關不同,需要注意)
ali.pay.signType=RSA2
ali.pay.charset=UTF-8
ali.pay.format=json
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.8.10.ALL</version>
</dependency>

2、支付寶支付狀態常量類

package com.ganguomob.dev.dsprwpt.domain.service.alipay;

import org.springframework.util.StringUtils;

/**
 * Created by luotj on 2019-08-15.
 */
public enum AliTradeStatus {

    WAIT_BUYER_PAY("WAIT_BUYER_PAY", "交易創建,等待買家付款"),
    TRADE_CLOSED("TRADE_CLOSED", "未付款交易超時關閉,或支付完成後全額退款"),
    TRADE_SUCCESS("TRADE_SUCCESS", "交易支付成功"),
    TRADE_FINISHED("TRADE_FINISHED", "交易結束,不可退款");
    private String code;
    private String msg;

    AliTradeStatus(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public static AliTradeStatus fromCode(String code) {

        if (StringUtils.pathEquals(code, WAIT_BUYER_PAY.code)) {
            return WAIT_BUYER_PAY;
        }
        if (StringUtils.pathEquals(code, TRADE_CLOSED.code)) {
            return TRADE_CLOSED;
        }
        if (StringUtils.pathEquals(code, TRADE_SUCCESS.code)) {
            return TRADE_SUCCESS;
        }
        if (StringUtils.pathEquals(code, TRADE_FINISHED.code)) {
            return TRADE_FINISHED;
        }

        throw new RuntimeException("code error");
    }
}

3、請求實體類

package com.ganguomob.dev.dsprwpt.ui.dto.request.api.alipay;

import lombok.Data;

import java.math.BigDecimal;

/**
 * @author: create by luotj
 * date: 2019-12-25 18:39
 **/
@Data
public class AliPayRequest {

    //支付金額
    private BigDecimal totalAmount;

    //客戶支付編碼
    private String outTradeNo;

    //訂單名稱
    private String subject;

    //商品描述
    private String body;

    //充值記錄id
    private Long rechargeId;

    //支付方式:1.支付寶
    private Integer type;
}

4、支付寶回調響應類

package com.ganguomob.dev.dsprwpt.ui.dto.response.api.alipay;

import com.ganguomob.dev.dsprwpt.domain.service.alipay.AliTradeStatus;
import io.swagger.annotations.ApiModel;
import lombok.Data;

/**
 * @author: create by luotj
 * date: 2019-12-28 16:02
 **/
@ApiModel
@Data
public class AlipayCallbackResponse {

    // notify_time	通知時間	Date	是	通知的發送時間。格式爲yyyy-MM-dd HH:mm:ss	2015-14-27 15:45:58
    private String notifyTime;

    //    notify_type	通知類型	String(64)	是	通知的類型	trade_status_sync
    private String notifyType;

    //    notify_id	通知校驗ID	String(128)	是	通知校驗ID	ac05099524730693a8b330c5ecf72da9786
    private String notifyId;

    //    app_id	支付寶分配給開發者的應用Id	String(32)	是	支付寶分配給開發者的應用Id	2014072300007148
    private String appId;

    //    charset	編碼格式	String(10)	是	編碼格式,如utf-8、gbk、gb2312等	utf-8
    private String charset;

    //    version	接口版本	String(3)	是	調用的接口版本,固定爲:1.0	1.0
    private String version;

    //    sign_type	簽名類型	String(10)	是	商戶生成簽名字符串所使用的簽名算法類型,目前支持RSA2和RSA,推薦使用RSA2	RSA2
    private String signType;

    //    sign	簽名	String(256)	是	請參考文末 異步返回結果的驗籤	601510b7970e52cc63db0f44997cf70e
    private String sign;

    //    trade_no	支付寶交易號	String(64)	是	支付寶交易憑證號	2013112011001004330000121536
    private String tradeNo;

    //    out_trade_no	商戶訂單號	String(64)	是	原支付請求的商戶訂單號	6823789339978248
    private String outTradeNo;

    //    out_biz_no	商戶業務號	String(64)	否	商戶業務ID,主要是退款通知中返回退款申請的流水號	HZRF001
    private String outBizNo;

    //    buyer_id	買家支付寶用戶號	String(16)	否	買家支付寶賬號對應的支付寶唯一用戶號。以2088開頭的純16位數字	2088102122524333
    private String buyerId;

    //    buyer_logon_id	買家支付寶賬號	String(100)	否	買家支付寶賬號	15901825620
    private String buyerLogonId;

    //    seller_id	賣家支付寶用戶號	String(30)	否	賣家支付寶用戶號	2088101106499364
    private String sellerId;

    //    seller_email	賣家支付寶賬號	String(100)	否	賣家支付寶賬號	[email protected]
    private String sellerEmail;

    //    trade_status	交易狀態	String(32)	否	交易目前所處的狀態,見下張表 交易狀態說明	TRADE_CLOSED
    private AliTradeStatus tradeStatus;

    //    total_amount	訂單金額	Number(9,2)	否	本次交易支付的訂單金額,單位爲人民幣(元)	20
    private String totalAmount;

    //    receipt_amount	實收金額	Number(9,2)	否	商家在交易中實際收到的款項,單位爲元	15
    private String receiptAmount;

    //    invoice_amount	開票金額	Number(9,2)	否	用戶在交易中支付的可開發票的金額	10.00
    private String invoiceAmount;

    //    buyer_pay_amount	付款金額	Number(9,2)	否	用戶在交易中支付的金額	13.88
    private String buyerPayAmount;

    //    point_amount	集分寶金額	Number(9,2)	否	使用集分寶支付的金額	12.00
    private String pointAmount;

    //    refund_fee	總退款金額	Number(9,2)	否	退款通知中,返回總退款金額,單位爲元,支持兩位小數	2.58
    private String refundFee;

    //    subject	訂單標題	String(256)	否	商品的標題/交易標題/訂單標題/訂單關鍵字等,是請求時對應的參數,原樣通知回來	當面付交易
    private String subject;

    //    body	商品描述	String(400)	否	該訂單的備註、描述、明細等。對應請求時的body參數,原樣通知回來	當面付交易內容
    private String body;

    //    gmt_create	交易創建時間	Date	否	該筆交易創建的時間。格式爲yyyy-MM-dd HH:mm:ss	2015-04-27 15:45:57
    private String gmtCreate;

    //    gmt_payment	交易付款時間	Date	否	該筆交易的買家付款時間。格式爲yyyy-MM-dd HH:mm:ss	2015-04-27 15:45:57
    private String gmtPayment;

    //    gmt_refund	交易退款時間	Date	否	該筆交易的退款時間。格式爲yyyy-MM-dd HH:mm:ss.S	2015-04-28 15:45:57.320
    private String gmtRefund;

    //    gmt_close	交易結束時間	Date	否	該筆交易結束時間。格式爲yyyy-MM-dd HH:mm:ss	2015-04-29 15:45:57
    private String gmtClose;

    //    fund_bill_list	支付金額信息	String(512)	否	支付成功的各個渠道金額信息,詳見下表 資金明細信息說明	[{“amount”:“15.00”,“fundChannel”:“ALIPAYACCOUNT”}]
    private String fundBillList;

    //    passback_params	回傳參數	String(512)	否	公共回傳參數,如果請求時傳遞了該參數,則返回給商戶時會在異步通知時將該參數原樣返回。本參數必須進行UrlEncode之後纔可以發送給支付寶	merchantBizType%3d3C%26merchantBizNo%3d2016010101111
    private String passbackParams;

    //    voucher_detail_list	優惠券信息	String	否	本交易支付時所使用的所有優惠券信息,詳見下表 優惠券信息說明	[{“amount”:“0.20”,“merchantContribute”:“0.00”,“name”:“一鍵創建券模板的券名稱”,“otherContribute”:“0.20”,“type”:“ALIPAY_DISCOUNT_VOUCHER”,“memo”:“學生卡8折優惠”]
    private String voucherDetailList;
}

5、支付查詢結果響應類

package com.ganguomob.dev.dsprwpt.ui.dto.response.api.alipay;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 阿里支付查詢響應
 *
 * @author: create by luotj
 * date: 2020-01-14 14:32
 **/
@ApiModel
@Data
public class AliQueryResponse {

    @ApiModelProperty("交易支付成功:1,其他類型返回0")
    public Integer status;

    @ApiModelProperty("支付返回data")
    public String data;

}

6、Controller層發起請求

package com.ganguomob.dev.dsprwpt.ui.controller.api.user;

import com.ganguomob.dev.dsprwpt.service.api.user.UserService;
import com.ganguomob.dev.dsprwpt.ui.dto.request.api.alipay.AliPayRequest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Api(tags = "01. 賬戶管理")
@RestController
@RequestMapping("/api/account")
public class AccountController {

    @Autowired
    private UserServiceImpl mUserService;

    @ApiOperation(value = "支付充值")
    @PutMapping("/pay")
    public String payRecharge() {
        //支付寶支付信息
        AliPayRequest aliPayRequest = new AliPayRequest()
                .setBody("測試Body")
                .setOutTradeNo("45313153")
                .setSubject("測試subject")
                .setRechargeId(1L)
                .setType(1)
                .setTotalAmount(new BigDecimal(100));
        return mUserService.alipayPreCreate(aliPayRequest);
    }

}

7、異步回調地址Controller

package com.ganguomob.dev.dsprwpt.ui.controller.api.alipay;

import com.ganguomob.dev.dsprwpt.service.api.alipay.AliPayService;
import com.ganguomob.dev.dsprwpt.ui.dto.response.api.alipay.AliQueryResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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;

/**
 * @author: create by luotj
 * date: 2019-12-27 10:47
 **/
@RestController
@Slf4j
@Api(tags = "12. 支付寶接口")
@RequestMapping("/api/alipay")
public class AdminAlipayNotificationController {

    @Autowired
    private UserServiceImpl mUserServiceImpl;

    @ApiOperation(value = "異步消息回調")
    @PostMapping("/notify/callback")
    public String alipayCallback(HttpServletRequest request) {
        return mUserServiceImpl.alipayCallback(request);
    }

    @ApiOperation(value = "查詢訂單支付狀態")
    @GetMapping("/check-status/{orderNo}")
    public AliQueryResponse checkOrderPayingStatus(
            @PathVariable("orderNo") @ApiParam(value = "訂單號", required = true) String orderNo) {
        return mUserServiceImpl.alipayQuery(orderNo);
    }
}

8、Service層生成預支付,並返回信息給前端生成支付二維碼(實現邏輯核心)

package com.ganguomob.dev.dsprwpt.service.api.user;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ganguomob.dev.base.infrastructure.exception.CommonBusinessException;
import com.ganguomob.dev.dsprwpt.constant.PayType;
import com.ganguomob.dev.dsprwpt.constant.RechargeStatus;
import com.ganguomob.dev.dsprwpt.domain.repository.fee.IFeeRecordRepository;
import com.ganguomob.dev.dsprwpt.domain.repository.user.IUserRepository;
import com.ganguomob.dev.dsprwpt.domain.service.alipay.AliTradeStatus;
import com.ganguomob.dev.dsprwpt.infrastructure.util.xinbao.XinBaoUtils;
import com.ganguomob.dev.dsprwpt.jooq.tables.pojos.FeeRecordPOJO;
import com.ganguomob.dev.dsprwpt.ui.dto.request.api.alipay.AliPayRequest;
import com.ganguomob.dev.dsprwpt.ui.dto.response.api.alipay.AliQueryResponse;
import com.ganguomob.dev.dsprwpt.ui.dto.response.api.alipay.AlipayCallbackResponse;
import com.google.common.base.CaseFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

@Slf4j
@Service
public class UserServiceImpl{

@Value("${ali.pay.appId}")
private String appId;

@Value("${ali.pay.appPrivateKey}")
private String privateKey;

@Value("${ali.pay.publicKey}")
private String aliKey;

@Value("${ali.pay.returnUrl}")
private String returnUrl;

@Value("${ali.pay.notifyUrl}")
private String notifyUrl;

@Value("${ali.pay.gateway}")
private String gateWay;

@Value("${ali.pay.signType}")
private String signType;

@Value("${ali.pay.charset}")
private String charset;

@Value("${ali.pay.format}")
private String format;


    /**
     * 支付寶支付回調邏輯
     * @param request
     * @return
     */
    @Override
    public String alipayCallback(HttpServletRequest request) {
        //將異步通知中收到的所有參數都存放到 map 中
        Map<String, String> paramsMap = callbackRequestToMap(request);

        alipayCheckRSAV1By(paramsMap);

        AlipayCallbackResponse response = mapToAlipayResponse(paramsMap);

        log.info("支付寶回調信息:訂單序列號{}", response.getOutTradeNo());

        if (response.getTradeStatus() == null) {
            log.error("ali pay callback response status is null " + "\n"
                    + "out trade no:" + response.getOutTradeNo());
            return "fail";
        }
        String orderNumber = response.getOutTradeNo();

        //支付查詢是否支付成功
        AliQueryResponse queryResponse = alipayQuery(orderNumber);
        if (queryResponse.getStatus() == 1) {
            //支付成功回調接口,並做狀態的判斷更改,邏輯
            
            if (recordPOJO.getRechargeStatus().equals(RechargeStatus.PAY_FAIL)) {
                //支付失敗,邏輯

            }
            return "success";
        } else {
            log.info("訂單序列號:{},支付失敗", orderNumber);
            return "fail";
        }
    }

//生成預支付邏輯方法
public String alipayPreCreate(AliPayRequest aliPayRequest) {
        log.info("預支付生成二維碼");
        //校驗數據
        checkPayParamsValid(aliPayRequest);
        //創建客戶端 
        AlipayClient alipayClient = new DefaultAlipayClient(gateWay, appId, privateKey, format,charset, aliKey, signType);
        //SDK中的請求實體類
        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

        //判斷訂單號是否爲空
        Map<String, Object> paramMap = new HashMap<>();
        if (aliPayRequest.getOutTradeNo().isEmpty()) {
            paramMap.put("type", "2");
            paramMap.put("data", "訂單號不能爲空");
            return null;
        }
        //設置異步、回調地址
        request.setNotifyUrl(notifyUrl);
        request.setReturnUrl(returnUrl);

        //設置主要參數,參數說明參考文檔
        paramMap.put("timeout_express", "5m");
        paramMap.put("out_trade_no", aliPayRequest.getOutTradeNo());
        paramMap.put("total_amount", aliPayRequest.getTotalAmount().doubleValue());
        paramMap.put("subject", aliPayRequest.getSubject());
        //將數據轉換爲json數據,並設置到request中
        ObjectMapper mapper = new ObjectMapper();
        String qrCode = null;
        try {
            //發起預支付請求
            String bizContent = mapper.writeValueAsString(paramMap);
            request.setBizContent(bizContent);
            AlipayTradePrecreateResponse precreateResponse = alipayClient.execute(request);
            //獲取支付二維碼內容
            qrCode = precreateResponse.getQrCode();
        } catch (AlipayApiException e) {
            log.error("AlipayApiException,錯誤信息:{},ErrMsg:{},msg:{}",
                    e.getErrCode(), e.getErrMsg(), e.getMessage());
        } catch (JsonProcessingException e) {
            log.error("轉換數據失敗,錯誤信息:{}", e.getMessage());
        }
        //返回支付二維碼內容(前段根據該內容生成支付二維碼)
        return qrCode;
    }


    /**
     * 阿里支付查詢
     *
     * @return
     */
    @Override
    public AliQueryResponse alipayQuery(String outTradeNo) {
        AlipayClient alipayClient = new DefaultAlipayClient(gateWay, appId, privateKey, format,
                charset, aliKey, signType);

        //創建查詢對象
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("out_trade_no", outTradeNo);
        //轉換爲json數據
        ObjectMapper mapper = new ObjectMapper();
        AliQueryResponse queryResponse = new AliQueryResponse();
        try {
            String queryMap = mapper.writeValueAsString(paramMap);
            request.setBizContent(queryMap);
            //執行查詢
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            JSONObject jsonObject = JSON.parseObject(response.getBody())
                    .getJSONObject("alipay_trade_query_response");
            String code = jsonObject.getString("code");
            String msg = jsonObject.getString("msg");
            if ("10000".equals(code) && "Success".equals(msg)) {
                //支付成功,邏輯
                queryResponse.setStatus(1)
                            .setData(tradeStatus);
            } else {
                //其它狀態,邏輯
                queryResponse.setStatus(0)
                            .setData(tradeStatus);
            }
        } catch (Exception e) {
            log.error("數據轉換異常:msg{}", e.getMessage());
        }
        return queryResponse;
    }


    private Map<String, String> callbackRequestToMap(HttpServletRequest request) {
        // 獲取支付寶POST過來反饋信息
        Map<String, String> 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] + ",";
            }
            // 亂碼解決,這段代碼在出現亂碼時使用。
            // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, name), valueStr);
        }
        return params;
    }

    //檢查簽名
    public void alipayCheckRSAV1By(Map<String, String> paramsMap) {
        boolean signVerified = false;
        try {
            //調用SDK驗證簽名
            signVerified = AlipaySignature.rsaCheckV1(paramsMap, aliKey, charset, signType);

            log.info("pay callback rsa check flag:" + signVerified);
        } catch (AlipayApiException e) {
            e.printStackTrace();
            log.error("pay callback rsa check error" + "\n" +
                    "error code:" + e.getErrCode() + "\n" +
                    "error msg:" + e.getErrMsg());

            throw new RuntimeException();
        }
    }


    //校驗數據
    private void checkPayParamsValid(AliPayRequest params) {

        //訂單編碼不能爲空
        if (Strings.isBlank(params.getOutTradeNo())) {
            throw new RuntimeException("訂單編碼不能爲空");
        }
        //商品信息不能爲空
        if (Strings.isBlank(params.getBody())) {
            throw new RuntimeException("商品信息不能爲空");
        }
        //訂單名稱不能爲空
        if (Strings.isBlank(params.getSubject())) {
            throw new RuntimeException("訂單名稱不能爲空");
        }
        //支付價格必須大於0
        if (params.getTotalAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new RuntimeException("支付價格必須大於0");
        }
    }

    private AlipayCallbackResponse mapToAlipayResponse(Map<String, String> params) {
        AlipayCallbackResponse response = new AlipayCallbackResponse();
        response.setNotifyTime(params.get("notify_time"));
        response.setNotifyType(params.get("notify_type"));
        response.setNotifyId(params.get("notify_id"));
        response.setAppId(params.get("app_id"));
        response.setCharset(params.get("charset"));
        response.setVersion(params.get("version"));
        response.setSignType(params.get("sign_type"));
        response.setSign(params.get("sign"));
        response.setTradeNo(params.get("trade_no"));
        response.setOutTradeNo(params.get("out_trade_no"));
        response.setOutBizNo(params.getOrDefault("out_biz_no", ""));
        response.setBuyerId(params.getOrDefault("buyerId", ""));
        response.setBuyerLogonId(params.getOrDefault("buyer_logon_id", ""));
        response.setSellerId(params.getOrDefault("seller_id", ""));
        response.setSellerEmail(params.getOrDefault("seller_email", ""));
        response.setTradeStatus(params.containsKey("trade_status") ?
                AliTradeStatus.fromCode(params.get("trade_status")) : null);
        response.setTotalAmount(params.getOrDefault("total_amount", ""));
        response.setReceiptAmount(params.getOrDefault("receipt_amount", ""));
        response.setInvoiceAmount(params.getOrDefault("invoice_amount", ""));
        response.setBuyerPayAmount(params.getOrDefault("buyer_pay_amount", ""));
        response.setPointAmount(params.getOrDefault("point_amount", ""));
        response.setRefundFee(params.getOrDefault("refund_fee", ""));
        response.setSubject(params.getOrDefault("subject", ""));
        response.setBody(params.getOrDefault("body", ""));
        response.setGmtCreate(params.getOrDefault("gmt_create", ""));
        response.setGmtPayment(params.getOrDefault("gmt_payment", ""));
        response.setGmtRefund(params.getOrDefault("gmt_refund", ""));
        response.setGmtClose(params.getOrDefault("gmt_close", ""));
        response.setFundBillList(params.getOrDefault("fund_bill_list", ""));
        response.setPassbackParams(params.getOrDefault("passback_params", ""));
        response.setVoucherDetailList(params.getOrDefault("voucher_detail_list", ""));
        return response;
    }
}

 

支付頁面效果:

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