支付宝buybuy off是什么意思思

国内最早最知名的海淘社区[海外e购]已经9周岁啦!感谢您一直以来的支持!
海淘晒单/汇报
代购 转让 交易
论坛版务/闲聊灌水
安全提问(未设置请忽略)
母亲的名字
爷爷的名字
父亲出生的城市
您其中一位老师的名字
您个人计算机的型号
您最喜欢的餐馆名称
驾驶执照最后四位数字
【新人求助】支付宝海外直购下单钱给了,GNC找不到订单号
查看: 10278|回复: 61
该用户从未签到
事情是这样的,,29日的时候通过支付宝海外直购买了BUY 3 $25,第一次被砍单了,,然后又下了一次同样的三件物品。。。然后操心的事情就来了,支付宝说已经完成支付,钱已经到了GNC那边。但是GNC一直说找不到订单号码,没收到钱。求助各位群里的大神,咋整。。。问支付宝他只会说给订单号让我找GNC,现在他说要他们的海外的同事去和GNC沟通。。&&p! ?$ p# z2 [+ A# C
感觉不会再用支付宝海外直购了,如果转运的话,估计不用几天就能到手了吧。
该用户从未签到
大家是还没有开电脑嘛……
TA的每日心情继续买买买 13:44
me,too* }1 x$ d1 `& c" e+ M1 F6 J
2 G7 c" v' O2 Q) Y
我跟你一样,我是27号下单的,走shoprunner支付宝支付,一直都是已付款但是没发货,GNC网站上显示的下单后5分钟就可以查到,但我一直也是没有订单。。。前后和支付宝在线沟通了4遍:总说给我找专人然后还一人我又得从头再讲一遍,可最终思想都是让我自己跟GNC联系,可是你丫连个订单都没有怎么跟人说?用你支付宝还不是英文渣涂个方便?!
然后,继之前每天一两次打不通客服电话之后,昨天恼火非打通不可,连着拨打,终于通了,前后交涉了数十分钟(又他娘的换了俩所谓的专人给我说),最终结果是,向上级提交查询申请(注意是查询申请)然后让我等到8号(7天发货期过了之后3-5天查询期)如果再没有订单发货信息就再打客服(那我岂不是还得再费一番口舌?!)
这个结果,费了老鼻子劲争取的也就是一个支付宝公司帮忙查询,而且还是查询的申请,是否申请上客服不保证!) w4 H3 v- J. R; r# D0 b& ^
走shoprunner本来是想着快,我给老妈买的要可以赶在母亲节的时候邮回去,支付宝这么一折腾可好,这个月8号(10号母亲节)才知道会不会发货。。。。#¥……&……%#&……%E&^#^%$@&%^&
该用户从未签到
我跟你一样,我是27号下单的,走shoprunner支付宝支付,一直都是已付款但是没发货,GNC网站上显 ...
不科学,论坛上好几单28号下单的都发货了。。。我觉得就是有BUG。。。现在支付宝的意思就是让我再等7个工作日!!!然后我投诉支付宝,还是回到原点。今天给支付宝打电话,已经有点怒了,转了2个客服,他们好像也没有办法的样子。。。诶,我说了最多的一句就是,你们支付宝既然这样,还做这个业务干嘛,,,不如我自己找转运公司了。。。。
该用户从未签到
[attachimg]5198458[/attachimg]诶,也不是我要黑支付宝,,但是就是能不能找个懂点的人!
TA的每日心情继续买买买 13:44
不科学,论坛上好几单28号下单的都发货了。。。我觉得就是有BUG。。。现在支付宝的意思就是让我再等7个工 ...
既然论坛里有这么多查不到订单的买友发求助,就很能说明问题:# Q9 q' o' d/ ~* d, M/ R
我感觉是支付宝太着急了,这个平台明显还存在一些衔接上的断层,却缺少大量的验证,就着急上线,可苦了我们这些英文渣图个方便的普通用户。。。& Q" b/ s6 R* v, i, {; x- n2 ^! x
你别提客服了,我在线联系的时候一连换了4个人跟我讲其中有俩还是所谓的专员。。。后来打通电话,一个专员竟然连shoprunner是干啥的都没闹清楚。。。真搞不懂支付宝客服都没培训么?就这样忽悠用户??
最后我问的有些恼火了,客服给我说什么大堆的海淘流程,丫谁不知道,你说这些废话能解决毛线问题??
之后我才听他说了一句GNC发到菜鸟再到国内。。。闹了半天和惠惠海淘是一个路线!可就你这服务,人家惠惠好歹是UPS航空速我之前的单子通常1-2天到库,就你这速度还敢号称10天直邮?!这是哪门子直邮,还不是转运!!!
该用户从未签到
# e9 p( {&&B& O2 O' ^
既然论坛里有这么多查不到订单的买友发求助,就很能说明问题:
我感觉是支付宝太着急了,这个平台明显还 ...+ D5 D- }( T. b$ z0 i9 S& X
我今天打电话也怒了,真的超级气愤,转了三次。最后的答复就是,再等7个工作日。9 I2 n' d" q' K0 \) O
然后转网上的客服投诉,我把GNC的邮件给她看,她叫我翻译给她(很晕,很醉)。。。。然后就是请稍等,正在为你核实。然后等了好久,好久,五分钟之后,就断线了。
TA的每日心情继续买买买 13:44
: R6 P. H! r- N&&?
我今天打电话也怒了,真的超级气愤,转了三次。最后的答复就是,再等7个工作日。1 D- M2 M' [2 q2 [1 A/ z&&^
然后转网上的客服投诉 ...' X* C$ O) O" B& o
你竟然有邮件?我怎么什么都没有。。。你是自己跟它联系了么?
该用户从未签到
$ {- F) I6 Z9 w% y* m+ L( {
你竟然有邮件?我怎么什么都没有。。。你是自己跟它联系了么?
我一直在和GNC的客服中心发邮件,,,询问订单的事情。。。GNC大概每封邮件的结果也是说我找不到你的订单你得和支付宝联系
TA的每日心情继续买买买 13:44
所以,就是踢皮球
该用户从未签到
所以,就是踢皮球7 t6 A3 r' j: l' ~
那你现在呢?解决了嘛?还是还在等?
TA的每日心情继续买买买 13:44
那你现在呢?解决了嘛?还是还在等?1 Q9 n% l0 B, O! Y8 M
只能等呗。。。。。支付宝傻×
该用户从未签到
在GNC网站上的确是查不到~~~~你进自己的支付宝,在账单里找找,应该在那里有显示~~~~
该用户从未签到
GNC 只认你支付宝的邮箱。你只能用支付宝的邮箱注册GNC&&然后就能看到订单了
TA的每日心情继续买买买 13:44
本帖最后由 srueee 于
21:33 编辑 2 D6 h! X, s+ `9 P
&&N# s9 V, i&&|! S
那你现在呢?解决了嘛?还是还在等?) G- ^/ R# |& p2 d0 G
刚才支付宝短信过来了:7 N, f/ G5 ?: A3 ^; z
& E8 I, |) N1 z+ p+ _
“您在支付宝海外直购商家gnc购买末位为**X订单,由于海外商家备货时间较长,超过七天未发货,我们已经联系商户尽快发货,请耐心等待【支付宝】4 j' F8 i3 z7 \&&B+ D
”9 x8 ^# m&&M) J( s' Q$ F, a( V
(**X是我打的马赛克)又它妹的耐心等待,shoprunner被狗吃了?!
0 E. |; n- J8 [& }* S) v% E3 @4 b
menu.innerHTML = ''+upload_form+'' + str + '提交取消';
menu.innerHTML = '' + str + '提交取消';
showMenu({'ctrlid':ctrlid,'evt':'click','duration':3,'cache':0,'drag':1});
function seditor_insertunit(key, text, textend, moveend, selappend) {
if($(key + 'message')) {
$(key + 'message').focus();
textend = isUndefined(textend) ? '' :
moveend = isUndefined(textend) ? 0 :
selappend = isUndefined(selappend) ? 1 :
startlen = strlen(text);
endlen = strlen(textend);
if(!isUndefined($(key + 'message').selectionStart)) {
if(selappend) {
var opn = $(key + 'message').selectionStart + 0;
if(textend != '') {
text = text + $(key + 'message').value.substring($(key + 'message').selectionStart, $(key + 'message').selectionEnd) +
$(key + 'message').value = $(key + 'message').value.substr(0, $(key + 'message').selectionStart) + text + $(key + 'message').value.substr($(key + 'message').selectionEnd);
if(!moveend) {
$(key + 'message').selectionStart = opn + strlen(text) -
$(key + 'message').selectionEnd = opn + strlen(text) -
text = text +
$(key + 'message').value = $(key + 'message').value.substr(0, $(key + 'message').selectionStart) + text + $(key + 'message').value.substr($(key + 'message').selectionEnd);
} else if(document.selection && document.selection.createRange) {
var sel = document.selection.createRange();
if(!sel.text.length && $(key + 'message').sel) {
sel = $(key + 'message').
$(key + 'message').sel =
if(selappend) {
if(textend != '') {
text = text + sel.text +
sel.text = text.replace(/\r?\n/g, '\r\n');
if(!moveend) {
sel.moveStart('character', -endlen);
sel.moveEnd('character', -endlen);
sel.select();
sel.text = text +
$(key + 'message').value +=
hideMenu(2);
if(BROWSER.ie) {
function dm_editor_squarestrip(str) {
str = str.replace('[', '%5B');
str = str.replace(']', '%5D');
function dm_ajaxpost(formid, showid, waitid, showidclass, submitbtn, recall) {
var waitid = typeof waitid == 'undefined' || waitid === null ? showid : (waitid !== '' ? waitid : '');
var showidclass = !showidclass ? '' :
var ajaxframeid = 'ajaxframe';
var ajaxframe = $(ajaxframeid);
var curform = $(formid);
var formtarget = curform.
var handleResult = function() {
var s = '';
var evaled =
showloading('none');
s = jQuery("#"+ajaxframeid).contents().find("body").first().html();
} catch(e) {
s = '内部错误 无法显示';
if(parseInt(s) > 0) {
// 上传成功
var attachnew = '';
if(jQuery("#fwin_reply").length > 0){
jQuery("#postform").append(attachnew);
seditor_insertunit("post", '[attachimg]' + s, '[/attachimg]', null, 1);
}else if(jQuery("#fwin_newthread").length > 0){
jQuery("#postform").append(attachnew);
seditor_insertunit("post", '[attachimg]' + s, '[/attachimg]', null, 1);
jQuery("#fastpostform").append(attachnew);
seditor_insertunit("fastpost", '[attachimg]' + s, '[/attachimg]', null, 1);
hideMenu();
s = dm_lang[0];
} else if (parseInt(s) < 0) {
var n = Math.abs(parseInt(s));
s = dm_lang[n];
if(showidclass) {
if(showidclass != 'onerror') {
$(showid).className =
showError(s);
if(submitbtn) {
submitbtn.disabled =
if(!evaled) {
ajaxinnerhtml($(showid), s);
if(curform) curform.target =
if(typeof recall == 'function') {
eval(recall);
if(!evaled) evalscript(s);
ajaxframe.loading = 0;
if(!BROWSER.firefox || BROWSER.safari) {
$('append_parent').removeChild(ajaxframe.parentNode);
setTimeout(
function(){
$('append_parent').removeChild(ajaxframe.parentNode);
if(!ajaxframe) {
var div = document.createElement('div');
div.style.display = 'none';
div.innerHTML = '';
$('append_parent').appendChild(div);
ajaxframe = $(ajaxframeid);
} else if(ajaxframe.loading) {
_attachEvent(ajaxframe, 'load', handleResult);
showloading();
curform.target =
var action = curform.getAttribute('action');
action = hostconvert(action);
curform.action = action.replace(/\&inajax\=1/g, '')+'&inajax=1';
curform.submit();
if(submitbtn) {
submitbtn.disabled =
您需要登录后才可以回帖
回帖后跳转到最后一页JPGOODBUY转运使用教程_购物攻略_什么值得买
JPGOODBUY转运使用教程
简介是由日本銘東株式会社运营的日本转运服务品牌,主要提供日本对中国大陆的国际第三方物流转运、仓储服务。现设有日本邮政线、速享包税线、航空包税线等多条转运线路,提供免费的外箱覆膜、合箱、分箱、大纸箱等增值服务。日本邮政内部设立的仓库,最快可在当日安全地完成入库、出库流程。有使用过的值友表示他们的运费相对总体比较实惠,打包流程规范,并且服务也很完善,在官网提供有较为详细的可供参考。注册及使用教程一、注册1、前期准备:一张双币/全币的国际信用卡(具体办卡信息可见我站);第一次发货收件人的身份证正反面清晰照片(新用户注册验证时需要上传);有一个国内的支付宝/微信账号,用于支付商品的国际转运费用;2、在首页点击页面顶部的【注册】按钮,即可进入注册页面;3、点击【注册】之后进入用户注册页面,在此处填写真实的注册信息,以方便接收通知信息以及问题联系处理。可根据自身需求选择使用邮箱或者手机验证注册。填写并核实完毕后,点击下方的【立即注册】按钮,即可完成创建新账户。4、注册完成后会直接跳转到会员中心资料管理页面,在页面填写基本信息、上传身份证等信息后,点击【提交验证信息】按钮后,页面会自动跳转到会员中心页面(此处填写的地址、身份证等信息会默认保存为第一个收件地址,请确保此页面填写的地址及身份证信息真实有效);二、管理个人信息及充值1、完成注册之后会自动进入【会员中心】页面,在此页面可查看日本购物所需的转运仓库地址,以及在购物网站的地址填写范例。(1)会员中心左侧的【包裹管理】页面,可查看已入库和处理中等包裹状态,也可进行【到货预报】、【货到即发】、【货到付款】、【申请退货】等包裹操作;(2)会员中心左侧的【订单管理】页面,可进行【提交发货】、【包税发货】操作,而【订单列表】则可查看已提交的发货订单等内容;(3)会员中心左侧的【财务管理】页面中可以查看【我的账户】、【优惠券】和其他账户信息,【在线充值】和【提现申请】也可以在此处操作,目前可支持支付宝和微信充值,点击【财务管理】页面右侧的【在线充值】即可;(4)进入充值页面后,填写充值金额,选择支付方式,第一个为微信支付,第二个为支付宝支付,会员备注可不填写。点击【提交申请】后请耐心等待页面跳转,然后在跳转的支付页面手机扫码或登录您的微信/支付宝账号进行支付。三、如何使用转运服务1、使用所提供的仓库地址购物完成后,卖家发货会邮件通知日本国内的快递运输单号,您可在【会员中心】——【到货预报】处进行登记,提前预报可方便管理自己的入库包裹,并且能避免因卖家忘填写入库码而导致的包裹无法入库的情况,预报完成后包裹单号会在【我的包裹】列表内显示(详细预报教程可参考);2、购买的包裹入库后会在【会员中心】——【我的包裹】列表显示状态为【已入库】,而【我的包裹】括号内显示的数字为【已入库】可提交发货的包裹数量 ,包裹入库会发送邮件通知;3、点击会员中心的【订单管理】——【提交发货】,即可进入已入库包裹的提交发货操作页面,包括线路选择、合箱分箱、申报填写、收货地址、增值服务等操作都在【提交发货】页面进行。 信息填写完成后点击【提交订单】即可(详细提交发货教程请参考,航空包税线路需在【包税发货】页面提交);4、订单提交成功后会在会员中心【订单列表】内显示,包裹打包完成付款后会在订单列表内【运单号】一栏更新国际快递单号;5、包裹发出后用户就可以耐心等待国内收货了;6、以上的包裹入库、提交发货后的订单处理等操作都会有相关邮件进行通知来提醒用户;此外,如果在转运过程中遇到任何问题,均可通过网站首页【QQ交谈】联系在线客服咨询,或是通过客户服务邮箱service@jpgoodbuy,或是拨打客服电话:的方式进行问题咨询。
本文著作权归作者本人和什么值得买共同所有,未经许可不得转载。文章仅代表作者看法,如有更多内容分享或是对文中观点有不同见解,值客原创欢迎您的投稿。
推荐关注:
鼠标移到标签上方,
尝试关注标签~
相关热门原创
作者其他原创(732)
赞18评论31
Edward /Bookman 近视眼镜
cneeds 车里子 智能轻车机C1
德国DC德式康线上药房100欧购物体验券
BWT 家用滤水壶
3.6L 一壶一芯
Honor 荣耀8青春版 智能手机
日本Bestco煎烤亲子锅具套组(赠生鲜食材)
野小兽 智能课程系统动感单车
Panasonic 松下 手持吸尘器
【轻众测】Gris perle 佩噜噜 洁面泥
兰蔻小黑瓶精华肌底液5ml*2
【轻众测】蜜芽 500元代金券
赞89评论113
赞58评论140
赞118评论73
赞1997评论549
赞1772评论1132
赞387评论283
赞582评论858
赞816评论537
扫一下,分享更方便,购买更轻松
用户名/邮箱
两周内免登录78126人阅读
支付宝WAP支付接口开发
因项目需要,要增加支付宝手机网站支付功能,找了支付宝的样例代码和接口说明,折腾两天搞定,谨以此文作为这两天摸索的总结。由于公司有自己的支付接口,并不直接使用这个接口,所以晚些时候打算把测试代码整理好放到Github上。
1. 开发前准备
到官网了解此接口的信息,下载样例代码(只有ASP.NET和PHP)以便随时参考。一个通过实名认证的企业支付宝账号,并申请开通手机WAP支付功能,我的测试账号是拿公司的,申请流程不清楚,官网有说怎么申请,各位各显神通吧。公网域名和node.js环境。下面的代码大多用coffee来表达,不过本文不会贴太多代码,即使对coffee不熟悉也没什么关系。关于coffee可以。
github上有两个开源小项目(搜索&alipay&),但都没有WAP支付功能,可以拿来当参考,可以认为是示例代码的js移植版,结构很相像。我原打算在其中一个项目基础上继续开发,看了代码和接口文档后,还是决定从头开发一个。因为原有代码层次不够清晰,有点过度设计的感觉,而且支付宝的接口很简单,重写工作量不大。
吐槽下: 官网的示例代码真只是示例级(test)而已,跟产品级(production)还隔比较远,感觉还谈不上SDK。接口文档相当的坑爹,正因如此我才觉得有必要好好写篇文章总结。
接口开发最重要的应该是理解数据交互流程了,流程弄清了,并理解为何这么设计,开发起来也是事半功倍
首先,要准备下面几个参数:
企业支付宝账号的PID(也叫ParnerID)和KEY,如果使用RSA签名而不是MD5的话,还要把RSA私钥准备好支付时用户看到的东西:商品名称(subject)、支付总额(total_fee)、购买数量(通常都是1吧)交易后的跳转地址,交易成功后用户可以手工点击,或页面延迟自动跳转到这个地址(return_url)交易状态异步通知地址,交易成功或交易关闭会把消息POST到这个地址(notify_url)
然后,看这幅流程图(不错吧,推荐下这个网站:)
Alipay WAP pay flow
Browzer-&&#43;Site: 1. HTTP GET
note over Site: 2. create a new trade
Site-&&#43;Alipay: 3. create redirect
Alipay-&-Site: 4. Token
note over Site: 5. build auth url
Site-&-Browzer: 6. redirect to auth url
Browzer-&Alipay: 7. redirect
Alipay-&Browzer: 8. trade info
Browzer-&Alipay: 9. auth and pay
Alipay-&&#43;Site: 10. HTTP POST notify
note over Site: 11. process trade
Site-&-Alipay: 12. reply &success&
Alipay-&Browzer: 13. pay success
Browzer-&Site: 14. goto return url
这个流程图基本囊括了整个交互过程,下面是说明:
用户点击购买按钮(或其他形式),向网站发起购买请求网站创建订单,指派一个唯一订单号网站把订单号、企业支付宝账号、交易金额、数量等信息,用私钥签名发送给支付宝支付宝创建一个交易订单,返回一个交易令牌(token)网站按照指定要求,用token和自己的私钥,构造一个重定向得到支付地址网站把重定向地址返回给浏览器浏览器自动重定向到该地址,即包含了token、网站签名的支付宝交易页面支付宝显示当前交易金额、数量、卖家等信息用户用自己的支付宝账号支付这笔金额支付宝把用户支付成功(或失败)这个消息和订单号加上支付宝的签名,使用HTTP POST的方式通知网站(失败的话,会隔段时间重新发送)网站处理交易后续逻辑(发货、订单状态存储之类的)网站返回&success&字符串给支付宝,表示该通知已经处理,不用再重发支付宝显示支付成功页面给用户(这一步和第10步是不分先后发生的)支付成功页面延迟自动跳转,或用户点击“返回商户页面”,跳转到网站的支付结束页面(此时不一定成功处理支付宝发来的通知),但会在URL带上当前的订单号和状态。
可以发现,整个流程有点像OAuth(哎呀,之前那篇文章还没写完呢!),主要分三步:
一是申请支付宝交易号(获取token),这一步可以理解为,让支付宝验证网站的有效性、让网站指定该交易要支付多少钱 二是用户到支付宝页面付款,这一步可以理解为,让支付宝验证用户有效性,让用户在一个不受网站监视的环境下进行支付 三是用户付款后,处理结果页面告诉用户支付成功(同步通知),另外异步通知网站服务器该订单已支付。
支付宝的接口文档说只有两个步骤,感觉不是很好理解,三步还是比较准确的(收钱肯定要办事的嘛)。
好困,细节问题下期继续。。。
3.1 网站向支付宝申请新订单
网站的订单系统先产生一个新订单,然后请求支付宝创建一个支付宝订单.
申请新订单的service是&alipay.wap.trade.create.direct&需要提交的关键参数包括:
3.1.1 用户在支付宝看到的订单信息:
subject: 商品名称
total_fee: 总金额
seller_account_name: 卖家支付宝账号(估计跟私钥绑定的)
merchant_url: 商品展示URL(&#20284;乎这个并非必要)
3.1.2 支付宝通知网站时将附带的信息:
out_trade_no: 该次交易对应网站的订单号(要求唯一)
call_back_url: 交易成功后,支付宝页面上“返回到商家页面”的地址(同步回调)
notify_url: 交易状态变更后,支付宝通知网站的回调地址(异步通知)
支付宝验证通过后,将返回新创建的支付宝订单号,网站可将该订单号与自己订单系统的的订单号绑定在一起。支付宝同时返回的还有该次交易的token,用于(3.2)用户支付。
3.2 用户在支付宝网站,查看订单消息,通过验证并支付
网站返回跳转到支付宝的地址,service是alipay.wap.auth.authAndExecute,包含(3.1)返回的token和网站对跳转地址的签名
这是个HTTPS页面,基本认为是安全的。当然前提是浏览器没被动手脚,安卓不少应用被捆绑广告那是常有的事,手机浏览器对HTTPS也不像PC那样有明显提示,这些也是我不怎么信任手机支付的原因。
用户跳转到支付宝页面后,可以在该页面里看到当前支付的订单的名称和金额,这些是3.1申请时由网站指定的,让用户在支付宝的页面确认一次再付款是合理的。
3.3 支付宝通知网站支付成功,网站收钱做事
这个过程是支付宝通知网站,网站处理后通知用户已到账,共包括两个并行部分:
3.3.1 异步通知
用户支付后,支付宝通过HTTP协议通知网站该订单交易结果。说白了就是支付宝悄悄地告诉网站“这个订单已经已经付款啦”
&#20540;得注意的是,异步通知有重发机制,支付宝需要得到响应为&success&才认为该通知成功被接收,否则会间隔一段时间重发,依次间隔2m,10m,10m,1h,2h,6h,15h,最多8次通知,由notify_id说明是同一个通知。8次通知都接收失败怎么办?额orz...文档没说,用那个支付宝订单号登录支付宝去查账吧。
3.3.2 同步通知
用户支付后,支付宝页面提示“支付成功”,可点击返回商家页面,也可等待一段时间自动跳回
个人认为,网站跳到这个页面后,如果仍未收到(3.3.1)异步通知,并且使用的是MD5签名,应该把状态从“待付款”调整为“等待对账”,而不应该贸然相信该通知的结果。原因是这个回调地址用户是可以知道的,MD5签名还是有被伪造的可能(4.3)。当然额外再做个token之类的理论上也行(需要放在urlpath而不是querystring)。
假如接口调用出错,通知是不会签名的。不签名的原因我怀疑是防止有人恶意收集请求-签名样本,见(4.3)。
4. 签名与加密
简单的说,签名防篡改,加密防窃听。上面的两种请求(3.1和3.2)和两个通知(3.3.1和3.3.2)都被要加签名,支付宝支持下面两类签名:
4.1 MD5: 业务数据不加密,防篡改
优点: 相对较简单(当然是相对DSA/RSA来说),计算速度快,明文更直观
缺点: 可抵赖,可能被窃听、安全性不如非对称加密
4.2 DSA/RSA: 业务数据加密,也防篡改
优点:不可抵赖,安全性较高
缺点:相对较复杂,解密速度慢
一开始我想不懂,支付宝既然支持RSA为何还要支持MD5,后来有人说RSA太慢,想想支付宝的业务量就释然了。由于每个商家的私钥都不同,并且跟商家的支付宝账号绑定,即使商家的私钥被破解了,用户支付时HTTPS协议基本可以保证用户支付的目标还是商家的账号。
4.3 使用MD5签名可能存在的风险
以下情景仅是我的推断,没有尝试过,所谓道高一尺魔高一丈,希望读者也别以身犯险。
在用户支付的步骤(3.2)和支付成功响应页面(3.3),用户可以得到一个明文请求内容和对应签名。由于网站和支付宝直接通信共用同一个密钥,一般长期不变,双方都可以对同一段数据产生签名,这就有可能抵赖的风险:
网站:“这个数据是你发过来的,上面有你的签名。”
支付宝:“不是我发的。这个数据是你伪造的,签名是你签的。”
另外,当攻击者收集到足够多的样本,是有可能破解出密钥的,继而可伪造网站或支付宝任意一方。
4.3.1 恶意消耗商家的订单号
攻击者伪造大量未使用的订单号(不少网站的订单号都是递增的纯数字,并公开给用户,且很容易推测到后面的数字),向支付宝请求订单,直到超时。由于商家对此并不知情(回调地址和通知地址均篡改掉),其他用户下单时假如商家用了被伪造过的订单号,就可能被支付宝认为提交了重复订单,结果支付失败。
4.3.2 欺骗商家已支付订单
由同步通知(3.3.2)返回的参数可以看到,网站订单标识和交易token和都是可以得到的。这样的话,关于步骤(3.1),用户不知道的参数包括notify_url和out_user_no,假如网站的用户id本身就是公开的,通知回调地址(3.3.1)被得知或同步通知(3.3.2)实现的不好,就可以通过伪造支付通知,欺骗商家订单已支付。
待续,下期补充实现代码
我只做了MD5签名,项目里没用到RSA签名,就没做那方面。按照文档说明和demo源代码,很容易就可以写出下面的签名代码:
getSign = (obj,key) -&
return null unless obj
arr = ([k,v] for k,v of obj when k isnt 'sign' and v? and v isnt '')
arr.sort()
src = (&#{i[0]}=#{i[1]}& for i in arr).join '&'
src = &#{src}#{key}&
crypto.createHash('md5').update(src,'utf8').digest 'hex'
支付宝发到网站的通知(3.3)的签名算法跟上面有点不一样,文档有这么段说明:
这里说要按通知的参数的原本顺序计算签名。所以我就把上面的arr.sort()去掉然后计算签名,结果发通知发来的签名和我自己计算的不一致,纠结半天后仔细看文档的样例说明,看到下面段:
仔细跟实际接收的数据比较之后发现,文档和样例都说发来的参数顺序是(service,v,sec_id,notify_data),但我实际收到的并不是按这个顺序,只要按照文档的参数顺序重新排列再计算签名就正确了,最终通知的签名算法如下( 真是个蛋疼的大坑orz):
getNotitySign = (obj,key) -&
return null unless obj
src = (&#{k}=#{obj[k]}& for k in [&service&,&v&,&sec_id&,&notify_data&]).join '&'
src = &#{src}#{key}&
crypto.createHash('md5').update(src,'utf8').digest 'hex'
文档里有说到字符编码参数_input_charset,我用的是utf8编码,发现不用传这个参数也可以,看来支付宝默认的字符编码就是utf8了
如果使用RSA签名,需要先解密再计算签名
5.2 辅助方法
为了代码层次更清晰,我把签名、url拼接等方法抽出到一个单独模块(alipay_api.coffee):
api_url = &/service/rest.htm&
regexTokenXml = /&request_token&(.*)&\/request_token&/
module.exports = api =
create: &alipay.wap.trade.create.direct&
auth: &alipay.wap.auth.authAndExecute&
toReqData: (name,obj) -&
arr = [&&#{name}&&]
arr.push &&#{k}&#{v}&/#{k}&& for k,v of obj
arr.push &&/#{name}&&
arr.join ''
createReq: (service,partner,req_data) -&
service : service
partner : partner
req_data: req_data
parseTokenFromXml: (xml) -&
return null unless xml
m = regexTokenXml.exec xml
m?[1]?.trim()
getSign: (obj,key='') -&
return null unless obj
arr = ([k,v] for k,v of obj when k isnt 'sign' and v? and v isnt '')
arr.sort()
src = (&#{i[0]}=#{i[1]}& for i in arr).join '&'
src = &#{src}#{key}&
crypto.createHash('md5').update(src,'utf8').digest 'hex'
getNotitySign: (obj,key='') -&
return null unless obj
src = (&#{k}=#{obj[k]}& for k in [&service&,&v&,&sec_id&,&notify_data&]).join '&'
src = &#{src}#{key}&
crypto.createHash('md5').update(src,'utf8').digest 'hex'
sendCreate: (req,done) -&
url: createCreateUrl req
request.get opt, (err,res,body) -&
return done err if err
body = && unless body
ret = querystring.parse body
body = null
done null,ret
createAuthUrl: (token='',key='') -&
req = api.createReq api.services.auth
req.req_data = &&auth_and_execute_req&&request_token&#{token}&/request_token&&/auth_and_execute_req&&
req.sign = api.getSign req, yes
createAuthUrl req
createCreateUrl = (req) -&
url = &#{api_url}?&
url &#43;= &req_data=#{encodeURIComponent req.req_data}&
url &#43;= &&service=#{encodeURIComponent req.service}&
url &#43;= &&sec_id=#{encodeURIComponent req.sec_id}&
url &#43;= &&partner=#{encodeURIComponent req.partner}&
url &#43;= &&req_id=#{encodeURIComponent req.req_id}&
url &#43;= &&sign=#{encodeURIComponent req.sign}&
url &#43;= &&format=#{encodeURIComponent req.format}&
url &#43;= &&v=#{encodeURIComponent req.v}&
createAuthUrl = (req) -&
url = &#{api_url}?&
url &#43;= &req_data=#{encodeURIComponent req.req_data}&
url &#43;= &&service=#{encodeURIComponent req.service}&
url &#43;= &&sec_id=#{encodeURIComponent req.sec_id}&
url &#43;= &&partner=#{encodeURIComponent req.partner}&
url &#43;= &&sign=#{encodeURIComponent req.sign}&
url &#43;= &&format=#{encodeURIComponent req.format}&
url &#43;= &&v=#{encodeURIComponent req.v}&
5.3 业务部分
5.3.1 购买(buy)
购买的逻辑对应于(2)流程图的(2,3,4,5),创建唯一请求ID,填充本次交易信息,发送到支付宝并获取token,然后拼接支付url并签名,然后重定向。
demo.buy = (info,done) -&
return done 'bad user' unless info?.user_id?.length&10
req = api.createReq api.services.create, info.partner
redirect: ''
token: null
async.series [
getRequestId req.service,(err,req_id) -&
req.req_id = req_id
createTrade info,req.req_id,(err, tradeId) -&
return cb err if err
req.req_data =
: info.subject
out_trade_no
: tradeId.toString()
: info.total_fee
seller_account_name: info.seller_account_name
call_back_url
: info.call_back_url
notify_url
: info.notify_url
: info.user_id
merchant_url
: info.merchant_url
req.pay_expire = info.pay_expire if info.pay_expire?
req.req_data = api.toReqData 'direct_trade_create_req',req.req_data
req.sign = api.getSign req, info.key
api.sendCreate req, (err,res) -&
return cb err if err
return cb 'bad sign from alipay server' unless req.sign is api.getSign req
ret.token = api.parseTokenFromXml res.res_data
ret.redirect = api.createAuthUrl ret.token
storeTradeInfo req.out_trade_no, req.total_fee, ret.token, (err,success) -&
return cb err if err
cb if success then null else 'store trade info fail'
],(err) -&
done err, ret.redirect
其中用到的几个方法跟存储相关,我用的是MySQL:
getRequestId = (service, done) -&
sql = &insert into alipay_requests(service,create_time,state) values(?,now(),'CREATE')&
db.queryAll sql,[service],(err,result) -&
done err, result?.insertId
createTrade = (info,req_id,callback) -&
sql = &insert into alipay_trades(user,req_id,create_time) values(?,?,now())&
db.queryAll sql,[info.user_id,req_id],(err,result) -&
callback err, result?.insertId
storeTradeInfo = (tradeId,rmb,token,callback) -&
sql = &update alipay_trades set rmb=?,token=?,state='WAIT_PAY' where id = ?&
args = [rmb,token,tradeId]
db.queryAll sql,args,(err,updateResult) -&
callback err,updateResult?.affectedRows is 1
5.3.2 通知(notify)
用户支付后就等着通知了,按道理应该在TRADE_SUCCESS时处理用户支付成功的逻辑,但我实际测试发现至发送了TRADE_FINISHED事件来,所以干脆两个一并处理了,反正只会有一次成功。
xmlreader = require 'xmlreader'rr,updateResult?.affectedRows is 1
demo.onNotify = (req,callback) -&
xmlreader.read req.notify_data,(err,xdoc) -&
return done err if err
notify = xdoc.notify
notify_id = notify?.notify_id?.text()
return done 'bad notify_data' unless notify_id
done = (response) -&
unless 'string' is typeof response
console.error &response notify error: & &#43; (response?.stack ? response ? '')
response = 'server error'
console.error &response notify: #{response}& unless response is 'success'
storeNotifyResponse notify_id,response, (err) -&
callback if response is 'success' then err else response
storeNotifyDetails notify_id, notify, req, (err,success) -&
return done err if err
unless success
return done &store detail error&
trade_status = notify.trade_status.text()
if trade_status is 'TRADE_FINISHED' or trade_status is 'TRADE_SUCCESS'
unless req.sign is api.getNotitySign req
return done 'bad sign'
out_trade_no = notify.out_trade_no.text()
getTradeUser out_trade_no,(err,user) -&
return done err if err
user_id = user?.user
return done 'unknown user' unless user_id
onPayed user_id,(err,success) -&
return done err if err
return done &onPayed error& unless success
storeTradeFinalState out_trade_no, no, console.error
done 'success'
return done 'unknown trade status'
跟存储相关的几个方法如下:
storeTradeFinalState = (tradeId,isError,callback) -&
state = if isError then 'FAILURE' else 'SUCCESS'
sql = &update alipay_trades set state=?,close_time=now() where id = ?&
args = [state,tradeId]
db.queryAll sql,args,(err,updateResult) -&
callback err,updateResult?.affectedRows is 1
getTradeUser = (out_trade_no,callback) -&
sql = &select id,user from alipay_trades where id=?&
db.queryOne sql,[out_trade_no],callback
onPayed = (user_id,callback) -&
sql = &update users set vip=1 where id=? and vip=0 limit 1&
db.queryAll sql,[user_id],(err,updateResult) -&
callback err, updateResult?.affectedRows is 1
storeNotifyDetails = (notify_id,notify,raw,callback) -&
sql = &insert ignore into alipay_notifies(id,recv_time,subject,trade_no,gmt_create,
quantity,out_trade_no,notify_time,total_fee,buyer_email,trade_status,
gmt_payment,gmt_close,raw) values(?,now(),?,?,?,?,?,?,?,?,?,?,?,? )&
notify_id,
notify.subject?.text()
notify.trade_no?.text()
notify.gmt_create?.text()
notify.quantity?.text()
notify.out_trade_no?.text()
notify.notify_time?.text()
notify.total_fee?.text()
notify.buyer_email?.text()
notify.trade_status?.text()
notify.gmt_payment?.text()
notify.gmt_close?.text()
JSON.stringify raw
db.queryAll sql,args,(err,updateResult) -&
callback err,updateResult?.affectedRows is 1
storeNotifyResponse = (notify_id,response,done) -&
sql = &update alipay_notifies set response=? where id=?&
db.queryAll sql,[response,notify_id],done
有些公司有自己的支付平台,封装了一层,结果调用流程变成下面这样:
Alipay WAP using platform pay flow
Browzer-&&#43;Site: 1. HTTP GET
note over Site: 2. create a new trade
Site-&-Browzer: 3. trade info
Browzer-&&#43;Platform: 4. redirect
note over Platform: 5. create a new trade
Platform-&&#43;Alipay: 6. create redirect
Alipay-&-Platform: 7. Token
note over Platform: 8. build auth url
Platform-&-Browzer: 9. redirect to auth url
Browzer-&Alipay: 10. redirect
Alipay-&Browzer: 11. trade info
Browzer-&Alipay: 12. auth and pay
Alipay-&&#43;Platform: 13. HTTP POST notify
Platform-&Site: 14. proxy
note over Site: 15. process trade
Site-&Platform: 16. reply &success&
Platform-&-Alipay: 17. reply &success&
Alipay-&Browzer: 18. pay success
Browzer-&Site: 19. goto return url
咋一看,挺方便的,Site只需要做个跳转即可,细节都被Platform隐藏起来了,切换不同的支付方式也变得很方便。
可是,这个平台的接口并不完善,这些数据都是明文并且没有要求Site做签名,于是就有一个风险。跟(2)的流程图对比,可以发现这个流程多了(3,4,14,16)四个步骤。后面两步只是个代理包装,没什么问题,问题在于步骤3和4。
可以看到,交易细节被放到了重定向url中了,即用户可以得知交易内容并篡改里面的数据。举个例子,假设重定向地址类&#20284;这样
由于该地址没有验签机制,所以攻击者很容易就可以发现这里面可以随意篡改数据。
方法1: 将rmb=100.00改成0.01,即将100元的支付变成1分钱,然后支付。结果Site得到通知的时候,就需要额外处理这种支付款项和要求款项不一致的情况。当然最简单就是金额不足就不退款并且支付失败了。假如没做这种判断,那就相当把100元的商品以1分钱卖出去了。
方法2: 伪造大量tradeno,然后请求,并且不支付款项。由于tradeno要求唯一,并只能使用一次,这样就相当于消耗掉了site的交易ID,并且site对此毫不知情。结果正常用户要购买时,创建的订单号对于site来说是未使用的,但对platform来说已经是使用过了,会返回支付失败。
要避免这个问题,需要在重定向的地址增加签名,签名异常的请求都抛弃掉。
(全文完)
转自:http://neutra.github.io/2013/%E6%94%AF%E4%BB%98%E5%AE%9DWAP%E6%94%AF%E4%BB%98%E6%8E%A5%E5%8F%A3%E5%BC%80%E5%8F%91/
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:433802次
积分:4192
积分:4192
排名:第6031名
原创:86篇
转载:98篇
评论:27条
(1)(4)(19)(6)(4)(2)(6)(8)(2)(13)(43)(19)(10)(18)(7)(4)(13)(1)(4)(1)

我要回帖

更多关于 buysell是什么意思 的文章

 

随机推荐