支付宝支付-电脑网站支付-实现

323 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

1. 交易

0ba3e82ad37ecf8649ee4219cfe9d16b.png

发起交易

官方文档地址 文档地址

[接入流程]

  • 前端发起支付
  • 后端创建订单
  • 后端访问支付宝发起交易接口-订单号
  • 后端返回form表单到前端
  • 前端自动提交表单,重定向到支付宝扫码支付页面
  • 支付
    • 扫码
    • 账号密码
  • 操作完成,等待重定向到后端设置好的url

[核心代码]

@Override
public String tradeCreate(Long productId) {
    /**
     * 订单服务 createOrderByProductId
     *  获取商品信息
     *  生成订单
     * */
    Order order = orderService.createOrderByProductId(productId);


    /**
     * 支付
     *  创建请求对象
     *  封装数据
     * */
    // 创建请求对象
    AlipayTradePagePayRequest request=new AlipayTradePagePayRequest();
    // 封装数据
    AlipayTradePagePayModel bizModel=new AlipayTradePagePayModel();
    bizModel.setOutTradeNo(order.getOrderNo());
    bizModel.setTotalAmount(order.getTotalFee().toString()); //单位是 元
    bizModel.setSubject(order.getTitle());
    bizModel.setProductCode("FAST_INSTANT_TRADE_PAY"); // 默认的
    request.setBizModel(bizModel);
    request.setNotifyUrl(notifyUrl+"/api/ali-pay/trade/notify");
    request.setReturnUrl(returnUrl); // 用户支付后支付宝会以GET方法请求returnUrl,并且携带out_trade_no,trade_no,total_amount等参数.

    AlipayTradePagePayResponse response=null;
    try{
        //完成签名并执行请求
        response=alipayClient.pageExecute(request);
        if(response.isSuccess()){
            log.debug("调用成功");
            return response.getBody();
        }
        else{
            log.error("调用失败");
            log.error(response.getMsg(), response.getCode());
            return null;
        }
    }catch(AlipayApiException e){
        log.error("调用异常");
        log.error(response.getMsg(), response.getCode());
        return null;
    }
}

交易通知

官方文档地址 文档地址

[接入流程]

  • 后端编写post接口,并且路径和下单时传入的notifyUrl一致
  • 用户支付完成,支付宝自动向该接口推送消息
  • 后端根据下单传入的订单号查询订单
  • 后端修改订单状态

[核心代码]

@PostMapping("/trade/notify")
public Object tradeNotify(@RequestParam Map<String,String> params){
    log.debug("收到支付宝回调");
    log.info("\n{}",params);
    Object object = aliPayService.tradeNotify(params);
    return object;
}
@Override
    public Object tradeNotify(Map<String, String> data) {
        //验签
        boolean signVerified=false;
        try{
            signVerified= AlipaySignature.rsaCheckV1(data,aliPayPublicKey, AlipayConstants.CHARSET_UTF8,AlipayConstants.SIGN_TYPE_RSA2);
            //验签成功
            if(signVerified){
                log.debug("验签成功");
                //从数据库中查出对应的订单
//                Order order = orderService.;
                synchronized(this){
                    // 通知可能会重复,必须处理 可能 24h8次 (需要做幂等)
                    // 处理订单状态
                    // 处理支付记录
                    // 记录支付日志
                    log.info("订单{}的支付记录处理成功,支付记录id为.",data.get("out_trade_no"));
                }
                //除了success外其他返回均认为是失败
                return "success";
            }
            //验签失败
            else{
                log.error("验签失败");
                return "failure";
            }
        }
        catch(AlipayApiException e){
            log.error("验签异常");
            return "failure";
        }
    }

查询交易

官方文档地址 文档地址

[接入流程]

  • 根据系统的订单号或者支付宝的单号查询交易信息

[核心代码]

private Map<String,Object> queryPay(String orderNo){
    //请求
    AlipayTradeQueryRequest request=new AlipayTradeQueryRequest();
    //数据
    AlipayTradeQueryModel bizModel=new AlipayTradeQueryModel();
    bizModel.setOutTradeNo(orderNo);
    request.setBizModel(bizModel);
    try{
        //完成签名并执行请求
        AlipayTradeQueryResponse response=alipayClient.execute(request);
        if(response.isSuccess()){
            log.debug("查询订单{}成功",orderNo);
            HashMap<String,Object> resultMap= JSONObject.parseObject(response.getBody(),HashMap.class);
            return resultMap;
        }
        else{
            log.error("查询订单{}失败,响应数据是{}.",orderNo,response.getBody());
            return null;
        }
    }
    catch(AlipayApiException e){
        log.error("查询订单{}异常",orderNo);
        return null;
    }
}

关闭交易

官方文档地址 文档地址

[接入流程]

  • 根据系统的订单号或者支付宝的单号关闭交易信息

[核心代码]

@Override
public void closeOrder(String orderNo) {
    // 远程关单
    cancelPay(orderNo);
    // 处理业务
    // xxx
}
private boolean cancelPay(String orderNo){
    //请求
    AlipayTradeCloseRequest request=new AlipayTradeCloseRequest();
    //数据
    AlipayTradeCloseModel bizModel=new AlipayTradeCloseModel();
    bizModel.setOutTradeNo(orderNo);
    request.setBizModel(bizModel);
    try{
        //完成签名并执行请求
        AlipayTradeCloseResponse response=alipayClient.execute(request);
        if(response.isSuccess()){
            log.debug("订单{}取消成功",orderNo);
        }
        else{
            log.debug("订单{}未创建,因此也可认为本次取消成功.",orderNo);
        }
        return true;
    }
    catch(AlipayApiException e){
        log.error("订单{}取消异常",orderNo);
        return false;
    }
}

2. 退款

支付宝退款流程.png

发起退款

官方文档地址 文档地址

[接入流程]

  • 全部退款

    • 根据系统的订单号或者支付宝的单号发起退款
    • 退款金额不能大于总金额
    • 后端访问支付宝退款接口
    • 支付宝同步返回退款信息
    • 后端记录退款详情
  • 部分退款

    • 生成退款单No.
    • 根据系统的订单号或者支付宝的单号发起退款,且必须填写退款单No.
    • 退款金额不能大于总金额,可分多笔退款单多次退款
    • 后端访问支付宝退款接口
    • 支付宝同步返回退款信息
    • 后端记录退款详情

[核心代码]

@Override
public Object refund(String orderNo) {
    //请求
    AlipayTradeRefundRequest request=new AlipayTradeRefundRequest();
    //数据
    AlipayTradeRefundModel bizModel=new AlipayTradeRefundModel();
    //订单号
    bizModel.setOutTradeNo(orderNo);
    //退款单号 可选 如需部分退款,则此参数必传。
    String outRequestNo = orderNo + "-refund-" + UUID.randomUUID().toString().substring(0, 10);
    bizModel.setOutRequestNo(outRequestNo);
    bizModel.setRefundAmount("1.0"); // 退款金额。需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数。
    bizModel.setRefundReason("不喜欢了");
    request.setBizModel(bizModel);
    HashMap<String,Object> resultMap=new HashMap<>();
    try{
        //完成签名并执行请求
        AlipayTradeRefundResponse response=alipayClient.execute(request);
        //成功则说明退款成功了
        resultMap.put("data",response.getBody());
        if(response.isSuccess()){
            resultMap.put("isRefundSuccess",true);
            log.info("订单{} 退款单号{}退款成功",orderNo,outRequestNo);
        }
        else{
            resultMap.put("isRefundSuccess",false);
            log.error("订单{}退款失败",orderNo);
        }
        return resultMap;
    }
    catch(AlipayApiException e){
        resultMap.put("isRefundSuccess",false);
        log.error("订单{}退款异常",orderNo);
        return resultMap;
    }
}

查询退款

官方文档地址 文档地址

[接入流程]

  • 全部退款

    • 根据系统的订单号或者支付宝的单号查询交易信息
  • 部分退款

    • 根据系统的订单号或者支付宝的单号发起查询,且必须填写退款单No.
    • 单笔退款详情只有一个,如需查询一个订单下的所有退款,需要轮询退款单No.

[核心代码]

@Override
public Map<String,Object> queryRefund(String orderNo, String outRequestNo){
    AlipayTradeFastpayRefundQueryRequest request=new AlipayTradeFastpayRefundQueryRequest();
    AlipayTradeFastpayRefundQueryModel bizModel=new AlipayTradeFastpayRefundQueryModel();
    //订单号
    bizModel.setOutTradeNo(orderNo);
    //退款单号
    bizModel.setOutRequestNo(outRequestNo);
    //想要额外返回的数据(也就是文档中响应可选的数据)
    ArrayList<String> extraResponseDatas=new ArrayList<>();
    extraResponseDatas.add("refund_status");
    bizModel.setQueryOptions(extraResponseDatas);
    request.setBizModel(bizModel);
    try{
        //完成签名并执行请求
        AlipayTradeFastpayRefundQueryResponse response=alipayClient.execute(request);
        if(response.isSuccess()){
            log.debug("退款{}查询成功",outRequestNo);
            return JSONObject.parseObject(response.getBody(),HashMap.class);
        }
        else{
            log.info("退款{}查询失败",outRequestNo);
            return null;
        }
    }
    catch(AlipayApiException e){
        log.debug("退款{}查询异常",outRequestNo);
        return null;
    }
}

3. 账单

对账

官方文档地址 文档地址

[账单类型]

  • trade
    • 商户基于支付宝交易收单的业务账单(交易账单)
  • signcustomer
    • 基于商户支付宝余额收入及支出等资金变动的账务账单(账号财务账单)

[日期]

  • 日账单

    • yyyy-MM-dd : 只能查前一日(次日9点后查前日)
  • 月账单

    • yyyy-MM : 只能查上月(下月3日后查上月)

[接入流程]

  • 根据类型和日期请求对账接口
  • 从返回体拿到bill_download_url字段并下载(zip压缩的csv)

[核心代码]

@Override
public String queryBillDownloadUrl(String billType, String billDate) {
    //请求
    AlipayDataDataserviceBillDownloadurlQueryRequest request=new AlipayDataDataserviceBillDownloadurlQueryRequest();
    //数据
    AlipayDataDataserviceBillDownloadurlQueryModel bizModel=new AlipayDataDataserviceBillDownloadurlQueryModel();
    bizModel.setBillType(billType);
    bizModel.setBillDate(billDate);
    request.setBizModel(bizModel);
    try{
        //完成签名并执行请求
        AlipayDataDataserviceBillDownloadurlQueryResponse response=alipayClient.execute(request);
        if(response.isSuccess()){
            log.debug("获取账单下载url成功");
            return response.getBillDownloadUrl();
        }
        else{
            log.error("获取账单下载url失败");
            return null;
        }
    }
    catch(AlipayApiException e){
        log.error("获取账单下载url异常");
        return null;
    }
}