支付宝当面付集成继集|一顿猛操作 当面付二维码生成 自主收银台功能 验签回调 公钥密钥更新 AES配置统统就位妥当 支付宝扫码啦

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!当面付​再难,有人带路,轻松搞定
感觉当面付的集成差不多告一段落了,今天也花了比较多的时候将当面付接到外网测试环境,进行全方位联调​。这一天下来也算是比较​充实了。一直有各种小问题出现,一直在处理问题。从不同的角度来看这个次当面付的集成的话,也能得出很多不同的观点与​思路。从流水账的角度看这一天的话,也是很自然与​平凡的一天。
一到公司,我在群里提前打个招呼,联系财务更新密钥,本以为正式更新密钥可能会墨迹到10点,但是没有,​9点就去更新密钥了。这一路走过去的话,我到是很开心,想想今天还是很顺利的开局,一分钟也没有​落下。
第一次更新完密钥后,我特意取了个版本号v1​。预想的话,这一更新,这个商户号的当面付功能就应该能正常使用了,接口通,二维码出,支付宝能扫。结果,却依旧​。v2,v3,新生成密钥,拿正常商户的密钥设置,一切都像见鬼了一样。向支付宝的技术支持反馈问题,也是让我更新一下密钥,真的就是没有用​!于是,换个大招,不仅更新了RSA公钥私密钥,也更新了AES,同时把身份申请为IVS集成商,因为发生变化的因素多了,也搞不明白到底是哪个地方​发生的真实效果。支付宝功能也是很神秘滴!话说,之前签约支付宝当面付,提供产品冲突不让签约,直接在技术支持群里@支付宝机器人转人工,一波反馈,又悄悄的开通了​。更新完密钥,将商户号接通,算是一个大步。
调通后,还得将自主收银台(其实就是一个html页面)配置配置,有效时长​得调整对。因为很次构建编译时间比较长,走内网部署系统,加上那14分钟的代码扫描,整个过程得20分钟,避免消耗在20分钟的部署时长?有的时候真心避免不了​。人在江湖身不由已。
对于Memcache实例能否共用的问题,也是第一时间考虑,更多当面付的分享,且看​下回分解。​

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

支付宝当面付集成经验分享|RSA RSA2 AES alipay沙箱环境 正式环境 签名失效 DIY当面付收银台QrCode图片

 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.payalipay.mobile.qrcode.manage
以上是收集到的支付宝扫码实现接口,还有当面付接口,这里就再列表了。总之接口变得更加产品化(折磨人)。一不小心就会掉到坑里。从技术的角度来讲,基础接口都是一样的,支付宝人为增加了产品facade接口,有好有坏了。好的是,细分了使用场景,避免迷失在大接口中。​同时,也好进行授权控制。
今天的目标是实现支付宝二维码有效进长配置,从之前电脑网站支付相关的支付宝接口经验上来分析,使用alipay.trade.page.pay​可以实现有效时长控制。但是,老项目采用了alipay.mobile.qrcode.manage​接口,支付宝开放平台都搜索不到这个接口,可想而知,这是一个多老的接口。
抱着试试看的态度问了一下支付宝技术支持。
@蚂蚁金服商户技术支持 alipay.mobile.qrcode.manage 这个接口支持设置二维码过期时间不@蚂蚁金服商户技术支持 转人工不支持设置过期时间@蚂蚁金服商户技术支持 使用哪个版本的支持设置过期时间您这边使用二维码管理接口是需要实现什么功能?@蚂蚁金服商户技术支持 扫码支付设置有效时长这个接口可以设置支付超时时间的。@蚂蚁金服商户技术支持 请给一份详细的完整的文档给我 谢谢支付宝二维码管理接口(关注技术岛公众号,回复alipay获取支付宝二维码管理接口文档)
从对话可以看出,支付宝技术支持一开始是说不支持设置有效时长的​。差点就换成alipay.trade.page.pay了,想想要把老接口项目大倒腾一下,挺大的工作量的。还好,支付宝技术支持又答复说支持了,否则一场不可避免的新老接口大梳理无法避免。
关键参数
pay_timeout 支付超时 时间 String 支付超时时间,单位为分钟, 最小 5 分钟最大两天,默认 15 分钟。 可空 30 logo
此参数为 ext_info中成员,直接在原来的接口参数中进行适当的调整,即可以满足二维码扫码时长的控制了。

发表在 未分类 | 留下评论

腾讯云滑块验证码集成|阿里滑块验证码被破解 换个腾讯云验证码 云服务与黑产灰色产业链的爱恨情仇 附送腾讯云99元云主机下单入口

 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

前端代码参考

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><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;@Servicepublic class TencentCaptchaAgent {  /**   * 网关地址   */  @Value("${tencent.secretId}")  private String secretId;  @Value("${tencent.secretKey}")  private String secretKey;    /**   * 网关地址   */  @Value("${tencent.captcha.captchaAppId}")  private Long captchaAppId;  @Value("${tencent.captcha.appSecretKey}")  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 * */@Controller@RequestMapping("/tencent")public class TencentController {  @Autowired  private TencentCaptchaAgent tencentCaptchaAgent;  @RequestMapping(value = "captcha")  public String captcha(Model model) throws Exception {    return "tencent/captcha";  }  @RequestMapping(value = "verify")  @ResponseBody  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/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

支付宝网银直连SDK封装记下|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环

 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.httpClientcom.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_charsetRename 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 + "&notify_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, IOExceptionRemove 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 = "";        }

还有几个坏味道,我也不想修改了。

从56个坏味道来看,变量命名规范、线程安全、代码简洁度、性能都有涉及,可以说阿里支付宝提供的这个demo,将一堆坏味道传染给了千千万万程序员。其实,也不用太惊慌,使用sonarqube这个开源的工具进行代码质量检查,就能防坑于未然,即能享受支付宝的demo成果,也能做得更好。
网银直连的demo比较老,最近看阿里支付宝的sdk,还是很规范的。不过,网银直连还是得在坑里爬行。希望这两篇支付宝网银直连的经验分享,能让更多的小伙伴快速出坑,节省些时间~

关注Yuema约吗公众号,回复”sq”,学习sonarqube代码质量检查工具使用与安装。

关注Yuema约吗公众号,回复”bankpay”,查看《支付宝网银直连SDK封装记上|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环》,了解代码结构的艺术。

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

支付宝网银直连SDK封装记上|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!微服务再难,有人带路,轻松搞定

名词约定
网银直连,通过第三方支付平台(比如支付宝)直接跳到银行的网上银行界面,不显示第三方平台支付界面(比如支付宝)。支付宝也称相关接口为《纯网关接口-create_direct_pay_by_user(20170526).zip》。
正文
蚂蚁金服支付宝网银直连是最难接的支付了,难不再于技术,在于技术前端的商务关。在线客服不再签约不给开通,只能联系客户经理走线下邮件开通。体验相当不好。今天依据支付宝提供的DEMO,封装了一个支付宝网银直连SDK。之所以要自己封装,是因为现有的alipay-sdk-java 4.6.0.ALL压根不包括网银直连接口。
<!-- 支付宝 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里,不仅让现有代码清晰。而且新项目也可以方便的使用。不用COPY多处代码,只需要引入sdk并将agent类copy过来。也是很方便的使用体验。
未封装的图示感觉

未封装的话,代码被打散在项目的角落里,对于这种“外来”代码,怎么可以跟小清新无坏味道的代码混在一起呢?
封装后的图示感觉

网银直连所使用到的验签、创建网银直连支付宝订单等相关的类都统一打包在这个AlipayBankSDK中。可方便的应用到多个项目中。
刚才有说到我们项目代码经过Sonarqube洗礼,清理了绝大数坏味道,而第三方的代码打包在sdk里就不用去坏味道么?当然,也是要去的。风格不统一,命名不符合我们的规范。自己封装,就自己清理吧!

       封装sdk的方法、清理坏味道的方法、从支付宝网银直连demo中发现的坏味道清单,请看篇的姐妹篇《支付宝网银直连SDK封装记下|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环》

关注Yuema约吗公众号,回复”bankpay”,查看《支付宝网银直连SDK封装记下|强力去掉支付宝网银直连DEMO中56个坏味道 余下6个坏味道 Sonarqube无视阿里支付宝光环》

关注Yuema约吗公众号,每天都有新收获

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

宜信星火金服月标来袭 新手292元红包邀请 月标加新手红包综合预期年化近30% 体验国内大平台专业服务 13年风控护航 疯抢月标

宜信是国内P2P网贷领域的创始企业,拥有13年风控实战经验,宜信推出的星火金服新手红包为292元,预期年化8.5%,使用新手红包,综合预期年化近30%。做为一款新手体验产品,备受网贷圈的欢迎,也是一款爆品。通过邀请链接二维码可获得​新手体验红包!

周期短,30天左右,可观的预期收益,新手注册后​必选的一款网货产品。新手注册后,一定要记得使用新手红包,仅能使用一个​。目前网贷圈最流行的选择是166红包+新手标(预期年化8.5%)。到期后,可直接提现,也可以续投。新手产品为体验产品,仅限新手体验。

抢标​小秘籍:

  1. 月标每天有额度限制,注册后要及时抢
  2. 每天会补充额度,没有抢到不要灰心,次日关注
  3. 宜信大数据风控有防羊毛党机制,被系统检查到恶意刷新红包,可能会被系统限制,无法查看到月标​,长期标可正常投。羊毛党要三思而行​。
  4. 如果月查看不到,可以等系统补充,也不要太​沮丧。
  5. 额度提醒服务可以加微信13439975582​ 注明:宜信抢标

网贷有风险 投资请谨慎

发表在 未分类 | 留下评论

让天底下没有难接的支付|支付宝网银直连&转账到银行卡对接故事续集 支付对接不是一个单纯技术问题 网银直连转账到银行卡开通方式揭晓

话说接支付宝的故事已经有几个回合了,30年河东,30年河西,之前的支付宝已经不再是之前的支付宝啦,从技术上来讲,变得越来越古怪了。接口都被当成产品,各种需要签约啦。今天的重点在于支付宝网银直连、转账到银行卡两个接口,故事的来来回回已经越约了技术层面,俨然成了一场非技术层面拉锯。读完本故事,您应该懂得怎么开通支付宝网银直连、转账到支付宝,故事写于2019年9月29日下午16点46分。如果支付宝变来变去,故事也可能成为历史传奇,不再有效。

一、捉摸不透的官方客服

向官方客服进行咨询的话,先是机器人伺候,再到人工客服,统一都是说网银直连下线了、转账到银行卡下线了,无在线签约入口。蹊跷的是,在线客服提供可至电xxxx咨询,联系业务经理云云。

二、线下业务经理的快速通道

原来被当成无解的支付宝网银直连&转账到银行卡,竟然在支付宝业务经理BD的线下邮件开通方式下,可以开通。这已经不再是技术领域的问题了。看接口路径中的pre-open,文档顶部标明了定向开放文档。这些接口不签约,统统是不生效了,参考文档开发,也调不通。能走线下就不要去走线上,感觉线下就像一个绿色通道,无所不能。

2.1 支付宝网银直连

网银直连的文档看起比较老旧,像是之前的支付宝对接文档。demo也不难,不过,不签约的话,照着demo做,也无法直接跳到网银。官方提供的demo是java jsp版本,有点小小的不适应。

依据官方demo,转了一份Spring MCV版本

@RequestMapping(value = "api")  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:状态未知。   */  @ApiField("status")  private String status;

经典问题

SUCCESS:成功(配合"单笔转账到银行账户接口"产品使用时, 同一笔单据多次查询有可能从成功变成退票状态);请告诉我,什么时候才能一个转账的状态能最终定下来不能说多次查询,又从成功变成了退票请告之你们的转账到银行卡状态,在什么时候才能明确的认定为成功状态,而不是有可能从成功状态变成退票状态

感觉接口死循环了。发起转账状态要查询,查询状态又可能从成功变成退款,很是尴尬的局面。支付宝客服一直未给一个明确的答复,一个下午都在等答复。

写在收尾处

对接蚂蚁金服支付宝的网银直连、转账到银行卡,不是一个技术问题,而商务签约问题,不签约,不给文档,不给文档没有下文​。对于技术人来讲,收藏一份网银直连、转账到银行卡文档,是很有必要的。估计签名还没有签约,功能都可以提前​开发好了。

让天底下没有难接的支付

关注公众号【Yuema约吗】回复alipay,获取支付网银直连、转账到银行卡技术文档

ps:不签约接口不生效,但是可以提前开发

发表在 未分类 | 留下评论

让天底下没有再难的微服务 一份有码有图的微服务教程分享| 找出教程Bug,还有礼品相送,学海无涯大师带路 行江湖,品微服务之美

 Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!微服务再难,有人带路,轻松搞定

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论

小白也能玩微服务|站点巨人肩上 玩玩微服务! 微服务虽好,可要三思!带不动节奏,肿么可能?源代码,分析,高人带路,相信你不会不懂

 Yuema约吗?一起学技术,一起成长!他山之石,可以攻玉系列

程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,可获取每日分享!小手点点,即可关注!

老是说微服务,微服务,有几个人在使用微服务,微服务真的好么?有必要使用么?怎么使用?怎么入门,怎么玩起来?今天先聊聊微服务,再原汁原味分享一位大神官方授权的小白带路篇微服务入门教程。

一、微服务的好 锁住构建规模

微服务是一种趋势?是的,随着系统越来越庞大,功能越来越多,“传统”的工程结构,会让今天的小伙伴,拖着10年前的老代码一起构建,有种心累的感觉。光构建一下,就很废时间,超过20分钟,是不是有种找PO哭的节奏,明明很简单的一个功能,明明一个简单的bug,却花了大量的时间等。有种前人挖坑,后人爬的节奏。微服务,可能破解了这个困境,让工程不再无止境的线性膨胀。

二、微服务好 防止业务拖累 防止代码污染

一个好端端的业务,运行稳定,每次发布部署新东西,都不相关,却要被影响一下,有种被拖累的不爽。微服务可能在防止不相关的业务拖累这块有所建树。同时,代码都分开,被污染到的可能性也就小了。pom包里新增加个东西,出现jar包冲突,是一件很不爽的事情,尤其在10年前一个老包,跟新包出现不可调和的冲突。

真的那么好么?当公司的业务规模达到一定程度,微服务是有所帮助的。但是,如果,那么就要好好想想了。

一、微服务不好 工程结构变得玄幻

当我打开一个支付集成的微服务项目,我已经找不到北,工程结构变得异常丰富,需要付出额外的学生成本。研发、部署都发生了变化。不过,不过,微服务的架子搭起来之后,也就按部就班,跟普通研发并无太大的变化。

二、微服务不好 学习维护成本高了

如果没有人带,微服务,真心不好玩。如果只是一个简单的功能,公司也没有成本走微服务,其实不玩微服务也好。玩微服务之后,就意味着,学习成本高了点。

好与不好,总是相伴相生,那对于小白来说,怎么掌握微服务呢?今天是拿到了官方授权,来Show一波小白教程,让天下没有再难的微服务。

https://github.com/zhongdaiqi/spring-cloud-example

关注公众号,查看教程更新
  • 创建微服务项目(Spring Boot)
  • 安装Consul服务中心
  • 注册服务到服务中心(Consul)
  • 创建Consul客户端项目
  • 调用Consul服务(消费服务)
  • 创建网关项目
  • Spring Cloud Gateway注册到服务器中心(Consul)
  • 网关中加入熔断机制
  • Consul集群Server模式
  • Consul集群Server+Client模式
  • Consul集群加入网关服务

For Boss

不要盲目追随微服务,重点考虑一下当前业务规模,业务系统简单、又没几个访问量,又没有钱,就不要微服务了。简单的也不错。

For 同行

快点学微服务,用得起微服务的公司,都是达到一定境界的公司。

学海无涯 高人带路

作者:钟代麒

出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处

发表在 未分类 | 留下评论