SpringBoot集成支付宝支付支付宝退款

支付宝支付

阅官方文档 https://opendocs.alipay.com/open/02e7gq

个人支付案例git地址[微信支付/支付宝支付/华为支付/苹果支付/小米支付]:https://gitee.com/wazk2008/demo-pay

前置:引入支付宝官方的jar

<!-- alipay sdk -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.13.33.ALL</version>
</dependency>

yml配置

alipay:
  app_id: 自己的appid
  private_key: 商户应用私钥
  app_cert_path: 商户应用公钥地址
  alipay_cert_path: 支付宝公钥地址
  alipay_root_cert_path: 支付宝根证书地址
  notify_url: www.baidu.com
  return_url: www.baidu.com
  alipay_gateway_url: https://openapi.alipaydev.com/gateway.do
  charset: UTF-8
  format: json
  sign_type: RSA2

配置类

@Component
@Slf4j
@Data
public class AlipayConfig {

    @Value("${alipay.app_id}")
    private String appId;

    // 商户应用私钥
    @Value("${alipay.private_key}")
    private String appPrivateKey;

    // 商户应用公钥证书地址
     @Value("${alipay.app_cert_path}")
    private String appPublicCertPath;

    // 支付宝公钥证书地址
     @Value("${alipay.alipay_cert_path}")
    private String alipayPublicCertPath;

    // 支付宝根证书地址
     @Value("${alipay.alipay_root_cert_path}")
    private String alipayRootCertPath;

    @Value("${alipay.notify_url}")
    private String notifyUrl;

    @Value("${alipay.return_url}")
    private String returnUrl;

    @Value("${alipay.alipay_gateway_url}")
    private String alipayGatewayUrl;

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

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

    @Value("${alipay.sign_type}")
    private String signType;


    /**
     * 设置支付宝客户端
     * @return
     */
    @Bean
    public AlipayClient getAlipayClient() {
        //构造client
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        //设置网关地址
        certAlipayRequest.setServerUrl(alipayGatewayUrl);
        //设置应用Id
        certAlipayRequest.setAppId(appId);
        //设置应用私钥
        certAlipayRequest.setPrivateKey(appPrivateKey);
        //设置请求格式,固定值json
        certAlipayRequest.setFormat(format);
        //设置字符集
        certAlipayRequest.setCharset(charset);
        //设置签名类型
        certAlipayRequest.setSignType(signType);
        //设置应用公钥证书路径
        certAlipayRequest.setCertPath(appPublicCertPath);
        //设置支付宝公钥证书路径
        certAlipayRequest.setAlipayPublicCertPath(alipayPublicCertPath);
        //设置支付宝根证书路径
        certAlipayRequest.setRootCertPath(alipayRootCertPath);
        //构造client
        try {
            return new DefaultAlipayClient(certAlipayRequest);
        } catch (AlipayApiException e) {
            log.error("构建AlipayClient对象失败");
            e.printStackTrace();
            return null;
        }
    }

}

1.下单支付(跳转支付宝app进行支付)

接口名称: 统一下单

接口地址: https://opendocs.alipay.com/open/02e7gq?scene=20

个人支付代码demo

@RestController
@RequestMapping(value = "/pay")
@Slf4j
public class PayController {

    @Autowired
    private AliPayService aliPayService;
  
    @PostMapping(value = "/ali")
    public Result<String> payAli (@RequestBody PayAliRequest payAliRequest) {
        try {
            log.info("支付宝支付接口开始执行,请求参数:{}", JSON.toJSONString(payAliRequest));
            String payAliResponse = aliPayService.pay(payAliRequest);
            log.info("支付宝付接口执行成功,请求参数:{},响应参数:{}", JSON.toJSONString(payAliRequest), JSON.toJSON(payAliResponse));
            return Result.getSuccess(payAliResponse);
        } catch (ResponseException e) {
            log.error("支付宝支付接口执行失败1,请求参数:{},异常原因:{}", JSON.toJSONString(payAliRequest), e.getMessage());
            e.printStackTrace();
            return Result.getFail(e);
        } catch (Exception e) {
            log.error("支付宝支付接口执行失败2,请求参数:{},异常原因:{}", JSON.toJSONString(payAliRequest), e.getMessage());
            e.printStackTrace();
            return Result.getFail(ResultCode.INTERNAL_SERVER_ERROR);
        }
    }

}
@Service
@Slf4j
public class AliPayService {

    @Autowired
    private AlipayConfig alipayConfig;
    @Autowired
    private AlipayClient alipayClient;


    // 支付宝支付,获取支付提交form表单字符串
    public String pay (PayAliRequest payAliRequest) {
        // 这里可以写各种校验
        // todo 校验该比订单的支付金额是否合法
        // todo 校验该笔订单的状态是否为待支付

        AlipayRequest alipayRequest = getAlipayRequest(payAliRequest);
        AlipayResponse aliPayResponse = null;
        try {
            // 支付宝返回数据是一个form表单
            aliPayResponse = alipayClient.pageExecute(alipayRequest);
            // 支付宝返回数据是一个url地址
            // aliPayResponse = alipayClient.pageExecute(alipayRequest, "GET");
        } catch (AlipayApiException e) {
            log.error("请求支付宝进行支付操作失败,支付宝支付请求参数为:{},请求支付宝的参数为:{}", JSON.toJSONString(payAliRequest), JSON.toJSONString(alipayRequest));
            e.printStackTrace();
            throw new ResponseException(ResultCode.ALI_PAY_ERROR);
        }

        String alipayResponseBody = aliPayResponse.getBody();
        log.info("请求支付宝进行支付操作成功,支付宝支付请求参数为:{},请求支付宝的参数为:{},支付宝的响应参数为:{}", JSON.toJSONString(payAliRequest), JSON.toJSONString(alipayRequest), alipayResponseBody);
        if (!StringUtils.isEmpty(alipayResponseBody)) {
            // todo 这里可以保存用户支付宝支付的唤醒记录
            return alipayResponseBody;
        } else {
            throw new ResponseException(ResultCode.ALI_PAY_ERROR);
        }
    }

    // 获取请求支付宝支付的参数
    public AlipayRequest getAlipayRequest (PayAliRequest payAliRequest) {
        boolean isApp = payAliRequest.getIsApp() > 0;
        if (isApp) {
            return getAlipayAppRequest(payAliRequest);
        } else {
            return getAlipayWapRequest(payAliRequest);
        }
    }

    // 获取app的支付宝支付请求参数
    private AlipayRequest getAlipayAppRequest (PayAliRequest payAliRequest) {
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setSubject(payAliRequest.getSubject());
        model.setBody(payAliRequest.getSubject());
        model.setOutTradeNo(payAliRequest.getOrderNo());
        model.setTimeoutExpress("30m");
        model.setTotalAmount(String.valueOf(CalcUtil.div(payAliRequest.getFee(), 100)));
        model.setProductCode("QUICK_MSECURITY_PAY"); // 固定值
        // 花呗分期相关的参数
        String hbFqNum = payAliRequest.getHbFqNum();
        if (isHbFq(hbFqNum)) {
            ExtendParams hbModel = new ExtendParams();
            hbModel.setHbFqNum(hbFqNum); // 花呗分期量 3 6 12
            hbModel.setHbFqSellerPercent(String.valueOf(100)); // 分期手续费,0表示用户承担,100表示商户承担
            model.setExtendParams(hbModel);
        }
        request.setBizModel(model);
        request.setNotifyUrl(alipayConfig.getNotifyUrl());
        request.setReturnUrl(alipayConfig.getReturnUrl());
        return request;
    }

    // 获取H5的支付宝支付请求参数
    private AlipayRequest getAlipayWapRequest (PayAliRequest payAliRequest) {
        AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
        //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        model.setBody(payAliRequest.getSubject());
        model.setSubject(payAliRequest.getSubject());
        model.setOutTradeNo(payAliRequest.getOrderNo());
        model.setTimeoutExpress("30m");
        model.setTotalAmount(String.valueOf(CalcUtil.div(payAliRequest.getFee(), 100)));
        model.setProductCode("QUICK_WAP_WAY"); // 固定值
        // 花呗分期相关的参数
        String hbFqNum = payAliRequest.getHbFqNum();
        if (isHbFq(hbFqNum)) {
            ExtendParams hbModel = new ExtendParams();
            hbModel.setHbFqNum(hbFqNum); // 花呗分期量 3 6 12
            hbModel.setHbFqSellerPercent(String.valueOf(100)); // 分期手续费,0表示用户承担,100表示商户承担
            model.setExtendParams(hbModel);
        }
        request.setBizModel(model);
        request.setNotifyUrl(alipayConfig.getNotifyUrl());
        request.setReturnUrl(alipayConfig.getReturnUrl());
        return request;
    }

    // 判断是否花呗分期
    private boolean isHbFq (String hbFqNum) {
        return "3".equals(hbFqNum) || "6".equals(hbFqNum) || "12".equals(hbFqNum);
    }
}

2.支付回调

@RestController
@RequestMapping(value = "/pay")
@Slf4j
public class PayController {

    @Autowired
    private AliPayService aliPayService;
  
    @PostMapping(value = "/aliPayCallback")
    public Result<Object> payAliCallback (@RequestBody PayAliCallbackRequest payAliCallbackRequest) {
        try {
            log.info("支付宝支付回调接口开始执行,请求参数:{}", JSON.toJSON(payAliCallbackRequest));
            aliPayService.payCallback(payAliCallbackRequest);
            log.info("支付宝支付回调接口执行成功,请求参数:{}", JSON.toJSON(payAliCallbackRequest));
            return Result.getSuccess();
        } catch (ResponseException e) {
            log.error("支付宝支付回调接口执行失败1,请求参数:{},异常原因:{}", JSON.toJSON(payAliCallbackRequest), e.getMessage());
            e.printStackTrace();
            return Result.getFail(e.getCode(), e.getMessage());
        } catch (Exception e) {
            log.error("支付宝支付回调接口执行失败2,请求参数:{},异常原因:{}", JSON.toJSON(payAliCallbackRequest), e.getMessage());
            e.printStackTrace();
            return Result.getFail(ResultCode.INTERNAL_SERVER_ERROR);
        }
    }

}
@Service
@Slf4j
public class AliPayService {

    @Autowired
    private AlipayConfig alipayConfig;
    @Autowired
    private AlipayClient alipayClient;
 
    // 支付宝支付回调
    public void payCallback (PayAliCallbackRequest payAliCallbackRequest) throws IllegalAccessException, AlipayApiException, ParseException {
        // 将 alipayCreateOrderCallbackRequest 转为 map格式
        Map<String, String> params = convertPayAliCallbackRequestToMap(payAliCallbackRequest);

        // 获取支付宝传入的参数并进行校验
        boolean validation = validateRequestParam(params);

        if (validation) {
            // todo 校验订单是否存在
            String outTradeNo = payAliCallbackRequest.getOut_trade_no();

            // todo 校验订单是否已支付

            // todo 校验支付金额
            Double buyerAmount = Double.valueOf(payAliCallbackRequest.getTotal_amount());

            // todo 修改支付和订单的状态
            // 支付宝的支付交易流水号
            String transactionId = payAliCallbackRequest.getTrade_no();
            // 用户支付时间
            Date payTime = DateUtils.parseDate(payAliCallbackRequest.getGmt_payment(), "yyyy-MM-dd HH:mm:ss");
            // 用户是否使用了蚂蚁花呗付款
            boolean isUsedAlipayHb = isAliPayHb(payAliCallbackRequest.getFund_bill_list());

            // todo 修改订单和支付信息

        } else {
            log.error("支付宝支付回调,签名校验失败,支付回调请求参数:{}", JSON.toJSONString(payAliCallbackRequest));
            throw new ResponseException(ResultCode.ALI_PAY_SIGN_ERROR);
        }

    }

    private Map<String, String> convertPayAliCallbackRequestToMap (PayAliCallbackRequest payAliCallbackRequest) throws IllegalAccessException {
        Map<String, String> map = new HashMap<>();
        Class<? extends PayAliCallbackRequest> clazz = payAliCallbackRequest.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String key = field.getName();
            Object val = field.get(payAliCallbackRequest);
            if (!Objects.isNull(val)) {
                map.put(key, String.valueOf(val));
            }
        }
        return map;
    }

    private Boolean validateRequestParam(Map<String, String> params) throws AlipayApiException {
        // 签名验证(对支付宝返回的数据验证,确定是支付宝返回的)
        return AlipaySignature.rsaCertCheckV1(params, alipayConfig.getAlipayPublicCertPath(), alipayConfig.getCharset(), alipayConfig.getSignType());
    }

    // 获取是否支付宝花呗支付
    private boolean isAliPayHb (String fundBillList) {
        if (StringUtils.isEmpty(fundBillList)) {
            return false;
        }
        JSONArray jsonArray = JSON.parseArray(fundBillList);
        for (Object obj : jsonArray) {
            JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(obj));
            String fundChannel = jsonObject.getString("fundChannel");
            if ("PCREDIT".equals(fundChannel)) {
                return true;
            }
        }
        return false;
    }
  
}

3.支付退款

@RestController
@RequestMapping(value = "/pay")
@Slf4j
public class RefundController {

    @Autowired
    private RefundService refundService;

    @PostMapping(value = "/orderRefund")
    public Result<Object> orderRefund (@RequestBody OrderRefundRequest orderRefundRequest) {
        try {
            log.info("订单退款接口开始执行,请求参数:{}", JSON.toJSONString(orderRefundRequest));
            refundService.orderRefund(orderRefundRequest);
            log.info("订单退款接口执行完成,请求参数:{}", JSON.toJSONString(orderRefundRequest));
            return Result.getSuccess();
        } catch (ResponseException e) {
            log.error("订单退款接口执行失败1,请求参数:{},异常原因:{}", JSON.toJSONString(orderRefundRequest), e.getMessage());
            e.printStackTrace();
            return Result.getFail(e.getCode(), e.getMessage());
        } catch (Exception e) {
            log.error("订单退款接口执行失败2,请求参数:{},异常原因:{}", JSON.toJSONString(orderRefundRequest), e.getMessage());
            e.printStackTrace();
            return Result.getFail(ResultCode.INTERNAL_SERVER_ERROR);
        }
    }

}
@Service
@Slf4j
public class RefundService {

    @Autowired
    private WechatService wechatService;

    // 订单退款
    public void orderRefund (OrderRefundRequest orderRefundRequest) {
        String orderNo = orderRefundRequest.getOrderNo();
        Integer curRefundFee = orderRefundRequest.getFee();

        // 获取该订单的支付详情
        OrderPayInfo payInfo = getOrderPayInfo(orderNo);
        // 获取该订单的支付金额
        int orderPaidFee = getOrderPaidFee(orderNo);
        // 获取该订单的已退金额
        int orderRefundedFee = getOrderRefundedFee(orderNo);

        if (curRefundFee+orderRefundedFee > orderPaidFee) {
            // 订单可退余额不足
            log.info("订单可退余额不足,退款请求参数:{}", JSON.toJSONString(orderRefundRequest));
            throw new ResponseException(ResultCode.REFUND_FEE_NOT_ENOUGH_ERROR);
        }

        // 获取该订单的支付方式
        PayTypeEnum payTypeEnum = getOrderPayTypeEnum(orderNo);
        switch (payTypeEnum) {
            case WECHAT_PAY:
                wechatService.refund(payInfo, curRefundFee);
                break;
            case ALI_PAY:
                break;
            case HUAWEI_PAY:
                break;
            case APPLE_PAY:
                break;
            case MI_PAY:
                break;
            default:
                break;
        }

    }

    // 获取订单的支付详情
    private OrderPayInfo getOrderPayInfo (String orderNo) {
        return new OrderPayInfo();
    }
    // 获取订单的支付金额
    private int getOrderPaidFee (String orderNo) {
        return 0;
    }

    // 获取订单的已退金额
    private int getOrderRefundedFee (String orderNo) {
        return 0;
    }

    // 获取订单的支付方式
    private PayTypeEnum getOrderPayTypeEnum (String orderNo) {
        return PayTypeEnum.WECHAT_PAY;
    }

}
@Service
@Slf4j
public class AliPayService {

    @Autowired
    private AlipayConfig alipayConfig;
    @Autowired
    private AlipayClient alipayClient;

    // 支付宝退款
    public void refund (OrderPayInfo payInfo, Integer refundFee) {
        // 从订单支付详payInfo中获取支付时微信的transaction_id
        String payTransactionId = "";

        // 生成退款时,请求微信的 out_refund_no
        String outRefundNo = "";

        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("trade_no", payTransactionId);
        bizContent.put("refund_amount", CalcUtil.div(refundFee, 100));
        bizContent.put("out_request_no", outRefundNo);
        request.setBizContent(bizContent.toString());
        try {
            AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
            if (response.isSuccess()) {
                // todo 退款申请成功,记录退款数据到db
            } else {
                log.error("支付宝退款失败");
                throw new ResponseException(ResultCode.ALI_PAY_REFUND_ERROR);
            }
        } catch (Exception e) {
            log.error("支付宝退款请求接口失败");
            throw new ResponseException(ResultCode.ALI_PAY_REFUND_ERROR);
        }
    }
  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

难过的风景

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值