第三方支付:微信公众号接入支付宝支付开发
引言
这篇文章使用一些简单的代码例子来解释微信接入支付宝支付功能的操作步骤,即使新手也可以轻松参透的。
第三方支付是指具备一定实力和信誉保障的独立机构,采用与各大银行签约的方式,通过与银行支付结算系统接口对接而促成交易双方进行交易的网络支付模式。
使用第三方支付,我们只要申请一个帐号平台即可以支持所以银行卡信用卡支付功能,具体支付功能由第三方支付平台来完成。本文所要介绍的是支付宝手机网站支付功能,而且是微信端的。
支付宝接入说难也难,说易也容易,正应正了那句话,难了不会会了不难。
先上个支付宝官方给出的交易流程图:
说明:
商户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 params = new HashMap();
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 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("验证成功
");
//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
//////////////////////////////////////////////////////////////////////////////////////////
}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引入官方提供的sdk1. 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 ydAndPc_Pay(Map maps)
throws AlipayApiException {
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
String NotifyUrl = maps.get("NotifyUrl");
String ReturnUrl = maps.get("ReturnUrl");
// 后台回调
if (!StringUtils.isEmpty(NotifyUrl)) {
alipayRequest.setNotifyUrl(NotifyUrl);
// bizContent 中不需要 公共参数
maps.remove("NotifyUrl");
}else{
alipayRequest.setNotifyUrl(AlibabaMainConfig.notify_url);
}
// 页面回调
if (!StringUtils.isEmpty(ReturnUrl)) {
alipayRequest.setReturnUrl(ReturnUrl);
// bizContent 中不需要 公共参数
maps.remove("ReturnUrl");
}else{
alipayRequest.setReturnUrl(AlibabaMainConfig.return_url);
}
String bizCon = JSON.toJSONString(maps);
alipayRequest.setBizContent(bizCon);
String form = "";
try {
form = AlipayClientFactory.getAlipayClientInstance()
.pageExecute(alipayRequest).getBody();
} catch (AlipayApiException e) {
form = "err";
e.printStackTrace();
}
return form;
}
// 查询订单状态
public AlipayTradeQueryResponse query(String appAuthToken, String bizContent)
throws AlipayApiException {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.putOtherTextParam("app_auth_token", appAuthToken);
request.setBizContent(bizContent);
return AlipayClientFactory.getAlipayClientInstance().execute(request);
}
// 条码支付
public AlipayTradePayResponse pay(String appAuthToken, String bizContent)
throws AlipayApiException {
AlipayTradePayRequest request = new AlipayTradePayRequest();
request.putOtherTextParam("app_auth_token", appAuthToken);
request.setBizContent(bizContent);
return AlipayClientFactory.getAlipayClientInstance().execute(request);
}
// 扫码支付
public AlipayTradePrecreateResponse precreate(String appAuthToken,
String bizContent) throws AlipayApiException {
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
request.putOtherTextParam("app_auth_token", appAuthToken);
request.setBizContent(bizContent);
return AlipayClientFactory.getAlipayClientInstance().execute(request);
}
// 订单撤销
public AlipayTradeCancelResponse cancel(String appAuthToken,
String bizContent) throws AlipayApiException {
AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();
request.putOtherTextParam("app_auth_token", appAuthToken);
request.setBizContent(bizContent);
return AlipayClientFactory.getAlipayClientInstance().execute(request);
}
// 申请退款
public AlipayTradeRefundResponse refund(String appAuthToken,
String bizContent) throws AlipayApiException {
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
request.putOtherTextParam("app_auth_token", appAuthToken);
request.setBizContent(bizContent);
return AlipayClientFactory.getAlipayClientInstance().execute(request);
}
}
这个类就是把支付请求公共的参数组装一下并创建支付请求。
1.3下面是比较关键的类。自己的商户系统接收前端请求,向支付宝系统发送构造订单支付数据请求,支付宝收到请求后会以form表单的形势返回请求。商户系统将数据返回给前端,前端页面通过Form表单的形式请求到支付宝。
构造订单数据接口如下:
@ApiOperation(value = "请求支付宝构造订单数据", notes = "请求支付宝构造订单数据")
@RequestMapping(value = "/alibabaOrdPrePay", method = RequestMethod.GET)
@ResponseBody
public void alibabaOrdPrePay(@RequestParam(value = "XXXParam", required = true) String XXXParam,
@RequestParam(value = "coupCd", required = true) String coupCd,
@RequestParam(value = "openId", required = true, HttpServletResponse response,
HttpServletRequest request) {
try {
//支付请求前的数据处理
// 参数目前都是 写死的 根据业务需求 写活
Map maps = new HashMap();
maps.put("out_trade_no", ordCd);
// 付款金额,必填
maps.put("total_amount", "0.01");
// 订单名称,必填
maps.put("subject", "测试订单名");
// 商品描述,可空
maps.put("body", "商品描述");
// 销售产品码 必填
maps.put("product_code", "QUICK_WAP_PAY");
// 超时时间 可空
maps.put("timeout_express", "2m");
maps.put("passback_params", URLEncoder.encode(openId, "UTF-8"));
try {
AlipayClientFactory ali = new AlipayClientFactory();
String form = ali.ydAndPc_Pay(maps);
logger.info("预请求返回数据:" + form);
if (!form.equals("err")) {
response.setContentType("text/html;charset=" + AlibabaMainConfig.CHARSET);
response.getWriter().write(form);// 直接将完整的表单html输出到页面
response.getWriter().flush();
response.getWriter().close();
} else {
buildFailure("请求失败,请重试!");
}
} catch (AlipayApiException e) {
e.printStackTrace();
logger.info(e);
}
} catch (Throwable t) {
buildFailure(ExpUtil.capture(BaseSvcMsgCode.insertFailure, "支付宝支付失败", t, logger));
}
}
返回的结果如下:
2前台
2.1前端返回支付结果页面return_url.jsp:
<%@page import="com.alipay.api.internal.util.AlipaySignature"%>
<%
/* *
功能:支付宝页面跳转同步通知页面
版本:3.2
日期:2011-03-17
说明:
以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
//***********页面功能说明***********
该页面可在本机电脑测试
可放入HTML等美化页面的代码、商户业务逻辑程序代码
TRADE_FINISHED(表示交易已经成功结束,并不能再对该交易做后续操作);
TRADE_SUCCESS(表示交易已经成功结束,可以对该交易做后续操作,如:分润、退款等);
//********************************
* */
%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<%@ page import="java.util.Map"%>
<%@ page import="yui.ui.web.bss.service.config.AlibabaMainConfig"%>
<%-- <%@ page import="com.alipay.api.*"%> --%>
支付宝页面跳转同步通知页面
<%
//获取支付宝GET过来反馈信息
Map params = new HashMap();
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 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("验证成功
");
//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
//////////////////////////////////////////////////////////////////////////////////////////
}else{
//该页面可做页面美工编辑
// out.clear();
// out.println("验证失败");
}
%>
我这里只做了提示并没有展示商品详情,具体根据需要来就可以了。
2.2 notify_url支付结果异常通知接口如下:
@ApiOperation(value = "支付宝支付回调", notes = "支付宝支付回调")
@RequestMapping(value = "/notifyCallBack")
public void notifyCallBack(HttpServletRequest request, HttpServletResponse response) {
try {
logger.info("收到支付成功异步接到消息通知,开始处理");
Map params = new HashMap();
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);
}
logger.info("支付成功异步接到消息通知,参数:" + params.toString());
// 商户订单号
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");
// 交易状态
String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
// 计算得出通知验证结果
// //boolean AlipaySignature.rsaCheckV1(Map params,
// String publicKey, String charset, String sign_type)
boolean verify_result = AlipaySignature.rsaCheckV1(params, AlibabaMainConfig.ALIPAY_PUBLIC_KEY,
AlibabaMainConfig.CHARSET, AlibabaMainConfig.SIGNTYPE);
if (verify_result) {
// 验证成功
// 请在这里加上商户的业务逻辑程序代码
if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
// 支付宝订单号,先判断该笔订单是否处理过,如果处理过,则直接返回
//这里写能正确收到返回数据的处理逻辑
}
response.getWriter().println("success"); // 请不要修改或删除
} else {
// 验证失败
response.getWriter().println("fail");
}
} catch (Throwable t) {
logger.info("阿里支付回调异常", t);
try {
response.getWriter().println("fail");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.3 由于微信端限制了支付宝,故引起用户打开系统浏览器。这里使用了一个jsp页面做页面引导。
pay.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
支付提示
请在菜单中选择在浏览器中打开,
以完成支付
红色部分找向pay.jsp页面。
2.4引入的ap.js代码如下,下载地址:
(function(){var b={};var a={};a.PADCHAR="=";a.ALPHA="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";a.makeDOMException=function(){var f,d;try{return new DOMException(DOMException.INVALID_CHARACTER_ERR)}catch(d){var c=new Error("DOM Exception 5");c.code=c.number=5;c.name=c.description="INVALID_CHARACTER_ERR";c.toString=function(){return"Error: "+c.name+": "+c.message};return c}};a.getbyte64=function(e,d){var c=a.ALPHA.indexOf(e.charAt(d));if(c===-1){throw a.makeDOMException()}return c};a.decode=function(f){f=""+f;var j=a.getbyte64;var h,e,g;var d=f.length;if(d===0){return f}if(d%4!==0){throw a.makeDOMException()}h=0;if(f.charAt(d-1)===a.PADCHAR){h=1;if(f.charAt(d-2)===a.PADCHAR){h=2}d-=4}var c=[];for(e=0;e>16,(g>>8)&255,g&255))}switch(h){case 1:g=(j(f,e)<<18)|(j(f,e+1)<<12)|(j(f,e+2)<<6);c.push(String.fromCharCode(g>>16,(g>>8)&255));break;case 2:g=(j(f,e)<<18)|(j(f,e+1)<<12);c.push(String.fromCharCode(g>>16));break}return c.join("")};a.getbyte=function(e,d){var c=e.charCodeAt(d);if(c>255){throw a.makeDOMException()}return c};a.encode=function(f){if(arguments.length!==1){throw new SyntaxError("Not enough arguments")}var g=a.PADCHAR;var h=a.ALPHA;var k=a.getbyte;var e,j;var c=[];f=""+f;var d=f.length-f.length%3;if(f.length===0){return f}for(e=0;e>18));c.push(h.charAt((j>>12)&63));c.push(h.charAt((j>>6)&63));c.push(h.charAt(j&63))}switch(f.length-d){case 1:j=k(f,e)<<16;c.push(h.charAt(j>>18)+h.charAt((j>>12)&63)+g+g);break;case 2:j=(k(f,e)<<16)|(k(f,e+1)<<8);c.push(h.charAt(j>>18)+h.charAt((j>>12)&63)+h.charAt((j>>6)&63)+g);break}return c.join("")};b.pay=function(d){var c=encodeURIComponent(a.encode(d));location.href="pay.jsp?goto="+c};b.decode=function(c){return a.decode(decodeURIComponent(c))};window._AP=b})();
其中红色部分找向的pay.jsp页面,如果pay.jsp页面和ap.js不在同一目录下,需要做进一步调整。
需要在我们的支付目录下引入ap.js文件,然后如下请求即可:
_AP.pay(basePath+ "/bss/alibabaPay/alibabaOrdPrePay?XXXparams=" + params);
只要把请求支付宝构造支付订单数据的请求填到pay()方法里即可。参数根据需求来写,和正常的业务开发一样。
第四步:测试结果如下
4.1 支付方式页面:
4.1 引导打开系统浏览器页面:
4.3 微信选择界面
4.4 支付宝路由页面
4.5 支付宝路由尝试唤起支付宝客户端界面
4.6 弹出支付确认界面
4.7 弹出输入密码框界面
4.8 支付成功界面
当然这里的提示“支付成功,请您手动退回微信!“是我自己提示的。
4.9 回界面显示,后台会收到支付宝的支付结果异步通知回调,程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
如果你对于接入支付宝的流程依然不清晰,那么最好的方式就是运行一下上面的例子,看看会发生什么。读懂一篇长篇大论要比理解一个例子难的多。