Java 支付宝使用公钥证书加签 进行支付-查询-退款-转账

1、引入相关依赖 pom.xml

        <!--ali支付-->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.9.79.ALL</version>
        </dependency>
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.10</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.2.1</version>
        </dependency>
        <!--ali支付end-->

2、配置文件 application.yml

[注意]  证书文件必须放到服务器上,放到项目里的证书,在win环境下可以根据路径找到,但打成jar包后就找不到项目里的证书文件了,解决办法是判断服务器环境,根据不同环境拼接证书路径

证书文件目录结构如下,在Linux服务器上部署时,将pay文件夹和jar包放到同一个文件夹下

  

# 支付
pay:
  alipay:
    gatewayUrl: https://openapi.alipay.com/gateway.do
    appid: *****
    appPrivateKey: 
    alipayPublicKey:
    appCertPath: /pay/alipay/cert/appCertPublicKey****.crt
    alipayCertPath: /pay/alipay/cert/alipayCertPublicKey_RSA2.crt
    alipayRootCertPath: /pay/alipay/cert/alipayRootCert.crt
    returnUrl: ****
    notifyUrl: **** 

3、读取配置文件

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.annotation.PostConstruct;


@Data
@Slf4j
@ConfigurationProperties(prefix = "pay.alipay")
public class AlipayProperties {

    /**
     * 支付宝gatewayUrl
     */
    private String gatewayUrl;
    /**
     * 商户应用id
     */
    private String appid;
    /**
     * RSA私钥,用于对商户请求报文加签
     */
    private String appPrivateKey;
    /**
     * 支付宝RSA公钥,用于验签支付宝应答
     */
    private String alipayPublicKey;

    /**
     * 签名类型
     */
    private String signType = "RSA2";

    /**
     * 格式
     */
    private String formate = "json";
    /**
     * 编码
     */
    private String charset = "UTF-8";

    /**
     * 同步地址
     */
    private String returnUrl;

    /**
     * 异步地址
     */
    private String notifyUrl;

    /**
     * 应用公钥证书
     */
    private String appCertPath;

    /**
     * 支付宝公钥证书
     */
    private String alipayCertPath;

    /**
     * 支付宝根证书
     */
    private String alipayRootCertPath;


    /**
     * 最大查询次数
     */
    private static int maxQueryRetry = 5;
    /**
     * 查询间隔(毫秒)
     */
    private static long queryDuration = 5000;
    /**
     * 最大撤销次数
     */
    private static int maxCancelRetry = 3;
    /**
     * 撤销间隔(毫秒)
     */
    private static long cancelDuration = 3000;

    private AlipayProperties() {
    }

    /**
     * PostContruct是spring框架的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。
     */
    @PostConstruct
    public void init() {
        log.info(description());
    }

    public String description() {
        StringBuilder sb = new StringBuilder("\n支付宝Configs{");
        sb.append("支付宝网关: ").append(gatewayUrl).append("\n");
        sb.append(", appid: ").append(appid).append("\n");
        sb.append(", 商户RSA私钥: ").append(getKeyDescription(appPrivateKey)).append("\n");
        sb.append(", 应用公钥证书: ").append(appCertPath).append("\n");
        sb.append(", 支付宝公钥证书: ").append(alipayCertPath).append("\n");
        sb.append(", 支付宝根证书: ").append(alipayRootCertPath).append("\n");
        sb.append(", 支付宝RSA公钥: ").append(getKeyDescription(alipayPublicKey)).append("\n");
        sb.append(", 签名类型: ").append(signType).append("\n");
        sb.append(", 查询重试次数: ").append(maxQueryRetry).append("\n");
        sb.append(", 查询间隔(毫秒): ").append(queryDuration).append("\n");
        sb.append(", 撤销尝试次数: ").append(maxCancelRetry).append("\n");
        sb.append(", 撤销重试间隔(毫秒): ").append(cancelDuration).append("\n");
        sb.append("}");
        return sb.toString();
    }

    private String getKeyDescription(String key) {
        int showLength = 6;
        if (StringUtils.isNotEmpty(key) && key.length() > showLength) {
            return new StringBuilder(key.substring(0, showLength)).append("******")
                    .append(key.substring(key.length() - showLength)).toString();
        }
        return null;
    }
}

4、在启动时初始化支付宝客户端

import com.alipay.api.AlipayApiException;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;

import java.io.IOException;


@Configuration
@Slf4j
@EnableConfigurationProperties(AlipayProperties.class)
public class AlipayConfiguration {

    @Autowired
    private AlipayProperties properties;

//    /**
//     * 支付宝接口加签模式为公钥时使用
//     *
//     * @return
//     */
//    @Bean
//    public AlipayClient alipayClient() {
//        return new DefaultAlipayClient(properties.getGatewayUrl(),
//                properties.getAppid(),
//                properties.getAppPrivateKey(),
//                properties.getFormate(),
//                properties.getCharset(),
//                properties.getAlipayPublicKey(),
//                properties.getSignType());
//    }

    /**
     * 支付宝接口加签模式为公钥证书时使用
     *
     * @return
     */
    @Bean
    public DefaultAlipayClient alipayClient() throws AlipayApiException, IOException {
        log.info("---------创建支付宝客户端--------");

        //判断系统环境
        String osName = System.getProperty("os.name");
        log.info("-------系统环境--------" + osName);
        String serverpath = null;
        if (osName.startsWith("Windows")) {
            // windows
            serverpath = ResourceUtils.getURL("classpath:").getPath();
            serverpath += "static/app";
            log.info("-------------win文件路径----------" + serverpath);
//        } else if (osName.startsWith("Mac OS")) {
//            // 苹果
        } else {
            // unix or linux
            serverpath = System.getProperty("user.dir");
            log.info("-------------unix or linux文件路径----------" + serverpath);
        }
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        certAlipayRequest.setServerUrl(properties.getGatewayUrl());
        certAlipayRequest.setAppId(properties.getAppid());
        certAlipayRequest.setFormat(properties.getFormate());
        certAlipayRequest.setPrivateKey(properties.getAppPrivateKey());
        certAlipayRequest.setCharset(properties.getCharset());
        certAlipayRequest.setSignType(properties.getSignType());
        certAlipayRequest.setCertPath(serverpath + properties.getAppCertPath());
        certAlipayRequest.setAlipayPublicCertPath(serverpath + properties.getAlipayCertPath());
        certAlipayRequest.setRootCertPath(serverpath + properties.getAlipayRootCertPath());
        return new DefaultAlipayClient(certAlipayRequest);
    }
}

5、APP支付

import cn.hutool.core.date.DateUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayFundTransPayModel;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

/**
 * app支付
 */
@Slf4j
@Controller
@RequestMapping("/app/alipay/app")
public class AlipayAppPayController {

    @Autowired
    private AlipayProperties alipayProperties;
    @Autowired
    private AlipayClient alipayClient;
    @Autowired
    private AlipayProperties aliPayProperties;

    /**
     * 去支付
     * <p>
     *
     * @param response
     * @throws Exception
     */
    @RequestMapping("/createOrder")
    public void createOrder(HttpServletRequest request, HttpServletResponse response,
                            @RequestBody Map<String, String> mapParams) throws AlipayApiException {
       
        BaseResault resPo = new BaseResault();
        // 订单模型
        String productCode = "QUICK_MSECURITY_PAY";
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        Order bean = orderService.getOrderByNum(mapParams.get("orderNum"));
        model.setOutTradeNo(bean.getOrderNum());
        log.info("----订单编号----" + model.getOutTradeNo());
        model.setSubject(bean.getShopName());
        log.info("----subject----" + model.getSubject());
        model.setTotalAmount(bean.getTotalMoney().toString());
        log.info("----金额----" + model.getTotalAmount());
        model.setTimeoutExpress("30m");
        model.setProductCode(productCode);
        AlipayTradeAppPayRequest appPayRequest = new AlipayTradeAppPayRequest();
        appPayRequest.setNotifyUrl(‘服务器域名’ + alipayProperties.getNotifyUrl());
        log.info("----notifyUrl----" + appPayRequest.getNotifyUrl());
        appPayRequest.setBizModel(model);
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
        AlipayTradeAppPayResponse ali_response = alipayClient.sdkExecute(appPayRequest); //这里和使用公钥加签支付使用的方法不同
        //就是orderString 可以直接给客户端请求,无需再做处理。
        log.info("---------支付宝返回---------{}", ali_response.getBody());
        resPo.setObj(ali_response.getBody());
        resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode());
        resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage());
        ResponseJsonUtils.json(response, resPo);
    }
} 

6、查询订单

    /**
     * 订单查询(最主要用于查询订单的支付状态)
     *
     * @param orderNo 商户订单号
     * @return
     */
    @GetMapping("/query")
    public void query(HttpServletRequest request, HttpServletResponse response, String orderNo) throws AlipayApiException {
        BaseResault resPo = new BaseResault();
        AlipayTradeQueryRequest aliRequest = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel bizModel = new AlipayTradeQueryModel();
        bizModel.setTradeNo(orderNo);
        aliRequest.setBizModel(bizModel);
        AlipayTradeQueryResponse aliResponse = alipayClient.certificateExecute(aliRequest);
        if (aliResponse.isSuccess()) {
            log.info("查询支付宝支付状态-----调用成功{}", aliResponse.getBody());
            if (aliResponse.getTradeStatus().equals("TRADE_SUCCESS") || aliResponse.getTradeStatus().equals("TRADE_FINISHED")) {
                //订单编号
                String orderNum = aliResponse.getOutTradeNo();
                // 处理业务
                resPo.setObj(aliResponse.getBody());
                resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode());
                resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage());
                ResponseJsonUtils.json(response, resPo);
                return;
            } else {
                log.error("支付宝订单" + orderNo + "交易失败,交易状态:" + aliResponse.getTradeStatus());
                resPo.setObj(aliResponse.getBody());
                resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode());
                resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage());
                ResponseJsonUtils.json(response, resPo);
                return;
            }
        } else {
            log.info("查询支付宝支付状态------调用失败{}", aliResponse.getBody());
            resPo.setObj(aliResponse.getBody());
            resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode());
            resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage());
            ResponseJsonUtils.json(response, resPo);
            return;
        }
    }

7、转账给用户

 /**
     * alipay.fund.trans.uni.transfer(单笔转账接口)
     * <p>文档地址
     * https://docs.open.alipay.com/api_28/alipay.fund.trans.uni.transfer/
     *
     * @param response
     * @throws Exception
     */
    @RequestMapping("/alipayToUser")
    public void alipayToUser(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException {
        BaseResault resPo = new BaseResault();
        //转账金额  不能少于0.1元
        String money = request.getParameter("money");
       //支付宝账号,支持邮箱、电话号码
        String phone = request.getParameter("phone");
        //支付宝账户名称
        String name = request.getParameter("name");
        // 订单模型
        AlipayFundTransPayModel model = new AlipayFundTransPayModel();
        //商户端的唯一订单号
        String orderNum = UUID.randomUUID().toString().replace("-", "");
        log.info("============支付宝转账给用户-订单号:" + orderNum + "==============");
        model.setOutBizNo(orderNum);
        //订单总金额,单位为元,精确到小数点后两位
        model.setTransAmount(money);
        //业务产品码,
        //收发现金红包固定为:STD_RED_PACKET;
        //单笔无密转账到支付宝账户固定为:TRANS_ACCOUNT_NO_PWD;
        //单笔无密转账到银行卡固定为:TRANS_BANKCARD_NO_PWD
        model.setProductCode("TRANS_ACCOUNT_NO_PWD");
        //收款方信息
        Participant participant = new Participant();
        //参与方的唯一标识
        participant.setIdentity(phone);
        //参与方的标识类型,目前支持如下类型:
        //1、ALIPAY_USER_ID 支付宝的会员ID
        //2、ALIPAY_LOGON_ID:支付宝登录号,支持邮箱和手机号格式
        participant.setIdentityType("ALIPAY_LOGON_ID");
        //参与方真实姓名,如果非空,将校验收款支付宝账号姓名一致性。当identity_type=ALIPAY_LOGON_ID时,本字段必填。
        participant.setName(name);
        model.setPayeeInfo(participant);
        //业务备注
        model.setRemark("转账测试");
        model.setBizScene("DIRECT_TRANSFER");
        AlipayFundTransUniTransferRequest appPayRequest = new AlipayFundTransUniTransferRequest();
        appPayRequest.setBizModel(model);
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.fund.trans.uni.transfer(单笔转账接口)
        AlipayFundTransUniTransferResponse ali_response = alipayClient.certificateExecute(appPayRequest);
        if (ali_response.isSuccess()) {
            log.info("========支付宝转账给用户-调用成功=======" + ali_response.getBody());
            resPo.setObj(ali_response.getBody());
            resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode());
            resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage());
            ResponseJsonUtils.json(response, resPo);
        } else {
            log.info("========支付宝转账给用户-调用失败=======" + ali_response.getBody());
            resPo.setObj(ali_response.getBody());
            resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode());
            resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage());
            ResponseJsonUtils.json(response, resPo);
        }
    }

8、异步通知

 /**
     * 证书支付异步通知
     * https://docs.open.alipay.com/194/103296
     */
    @RequestMapping("/notify")
    public String notify(HttpServletRequest request) {
        log.info("-----------支付异步通知----------------");
        Map requestParams = request.getParameterMap();
        log.info(">>>支付宝回调参数:" + requestParams);
        Map<String, String> params = new HashMap<>();
        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(name, valueStr);
        }
        log.info(">>>支付宝回调参数解析:" + params);
        try {
            String alipayCertPath = System.getProperty("user.dir") + aliPayProperties.getAlipayCertPath();
            //切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
            boolean flag = AlipaySignature.rsaCertCheckV1(
                    params,
                    alipayCertPath,
                    aliPayProperties.getCharset(),
                    aliPayProperties.getSignType());
            if (flag) {
                log.info(">>>支付宝回调签名认证成功");
                //商户订单号
                String out_trade_no = params.get("out_trade_no");
                //交易状态
                String trade_status = params.get("trade_status");
                //交易金额
                String amount = params.get("total_amount");
                //商户app_id
                String app_id = params.get("app_id");

                if ("TRADE_SUCCESS".equals(trade_status) || "TRADE_FINISHED".equals(trade_status)) {
                    /**
                     * 自己的业务处理,一定要判断是否已经处理过订单
                     */
                    log.info("-----------^v^支付成功^v^------------");
                  
                    }
                } else {
                    log.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}", trade_status, params);
                }
            } else {
                log.info("支付宝回调签名认证失败,signVerified=false, params:{}", params);
                return "failure";
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            log.info("支付宝回调签名认证失败,signVerified=false, params:{}", params);
            return "failure";
        }
        return "success";
    }

9、退款

 /**
     * 同一订单多次退款
     *
     * @param orderNo 商户订单号
     * @return
     */
    @Override
    public String multipleRefunds(String orderNo, String refundAmount, String refundReason) throws AlipayApiException {
        log.info("------------支付宝退款开始--------------");
        AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        // 商户订单号
        model.setOutTradeNo(orderNo);
        // 退款金额 单位元
        model.setRefundAmount(refundAmount);
        // 退款原因
        model.setRefundReason(refundReason);
        // 退款订单号(同一个订单可以分多次部分退款,当分多次时必传)
        model.setOutRequestNo(UUID.randomUUID().toString());
        log.info("------退款参数-----{}-----{}-----{}------{}", orderNo, refundAmount, refundReason, model.getOutRequestNo());
        alipayRequest.setBizModel(model);
        AlipayTradeRefundResponse alipayResponse = alipayClient.certificateExecute(alipayRequest);
        log.info("--------支付宝退款结果---------" + alipayResponse.getBody() + "---------订单号--" + orderNo);
        return alipayResponse.getBody();
    }

  

posted @ 2020-04-01 21:24  执断剑披荆斩棘  阅读(3257)  评论(0编辑  收藏  举报