关于支付:由某披萨品牌的联动事故聊聊支付流程的设计以及区块链解决方案

最近某披萨品牌与出名游戏的联动再次冲上热搜,只不过起因是许多用户在领取过程中遇到问题,呈现长时间无奈领取,反复领取等异常情况。本文想要从个别的秒杀及领取零碎的设计方面探讨一下故障呈现的可能起因,以及预防计划,最初聊一聊区块链在这方面的解决方案。因为作者并不理解本次故障具体的技术细节,所以文中可能蕴含大量的主观揣测,还请读者见谅。 流程复盘流动开始当日上午9点,披萨APP上开启流动产品的预约,不久,产品就已售罄。同时,社交网络上开始呈现“披萨APP解体“的用户反馈。次要反馈的问题有: 订单曾经领取,然而显示领取未实现一份订单领取胜利两次订单无奈领取(提醒网络异样)在大量用户反馈问题之后,披萨品牌示意会将订单的可领取工夫由半小时缩短到24小时。 问题剖析本次流动的次要步骤能够合成为:1. 创立订单;2.领取订单。 在创立订单这一步是没有呈现用户反馈问题的,只管同时抢购的用户数量很多,然而个别只须要正当地将用户进行分区,例如,假如产品限量10万份,能够创立10个并行的服务,每个服务上搁置事后生成的1万份产品编码,咱们这里用 token 来示意。而后应用哈希将用户分流到这10个服务上。每个服务就只须要保护1万 token 的汇合。 次要问题是在领取订单这一步,首先,只有取得 token 的用户才有资格进行领取。那么当服务器拿到用户发来的 token 的时候,须要做以下工作:1. 验证此token是否是实在的;2. 验证此token是否过期;3. 验证此token是否被应用过。 第一步是比较简单的,能够应用数字签名技术来验证,比方每个 token 都是有商家私钥签名的。第二步也很简略,token内的工夫戳和以后的工夫比照一下,就晓得有没有过期。真正简单的是第三步,咱们方才提到了,总共有 10 万份产品,那么至多就有 10 万个 token,且它们始终在更新(每时每刻都有订单领取胜利,领取胜利,token 生效)。这意味着这一步是无奈并行的,一旦并行,就有可能呈现双花 ———— 一个 token 被应用两次。 这一步里极大的串行工作的沉积,有可能是导致服务解体的次要起因。 另外,有用户示意同一笔订单领取了两次,这是因为大多数用户应用的是第三方领取服务,在“领取胜利->token生效“这个过程中,如果两头断开了,就可能导致领取两次。 解决方案首先咱们探讨一下在业务层面的解决方案,能够采纳预付订金的机制,比方,在流动开始前的一周里,用户能够预付订金参加抽签,流动开始当日颁布抽签后果,未中签的用户主动退还订金。将领取的工作分散开来。 如果不想扭转业务逻辑,咱们还能够在 token 上做文章,比方,将全副 token 分为 n 组,并在 token 上标注分组编号;同时启用 n 个验证服务,每个服务只负责保护对应分组的 token 汇合,相当于把串行工作分为了 n 个并行任务。 区块链的利用依据白皮书 "A Peer-to-Peer Electronic Cash System" 实现的区块链零碎实践上是能够有限扩容的,并且由其特有的工作量证实(POW)机制,可能超过分布式系统不可能三角(CAP),无效解决领取过程中的双花问题。 这里咱们尝试设计一个使用区块链零碎的解决方案。首先,商家发行10万个 token,每个 token 是一个 UTXO(未破费输入),其中蕴含的金额能够是 1 聪(区块链上最小的金额单位)。 另外,咱们须要第三方领取也反对这一区块链零碎,例如当初的数字人民币零碎。 在用户取得了 token,进行领取的时候,用户将发动一笔区块链交易,这笔交易的会耗费 token,并向商家领取对应的金额。当商家从区块链的“交易解决商”(俗称矿工)那里理解到这笔交易曾经有极大概率胜利 ———— 这个过程只实践上不到1秒钟,理论取决于区块链零碎中矿工的网络情况 ———— 就能够认为这个token曾经生效,且领取已胜利,这是一个原子操作。 ...

March 1, 2023 · 1 min · jiezi

关于支付:聊聊支付流程的设计与实现逻辑

老手打怵新手头疼的业务;一、业务背景通常在业务体系中,都会或多或少的波及到领取相干的性能;对于一些教训欠缺同学来说,最缓和的就是面对这类领取结算的逻辑,因为流程中的任何细节问题,都可能引发对账异样的状况; 谬误产生之后,再想去修复流程,破费的工夫老本又是昂扬的,还牵扯谬误数据的调平问题,最终很可能引发乱账算不清的后果,而后须要人工染指手动解决; 在领取场景中,岂但波及诸多的简单业务,结算规定,超长的流程,第三方对接,其中更是波及到诸多技术细节,比方:事务管理、异步解决、重试机制、加锁等;上面来剖析具体的细节逻辑。 二、领取业务1、流程拆解面对简单业务的时候,最根本的能力就是要懂得把流程拆成模块,做好各个模块治理,再思考如何连接起整个流程,从而造成解决问题的思路和教训; 如图是对交易场景常见的合成,大抵能够分为四个模块: 账面治理:对于开明领取性能的用户,必须清晰的治理资金信息;比方可用,解冻,账单等;交易流水:整个资金治理的流水记录,不局限于交易场景,还有充值,提现,退款等;领取对接:通常流程中的领取性能都是对接第三方领取平台来实现的,所以要做好申请和报文的记录;订单构造:比方在电商交易中,订单模型的治理,拆单策略等,领取的商品规格等;这里只是从一个惯例的交易流程中去剖析,理论的细节形容会远比图例简单,尽管业务细节各不相同,然而解决思路是大体相通的;再依据各个模块设计流程时序图,布局好节点之间的连接和合作; 2、流程时序通过时序图的设计,来剖析各个节点在连接合作时应该如何解决,在领取业务中,通常分为领取前、领取对接、领取后三个外围阶段: 领取前:在商品下单时,构建订单模型,依据拆单规定校验库存、商品状态等,而后进行账户资金解冻,生成交易流水,此时的状态都是待领取;领取对接:领取前业务模型初始化胜利之后,构建第三方领取对接申请,发动付款流程,并记录相应的申请动作和参数,期待领取后果的告诉;领取后:依据领取后果的胜利与否,执行相应的业务模型状态更新,如果领取胜利则交易记录、解冻的资金、订单构造与库存等都须要做一系列更新;实际上对业务有清晰的了解和拆分之后,再做好时序流程的设计,这样就曾经让一个简单的场景看起来简略许多了,之后就是设计各个节点的数据结构; 3、结构设计基于下面的业务场景剖析和拆解,以及流程时序图的出现,能够很容易输入一份根底维度的结构设计,下图能够作为参考: 账面治理:三个外围维度,账户金额,可用余额,解冻金额;交易记录:存储用户的交易动作,然而可能会产生多个交易明细,典型的场景就是购物车下单;交易明细:通常因为订单拆分,从而导致交易被拆分多条明细,进而将资金领取给不同商家;领取对接:申请第三方领取平台时,须要记录申请时参数,以及第三方回调告诉的报文;订单记录:在一笔订单中可能存在多个拆分的子单,拆分策略也很多,比方仓库,商家,品类等;订单明细:治理每笔子订单的信息,下单的商品、规格、买卖双方、单价、数量、金额等;即便单看下面的简略设计,都能感觉到领取业务的复杂性,更何况还会叠加红包或满减等优惠规定之后,其复杂程度可想而知; 当然如果有明确的开发标准,在简单版本中,所有开发必须输入业务的合成拆分思路,时序和结构设计,在对立评审之后再落地编码,这样即使是简单的业务也会有极大的质量保证。 三、关联业务下面单从领取的主逻辑去剖析流程,实际上波及到的业务远不止流程中提到的这些,以常见的电商场景为例,交易中还存在商品治理、库存治理、物流治理,领取对接还会波及优惠规定嵌入等等; 商品治理 商品主体:保护商品各个维度的信息,并提供各种规格选项,以及根底的定价阶梯,构建商品详情形容;仓储治理:订单拆单之后,须要依据商品编号去校验仓储信息,进行相应的库存解冻以及领取后的仓库发货;优惠券规定 优惠券主体:为了适配更多的业务场景,须要对优惠规定有诸多的设计,比方满减或折扣比例、按价格阶梯优惠、有效期限度等;发放规定:撑持日常的经营流动,用户生命周期的保护,以及渠道流量的转化,提供用户群营销的根底能力;这里简述的商品和优惠券业务,都是与领取流程有严密的分割,比方拆单后库存有余,须要移除该商品;优惠券在领取中的应用策略,以及退款时的解决形式等; 四、实际总结最初从技术实现的角度,总结一下领取流程中的一些关键问题: 业务模型:对业务有清晰的了解,并能拆分出外围的节点,设计出相应的流程时序和数据结构;事务管理:交易流程中罕用TCC事务机制,即Try(预处理)、Confirm(确认)、Cancel(勾销)模式;加锁与重试:领取实现后收回领取胜利的音讯,而后进行业务更新,通常须要对解决的订单号加锁,防止音讯重试机制引发数据问题;资金结算:波及金额的计算,天然要求不能呈现精度损失的问题,在一次交易中必须保障每笔资金能够通过对账核验;流程保护:流程自身是很难保障不呈现谬误的,须要在开发的时候,提供流程的可视化界面,并且反对手动保护的机制;很多简单的业务场景治理,都须要一个长期的迭代过程,然而前提须要牢牢把握住外围的逻辑;对业务的认知是一个由繁入简的过程,而业务的实现是一个由浅到深的过程,即剖析与了解,到落地实现,再到摸索与翻新。 五、参考源码编程文档:https://gitee.com/cicadasmile/butte-java-note利用仓库:https://gitee.com/cicadasmile/butte-flyer-parent

July 3, 2022 · 1 min · jiezi

关于支付:风带来的消息-狂人日记

老王经营着一家蹄花店,凌晨,像平常一样,老王关上店门 “老板,来一碗蹄花” 老王抬起头,空空的街道上一个人也没有,只有远处的小区保安在打盹 肯定是我听错了,最近压力好大,头发都掉了不少,得好好劳动了 “老板,听到没有啊,一碗蹄花” 老王暴怒,一拳打在桌子上,筷子哗啦啦散了一地 “别躲在角落里,要买货色你给我进去!” 无人回应,大风吹起,一张白纸飘到老王背后 商品:蹄花 x1客户: 二狗老王又怒,“先付钱!”,抄起一支笔在纸下边写上 价格:40元完了把纸一揉,使劲丢了进来,纸团随着风滚走了。一会儿,又滚了回来,老王捡起来关上一看,外面夹了两张 20 元纸币 “老板,快点儿吧,我快饿死了” 老王一点不焦急,拿起纸币举起来细看,嗯,有水印,是真的 起初,据说老王把门店大半改成了厨房,分心做外卖,登上了2019年外卖必吃榜

May 11, 2022 · 1 min · jiezi

关于支付:多国政府严厉打击勒索软件支付渠道以色列医院遭受重大勒索攻击|10月18日全球网络安全热点

平安资讯报告世界各国政府严厉打击勒索软件领取渠道来自30多个国家的高级官员示意,他们的政府将采取行动毁坏为勒索软件提供资金的非法加密货币领取渠道。 该联合声明是在白宫国家平安委员会本周促成的虚构反勒索软件倡导会议之后公布的,以应答继续的攻打,这些攻打揭示了寰球要害基础设施的重大破绽。 打击勒索软件个人滥用加密货币的致力将包含监管机构、金融情报部门和执法部门,对虚构资产的利用进行监管、监督、考察和采取行动。 “咱们还将寻求与虚构资产行业单干的形式,以增强与勒索软件相干的信息共享,”官员补充道。这一口头背地的国家将利用其金融机构和基础设施,独特抵挡针对国内合作伙伴要害基础设施的勒索软件流动。补充性致力还将包含通过执法单干毁坏勒索软件生态系统、进步网络弹性以避免攻打、解决勒索软件犯罪分子的避风港问题,以及通过内政接触激励其余国家应答在其领土内沉闷的勒索软件流动。 新闻来源: https://www.bleepingcomputer.... 钻研人员称要害基础设施安全性“糟糕透顶”新的钻研发现,工业控制系统(ICS)的“极差”平安状态正在将要害服务置于重大危险之中。 查看往年针对ColonialPipeline发动的勒索软件攻打造成的凌乱——导致美国局部地区的恐慌性购买和燃料短缺——即可理解事实世界中的网络中断事件可能引发什么,及其结果能够远远超出一家公司必须修复的侵害。 周五,CloudSEK的Sparsh Kulshrestha公布了一份新报告,依据最近针对工业、公用事业和制作指标的网络攻击,摸索ICS及其平安态势。该钻研侧重于可通过互联网取得的ICS。 “尽管攻击者领有丰盛的工具、工夫和资源,但其余威逼行为者次要依附互联网来抉择指标并辨认他们的破绽,”该团队指出。“尽管大多数ICS都有肯定水平的网络安全措施,但人为谬误是导致威逼行为者依然可能一次又一次地毁坏它们的次要起因之一。” 报告中援用的一些容许初始拜访的最常见问题,包含默认凭据、易受破绽利用的过期或未打补丁的软件、第三方导致的凭据透露、影子IT和源代码透露等。 新闻来源: https://www.zdnet.com/article... 安全漏洞威逼以色列医院成为勒索软件攻打的指标官员们示意,以色列一家医院周三受到勒索软件攻打,该州网络局称这是该国第一次针对医院的此类攻打。HillelYaffe医疗核心“目前正在应用代替零碎来医治患者”,它在一份申明中示意,称这次袭击“齐全出其不意”。 “除了非紧急的选择性手术外,医疗照常持续,”希勒尔·亚菲说。 以色列国家网络局局长YigalUnna称这是一次“重大”攻打。 “咱们正在致力解决它,看起来不太好,”他在美国国家平安委员会主办的勒索软件攻打虚构峰会上说。 依据Unna的说法,勒索软件网络攻击针对医院是“以色列第一次”,只管其余国家的医疗核心也经验过相似的攻打。 他的机构示意,“卫生部已更新医院作为预防措施”。 依据Unna7月份提供的数据,以色列五分之一的企业成为网络攻击的指标,这些攻打对五分之一的被攻打企业造成了侵害。 新闻来源: https://www.securityweek.com/... 勒索软件攻打了美国3个供水设施的SCADA零碎美国政府机构周四公布警报,正告水和废水行业的组织无关正在进行的网络攻击。该警报还形容了三个以前未报告的勒索软件攻打,这些攻打影响了供水设施的工业控制系统(ICS)。该警报由FBI、CISA、EPA和NSA公布。 新警报突出了与数据、勒索软件、网络分段、网络复杂性和系统维护相干的危险,并分享了无关威逼行为者用来毁坏IT和OT零碎和网络的策略、技术和程序(TTP)的信息。它还提供无关组织如何预防、检测和响应网络威逼的倡议。 该警报还提供了过来几年由歹意内部人员和内部威逼参与者进行的攻打的几个示例。这些例子包含往年产生的三起事件,之前没有公开。在每一次攻打中,监控和数据采集(SCADA)零碎都受到了影响。 在3月份产生的一起事件中,网络犯罪分子应用未知勒索软件攻打内华达州的一个供水设施。 另一起事件产生在7月,指标是缅因州的一家工厂。黑客部署了ZuCaNo勒索软件,该软件已进入废水SCADA计算机。 第三次新披露的攻打产生在8月。威逼行为者在加利福尼亚州一家水厂的零碎上部署了一种名为Ghost的勒索软件。 平安警报还形容了2019年和2020年产生的两起已知事件,其中一起波及往年早些时候被指控的内部人员。 据政府称,有超过150,000个公共供水系统为数百万美国人提供饮用水,废水处理设施解决大概340亿加仑的废水。美国将供水和废水零碎归类为国家要害性能,它们的毁坏或糜烂将“对平安、国家经济平安、国家公共衰弱或平安,或其任何组合产生减弱作用”。 新闻来源: https://www.securityweek.com/...

October 18, 2021 · 1 min · jiezi

关于支付:简单易用且优雅的跨境支付-PHP-SDK-扩展包

GlobalPay反对国际版领取的 PHP SDK,目前只反对支付宝国际版。因目前支付宝跨境在线领取服务只反对 app、wap、web 和报关这四种,本 SDK 提供了 app、wap、web 这三种跨境领取,详见国内支付宝官网文档 。 装置composer require pudongping/global-pay -vvv特点命名标准暗藏开发者不须要关注的细枝末节合乎 PSR 标准,能够不便的与各种 PHP 框架集成有良好的文档,蕴含各种示例办法以及官网返回后果。文档地址 : https://pudongping.github.io/...运行环境PHP >= 7.1.3Composer反对的领取办法支付宝电脑领取手机网站领取APP 领取method形容web电脑领取wap手机网站领取appAPP 领取反对的办法所有网关均反对以下办法find(array|string $order) 阐明: 查找订单接口 参数: $order 为 string 类型时,请传入零碎订单号,对应跨境支付宝中的 out_trade_no 参数; array 类型时,参数请参考支付宝境外订单单笔查问文档 。 返回: 查问胜利,返回 Illuminate\Support\Collection 实例,能够通过 $collection->toArray() 或者 $collection->all() 或者 $collection->get('field') 拜访服务器返回的数据。refund(array $order) 阐明: 退款接口 参数: $order 数组格局,退款参数请参考支付宝境外退款接口文档 。 返回: 退款胜利,返回 Illuminate\Support\Collection 实例,能够通过 $collection->toArray() 或者 $collection->all() 或者 $collection->get('field') 拜访服务器返回的数据。verify() 阐明: 验证服务器返回数据是否非法 返回: 验证胜利,返回 Illuminate\Support\Collection 实例,能够通过 $collection->toArray() 或者 $collection->all() 或者 $collection->get('field') 拜访服务器返回的数据。其余通用办法getExchangeRate() 阐明: 获取汇率。详见支付宝境外汇率查问接口 。 返回: 获取胜利,返回 Illuminate\Support\Collection 实例,能够通过 $collection->toArray() 或者 $collection->all() 或者 $collection->get('field') 拜访服务器返回的数据。 留神: 1、货币间的汇率会在北京工夫每日 9:00 到 11:00 间变动一次; 2、汇率每日获取下限为 100 次。 (可能须要思考通过缓存保留汇率,避免接口出现异常,因为本 SDK 没有做缓存解决)getHbFqCost(float $totalAmount, bool $isShowAll = false, bool $isSellerPercent = false) 阐明: 获取花呗分期计费状况 参数: $totalAmount 为分期的本金,$isShowAll 为是否显示每一期的还款数,$isSellerPercent 为 true 示意商家承当全副手续费,为 false 示意用户承当全副手续费。 返回: 获取胜利,返回 Illuminate\Support\Collection 实例,能够通过 $collection->toArray() 或者 $collection->all() 或者 $collection->get('field') 拜访服务器返回的数据。返回参数阐明 ...

October 10, 2021 · 6 min · jiezi

关于支付:关于聚合支付方案支付策略模式

公司最近在做聚合领取的性能,原来领取计划是不同的领取商户不同的业务解决,扩展性极差,且每新增一个业务商户都要独自开发一套领取接口,很显著这违反了OOP的思维。策略模式的组成部分有三局部 环境类(Context):用一个ConcreteStrategy对象来配置。保护一个对Strategy对象的援用。可定义一个接口来让Strategy拜访它的数据。形象策略类(Strategy):定义所有反对的算法的公共接口。 Context应用这个接口来调用某ConcreteStrategy定义的算法。 具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。领取形式的组成也有三局部 领取策略接口(PayStrategy):定义领取形式 具体领取形式(H5PayStrategy、WxPayStrategy、依据本人的):具体的领取算法 领取策略上下文(PayStrategyContent):治理所有领取形式的援用,并依据用户抉择援用对应的领取形式。代码实现(本案例是接的银联领取)   领取策略接口(PayStrategy) /***@program:traffic-resource*@description领取策略接口*@author:belive*@create:2020-12-2521:06**/publicinterfacePayStrategy{/***领取接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@param param*@returnStringjson字符串*/RestRes<String>consumeApply(Stringparam);/***查问领取后果接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@param param*@returnStringjson字符串*/RestRes<String>consumeApplyDetail(Stringparam);/***退款接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@param param*@returnStringjson字符串*/RestRes<String>refund(Stringparam);/***查问退款后果接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@param param*@returnStringjson字符串*/RestRes<String>refundQuery(Stringparam);/***敞开订单接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@param param*@returnStringjson字符串*/RestRes<String>depositApply(Stringparam);具体领取形式(MiniPayStrategy) 本案例就以扫码领取为例 /***@program:traffic-resource*@description*@author:belive*@create:2020-12-2521:18**/@Slf4j@ServicepublicclassQrCodePayStrategyimplementsPayStrategy{@Value("${unionPay.url.qrcode_get_qrcode}")privateStringqrcode_get_qrcode;@OverridepublicRestRes<String>consumeApply(Stringbody){RestRes<String>restRes=newRestRes<>();TransactionHttptransactionHttp=newTransactionHttp();Stringresult=transactionHttp.send("https://api-mop.chinaums.com/v1/netpay/bills/get-qrcode",body);JSONObjectresultObj=JSON.parseObject(result);if("SUCCESS".equals(resultObj.getString("errCode"))){restRes.setMessage("获取领取链接胜利");restRes.setCode(0);}else{restRes.setMessage("获取领取链接失败");restRes.setCode(1000);}restRes.setData(result);Return restRes;}@OverridepublicRestRes<String>consumeApplyDetail(Stringparam){Return null;}@OverridepublicRestRes<String>refund(Stringparam){Return null;}@OverridepublicRestRes<String>refundQuery(Stringparam){Return null;}@OverridepublicRestRes<String>depositApply(Stringparam){Return null;}}领取策略上下文(PayStrategyContent) /***@program:traffic-resource*@description领取策略上下文*@author:belive*@create:2020-12-2521:19**/@ComponentpublicclassPayStrategyContent{/**策略实例汇合*/ privateConcurrentHashMap<String,PayStrategy>strategyMap=newConcurrentHashMap<>(20);/***注入策略实例*如果应用的是结构器注入,可能会有多个参数注入进来。**如果应用的是field反射注入**如果应用的是setter办法注入,那么你将不能将属性设置为final。**@paramstrategyMap*留神注入类型要是Map根底类型*/@AutowiredpublicPayStrategyContent(Map<String,PayStrategy>strategyMap){ //清空集合数据 this.strategyMap.clear(); if(!CollectionUtils.isEmpty(strategyMap)){ strategyMap.forEach((beanName,payStrategy)->{ if(StringUtils.isEmpty(beanName)||payStrategy==null){ return; } this.strategyMap.put(beanName.toLowerCase(),payStrategy); }); } }/***抉择领取形式*小程序、H5、扫码**@parampaymentEnums**@returnRemoteResult*/publicRestRes<String>consumeApply(PaymentEnumspaymentEnums,Stringparam){if(CollectionUtils.isEmpty(strategyMap)){thrownewApiException(ErrorCodeEnum.UNION05);}returnthis.strategyMap.get(paymentEnums.getBeanName()).consumeApply(param);}/***查问领取后果接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@parampaymentEnums**@returnRemoteResult*/publicRestRes<String>consumeApplyDetail(PaymentEnumspaymentEnums,Stringparam){if(CollectionUtils.isEmpty(strategyMap)){thrownewApiException(ErrorCodeEnum.UNION05);}returnthis.strategyMap.get(paymentEnums.getBeanName()).consumeApplyDetail(param);}/***退款接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@parampaymentEnums**@returnRemoteResult*/publicRestRes<String>refund(PaymentEnumspaymentEnums,Stringparam){if(CollectionUtils.isEmpty(strategyMap)){thrownewApiException(ErrorCodeEnum.UNION05);}returnthis.strategyMap.get(paymentEnums.getBeanName()).refund(param);}/***查问退款后果接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@parampaymentEnums**@returnRemoteResult*/publicRestRes<String>refundQuery(PaymentEnumspaymentEnums,Stringparam){if(CollectionUtils.isEmpty(strategyMap)){thrownewApiException(ErrorCodeEnum.UNION05);}returnthis.strategyMap.get(paymentEnums.getBeanName()).refundQuery(param);}/***敞开订单接口*1.小程序领取*2.h5领取*3.扫码领取*4.app领取**@parampaymentEnums**@returnRemoteResult*/publicRestRes<String>depositApply(PaymentEnumspaymentEnums,Stringparam){if(CollectionUtils.isEmpty(strategyMap)){thrownewApiException(ErrorCodeEnum.UNION05);}returnthis.strategyMap.get(paymentEnums.getBeanName()).depositApply(param);} 领取形式枚举(PaymentEnums) /***@program:traffic-resource*领取形式枚举对象*code->领取形式别名*beanName->实例的名称*@author:belive*@create:2020-12-2521:00**/publicenumPaymentEnums{/**领取形式*/H5_PAY("H5_PAY",H5PayStrategy.class.getSimpleName()),MINI_PAY("MINI_PAY",MiniPayStrategy.class.getSimpleName()),QR_CODE_PAY("QR_CODE_PAY",QrCodePayStrategy.class.getSimpleName()),;/***枚举定义+形容*/privateStringcode;privateStringbeanName;PaymentEnums(Stringcode,StringbeanName){this.code=code;this.beanName=StringUtils.isNotEmpty(beanName)?beanName.toLowerCase():null;}/***依据code获取对应的枚举对象*/publicstaticPaymentEnumsgetEnum(Stringcode){PaymentEnums[]values=PaymentEnums.values();if(null!=code&&values.length>0){for(PaymentEnumsvalue:values){if(value.code.equals(code)){returnvalue;}}}returnnull;}/***该code在枚举列表code属性是否存在*/publicstaticbooleancontainsCode(Stringcode){PaymentEnumsanEnum=getEnum(code);returnanEnum!=null;}/***判断code与枚举中的code是否雷同*/publicstaticbooleanequals(Stringcode,PaymentEnumscalendarSourceEnum){returncalendarSourceEnum.code.equals(code);}publicStringgetCode(){returncode;}publicStringgetBeanName(){returnbeanName;}}自定义Bean加载(PayStrategyContentConfig) /***@program: traffic-resource*@description*@author: belive*@create: 2020-12-3023:07**/@Configurationpublic class PayStrategyContentConfig {@Bean public PayStrategyContent payStrategyContentInit() { H5PayStrategy h5PayStrategy = new H5PayStrategy(); MiniPayStrategy miniPayStrategy = new MiniPayStrategy(); QrCodePayStrategy qrCodePayStrategy = new QrCodePayStrategy(); HashMap<String, PayStrategy> strategyMap = new HashMap<>(); strategyMap.put(PaymentEnums.H5_PAY.getBeanName(), h5PayStrategy); strategyMap.put(PaymentEnums.MINI_PAY.getBeanName(), miniPayStrategy); strategyMap.put(PaymentEnums.QR_CODE_PAY.getBeanName(), qrCodePayStrategy); PayStrategyContent payStrategyContent = new PayStrategyContent(strategyMap); return payStrategyContent; }} 领取相干业务调用(代码片段) ...

January 11, 2021 · 1 min · jiezi

关于支付:一笔订单但是误付了两笔钱这种重复付款异常到底该如何解决原创

封面送给我狗哥~ Hello,大家好,我是楼下小黑哥~ 明天的文章咱们接着上次的话题,持续聊聊领取零碎异样解决办法。 在上篇文章中「领取掉单异样解决方案」,咱们次要提到的是领取过程中掉单的场景,用户明明付款胜利,银行卡都扣款了,然而订单却还显示待付款。 而在明天的文章中,咱们将聊到反复付款的异样,即同一笔订单,扣了用户两笔钱。 另外咱们还将会提到另外一种异样,用户扣款胜利,然而订单却领取失败的场景。 以上两种异样对于被扣款的用户来讲,应用体验极差,本人多付了钱,订单却还不胜利。所以如果不及时处理这两类异样,那就真的等着被投诉吧。 欢送关注我的公众号:程序通事,取得日常干货推送。如果您对我的专题内容感兴趣,也能够关注我的博客:studyidea.cn反复付款异样异样场景反复付款异样个别常见于网银领取,微信领取,支付宝等这类须要跳转到一个领取网关页(网银领取),或者跳转到钱包 APP(支付宝、微信),从而异步实现扣款的领取场景。 这种领取场景下,只能通过承受异步告诉能力晓得领取后果,咱们个别将其称为异步领取。 PS:有了异步领取,那么同步领取是什么?其实同步领取指的就是调用领取接口之后,就能够立即返回领取后果的,比方银行卡类快捷/代扣等领取就是同步领取。 当然也有一些奇葩的银行卡领取渠道,同步领取后果为受理胜利,只能承受异步告诉或者查问返回领取后果。 因为银行卡领取须要返回明确领取后果,对于这类渠道只能外部设计将异步转为同步,感兴趣能够看下之前历史文章: 架构设计|异步申请如何同步解决? 后盾领取流程如下: 图片来自之前的文章:银行卡领取原理 为什么会产生反复付款?次要起因其实跟上次外部掉单异样一样,跟业务表设计无关。 上次咱们提到,领取零碎次要表构造如下: 在这个表构造下,只有领取订单未胜利,商户就能够重复使用其外部同一订单号调用领取接口。 假如这样一个场景,用户在收银台领取时抉择招行进行网银领取,当他点击领取之后,商户零碎将会调用领取公司的网银接口。 这时领取零碎外部将会创立一笔领取单以及关联的渠道订单,并且调用招行零碎的接口。 而后用户的浏览器页面将会关上一个新页面,而后跳转到招行网站。 这时如果此时用户再次在收银台点击领取,将会再次调用领取零碎接口。这时候因为领取单已存在,所以仅仅会再创立一条渠道订单记录,并且调用招行零碎的接口。这时用户的浏览器将会再次关上一个招行的网站。 如果用户在两个招行网银页都实现领取,这时就产生了反复付款。 下面这种场景看起来有点傻,然而实在用户操作真的会产生。除了这种,博客园上的小伙伴还提到这么上面这种状况: 解决办法反复付款异样的次要的解决办法有两种,分为事先与预先。 事先次要的目是尽可能避免用户反复付款,次要解决办法为优化付款页面,尽可能做好提醒。 第一种优化形式,付款页面间接跳转到第三方/银行的网银页面,不要关上新的页面去跳转。 这种形式能够避免用户误关上两个网银付款的页面,从而导致反复付款。 然而这里会有一个问题,银行网银页面付款胜利之后,用户如何晓得其在商户侧订单状态也胜利了? 其实很简略,当初网银领取接口,个别都会有一个参数 return_url:同步跳转地址 。 只有在接口传入这个地址,当领取胜利之后,页面最终就会跳转到这个传入的地址,商户侧就能够在地址显示订单是否领取胜利。 下面咱们提到,用户有可能会应用浏览器回退性能,跳转到领取页,从而导致反复付款。 对于这种状况,咱们能够在其回退领取页时,首先向后盾查问这笔订单领取后果,如果已领取胜利,那就间接显示胜利页面。 第二种优化,对于这种从新关上一个页面跳转到银行网站,咱们能够在页面退出弹窗提醒,询问用户是否已领取实现。 比方下面这种解决形式,当用户点击确认实现充值,能够马上向后盾发动查问订单状态。 上面来聊聊预先的解决办法,其实解决办法很简略,发动外部退款,将多余领取的一笔反向退款回去。 领取零碎外部能够有个定时工作,定时扫描领取单下有多条胜利渠道订单的记录,而后抉择将反复领取渠道订单发动退款。 这种形式是领取公司零碎外部的操作,不须要商户侧发动指令。 订单生效异样异样场景这种场景个别常见于电商购物,秒杀等购物场景。当用户下单之后,页面将会开始倒计时,用户须要在有效期内领取胜利。 假如用户点击跳转到支付宝,然而其没有立即领取,而是停留了很久,在订单最初一秒工夫内实现了领取,然而这个时候订单早已因为工夫到期而被主动勾销。 这样就产生用户扣款曾经胜利,然而订单却是失败或敞开的场景的。 另外还有一种状况,用户在有效期内领取胜利,然而因为网络、外部利用等问题,领取后果的异步告诉过了很久才收到,这时外部订单的早因为工夫到期而被勾销。 解决办法第一种解决办法,上送有效期给领取渠道。 个别领取接口都会有一个领取有效期的字段,表明这笔领取最晚能够领取的工夫。如果超时未领取,这笔领取将会被敞开。 当然个别状况下,如果未上送,这个字段外部个别会有个默认的有效期,比方 3 天,这个工夫就比拟长了。 所以当调用领取接口时,能够将订单残余有效期传入领取接口。这样用户如果在超时工夫内未实现领取,领取将会失败。 第二种解决办法,外部发动退款。 这个解决办法仍然预先托底的解决办法,对于领取订单已敞开,然而领取却胜利的状况,发动外部退款,将钱退给用户。 外部能够有个定时工作,定时扫描领取订单已敞开然而领取却胜利的状况,而后发动退款指令。 最初最初用思维导图形式帮大家总结一下领取零碎可能会碰到的异样。 历史领取零碎相干文章收款神器!解读聚合收款码背地的原理|原创手机没网了,却还能领取,这是什么原理?|原创微微一扫,立即扣款,付款码背地的原理你不想晓得吗?|原创领取渠道路由零碎进化史从零开始设计对账零碎

October 27, 2020 · 1 min · jiezi

关于支付:微信支付踩坑合集微信小程序支付失败是什么原因持续更新

微信小程序开发的过程肯定会遇到各种问题,最让人辣手的就是领取问题,因为没有领取做商城相似的小程序就没有方法实现最要害的一步。那么领取失败到底什么起因呢?一下子收集了几个谬误相似,心愿对你有帮忙: No.1{err_code: "-1", err_desc: "调用领取JSAPI短少参数: total_fee", errMsg: "requestPayment:fail"}errMsg:"requestPayment:fail"err_code:"-1"err_desc:"调用领取JSAPI短少参数: total_fee" <?xml version="1.0" encoding="utf-8"?> <xml> <appid>xx</appid> <body><![CDATA[测试商品名称]]></body> <mch_id>xxx</mch_id> <nonce_str>krwub67ymmvm1nkjb9fitm6muqplqa45</nonce_str> <notify_url>xx</notify_url> <openid>xxx</openid> <out_trade_no>xx</out_trade_no> <spbill_create_ip>xxx</spbill_create_ip> <total_fee>1</total_fee> <trade_type>JSAPI</trade_type> <sign>xxx</sign> </xml> <?xml version="1.0" encoding="utf-8"?> <xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[wx64db6c2b70842ec1]]></appid> <mch_id><![CDATA[1504167501]]></mch_id> <nonce_str><![CDATA[Y34OjPAY9ReOCPDZ]]></nonce_str> <sign><![CDATA[039CF2A0B217BFCCAA01CCF4ECA1B32E]]></sign> <result_code><![CDATA[FAIL]]></result_code> <err_code><![CDATA[ORDERPAID]]></err_code> <err_code_des><![CDATA[该订单已领取]]></err_code_des> </xml> No.2如果微信小程序在进行微信领取时若提醒“商户号mch_id与appid不匹配”或者是提醒“无奈实现微信领取““签名谬误”等,请参考以下步骤查看; 筹备工作:查看以下步骤前,请先留神核查appid是否是同一个。具体核查形式: 1、微信小程序核查地址为:设置-开发设置 有显示具体 appid,如图: 2、我司后盾受权的 小程序 appid 地址,如图: 若核查无误的前提下,筹备以下三步的查看。 第一步:登录微信领取平台 https://pay.weixin.qq.com 查看对应的商户号与密钥及证书,并重置下密钥及重新安装下证书。 第二步:登录到对应的微信小程序账户下 https://mp.weixin.qq.com(留神此处是微信小程序账户不是公众号账户),查看是否开明微信领取。 若显示未开明,请参考教程,绑定第一步外面的商户号 第三步:登录我司小程序管理后盾,填写步骤一外面的商户号和密钥及证书(注:.P12证书) 以上步骤操作实现后,再从新去下单领取测试。 No.3如果你应用的是第三方开源我的项目,个别都是封装好了,依照要求去填写即可,出错的时候次要是证书的配置问题,譬如来客推商城领取的谬误解决方案: 起因有几点: 1、微信小程序没有与微信领取绑定 2、来客零碎后盾领取设置不正确,如图地位: 3、我的项目的目录权限请设置为 chmod 777 No.4ios的微信小程序领取失败解决办法:前两天做了个小程序,波及到了领取,后果在自测ios零碎时碰到了钉子 苹果手机领取失败的起因是:微信早在「微信小程序经营规定」第 14 条「小程序领取标准」中已明确示意:目前,在iOS 零碎下,微信小程序暂不得为虚构物品购买提供领取性能 这就导致,许多须要线上领取,并且无实物的领取时,每每碰壁 你须要做的就是,让后盾人员在生成预领取订单时,不要呈现,费用、领取、付费、续费、转账等与费用相干的字眼 如果改了这个还不行,那么就须要查看是否有代码中的谬误 我解决的计划就是去掉了无关费用的字眼 No.5微信小程序领取性能开发与踩坑经验总结 ...

September 4, 2020 · 3 min · jiezi

关于支付:支付微信h5

背景h5领取分两种1.浏览器2.app 浏览器里的h5,最终也会跳转到app。 而app里的h5,实质是公众号。在微信里叫公众号,支付宝叫服务窗。 这里次要讲微信h5。 外围原理最终目标是下单,所以先倒着看,间接看最初一步须要什么入参,而后倒推。 1.下单 须要用户id //外围就是获取用户id,后面的获取受权码只是为了平安的获取用户id 2.如何获取用户id? 须要先受权 所以流程是 1.受权 获取受权码 //不同的app,名字不同,微信叫code,其实目标就是获取受权码 2.用户id 依据公众号id和受权码,就能够获取用户id //外围业务逻辑 3.下单 入参:用户id //即依据用户id,下单 流程图 和微信打交道的外围步骤就三步 受权码会变 用户id不变 //惟一标识用户/消费者 总结微信 支付宝 银联,都差不多,大的流程基本上都是一样的,只有受权码和用户id的名字可能不一样。 非间接对接微信间接对接微信,就会须要用户id,须要用户id就须要获取受权码。 如果是非间接对接微信,而是对接第三方领取公司,就不须要1.获取用户id 2.获取受权码,因为第三方领取公司曾经封装好了。 弹起微信领取明码框 如果是间接对接微信,须要本人调用微信js弹出微信领取明码框。 如果是对接第三方公司,就是调用第三方公司js,而后由第三方公司去调用微信js和弹出微信领取明码框。 参考https://pay.weixin.qq.com/wik... https://developers.weixin.qq.... https://www.cnblogs.com/0201z...

July 29, 2020 · 1 min · jiezi

关于支付:支付微信h5

背景h5领取分两种1.浏览器2.app 浏览器里的h5,最终也会跳转到app。 而app里的h5,实质是公众号。在微信里叫公众号,支付宝叫服务窗。 这里次要讲微信h5。 外围原理最终目标是下单,所以先倒着看,间接看最初一步须要什么入参,而后倒推。 1.下单 须要用户id //外围就是获取用户id,后面的获取受权码只是为了平安的获取用户id 2.如何获取用户id? 须要先受权 所以流程是 1.受权 获取受权码 //不同的app,名字不同,微信叫code,其实目标就是获取受权码 2.用户id 依据公众号id和受权码,就能够获取用户id //外围业务逻辑 3.下单 入参:用户id //即依据用户id,下单 流程图 和微信打交道的外围步骤就三步 受权码会变 用户id不变 //惟一标识用户/消费者 总结微信 支付宝 银联,都差不多,大的流程基本上都是一样的,只有受权码和用户id的名字可能不一样。 非间接对接微信间接对接微信,就会须要用户id,须要用户id就须要获取受权码。 如果是非间接对接微信,而是对接第三方领取公司,就不须要1.获取用户id 2.获取受权码,因为第三方领取公司曾经封装好了。 弹起微信领取明码框 如果是间接对接微信,须要本人调用微信js弹出微信领取明码框。 如果是对接第三方公司,就是调用第三方公司js,而后由第三方公司去调用微信js和弹出微信领取明码框。 参考https://pay.weixin.qq.com/wik... https://developers.weixin.qq.... https://www.cnblogs.com/0201z...

July 29, 2020 · 1 min · jiezi

初步认识-Stripe-支付

前言这段时间在做支付相关的工作,由于业务主要是面向国外的用户,因而就接触了部分国外的支付支付相关的平台。接下来的内容主要是初步看了 Stripe 平台的文档所了解到的基本内容,后面会在使用的过程中不断地进行完善。 基本介绍和与其他支付平台的对比什么是StripeStripe - 基于API的便捷支付渠道 中对Stripe所提供的功能/产品给了较为不错的参考。 使用范围在我写这篇博客之时(2019-04-07),stripe已经支持32个国家的使用,而我在查找资料的时候,看到的基本都是说25个国家。支持的国家(https://stripe.com/global) 从列表中我们可以看到,其实中国大陆是还没有被支持的。为了能够进行收钱,我们需要一个在Stripe支持国家列表中的国家的银行账号。如果中国的企业单位希望使用的话,可以通过 Stripe 的 Atlas 创建一个美国的银行账号。更多关于 Atlas 可以看下 Atlas 的主页介绍,或者看下36氪的《为什么说想把业务做到海外去的公司,都需要关注 Stripe Atlas ?》。这里就不做展开介绍了。 和其他平台的简单对比有朋友使用过[PayPal]()和[Square]()。PayPal的开发难度更大,隐藏的价格远比想象中的还要高,表面上看,每笔钱进来,需要付给PayPal 2.99%的费用,但实际上确付了5%还要高。而且在使用新的PayPal账号的时候,发现新账号不接受信用卡支付。而Square相对于PayPal,开发难度要更简单。但在开发App端的支付时,发现Square的SDK仅仅支持较低版本的安卓系统,这就不得不放弃Square了。 不同的平台有不同的优缺点,总的来说。Stripe开发简单,支付的支付方式很多(支付宝和微信支付目前都已经支持了),适合创业团队使用。而Square对线下的支付有着很好的支持,如果有线下支付的需要的业务可以考虑使用Square。而PayPal更加适合大型企业的使用。 支付平台的选择不是这篇文章的重点,如果想要了解更多的对比细节,可以查看如下的参考文章。 点击这里,可以查看Stripe平台的价格。 参考 Stripe vs PayPal vs Braintree: How do these payment platforms compare?The Best Payment Platforms: Stripe vs Paypal vs Square vs BraintreeThe Best Payment Platform for Your Business: Paypal, Square, Stripe and Braintree ComparedStripe vs Square vs PayPal Here vs GoPayment vs Braintree vs Clover vs Propay & More!支付流程支付流程)。整个支付流程还是比较简单的,总的来说就是前端通过Stripe平台获取支付令牌,后端利用这个支付令牌进行请求Stripe进行实际支付。用户的信用卡敏感信息不会通过我们的后台,Stripe会帮我们处理复杂的PCI compliance。这里会涉及到企业的免责问题。 ...

November 2, 2019 · 4 min · jiezi

码付宝个人免签收款神器

码付宝个人免签即时到账收款平台。 无需公司资质免签约,即时到账,个人微信、支付宝账号即可收款,即开即用,高效稳定。 支持一带多,一个帐号管理多个商户。 可以在账号查看和操作商户订单,用于统一管理多个商户,无需重复注册。 官网码付宝官网: https://pay.shopxo.net/ 支持平台微信支付宝银联接口文档码付宝接入文档 码付宝, 个人收款利器, 实时回调通知, 再也不用担心收款困难了. 实现原理通过app监听微信, 支付宝的收款通知栏的通知, 来做到回调识别. 对于通知的金额, 为了防止订单冲突, 金额相同而无法识别, 我们会在订单金额的基础之上做随机立减, 目前的立减规则为, 每次递减0.01元, 如果递减后的金额被占用, 则再递减0.01元, 依次类推. 让然, 会有2分钟的超时订单释放规则, 超过2分钟未付款的订单金额, 会被释放, 释放后可以重复生成此金额订单. 码付宝原理示例订单金额为10元: 第一个人进来占用了 10元的金额, 支付中 第二个人进来则会分配 9.99 元, 支付成功 第三个人在第二个人支付成功后下单分配 9.99 元 如果第一个人的订单因为超过2分钟没有支付, 则自动释放掉 第四个人在2分钟后下单, 则分配 10元

July 26, 2019 · 1 min · jiezi

马蜂窝支付中心架构演进

为了更好地支持交易业务的快速发展,马蜂窝支付中心从最初只支持基础支付和退款的「刀耕火种」阶段,经历了架构调整的「刮骨疗伤」阶段,完成了到实现综合产品平台形态的「沉淀蓄力」阶段的演进。 目前,马蜂窝支付中心集成了包括基础订单、收银台、路由管理、支付通道、清算核对、报表统计等多种能力,为马蜂窝度假(平台、定制)、交通(机票、火车票、用车)、酒店(开放平台、代理商)等近 20 条业务线提供服务。本文将围绕支付中心整体演变过程中不同阶段的核心部分进行简要介绍。 一、支付中心 1.0初期为快速响应业务的支付、退款以及一些基础需求,支付中心主要负责接入支付通道(支付宝、微信、连连等),由各业务线分别实现收银台,然后调用支付中心进行支付。业务系统、支付中心和第三方通道的交互流程图如下: 各系统交互流程为: 业务线将订单信息封装后请求到支付中心支付中心对订单信息简要处理后增加支付信息请求到第三方支付通道第三方支付通道将支付结果异步回调到支付中心支付中心将第三方响应的数据简易处理后同步通知到各业务系统业务系统进行逻辑处理、用户通知及页面跳转等业务发展初期,业务量较小,交易场景也比较单一,这样的设计可以快速响应业务需求,实现功能。但当业务复杂性不断提高,接入的业务也越来越多时,该架构就显得力不从心了。各业务线需要重复开发一些功能,并且支付中心不具备整体管控能力,开发维护成本越来越大。主要的问题包括: 维护成本高:各业务线需单独维护收银台,调用支付系统完成支付,需分别保证幂等、安全等问题容灾能力差:所有功能集中在一个大模块里,某个功能出问题,直接影响全部结构不合理:架构单一,不能满足复杂业务场景系统职责乱:收银台维护了收款方式及部分业务路由,缺乏统一的管控为了兼顾对快速发展中的业务的需求响应和系统的高可用性,保证线上服务的质量,我们快速进行了架构调整,开始了向支付中心 2.0 的演进。 二、支付中心 2.02.0 架构将各业务的公共交易、支付、财务等沉淀到支付中心,并主要解决了以下三个主要问题: 建立基础订单、支付、财务统一体系,抽象和封装公共处理逻辑,形成统一的基础服务,降低业务的接入成本及重复研发成本;构建安全、稳定、可扩展的系统,为业务的快速发展和创新需求提供基础支撑,解决业务「快」和支付「稳」之间的矛盾;沉淀核心交易数据,同时为用户、商家、财务提供大数据支撑。2.1 核心能力支付中心 2.0 是整个交易系统快速发展的重要时段。在此过程中,不仅要进行架构的升级,还要保证服务的稳定。 目前支付中心对业务提供的主要能力包括: 平台支付:用户可以使用微信、支付宝等第三方平台来完成支付快捷支付:用户提供银行卡信息,进行便捷支付协议支付:用户完成授权后,可以在不打断用户体验的场景下进行便捷支付信用支付:用户可以选择花呗等分期产品进行透支支付境外支付:用户可以选择境外支付通道完成境外产品的购买线下支付:用户可以选择 ToB 通道完成特定场景的支付针对马蜂窝业务的特点,目前支持的核心交易场景包括: 支付和退款:适用于普通商品的购买及退款拆分支付:适用于限额或金额较大场景合单支付:适用于保险等分账到不同收款账号的场景2.2 架构设计演进过程中,首先是对相对独立,同时作为统一体系基础的网关进行模块化。支付网关对外抽象出支付、退款、查询这些标准请求,然后在网关基础上逐步梳理各支付通道,并逐步抽取出基础订单模块,解耦业务功能与支付功能,同时可支持复杂的业务场景。目前的系统功能整体架构如下: 如图所示,从架构上主要分为三个层次: 产品层:组合核心层提供的支付能力,对终端用户提供收银台、对运营财务人员提供运营财务系统核心层:支付中心核心模块,包括基础订单、支付路由、支付通道等支撑层:用来支撑整个系统的基础设施,包括监控报警、日志、消息队列等2.2.1 产品层产品层主要包含消费者可见的收银台、支付管理后台和财务核算、对账的财会系统。本文重点介绍收银台的设计思路。 收银台收银台包含 H5 收银台和 PC 收银台两部分: 移动端: PC端: 如上图所示,收银台主要由三部分组成:订单基本信息(含订单号及支付金额)、订单详情(含日期信息、商品信息及基础信息)、支付方式(平台支付、信用支付等)。 由于收银台是整个支付中心面向用户的唯一入口,用户体验及安全性至关重要。为同时支持业务个性化和用户的一致性体验,收银台主要是通过定制化和配置化的方式实现。对业务同学来讲接入也非常简单,仅需通过订单号跳转至收银台页面,后续流程均由支付中心完成。  用户下单后到达收银台页面,收银台通过订单所属业务线、支付金额、是否合单等信息,展示可用的支付通道。同时风控系统会从商品、订单、用户行为等维度进行监控,屏蔽高风险的支付渠道。支付渠道出现故障时可在收银台暂停展示。 (1)定制化 为支持统一收银台下各业务线不同模式、不同展示的特性,使用工厂类继承的模式实现各业务数据及展示样式。收银台主要属性分为展示模块和通道路由,其中重复及默认功能的模块由抽象类用模板的方式实现,子类使用默认方法或者重写父类方法即可达到自定义的实现。收银台展示实现类已经实现了一套默认的收银台,其中包含大多数必须的组件(如倒计时,头部定制,订单详情等)。一般情况下,各个业务线仅需简单添加特定的实现类,即可生成一个清晰又丰富的页面(2)配置化 收银台的配置化主要根据各业务的属性(业务类型、品类等)对后续操作做一定的流程处理配置化,比如: 基于后端路由对收银台展示层做不同的处理,用户看到的可支持的通道列表(微信、支付宝等),以及排序置顶打标记等满足不同场景、不同业务在同一种支付方式下收款到不同的收款账号根据场景不同,走不同的结算方式,以及结算渠道等2.2.2 核心层支付中心中的核心模块,包括基础订单、支付路由、支付通道等。 基础订单基础订单系统是连接交易业务线、支付中心和结算系统的桥梁,实现了业务和支付结算解耦。主要涵盖了业务创建订单、关单、支付、退款、回调通知等 API 模块。基础数据支持普通支付、合单支付、拆分支付、保险支付等多种场景的支付功能,各个系统的交互流程如下: 目前基础订单系统可支持如下两种特殊场景: (1)一订单 VS 多商品 创建一个基础订单可以包含 N 个商品(商品信息包含商品名称、商品 ID、单价、数量、折扣等,订单信息包含用户 UID、手机号、支付金额、订单折扣等汇总基础信息),N 个商品对应 M 个业务子订单 (M≦N),所有业务子订单的业务类型若一样则为普通模式,否则为搭售模式;每个业务订单对应一个对账单元(支付成功后会将支付信息同步给对账系统),一订单 VS 多商品的创单模式基本支持目前所有场景,包括未来可能的购物车模式。 (2)一订单 VS 多支付单 ...

July 5, 2019 · 1 min · jiezi

公众号开发微信公众平台配置

很久就想写一下开发微信公众号的所有配置,以及需要注意的坑点。最近项目提测,配合测试调试的空暇做个记录 微信端配置1、公众平台配置1、长期运营者和开发者配置绑定长期运营者:用于配置ip白名单和公众号后台登录Note:进入人员设置 Note:绑定运营者根据提示还需要关注名为公众号助手的公众号,不截图了 绑定开发者:用于开发者工具测试网站接口 2、基本配置Note:基本配置中,如图上半部分直接设置并保存在代码的配置文件中,下半部分需要在服务器配置好/wechat接口,并正确响应微信请求,注意此接口需要使用any方式 3、网页授权、JS安全域名、业务域名配置Note:两个入口可以进入配置 Note:这里的这个文件,前后端域名都需要可以直接访问,同一公众号三个需要配置的地方文件一模一样,不同公众号文件名和内容均不一样(vue不能放在/static下面) 2、商户号配置1、要开通微信支付功能,必须要自己申请商户号并与公众号进行绑定,这个流程根据微信提示操作即可(开通商户号,提交资料之后需要等待微信审核) Note:商户号登录建议用qq浏览器,总之用qq浏览器就对了,如图在画框的地方进行安装 2、安装完证书,就到api安全处获取后端调用退款的证书文件,包括设置密钥(这个页面因为要校验证书等,所以会巨卡,得有耐心) 3、设置支付授权目录,注意:授权目录我配置了前端的url,后端处理支付的接口必须要配置到最后一个"/",(比如,后端接口www.XX.com/wechat/pay/notice处理支付通知,那这个地方就要填写:www.XX.com/wechat/pay/,而且在后端处理退款回调通知的url最好也在/pay/下面,比如www.XX.com/wechat/pay/refund,这样在商户号这里就只需要配置一条就好了) 后端配置公众号1、公众号基础配置note:结合公众号后台的参数,写入配置 Note:/wechat接口逻辑处理 2、欢迎语,配置了根据公众号后台输入内容自动回复的内容 3、微信页授权回调 4、模拟用户,为了能在本地开发,于是配置了模拟用户,只需要在中间件中写入session就行了 5、再贴一个我单独配置的菜单吧,我把它写在配置文件里头,有artisan命令直接调用菜单接口就可以实现自定义菜单Note:还有一个更方便的设置菜单的方法,就是用微信的在线测试接口,聪明如我~_~ 支付1、支付配置 Note:支付相关的具体实现就不贴出来了,网上一大堆模板~

July 4, 2019 · 1 min · jiezi

开发了一个微信群付费进群功能

准备本案例使用的支付接口是PayJs微信支付商户,没有商户号可以自己申请:https://payjs.cn/ref/DNXBJD 演示 1分钱体验 流程1、写出进群界面2、对接PayJs的JSAPI支付接口3、支付成功后跳转进群二维码 代码jiaqun-login.php这个代码主要是授权登录,获取用户的openid <!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="format-detection" content="telephone=no"> <link rel="stylesheet" href="https://weui.io/weui.css"/> <link rel="stylesheet" href="https://weui.io/example.css"/> <title>正在登录</title></head><body></body></html><?phpheader("Content-type:text/html;charset=utf8");//发送请求$url = 'https://payjs.cn/api/openid?callback_url=你的域名/你的支付目录/jiaqun-getopenid.php';function post($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); $rst = curl_exec($ch); curl_close($ch); return $rst;}//发送$result = post($url);//返回接口的数据echo "<p style=\"display:none;\">".$result."</p>";?>jiaqun-getopenid.php这个主要是把获取到的openid传到支付订单页面 <?phpheader("Content-type:text/html;charset=utf8");$openid = $_GET["openid"];//浏览器跳转echo "<script>location.href=\"你的域名/你的支付目录/jiaqun-pay.php?openid=$openid\";</script>";?><title>请稍候</title>jiaqun-pay.php这个主要是构建订单发起请求参数,创建支付订单 <?php//引入配置文件require_once("config.php");//获得OPENID$openid = $_GET["openid"];//定义金额$total_fee = 1;$length = strlen($total_fee);if ($length == 1) { $money = "0.0".$total_fee;}else if ($length == 2) { $money = "0.".substr($total_fee, 0,1).substr($total_fee, 1,1);}else if ($length == 3) { $money = substr($total_fee, 0,1).".".substr($total_fee, 1,1).substr($total_fee, 2,3);}else if ($length == 4) { $money = substr($total_fee, 0,1).substr($total_fee, 1,1).".".substr($total_fee, 2,3).substr($total_fee, 4,4);}else if ($length == 5) { $money = substr($total_fee, 0,1).substr($total_fee, 1,1).substr($total_fee, 2,1).".".substr($total_fee, 3,2);}// 构造订单参数$data = [ 'mchid' => $mchid, 'body' => '付费进群', 'total_fee' => $total_fee, 'openid' => $openid, 'out_trade_no' => 'likeyunkeji.' . time(),];// 添加数据签名$data['sign'] = sign($data, $key);//发送请求$url = 'https://payjs.cn/api/jsapi?' . http_build_query($data);function post($data, $url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); $rst = curl_exec($ch); curl_close($ch); return $rst;}//发送$result = post($data, $url);//返回接口的数据$results = json_decode($result);//返回字符串$appId = $results->jsapi->appId;$timeStamp = $results->jsapi->timeStamp;$nonceStr = $results->jsapi->nonceStr;$package = $results->jsapi->package;$signType = $results->jsapi->signType;$paySign = $results->jsapi->paySign;// 获取签名function sign($data, $key){ array_filter($data); ksort($data); return strtoupper(md5(urldecode(http_build_query($data) . '&key=' . $key)));}?><html><head> <title>群聊邀请</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="email=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <link href="https://cdn.bootcss.com/weui/1.1.2/style/weui.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/zepto/1.2.0/zepto.min.js"></script> <link rel="stylesheet" type="text/css" href="css/style.css"></head><body> <!-- 顶部区域 --> <div id="logo_con"> <div class="logoimg"><img src="css/qunlogo.png"/></div> <p class="qunname">里客云科技</p> <p class="qunrenshu">100人</p> </div> <!-- 空白部分 --> <p id="yqjrql">TANKING邀请你加入群聊</p> <!-- 支付按钮 --> <a href="javascript:;" id="jrql">加入群聊</a> <!-- 进群说明 --> <p id="jqsm">1. 该群聊人数较多,为减少新消息给你带来的打扰,建议谨慎加入。 <p id="jqsm">2. 你需要实名验证后才能接受邀请,可绑定银行卡进行验证。</p></body><script> if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); } else if (document.attachEvent) { document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } } function onBridgeReady() { WeixinJSBridge.call('hideOptionMenu'); } $('#jrql').on('click', function () { WeixinJSBridge.invoke( //构造发起支付参数,请在接口服务端生成 'getBrandWCPayRequest', { "appId": "<?php echo $appId ?>", "timeStamp": "<?php echo $timeStamp ?>", "nonceStr": "<?php echo $nonceStr ?>", "package": "<?php echo $package ?>", "signType": "<?php echo $signType ?>", "paySign": "<?php echo $paySign ?>" }, function (res) { if (res.err_msg == "get_brand_wcpay_request:ok") { // 支付成功后跳转页面 location.href="https://www.liketube.cn/payjs/css/wxqunqrcode.jpg"; } } ); });</script></html>config.php支付接口的一些参数 ...

June 29, 2019 · 3 min · jiezi

我对支付平台架构设计的一些思考

微信公众号「后端进阶」,专注后端技术分享:Java、Golang、WEB框架、分布式中间件、服务治理等等。 老司机倾囊相授,带你一路进阶,来不及解释了快上车!我在前一家公司的第一个任务是开发统一支付平台,由于公司的业务需求,需要接入多个第三方支付,之前公司的支付都是散落在各个项目中,及其不利于支付的管理,于是聚合三方支付,统一支付平台的任务就落在我手上,可以说是完全从 0 开始设计,经过一翻实战总结,我得出了一些架构设计上的思考,之前就一直很想把自己的架构设计思路写出来,但一直没动手,前几天在技术群里有人问到相关问题,我觉得有必要把它写出来,以帮助到更多需要开发支付平台的开发人员。 组件模式由于公司业务在很多地区都有,需要提供多种支付途径,以满足业务的发展,所以设计的支付平台需要接入多种第三方支付渠道,如:微信支付、支付宝支付、PayPal、IPayLinks 等等,我们都知道,每个第三方支付,都有自己一套对外 API,官方都有一套 SDK 来实现这些 API,我们应该如何组织这些 API 呢? 由于第三方支付渠道会随着业务的发展变动,所以组织这些 SDK 就需要在不影响支付平台整体架构的前提下可灵活插拔,这里我使用了组件的思想,将支付 API 拆分成各种组件支付组件、退款组件、订单组件、账单组件等等,那么这样就可以当引入一个第三方支付 SDK 时,可灵活在组件上面添加需要的 API,架构设计如下: 通过 Builder 模式根据请求参数构建对应的组件对象,将组件与外部分离,隐藏组件构建的实现。组件模式 + Builder 模式使得支付平台具备了高扩展性。 多账户体系在接入各种第三方支付平台,我们当时又遇到一个账户的问题,原因是公司当时的小程序与 APP 使用的是不同的微信账号,因此会出现微信支付会对应到多个账户的问题,而我当时设计支付平台时,没有考虑到这个问题,当时第三方支付只对应了一个账户,而且不同的第三方支付的账户之间相互独立且不统一。 于是我引入了多账户体系,多账户体系最重要的一个核心概念是以账户为粒度,接入多个第三方支付,统一账户的参数,构建了统一的支付账户体系,支付平台无需关心不同支付之间的账户差异以及第三方支付是否有多少个账户。 此时我在支付平台架构图加上账户层: 前端只需要传递 accountId,支付平台就可以根据 accountId 查询出对应的支付账户,然后通过 Builder 模式构建支付账户对应的组件对象,完全屏蔽不同支付之间的差异,在多账户体系里面,可以支持无限多个支付账户,完全满足了公司业务的发展需求。 统一回调与异步分发处理做过支付开发的同学都知道,目前的第三方支付都有一个特点,就是支付/退款成功后,会有一个支付/退款回调的功能,目的是为了让商户平台自行校验该笔订单是否合法,比如:防止在支付时,客户端恶意篡改金额等参数,那么此时支付成功后,订单会处于支付中状态,需要等待第三方支付的回调,如果此时收到了回调,在校验时发现订单的金额与支付的金额不对,然后将订单改成支付失败,以防止资金损失。回调的思想是只要保证最终的一致性,所以我们调起支付时,并不需要在此时校验参数的正确性,只需要在回调时校验即可。 讲完了回调的目的,那么我们如何来设计支付平台的回调呢? 由于支付平台接入了多个第三方支付,如果此时每个第三方支付设置一个回调地址,那么将会出现多个回调地址,由于回调的 API 必须是暴露出去才能接受第三方的回调请求,所以就会有安全问题,我们必须在 API 外层设置安全过滤,不然很容易出现一些非法暴力访问,所以我们需要统一回调 API,统一做安全校验,之后再进行一层分发。 分发的机制我这里建议用 RocketMQ 来处理,可能有人会问,如果用 RocketMQ 来做分发处理,此时怎么实时返回校验结果到第三方支付呢?这个问题也是我当时一直头疼的问题,以下是我对回调设计的一些思考: 公司的系统是基于 SpringCloud 微服务架构,微服务之间通过 HTTP 通信,当时有很多个微服务接入了我的支付平台,如果用 HTTP 作分发,可以保证消息返回的实时性,但也会出现一个问题,由于网络不稳定,就会出现请求失败或超时的问题,接口的稳定性得不到保障。由于第三方支付如果收到 false 响应,就在接下来一段时间内再次发起回调请求,这么做的目的是为了保证回调的成功率,对于第三方支付来说,这没毛病,但对于商户支付平台来说,也许就是一个比较坑爹的设计,你想一下,假设有一笔订单在支付时恶意篡改了金额,回调校验失败,返回 false 到第三方支付,此时第三方支付会再重复发送回调,无论发送多少次回调,都会校验失败,这就额外增加了不必要的交互,当然这里也可以用幂等作处理,以下是微信支付回调的应用场景说明: 基于以上两点思考,我认为返回 false 到第三方支付是没必要的,为了系统的健壮性,我采用了消息队列来做异步分发,支付平台收到回调请求后直接返回 true,这时你可能会提出一个疑问,如果此时校验失败了,但此时返回 true,会不会出现问题?首先,校验失败情况,订单必定是处于支付失败的状态,此时返回 true 目的是为了减少与第三方支付不必要的远程交互。 ...

June 6, 2019 · 1 min · jiezi

支付系统设计实现1支付与退款

支付流程 以上是微信app支付的流程: 用户进入app选择商品进行购买,在app内部结算时生成用户本系统订单(待支付状态),此时返回订单信息与支付方式列表用户确认金额无误,并选择支付方式。此时app将订单id与支付方式传给服务器,服务器根据订单金额与支付方式在外部支付系统下单(预支付订单),并给app返回可以唤起响应支付工具的‘支付数据’app获取到‘支付数据’调用响应支付sdk,此时用户会看见微信支付或支付宝支付的页面,此时用户需要确认支付金额并输入正确的密码sdk会检查用户的密码信息是否正确并返回支付结果,此时app收到sdk同步通知的支付结果,并提交到服务器,服务器会记录该笔支付的状态,切记不能使用app上传回来的支付结果作为最终的支付结果,不能信任前端数据外部支付系统在处理完成该笔支付后会回调服务器设置的回调接口,通知服务器该笔支付的最终支付状态支付流程的反思以上流程时微信与支付宝给出的官方流程,并且也是最标准的流程。但是当外部支付系统并没有微信与支付宝那么优秀的时候,我们的系统就不能按照该流程正常运行下去,下面我说说在使用‘建行支付’时遇到的问题 服务器收不到支付结果的回调即使主动查询支付状态‘建行支付’依然返回未支付的状态以上两个问题会引发更复杂的问题 由于没有支付回调,订单状态就不会发送改变用户就会再次支付,造成重复支付的现象系统没有预料到会出现重复支付也就没有相应的策略去弥补由于系统并不知道支付已经成功,用户在取消订单的时候就不会收到退款根据这些线上出现的问题,我们决定进行重构,并深层次的处理整个支付流程! 设计思路1.确认支付方式 1.微信App支付2.支付宝App支付3.建行支付App支付2.如何确保支付成功 1.外部支付系统(支付宝)成功后回调通知2.本系统主动查询外部支付系统订单状态3.如何避免用户重复支付 1.本系统在发起支付的时候检查‘订单号’是否有已经成功的支付记录2.本系统在发起支付的时候检查‘订单号’是否有已经提交的支付记录,如果有需要同步查询外部支付系统该订单的支付状态4.如果用户出现重复支付系统如何处理 1.系统需要定时检查是否有同一个‘订单号’出现多条成功支付的记录,如果有需要保留一笔支付,其余的进行退款处理 5.数据出现异常怎么办(例如:用户说支付完成,但是订单依然是待支付的状态) 1.所有的支付流程都需要进行数据记录,形成支付流水,这样可以直观的看到用户支付的路径,也方便外部支付系统查询具体逻辑 ‘支付’是一次支付的记录,可能包含多个订单的支付金额,因为用户在购买商品生成订单的时候会根据商家拆单‘支付与订单的隐射’表明该支付中所有的订单信息,每个‘映射’都记录了订单的金额与支付状态,并且重复支付也是发生在该‘映射’上的,因为一个订单智能有一次最终成功支付的记录,最终哪一个映射是有效的由‘是否当前使用’决定,任何时候一个订单只有一个映射有效‘支付’可能有多条退款记录,退款的总金额不能超过支付金额,并且每一笔退款都需要一个唯一的退款交易号来保证不会重复退款,退款交易号由具体业务系统生成(比如退货,取消订单,重复支付)所有的退款必须成功系统需要主动查询支付状态是‘发起支付’,‘app同步通知成功’记录在外部支付系统的支付状态,如果在外部支付系统支付成功,这里需要重新设置支付状态为‘已完成’支付的外部交易号与退款的退款交易号都是唯一的为了保证系统的正常工作我们还需要一些定时器来作为最后的防线 接口实现1. 支付业务逻辑 public interface IPaymentApplicationService { /** * 创建支付,待支付状态 * @param orders 订单JSONArray * @param paymentAmount 支付金额 * @param paymentChannel 支付渠道 * @param paymentType 支付方式 * @param accountId 账户 * @param serviceId 服务 * @param platformId 平台 * @param hockParams 回传参数 * @return */PaymentResultDTO createPayment(List<PaymentOrderDTO> orders, BigDecimal paymentAmount, PaymentChannelEnum paymentChannel, PaymentTypeEnum paymentType, String accountId, String serviceId, String platformId,String hockParams) throws InvalidOperationException;/** * app、小程序、H5收到回调 * @param paymentId 支付id * @param isSuccess 是否支付成功 */void synchronizedCallback(String paymentId,boolean isSuccess) throws InvalidOperationException, PaymentQueryException, PaymentNotExistException;/** * app、小程序、H5收到回调 * @param orderIds 本次支付的所有订单id * @param isSuccess 是否成功 */void synchronizedCallback(Collection<String> orderIds,boolean isSuccess);/** * 服务器端收到回调 * @param paymentId 支付id * @param isSuccess 是否支付成功 * @param tradeNo 交易号 */void asyncCallback(String paymentId,boolean isSuccess,String tradeNo) throws InvalidOperationException;/** * 服务器端收到回调 * @param outTradeNo 外部交易号 * @param isSuccess 是否支付成功 * @param tradeNo 交易号 */void asyncCallbackForOutTradeNo(String outTradeNo,boolean isSuccess,String tradeNo);} ...

April 27, 2019 · 7 min · jiezi

coder,你会设计交易系统吗(实干篇)?

通过 上篇文章 的分析,我们已经明确了这个系统要干些什么。接下来的都是实打实的干货。这些内容认真阅读掌握后,相信你能够以此为基础设计一个维护性好、扩展性好的交易系统。数据库设计数据的设计是按照:交易、退款、日志 来设计的。对于上面说到的对账等功能并没有。这部分不难大家可以自行设计,按照上面讲到的思路。主要的表介绍如下:pay_transaction 记录所有的交易数据。pay_transaction_extension 记录每次向第三方发起交易时,生成的交易号pay_log_data 所有的日志数据,如:支付请求、退款请求、异步通知等pay_repeat_transaction 重复支付的数据pay_notify_app_log 通知应用程序的日志pay_refund 记录所有的退款数据具体的表结构:– ——————————————————- Table 创建支付流水表– —————————————————–CREATE TABLE IF NOT EXISTS pay_transaction ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, app_id VARCHAR(32) NOT NULL COMMENT ‘应用id’, pay_method_id INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘支付方式id,可以用来识别支付,如:支付宝、微信、Paypal等’, app_order_id VARCHAR(64) NOT NULL COMMENT ‘应用方订单号’, transaction_id VARCHAR(64) NOT NULL COMMENT ‘本次交易唯一id,整个支付系统唯一,生成他的原因主要是 order_id对于其它应用来说可能重复’, total_fee INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘支付金额,整数方式保存’, scale TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘金额对应的小数位数’, currency_code CHAR(3) NOT NULL DEFAULT ‘CNY’ COMMENT ‘交易的币种’, pay_channel VARCHAR(64) NOT NULL COMMENT ‘选择的支付渠道,比如:支付宝中的花呗、信用卡等’, expire_time INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘订单过期时间’, return_url VARCHAR(255) NOT NULL COMMENT ‘支付后跳转url’, notify_url VARCHAR(255) NOT NULL COMMENT ‘支付后,异步通知url’, email VARCHAR(64) NOT NULL COMMENT ‘用户的邮箱’, sing_type VARCHAR(10) NOT NULL DEFAULT ‘RSA’ COMMENT ‘采用的签方式:MD5 RSA RSA2 HASH-MAC等’, intput_charset CHAR(5) NOT NULL DEFAULT ‘UTF-8’ COMMENT ‘字符集编码方式’, payment_time INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘第三方支付成功的时间’, notify_time INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘收到异步通知的时间’, finish_time INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘通知上游系统的时间’, trade_no VARCHAR(64) NOT NULL COMMENT ‘第三方的流水号’, transaction_code VARCHAR(64) NOT NULL COMMENT ‘真实给第三方的交易code,异步通知的时候更新’, order_status TINYINT NOT NULL DEFAULT 0 COMMENT ‘0:等待支付,1:待付款完成, 2:完成支付,3:该笔交易已关闭,-1:支付失败’, create_at INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘创建时间’, update_at INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘更新时间’, create_ip INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘创建的ip,这可能是自己服务的ip’, update_ip INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘更新的ip’, PRIMARY KEY (id), UNIQUE INDEX uniq_tradid (transaction_id), INDEX idx_trade_no (trade_no), INDEX idx_ctime (create_at)),ENGINE = InnoDBDEFAULT CHARACTER SET = utf8mb4COMMENT = ‘发起支付的数据’;– ——————————————————- Table 交易扩展表– —————————————————–CREATE TABLE IF NOT EXISTS pay_transaction_extension ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, transaction_id VARCHAR(64) NOT NULL COMMENT ‘系统唯一交易id’, pay_method_id INT UNSIGNED NOT NULL DEFAULT 0, transaction_code VARCHAR(64) NOT NULL COMMENT ‘生成传输给第三方的订单号’, call_num TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘发起调用的次数’, extension_data TEXT NOT NULL COMMENT ‘扩展内容,需要保存:transaction_code 与 trade no 的映射关系,异步通知的时候填充’, create_at INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘创建时间’, create_ip INT UNSIGNED NOT NULL COMMENT ‘创建ip’, PRIMARY KEY (id), INDEX idx_trads (transaction_id, pay_status), UNIQUE INDEX uniq_code (transaction_code)),ENGINE = InnoDBDEFAULT CHARACTER SET = utf8mb4COMMENT = ‘交易扩展表’;– ——————————————————- Table 交易系统全部日志– —————————————————–CREATE TABLE IF NOT EXISTS pay_log_data ( id BIGINT UNSIGNED NOT NULL, app_id VARCHAR(32) NOT NULL COMMENT ‘应用id’, app_order_id VARCHAR(64) NOT NULL COMMENT ‘应用方订单号’, transaction_id VARCHAR(64) NOT NULL COMMENT ‘本次交易唯一id,整个支付系统唯一,生成他的原因主要是 order_id对于其它应用来说可能重复’, request_header TEXT NOT NULL COMMENT ‘请求的header 头’, request_params TEXT NOT NULL COMMENT ‘支付的请求参数’, log_type VARCHAR(10) NOT NULL COMMENT ‘日志类型,payment:支付; refund:退款; notify:异步通知; return:同步通知; query:查询’, create_at INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘创建时间’, create_ip INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘创建ip’, PRIMARY KEY (id), INDEX idx_tradt (transaction_id, log_type)),ENGINE = InnoDBDEFAULT CHARACTER SET = utf8mb4COMMENT = ‘交易日志表’;– ——————————————————- Table 重复支付的交易– —————————————————–CREATE TABLE IF NOT EXISTS pay_repeat_transaction ( id BIGINT UNSIGNED NOT NULL, app_id VARCHAR(32) NOT NULL COMMENT ‘应用的id’, transaction_id VARCHAR(64) NOT NULL COMMENT ‘系统唯一识别交易号’, transaction_code VARCHAR(64) NOT NULL COMMENT ‘支付成功时,该笔交易的 code’, trade_no VARCHAR(64) NOT NULL COMMENT ‘第三方对应的交易号’, pay_method_id INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘支付方式’, total_fee INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘交易金额’, scale TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘小数位数’, currency_code CHAR(3) NOT NULL DEFAULT ‘CNY’ COMMENT ‘支付选择的币种,CNY、HKD、USD等’, payment_time INT NOT NULL COMMENT ‘第三方交易时间’, repeat_type TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT ‘重复类型:1同渠道支付、2不同渠道支付’, repeat_status TINYINT UNSIGNED DEFAULT 0 COMMENT ‘处理状态,0:未处理;1:已处理’, create_at INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘创建时间’, update_at INT UNSIGNED NOT NULL COMMENT ‘更新时间’, PRIMARY KEY (id), INDEX idx_trad ( transaction_id), INDEX idx_method (pay_method_id), INDEX idx_time (create_at)),ENGINE = InnoDBDEFAULT CHARACTER SET = utf8mb4COMMENT = ‘记录重复支付’;– ——————————————————- Table 通知上游应用日志– —————————————————–CREATE TABLE IF NOT EXISTS pay_notify_app_log ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, app_id VARCHAR(32) NOT NULL COMMENT ‘应用id’, pay_method_id INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘支付方式’, transaction_id VARCHAR(64) NOT NULL COMMENT ‘交易号’, transaction_code VARCHAR(64) NOT NULL COMMENT ‘支付成功时,该笔交易的 code’, sign_type VARCHAR(10) NOT NULL DEFAULT ‘RSA’ COMMENT ‘采用的签名方式:MD5 RSA RSA2 HASH-MAC等’, input_charset CHAR(5) NOT NULL DEFAULT ‘UTF-8’, total_fee INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘涉及的金额,无小数’, scale TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘小数位数’, pay_channel VARCHAR(64) NOT NULL COMMENT ‘支付渠道’, trade_no VARCHAR(64) NOT NULL COMMENT ‘第三方交易号’, payment_time INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘支付时间’, notify_type VARCHAR(10) NOT NULL DEFAULT ‘paid’ COMMENT ‘通知类型,paid/refund/canceled’, notify_status VARCHAR(7) NOT NULL DEFAULT ‘INIT’ COMMENT ‘通知支付调用方结果;INIT:初始化,PENDING: 进行中; SUCCESS:成功; FAILED:失败’, create_at INT UNSIGNED NOT NULL DEFAULT 0, update_at INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX idx_trad (transaction_id), INDEX idx_app (app_id, notify_status) INDEX idx_time (create_at)),ENGINE = InnoDBDEFAULT CHARACTER SET = utf8mb4COMMENT = ‘支付调用方记录’;– ——————————————————- Table 退款– —————————————————–CREATE TABLE IF NOT EXISTS pay_refund ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, app_id VARCHAR(64) NOT NULL COMMENT ‘应用id’, app_refund_no VARCHAR(64) NOT NULL COMMENT ‘上游的退款id’, transaction_id VARCHAR(64) NOT NULL COMMENT ‘交易号’, trade_no VARCHAR(64) NOT NULL COMMENT ‘第三方交易号’, refund_no VARCHAR(64) NOT NULL COMMENT ‘支付平台生成的唯一退款单号’, pay_method_id INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘支付方式’, pay_channel VARCHAR(64) NOT NULL COMMENT ‘选择的支付渠道,比如:支付宝中的花呗、信用卡等’, refund_fee INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘退款金额’, scale TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘小数位数’, refund_reason VARCHAR(128) NOT NULL COMMENT ‘退款理由’, currency_code CHAR(3) NOT NULL DEFAULT ‘CNY’ COMMENT ‘币种,CNY USD HKD’, refund_type TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘退款类型;0:业务退款; 1:重复退款’, refund_method TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT ‘退款方式:1自动原路返回; 2人工打款’, refund_status TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘0未退款; 1退款处理中; 2退款成功; 3退款不成功’, create_at INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘创建时间’, update_at INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘更新时间’, create_ip INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘请求源ip’, update_ip INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ‘请求源ip’, PRIMARY KEY (id), UNIQUE INDEX uniq_refno (refund_no), INDEX idx_trad (transaction_id), INDEX idx_status (refund_status), INDEX idx_ctime (create_at)),ENGINE = InnoDBDEFAULT CHARACTER SET = utf8mb4COMMENT = ‘退款记录’;表的使用逻辑进行下方简单描述:支付,首先需要记录请求日志到 pay_log_data中,然后生成交易数据记录到 pay_transaction与pay_transaction_extension 中。收到通知,记录数据到 pay_log_data 中,然后根据时支付的通知还是退款的通知,更新 pay_transaction 与 pay_refund 的状态。如果是重复支付需要记录数据到 pay_repeat_transaction 中。并且将需要通知应用的数据记录到 pay_notify_app_log,这张表相当于一个消息表,会有消费者会去消费其中的内容。退款 记录日志日志到 pay_log_data 中,然后记录数据到退款表中 pay_refund。当然这其中还有些细节,需要大家自己看了表结构,实际去思考一下该如何使用。如果有任何疑问欢迎到我们GitHub的项目(点击阅读原文)中留言,我们都会一一解答。这些表能够满足最基本的需求,其它内容可根据自己的需求进行扩张,比如:支持用户卡列表、退款走银行卡等。系统设计这部分主要说下系统该如何搭建,以及代码组织方式的建议。系统架构由于支付系统的安全性非常高,因此不建议将对应的入口直接暴露给用户可见。应该是在自己的应用系统中调用支付系统的接口来完成业务。另外系统对数据要求是:强一致性的。因此也没有缓存介入(当如缓存可以用来做报警,这不在本位范畴)。具体的实现,系统会使用两个域名,一个为内部使用,只有指定来源的ip能够访问固定功能(访问除通知外的其它功能)。另一个域名只能访问 notify return 两个路由。通过这种方式可以保证系统的安全。在数据库的使用上无论什么请求直接走 Master 库。这样保证数据的强一致。当然从库也是需要的。比如:账单、对账相关逻辑我们可以利用从库完成。代码设计不管想做什么最终都要用代码来实现。我们都知道需要可维护、可扩展的代码。那么具体到支付系统你会怎么做呢?我已支付为例说下我的代码结构设计思路。仅供参考。比如我要介入:微信、支付宝、招行 三家支付。我的代码结构图如下:用文字简单介绍下。我会将每一个第三方封装成: XXXGateway 类,内部是单纯的封装第三方接口,不管对方是 HTTP 请求还是 SOAP 请求,都在内部进行统一处理。另外有一层XXXProxy 来封装这些第三方提供的能力。这一层主要干两件事情:对传过来请求支付的数据进行个性化处理。对返回的结构进行统一处理返回上层统一的结构。当然根据特殊情况这里可以进行一切业务处理;通过上面的操作变化已经基本上被完全封装了。如果新增一个支付渠道。只需要增加:XXXGateway 与 XXXProxy。那么 Context 与 Server 有什么用呢?Server 内部封装了所有的业务逻辑,它提供接口给 action 或者其它 server 进行调用。而 Context 这一层存在的价值是处理 Proxy 层返回的错误。以及在这里进行报警相关的处理。上面的结构只是我的一个实践,欢迎大家讨论。本文描述的系统只是满足了最基本的支付需求。缺少相关的监控、报警。大家可以到我们的 GitHub主页留言 ...

March 20, 2019 · 5 min · jiezi

PHP开发银联云闪付二维码支付

你好,是我琉忆。最近刚好有在研究银联云闪付的支付模块,所以就写篇总结分享给大家。这算是第二次接触支付的东西了,接触得最多的是接入微信支付,自己也有相关的总结文章,可以去segmentfault搜索“琉忆”,查看我的文章里查看。自己整理了一篇“新手开发PayPal境外支付”的文章,关注:琉忆编程库,回复“pay”我发给你。前言写这个总结也是因为搜索不到相关的开发文章所以才想写的。其次,发现看文档研究后总结了一句话:“自学,你才会发现你能力提升的更快”。预备先献上银联全部产品开发文档路径(具体可以自己选择的参考):银联全部产品二维码支付的开发文档: 银联二维码支付官方文档二维码支付PHP示例代码下载:二维码支付示例代码下载路径一点小建议:一边看文档,一边看示例半天足够上手开发完毕。(往下看,就会明白怎么边学边应用了!!)上路浏览器打开了开发文档,又下载好了代码示例,那么我们开始上路。步骤一:查看产品简介先了解清楚这个东西是做什么的,然后接入的流程是怎么样的,怎么应用。很方便的就是,银联云闪付的接入给我们准备了测试模式,就是你并不需要真的有商户号才能开发,不像微信支付那样非要有商户号才能测试开发。步骤二:点击“我要测试”,注册测试商户号注意,注册的时候需要用IE浏览器打开这个注册页面,需要下载一个安全插件,只支持使用IE浏览器登录!步骤三:注册完登录,添加测试产品因为我已经添加过了,并且测试完整个流程了,所以就是在“已测试”里面了。具体你应该选择“未测试”里面选“二维码支付”。步骤四:看文档,“快速接入”这个介绍很重要,建议好好看一遍,并且从第一步开始做,不要跳步骤!而第一步“申请入网”,是我这里写的前几个步骤注册商户号就做了。所以,可以直接从文档的第二步起做。其实第二步我们也做了,那就是下载的开发代码里面有证书了。步骤五:看文档第三步,接入被扫或主扫(具体看自己的业务决定)其实二者步骤类似,我这里开发的是主扫,所以以主扫的步骤来讲解。查看主扫流程:大致知道自己开发的每个流程,主要是从1-3,5-6都是银联第三方在操作,后续的7-11就是支付成功的操作了。步骤六:查看异步通知说明这个决定了支付后订单改没改订单状态,支付成功跳转回来的路径问题,建议理解清楚。具体的,看完上面几点可以开始开发了,如果你想更全面的了解这个产品,那么肯定是需要看完全部项的说明介绍的,具体看个人选择。总结:看完上面的大概了解了开发流程,开发任务,还有支付成功的重要操作。那么就可以直接进行API上手代码的操作了。自己整理了一篇“新手开发PayPal境外支付”的文章,关注:琉忆编程库,回复“pay”我发给你。实操演练点击查看“API列表”,对应的做操作。API列表链接:API链接详情我主要讲解以下几个,其他的方法类同(举一反三并不难,同等去操作)下载完上面的路径后,就可以打开里面的实例包在本地进行测试开发。具体可以根据示例进行整合开发。在开发的配置过程有什么问题都可以直接留言,我回复你。感谢你的阅读。获取跟多相关开发技巧、PHP面试知识真题等,可以关注我的公众号:琉忆编程库

February 21, 2019 · 1 min · jiezi

智能支付稳定性测试实战

本文根据美团高级测试开发工程师勋伟在美团第43期技术沙龙“美团金融千万级交易系统质量保障之路”的演讲整理而成。主要介绍了美团智能支付业务在稳定性方向遇到的挑战,并重点介绍QA在稳定性测试中的一些方法与实践。背景美团支付承载了美团全部的交易流量,按照使用场景可以将其分为线上支付和智能支付两类业务。线上支付,支撑用户线上消费场景,处理美团所有线上交易,为团购、外卖、酒店旅游等业务线提供支付能力;智能支付,支撑用户到店消费场景,处理美团所有线下交易,通过智能POS、二维码支付、盒子支付等方式,为商家提供高效、智能化的收银解决方案。其中,智能支付作为新扩展的业务场景,去年也成为了美团增速最快的业务之一。面临的挑战而随着业务的快速增长,看似简单的支付动作,背后系统的复杂度却在持续提升。体现在:上层业务入口、底层支付渠道的不断丰富,微服务化背景下系统的纵向分层、服务的横向拆分,还有对外部系统(营销中心、会员中心、风控中心等)、内部基础设施(队列、缓存等)的依赖也越来越多,整条链路上的核心服务节点超过20个,业务复杂度可想而知。此外,技术团队在短时间内就完成了从几个人到近百人规模的扩张,这也是一个潜在的不稳定因素。曾经在一段时间内,整个系统处在“牵一发而动全身”的状态,即使自身系统不做任何发版升级,也会因为一些基础设施、上下游服务的问题,业务会毫无征兆地受到影响。痛定思痛,我们对发生过的线上问题进行复盘,分析影响服务稳定性的原因。通过数据发现,72%的严重故障集中在第三方服务和基础设施故障,对应的一些典型事故场景,比如:第三方支付通道不稳定、基础设施(如消息队列)不稳定,进而导致整个系统雪崩,当依赖方故障恢复后,我们的业务却很难立即恢复。解决方案基于这些问题,我们开展了稳定性建设专项,目的很明确:提升服务的可用性。目标是逐步将系统可用性从2个9提升到3个9,再向4个9去努力。这个过程中最核心的两个策略:柔性可用,意思是尽可能保证核心功能可用,或在有损情况下尽可能保证核心用户体验,降低影响;另一个是快速恢复,即用工具或机制保证故障的快速定位和解决,降低故障修复时间。围绕这两个策略,在稳定性建设中的常见操作:限流、熔断降级、扩容,用于打造系统的柔性可用;故障响应SOP、故障自动处理,用于故障处理时的快速恢复。而QA的工作更侧重于对这些“常见操作”进行有效性验证。基于经验,重点介绍“三把利剑”:故障演练、线上压测、持续运营体系。故障演练的由来举个真实的案例,在一次处理某支付通道不稳定的线上问题时,开发同学执行之前已经测试通过的预案(服务端关闭该通道,预期客户端将该支付通道的开关置灰,并会提示用户使用其他支付方式),但执行中却发现预案无法生效(服务端操作后,客户端该支付通道仍处于开启状态)。非故障场景下预案功能正常,故障场景下却失效了。这就是故障演练的由来,我们需要尽可能还原故障场景,才能真正验证预案的有效性。故障演练的整体方案故障演练的整体方案,主要分为三部分:负载生成模块,负责尽可能还原系统的真实运行场景(要求覆盖核心业务流程)。故障注入模块,包含故障注入工具、故障样本库(涵盖外部服务、基础组件、机房、网络等各种依赖,并重点关注超时、异常两种情况)。业务验证模块,结合自动化测试用例和各个监控大盘来进行。为了更高效地开展故障演练,我们的策略是分为两个阶段进行。首先,针对单系统进行故障演练,从故障样本库出发,全面覆盖该系统所有的保护预案;在此基础上,进行全链路故障演练,聚焦核心服务故障,验证上下游服务的容错性。故障演练的效果事实证明,故障演练确实给我们带来了很多“惊喜”,暴露了很多隐患。这里列举三类问题:数据库主从延迟影响交易;基础设施故障时,业务未做降级;依赖服务超时设置不合理、限流策略考虑不足等。线上压测的由来面对业务的指数级增长,我们必须对系统可承载的流量做到心中有数。对于QA来说,需要找到精准、高效的系统容量评估方法。我们碰到的难点包括:链路长、环节多、服务错综复杂,线下环境与线上差异大等等,基于测试有效性和测试成本考虑,我们决定要做线上压测,而且要实现全链路的线上压测。线上压测的整体方案全链路压测的实现方案,与业界主流方案没有太大区别。根据压测流程,首先,场景建模,以便更真实的还原线上系统运行场景;其次,基础数据构造,应满足数据类型以及量级的要求,避免数据热点;之后,流量构建,读写流量构造或回放,同时对压测流量进行标记和脱敏;再之后,压测执行,过程中收集链路各节点的业务运行状态、资源使用情况等;最后,生成压测报告。基于全链路线上压测方案,可以根据业务需求,灵活地进行单链路压测、分层压测等。更为重要的是,基于压测我们可以进行线上的故障演练,用于更加真实的验证系统限流、熔断等保护预案。线上压测的效果通过全链路线上压测,一方面让我们对系统容量做到心中有数,另一方面也让我们发现了线上系统运行过程中的潜在问题,而且这些问题一般都是高风险的。同样列举三类问题:基础设施优化,如机房负载不均衡、数据库主从延迟严重等;系统服务优化,如线程池配置不合理、数据库需要拆分等;故障预案优化,如限流阈值设置过低,有的甚至已经接近限流边缘而浑然不知等等。持续运营体系的由来智能支付的稳定性建设是作为一个专项在做,持续了近3个月的时间;在效果还不错的情况下,我们从智能支付延伸到整个金融服务平台,以虚拟项目组的方式再次运转了3个月的时间。通过项目方式,确实能集中解决现存的大部分稳定性问题,但业务在发展、系统在迭代,稳定性建设必然是一项长期的工作。于是,QA牵头SRE、DBA、RD,建立了初步的稳定性持续运营体系,并在持续完善。持续运营体系的整体方案下面介绍持续运营体系的三大策略:流程规范工具化,尽可能减少人为意识因素,降低人力沟通和维护成本。如:配置变更流程,将配置变更视同代码上线,以PR方式提交评审;代码规范检查落地到工具,尽可能将编码最佳实践抽取为规则,将人工检查演变为工具检查。质量度量可视化,提取指标、通过数据驱动相关问题的PDCA闭环。如:我们与SRE、DBA进行合作,将线上系统运维中与稳定性相关的指标提取出来,类似数据库慢查询次数、核心服务接口响应时长等等,并对指标数据进行实时监控,进而推进相关问题的解决。演练压测常态化,降低演练和压测成本,具备常态化执行的能力。如:通过自动化的触发演练报警,验证应急SOP在各团队实际执行中的效果。基于以上三个策略,构建稳定性持续运营体系。强调闭环,从质量度量与评价、到问题分析与解决,最终完成方法与工具的沉淀;过程中,通过平台建设来落地运营数据、完善运营工具,提升运营效率。持续运营体系的效果简单展示当前持续运营体系的运行效果,包含风险评估、质量大盘、问题跟进以及最佳实践的沉淀等。未来规划综上便是智能支付QA在稳定性建设中的重点工作。对于未来工作的想法,主要有3个方向。第一,测试有效性提升,持续去扩展故障样本库、优化演练工具和压测方案;第二,持续的平台化建设,实现操作平台化、数据平台化;第三,智能化,逐步从人工运营、自动化运营到尝试智能化运营。作者介绍勋伟,美团高级测试开发工程师,金融服务平台智能支付业务测试负责人,2015年加入美团点评。招聘如果你想学习互联网金融的技术体系,亲历互联网金融业务的爆发式增长,如果你想和我们一起,保证业务产品的高质量,欢迎加入美团金融工程质量组。有兴趣的同学可以发送简历到:fanxunwei#meituan.com。

December 14, 2018 · 1 min · jiezi

第三方支付的流程分析与总结

这几年的工作中一直与支付打交到,借着 skr-shop 这个项目来与大家一起分享探索一下支付系统该怎么设计、怎么做。我们先从支付的一些常见流程出发分析,找出这些支付的共性,抽象后再去探讨具体的数据库设计、代码结构设计。相关项目:PHP 版本的支付SDKGo 版本的支付SDK-开发中支付整体而言的一个流程是:给第三方发起了一笔交易,用户通过第三方完成支付,第三方告诉我支付成功,我把用户购买的产品给用户。看似简单的流程,这里边不同的支付机构却有不同的处理。下面以我接触过的一些支付来总结一下国内支付国内的典型支付代表是:支付宝、微信、银行(以招商银行为例),由于国内的支付都支持多种渠道的支付方式,为了描述简单,我们均以pc上的支付为例进行讲解。支付宝支付宝的接入是我觉得最简单的一种支付。对于在PC上的支付能力,支付宝提供了【电脑支付】。当用户下单后,商户系统根据支付宝的规则构建好一个url,用户跳转到这个url后进入到支付宝的支付页面,然后完成支付流程。在支付成功后,支付宝会通过 同步通知、异步通知 两种方式告诉商户系统支付成功,并且两种通知方式的结果都是可信的,而且异步通知的消息延迟也非常短暂。对于退款流程,支付宝支持全额、部分退款。并且能够根据商户的退款单号区分是否是同一笔退款进而避免了重复退款的可能。支付的退款是调用后同步返回结果,不会异步通知。微信支付微信并没有提供真的PC支付能力,但是我们可以利用【扫码支付】来达成电脑支付的目的。扫码支付有两种模式,这里以模式二为例。微信调用下单接口获取到这个二维码链接,然后用户扫码后,进入支付流程。完成支付后微信会 异步通知,但是这里并没有 同步通知,因此前端页面只能通过定时轮训的方式检查这笔交易是否支付,直到查询到成功、或者用户主动关闭页面。退款流程与支付宝最大的不同是,有一个 异步通知 需要商户系统进行处理。第一个不同点:异步通知的接口需要处理多种不同类型的异步消息招商银行随着在线支付在国内的蓬勃发展,各家银行也是不断推出自己的在线支付能力。其中的佼佼者当属 招商银行。大家经常用的滴滴上面就有该支付方式,可以体验一下。招商支付使用的是银行卡,因此首次用户必须进行绑卡。因此这里可能就多了一个流程,首先得记录用户是否绑过卡,然后用于签名的公钥会发生变化,需要定期更新。招商所有平台的支付体验都是一致的,会跳转到招行的H5页面完成逻辑,支付成功后并不会自动跳回商户,也就是没有 同步通知,它的支付结果只会走异步通知流程,延迟非常短暂。退款流程与支付宝一样,也是同步返回退款结果,没有异步通知。第二个不同点:支付前需要检查用户是否签约过,有签约流程小结国内在线支付流程相对都比较完善,接入起来也非常容易。需要注意的一点是:退款后之前支付的单子依然是支付成功状态,并不会变成退款状态。因为退款与支付属于不同的交易。这一点基本上是国内在线支付的通用做法。国际支付国际支付的平台非常多,包括像支付宝、微信也在扩展这一块市场。我以我接触的几家支付做一个简单的总结。WorldPay这是比较出名的一家国际支付公司,它主要做的是银行卡支付,公司在英国支付流程上,也是根据规则构建好请求的url后,直接跳转到 WorldPay 的页面,通过信用卡完成支付。这里比较麻烦的处理机制是:支付成功后,他首次给你的异步/同步消息通知并不能作为支付成功的依据。真的从银行确认划款成功后,才会给出真的支付成功通知。这中间还可能会异步通知告诉你支付请求被拒绝。最头痛的是不同状态的异步消息时间间隔都是按照分钟以上级别的延迟来计算退款流程上,状态跟微信一样,需要通过异步消息来确认退款状态。其次它的不同点在于无法根据商户退款单号来确认是否已经发起过退款,因此对于它来说只要请求一次退款接口,那它就默认发起了一次退款。第三、四不同点:支付成功后的通知状态有多种,涉及到商户系统业务流程的特殊处理退款不支持商户退款单号,无法支持防重复退款需要商户自己处理Assist这是俄罗斯的一家支付公司,这也是一家搞死人不偿命的公司,看下面介绍它的支付发起是需要构建一个form表单,向它post支付相关的数据。成功后会跳转到它的支付页,用户完成支付即可。对于 同步通知,它需要用户手动触发跳回商户,与招商的逻辑很像,同步也仅仅是做返回并不会真的告知支付结果。异步通知 才是真的告知支付状态。比较恶心的是,支付时必须传入指定格式的商品信息,这会在部分退款时用到。现在来说退款,退款也是与 WorldPay 一样,不支持商户的退款单号,因此防重方面也许自己的系统进行设计。并且如果是部分退款,需要传入指定的退款商品,这就会出现一个非常尴尬的局面:部分退款的金额与任何一个商品金额都对应不上,退款则会失败。第五个不同点:部分退款时需要传入部分退款的商品信息,并且金额要一致Doku接下来再来聊聊印尼的这家支付机构 doku。由于印尼这个国家信用卡的普及程度并不高,它的在线支付提供一种超商支付方式。什么是超商支付呢?也就是用户在网络上完成下单后,会获取到一个二维码或者条形码。用户拿着这个条形码到超商(711、全家这种)通过收银员扫码,付现金给超商,完成支付流程。这种方式带来的问题是,用户长时间不去支付,导致订单超时关单后才去付款。对整个业务流程以及用户体验带来很多伤害。再来说退款,由于存在超商这种支付方式,导致这种支付无法支持在线自动退款,需要人工收集用户银行卡信息,然后完成转账操作。非常痛苦不堪。第六个不同点:线上没有付款,只有获取付款码,退款需要通过人工操作AmazonPay亚马逊出品,与支付宝非常类似。提供的是集成式的钱包流程。支付时直接构建一个url,然后跳转到亚马逊即可完成支付。它还提供一种授权模式,能够不用跳转amazon,再商户端即完成支付。支付成功后也会同步跳转,同步通知 的内容可以作为支付是否成功的判断依据。经过实际检查 异步通知 的到达会稍有延迟,大概10s以内。退款方面也支持商户退款单号可以依赖此进行防重。但是退款的状态也是基于异步来的。总结这其中还有一些国际支付,如:PayPal、GooglePay、PayTM 等知名支付机构没有进行介绍,是因为基本它们的流程也都在上面的模式之中。我们后续的代码结构设计、数据库设计都基于满足上面的各种支付模型来完成设计。最后,赠送大家一副脑图,这是接入一家支付时必须弄清楚的问题清单下篇预告:《支付数据库与代码结构设计》这是我们几个小伙伴利用业余时间思考的一些业务设计,如果有写的不对或者不完善的地方,希望大家多多评论,互相学习互相进步~项目地址: https://github.com/skr-shop/m...skr-shop项目成员简介排名不分先后,字典序昵称简介个人博客AStraw研究生创业者, 现于小米科技海外商城组从事商城后端研发工作——–DayuPayment开源作者,服务端开发者dayutalk.cnlwhcv曾就职于百度/融360, 现于小米科技海外商城组从事商城后端研发工作——–TIGERBPHP框架EasyPHP作者,拥有A/B/C轮电商创业公司工作经验,现于小米科技海外商城组从事商城后端研发工作TIGERB.cn

November 26, 2018 · 1 min · jiezi