Paypal API 设计标准
PayPal API 设计标准
在构建
URI Components
Version: 版本控制
vN
,其中N
指明版本号。基于
- URI Template
/v{version}/
- Example
/v1/
Namespaces: 命名空间
如果在version
之后的第一个字段。命名空间折射出消费者对于
- URI Template
/{version}/{namespace}/
- Example
/v1/vault/
Resource References: 资源关联
- URI Template
/{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}
Collection Resources
支持/users
,这样可以和下面提及的
Verb | Usage | Notes | |
---|---|---|---|
GET | Read | X | |
POST | Create | 仅当使用 PayPal-Request-Id 请求头时具有幂等性 | |
PUT | Create | 仅当在客户端提供了资源标识符时具有创建功能 | |
PUT | Update | X | 仅用于某个资源的全部属性的更新,不可用于局部 |
PATCH | Update | 使用JSON Patch 消息格式 | |
DELETE | Delete | 应当在多次请求下具有相同的响应 |
Collection Resource: 获取资源集合/ 列表
包含任何相关联的元信息的给定资源的列表,所有的资源应当包含在items
域中,而类似于total_items
以及 total_pages
的域指明整个关于数据整体的信息。这种命名的一致性有助于客户端开发者构建面向不同的资源集合的通用的处理函数。如果使用GET
动作进行访问,那么注意不应该影响到整个系统,并且保证除非数据发送变化否则响应消息也应该保持一致性。另外还需要注意的是,譬如日志输出这种动作不会被认为是对系统的修改。在
Filtering: 过滤
Paging: 分页
关于分页的操作应该来源于请求时的page
与page_size
参数,其中page_size
指明了每次请求的结果数目,page
指明了请求的是第几页。另外,响应时应当保证包含total_items
与total_pages
这两个参数,其中total_items
指示请求的集合中总的数目,total_pages
指向总的页数total_items
page_size
Hypermedia links: 超链接
next
previous
first
last
等等类似的命名下。
Time selection
如果需要根据时间进行选择,那么需要添加start_time
或者{property_name}_after
end_time
{property_name}_before
这些查询参数。
Sorting: 排序
sort_by
以及 sort_order
参数可以用来指明需要被排序的资源集合。一般来说sort_by
需要包含某个独立资源名,而sort_order
应该是asc
或者desc
值。
- URI Template
GET /{version}/{namespace}/{resource}
- Example Request
GET /v1/vault/credit-cards
- Example Resopnse
~~~
{
"total_items": 1,
"total_pages": 1,
"items": [
{
"id": "CARD-1SV265177X389440GKLJZIYY",
"state": "ok",
"payer_id": "user12345",
"type": "visa",
"number": "xxxxxxxxxxxx0331",
"expire_month": "11",
"expire_year": "2018",
"first_name": "Joe",
"last_name": "Shopper",
"valid_until": "2017-01-12T00:00:00Z",
"create_time": "2014-01-13T07:23:15Z",
"update_time": "2014-01-13T07:23:15Z",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1SV265177X389440GKLJZIYY",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1SV265177X389440GKLJZIYY",
"rel": "delete",
"method": "DELETE"
},
{
"href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1SV265177X389440GKLJZIYY",
"rel": "patch",
"method": "PATCH"
}
]
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/?page_size=10&sort_by=create_time&sort_order=asc",
"rel": "first",
"method": "GET"
}
]
}
~~~
HTTP Status
如果返回的资源集合为空,即没有任何的资源项,此时也不应该返回404 Not Found
,而应该将items
项设置为空,并且提供一些集合的元信息,譬如total_count
设置为404 Bad Request
。否则应该返回200 OK
来表示成功的返回值。
Read Single Resource
单个资源一般比资源集合中的对应项更详细,同时需要注意
- URI Template
GET /{version}/{namespace}/{resource}/{resource-id}
- Example Request
GET /v1/vault/customers/CUSTOMER-66W27667YB813414MKQ4AKDY
- Example Response
{
"merchant_customer_id": "merchant-1",
"merchant_id": "target",
"create_time": "2014-10-10T16:10:55Z",
"update_time": "2014-10-10T16:10:55Z",
"first_name": "Kartik",
"last_name": "Hattangadi"
}
- HTTP Status
如果指定的资源并不存在,那么应该返回404 Not Found
状态,否则应该返回200 OK
状态码
Update Single Resource
注意,使用create_time
这样系统自动计算的值也可以忽略。
- URI Template
PUT /{version}/{namespace}/{resource}/{resource-id}
- Example Request
PUT /v1/vault/customers/CUSTOMER-66W27667YB813414MKQ4AKDY
{
"merchant_customer_id": "merchant-1",
"merchant_id": "target",
"create_time": "2014-10-10T16:10:55Z",
"update_time": "2014-10-10T16:10:55Z",
"first_name": "Kartik",
"last_name": "Hattangadi"
}
HTTP Status 任何处理失败的请求都应该返回400 Bad Request
, 特别是如果客户端想要更改某个只读的字段,也应该返回400 Bad Request
。如果具体的业务逻辑上存在校验规则,譬如对于数据的类型、长度等等,那么应该提供具体的操作码说明。如果部分场景下需要客户单与其他API 进行交互或者在本次请求之外发出额外的请求,那么应该返回422
状态码,详情可以参考PPaaS Blog on this topic这篇文章。对于其他成功的更新请求,应该返回204 No Content
状态码,即没有任何的返回体。
Update Partial Single Resoure
不同于每次204 No Content
- URI Template
PATCH /{version}/{namespace}/{resource}/{resource-id}
- Example Request
PATCH /v1/notifications/webhooks/52Y53119KP6130839
[
{
"op": "replace",
"path": "/url",
"value": "https://www.yeowza.com/paypal_webhook_new_url"
}
- Example Response
204 No Content
HTTP Status 和PUT 请求一致。
Delete Single Resource
在删除一个资源的时候,为了保证客户端的可重试性,应当将204 No Content
状态码,否则如果你返回的是404 Not Found
可能会让客户端误认为该资源是并不存在,而不是被删除了。应该使用
- URI Template
DELETE /{version}/{namespace}/{resource}/{resource-id}
- Example Request
DELETE /v1/vault/customers/CUSTOMER-66W27667YB813414MKQ4AKDY
204 No Content
Create New Resource
一般来说,创建某个资源的请求体与rel
self
。
- URI Template
POST /{version}/{namespace}/{resource}
- Example Request
~~~
POST /v1/vault/credit-cards
{
"payer_id": "user12345",
"type": "visa",
"number": "4417119669820331",
"expire_month": "11",
"expire_year": "2018",
"first_name": "Betsy",
"last_name": "Buyer",
"billing_address": {
"line1": "111 First Street",
"city": "Saratoga",
"country_code": "US",
"state": "CA",
"postal_code": "95070"
}
}
~~~
- Example Response
~~~
201 Created
{
"id": "CARD-1MD19612EW4364010KGFNJQI",
"valid_until": "2016-05-07T00:00:00Z",
"state": "ok",
"payer_id": "user12345",
"type": "visa",
"number": "xxxxxxxxxxxx0331",
"expire_month": "11",
"expire_year": "2018",
"first_name": "Betsy",
"last_name": "Buyer",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1MD19612EW4364010KGFNJQI",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/vault/credit-cards/CARD-1MD19612EW4364010KGFNJQI",
"rel": "delete",
"method": "DELETE"
}
]
}
~~~
Create New Resource - Consumer Supplied Identifier
当某个
Sub-Resource Collection
在某些情况下,我们可能需要多个标识符来定位到某个资源,这一类资源往往是其他资源的子类。
Cautions
- 多层的资源标识符本身对于
Consumer 而言也是一种负担。- 尽可能地将具有唯一标识符的资源或者没必要指明父资源的资源作为
First-Level Resource 。
- 尽可能地将具有唯一标识符的资源或者没必要指明父资源的资源作为
- 要注意使用多个资源标识符的时候务必不能产生歧义,譬如
/{version}/{namespace}/{resource}/{resource-id}/{sub-resource-id}
这种直接将子资源标识符放在父资源标识符之后的做法就是不合适的,会让Consumer 迷糊。 - 实践中这种资源的层叠嵌套不要超过两层。
- 要保证
API 客户端的可用性,如果在某个URI 中维持大量的层级资源标识符会大大增加复杂度。 - 服务端开发者需要校验每一层级的标识符来判断是否具有访问权限,如果层级过深极易导致复杂度的陡升。
- 要保证
- URI Templates
POST /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}
GET /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}
GET /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}
PUT /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}
DELETE /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}
- Examples
GET /v1/notifications/webhooks/{webhook-id}/event-types
POST /v1/factory/widgets/PART-4312/sub-assemblies
GET /v1/factory/widgets/PART-4312/sub-assemblies/INNER_COG
PUT /v1/factory/widgets/PART-4312/sub-assemblies/INNER_COG
DELETE /v1/factory/widgets/PART-4312/sub-assemblies/INNER_COG
Sub-Resource Singleton
当父子资源之间实际上是一一映射的关系时,可以使用单数形式的资源名来表明多个资源标识符的作用。这种情况下子资源往往也是父资源的一部分,即所谓的被父资源所有。否则子资源应当被放置于独立的资源集合中,并且以其他方式表明父子资源的关联。如果需要创建这种所谓的
- URI Template
GET/PUT /{version}/{namespace}/{resource}/{resource-id}/{sub-resource}
- Examples
GET /v1/customers/devices/DEV-FDU233FDSE213f)/vendor-information
Complex Operation
- URI Template
~~~
POST /{namespace}/{action-resource}
~~~
所谓复杂的操作有时候也被称为controller
或者actions
,务必要审慎地使用,只有在仔细考虑过上文提及的Resource Collection设计并不能满足需要的时候再进行使用。可以参考section 2.6 of the RESTful Web Services Cookbook这一章节来了解更多的关于controller
的概念。复杂操作往往是与
Risks
- 架构设计的可扩展性
_ 一旦这种模式被滥用了,URI 的数量会急剧增长,特别是根级别的Action 可以随着时间疯狂增长。同样的这也会导致路由或者对外提供服务的配置复杂度急速增长。_ URI 无法再被扩展,即不能再使用子资源。 - 可测试性
: 因为缺乏丰富的GET 等读取类操作而使得与 Resource Collection-oriented 模式相比有较大缺陷 - 历史
: 所有对于Action 的调用应该存在某种资源中,譬如/action-resource-history
。
Benefits
- 避免因为短暂性数据而导致资源集合模型的损害。
- 可用性的提升:这种
Action-Oriented 模式能够大大简化客户端交互内容,不过客户端并不能获益于资源本身的可读性
Resource-Oriented Alternative
与上文提及的这种单纯的GET /{actions}
来获取历史记录。这也允许未来基于资源模型的扩展。除此之外,这种模式也能较好地与event sourcing概念相结合。
- URI Template
POST /{version}/{namespace}/{action}
- Example Request
~~~
POST /v1/risk/payment-decisions
{
"code": "h43j5k6iop"
}
~~~
- Example Response
~~~
201 Created
{
"code": "h43j5k6iop",
"status": "APPROVED",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/risk/payment-decisions/ID-FEF8EWR8E9FW)",
"rel": "self",
"method": "GET"
}
]
}
~~~
Complex Operation - Sub-Resource
很多时候我们需要对于资源进行些特定的操作或者状态修正,而这些操作是无法准确的用
一般来说,200 OK
以及资源本身,如果没有任何的资源状态的修正,那么应该返回204 No Content
,并且不应该附上任何的响应体。
- URI Template
POST /{version}/{namespace}/{resource}/{resource-id}/{complex-operation}
- Example Request
~~~
POST /v1/payments/billing-agreements/I-0LN988D3JACS/suspend
{
"note": "Suspending the agreement."
}
~~~
- Example Response
~~~
204 No Content
~~~
不过需要注意的是,虽然这种模式可以改变状态,也并不意味着所有的关于资源的状态改变都要使用所谓的复杂操作模式。简单的状态的改变仍然可以使用
- Example Request (for mixed use of PUT)
~~~
PATCH /v1/payments/billing-agreements/I-0LN988D3JACS
[
{
"op": "replace",
"path": "/",
"value": {
"description": "New Description",
"shipping_address": {
"line1": "2065 Hamilton Ave",
"city": "San Jose",
"state": "CA",
"postal_code": "95125",
"country_code": "US"
}
}
}
]
~~~
Complex Operation - Composite
该系列的操作往往可以在一次请求中处理多个
- URI Template
POST /{version}/{namespace}/{action}
- Example Request
~~~
POST /v1/payments/captures/{capture-id}/refund
~~~
- Example Response
~~~
{
"id": "0P209507D6694645N",
"create_time": "2013-05-06T22:11:51Z",
"update_time": "2013-05-06T22:11:51Z",
"state": "completed",
"amount": {
"total": "110.54",
"currency": "USD"
},
"capture_id": "8F148933LY9388354",
"parent_payment": "PAY-8PT597110X687430LKGECATA",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/refund/0P209507D6694645N",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-8PT597110X687430LKGECATA",
"rel": "parent_payment",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/capture/8F148933LY9388354",
"rel": "capture",
"method": "GET"
}
]
}
~~~
Complex Operation - Transient
这一个类型的复杂操作并不会保留客户端的状态或者创建新的资源。往往就是一个简单的200 OK
这个状态码。
- URI Template
POST /{version}/{namespace}/{action}
- Example Request
POST /v1/risk/evaluate-payment
{
"code": "h43j5k6iop"
}
- Example Response
200 OK
{
"status": "VALID"
}
Complex Operation - Search
在使用Resource Collections的时候,最好是使用查询参数来进行集合内容的过滤。不过有时候我们会需要一些更为复杂的查询语法,单纯的查询参数的方式会导致使用的问题或者受限于查询参数的长度。这时候,应该选择使用
Paging
如果响应体比较大的情况下应当使用分页方式,需要注意的是,next
previous
first
last
- URI Template
POST /{version}/{namespace}/{search-resource}
- Example Request
POST /v1/factory/widgets-search
{
"created_before":"1975-05-13",
"status": "ACTIVE",
"vendor": "Parts Inc."
}
- Example Response
200 OK
{
"items": [
<<lots of part objects here>>
]
"links": [
{
"href": "https://api.sandbox.factory.io/v1/factory/widgets-search?page=2&page_size=10",
"rel": "next",
"method": "POST"
},
{
"href": "https://api.sandbox.factory.io/v1/factory/widgets-search?page=124&page_size=10",
"rel": "last",
"method": "POST"
},
]
}
Read-only resources
在部分需要进行计算或者静态引用的场景下,
- URI Template
GET /{version}/{namespace}/{read-only-resource}
- Example Request
GET /v1/location/geocode?address=77+N.+Washington+Street%2C+Boston%2C+MA%2C+02114