支付宝手机网站支付接入(沙箱环境)

参考阿里云文档: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");
    }
}

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

原文地址:https://www.cnblogs.com/everyingo/p/12830378.html