欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

支付宝接口的调用

程序员文章站 2022-05-27 20:06:00
...

研究了几天支付宝接口的调用,期间遇到了很多的问题,直到现在都还没解决,首先列举下问题
1、问题:
支付宝进入扫码付款界面后,手机扫描二维码付款,有时候电脑界面没有反应
可能性分析:
在成功付款的单子里,我观察了一下付款成功地址会变化,并且这个地址变化首先调用的不是我的
return_url地址,而是支付宝自己反馈的某些地址,比如:出现支付成功,倒数五秒进入另一个地址的
页面,还有显示交易成功的交易,再之后才是调用return_url的地址,也就是说这期间会连续跳转三次
的地址,而我扫码支付成功后(为什么说支付成功,其一支付宝账单里显示支付成功,其二服务器端
的tomcat的日志里显示notify_url被调用了),页面连第一次跳转都未实现,所以,根据判断,很可能
是支付宝沙箱那边出现的问题。

2、问题:
notify_url必须要放在外网能够访问到的地方,即服务器上。有个疑问,之前这个地址我都是放在本地的,然后使用手机支付宝沙箱进行了支付,在tomcat日志中虽然没有看到它调用notify_url地址,但是在手机支付宝账单中显示支付成功。
解决:
return_url是你发送支付请求成功后,客户端就会进行响应的页面跳转,但是无法判断是否支付成功,是否支付成功需要notify_url中的trade_status参数进行判断。

本次使用的是支付宝提供的demo进行的测试学习,体验了五个功能,分别是支付,交易查询,退款,
退款查询,交易结束。其中我还进行了部分的修改。

demo中给我们提供了一个AlipayConfig类和几个jsp页面:
AlipayConfig类如下:

    public class AlipayConfig {
        
    //↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    
        // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
        public static String app_id = "";
        
        // 商户私钥,您的PKCS8格式RSA2私钥
        public static String merchant_private_key = "";
        
        // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
        public static String alipay_public_key = "";
    
        public static String notify_url = "http://193.112.64.192/alipay/notify_url";
    
        // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
        public static String return_url = "http://193.112.64.192/alipay/return_url";
    
        // 签名方式
        public static String sign_type = "RSA2";
        
        // 字符编码格式
        public static String charset = "utf-8";
        
        // 支付宝网关
        public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
        
        // 支付宝网关
        public static String log_path = "D:\\logger";
    
    
    //↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    
        /** 
         * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
         * @param sWord 要写入日志里的文本内容
         */
        public static void logResult(String sWord) {
            FileWriter writer = null;
            try {
                writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
                writer.write(sWord);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

1、其中app_id可以在https://openhome.alipay.com/platform/appDaily.htm?tab=info 中的沙箱环境中去找自己的app_id
2、使用公钥和私钥生成器,生成对应的公钥和私钥,将公钥填入以上地址中的应用公钥中,验证的时候会提示你使用签名,他提供签名工具的生成工具,通过私钥生成,然后进行验证,注意以上代码中的alipay_public_key不是我们刚刚生成的公钥,而是我们公钥和签名传给对方,对方自己生成的一个支付宝公钥(第一天在这个地方卡了很久),然后将这个值复制粘贴到代码中
3、商品私钥就是之前通过公私钥生成器生成的私钥,直接复制粘贴
4、notify_url和return_url需要在外网可以正常访问的网址,也就是需要挂在服务器

return_url同步通知的介绍:
买家在支付成功后会看到一个支付宝交易提示成功的页面,该页面会停留几秒,然后会自动跳转回商户指定的同步通知页面(参数return_url)
该页面中获得参数的方式,需要使用GET方式获取,如request.QueryString("out_trade_no")、$_GET['out_trade_no'];
该方式仅仅在买家付款完成以后进行自动跳转,因此只会进行一次
该方式不是支付宝主动去调用商户页面,而是支付宝的程序利用页面自动跳转的函数,使用户的当前页面自动跳转;
基于(4)的原因,可在本机而不是只能在服务器上进行调试;
返回URL只有一分钟的有效期,超过一分钟该链接地址会失效,验证则会失败;
设置页面跳转同步通知页面(return_url)的路径时,不要在页面文件的后面再加上自定义参数。例如:
错误的写法:http://www.alipay.com/alipay/return_url.php?xx=11
正确的写法:http://www.alipay.com/alipay/return_url.php

notify_url异步通知的介绍:
必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等;
支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:
request.Form("out_trade_no")、$_POST['out_trade_no']。
支付宝主动发起通知,该方式才会被启用;
只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账中交易状态为“等待买家付款”的状态默认是不会发送通知的);
服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅页面跳转同步通知页面会启用,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;
程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。
一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:2m,10m,10m,1h,2h,6h,15h);
程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
cookies、session等在此页面会失效,即无法获取这些数据;
该方式的调试与运行必须在服务器上,即互联网上能访问;
该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
通知ID(参数notify_id)只有一分钟有效期,超过一分钟该次通知会验证失败。一旦验证成功下次再验证就会失效。

总结:
notify_url是支付宝模拟post数据给你,只要状态改变就会post给你
return_url是跳转,用户付款后跳转到你的页面.只有第一次会通知你,以后不会了.
参考链接:https://www.cnblogs.com/xishuqingchun/p/4864452.html

接下里就是介绍代码实现:

首先是创建他提供的发送http请求的工具包AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

alipayClient封装公共请求参数,
AlipayTradePagePayRequest设置付款请求;
付款其中的方法biz_content封装业务请求参数
其中biz_content必填的参数有
out_trade_no//订单号
total_amount//金额
subject//商品名称
product_code//销售产品码,与支付宝签约的产品码名称。 注:目前仅支持FAST_INSTANT_TRADE_PAY
请求以json格式传递

例如:

    AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

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

    //商户订单号,商户网站订单系统中唯一订单号,必填
    String out_trade_no = new String(request.getParameter("WIDout_trade_no").getBytes("ISO-8859-1"),"UTF-8");
    //付款金额,必填
    String total_amount = new String(request.getParameter("WIDtotal_amount").getBytes("ISO-8859-1"),"UTF-8");
    //订单名称,必填
    String subject = new String(request.getParameter("WIDsubject").getBytes("ISO-8859-1"),"UTF-8");
    //商品描述,可空
    String body = new String(request.getParameter("WIDbody").getBytes("ISO-8859-1"),"UTF-8");

    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-请求参数】章节

    //请求
    String result = alipayClient.pageExecute(alipayRequest).getBody();//调用sdk生成表单


    //输出,注意输出格式,错误容易验签失败
    response.setContentType("text/html; charset=utf-8");
    PrintWriter out = response.getWriter();
    out.println(result);

发送完该请求到支付宝后,对于PC网站支付的交易,在用户支付完成之后,支付宝会根据API中商户传入的return_url参数,通过GET请求的形式将部分支付结果参数通知到商户系统。
页面返回的业务参数只有订单号,交易号,金额和支付宝唯一用户号(select_id)
注意:也就是获取不到商品描述信息Body

交易查询的功能,我修改了一下demo中直接向支付宝发送请求来获取自己的数据,我是在return_url验签成功后,直接插入到数据库表格中,然后交易查询的时候,直接查询插入的数据

退款申请我额外做了个退款申请列表,当用户发起退款申请,不是直接就申请成功,他会修改商品的status属性,变更为“退款申请中”的状态,然后根据这个状态进行表数据的检索,汇成一张子表来显示退款申请用户的列表,然后商户可以选择确认或者取消退款

退款申请查询,当用户退款成功,在退款申请查询中能查询到相应的退款订单信息否则显示信息为空

交易结束,交易结束是发生在订单形成,却没有付款的情况下,这一功能我没有进行修改,直接是发送请求到支付宝,而后返回相应的信息。