“ Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!
当面付再难,有人带路,轻松搞定
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处
“ Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!
当面付再难,有人带路,轻松搞定
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处
“ Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!
当面付再难,加班也要搞定
上回有聊到支付宝扫码支付的几种实现方式,老接口实现的扫码,与现在支付宝当面付的实现方式,完全是两码事了,差太多了,我好难。接口不再返回QrCodeImageUrl,仅返回QrCode,同时也没有收银台提供了。当把当面付的功能写完之后,内外网联调,各种意想不到的问题都跑过来了。在这里记录一下这场战役的一些感受。
一、支付宝沙箱vs支付宝正式环境
原本沙箱是用来任性的支付宝,完成上正式前踩坑的绝佳环境,但是这次研发体验却发现支付宝沙箱埋了一堆坑,就连支付宝对接的“机器人”都不建议使用沙箱。
1.1 验签方式异常
沙箱里使用RSA畅通无阻,让人已经形成了基本的RSA1\RSA2的两种接口验签概念,RSA1咱们对应上公钥方式,RSA2咱们对应证书方式,结果沙箱里写死的RSA,在正式上通不过,得换成RSA2。
1.2 接口功能异常
在沙箱里设置交易超时时长为一分钟,刚扫完码就失效了。只好在沙箱(沙盒)里配置5分钟交易时长。测试QA们卡一分钟,简直就是坑得一波一波的。
二、环境配置稍微有点多
为了适应支付宝沙盒环境与正式环境的切换,特意弄成了可配置的,结果联调的时候,有时候会忘掉,就出现了神一样的提示语。
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or a non-white space character among the padding characters
三、当面付收银台功能
老接口有一个功能是生成一个商品码,买家扫码后,会有一个简单的收银界面,买家可以调整商品数量,确认完后,支付宝又会通知我方接口生成订单,然后支付宝发生支付操作。新版的支付宝当面付,不提供这个收银界面。带来了新的问题,得自己实现收银界面,生成收银界面的二维码,要解决修改host后,正式的支付宝客户端无法访问到测试的内网站点。
3.1 二维码生成问题
收银台接收了比较多的参数,参数一多,二维生成失败,直接报超过数组标,将二维码的生成版本改成0,也就是让二维码随着内容自己适应大小,到是让二维码出来了,开始还能识别出来。当我对些敏感参数进行加密处理后,url变得稍微有些长,二维码生成出来的效果是密密麻麻的,手机识别好久都识别不出来。解决方案:参数仅传一个token,对应的参数放到后端的内存中(redis,memcache之类的东东里)。Token生成的话,类似于短链接生成,保持不重复,短小就好。这种规则,跟生成单号有些类似,要是琢磨最佳的生产算法的话,还得仔细分析分析,拿到最短的又能满足要求的。为了节省时间,可以直接使用Guid.
内容多,生成的码就密集。
比如如下链接:
http://yue.ma/?看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧看看这个码有多么密集吧
生成二维码效果:
识别起来会比较费劲,看起来也有点不舒服。
如果换成简短的链接,比如:
http://yue.ma/?qr=helloworld
生成出来的二维码就比较清爽了。
3.2 外网联调
修改hosts不行,那就直能提供一个真实的host,支付宝有防止host补“黑”的功能。试试微信,也有这样子的功能。对于研发人员来讲,这算一个坏消息。由是在一个专用的测试站点,带有真实的dns解析,完成。
3.3 惊天动地的QrCodeImageUrl
支付宝老接口返回QrCode以及对应的ImageUrl,当我劲了九牛二户之力,生成完二维码图片,按着老规则返回给客户端,吓坏了,一堆乱码。这不是一张图片,还是一个超小型的网页,里面放了一张图片。这下子,前端工程师上战场了,调整大小,位置,很是需要耐心。
四、密钥大坑
应用公钥2048、应用私钥2048、支付宝公钥,因为是非对称的密钥配置,应用的公钥要告诉支付宝,支付宝要把TA的公钥告诉咱们,这都好理解是吧。就是有行小字比较伤人。密钥也分java使用,非java使用。
正好前阵子玩java版当面付,结果把密钥都无脑的配置成了默认的java版本PKCS8,结果就在坑里爬了好久好久。
今天的当面付经验分享就告一段落了,主要的踩坑点都已经标记出来,每个人看问题的角度与处理速度不一样,也许也帮不上什么忙,就当自己记录一下自己的心得好了。关注公众号,且看下回分解。
让天地下没有难接的支付
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处
alipay.mobile.public.qrcode.create(生成带参数的二维码)
alipay.trade.page.pay
alipay.mobile.qrcode.manage
不支持设置过期时间
您这边使用二维码管理接口是需要实现什么功能?
这个接口可以设置支付超时时间的。
支付宝二维码管理接口
(关注技术岛公众号,回复alipay获取支付宝二维码管理接口文档)
pay_timeout
时间
String
最小 5 分钟最大两天,默认 15 分钟。
30
logo
“ Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!
验证码再难,有人带路,轻松搞定
使用阿里滑块验证码的风险:
一、产品下线风险
二、破解风险与成本风险并存
实例导航
https://i.zuime.com/2019/10/11/%E8%85%BE%E8%AE%AF%E4%BA%91%E9%AA%8C%E8%AF%81%E7%A0%81%E9%9B%86%E6%88%90/
前端示例
https://i.zuime.com/tencent/captcha
验证接口
https://i.zuime.com/tencent/verify?ticket=t02PT9ySVJ80sIcI3xYNU-2CkRrXxquqXcPsdNYstOny7DVDl7i00VqHK4RrTFxIijSv2FXgn0TpXg0aDfc6sTij0Nncf3NUtzV9a89AEJ65YTOqAY6S9gY4A**&randstr=@UZL
验证码后台文档 https://cloud.tencent.com/document/product/1110/36926Web
前端接入文档 https://cloud.tencent.com/document/product/1110/36841
前端代码参考
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>腾讯云验证码 demo</title>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
<link rel="stylesheet" type="text/css" href="/css/markdown.css"/>
<script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
</head>
<body>
腾讯云验证码 demo
<button id="TencentCaptcha"
data-appid="2046804662"
data-cbfn="callback"
type="button"
>验证</button>
<!--===============================================================================================-->
<script src="/jquery/jquery-3.2.1.min.js"></script>
<script src="/jquery/jquery.datetimepicker.full.min.js"></script>
<script src="/jquery/jquery-confirm.min.js"></script>
<!--===============================================================================================-->
<script src="/js/main.js"></script>
<script src="/js/jblog-admin.js"></script><script>
window.callback = function(res){
console.log(res)
// res(用户主动关闭验证码)= {ret: 2, ticket: null}
// res(验证成功) = {ret: 0, ticket: "String", randstr: "String"}
if(res.ret === 0){
alert(res.ticket); // 票据
$.get("/tencent/verify",
{
ticket:res.ticket,
randstr:res.randstr
}
,function(data){
alert(data);
});
}
}
</script>
</body>
</html>
后端代码:agent
package jblog.guohai.org.bll.agent;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.tencentcloudapi.captcha.v20190722.CaptchaClient;
import com.tencentcloudapi.captcha.v20190722.models.DescribeCaptchaResultRequest;
import com.tencentcloudapi.captcha.v20190722.models.DescribeCaptchaResultResponse;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
public class TencentCaptchaAgent {
/**
* 网关地址
*/
private String secretId;
private String secretKey;
/**
* 网关地址
*/
private Long captchaAppId;
private String appSecretKey;
public String verify(String ticket,String randstr,String userIp){
try{
// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey
Credential cred = new Credential(secretId, secretKey);
// 实例化要请求产品(以cvm为例)的client对象
ClientProfile clientProfile = new ClientProfile();
clientProfile.setSignMethod(ClientProfile.SIGN_TC3_256);
CaptchaClient client = new CaptchaClient(cred, "ap-beijing", clientProfile);
// 实例化一个请求对象
DescribeCaptchaResultRequest req = new DescribeCaptchaResultRequest();
req.setCaptchaType(9L);
req.setTicket(ticket);
req.setCaptchaAppId(captchaAppId);
req.setAppSecretKey(appSecretKey);
req.setRandstr(randstr);
req.setUserIp(userIp);
// 通过client对象调用想要访问的接口,需要传入请求对象
DescribeCaptchaResultResponse resp = client.DescribeCaptchaResult(req);
// 输出json格式的字符串回包
System.out.println(DescribeCaptchaResultRequest.toJsonString(resp));
return DescribeCaptchaResultRequest.toJsonString(resp);
} catch (TencentCloudSDKException e) {
System.out.println(e.toString());
return e.toString();
}
}
}
后端代码 :控制器
package jblog.guohai.org.web;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import jblog.guohai.org.bll.agent.TencentCaptchaAgent;
/**
* 腾讯云
*
* @author zhongdaiqi
*
*/
public class TencentController {
private TencentCaptchaAgent tencentCaptchaAgent;
public String captcha(Model model) throws Exception {
return "tencent/captcha";
}
public String verify(Model model, String ticket, String randstr,HttpServletRequest request) throws Exception {
return tencentCaptchaAgent.verify(ticket, randstr,request.getRemoteAddr());
}
}
后端代码:POM包
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<!-- go to https://search.maven.org/search?q=tencentcloud-sdk-java and get the latest version. -->
<!-- 请到https://search.maven.org/search?q=tencentcloud-sdk-java查询最新版本 -->
<version>3.0.95</version>
</dependency>
以上代码仅供参考,未处理坏味道,请谨慎使用!获取完整的可运行项目代码方式:关注公众号,回复”tencent”。
关注公众号Yuema约吗,每天进步一点点
腾讯云福利
【新用户限量秒杀】热门云产品限量秒杀,云服务器1核1G 首年99元
识别二维码进入活动现场!
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处
“ Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!
支付再难,有人带路,轻松搞定
每次看动漫看美剧,都有狗血的前情回顾,占了好长的时间,有种说不同的酸痛感!腰疼!
想不到,我也活成了自己讨厌的人,我也要写个前情回顾
前情回顾
上回讲到接阿里蚂蚁金服支付宝网银直连的整体代码结构,提到封装了一个alipay-bankpay-sdk,并画了2个对比图,介绍了一下这么干的好处,代码清晰,通用性强。按文章的条理来讲,您应该先看上篇,再来看这篇。
关注Yuema约吗公众号,回复”bankpay”,查看《支付宝网银直连SDK封装记上|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环》
本文向导
本文主要分享一下封装支付宝网银直连sdk的思路、坏味道清理,并做个小小的总结。完整的看完本文,您应该学会一种取巧的sdk封装思路并能运行于实战,并且能看到一些更加简洁的代码写法,并能学会如何从BTA中学东西,并从中剔除不良习惯。
封装思路
虽然阿里支付宝的程序员在代码中明确表明给出来的demo仅供参考blabla的。但是整体上来讲,封装sdk还是主要使用他们的demo,从作为sdk的角度对代码进行细微调整。
/* *
*类名:AlipaySubmit
*功能:支付宝各接口请求提交类
*详细:构造支付宝各接口表单HTML文本,获取远程HTTP数据
*版本:3.3
*日期:2012-08-13
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
封装思路一
做为sdk,我去掉了demo中的AlipayConfig.java,因为不同的项目所使用到的商户号、密钥等都不相同,放在sdk中,不妥当。去掉AlipayConfig.java后,其他的代码要相应的调整一下。我们将配置以参数的形式,传递进去。这样子确保,我们能通过其他方式配置网银直连所需的商户信息。我采用了这种思路,很简单很傻瓜式的调整一下。
封装思路二
可以参考最新的支付宝sdk的封装方式,抽象一个AlipayClient出来,初始化的时候,把AlipayConfig.java的工作做了。我没有使用这种思路,但是感觉这是一种不错的思路。调整稍微大点。
坏味道清理
程序的世界可以说很大很大,咱们就像盲人摸象。坏味道清理是将代码中不规范不合理的地方进行调整。通过使用sonarqube对阿里的网银直连demo进行扫描,发现了56个坏味道,还有1个bug,1个漏洞。
坏味道1:命名空间不规范
支付宝
com.alipay.util.httpClient
com.alipay.util.httpclient
坏味道2:冗余写法,不够简洁
Replace the type specification in this constructor call with the diamond operator ("<>"). (sonar.java.source not set. Assuming 7 or greater.)
List<String> keys = new ArrayList<String>(sPara.keySet());
List<String> keys = new ArrayList<>(sPara.keySet());
坏味道3:多余的转化
String name = (String) keys.get(i);
Remove this unnecessary cast to "String".
坏味道4:直接返回即可
String strResult = response.getStringResult(charset);
return strResult;
Immediately return this expression instead of assigning it to the temporary variable "strResult".
坏味道5:这里不要线程安全
Replace the synchronized class "StringBuffer" by an unsynchronized one such as "StringBuilder".
StringBuffer result = new StringBuffer();
坏味道6:静态类不要初始化
Add a private constructor to hide the implicit public one.
坏味道7:一行代码搞定
修改前:
if (isSign && responseTxt.equals("true")) {
return true;
} else {
return false;
}
Replace this if-then-else statement by a single return statement.
修正后:
return isSign && responseTxt.equals("true");
坏味道8:局部变量命名不规范
input_charset
Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.
坏味道9:static final变量命名不规范
public static final String dtLong = "yyyyMMddHHmmss";
Rename this constant name to match the regular expression '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'.
坏味道10:参数不符合规范
Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.
private static boolean getSignVeryfy(Map<String, String> Params, String sign,String signType,String key,String charset)
坏味道11:局部变量命名不符合规范
String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "¬ify_id=" + notify_id;
Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.
坏味道12:多此一举
inputLine = in.readLine().toString();
"readLine" returns a string, there's no need to call "toString()".
坏味道13:一行代码搞定
Replace this if-then-else statement by a single return statement.
if(mysign.equals(sign)) {
Replace this if-then-else statement by a single return statement.
clumsy
return true;
}
else {
return false;
}
坏味道14:局部变量命名不规范
Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'.
private static String verifyResponse(String notify_id,String partner)
坏味道15:抛异常不规范
public static String query_timestamp(String partner,String charset) throws MalformedURLException, DocumentException, IOException
Remove the declaration of thrown exception 'java.net.MalformedURLException' which is a subclass of 'java.io.IOException'.
坏味道16:Use a StringBuilder instead
Use a StringBuilder instead.
StringBuilder prestr = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
prestr.append(key + "=" + value);
} else {
prestr.append(key + "=" + value + "&");
}
}
return prestr.toString();
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
坏味道17:不规范的变量命名
private static String DEFAULT_CHARSET = "GBK";
Rename this field "DEFAULT_CHARSET" to match the regular expression '^[a-z][a-zA-Z0-9]*$'.
private static final String DEFAULT_CHARSET = "GBK";
坏味道18:不规范的变量命名
/** 默认等待HttpConnectionManager返回连接超时(只有在达到最大连接数时起作用):1秒*/
private static final long defaultHttpConnectionManagerTimeout = 3 * 1000L;
Rename this constant name to match the regular expression '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'.
private static final long DEFAULT_HTTP_CONNECTION_MANAGER_TIMEOUT = 3 * 1000L;
坏味道19:不规范的变量命名
Iterate over the "entrySet" instead of the "keySet".
When only the keys from a map are needed in a loop, iterating the keySet makes sense. But when both the key and the value are needed, it's more efficient to iterate the entrySet, which will give access to both the key and value, instead.
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
for (Entry<String,String> entry : sArray.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
坏味道20:漏洞
Use a logger to log this exception.
try {
URL url = new URL(urlvalue);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
inputLine = in.readLine();
} catch (Exception e) {
e.printStackTrace();//漏洞
inputLine = "";
}
还有几个坏味道,我也不想修改了。
关注Yuema约吗公众号,回复”sq”,学习sonarqube代码质量检查工具使用与安装。
关注Yuema约吗公众号,回复”bankpay”,查看《支付宝网银直连SDK封装记上|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环》,了解代码结构的艺术。
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处
“ Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!
微服务再难,有人带路,轻松搞定
<!-- 支付宝 https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.6.0.ALL</version>
</dependency>
<!-- 支付宝网银直连SDK -->
<dependency>
<groupId>com.zhongdaiqi.alipay</groupId>
<artifactId>alipay-bankpay-sdk</artifactId>
<version>1.0.0</version>
</dependency>
封装sdk的方法、清理坏味道的方法、从支付宝网银直连demo中发现的坏味道清单,请看篇的姐妹篇《支付宝网银直连SDK封装记下|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环》
关注Yuema约吗公众号,回复”bankpay”,查看《支付宝网银直连SDK封装记下|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环》
关注Yuema约吗公众号,每天都有新收获
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处
宜信是国内P2P网贷领域的创始企业,拥有13年风控实战经验,宜信推出的星火金服新手红包为292元,预期年化8.5%,使用新手红包,综合预期年化近30%。做为一款新手体验产品,备受网贷圈的欢迎,也是一款爆品。通过邀请链接二维码可获得新手体验红包!
周期短,30天左右,可观的预期收益,新手注册后必选的一款网货产品。新手注册后,一定要记得使用新手红包,仅能使用一个。目前网贷圈最流行的选择是166红包+新手标(预期年化8.5%)。到期后,可直接提现,也可以续投。新手产品为体验产品,仅限新手体验。
抢标小秘籍:
网贷有风险 投资请谨慎
话说接支付宝的故事已经有几个回合了,30年河东,30年河西,之前的支付宝已经不再是之前的支付宝啦,从技术上来讲,变得越来越古怪了。接口都被当成产品,各种需要签约啦。今天的重点在于支付宝网银直连、转账到银行卡两个接口,故事的来来回回已经越约了技术层面,俨然成了一场非技术层面拉锯。读完本故事,您应该懂得怎么开通支付宝网银直连、转账到支付宝,故事写于2019年9月29日下午16点46分。如果支付宝变来变去,故事也可能成为历史传奇,不再有效。
一、捉摸不透的官方客服
向官方客服进行咨询的话,先是机器人伺候,再到人工客服,统一都是说网银直连下线了、转账到银行卡下线了,无在线签约入口。蹊跷的是,在线客服提供可至电xxxx咨询,联系业务经理云云。
二、线下业务经理的快速通道
原来被当成无解的支付宝网银直连&转账到银行卡,竟然在支付宝业务经理BD的线下邮件开通方式下,可以开通。这已经不再是技术领域的问题了。看接口路径中的pre-open,文档顶部标明了定向开放文档。这些接口不签约,统统是不生效了,参考文档开发,也调不通。能走线下就不要去走线上,感觉线下就像一个绿色通道,无所不能。
2.1 支付宝网银直连
网银直连的文档看起比较老旧,像是之前的支付宝对接文档。demo也不难,不过,不签约的话,照着demo做,也无法直接跳到网银。官方提供的demo是java jsp版本,有点小小的不适应。
依据官方demo,转了一份Spring MCV版本
"api") (value =
public String api(Model model, HttpServletRequest request) throws Exception {
// 支付类型
String payment_type = "1";
// 必填,不能修改
// 服务器异步通知页面路径
String notify_url = "https://i.zuime.com/create_direct_pay_by_user-JAVA-UTF-8/notify_url.jsp";
// 需http://格式的完整路径,不能加?id=123这类自定义参数
// 页面跳转同步通知页面路径
String return_url = "https://i.zuime.com/create_direct_pay_by_user-JAVA-UTF-8/return_url.jsp";
// 需http://格式的完整路径,不能加?id=123这类自定义参数,不能写成http://localhost/
// 商户订单号
String out_trade_no = new String(request.getParameter("WIDout_trade_no").getBytes("ISO-8859-1"), "UTF-8");
// 商户网站订单系统中唯一订单号,必填
// 订单名称
String subject = new String(request.getParameter("WIDsubject").getBytes("ISO-8859-1"), "UTF-8");
// 必填
// 付款金额
String total_fee = new String(request.getParameter("WIDtotal_fee").getBytes("ISO-8859-1"), "UTF-8");
// 必填
// 订单描述
String body = new String(request.getParameter("WIDbody").getBytes("ISO-8859-1"), "UTF-8");
// 默认支付方式
String paymethod = "bankPay";
// 必填
// 默认网银
String defaultbank = new String(request.getParameter("WIDdefaultbank").getBytes("ISO-8859-1"), "UTF-8");
// 必填,银行简码请参考接口技术文档
// 商品展示地址
String show_url = new String(request.getParameter("WIDshow_url").getBytes("ISO-8859-1"), "UTF-8");
// 需以http://开头的完整路径,例如:http://www.商户网址.com/myorder.html
// 防钓鱼时间戳
String anti_phishing_key = "";
// 若要使用请调用类文件submit中的query_timestamp函数
// 客户端的IP地址
String exter_invoke_ip = "";
// 非局域网的外网IP地址,如:221.0.0.1
//////////////////////////////////////////////////////////////////////////////////
// 把请求参数打包成数组
Map<String, String> sParaTemp = new HashMap<String, String>();
sParaTemp.put("service", "create_direct_pay_by_user");
sParaTemp.put("partner", AlipayConfig.partner);
sParaTemp.put("seller_email", AlipayConfig.seller_email);
sParaTemp.put("_input_charset", AlipayConfig.input_charset);
sParaTemp.put("payment_type", payment_type);
sParaTemp.put("notify_url", notify_url);
sParaTemp.put("return_url", return_url);
sParaTemp.put("out_trade_no", out_trade_no);
sParaTemp.put("subject", subject);
sParaTemp.put("total_fee", total_fee);
sParaTemp.put("body", body);
sParaTemp.put("paymethod", paymethod);
sParaTemp.put("defaultbank", defaultbank);
sParaTemp.put("show_url", show_url);
sParaTemp.put("anti_phishing_key", anti_phishing_key);
sParaTemp.put("exter_invoke_ip", exter_invoke_ip);
// 建立请求
String sHtmlText = AlipaySubmit.buildRequest(sParaTemp, "get", "确认");
model.addAttribute("page", sHtmlText);
return "pay/alipay/mapi/api";
重点参数在paymethod、defaultbank。
String paymethod = "bankPay";//网银直连
String defaultbank= "CMB";//银行简码,参考支付宝对接文档详细说明
PS:敲黑板画重点,没有签约的话,是无法跳过去的,这些参数都不会生效,依旧会跳到支付宝的支付界面,而不是网银。
2.2 转账到银行卡
支付宝转账到银行卡
https://docs.alipay.com/pre-open/api_pre/alipay.fund.trans.tobank.transfer
发起转账到银行卡后,支付宝会返回很详细的错误信息。
错误码 | 错误描述 | 解决方案 |
---|---|---|
INVALID_PARAMETER | 参数有误。 | 请根据入参说明检查请求参数合法性。 |
SYSTEM_ERROR | 系统繁忙 | 可能发生了网络或者系统异常,导致无法判定准确的转账结果。此时,商户不能直接当做转账成功或者失败处理,可以考虑采用相同的out_biz_no重发请求,或者通过调用“(alipay.fund.trans.order.query)”来查询该笔转账订单的最终状态。 |
EXCEED_LIMIT_SM_AMOUNT | 单笔额度超限 | 请根据接入文档检查amount字段 |
EXCEED_LIMIT_DM_AMOUNT | 日累计额度超限 | 请根据接入文档说明检查本日请求总金额+本次请求金额是否超限。 |
EXCEED_LIMIT_MM_AMOUNT | 月累计金额超限 | 请根据接入文档说明检查本月请求总金额+本次请求金额是否超限。 |
PAYCARD_UNABLE_PAYMENT | 付款账户余额支付功能不可用 | 请付款账户登录支付宝账户开启余额支付功能。 |
PAYER_STATUS_ERROR | 付款账号状态异常 | 请检查付款方是否进行了自助挂失,如果无,请联系支付宝客服检查用户状态是否正常。 |
PAYER_CERTIFY_CHECK_FAIL | 付款方人行认证受限 | 付款方请升级认证等级。 |
PAYER_STATUS_ERROR | 付款方用户状态不正常 | 请检查付款方是否进行了自助挂失,如果无,请联系支付宝客服检查用户状态是否正常。 |
PAYER_BALANCE_NOT_ENOUGH | 付款方余额不足 | 支付时间点付款方余额不足,请向付款账户余额充值后再原请求重试。 |
PAYER_USER_INFO_ERROR | 付款用户姓名或其它信息不一致 | 检查付款用户姓名payer_real_name与真实姓名一致性。 |
PAYMENT_INFO_INCONSISTENCY | 两次请求商户单号一样,但是参数不一致 | 如果想重试前一次的请求,请用原参数重试,如果重新发送,请更换单号。 |
CARD_BIN_ERROR | 收款人银行账号不正确 | 请确认收款人银行账号正确性,要求为借记卡卡号。 |
PAYEE_CARD_INFO_ERROR | 收款方卡信息错误 | 请联系收款方确认卡号与姓名一致性。 |
INST_PAY_UNABLE | 资金流出能力不具备 | 可能由于银行渠道在维护或无T0渠道,与联系支付宝客服确认。 |
PAYER_ACC_OCUPIED | 付款人登录账号存在多个重复账户,无法确认唯一 | 如果未传输payer_account_name,请传入payer_account_name; 如果传入了payer_account_name,则是由于登录账号对应的多个重复账户的真实姓名一致,请更换登录号。 |
MEMO_REQUIRED_IN_TRANSFER_ERROR | 根据监管部门的要求,单笔转账金额达到50000元时,需要填写付款理由 | 请填写remark或memo字段。 |
PERMIT_CHECK_PERM_IDENTITY_THEFT | 您的账户存在身份冒用风险,请进行身份核实解除限制 | 您的账户存在身份冒用风险,请进行身份核实解除限制 |
依据官方提示,状态可以通过alipay.fund.trans.order.query接口进行查询核实。而官方的接口描述,又给出了神奇的描述。
https://docs.open.alipay.com/api_28/alipay.fund.trans.order.query
* 转账单据状态。
SUCCESS:成功(配合"单笔转账到银行账户接口"产品使用时, 同一笔单据多次查询有可能从成功变成退票状态);
FAIL:失败(具体失败原因请参见error_code以及fail_reason返回值);
INIT:等待处理;
DEALING:处理中;
REFUND:退票(仅配合"单笔转账到银行账户接口"产品使用时会涉及, 具体退票原因请参见fail_reason返回值);
UNKNOWN:状态未知。
*/
private String status;
经典问题
SUCCESS:成功(配合"单笔转账到银行账户接口"产品使用时, 同一笔单据多次查询有可能从成功变成退票状态);
请告诉我,什么时候才能一个转账的状态能最终定下来
不能说多次查询,又从成功变成了退票
请告之你们的转账到银行卡状态,在什么时候才能明确的认定为成功状态,而不是有可能从成功状态变成退票状态
感觉接口死循环了。发起转账状态要查询,查询状态又可能从成功变成退款,很是尴尬的局面。支付宝客服一直未给一个明确的答复,一个下午都在等答复。
写在收尾处
对接蚂蚁金服支付宝的网银直连、转账到银行卡,不是一个技术问题,而商务签约问题,不签约,不给文档,不给文档没有下文。对于技术人来讲,收藏一份网银直连、转账到银行卡文档,是很有必要的。估计签名还没有签约,功能都可以提前开发好了。
让天底下没有难接的支付
关注公众号【Yuema约吗】回复alipay,获取支付网银直连、转账到银行卡技术文档
ps:不签约接口不生效,但是可以提前开发
“ Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!
微服务再难,有人带路,轻松搞定
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处
“ Yuema约吗?一起学技术,一起成长!他山之石,可以攻玉系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,可获取每日分享!小手点点,即可关注!
老是说微服务,微服务,有几个人在使用微服务,微服务真的好么?有必要使用么?怎么使用?怎么入门,怎么玩起来?今天先聊聊微服务,再原汁原味分享一位大神官方授权的小白带路篇微服务入门教程。
一、微服务的好 锁住构建规模
微服务是一种趋势?是的,随着系统越来越庞大,功能越来越多,“传统”的工程结构,会让今天的小伙伴,拖着10年前的老代码一起构建,有种心累的感觉。光构建一下,就很废时间,超过20分钟,是不是有种找PO哭的节奏,明明很简单的一个功能,明明一个简单的bug,却花了大量的时间等。有种前人挖坑,后人爬的节奏。微服务,可能破解了这个困境,让工程不再无止境的线性膨胀。
二、微服务好 防止业务拖累 防止代码污染
一个好端端的业务,运行稳定,每次发布部署新东西,都不相关,却要被影响一下,有种被拖累的不爽。微服务可能在防止不相关的业务拖累这块有所建树。同时,代码都分开,被污染到的可能性也就小了。pom包里新增加个东西,出现jar包冲突,是一件很不爽的事情,尤其在10年前一个老包,跟新包出现不可调和的冲突。
真的那么好么?当公司的业务规模达到一定程度,微服务是有所帮助的。但是,如果,那么就要好好想想了。
一、微服务不好 工程结构变得玄幻
当我打开一个支付集成的微服务项目,我已经找不到北,工程结构变得异常丰富,需要付出额外的学生成本。研发、部署都发生了变化。不过,不过,微服务的架子搭起来之后,也就按部就班,跟普通研发并无太大的变化。
二、微服务不好 学习维护成本高了
如果没有人带,微服务,真心不好玩。如果只是一个简单的功能,公司也没有成本走微服务,其实不玩微服务也好。玩微服务之后,就意味着,学习成本高了点。
好与不好,总是相伴相生,那对于小白来说,怎么掌握微服务呢?今天是拿到了官方授权,来Show一波小白教程,让天下没有再难的微服务。
https://github.com/zhongdaiqi/spring-cloud-example
关注公众号,查看教程更新
For Boss
不要盲目追随微服务,重点考虑一下当前业务规模,业务系统简单、又没几个访问量,又没有钱,就不要微服务了。简单的也不错。
For 同行
快点学微服务,用得起微服务的公司,都是达到一定境界的公司。
学海无涯 高人带路
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处