每个渠道的接口,传输方式都不尽相同,所以在这里,支付网关相对于支付渠道模块的作用,类似设计模式中的wrapper,封装各个渠道的差异,对网关呈现统一的接口。而网关的功能是为业务提供通用接口,一些和渠道交互的公共操作,也会放置到网关中。
不同类型的支付产品,其对外提供的接口也会有区别。 后续分类别介绍各种支付产品的设计。 这里重点介绍支付API网关设计、支付产品的整体流程实现。 而软件架构的设计,是基于微服务架构来描述的。
支付网关是直接对接业务系统的接口,它本身并不执行任何支付相关的业务逻辑。它将支付产品接口中和业务无关的功能提取出来,在这里统一实现。这样在具体产品接口中,就无需考虑这些和业务无关的逻辑。支付网关设计还和对外接口参数有关。我们看一下业内几个主流的支付平台的接口设计。
和支付宝不一样,微信支付是采用XML格式来作为报文传输。在其接口文档说明中, 对XML报文格式有详细的描述。当然,也使用签名字符串来保证接口的安全,签名结果放在sign标签下。
PayPal是标准的Restful设计,将支付中涉及到的对象,如Payment, Order, Credit Card等,以资源的形式,支持通过Restful API来操作。
#mermaid-svg-UtjsakEAIn2AOBYs .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-UtjsakEAIn2AOBYs .label text{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .node rect,#mermaid-svg-UtjsakEAIn2AOBYs .node circle,#mermaid-svg-UtjsakEAIn2AOBYs .node ellipse,#mermaid-svg-UtjsakEAIn2AOBYs .node polygon,#mermaid-svg-UtjsakEAIn2AOBYs .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-UtjsakEAIn2AOBYs .node .label{text-align:center;fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .node.clickable{cursor:pointer}#mermaid-svg-UtjsakEAIn2AOBYs .arrowheadPath{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-UtjsakEAIn2AOBYs .flowchart-link{stroke:#333;fill:none}#mermaid-svg-UtjsakEAIn2AOBYs .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-UtjsakEAIn2AOBYs .edgeLabel rect{opacity:0.9}#mermaid-svg-UtjsakEAIn2AOBYs .edgeLabel span{color:#333}#mermaid-svg-UtjsakEAIn2AOBYs .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-UtjsakEAIn2AOBYs .cluster text{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-UtjsakEAIn2AOBYs .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-UtjsakEAIn2AOBYs text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-UtjsakEAIn2AOBYs .actor-line{stroke:grey}#mermaid-svg-UtjsakEAIn2AOBYs .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-UtjsakEAIn2AOBYs .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-UtjsakEAIn2AOBYs #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-UtjsakEAIn2AOBYs .sequenceNumber{fill:#fff}#mermaid-svg-UtjsakEAIn2AOBYs #sequencenumber{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs #crosshead path{fill:#333;stroke:#333}#mermaid-svg-UtjsakEAIn2AOBYs .messageText{fill:#333;stroke:#333}#mermaid-svg-UtjsakEAIn2AOBYs .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-UtjsakEAIn2AOBYs .labelText,#mermaid-svg-UtjsakEAIn2AOBYs .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-UtjsakEAIn2AOBYs .loopText,#mermaid-svg-UtjsakEAIn2AOBYs .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-UtjsakEAIn2AOBYs .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-UtjsakEAIn2AOBYs .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-UtjsakEAIn2AOBYs .noteText,#mermaid-svg-UtjsakEAIn2AOBYs .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-UtjsakEAIn2AOBYs .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-UtjsakEAIn2AOBYs .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-UtjsakEAIn2AOBYs .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-UtjsakEAIn2AOBYs .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs .section{stroke:none;opacity:0.2}#mermaid-svg-UtjsakEAIn2AOBYs .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-UtjsakEAIn2AOBYs .section2{fill:#fff400}#mermaid-svg-UtjsakEAIn2AOBYs .section1,#mermaid-svg-UtjsakEAIn2AOBYs .section3{fill:#fff;opacity:0.2}#mermaid-svg-UtjsakEAIn2AOBYs .sectionTitle0{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .sectionTitle1{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .sectionTitle2{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .sectionTitle3{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-UtjsakEAIn2AOBYs .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs .grid path{stroke-width:0}#mermaid-svg-UtjsakEAIn2AOBYs .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-UtjsakEAIn2AOBYs .task{stroke-width:2}#mermaid-svg-UtjsakEAIn2AOBYs .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs .taskText:not([font-size]){font-size:11px}#mermaid-svg-UtjsakEAIn2AOBYs .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-UtjsakEAIn2AOBYs .task.clickable{cursor:pointer}#mermaid-svg-UtjsakEAIn2AOBYs .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-UtjsakEAIn2AOBYs .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-UtjsakEAIn2AOBYs .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-UtjsakEAIn2AOBYs .taskText0,#mermaid-svg-UtjsakEAIn2AOBYs .taskText1,#mermaid-svg-UtjsakEAIn2AOBYs .taskText2,#mermaid-svg-UtjsakEAIn2AOBYs .taskText3{fill:#fff}#mermaid-svg-UtjsakEAIn2AOBYs .task0,#mermaid-svg-UtjsakEAIn2AOBYs .task1,#mermaid-svg-UtjsakEAIn2AOBYs .task2,#mermaid-svg-UtjsakEAIn2AOBYs .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-UtjsakEAIn2AOBYs .taskTextOutside0,#mermaid-svg-UtjsakEAIn2AOBYs .taskTextOutside2{fill:#000}#mermaid-svg-UtjsakEAIn2AOBYs .taskTextOutside1,#mermaid-svg-UtjsakEAIn2AOBYs .taskTextOutside3{fill:#000}#mermaid-svg-UtjsakEAIn2AOBYs .active0,#mermaid-svg-UtjsakEAIn2AOBYs .active1,#mermaid-svg-UtjsakEAIn2AOBYs .active2,#mermaid-svg-UtjsakEAIn2AOBYs .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-UtjsakEAIn2AOBYs .activeText0,#mermaid-svg-UtjsakEAIn2AOBYs .activeText1,#mermaid-svg-UtjsakEAIn2AOBYs .activeText2,#mermaid-svg-UtjsakEAIn2AOBYs .activeText3{fill:#000 !important}#mermaid-svg-UtjsakEAIn2AOBYs .done0,#mermaid-svg-UtjsakEAIn2AOBYs .done1,#mermaid-svg-UtjsakEAIn2AOBYs .done2,#mermaid-svg-UtjsakEAIn2AOBYs .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-UtjsakEAIn2AOBYs .doneText0,#mermaid-svg-UtjsakEAIn2AOBYs .doneText1,#mermaid-svg-UtjsakEAIn2AOBYs .doneText2,#mermaid-svg-UtjsakEAIn2AOBYs .doneText3{fill:#000 !important}#mermaid-svg-UtjsakEAIn2AOBYs .crit0,#mermaid-svg-UtjsakEAIn2AOBYs .crit1,#mermaid-svg-UtjsakEAIn2AOBYs .crit2,#mermaid-svg-UtjsakEAIn2AOBYs .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-UtjsakEAIn2AOBYs .activeCrit0,#mermaid-svg-UtjsakEAIn2AOBYs .activeCrit1,#mermaid-svg-UtjsakEAIn2AOBYs .activeCrit2,#mermaid-svg-UtjsakEAIn2AOBYs .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-UtjsakEAIn2AOBYs .doneCrit0,#mermaid-svg-UtjsakEAIn2AOBYs .doneCrit1,#mermaid-svg-UtjsakEAIn2AOBYs .doneCrit2,#mermaid-svg-UtjsakEAIn2AOBYs .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-UtjsakEAIn2AOBYs .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-UtjsakEAIn2AOBYs .milestoneText{font-style:italic}#mermaid-svg-UtjsakEAIn2AOBYs .doneCritText0,#mermaid-svg-UtjsakEAIn2AOBYs .doneCritText1,#mermaid-svg-UtjsakEAIn2AOBYs .doneCritText2,#mermaid-svg-UtjsakEAIn2AOBYs .doneCritText3{fill:#000 !important}#mermaid-svg-UtjsakEAIn2AOBYs .activeCritText0,#mermaid-svg-UtjsakEAIn2AOBYs .activeCritText1,#mermaid-svg-UtjsakEAIn2AOBYs .activeCritText2,#mermaid-svg-UtjsakEAIn2AOBYs .activeCritText3{fill:#000 !important}#mermaid-svg-UtjsakEAIn2AOBYs .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-UtjsakEAIn2AOBYs g.classGroup text .title{font-weight:bolder}#mermaid-svg-UtjsakEAIn2AOBYs g.clickable{cursor:pointer}#mermaid-svg-UtjsakEAIn2AOBYs g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-UtjsakEAIn2AOBYs g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-UtjsakEAIn2AOBYs .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-UtjsakEAIn2AOBYs .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-UtjsakEAIn2AOBYs .dashed-line{stroke-dasharray:3}#mermaid-svg-UtjsakEAIn2AOBYs #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs .commit-id,#mermaid-svg-UtjsakEAIn2AOBYs .commit-msg,#mermaid-svg-UtjsakEAIn2AOBYs .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-UtjsakEAIn2AOBYs g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-UtjsakEAIn2AOBYs g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-UtjsakEAIn2AOBYs g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-UtjsakEAIn2AOBYs .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-UtjsakEAIn2AOBYs .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-UtjsakEAIn2AOBYs .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-UtjsakEAIn2AOBYs .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-UtjsakEAIn2AOBYs .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-UtjsakEAIn2AOBYs .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-UtjsakEAIn2AOBYs .edgeLabel text{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-UtjsakEAIn2AOBYs .node circle.state-start{fill:black;stroke:black}#mermaid-svg-UtjsakEAIn2AOBYs .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-UtjsakEAIn2AOBYs #statediagram-barbEnd{fill:#9370db}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-state .divider{stroke:#9370db}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-UtjsakEAIn2AOBYs .note-edge{stroke-dasharray:5}#mermaid-svg-UtjsakEAIn2AOBYs .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-UtjsakEAIn2AOBYs .error-icon{fill:#522}#mermaid-svg-UtjsakEAIn2AOBYs .error-text{fill:#522;stroke:#522}#mermaid-svg-UtjsakEAIn2AOBYs .edge-thickness-normal{stroke-width:2px}#mermaid-svg-UtjsakEAIn2AOBYs .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-UtjsakEAIn2AOBYs .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-UtjsakEAIn2AOBYs .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-UtjsakEAIn2AOBYs .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-UtjsakEAIn2AOBYs .marker{fill:#333}#mermaid-svg-UtjsakEAIn2AOBYs .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-UtjsakEAIn2AOBYs {color: rgba(0, 0, 0, 0.75);font: ;}
webhooks 网勾
payout item 花销项目
payouts 花销
orders 订单
captures
authorization 授权
refund 退款
SaleTransactions 销售交易
Payments 付款
create webhook 创建网勾
show webhook details 展示网勾详情
list all webhooks 列出所有网勾
update webhook 更新网勾
delete webhook 删除网勾
show payout item details 展示花销项目详情
cancel unclaimed payout item 取消无人认领的花销项目
create payouts 创建花销
show batch payout details 展示批量的花销详情
show order details 展示订单详情
authorize order 批准订单
capture order 捕获订单
void an order 废除一个订单
show captured payment details 展示捕获的付款详情
refund captured payment 退还捕获的付款
show authorization details 展示授权详情
capture an authorization 获取一个授权
void an authorization 废除一个授权
reauthorization payment 重新授权支付
show refund details 展示退款详情
show sale details 展示销售详情
refund sale 销售退款
create payment 创建付款
execute approved payment 实施核实的付款
show payment details 展示付款详情
update payment 更新付款(记录)
list payments 列出付款(记录)
Paypal-Services
Billing Agreements 账单协议
create agreement 创建协议
update agreement 更新协议
agreement details 协议详情
bill agreement balance 协议账单余额
cancel agreement 取消协议
re-active agreement 重新激活协议
set agreement balance 设置协议余额
suspend agreement 终止协议
list agreement transactions 列出协议交易记录
execute agreement 执行协议
Billing Plans 计费计划
Identity 身份
Invoice 发票
Payment Experience 支付经历
Payments 付款
Payouts 支出
Vault 金库
webhook 网勾
created plan 创建计划
更新计划
计划详情
列出计划
TokenService
OpenId connect
create invoice 创建发票
generate QR code 生成二维码
list merchant invoice 列出商户发票
serach for invoice 搜索发票
create web experience profile 创建web支付经历简介
show web experience details 展示web支付经历详情
list web experience profiles 列出创建web支付经历简介
update web experience profiles 更新web支付经历简介
delete web experience profile 删除web支付经历简介
show credit card 出示信用卡
list vaulted credit cards 列出金库中的信用卡
show vaulted credit card details 列出金库中的信用卡详情
update vaulted credit card 更新金库中的信用卡
delete vaulted credit card 删除金库中的信用卡
event notification 事件提醒
PayPal的定位以及设计目标和国内第三方支付平台不同,它以支持国际营收为主。对国内应用来说,其易用性和支付宝、微信支付相比还稍逊一些,不过Paypal一直是支付API设计的典范。
对电商支付平台来说,其定位更接近于一个聚合支付。聚合多种支付方式,为公司各个业务提供支持。 在这里,支付网关和支付产品的设计尤为关键。合理的接口设计能够大大降低支付渠道对接的开发工作量。一般支付产品不会超过10个,而根据公司的规模,对接的支付渠道超过100个都有可能。