生成 Delphi Stripe 客户端
Stripe 为自己的 API 发布并维护一份官方的 OpenAPI 3 描述。sgcOpenAPI 提供的不是手写的 Stripe 组件,而是一个生成器。对该规范运行一次 sgcOpenAPI.exe,您就得到一个 Pascal 单元,其中每个操作一个方法,每个操作一个类型化的响应类,还有一个 GetOpenAPIClient 函数,直接交给您一个可用的客户端。
Stripe 为自己的 API 发布并维护一份官方的 OpenAPI 3 描述。sgcOpenAPI 提供的不是手写的 Stripe 组件,而是一个生成器。对该规范运行一次 sgcOpenAPI.exe,您就得到一个 Pascal 单元,其中每个操作一个方法,每个操作一个类型化的响应类,还有一个 GetOpenAPIClient 函数,直接交给您一个可用的客户端。
下面的数字来自对当前 spec3.json 运行生成器并编译其结果的实测,不是估算。
github.com/stripe/openapi 中的 openapi/spec3.json,声明为 OpenAPI 3.0.0,无需任何转换步骤。
419 条路径变成 594 个方法和 594 个响应类,另有 1,747 个模型类,全部位于一个约 110,000 行的单元中。
使用 -a 2 生成,并在运行时设置 Authentication.Token.BearerToken。客户端随后会在每个请求上发送 Authorization: Bearer。
生成的单元在 RAD Studio 12 的 Win32 平台上干净编译通过,库路径上除了 sgcOpenAPI 的 Source 文件夹之外无需任何内容。
从 Stripe 的公共仓库下载 spec3.json,或者把原始 URL 直接传给 -i。-i 和 -o 两个开关是必需的,其余都有默认值。
> sgcOpenAPI.exe -i "spec3.json" -o "stripe.pas" -a 2 File successfully created stripe.pas
-i 接受本地文件或 URL,JSON 和 YAML 都可以。-o 是要写出的 Pascal 单元,单元名取自该文件名。-a 2 选择令牌认证,这正是 Stripe 的密钥所需要的。同一个可执行文件在不带参数启动时是一个图形向导。运行成功时退出码为 0,构建脚本可以检查 5(输入文件)、6(输出文件)或 7(该文档无法转换为有效的 OpenAPI 3 文档)。
把生成的 .pas 加入项目,写进 uses 子句,集成工作就到此为止。没有需要安装的组件,因为 sgcOpenAPI 不注册任何组件,也不提供设计期包。
在客户端上设置一次密钥,然后调用生成器按操作 id 命名的方法。Stripe 的操作 id 本身就是合法的 Pascal 标识符,因此您得到的正是 PostCharges。
uses stripe; // 刚刚生成的单元 procedure TfrmStripe.btnChargeClick(Sender: TObject); var oResponse: TsgcOpenAPI_PostCharges_Response; begin GetOpenAPIClient.Authentication.Token.BearerToken := 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'; oResponse := GetOpenAPIClient.PostCharges( 'amount=2000¤cy=usd&source=tok_visa&description=Order+1234'); try if oResponse.IsSuccessful then memoLog.Lines.Text := 'charge : ' + oResponse.Successful.Id + #13#10 + 'status : ' + oResponse.Successful.Status + #13#10 + 'paid : ' + BoolToStr(oResponse.Successful.Paid, True) else memoLog.Lines.Text := IntToStr(oResponse.ResponseCode) + ' ' + oResponse.ResponseError; finally oResponse.Free; end; end;
GetOpenAPIClient 不带参数,返回的客户端无需您释放。基础 URL 来自规范中的 servers 条目,因此生成的构造函数已经设好 https://api.stripe.com/,只有在生成时用 -u 或运行时用 SetBaseURL 才会覆盖它。响应对象归您所有,示例因此使用了 try finally。IsSuccessful 在状态码 200 到 299 时为 true,其余信息由 ResponseCode 和 ResponseError 承载。
这是 Stripe 最让人意外的一点,它来自规范本身,而不是生成器。
var oCustomer: TsgcOpenAPI_PostCustomers_Response; oSub: TsgcOpenAPI_PostSubscriptions_Response; begin oCustomer := GetOpenAPIClient.PostCustomers( 'email=jane@example.com&payment_method=pm_card_visa'); try if not oCustomer.IsSuccessful then raise Exception.Create(oCustomer.ResponseError); oSub := GetOpenAPIClient.PostSubscriptions( 'customer=' + oCustomer.Successful.Id + '&items[0][price]=price_1JxYzZAbCdEfGhIj'); try memoLog.Lines.Add(oSub.Successful.Id); finally oSub.Free; end; finally oCustomer.Free; end; end;
Stripe 规范中全部 593 个请求体都声明为 application/x-www-form-urlencoded,因此生成的参数是 const aBody: string,表单要由您自己按 Stripe 的方括号写法拼出来。响应则是另一回事:它们用具名模式声明,因此每一个都会变成一个类,您通过属性读取它。
单元是文档的镜像。没有任何人工筛选,因此 Stripe 描述过的内容都在,Stripe 没有写的内容也就没有。
共 594 个,名称取自操作 id,其中不能出现在 Pascal 标识符里的字符会被去掉。-m 1 改用摘要命名,-m 2 则用端点命名。
TsgcOpenAPI_PostCharges_Response 继承自 TsgcOpenAPIResponse,带有 Successful,以及每个已声明错误状态各一个属性,并继承 IsSuccessful、ResponseCode 和 ResponseError。
Stripe 声明的每一个模式,包括共享的 error 对象,charge、customer、invoice 和 subscription 对象,以及事件负载。
可选查询参数按声明顺序变成带默认值的参数,因此 GetCharges 接受 aCreated、aCustomer、aEnding_before、aExpand、aLimit 等等,您完全不必自己拼 URL。
文档中的标签会输出为注释,把方法在同一个类里分组。它们不会变成独立的类,因此一切都挂在 GetOpenAPIClient 之下。
Stripe 自己的描述会作为 Pascal 注释保留在每个方法和属性上方,除非您关掉它们。
这四点都来自对当前规范的一次真实生成运行。
大约 110,000 行、5.5 MB。它编译很快,但 IDE 的代码编辑器处理这么大的文件会变慢。-x 会剔除您以 "VERB endpoint" 形式列出的操作,随后 -p 移除剩余操作都不再用到的类,这决定了单元是打得开还是打不开。
每一条都与组合有关。Stripe 在很多地方使用 anyOf 和 oneOf 却没有判别符映射,因此生成的类为每个分支保留一个成员,由您的代码判断哪一个被填充。生成器会逐个模式说明这一点,而不是默默替您选一个。
POST /v1/files 是文档中唯一的 multipart/form-data 操作,生成的 PostFiles 只接受 aExpand。如果需要上传,请直接使用 TsgcHTTP1Client 或文件上传 API。
Stripe 为 API 划分版本,并经常修订规范。把您用来生成的 spec3.json 固定下来,和项目放在一起,并在需要时有意识地重新生成。生成器是确定性的,同一份文档给出同一个单元。