zoukankan      html  css  js  c++  java
  • 支付宝手机网站支付接入(沙箱环境)

    参考阿里云文档:https://docs.open.alipay.com/203/105285/

    1.调用流程

    手机网站支付产品包含两类API:

    • 页面跳转类:需要从前端页面以Form表单的形式发起请求,浏览器会自动跳转至支付宝的相关页面(一般是收银台或签约页面),用户在该页面完成相关业务操作后再回跳到商户指定页面。例如本产品中的手机网站支付接口alipay.trade.wap.pay。
    • 系统调用类:直接从服务端发起HTTP请求,支付宝会同步返回请求结果。例如本产品中的交易查询等配套API。
      调用流程:
    • 如上图所示,用户在商户的H5网站下单支付后,商户系统按照手机网站支付接口alipay.trade.wap.payAPI的参数规范生成订单数据,然后在前端页面通过Form表单的形式请求到支付宝。此时支付宝会自动将页面跳转至支付宝H5收银台页面,如果用户手机上安装了支付宝APP,则自动唤起支付宝APP。开发者需要关注安装了支付宝和未安装支付宝的两种测试场景,对于在手机浏览器唤起H5页面的模式下,如果安装了支付宝却没有唤起,大部分原因是当前浏览器不在支付宝配置的白名单内。
    • 对于商户app内嵌webview中的支付场景,建议集成支付宝App支付产品。或者您可以使用手机网站支付转Native支付的方案,不建议在您的APP中直接接入手机网站支付。
    • 用户在支付宝APP或H5收银台完成支付后,会根据商户在手机网站支付API中传入的前台回跳地址return_url自动跳转回商户页面,同时在URL请求中以Query String的形式附带上支付结果参数,详细回跳参数见“手机网站支付接口alipay.trade.wap.pay”前台回跳参数。

    注意:在ios系统中,唤起支付宝App支付完成后,不会自动回到浏览器或商户APP。用户可手工切回到浏览器或商户APP;支付宝H5收银台会自动跳转回商户return_url指定的页面。

    • 支付宝还会根据原始支付API中传入的异步通知地址notify_url,通过POST请求的形式将支付结果作为参数通知到商户系统,详情见支付结果异步通知。
    • 除了正向支付流程外,支付宝也提供交易查询、关闭、退款、退款查询以及对账等配套API。

    特别注意:

    • 由于前台回跳的不可靠性,前台回跳只能作为商户支付结果页的入口,最终支付结果必须以异步通知或查询接口返回为准,不能依赖前台回跳。
    • 商户系统接收到异步通知以后,必须通过验签(验证通知中的sign参数)来确保支付通知是由支付宝发送的。详细验签规则参考异步通知验签。
    • 接受到异步通知并验签通过后,一定要检查通知内容,包括通知中的app_id, out_trade_no, total_amount是否与请求中的一致,并根据trade_status进行后续业务处理

    2.环境接入

    2.1SDK包下载:

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

    2.2公私钥文件配置


    3.沙箱测试

    沙箱环境提供测试账户:买家账号,商户账户。可自行充值

    3.1java后端发送支付请求:

    @RequestMapping("/pay")
    public void pay(HttpServletResponse response, String amount) {
       String form = null;
       try {
           form = PayUtil.pay(amount);
       } catch (AlipayApiException e) {
           form = "pay error!";
           e.printStackTrace();
       }
       response.setContentType("text/html;charset=" + "utf-8");
       try {
           response.getWriter().write(form);//直接将完整的表单html输出到页面
           response.getWriter().flush();
           response.getWriter().close();
       } catch (IOException e) {
           e.printStackTrace();
       }
    
    }
    
    public static String pay(String amount) throws AlipayApiException {
       AlipayClient alipay_client = new DefaultAlipayClient(server_url, app_id, private_key, format, charset,   alipay_pubic_key, sign_type);
       AlipayTradeWapPayRequest alipay_request = new AlipayTradeWapPayRequest();
       AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
       model.setOutTradeNo(getOutOrderNo());
       model.setSubject(subject);
       model.setTotalAmount(amount);
       model.setTimeoutExpress(timeout_express);
       model.setProductCode(product_code);
       alipay_request.setBizModel(model);
       // 设置异步通知地址
       alipay_request.setNotifyUrl(notify_url);
       // 设置同步地址
       alipay_request.setReturnUrl(return_url);
    
       // 调用SDK生成表单
       AlipayResponse alipay_response = alipay_client.pageExecute(alipay_request);
       String form = alipay_response.getBody();
       System.out.println(alipay_response.isSuccess() + ":" + form);
       return form;
    
    }
    

    后端向支付宝发送支付请求,SDK将根据私钥内容对请求进行签名,支付宝根据提前配置的公钥进行验签通过后响应。返回的字符内容前端以H5的形式展示,依据内容来看form表单自动提交至支付宝付款界面。登录之前的沙箱买家账户进行支付即可。

    3.2支付回调接口和 结果页面回调接口

    @RequestMapping("/notify")
    public void alipayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
       PrintWriter writer = response.getWriter();
       //获取支付宝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);
       }
       //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
    
       String out_trade_no = "";
       String trade_no = "";
       String trade_status = "";
    
       try {
           //商户订单号
    
           out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
    
           //支付宝交易号
    
           trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
    
           //交易状态
           trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
    
       } catch (UnsupportedEncodingException e) {
           e.printStackTrace();
       }
    
       //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
       //计算得出通知验证结果
       //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
       boolean verify_result = false;
       try {
           verify_result = AlipaySignature.rsaCheckV1(params, PayUtil.alipay_pubic_key, PayUtil.charset, "RSA2");
       } catch (AlipayApiException e) {
           e.printStackTrace();
       }
    
       if (verify_result) {//验证成功
           //////////////////////////////////////////////////////////////////////////////////////////
           //请在这里加上商户的业务逻辑程序代码
           System.out.println("商户的业务逻辑程序代码-notify:" + out_trade_no + " trade_status:" + trade_status);
    
           //——请根据您的业务逻辑来编写程序(以下代码仅作参考)——
    
           if (trade_status.equals("TRADE_FINISHED")) {
               //判断该笔订单是否在商户网站中已经做过处理
               //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
               //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
               //如果有做过处理,不执行商户的业务程序
               System.out.println("TRADE_FINISHED");
               //注意:
               //如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
               //如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
           } else if (trade_status.equals("TRADE_SUCCESS")) {
               //判断该笔订单是否在商户网站中已经做过处理
               //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
               //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
               //如果有做过处理,不执行商户的业务程序
               System.out.println("TRADE_SUCCESS");
               //注意:
               //如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
           }
    
           //——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
           //writer.clear();
           writer.println("success");    //请不要修改或删除
    
           //////////////////////////////////////////////////////////////////////////////////////////
       } else {//验证失败
           writer.println("fail");
       }
    }
    

    支付回调时,首先进行验签操作,验证成功后根据提供的订单号,支付状态做相应的业务处理。

    @RequestMapping("/returnUrl")
    public void alipayReturn(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();
        //获取支付宝GET过来反馈信息
        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不相等也可以使用这段代码转化
            try {
                valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            params.put(name, valueStr);
        }
    
    
        String out_trade_no = "";
        String trade_no = "";
        try {
            //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
            //商户订单号
            out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
    
            //支付宝交易号
            trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
    
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    
        System.out.println("商户的业务逻辑程序代码-return_url:" + out_trade_no);
    
        //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
        //计算得出通知验证结果
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        boolean verify_result = false;
        try {
            verify_result = AlipaySignature.rsaCheckV1(params, PayUtil.alipay_pubic_key, PayUtil.charset, "RSA2");
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    
        if (verify_result) {//验证成功
            //////////////////////////////////////////////////////////////////////////////////////////
            //请在这里加上商户的业务逻辑程序代码
    
            //该页面可做页面美工编辑
            //out.clear();
            writer.println("check success<br />");
            //——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
    
            //////////////////////////////////////////////////////////////////////////////////////////
        } else {
            //该页面可做页面美工编辑
            //out.clear();
            writer.println("check fail");
        }
    }
    
    

    结果页面回调时,首先进行验签操作,验证成功后根据提供的订单号,做相应的业务处理,并可以控制跳转的结果页面。

  • 相关阅读:
    JS reduce方法的使用
    面试娱录
    sticky置顶功能影响了锚点定位
    postcss-px-to-viewport移动端自适应
    axios请求参数自动拼接到了地址那里
    ping 不通。无法访问目标主机
    JS前后台方法的相互调用
    SQL server2008 无法连接服务器
    Assembly.Load未能加载文件或程序集“”或它的某一个依赖项。系统找不到指定的文件
    JS判断IE和非IE
  • 原文地址:https://www.cnblogs.com/everyingo/p/12830378.html
Copyright © 2011-2022 走看看