Go
可并发复用的服务端客户端:Customer、持有、权益检查与用量上报。
API v1更新于
安装
客户端
package main import ( "context" "log" "os" indiana "tinker.cash/sdk/go") func main() { client, err := indiana.NewClient(indiana.Config{ APIKey: os.Getenv("INDIANA_API_KEY"), ProjectID: os.Getenv("INDIANA_PROJECT_ID"), Environment: indiana.Sandbox, BaseURL: os.Getenv("INDIANA_API_URL"), }) if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() customer, err := client.GetOrCreateCustomer(ctx, indiana.CreateCustomerParams{ ExternalUserID: indiana.Ptr("merchant-user-123"), }) if err != nil { log.Fatal(err) } access, err := client.CheckEntitlement(ctx, indiana.CheckEntitlementParams{ CustomerID: customer.Customer.ID, Requirement: indiana.CheckEntitlementParamsRequirement{ InternalFeatureID: os.Getenv("INDIANA_FEATURE_ID"), Quantity: "1", Properties: map[string]any{}, }, }) if err != nil { log.Fatal(err) } if !access.Check.Allowed { return } usage, err := client.TrackUsage(ctx, indiana.TrackUsageParams{ CustomerID: customer.Customer.ID, Command: indiana.TrackUsageParamsCommand{ EventName: indiana.Ptr("image_generated"), Quantity: indiana.Ptr("1.25"), IdempotencyKey: "merchant-event-123", }, }) if err != nil { log.Fatal(err) } for _, receipt := range usage.Receipts { log.Println("usage accepted:", receipt.Accepted) }}nil指针表示省略可选字段;用indiana.Ptr(value)显式传空字符串、空 map 或零值。- 响应结构与 JSON 名称由公开契约生成,字段使用 Go 缩写,如
CustomerID、RequestID。数量与金额保持十进制字符串。 indiana.LookupByID(id)与indiana.LookupByExternalUserID(id)构造GetCustomer/UpdateCustomer的查找条件。
方法
| 方法 | 范围 | 权限 |
|---|---|---|
GetOrCreateCustomer | ExternalUserID 或 email;可选资料与 fingerprint,已有资料不变。 | customers:write |
GetCustomer | LookupByID 或 LookupByExternalUserID。 | customers:read |
ListCustomers | 显式筛选、offset 与 limit;检查 HasMore。 | customers:read |
UpdateCustomer | 查找条件、保留的 RequestID 与部分 Patch;标签用 TagChanges 增量修改。 | customers:write |
ReadHoldings | Customer ID,可选 Entity ID。 | entitlements:read |
CheckEntitlement | Customer 与正数十进制字符串的 Feature 需求。 | entitlements:read |
TrackUsage | Customer 与命令:EventName 与 InternalFeatureID 二选一,外加保留的 IdempotencyKey。 | credits:consume |
所有方法接收 `context.Context`,返回响应指针和 `error`。
GetOrCreateCustomer- 范围
ExternalUserID或 email;可选资料与 fingerprint,已有资料不变。- 权限
customers:write
GetCustomer- 范围
LookupByID或LookupByExternalUserID。- 权限
customers:read
ListCustomers- 范围
- 显式筛选、offset 与 limit;检查
HasMore。 - 权限
customers:read
UpdateCustomer- 范围
- 查找条件、保留的
RequestID与部分Patch;标签用TagChanges增量修改。 - 权限
customers:write
ReadHoldings- 范围
- Customer ID,可选 Entity ID。
- 权限
entitlements:read
CheckEntitlement- 范围
- Customer 与正数十进制字符串的 Feature 需求。
- 权限
entitlements:read
TrackUsage- 范围
- Customer 与命令:
EventName与InternalFeatureID二选一,外加保留的IdempotencyKey。 - 权限
credits:consume
并发、超时与错误
一个客户端可被多个 goroutine 复用。取消与截止时间使用普通的 context。Config.Timeout 限制整个请求与响应读取(默认 10 秒,最大 120 秒)。Close() 拒绝新调用并释放空闲连接;注入的 HTTPClient 会被复制,Transport 仍归调用方所有。不使用环境代理、cookie 和重定向。
package billing import ( "context" "errors" "log" indiana "tinker.cash/sdk/go") func readHoldings(ctx context.Context, client *indiana.Client, customerID string) error { _, err := client.ReadHoldings(ctx, indiana.ReadHoldingsParams{CustomerID: customerID}) var sdkError *indiana.Error if errors.As(err, &sdkError) { log.Println(sdkError.Code, sdkError.Status, sdkError.RequestID, sdkError.Recovery) } if errors.Is(err, context.Canceled) { // The caller cancelled. An accepted write may still have committed. } return err}indiana.Error 提供 Code、Status、RequestID、Retryable、Recovery,以及可选的 Reason 和 RetryAfterSeconds。SDK 不自动重试;用量事件与 Customer 更新在结果未知时重放原 IdempotencyKey/RequestID 与原输入。
调用其他操作
Go SDK 覆盖 Customer、持有、权益检查与用量上报七项操作。会话签发、支付、优惠、发票、余额管理与 Funnel 决策等其他服务端操作可以直接调用 HTTP API:使用同一把 Server Key、X-Indiana-Protocol: 0.6 与 JSON 请求体,按错误对象的 code 和 recovery 处理失败。
package sessions import ( "bytes" "context" "encoding/json" "fmt" "net/http" "os") // IssueCustomerToken calls an operation outside the SDK (sessions:create).func IssueCustomerToken(ctx context.Context, customerID string) (string, error) { body, err := json.Marshal(map[string]string{"customerId": customerID}) if err != nil { return "", err } request, err := http.NewRequestWithContext(ctx, http.MethodPost, os.Getenv("INDIANA_API_URL")+"/api/v1/customer-sessions", bytes.NewReader(body)) if err != nil { return "", err } request.Header.Set("Authorization", "Bearer "+os.Getenv("INDIANA_API_KEY")) request.Header.Set("X-Indiana-Protocol", "0.6") request.Header.Set("Content-Type", "application/json") response, err := http.DefaultClient.Do(request) if err != nil { return "", err } defer response.Body.Close() var result struct { CustomerToken string `json:"customerToken"` Error *struct { Code string `json:"code"` } `json:"error"` } if err := json.NewDecoder(response.Body).Decode(&result); err != nil { return "", err } if result.Error != nil { return "", fmt.Errorf("indiana: %s", result.Error.Code) } return result.CustomerToken, nil}