Go SDK github.com/watenga-africa/watenga-go

Go 1.23 or later.

Installation

bash
go get github.com/watenga-africa/watenga-go

Before the first release

The package is release-ready; until it is published, install it from a checkout of the Watenga repository:
bash
go mod edit -replace github.com/watenga-africa/watenga-go=../watenga-payment-network/packages/watenga-go

Take a test payment

Use a test secret key (sk_test_…) from the developer console. Test keys never move money: use the test cards and wallet numbers.

go
client := watenga.New(os.Getenv("WATENGA_SECRET_KEY")) // sk_test_...

co, err := client.Checkout.Create(ctx, watenga.CheckoutCreateParams{
    Amount:                "25.00",
    Currency:              "USD",
    MerchantTransactionID: "ORD-1001",
    ReturnURL:             "https://shop.example/return",
})
if err != nil {
    return err
}
http.Redirect(w, r, co.PaymentURL, http.StatusSeeOther)

Receive the webhook

Verify the Watenga-Signature header against the raw request body before you trust an event. While you rotate the endpoint's signing secret, pass both secrets: either one verifies. On your machine, watenga listen --forward-to <your URL> (the CLI) forwards test events signed with a session secret.

go
import "github.com/watenga-africa/watenga-go/webhook"

func handleWebhook(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    event, err := webhook.ConstructEvent(body, r.Header.Get(webhook.SignatureHeader), os.Getenv("WATENGA_WEBHOOK_SECRET"))
    if err != nil {
        http.Error(w, "bad signature", http.StatusBadRequest)
        return
    }
    if event.Type == "payment.capture.completed" {
        var txn watenga.Transaction
        _ = json.Unmarshal(event.Data.Object, &txn)
        // fulfil the order for txn
    }
    w.WriteHeader(http.StatusOK)
}

Errors

Every failed call raises one error type with the stable code, the HTTP status, the request_id to quote to support, the field at fault (param) and the code's page in the error reference.

go
_, err := client.Refunds.Create(ctx, "txn_...", watenga.RefundCreateParams{Amount: watenga.String("5.00"), Reason: "customer_request"})
if apiErr, ok := watenga.AsError(err); ok {
    log.Println(apiErr.Code, apiErr.StatusCode, apiErr.Param, apiErr.RequestID, apiErr.DocURL)
}

Retries and idempotency

Network errors, 429 (after Retry-After) and 5xx answers are retried twice with backoff. Every write carries an Idempotency-Key, kept across those retries, so a retry is answered once and never applied twice; checkout.create uses checkout:<merchant_transaction_id>. See Idempotency.

go
// Every write gets an Idempotency-Key, kept across the SDK's own retries.
// When you retry a call yourself, pass the same key:
client.Refunds.Create(ctx, "txn_...", watenga.RefundCreateParams{Reason: "duplicate"}, watenga.WithIdempotencyKey("refund-ORD-1001"))

Pagination

Every list answers one page, { object: "list", data, has_more, next_cursor }; the auto-paginating form reads them all. See Pagination.

go
page, err := client.Transactions.List(ctx, &watenga.ListParams{Limit: 50, Filters: map[string]string{"status": "completed"}})
// page.Data, page.HasMore, page.NextCursor

for txn, err := range client.Transactions.ListAll(ctx, &watenga.ListParams{Filters: map[string]string{"status": "completed"}}) {
    if err != nil {
        return err
    }
    fmt.Println(txn.ID, txn.Amount, txn.Currency)
}

Resources

The SDK sends Watenga-Version: 2026-10-29; fields use the API's names (snake_case) and money is a decimal string. The same method names in every language.

ResourceMethods
CheckoutCreate, GetStatus
CheckoutSessionsCreate, Retrieve, Expire
TransactionsList, ListAll, Retrieve, GetMobileStatus
RefundsCreate
PaymentLinksCreate, List, ListAll, Retrieve, Update, Deactivate, Clone
InvoicesCreate, List, ListAll, Retrieve, Update, Send, Void
QuotationsCreate, List, ListAll, Retrieve, Update, Issue, Void, ConvertToInvoice
PlansCreate, List, ListAll, Retrieve, Update, Delete
SubscriptionsCreate, List, ListAll, Retrieve, Cancel, Pause, Resume, UpcomingInvoice
CustomersCreate, List, ListAll, Retrieve, Update
PaymentMethodsCreate, List, ListAll, Retrieve, Delete, Charge
AccountGetBalance, GetProfile, GetAPIKey
webhookConstructEvent, ConstructEventWithOptions, GenerateTestSignature

Optional request fields are pointers: watenga.String, watenga.Int64, watenga.Bool.

Refunds and payouts

A secret key refunds when its app's Refunds switch is on and an Admin has set refund limits; a restricted key needs the refunds scope (Refunds). Payouts to your bank are dashboard-only, with two-step verification: they are not in the API or the SDKs.