SDK · Go
Go
github.com/Appiasoft/compense-saas-sdk-go 1.1.0: installation, client with functional options, context, parameters, errors with errors.Is, pagination iterators, the webhook subpackage and testing with httptest.
On this page
| Module | github.com/Appiasoft/compense-saas-sdk-go (package compense, subpackage webhook) |
| Version | 1.1.0 (compense.Version) · API v1 |
| Requirements | Go 1.21+ (range-over-func iterators with Go 1.23+) |
| Dependencies | Standard library only |
| Repository | Appiasoft/compense-saas-sdk-go · guides in docs/ · examples in examples/ |
Install #
go get github.com/Appiasoft/compense-saas-sdk-go@v1.1.0
# Private repository: GOPRIVATE=github.com/Appiasoft/* and git credentialsClient #
import compense "github.com/Appiasoft/compense-saas-sdk-go"
client, err := compense.NewClient(os.Getenv("COMPENSE_API_KEY"),
compense.WithEnvironment(compense.Sandbox), // Production (default) | Sandbox | Dev
compense.WithBaseURL("https://gw.example.co"), // another URL; takes precedence over the environment
compense.WithHTTPClient(myHTTPClient), // proxies, mTLS, tracing, tests (not modified)
compense.WithTimeout(30*time.Second), // per attempt (only for the client the SDK creates)
compense.WithRetry(compense.RetryConfig{MaxAttempts: 3}),
compense.WithRetryWrites(false),
compense.WithUserAgent("horizonte-erp/2.0"),
compense.WithLogger(slog.Default()), // deprecation warnings
)
if err != nil {
log.Fatal(err) // *compense.ConfigError
}The *Client is safe for concurrent use: create one per key and reuse it. Every method takes a
context.Context first (deadlines and cancellation, including during the waits between retries).
Conventions #
- Services in
PascalCasewith the same namespaces as Python:client.Wallet.Bills.Pay,client.Admin.HR.CreateNovelty,client.PQRS.Submit. - Optional parameters in
*Paramsstructs (passnilif you have none); empty fields aren’t sent. Optional booleans withcompense.Bool(true). - The
Idempotency-Keygoes as a variadic option:compense.WithIdempotencyKey("..."). - Writes →
*compense.Transaction(Data map[string]any); reads → models (*Balance,*Resolution,*Page,*OffsetPage…) ormap[string]any; every model keepsRaw. - Responses are decoded with
json.Number: numbers keep their exact digits. - Amounts:
compense.Amountaccepts text constants ("65000") orAmountFromInt,AmountFromRatandAmountFromFloat(discouraged). - Decisions:
compense.DecisionApprove/compense.DecisionReject.
Errors #
Every non-2xx response is a *compense.APIError (StatusCode, Title, Detail, TraceID,
Errors(), RetryAfter, Problem). Classify them with errors.Is or the helpers:
| Status | Sentinel | Helper |
|---|---|---|
| 401, 403 | ErrAuth | IsAuthError |
| 404 | ErrNotFound | IsNotFound |
| 409 | ErrIdempotencyConflict | IsIdempotencyConflict |
| 422 | ErrValidation | IsValidationError |
| 429 | ErrRateLimited | IsRateLimited |
| 502, 504 | ErrUpstream | IsUpstreamError |
| other 5xx | ErrServer | IsServerError |
The SDK’s client doesn’t follow redirects: a 3xx arrives as an *APIError.
Pagination #
it := compense.NewPageIterator(ctx, func(ctx context.Context, page int) (*compense.Page, error) {
return client.Webhooks.Deliveries.List(ctx, &compense.DeliveryListParams{Status: "failed", Page: page, PageSize: 100})
})
for it.Next() {
client.Webhooks.Deliveries.Replay(ctx, fmt.Sprint(it.Item()["id"]))
}
if err := it.Err(); err != nil {
log.Fatal(err)
}
// Go 1.23+: for item, err := range it.All() { ... }NewOffsetIterator does the same for limit/offset lists.
Webhooks #
import "github.com/Appiasoft/compense-saas-sdk-go/webhook"
http.Handle("/compense/webhooks", webhook.Handler([]string{newSecret, oldSecret},
func(ctx context.Context, ev *webhook.Event) error {
if ev.IsTest() || alreadyApplied(ev.ID) {
return nil
}
return handle(ev.Event(), ev.Resource())
}))Handler responds 401 if verification fails, 500 if your function returns an error (so Compense
retries) and 200 otherwise. Outside net/http, use webhook.Verify(rawBody, header, secrets, opts...) or webhook.VerifyRequest(r, secrets). Options: WithTolerance, WithoutTolerance,
WithNow. Errors: webhook.ErrVerification (ErrSignature, ErrTimestamp) and ErrNoSecret. See
Verifying the signature.
Testing #
Start an httptest.Server and point the client at it:
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-API-Key") != "cmp_test" {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"total":"150000.00","available_cash":"150000.00","currency":"COP"}`)
}))
defer srv.Close()
client, _ := compense.NewClient("cmp_test", compense.WithBaseURL(srv.URL))
balance, _ := client.Wallet.Balance(context.Background(), "1032456789")
fmt.Println(balance.Available.Amount) // 150000.00