搞定支付接口(一) 支付宝即时到账支付接口详细流程和代码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tom9238/article/details/70213528

搞定支付接口(一) 支付宝即时到账支付接口详细流程和java代码

为避免你们和我一样被支付接口搞得焦头烂额,写一个从申请开始到能收到钱为止的详细教程,实际上各个语言都可以用来集成支付接口,我用java来举例。


正所谓知己知彼,百战不殆。首先,我们来看一看支付宝平台给我们的说明。

  1. 打开支付宝
  2. 选择商家平台
  3. 选择电脑支付

进行如上操作后,来到如下图所示的页面

打开支付宝商家平台显示的页面

没有商家支付宝账号的需要注册,需要营业执照,经营信息,网址信息,联系人等等数据(图里说的很详细)

服务开通后,我们就可以集成了,我们点击如何集成查看文档

即时到账接口描述

如图所示,我们可以直接下载demo,进行快速集成,这是最方便的办法了,我会采用这种方法,但使用即时到账接口首先需要签约,点击如何签约,支付宝就教你怎么签,无非就是填表,审核。但这一步很重要,因为我们需要生成的密钥组成参数向支付宝发出请求(下面会详细说)。

签约成功之后,我们需要合作伙伴PID和MD5密钥,在如下页面获取(图我从官网文档截得)
获取pid和md5密钥


前期所有准备都做好了,再总结一下前期需要的东西

  1. 开通商家账户和即时到账服务
  2. 下载demo
  3. pid和md5密钥

我们来继续,解压demo,选择java utf-8版本,导入项目

支付的流程为

  1. 买家点击提交订单
  2. 商家生成订单,以key=value的形式向支付宝发送请求
  3. 支付宝接到请求后生成订单
  4. 买家选择扫码或密码支付完成后,支付宝同步或异步向商家发送请求,提示订单完成

商家要传递给支付宝的参数列表在前面给的开发文档中也能找到,支付宝提示的参数有必填和不必填两种,可以自己选择。

在demo src的com\alipay\config包下有AlipayConfig类。大部分参数可以在其中配置,在使用时直接用就可以了,为了维护方便,我们可以用配置文件的方法写到文件里,动态读取。但是有一些参数需要注意:
订单号需要自己随机生成sign签名是动态生成的

package com.alipay.config;

/* *
 *类名:AlipayConfig
 *功能:基础配置类
 *详细:设置帐户有关信息及返回路径
 *版本:3.4
 *修改日期:2016-03-08
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */

public class AlipayConfig {

//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    // 合作身份者ID,签约账号,以2088开头由16位纯数字组成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
    public static String partner = "2088好几个数字";

    // 收款支付宝账号,以2088开头由16位纯数字组成的字符串,一般情况下收款账号就是签约账号
    public static String seller_id = partner;

    // MD5密钥,安全检验码,由数字和字母组成的32位字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
    public static String key = "好长一串数字和字母";

    // 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    public static String notify_url = "http://www.wechat.com/AliPayTest/pay_notify_url";  // 体会到我的幽默感了吗

    // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    public static String return_url = "http://www.wechat.com/AliPayTest/pay_return_url";

    // 签名方式
    public static String sign_type = "MD5";

    // 调试用,创建TXT日志文件夹路径,见AlipayCore.java类中的logResult(String sWord)打印方法。
    public static String log_path = "F:\\";

    // 字符编码格式 目前支持 gbk 或 utf-8
    public static String input_charset = "utf-8";

    // 支付类型 ,无需修改
    public static String payment_type = "1";

    // 调用的接口名,无需修改
    public static String service = "create_direct_pay_by_user";


//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

//↓↓↓↓↓↓↓↓↓↓ 请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    // 防钓鱼时间戳  若要使用请调用类文件submit中的query_timestamp函数
    public static String anti_phishing_key = "";

    // 客户端的IP地址 非局域网的外网IP地址,如:221.0.0.1
    public static String exter_invoke_ip = "";

//↑↑↑↑↑↑↑↑↑↑请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

}

写一个配置类获取配置

public class Property {
    private static Properties p = new Properties();
    static {
        InputStream in = AlipayConfig.class.getResourceAsStream("alipay.properties");
        try {
            p.load(in);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    static public String getProperty(String property) {
        return p.getProperty(property);
    }
}

然后将参数配置写到alipay.properties的文件,有参数要修改的时候不用重新编译代码,只要修改配置文件就可以了。


前面主要是配置,按支付宝提供的文档配置好各种参数后,配置这一步就完成了。

后面我们来讲一讲下订单。

买家下订单之后,我们接收请求参数,再加上配置的参数生成要发送给支付宝的订单Map集合。

// 订单号:
String out_trade_no = "test20170213145553";

//把请求参数打包成Map
Map<String, String> sParaTemp = new HashMap<>();
sParaTemp.put("service", AlipayConfig.service);
sParaTemp.put("partner", AlipayConfig.partner);
sParaTemp.put("seller_id", AlipayConfig.seller_id);
sParaTemp.put("_input_charset", AlipayConfig.input_charset);
sParaTemp.put("payment_type", AlipayConfig.payment_type);
sParaTemp.put("notify_url", AlipayConfig.notify_url);
sParaTemp.put("return_url", AlipayConfig.return_url);
sParaTemp.put("out_trade_no", out_trade_no);
sParaTemp.put("subject", subject);
sParaTemp.put("total_fee", total_fee);
sParaTemp.put("body", desc);

调用支付宝demo给的类的buildRequest静态方法,将刚才的sParaTemp传递过去,生成String类型的字符串,这个字符串其实是个超链接,直接放地址栏上就直接将参数发送给支付宝了。

注:支付宝demo在幕后做的工作有很多,分为以下几步,若不想用demo的可以自己在官网查阅文档实现

  1. 组装待签名字符串

    • 筛选
      大部分支付宝接口中要剔除sign_type、sign两个参数,个别接口只剔除sign参数。存在空值的参数必须剔除。
    • 排序
      在参数集合中,根据参数(不是参数对应的值)的第一个字符的键值ASCII码递增排序,如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。

    • 拼接
      在参数集合中,把每个参数及其值组合成“参数=参数值”的格式(在无线产品手机安全支付中,每个参数的组合格式是“参数=”参数值””),并且把这些参数用&字符连接起来

  2. 调用签名函数

    • 签名函数
      调用md5加密函数,对已经与MD5密钥拼接好的新字符串做加密运算
  3. 签名结果的用途

    • 得到的签名结果也是一串字符串,这串字符串便是sign参数的值,把这串字符串赋值于sign参数。
String sHtmlText = AlipaySubmit.buildRequest(sParaTemp, "post", "确认");  // 这里使用支付宝demo给的类来生成请求参数,这点支付宝比微信的文档方便太多了

直接用response.write()或者我用的struts,用action跳转到页面再用字符串链接跳转就行了。

PrintWriter out = response.getWriter();
out.print(sHtmlText);

struts 支付action:

context.put("jump", sHtmlText);
return "send";  // 跳转给支付宝

struts.xml

<result name="send">/WEB-INF/jump.jsp</result>

jump.jsp:

${requestScope.jump }

这样就会直接跳转了。


支付宝接到参数就会生成二维码,客户支付后商户后台就会收到支付宝发送的返回参数,有sign验证签名,订单号,价格等等信息,若出错的话支付宝也会返回错误码ILLEGAL_SIGN等,可在支付宝历史公共错误码页面查看。

若支付成功,支付宝有同步和异步两种方式回调商户网站,这个看当初怎么配置的,配置的时候会把回调地址写在请求参数里发给支付宝,若在自己的电脑上调试,一般会调用同步方法,在服务器上会调用异步方法。

// 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://www.wechat.com/pay_notify_url";   // 请再次感受我的幽默感

// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "http://www.wechat.com/pay_return_url";

假如你的配置是这样写的,支付宝就会调用同步urlhttp://www.wechat.com/pay_notify_url或者异步urlhttp://www.wechat.com/pay_return_url并附带请求参数,你在接到请求参数后,需要和你自己的信息比对,若成功的话,就算这笔订单完成了。

异步回调

public void notifyUrl() {
        Map<String, Object> requestParams = ActionContext.getContext().getParameters();
        Map<String, String> params = null;
        HttpServletResponse response = ServletActionContext.getResponse();
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
        } catch (IOException e) {
            log.warn("支付宝异步支付获取输出流失败:" + e.getMessage());
            return;
        }
        try {
                params = tradeService.splitParam(requestParams);// 分割参数
        } catch (UnsupportedEncodingException e) {
            log.warn("alipay sign convert failed: " + e.getMessage());
            return ;
        }

        //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
        if (AlipayNotify.verify(params)){   //验证成功
            if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {  
                Order verifiedAlipay = tradeService.verifiedAlipay(seller_id, out_trade_no, total_fee);
                if (verifiedAlipay == null) {
                    log.warn("参数验证失败");
                    return; // 跳转支付失败页
                }
                writer.print("success");    //请不要修改或删除
                return;
            } else {  
                log.warn("没有完成订单");
                return; 
            }  
        } else {
            log.warn("支付宝异步支付验证失败");
            writer.print("fail");   //请不要修改或删除
        }
    }

同步回调

public String returnUrl() {
        Map<String, Object> requestParams = ActionContext.getContext().getParameters();
        Map<String, String> params = null;
        try {
            params = tradeService.splitParam(requestParams);
            if (params == null) {
                log.warn("签名验证失败-参数列表为空");
                return TRADEERROR;  // 跳转支付失败页
            }
        } catch (UnsupportedEncodingException e) {
            log.warn("alipay sign convert failed: " + e.getMessage());
        }
        if (AlipayNotify.verify(params)) {// 验证成功  
            if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {  
                Order verifiedAlipay = tradeService.verifiedAlipay(seller_id, out_trade_no, total_fee);
                if (verifiedAlipay == null) {
                    log.warn("参数验证失败");
                    return TRADEERROR;  // 跳转支付失败页
                }
                putSession(S_TRADE_FINISHED, verifiedAlipay);// 将支付详情放入session域中,到页面显示
            } else {  
                log.warn("没有完成订单");
                return TRADEERROR;  // 跳转支付失败页
            }  
        } else {// 验证失败  
            log.warn("签名验证失败");
            return TRADEERROR;  // 跳转支付失败页
        }  
        return TRADESUCCESS;
    }

然后就全部完毕了。

这个文章写的并不太满意,这是我做过支付宝接口很久以后才写的文章,好多东西都记不起来了,之前记得还在官网看过支付宝支付流程的序列图,那个很清晰,现在我也没找到。
这几天也一直在忙,每次都是写几个字就不得不忙别的了,断断续续前后也有点连不起来,以后有时间我会再把这篇文章细化修改一下,看了有不懂的地方也欢迎留言,我看到会尽量解答

阅读更多
换一批

没有更多推荐了,返回首页