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

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

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


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

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

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

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

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

服务开通后,咱们就能够集成了,咱们点击如何集成查看文档bash

即时到帐接口描述

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

签约成功以后,咱们须要合做伙伴PID和MD5密钥,在以下页面获取(图我从官网文档截得)
获取pid和md5密钥微信


前期全部准备都作好了,再总结一下前期须要的东西session

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

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

支付的流程为jsp

  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;
    }

而后就所有完毕了。

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