这篇文章使用一些简单的代码例子来解释微信接入支付宝支付功能的操作步骤,即使新手也可以轻松参透的。
第三方支付是指具备一定实力和信誉保障的独立机构,采用与各大银行签约的方式,通过与银行支付结算系统接口对接而促成交易双方进行交易的网络支付模式。
使用第三方支付,我们只要申请一个帐号平台即可以支持所以银行卡信用卡支付功能,具体支付功能由第三方支付平台来完成。本文所要介绍的是支付宝手机网站支付功能,而且是微信端的。
支付宝接入说难也难,说易也容易,正应正了那句话,难了不会会了不难。
先上个支付宝官方给出的交易流程图:
说明:
商户H5网站先向自己的后台系统发送请求,由后台系统向支付宝系统发送请求,请求生成订单数据,当商户系统收到支付宝返回的订单数据后(注意支付宝是以form表单的形势返回订单数据的字符串),我们后台系统将数据返回给前端,前端页面通过Form表单的形式请求到支付宝,支付宝验证签名后进入支付宝路由页面,如果用户手机已经安装支付宝客户端,则它会尝试唤起支付宝客户端,如果没安装客户端,则路由页面会以web形势打开付款页面,成功后会弹出输入密码框供用户输入。
输入正确密码后,支付宝根据商户在手机网站支付API中传入的前台回跳地址return_url自动跳转回商户页面,同时在URL请求中以Query String的形式附带上支付结果参数,支付宝还会根据原始支付API中传入的异步通知地址notify_url,通过POST请求的形式将支付结果作为参数通知到商户系统。我们return_url地址负责前端展示,notify_url负责页面处理逻辑。其实notify_url不一定非要是jsp页面,(虽然官方demo里使用的是jsp页面),也可以是接口,入参是request和response,后面会提供样例。
第一步,配置环境
1.创建应用,签约“手机网站支付”功能。然后配置关键参数。
支付宝网关固定的:https://openapi.alipay.com/gateway.do
应用网关:直接填写网站的域名。比如http://www.test.com
授权回调地址:可以不填写,没啥特殊功能,不填写也可以调用支付功能
接访加答方式:去官网或者https://docs.open.alipay.com/291/105971/地址上下载个RSA密钥生成工具。如下图:
密钥格式选pkcs8,密钥长度强列推荐2048长度的。工具自动生成了商户的公钥和私钥,而且私钥还是pkcs8格式的。这里有个坑。官方也提供了另一个密钥生成工具如下图
这个工具生成的密钥用于老版本的支付设置的,即mapi网关产品密钥,新版本的其实不用设置mapi网关产品官钥的,如下图
既然不用设置,就不用管它,继续我们的设置。
将新版密钥生成工具生成的公钥填到“RSA(SHA256)密钥“里。之后就可以查看支付宝公钥了。
至此相关参数配置基本完成。
特别特别特别要注意的是用正确的工具生成对应的公私钥填到正确的地方。
特别特别特别要注意的是用正确的工具生成对应的公私钥填到正确的地方。
第二步:概念介绍
2.1 RSA和RSA2签名算法
上文中提供了2种加密算法
RSA2算法标准名称是SHA256WithRSA,强制要求RSA密钥的长度至少为2048,是官文极力推荐的算法,这种算法更安全,该算法在摘要算法上比SHA1WithRSA有更强的安全能力(签名类型sign_type=RSA2).
RSA算法标准名称是SHA1WithRSA,对RSA密钥的长度不限制,推荐使用2048位。(签名类型sign_type=RSA).
也就是说2种算法都可以用上文工具生成的RSA密钥。换句话说,上文生成的RSA密钥都可以做为2种加密算法的公私钥使用,千万不要被名称混淆了哦。
2.2 RSA算法使用原理。
RSA算法是一种非对称密码算法,所谓非对称,就是指该算法需要一对密钥(即公钥和私钥),私钥用来加密,公钥用来解密。私钥相当于锁,公钥相当于钥匙
支付宝与商户交互时,需要用到两对RSA密钥,支付宝一对,商户一对。
2.2.1 支付宝发送信息给商户时,使用支付宝私钥对数据进行加密,商户获取到支付宝加密的信息后使用支付宝公钥对数据进行解密,得到正确的数据。
2.2.2 商户给支付宝发送信息时,使用商户自己的私钥对数据加密,支付宝获取到数据后使用商家上传的公钥进行解密。
所以当我们当商户的公钥设置到支付宝系统后,我们同时可以查看”支付宝公钥“是多少,这个支付宝公钥其实就是我们解密支付宝给我们发送消息的钥匙。
2.3 回调
任何一种支付都会分别给前后端一个响应。支付宝也不例如。
在我们的支付请求里分别设置了return_url(前端回调)和notify_url(后端回调)。2种没有先后顺序,后台数据的处理只能依赖notify_url返回,不能依赖return_url来处理数据,return_url只能当做展示用。
支付宝有个特别的地方是它不是以数据的形势返回响应,而是以页面响应的方式返回数据。比如http://www.test.com/return_url.jsp? out_trade_no=XXX&total_amount=xxx…形势返回数据的。当然们也可以用其它方式处理这种请求。比如用request,response取数据。
根据官方demo我们找到了2个参数的用法。
return_url.jsp:接收数据,验证参数是否是支付宝发来的,若是则展示页面或只是一个提示都可以。比如:
<%
//获取支付宝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不相等也可以使用这段代码转化
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
// out.println("params:" + params);
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
//计算得出通知验证结果
//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
boolean verify_result = AlipaySignature.rsaCheckV1(params, AlibabaMainConfig.ALIPAY_PUBLIC_KEY, AlibabaMainConfig.CHARSET, AlibabaMainConfig.SIGNTYPE);
// out.println("verify_result:" + verify_result);
if(verify_result){//验证成功
//
//请在这里加上商户的业务逻辑程序代码
//该页面可做页面美工编辑
// out.clear();
// out.println("验证成功<br />");
//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
//
}else{
//该页面可做页面美工编辑
// out.clear();
// out.println("验证失败");
}
%>
notify_url.jsp:我不建议使用jsp的方式接收异步通知。根据官方定义了notify_url.jsp页面的相关规定。
服务器异步通知页面特性
o 必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等;
o 支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:request.Form(“out_trade_no”)、$_POST[‘out_trade_no’];
o 支付宝主动发起通知,该方式才会被启用;
o 只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账交易状态为“等待买家付款”的状态默认是不会发送通知的);
o 服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
o 第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅会返回同步处理结果,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;
o 程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
o 程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
o cookies、session等在此页面会失效,即无法获取这些数据;
o 该方式的调试与运行必须在服务器上,即互联网上能访问;
o 该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
o 当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。也就是说在支付宝发送同一条异步通知时(包含商户并未成功打印出success导致支付宝重发数次通知),服务器异步通知参数notify_id是不变的。
其中第一条我就不是很明白什么意思,太容易出错了。我建议使用接口回调的方式,用request处理数据,后面会给出例子。
2.4 微信公众号里接入支付宝的其它方法,由于2家的竞争对象,微信中无法打开支付宝收款是微信浏览器限制所致,可以引导用户转到系统浏览器,即可用支付宝收款。
第三步:实现代码
下载SDK,Java引入官方提供的sdk
1. java后台代码:
1.1支付宝相关配置AlibabaMainConfig.java:
public class AlibabaMainConfig {
// 商户appid
public static String APPID = "2017051807272022";
// 私钥 pkcs8格式的,我发支付宝发送消息的锁,
public static String RSA_PRIVATE_KEY = "MIIEvQIBADANBgkqhkiG9w0BXXX";
// 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://www.dong-ai.com/wx/bss/alibabaPay/notifyCallBack";
// 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
// 商户可以自定义同步跳转地址
public static String return_url = "http://www.dong-ai.com/wx/page/alibaba/return_url.jsp";
// 请求网关地址
public static String URL = "https://openapi.alipay.com/gateway.do";
// 编码
public static String CHARSET = "UTF-8";
// 返回格式
public static String FORMAT = "json";
// 支付宝公钥,用于获取同步返回信息后进行验证,验证是否是支付宝发送的信息。,支付宝发给商户消息的钥匙
public static String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCXXX";
// 日志记录目录
public static String log_path = "/log";
// RSA2
public static String SIGNTYPE = "RSA2";
}
APPID:支付宝后台很容易能找到
RSA_PRIVATE_KEY:pkcs8格式的私钥。这就是上文RSA密钥工具生成的2048长度的私钥,且已经是pkcs8格式的私钥了。与它对应的公钥已经设置到支付宝后台了,而且我们选择了“RSA2(SHA256)密钥(推荐)“方式加密。
notify_url:接收支付结果异步通知的接口,我这里不是以jsp页面的形势而是以接口的形势接收返回结果的。
return_url:前端接收支付结果的页面。
ALIPAY_PUBLIC_KEY:是我们将公钥填好后对应查看出来的支付宝公钥。如下图:
SIGNTYPE:接口加签方式。选择” SHA256WithRSA “加签方式则SIGNTYPE=”RSA2”,如果选择“SHA1WithRSA“则SIGNTYPE=”RSA”
其它几个参数照写就行,没啥噱头。
1.2创建支付请求AlipayClientFactory.java:
public class AlipayClientFactory {
private static final AlipayClient client = new DefaultAlipayClient(
AlibabaMainConfig.URL, AlibabaMainConfig.APPID, AlibabaMainConfig.RSA_PRIVATE_KEY, AlibabaMainConfig.FORMAT,
AlibabaMainConfig.CHARSET, AlibabaMainConfig.ALIPAY_PUBLIC_KEY, AlibabaMainConfig.SIGNTYPE);
public static AlipayClient getAlipayClientInstance() {
return client;
}
/**
* appAuthToken
* 如ISV代替商家调用当面付接口,需将商户授权后获取的app_auth_token带上;如商家申请当面付自己调用,则传null bizContent
* JSON格式 商户的请求参数
*/
// 手机网页支付 网站支付
public String yd