什么是第三方API
首先要知道 API 就是应用程序编程接口,就是我们常说的"接口"。在项目内部,后端提供给前端调用的就是接口。而在外部,其他服务商提供给我们的就是第三方接口。
当然,第三方的概念不是绝对的,如果我们自己有个开放平台,向外开放接口,从别人的角度来看我们的就是第三方接口,对自己来讲则是开放 API(Open API)。
API规范
无论是我们自己开发的接口,还是第三方接口,都需要遵循一定的设计规范,例如:
- RESTful API,接口遵循
RESTful
风格。 - 接口安全,包括身份认证,防止重放,签名规则等。
我们以 微信商户平台接口规则 文档为参考,可以看到 API 的规则描述,如:
- 遵循统一的REST的设计风格
- 使用JSON作为数据交互的格式
- 所有的API请求必须使用HTTPS
- 错误码和错误提示
- 证书/密钥/签名介绍
在开发中,我们通常主要关注 API 的基本规则;而对于数据加解密,请求签名与验证签名这类规则,在官方提供的 SDK 中都会集成,比如wechatpay-apache-httpclient
其中的WechatPayHttpClientBuilder
会帮助我们完成身份认证与请求签名。
封装第三方服务接口
当我们需要对接第三方 API 时,都应该封装第三方服务接口,这样不仅能提高代码质量和开发效率,还方便后续升级和维护。
我们以对接微信支付为例,解析在开发工作中如何封装第三方服务接口。
集成SDK
为了减轻开发工作量,集成 SDK 是最省时省力的方法。这里我们引入 微信支付服务端SDK :
xml
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.8</version>
</dependency>
内容拆分
第三方接口文档整个的内容会很多,但总体都是围绕介绍接口的请求与响应。比如微信支付的 JSAPI下单 ,它主要有接口说明、请求参数、应答参数,以及错误码。因此我们在项目代码中可以划分出:
- api,存放接口URL常量
- request,存放请求参数的实体类
- response,存放响应参数的实体类
同时,微信支付 API 还有回调通知,如 支付通知 ,这里也单独划分出:
- notify,存放通知参数的实体类
此外,在开发中我们还要再考虑其他相关的类,比如工具类,配置类等:
- util,存放工具类,比如用于 HTTP 请求,加解密之类的
- config,配置类,用于加载请求必要的身份信息、证书、密钥等属性
- client,客户端,用于发起请求的类,其中定义了各个接口的请求与响应
我们将上面的内容分门别类,划分到不同的包中,在代码中的目录结构中如下:
config
我们先从配置类出发,因为这是开发工作前的一切。对于微信支付来讲,我们要用到的APIv3密钥
,商户API证书
,商户API私钥
等支付相关的都需要提前在商户平台中申请与配置。
以下是微信支付配置类的一个示例:
api
接下来是存放接口URL的常量类。URL中有些是完整的请求地址,也有的是需要拼接参数的字符串模版,比如 微信支付订单号查询订单 ,/v3/pay/transactions/id/{transaction_id}
。我们可以依照官方给的照搬,也可以改成代码适用的,将{transaction_id}
改成%s
。
以下是微信支付 APIv3 的一个示例:
request、response、notify
我们不推荐使用Map
来put
或get
请求参数和响应的数据,因为根据过往开发经历,那样会使得代码冗余冗长,也会对代码阅读造成困扰。因此分别定义了 request、response、notify 这些存放参数相关的实体类。
在查阅微信支付 API 文档时,会发现无论是接口的请求参数还是应答参数,其数据体都是有结构的,比如在 JSAPI下单 请求参数中,除了appid
等外层的数据,还嵌套了detail
数据,而detail
中还继续嵌套了goods_detail
数组类型的数据。其代码片段如下:
json
{
"appid" : "wxd678efh567hg6787",
"mchid" : "1230000109",
"detail" : {
"cost_price" : 608800,
"goods_detail" : [
{
"merchant_goods_id" : "1246464644"
}
]
}
}
在代码中,我们依照官方文档给的数据结构,依样编码实体类,有嵌套的数据的就将其作为嵌套类。需要注意的是,无论内部嵌套多少层,每个类都需要实现Serializable
用于序列化;同时嵌套类修饰为静态的,则是为了便于引用。
以下是JSAPI下单
请求参数的实体类示例:
client
client 就是客户端,是业务代码中向第三方服务 API 发起请求的类,它向下是HttpClient
,向上则是service
层;client 就如工具类一样,其中定义了不同的静态方法,每个方法都对应了一个第三方 API。
以下是微信支付客户端的示例:
在上面的WechatPayClient#prepay
方法中,入参是 request,返回则是接口对应的 response。但是需要注意的是,并不是所有的第三方 API 都会返回有结构数据,比如微信小程序中 获取小程序码 返回参数说明如下:
如果调用成功,会直接返回图片二进制内容,如果请求失败,会返回 JSON 格式的数据。
这样的话,方法的返回参数不能直接用实体类了,而应该用Response
或ResponseBody
接收,之后再返回上层service
处理。例如微信小程序中 获取不限制的小程序码 的方法示例如下:
util
util 用于存放工具类,比如用于 HTTP 请求,加解密,签名验证,证书管理等等的。通常在第三方的 SDK 中都有提供这类工具类,但是为了适配业务,有时候也需要在这些工具类的基础上再封装。
以下是一个用于微信支付 HTTP 请求的工具类示例:
业务对接
当我们完成第三方 API 的封装后,就可以对接业务了。
控制层
这里以下单业务对接微信支付为例,在支付环节中,当用户点击提交订单,此时服务端生成订单后向微信发起预支付(即 JSAPI下单/APP下单/Native下单......),等待预支付成功后返回前端并由前端调起支付面板,用户付款才最终完成支付。
以下是一个预支付接口示例:
服务层
在控制层中,预支付方法只做基本的流程控制,其核心业务(PayService#prepay
)交由服务层处理。核心业务中包括 request 参数实体类的组装,客户端请求的调用(WechatPayClient#prepay
)以及响应数据的处理。
业务代码如下:
总结
经过对第三方服务 API 的封装和业务对接演示,可以看到在业务中整个代码流程清晰顺畅,调用层次简单,基本不会涉及很底层的代码编码。且在业务实现中也无需关心对签名、验签、证书等等的处理,让业务真正回到业务。