支付宝对接 -- JAVA版

应用配置

  • 登录支付宝平台,签约需要的服务
  • 切换到开放平台,创建应用并审核上线
    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(未签约)支付尚未验证是否能唤起。希望对大家有帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java对接支付宝(Alipay)的沙箱环境,通常涉及以下几个步骤: 1. **创建账号和申请API密钥**:首先,在支付宝开放平台(https://open.alipay.com)注册并成为开发者,然后申请沙箱测试账号,获取应用公钥(App Key)和私钥(App Secret)。 2. **集成SDK**:下载官方提供的支付宝SDK,例如Alipay Mobile SDK或Alipay Open Platform SDK,将其添加到项目中。对于Java项目,可以选择maven依赖或者直接将jar包导入。 3. **配置环境变量**:设置`ALIPAY_DEBUG=true`启用调试模式,并配置好沙箱环境的相关参数,如Endpoint(沙箱环境为`https://openapi.alipay.com/gateway.do`),以及支付相关的公共参数,如支付宝账户的回传地址(通知URL)。 4. **编写接口代码**:根据SDK文档实现接口调用,包括创建订单(Order creation)、付款请求(Payment request)、查询订单状态(Order status query)等。常用的方法如`alipay.trade.page.pay`。 5. **发起交易**:在用户触发支付操作后,使用SDK中的相应方法发送请求到支付宝服务器,获取预支付链接或者直接跳转到支付宝页面完成支付。 6. **处理回调**:为接收来自支付宝的异步通知或同步结果,设置相应的回调路由,处理成功、失败等各种场景。 7. **测试和优化**:在本地环境中反复测试各项功能,确保接口调用正常,并对可能出现的问题进行调试和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值