支付宝手机网站支付流程(Node实现)

时间:2021-01-09 23:42:34

前言

公司M站要接入支付宝,借机研究了一下支付宝的支付流程。毕竟,只有公司才能拿到支付接口权限。

主要参考文档:

https://doc.open.alipay.com/doc2/detail?treeId=60&articleId=103564&docType=1

https://b.alipay.com/order/productDetail.htm?productId=2015110218008816

手机网站支付接口

产品简介

手机网站支付主要应用于手机、掌上电脑等无线设备的网页上,通过网页跳转或浏览器自带的支付宝快捷支付实现买家付款的功能,资金即时到账。

申请条件

1、您申请前必须拥有企业支付宝账号(不含个体工商户账号),且通过支付宝实名认证审核。

2、如果您有已经建设完成的无线网站(非淘宝、天猫、诚信通网店),网站经营的商品或服务内容明确、完整(古玩、珠宝等奢侈品、投资类行业无法申请本产品)。

3、网站必须已通过ICP备案,备案信息与签约商户信息一致。

配置

假设我们已经成功申请到手机网站支付接口,在进行开发之前,需要使用公司账号登录支付宝开放平台。

查看PID

1、开发者登录开放平台,点击右上角的“账户及密钥管理”。

支付宝手机网站支付流程(Node实现)

2、选择“合作伙伴密钥”,即可查询到合作伙伴身份(PID),以2088开头的16位纯数字。

支付宝手机网站支付流程(Node实现)

配置密钥

支付宝提供了DSA、RSA、MD5三种签名方式,本次开发中,我们使用RSA签名和加密,那就只配置RSA密钥就好了。

关于RSA加密的详解,参见《支付宝签名与验签》。

应用环境

本节可以忽略,本节可以忽略,本节可以忽略!因为官方文档并没有提及应用环境配置的问题。

进入管理中心,对应用进行设置。

支付宝手机网站支付流程(Node实现)

支付宝手机网站支付流程(Node实现)

上图是我的应用配置选项,公司账号也许会有所不同。具体哪些参数需要配置?请参照接口参数说明,需要什么就配置什么。

交互

Node端发起支付请求

支付宝手机网站支付流程(Node实现)

我们公司采用的,就是这种方式,步骤3中Node端获取到的支付宝参数,包括sign。其实,sign的计算工作也可以放在Node端,只不过支付宝没有给出Node的demo,实现起来需要耗费多一点时间。

后端发起支付请求

支付宝手机网站支付流程(Node实现)

这种方式也很好,而且,步骤4中后端获取到支付页面,也可以不传给Node端,自己显示出来。这样,整个流程就更加简单。

return_url和notify_url

return_url,支付完成后的回调url;notify_url,支付完成后通知的url。支付宝发送给两个url的参数是一样的,只不过一个是get,一个是post。

以上两种发起请求的方式中,return_url在Node端,notify_url在后端。我们也可以根据需要,把两个url都放在后端,或者都放在Node端,改变相应业务逻辑即可。

Node端详解

Node端发起支付请求有两种选择,一种是获取到后端给的参数后,通过request模块发起get请求,获取到支付宝返回的支付页面,然后显示到页面上;另一种是获取到后端给的参数后,把参数全部输出到页面中的form表单,然后通过js自动提交表单,获取到支付宝返回的支付页面(同时显示出来)。

request发起请求

// 通过orderId向后端请求获取支付宝支付参数alidata
var alipayUrl = 'https://mapi.alipay.com/gateway.do?'+ alidata;
request.get({url: alipayUrl},function(error, response, body){
res.send(response.body);
});

理论上完全正确的请求,然而,获取到的支付页面,输出到页面上,却是乱码。没错,还是一个错误提示页面。

支付宝手机网站支付流程(Node实现)

神奇的地方在于,在刷新页面多次后,正常了!!!啊嘞,这是什么鬼?

支付宝手机网站支付流程(Node实现)

先解决乱码问题,看看报什么错!

request.get({url: alipayUrl},function(error, response, body){
var str = response2.body;
str = str.replace(/gb2312/, "utf-8");
res.setHeader('content-type', 'text/html;charset=utf-8');
res.send(str);
});

很遗憾,无效!乱码依然是乱码。。。和沈晨帅哥讨论很久,最终决定换一种方案——利用表单提交。

表单提交请求

Node端

// node端

// 通过orderId向后端请求获取支付宝支付参数alidata
function getArg(str,arg) {
var reg = new RegExp('(^|&)' + arg + '=([^&]*)(&|$)', 'i');
var r = str.match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
} var alipayParam = {
_input_charset: getArg(alidata,'_input_charset'),
body: getArg(alidata,'body'),
it_b_pay: getArg(alidata, 'it_b_pay'),
notify_url: getArg(alidata, 'notify_url'),
out_trade_no: getArg(alidata, 'out_trade_no'),
partner: getArg(alidata, 'partner'),
payment_type: getArg(alidata, 'payment_type'),
return_url: getArg(alidata, 'return_url'),
seller_id: getArg(alidata, 'seller_id'),
service: getArg(alidata, 'service'),
show_url: getArg(alidata, 'show_url'),
subject: getArg(alidata, 'subject'),
total_fee: getArg(alidata, 'total_fee'),
sign_type: getArg(alidata, 'sign_type'),
sign: getArg(alidata, 'sign'),
app_pay: getArg(alidata, 'app_pay')
}; res.render('artist/alipay',{
// 其他参数
alipayParam: alipayParam
});

html

<!--alipay.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付宝支付</title>
</head>
<body>
<form id="ali-form" action="https://mapi.alipay.com/gateway.do" method="get">
<input type="hidden" name="_input_charset" value="<%= alipayParam._input_charset%>">
<input type="hidden" name="body" value="<%= alipayParam.body%>">
<input type="hidden" name="it_b_pay" value="<%= alipayParam.it_b_pay%>">
<input type="hidden" name="notify_url" value="<%= alipayParam.notify_url%>">
<input type="hidden" name="out_trade_no" value="<%= alipayParam.out_trade_no%>">
<input type="hidden" name="partner" value="<%= alipayParam.partner%>">
<input type="hidden" name="payment_type" value="<%= alipayParam.payment_type%>">
<input type="hidden" name="return_url" value="<%= alipayParam.return_url%>">
<input type="hidden" name="seller_id" value="<%= alipayParam.seller_id%>">
<input type="hidden" name="service" value="<%= alipayParam.service%>">
<input type="hidden" name="show_url" value="<%= alipayParam.show_url%>">
<input type="hidden" name="subject" value="<%= alipayParam.subject%>">
<input type="hidden" name="total_fee" value="<%= alipayParam.total_fee%>">
<input type="hidden" name="sign_type" value="<%= alipayParam.sign_type%>">
<input type="hidden" name="sign" value="<%= alipayParam.sign%>">
<input type="hidden" name="app_pay" value="<%= alipayParam.app_pay%>">
</form>
<% include ../bootstrap.html %>
<script type="text/javascript" src="<%= dist %>/js/business-modules/artist/alipay.js?v=2016052401"></script>
</body>
</html>

js

// alipay.js
seajs.use(['zepto'],function($){
var index = {
init:function(){
var self = this;
this.bindEvent();
},
bindEvent:function(){
var self = this;
$('#ali-form').submit();
}
}
index.init();
});

小结

支付宝手机网站支付流程(Node实现)

选择支付宝支付后,成功跳转到了支付宝支付页面,nice!看来这种方案很靠谱。

开始时,打算把alidata直接输出到form表单的action中接口的后面,因为这样似乎最简便。但是,提交表单时,后面的参数全部被丢弃了。所以,不得不得把所有参数放在form表单中。Node端拆分了一下参数,组装成了一个alipayParam对象,这个工作也可以交给后端来做。

显然,request模拟表单提交和真实表单提交结果的不同,得出的结论是,request并不能完全模拟表单提交。或者,request可以模拟,而我不会-_-|||。

错误排查

支付宝手机网站支付流程(Node实现)

值得点赞的是,支付宝给的错误代码很明确,一看就懂。上面这个错误,签名不对。因为我给alipayParam私自加了个app_pay属性,没有经过签名。

微信屏蔽支付宝

问题描述

以上,大功告成?不!还有一个坑要填,因为微信屏蔽了支付宝!

在电脑上,跳转支付宝支付页面正常,很完美!然而,在微信浏览器中测试时,却没有跳转,而是出现如下信息。

支付宝手机网站支付流程(Node实现)

完美解决办法

微信端支付宝支付,iframe改造

http://www.cnblogs.com/jiqing9006/p/5584268.html

该办法的核心在于:把微信屏蔽的链接,赋值给iframe的src属性。

Node端

res.render('artist/alipay',{
alipayParam: alipayParam,
param: urlencode(alidata)
});

html

<input type="hidden" id="param" value="<%= param%>">
<iframe id="payFrame" name="mainIframe" src="" frameborder="0" scrolling="auto" ></iframe>

js

var iframe = document.getElementById('payFrame');
var param = $('#param').val();
iframe.src='https://mapi.alipay.com/gateway.do?'+param;

报错

然而,在改造时,先是报错ILLEGAL_SIGN,于是利用urlencode处理了字符串。接着,又报错ILLEGAL_EXTERFACE,没有找到解决办法。

暂时放弃,以后如果有了解决办法再补上。

官方解决办法

关于微信公众平台无法使用支付宝收付款的解决方案说明

https://cshall.alipay.com/enterprise/help_detail.htm?help_id=524702

该方法的核心在于:确认支付时,提示用户打开外部系统浏览器,在系统浏览器中支付。

html

<!--alipay.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付宝支付</title>
</head>
<body>
<form id="ali-form" action="https://mapi.alipay.com/gateway.do" method="get">
<input type="hidden" name="_input_charset" value="<%= alipayParam._input_charset%>">
<input type="hidden" name="body" value="<%= alipayParam.body%>">
<input type="hidden" name="it_b_pay" value="<%= alipayParam.it_b_pay%>">
<input type="hidden" name="notify_url" value="<%= alipayParam.notify_url%>">
<input type="hidden" name="out_trade_no" value="<%= alipayParam.out_trade_no%>">
<input type="hidden" name="partner" value="<%= alipayParam.partner%>">
<input type="hidden" name="payment_type" value="<%= alipayParam.payment_type%>">
<input type="hidden" name="return_url" value="<%= alipayParam.return_url%>">
<input type="hidden" name="seller_id" value="<%= alipayParam.seller_id%>">
<input type="hidden" name="service" value="<%= alipayParam.service%>">
<input type="hidden" name="show_url" value="<%= alipayParam.show_url%>">
<input type="hidden" name="subject" value="<%= alipayParam.subject%>">
<input type="hidden" name="total_fee" value="<%= alipayParam.total_fee%>">
<input type="hidden" name="sign_type" value="<%= alipayParam.sign_type%>">
<input type="hidden" name="sign" value="<%= alipayParam.sign%>">
<div class="wrapper buy-wrapper" style="display: none;">
<a href="javascript:void(0);"
class="J-btn-submit btn mj-submit btn-strong btn-larger btn-block">确认支付</a>
</div>
</form>
<input type="hidden" id="param" value="<%= param%>">
<% include ../bootstrap.html %>
<script type="text/javascript" src="<%= dist %>/js/business-modules/artist/ap.js"></script>
<script>
var btn = document.querySelector(".J-btn-submit");
btn.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation(); var queryParam = ''; Array.prototype.slice.call(document.querySelectorAll("input[type=hidden]")).forEach(function (ele) {
queryParam += ele.name + "=" + encodeURIComponent(ele.value) + '&';
});
var gotoUrl = document.querySelector("#ali-form").getAttribute('action') + '?' + queryParam;
_AP.pay(gotoUrl); return false;
}, false);
btn.click();
</script>
</body>
</html>

该页面会自动跳转到同一文件夹下的pay.htm,该文件官方已经提供,把其中的引入ap.js的路径修改一下即可。最终效果如下:

支付宝手机网站支付流程(Node实现)

后记

支付宝的支付流程和微信的支付流程,有很多相似之处。沈晨指出一点不同:支付宝支付完成后有return_url和notify_url;微信支付完成后只有notify_url。

研读了一下微信支付的开发文档,确实如此。微信支付完成后的通知也分成两路,一路通知到notify_url,另一路返回给调用支付接口的JS,同时发微信消息提示。也就是说,跳转return_url的工作我们需要自己做。

最后,感谢沈晨帅哥提供的思路和帮助,感谢体超帅哥辛苦改后端。

书签

支付宝开放平台

https://openhome.alipay.com/platform/home.htm

支付宝开放平台-手机网站支付-文档中心

https://doc.open.alipay.com/doc2/detail?treeId=60&articleId=103564&docType=1

支付宝WAP支付接口开发

http://blog.csdn.net/tspangle/article/details/39932963

wap h5手机网站支付接口唤起支付宝钱包付款

商家服务 - 支付宝 知托付!

https://b.alipay.com/order/techService.htm

微信支付-开发文档

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

微信端支付宝支付,iframe改造

http://www.cnblogs.com/jiqing9006/p/5584268.html

微信如何突破支付宝的*

http://blog.csdn.net/lee_sire/article/details/49530875

JavaScript专题(二):深入理解iframe

http://www.cnblogs.com/fangjins/archive/2012/08/19/2645631.html

关于微信公众平台无法使用支付宝收付款的解决方案说明

https://cshall.alipay.com/enterprise/help_detail.htm?help_id=524702

支付宝手机网站支付流程(Node实现)的更多相关文章

  1. 自己动手搞定支付宝手机网站支付接口 FOR ECShop

    支付宝WAP网站版本的支付接口网上整合的比较少,看到很多网站在卖,顿觉无语. 主要是得自己查看支付宝官方提供的SDK中的开发文档. 支付宝sdk下载地址:https://doc.open.alipay ...

  2. Laravel5&period;5 支付宝手机网站支付的教程

    https://segmentfault.com/a/1190000015559571 这篇文章主要介绍了Laravel5.5 支付宝手机网站支付的教程,小编觉得挺不错的,现在分享给大家,也给大家做个 ...

  3. tp3&period;2 支付宝手机网站支付

    手机网站支付接口,支付宝官方文档:https://b.alipay.com/signing/productSet.htm?navKey=all 第一步: 1)登陆支付宝企业账号 进入支付宝官网 ,登陆 ...

  4. Asp&period;Net支付宝手机网站支付接口API之C&num;版

    一.准备工作 1.使用企业支付宝签约手机网站支付 2.下载支付宝官方demo 文档地址:https://doc.open.alipay.com/doc2/detail?treeId=60&ar ...

  5. 在laravel中使用支付宝手机网站支付 和 支付宝退款

    1.在蚂蚁金服开放平台中申请对应的应用,并根据提示进行签约 2.给对应的应用信息 生成私钥和公钥(私钥会保存在本地,公钥会放在对应的应用信息中) 3.下载手机网站支付 对应的php sdk 4.将sd ...

  6. PHP支付宝手机网站支付功能

    1.开通支付宝商家中心里面的手机网站支付 2.再去开放平台-开发者中心-创建移动支付的应用-获取到APPID 3.接着去文档中心下载DEMO 其实demo很简单.如果第一次看的话会存在看不懂的状态. ...

  7. 支付宝手机网站支付&lpar;基于Java实现支付宝手机网站支付&rpar;

    支付宝支付核心需要的参数是(APPID,PRIVATE_KEY,ALIPAY_PUBLIC_KEY) APPID:创建应用后就有的APPID. PRIVATE_KEY:应用私钥 ALIPAY_PUBL ...

  8. 支付宝开发之手机网站支付&lpar;H5支付&rpar;

    其实官方的文档中:https://docs.open.alipay.com/203 介绍的已经很详细了,我就实地的看文档操作了一遍,具体步骤如下: 一.创建应用 流程如下: 首先要有支付宝开放平台: ...

  9. PHP开发支付宝之电脑网站支付--流程简介

    前言 前端时间自己开发了一个drupal的支付宝模块,现在整理一下过程,因为支付宝官方网站提供的接口及文档都是新接口的,而且使用新接口的过程比较麻烦一点,所以整理一下 1.支付宝的账号必须经过企业资格 ...

随机推荐

  1. 如何安装NodeJS到阿里云Centos &lpar;64位版本V5-7&rpar;

    如何安装NodeJS到阿里云Centos (64位版本V5-7) (Centos与Red Hat® Enterprise Linux® / RHEL, Fedora属于一类) 1) 安装v0.10版 ...

  2. Ubuntu gcc编译报错:format &OpenCurlyQuote;&percnt;llu’ expects argument of type &OpenCurlyQuote;long long unsigned int’&comma; but argument 2 has type &OpenCurlyQuote;&lowbar;&lowbar;time&lowbar;t’ &lbrack;-Wformat&equals;&rsqb;

    平时用的都是Centos系统,今天偶然在Ubuntu下编译了一次代码,发现报错了: 源码: #include <stdio.h> #include <sys/time.h> # ...

  3. MyEcpilise引入Maven项目目录不正常,无JRE&comma;无Maven Dependencies

    右键项目--> Maven4MyEclipse --> Update Project Configuration

  4. UESTC&lowbar;树上的距离 2015 UESTC Training for Graph Theory&lt&semi;Problem E&gt&semi;

    E - 树上的距离 Time Limit: 2000/1000MS (Java/Others)     Memory Limit: 262143/262143KB (Java/Others) Subm ...

  5. 初识Android触摸事件传递机制

    前言 今天总结的一个知识点是Andorid中View事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View的另外一个难题滑动冲突,比如在ScrollView中嵌套Li ...

  6. TabLayoutBottomDemo【TabLayout实现底部选项卡】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 使用TabLayout实现底部选项卡切换功能. 效果图 代码分析 1.演示固定模式的展现 2.演示自定义布局的实现 使用步骤 一.项 ...

  7. mybatis generator 双击创建失败

    https://coding.imooc.com/learn/questiondetail/20460.html 用的下面这个方法

  8. 使用C&num;利用cmd来调用java jar包获取其中的数据

    其实也很简单,就是在C#中构建一个Process,启动jar包,并且给jar包传递参数 因为我并没有怎么学过JAVA,所以只写了个很小的Demo,就是根据传入的参数获取对应的数据 以下是JAVA De ...

  9. bootstrap table教程--使用入门基本用法

    笔者在查询bootstrap table资料的时候,看了很多文章,发觉很多文章都写了关于如何使用bootstrap table的例子,当然最好的例子还是官网.但是对于某部分技术人员来说,入门还是不够详 ...

  10. Maven项目Eclipse启动时报错: java&period;lang&period;ClassNotFoundException&colon; org&period;springframework&period;web&period;util&period;IntrospectorCleanupListener

    Eclipse中启动Maven项目时报如下错误: 严重: Error configuring application listener of class org.springframework.web ...