SpringBoot 对接支付宝电脑网站支付和手机网站支付

avatar 2022年02月15日00:15:25 6 1548 views
博主分享免费Java教学视频,B站账号:Java刘哥

前两天帮朋友做一个售货机项目,需要对接支付宝

这里记录下

一、支付宝沙箱准备

沙箱地址:https://open.alipay.com/platform/appDaily.htm

这里比较重要的,我用黄色框起来了

RSA2密钥生成用的 支付宝开放平台助手软件

生成私钥,然后复制私钥和公钥

把公钥填到页面上,会生成对应的支付宝公钥

最终我们需要的是私钥和支付宝公钥

 

下一步是下载沙箱工具,即沙箱支付宝APP,还有就是沙箱账号,这里就不一一列了

 

注意一点,这个回调通知地址,必须是外网环境才能收到支付结果通知

 

二、电脑网站支付

1、依赖

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

下面手机网站支付也是用这个依赖

 

2、支付配置常量类

package com.example.sens.common.config;

import lombok.Data;

/**
 * @author 言曌
 * @date 2022/2/13 2:43 下午
 */


@Data
public class AlipayConfig {

    // 商户appid
    public static String APPID = "2016101xxx省略";
    // 私钥 pkcs8格式的
    public static String RSA_PRIVATE_KEY = "MIIEvwIBxxx省略";
    // 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    public static String notify_url = "http://公网ip:8080/payment/alipay/notify";
    // 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址
    public static String return_url = "http://公网ip:8080/payment/paySuccess";
    // 请求网关地址
    public static String URL = "https://openapi.alipaydev.com/gateway.do";
    // 编码
    public static String CHARSET = "UTF-8";
    // 返回格式
    public static String FORMAT = "json";
    // 支付宝公钥
    public static String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkxxxxx省略";
    // 日志记录目录
    public static String log_path = "/log";
    // RSA2
    public static String SIGNTYPE = "RSA2";
}

 

3、电脑支付接口

这个会打开电脑支付的二维码

    /**
     * 支付创建
     * PC二维码支付
     *
     * @param orderNo
     * @param response
     */
    @RequestMapping("/payment/alipay/create")
    @Transactional(rollbackFor = Exception.class)
    public void alipayCreate(String orderNo,
                             HttpServletResponse response) {

        if (StringUtils.isEmpty(orderNo)) {
            throw new MyBusinessException("订单号不能为空");
        }
        LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Order::getOrderNo, orderNo);
        Order order = orderService.getOne(queryWrapper);
        if (order == null) {
            throw new MyBusinessException("订单不存在");
        }

        //获得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, "json", AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE);

        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.return_url);
        alipayRequest.setNotifyUrl(AlipayConfig.notify_url);


        // 商户订单号,商户网站订单系统中唯一订单号,必填
        String out_trade_no = orderNo;
        // 订单名称,必填
        String subject = "订单" + order.getOrderNo();
        // 付款金额,必填
        String total_amount = order.getTotalPrice().toString();
        // 商品描述,可空
        String body = "";

        alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
                + "\"total_amount\":\"" + total_amount + "\","
                + "\"subject\":\"" + subject + "\","
                + "\"body\":\"" + body + "\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

        //若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明
        //alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
        //		+ "\"total_amount\":\""+ total_amount +"\","
        //		+ "\"subject\":\""+ subject +"\","
        //		+ "\"body\":\""+ body +"\","
        //		+ "\"timeout_express\":\"10m\","
        //		+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        //请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节

        // form表单生产
        try {
            // 调用SDK生成表单
            String form = alipayClient.pageExecute(alipayRequest).getBody();
            response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
            response.getWriter().write(form);//直接将完整的表单html输出到页面
            response.getWriter().flush();
            response.getWriter().close();
        } catch (AlipayApiException | IOException e) {
            // TODO Auto-generated catch block
            log.error("支付宝电脑支付失败:", e);
        }
    }

效果是这样的

 

 

三、手机网站支付

依赖同上,支付常量类同上

主要贴下接口

    /**
     * 支付创建
     * 支付宝手机支付
     *
     * @param orderNo
     * @param response
     */
    @RequestMapping("/payment/alipay/create")
    @Transactional(rollbackFor = Exception.class)
    public void alipayCreate(String orderNo,
                             HttpServletResponse response) {

        if (StringUtils.isEmpty(orderNo)) {
            throw new MyBusinessException("订单号不能为空");
        }
        LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Order::getOrderNo, orderNo);
        Order order = orderService.getOne(queryWrapper);
        if (order == null) {
            throw new MyBusinessException("订单不存在");
        }

        // 商户订单号,商户网站订单系统中唯一订单号,必填
        String out_trade_no = orderNo;
        // 订单名称,必填
        String subject = "订单" + order.getOrderNo();
        // 付款金额,必填
        String total_amount = order.getTotalPrice().toString();
        // 商品描述,可空
        String body = "";
        // 超时时间 可空
        String timeout_express = "2m";
        // 销售产品码 必填
        String product_code = "QUICK_WAP_WAY";
        /**********************/
        // SDK 公共请求类,包含公共请求参数,以及封装了签名与验签,开发者无需关注签名与验签
        //调用RSA签名方式
        AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE);
        AlipayTradeWapPayRequest alipayTradeWapPayRequest = new AlipayTradeWapPayRequest();

        // 封装请求支付信息
        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        model.setOutTradeNo(out_trade_no);
        model.setSubject(subject);
        model.setTotalAmount(total_amount);
        model.setBody(body);
        model.setTimeoutExpress(timeout_express);
        model.setProductCode(product_code);
        alipayTradeWapPayRequest.setBizModel(model);
        // 设置异步通知地址
        alipayTradeWapPayRequest.setNotifyUrl(AlipayConfig.notify_url);
        // 设置同步地址
        alipayTradeWapPayRequest.setReturnUrl(AlipayConfig.return_url);

        // form表单生产
        String form = "";
        try {
            // 调用SDK生成表单
            form = client.pageExecute(alipayTradeWapPayRequest).getBody();
            response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
            response.getWriter().write(form);//直接将完整的表单html输出到页面
            response.getWriter().flush();
            response.getWriter().close();
        } catch (AlipayApiException | IOException e) {
            // TODO Auto-generated catch block
            log.error("支付宝手机支付失败:", e);
        }
    }

效果是这样的,会拉出支付宝APP

 

四、支付回调通知

支付失败/成功后,都会收到支付宝那边的通知

注意,必须部署到服务器上,即有公网ip,才能收到通知

/**
     * 支付创建
     *
     * @param response
     */
    @PostMapping("/payment/alipay/notify")
    @Transactional(rollbackFor = Exception.class)
    @ResponseBody
    public void alipayNotify(HttpServletRequest request,
                             HttpServletResponse response) throws UnsupportedEncodingException, AlipayApiException {

        log.info("alipayNotify进来了");
        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        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] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            params.put(name, valueStr);
        }
        log.info("alipayNotify params:" + JSON.toJSONString(params));


        //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
        //商户订单号
        String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
        //支付宝交易号
        String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
        //交易状态
        String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
        //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
        //计算得出通知验证结果
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)

        log.info("alipayNotify out_trade_no:" + out_trade_no);
        log.info("alipayNotify trade_no:" + trade_no);
        log.info("alipayNotify trade_status:" + trade_status);
        if ("TRADE_FINISHED".equals(trade_status) || "TRADE_SUCCESS".equals(trade_status)) {
            boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, "RSA2");
            log.info("alipayNotify verify_result:" + verify_result);
            if (verify_result) {
                LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(Order::getOrderNo, out_trade_no);
                Order order = orderService.getOne(queryWrapper);
                if (order != null) {
                    order.setPayTime(new Date());
                    order.setPayStatus(1);
                    orderService.updateById(order);

                    // 商品库存减少
                    LambdaQueryWrapper<OrderItem> orderItemLambdaQueryWrapper = new LambdaQueryWrapper<>();
                    orderItemLambdaQueryWrapper.eq(OrderItem::getOrderId, order.getId());
                    List<OrderItem> orderItemList = orderItemService.list(orderItemLambdaQueryWrapper);
                    for (OrderItem orderItem : orderItemList) {
                        GoodsInfo goodsInfo = goodsInfoService.getById(orderItem.getGoodsId());
                        if (goodsInfo != null) {
                            goodsInfo.setStockNum(goodsInfo.getStockNum() - orderItem.getGoodsCount());
                            goodsInfo.setSellNum(goodsInfo.getSellNum() + orderItem.getGoodsCount());
                            goodsInfoService.updateById(goodsInfo);
                        }
                    }

                    // 购物车清空
                    cartItemService.remove(new QueryWrapper<>());
                }
            }

        }
    }

 

说明,因为我们这个项目比较简单,暂时不考虑超买超卖,需要加分布式锁等问题

本文中主要介绍对接支付宝

 

 

 

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
匿名评论,评论回复后会有邮件通知

  

已通过评论:0   待审核评论数:0