美文网首页
支付宝对接 -- JAVA版

支付宝对接 -- JAVA版

作者: 千年的心 | 来源:发表于2021-03-23 11:51 被阅读0次

    应用配置

    • 登录支付宝平台,签约需要的服务
    • 切换到开放平台,创建应用并审核上线


      image.png

    支付配置

    获取方式已在字段注释中

    package org.weapon.core.pay.alipay.config;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * 支付配置信息
     *
     * @author lieber
     */
    @Data
    public class AliPayConfig implements Serializable {
    
        /**
         * 设置应用Id,创建应用时支付宝提供,后续可查看
         */
        private String appId;
    
        /**
         * 设置应用私钥,设置“接口加签方式”时设置的,后续不能查看,设置时牢记,文件名为:域名_私钥.txt, 非->应用私钥2048.txt
         */
        private String privateKey;
    
        /**
         * 应用公钥证书内容 appCertPublicKey_xxx.cert
         */
        private String appCertContent;
    
        /**
         * 支付宝公钥证书内容 -- alipayCertPublicKey_RSA2.cert
         */
        private String aliPayCertContent;
    
        /**
         * 支付宝根证书内容  alipayRootCert.cert
         */
        private String aliPayRootCertContent;
    
        /**
         * 可直接从证书中获取
         */
        private String publicKey;
    
    }
    

    各个方式的支付及查询处理

    引入支付宝SDK

            <!-- 支付宝sdk -->
            <dependency>
                <groupId>com.alipay.sdk</groupId>
                <artifactId>alipay-sdk-java</artifactId>
                <version>4.10.97.ALL</version>
            </dependency>
             <!-- 二维码sdk -->
            <dependency>
                <groupId>com.google.zxing</groupId>
                <artifactId>core</artifactId>
                <version>3.1.0</version>
            </dependency>
    

    通用实体

    • 下单结果,唤起支付参数
    package org.weapon.core.pay.center.ali;
    
    import lombok.Builder;
    import lombok.Data;
    
    /**
     * @author lieber
     */
    @Data
    @Builder
    public class OrderResult {
    
        private String body;
    
    }
    
    • 下单参数
    package org.weapon.core.pay.center.ali;
    
    import lombok.Data;
    
    import javax.validation.constraints.Max;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotBlank;
    
    /**
     * @author lieber
     */
    @Data
    public class PayParam {
    
        /** 商品标题/交易标题/订单标题/订单关键字 */
        @NotBlank(message = "subject不能为空")
        private String subject;
    
        /** 外部订单号 */
        @NotBlank(message = "系统订单号不能为空")
        private String outTradeNo;
    
        /** 支付总金额,单位分 */
        @Min(0)
        @Max(Integer.MAX_VALUE)
        private Integer totalAmount;
    
        /** 用户付款中途退出返回商户网站的地址 */
        private String quitUrl;
    
        /** 原样返回字符 */
        private String addition;
    
        /** 商品描述,不必填 */
        private String body;
    
        /** 通知地址 */
        @NotBlank(message = "通知地址不能为空")
        private String notifyUrl;
    
        @NotBlank(message = "回跳地址不能为空")
        private String redirectUrl;
    
        /** 支付者 */
        private String payerIp;
    
        private Integer timeoutExpress;
    
    }
    
    • 退款参数
    package org.weapon.core.pay.center.ali;
    
    import lombok.Data;
    
    import javax.validation.constraints.Max;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotBlank;
    
    /**
     * @author lieber
     */
    @Data
    public class RefundParam {
    
        /**
         * 商户订单号
         */
        @NotBlank(message = "原订单号不能为空")
        private String outTradeNo;
    
        /**
         * 商户退款单号
         */
        @NotBlank(message = "退款订单号不能为空")
        private String outRefundNo;
    
        /**
         * 退款金额
         */
        @Min(0)
        @Max(Integer.MAX_VALUE)
        private int refund;
    
        /**
         * 原订单总额
         */
        @Min(0)
        @Max(Integer.MAX_VALUE)
        private int total;
    
        /**
         * 退款原因;非必传
         */
        private String reason;
    
        @NotBlank(message = "通知地址不能为空")
        private String notifyUrl;
    
    }
    
    
    • 退款查询参数
    package org.weapon.core.pay.center.ali;
    
    import lombok.Data;
    
    import javax.validation.constraints.NotBlank;
    import java.io.Serializable;
    
    /**
     * @author lieber
     */
    @Data
    public class RefundQueryParam implements Serializable {
    
        /**
         * 原订单外部交易订单号
         */
        @NotBlank(message = "原订单号不能为空")
        private String outTradeNo;
    
        /**
         * 退款外部交易订单号
         */
        @NotBlank(message = "退款订单号不能为空")
        private String outRefundNo;
    
    }
    
    

    工具类

    • 生成二维码
    package org.weapon.core.pay.center.ali;
    
    import com.google.zxing.BarcodeFormat;
    import com.google.zxing.EncodeHintType;
    import com.google.zxing.MultiFormatWriter;
    import com.google.zxing.common.BitMatrix;
    import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
    import lombok.extern.slf4j.Slf4j;
    
    import javax.imageio.ImageIO;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.util.Base64;
    import java.util.Hashtable;
    import java.util.Map;
    
    
    @Slf4j
    public class QrCodeUtil {
    
        private static final int BLACK = 0xFF000000;
    
        private static final int WHITE = 0xFFFFFFFF;
    
        private static String toBufferedImage(BitMatrix matrix) throws IOException {
            int width = matrix.getWidth();
            int height = matrix.getHeight();
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
                }
            }
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            ImageIO.write(image, "png", stream);
            return Base64.getEncoder().encodeToString(stream.toByteArray());
        }
    
        /**
         * 将内容contents生成长宽均为width的图片
         */
        public static String getQrCodeImage(String contents, int width) {
            return getQrCodeImage(contents, width, width);
        }
    
        /**
         * 将内容contents生成长为width,宽为width的图片
         */
        public static String getQrCodeImage(String contents, int width, int height) {
            try {
                Map<EncodeHintType, Object> hints = new Hashtable<>();
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
                hints.put(EncodeHintType.CHARACTER_SET, "UTF8");
    
                BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints);
    
                return toBufferedImage(bitMatrix);
    
            } catch (Exception e) {
                log.error("create QR code error!", e);
                return null;
            }
        }
    }
    
    
    • 获取支付配置,可使用配置文件、数据库等
    package org.weapon.core.pay.center.ali;
    
    /**
     * @author lieber
     */
    public interface IAliPayConfigService {
    
        /**
         * 获取支付宝支付配置
         *
         * @return 配置
         */
        AliPayConfig getConfig();
    
    
    
    }
    
    

    支付宝支付及相关处理对接

    package org.weapon.core.pay.center.ali;
    
    import com.alipay.api.AlipayApiException;
    import com.alipay.api.AlipayClient;
    import com.alipay.api.CertAlipayRequest;
    import com.alipay.api.DefaultAlipayClient;
    import com.alipay.api.domain.*;
    import com.alipay.api.internal.util.AlipaySignature;
    import com.alipay.api.request.*;
    import com.alipay.api.response.*;
    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.UnsupportedEncodingException;
    import java.math.BigDecimal;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author lieber
     */
    @Slf4j
    public class AliPay {
    
        private final static String SERVER_URL = "https://openapi.alipay.com/gateway.do";
    
        private final static String SIGN_TYPE = "RSA2";
    
        private final static String FORMAT = "json";
    
        private final BigDecimal ONE_HUNDRED = new BigDecimal(100);
    
        private IAliPayConfigService configService;
    
        public AliPay(IAliPayConfigService configService) {
            this.configService = configService;
        }
    
        private AlipayClient client = null;
    
        /**
         * 扫码支付
         *
         * @param payParam 支付参数
         * @return 结果
         * @throws AlipayApiException 支付宝调用异常
         */
        public OrderResult callNative(PayParam payParam) throws AlipayApiException {
            AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
            AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
            model.setBody(payParam.getBody());
            model.setSubject(payParam.getSubject());
            model.setOutTradeNo(payParam.getOutTradeNo());
            String timeoutExpress = this.timeoutExpress(payParam.getTimeoutExpress());
            model.setTimeoutExpress(timeoutExpress);
            model.setQrCodeTimeoutExpress(timeoutExpress);
            model.setTotalAmount(new BigDecimal(payParam.getTotalAmount()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
            model.setProductCode("FACE_TO_FACE_PAYMENT");
            request.setBizModel(model);
            request.setNotifyUrl(payParam.getNotifyUrl());
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradePrecreateResponse response = this.getClient().sdkExecute(request);
            return OrderResult.builder().body(QrCodeUtil.getQrCodeImage(response.getQrCode(), 400)).build();
    
        }
    
        /**
         * APP支付
         *
         * @param payParam 支付参数
         * @return 结果
         * @throws AlipayApiException 支付宝调用异常
         */
        public OrderResult callApp(PayParam payParam) throws AlipayApiException {
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            if (payParam.getAddition() != null) {
                try {
                    model.setPassbackParams(URLEncoder.encode(payParam.getAddition(), StandardCharsets.UTF_8.name()));
                } catch (UnsupportedEncodingException e) {
                    log.error("加密参数出现异常:{} exception: ", payParam.getAddition(), e);
                }
            }
            model.setBody(payParam.getBody());
            model.setSubject(payParam.getSubject());
            model.setOutTradeNo(payParam.getOutTradeNo());
            model.setTimeoutExpress(this.timeoutExpress(payParam.getTimeoutExpress()));
            model.setTotalAmount(new BigDecimal(payParam.getTotalAmount()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
            model.setProductCode("QUICK_MSECURITY_PAY");
            request.setBizModel(model);
            request.setNotifyUrl(payParam.getNotifyUrl());
            request.setReturnUrl(payParam.getRedirectUrl());
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = this.getClient().sdkExecute(request);
            return OrderResult.builder().body(response.getBody()).build();
    
        }
    
        /**
         * H5支付
         *
         * @param payParam 支付参数
         * @return 结果
         * @throws AlipayApiException 支付宝调用异常
         */
        public OrderResult callH5(PayParam payParam) throws AlipayApiException {
            AlipayTradeWapPayModel wapPayModel = new AlipayTradeWapPayModel();
            if (payParam.getAddition() != null) {
                try {
                    wapPayModel.setPassbackParams(URLEncoder.encode(payParam.getAddition(), StandardCharsets.UTF_8.name()));
                } catch (UnsupportedEncodingException e) {
                    log.error("加密参数出现异常:{} exception: ", payParam.getAddition(), e);
                }
            }
            wapPayModel.setOutTradeNo(payParam.getOutTradeNo());
            wapPayModel.setSubject(payParam.getSubject());
            wapPayModel.setTotalAmount(new BigDecimal(payParam.getTotalAmount()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
            wapPayModel.setBody(payParam.getBody());
            wapPayModel.setTimeoutExpress(this.timeoutExpress(payParam.getTimeoutExpress()));
            wapPayModel.setProductCode("QUICK_WAP_WAY");
            if (null != payParam.getQuitUrl()) {
                wapPayModel.setQuitUrl(payParam.getQuitUrl());
            }
            AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
            request.setBizModel(wapPayModel);
            request.setNotifyUrl(payParam.getNotifyUrl());
            request.setReturnUrl(payParam.getRedirectUrl());
            AlipayTradeWapPayResponse response = this.getClient().pageExecute(request, "GET");
            return OrderResult.builder().body(response.getBody()).build();
    
        }
    
    
        /**
         * PC支付
         *
         * @param payParam 支付参数
         * @return 结果
         * @throws AlipayApiException 支付宝调用异常
         */
        public OrderResult callPc(PayParam payParam) throws AlipayApiException {
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            AlipayTradePagePayModel model = new AlipayTradePagePayModel();
            if (payParam.getAddition() != null) {
                try {
                    model.setPassbackParams(URLEncoder.encode(payParam.getAddition(), StandardCharsets.UTF_8.name()));
                } catch (UnsupportedEncodingException e) {
                    log.error("加密参数出现异常:{} exception: ", payParam.getAddition(), e);
                }
            }
            model.setBody(payParam.getBody());
            model.setSubject(payParam.getSubject());
            model.setOutTradeNo(payParam.getOutTradeNo());
            model.setTimeoutExpress(this.timeoutExpress(payParam.getTimeoutExpress()));
            model.setTotalAmount(new BigDecimal(payParam.getTotalAmount()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
            model.setProductCode("FAST_INSTANT_TRADE_PAY");
            request.setBizModel(model);
            request.setNotifyUrl(payParam.getNotifyUrl());
            request.setReturnUrl(payParam.getRedirectUrl());
            AlipayTradePagePayResponse response = this.getClient().pageExecute(request);
            return OrderResult.builder().body(response.getBody()).build();
    
        }
    
        /**
         * 订单查询
         *
         * @param outTradeNo 订单号
         * @return 订单信息
         * @throws AlipayApiException 支付宝调用异常
         */
        public AlipayTradeQueryResponse query(String outTradeNo) throws AlipayApiException {
            AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
            AlipayTradeQueryModel model = new AlipayTradeQueryModel();
            model.setOutTradeNo(outTradeNo);
            queryRequest.setBizModel(model);
            AlipayTradeQueryResponse response = this.getClient().certificateExecute(queryRequest);
            return response;
    
        }
    
        /**
         * 退款
         *
         * @param refundParam 退款参数
         * @return 退款响应
         * @throws AlipayApiException 支付宝调用异常
         */
        public AlipayTradeRefundResponse refund(RefundParam refundParam) throws AlipayApiException {
            AlipayTradeRefundModel model = new AlipayTradeRefundModel();
            model.setOutTradeNo(refundParam.getOutTradeNo());
            model.setOutRequestNo(refundParam.getOutRefundNo());
            model.setRefundReason(refundParam.getReason());
            model.setRefundAmount(new BigDecimal(refundParam.getRefund()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
    
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            request.setBizModel(model);
            request.setNotifyUrl(refundParam.getNotifyUrl());
    
            AlipayTradeRefundResponse response = this.getClient().certificateExecute(request);
            return response;
    
        }
    
        /**
         * 退款查询
         *
         * @param refundQueryParam 退款查询参数
         * @return 退款单信息
         * @throws AlipayApiException 支付宝调用异常
         */
        public AlipayTradeFastpayRefundQueryResponse queryRefund(RefundQueryParam refundQueryParam) throws AlipayApiException {
            AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
            AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
            model.setOutTradeNo(refundQueryParam.getOutTradeNo());
            model.setOutRequestNo(refundQueryParam.getOutRefundNo());
            request.setBizModel(model);
    
            AlipayTradeFastpayRefundQueryResponse response = this.getClient().certificateExecute(request);
            return response;
    
        }
    
        /**
         * 支付结果通知处理
         *
         * @param request 请求
         * @return 处理成功时返回数据信息,否则返回空
         */
        public Map<String, String> payNotify(HttpServletRequest request) {
            Map<String, String> body = this.getRequestBody(request);
            boolean flag = this.verifySign(body);
            log.info("验签结果为:{}", flag);
            if (!flag) {
                return null;
            }
            return body;
        }
    
        /**
         * 支付宝通知响应
         *
         * @param success 是否处理成功
         * @return 响应字符串
         */
        public String response(boolean success) {
            return success ? "success" : "fail";
        }
    
        private boolean verifySign(Map<String, String> notifyParam) {
            AliPayConfig config = this.configService.getConfig();
            String signType = notifyParam.get("sign_type");
            boolean flag = false;
            try {
                flag = AlipaySignature.rsaCheckV1(notifyParam, config.getPublicKey(), StandardCharsets.UTF_8.name(), signType);
            } catch (AlipayApiException e) {
                log.error("验证签名出现异常:{} {}", e, notifyParam);
            }
            return flag;
        }
    
        private Map<String, String> getRequestBody(HttpServletRequest request) {
            Map<String, String> params = new HashMap<>(10);
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                String[] values = requestParams.get(name);
                StringBuilder stringBuilder = new StringBuilder();
                for (String val : values) {
                    stringBuilder.append(val).append(",");
                }
                params.put(name, stringBuilder.substring(0, stringBuilder.length() - 1));
            }
            return params;
        }
    
        private AlipayClient getClient() {
            if (client == null) {
                client = buildClient();
                if (client == null) {
                    throw new IllegalArgumentException("参数错误");
                }
            }
            return client;
        }
    
        private AlipayClient buildClient() {
            AliPayConfig config = configService.getConfig();
            if (config == null) {
                return null;
            }
            // 构造client
            CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
            // 设置网关地址
            certAlipayRequest.setServerUrl(SERVER_URL);
            // 设置应用Id
            certAlipayRequest.setAppId(config.getAppId());
            // 设置应用私钥
            certAlipayRequest.setPrivateKey(config.getPrivateKey());
            // 设置请求格式,固定值json
            certAlipayRequest.setFormat(FORMAT);
            // 设置字符集
            certAlipayRequest.setCharset(StandardCharsets.UTF_8.name());
            // 设置签名类型
            certAlipayRequest.setSignType(SIGN_TYPE);
            // 设置应用公钥证书路径
            certAlipayRequest.setCertContent(config.getAppCertContent());
            // 设置支付宝公钥证书路径
            certAlipayRequest.setAlipayPublicCertContent(config.getAliPayCertContent());
            // 设置支付宝根证书路径
            certAlipayRequest.setRootCertContent(config.getAliPayRootCertContent());
            // 构造client
            try {
                return new DefaultAlipayClient(certAlipayRequest);
            } catch (AlipayApiException e) {
                log.error("创建AliPayClient异常:{}", e);
                return null;
            }
        }
    
        private String timeoutExpress(Integer timeoutExpress) {
            if (timeoutExpress == null) {
                return "15m";
            }
            return String.format("%dm", timeoutExpress);
        }
    
    }
    
    

    到此即可使用,上述中二维码和H5支付均已投入使用,APP和PC(未签约)支付尚未验证是否能唤起。希望对大家有帮助。

    相关文章

      网友评论

          本文标题:支付宝对接 -- JAVA版

          本文链接:https://www.haomeiwen.com/subject/bdwxhltx.html