Skip to content
Ask the docs

Find answers across the QairoPay docs.

Type a question and we'll synthesize an answer from the docs with citations back to the source pages.

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

Terminal window
go get github.com/qairopay/go-sdk

Module path: github.com/qairopay/go-sdk

Full API reference: pkg.go.dev/github.com/qairopay/go-sdk

Initialize a client

main.go
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

passes.go
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

passes.go
pass, err := client.Passes.Get(ctx, "pss_01HABC")

List passes

passes.go
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

templates.go
template, err := client.PassTemplates.Create(ctx, qairopay.CreatePassTemplateParams{
Name: "Loyalty Gold",
Kind: "loyalty",
Brand: &qairopay.Brand{
BackgroundColor: "#1F7A5A",
ForegroundColor: "#FCFAF6",
},
})

Get and list templates

templates.go
template, err := client.PassTemplates.Get(ctx, "tpl_01HXYZ")
page, err := client.PassTemplates.List(ctx, qairopay.ListPassTemplatesParams{
Limit: 25,
})

Payments

Authorize a payment

payments.go
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

payments.go
payment, err := client.Payments.Get(ctx, "pay_01HABC")

Webhook subscriptions

Create a subscription

webhooks.go
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 once

Get and list subscriptions

webhooks.go
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.

webhook_handler.go
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:

errors.go
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:

context.go
import (
"context"
"time"
)
// Timeout a single call
ctx, 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.