Go SDK
The Go SDK is the official client for the QairoPay API in Go. It wraps every QairoPay resource with idiomatic Go types, handles HMAC-SHA256 webhook verification, and is safe for concurrent use. Minimum Go version: 1.22.
Installation
go get github.com/qairopay/go-sdkModule path: github.com/qairopay/go-sdk
Full API reference: pkg.go.dev/github.com/qairopay/go-sdk
Initialize a client
package main
import ( "log" "os"
"github.com/qairopay/go-sdk")
func main() { client, err := qairopay.NewClient(os.Getenv("QAIROPAY_API_KEY")) if err != nil { log.Fatal(err) } _ = client}NewClient validates the API key format and returns an error if the key is empty or malformed. The client is safe for concurrent use — create one at startup and share it across goroutines.
Passes
Create a pass
import ( "context" "fmt" "os"
"github.com/qairopay/go-sdk")
ctx := context.Background()
client, _ := qairopay.NewClient(os.Getenv("QAIROPAY_API_KEY"))
pass, err := client.Passes.Create(ctx, qairopay.CreatePassParams{ TemplateID: "tpl_01HXYZ", HolderEmail: "ada@example.com", ExternalID: "membership-42", // optional idempotency anchor})if err != nil { // see Error handling below}fmt.Println(pass.ID, pass.Download.AppleURL)Get a pass
pass, err := client.Passes.Get(ctx, "pss_01HABC")List passes
page, err := client.Passes.List(ctx, qairopay.ListPassesParams{ Status: "installed", Limit: 50,})if err != nil { log.Fatal(err)}for _, p := range page.Data { fmt.Println(p.ID, p.Status)}Pass templates
Create a template
template, err := client.PassTemplates.Create(ctx, qairopay.CreatePassTemplateParams{ Name: "Loyalty Gold", Kind: "loyalty", Brand: &qairopay.Brand{ BackgroundColor: "#1F7A5A", ForegroundColor: "#FCFAF6", },})Get and list templates
template, err := client.PassTemplates.Get(ctx, "tpl_01HXYZ")
page, err := client.PassTemplates.List(ctx, qairopay.ListPassTemplatesParams{ Limit: 25,})Payments
Authorize a payment
payment, err := client.Payments.Authorize(ctx, qairopay.AuthorizeParams{ MemberToken: "member_token_from_pass_scan", MerchantID: "merch_01HABC", AmountCents: 2500, // $25.00 Currency: "USD",})if err != nil { log.Printf("authorization failed: %v", err)}fmt.Println(payment.ID, payment.Status)Get a payment
payment, err := client.Payments.Get(ctx, "pay_01HABC")Webhook subscriptions
Create a subscription
sub, err := client.WebhookSubs.Create(ctx, qairopay.CreateWebhookSubscriptionParams{ URL: "https://hooks.example.com/qairopay", EnabledEvents: []string{ "pass.installed", "pass.revoked", "payment.authorized", "webhook_endpoint.disabled", },})fmt.Println(sub.ID, sub.SigningSecret) // save the secret now — shown onceGet and list subscriptions
sub, err := client.WebhookSubs.Get(ctx, "whe_01HABC")
page, err := client.WebhookSubs.List(ctx, qairopay.ListWebhookSubscriptionsParams{ Limit: 25,})Webhook verification
Always verify the X-QairoPay-Signature header before processing a payload.
package main
import ( "fmt" "io" "net/http" "os"
"github.com/qairopay/go-sdk")
func webhookHandler(w http.ResponseWriter, r *http.Request) { payload, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "cannot read body", http.StatusBadRequest) return }
client, _ := qairopay.NewClient(os.Getenv("QAIROPAY_API_KEY"))
event, err := client.Webhooks.ConstructEvent( payload, r.Header.Get("X-QairoPay-Signature"), os.Getenv("QAIROPAY_WEBHOOK_SECRET"), ) if err != nil { http.Error(w, fmt.Sprintf("signature verification failed: %v", err), http.StatusBadRequest) return }
switch e := event.Data.(type) { case qairopay.PassCreatedEvent: fmt.Printf("pass created: %s\n", e.Pass.ID) case qairopay.PaymentAuthorizedEvent: fmt.Printf("payment authorized: %s, amount: %d\n", e.Payment.ID, e.Payment.AmountCents) }
w.WriteHeader(http.StatusOK)}ConstructEvent signature: (payload []byte, sigHeader, secret string) (Event, error).
Error handling
All API errors implement the *qairopay.APIError type. Type-assert to access the status code and structured fields:
import ( "errors" "fmt"
"github.com/qairopay/go-sdk")
pass, err := client.Passes.Create(ctx, params)if err != nil { var apiErr *qairopay.APIError if errors.As(err, &apiErr) { fmt.Printf("API error %d: %s (request_id: %s)\n", apiErr.StatusCode, apiErr.Message, apiErr.RequestID) }
var rateLimitErr *qairopay.RateLimitError if errors.As(err, &rateLimitErr) { // rateLimitErr.RetryAfter is the recommended wait duration time.Sleep(rateLimitErr.RetryAfter) }
var authErr *qairopay.AuthenticationError if errors.As(err, &authErr) { log.Fatal("invalid API key") }}Context and timeouts
Pass a context to any SDK call to control cancellation and deadlines:
import ( "context" "time")
// Timeout a single callctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()
pass, err := client.Passes.Get(ctx, "pss_01HABC")
// Cancel from a parent context (e.g., HTTP request context)pass, err = client.Passes.Get(r.Context(), "pss_01HABC")The SDK does not set its own timeout — always pass a context with a deadline for production use.