paysafe-go

module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2026 License: MIT

README

paysafe-go

Production-grade, dependency-free Go SDK for the Paysafe API.

Full coverage of the Paysafe developer platform: Payment Handles, Payments, Settlements, Refunds, Payouts (standalone & original credits), Verifications, Customer Vault, the Payment Scheduler (Plans & Subscriptions), merchant onboarding Applications, Value Added Services (FX Rates, Customer Identity/KYC, Bank Account Validation, Interac Verification Service), and webhook signature verification.

Install

go get github.com/iamkanishka/paysafe-go

Requires Go 1.22+. Zero third-party dependencies — standard library only.

Quick start

import "github.com/iamkanishka/paysafe-go/pkg/paysafe"

client, err := paysafe.NewClient(
    paysafe.WithUsername("1001062690"),
    paysafe.WithPassword("B-qa2-0-..."),
    paysafe.WithEnvironment(paysafe.Test),
    paysafe.WithAccountID("1009688230"),
)
if err != nil {
    log.Fatal(err)
}

ctx := context.Background()

handle, err := client.PaymentHandles.Create(ctx, paysafe.CreatePaymentHandleRequest{
    MerchantRefNum:  "order-1",
    Amount:          5000, // $50.00 in minor units
    CurrencyCode:    "USD",
    PaymentType:     "CARD",
    TransactionType: paysafe.TxnPayment,
    Card: &paysafe.Card{
        CardNum:    "4111111111111111",
        CardExpiry: &paysafe.CardExpiry{Month: 12, Year: 2030},
        CVV:        "123",
        HolderName: "Jane Doe",
    },
    BillingDetails: &paysafe.BillingDetails{
        Street: "123 Main St", City: "New York", State: "NY",
        Country: "US", Zip: "10001",
    },
})
if err != nil {
    log.Fatal(err)
}

if action, redirectURL := client.HandleAction(handle); action == paysafe.ActionRedirect {
    // 3DS or an APM requires a customer redirect before continuing.
    fmt.Println("redirect customer to:", redirectURL)
    return
}

payment, err := client.Payments.Create(ctx, paysafe.CreatePaymentRequest{
    MerchantRefNum:     handle.MerchantRefNum,
    Amount:             handle.Amount,
    CurrencyCode:       handle.CurrencyCode,
    PaymentHandleToken: handle.PaymentHandleToken,
})

Or configure from environment variables (PAYSAFE_USERNAME, PAYSAFE_PASSWORD, PAYSAFE_ENVIRONMENT, PAYSAFE_ACCOUNT_ID):

client, err := paysafe.NewClientFromEnv()

See examples/ for runnable end-to-end programs: a basic card payment, a webhook receiver, and a full subscription/recurring-billing flow.

Configuration options

Option Default Description
WithUsername / WithPassword — (required) API credentials from the Paysafe Business Portal
WithEnvironment Test paysafe.Test or paysafe.Production
WithAccountID Default account ID used when a call doesn't specify one
WithBaseURLOverride Override the resolved base URL (mock servers, proxies)
WithTimeout 30s Per-request HTTP timeout
WithMaxRetries 3 Max retries on retryable failures (429/5xx, timeouts, specific API error codes)
WithRetryBaseDelay 500ms Base delay for exponential backoff + jitter
WithRateLimit 100 req/s Local token-bucket rate limit, keyed per credential
WithHTTPClient Supply a custom *http.Client (custom transport, mTLS, test interception)
WithTelemetryPrefix "paysafe" Prefix used in telemetry events

Error handling

Every operation returns (T, error). On failure, error can always be type-asserted to *paysafe.Error for structured detail:

payment, err := client.Payments.Create(ctx, req)
if err != nil {
    if pErr, ok := err.(*paysafe.Error); ok {
        switch pErr.Kind {
        case paysafe.KindAPIError:
            fmt.Println(pErr.Code, pErr.Message, pErr.FieldErrors)
        case paysafe.KindRateLimited:
            // back off and retry later
        case paysafe.KindTimeout, paysafe.KindHTTPError:
            // transport-level issue
        }
    }
}
Kind Meaning
KindAPIError Paysafe returned a structured API error (4xx/5xx with an error body)
KindHTTPError Transport failure (DNS, connection refused, TLS)
KindTimeout Request exceeded the configured timeout
KindRateLimited The local rate limiter rejected the request before sending
KindInvalidConfig Config validation failed at construction
KindInvalidParams Request could not be encoded/built client-side
KindWebhookSignatureMismatch HMAC-SHA256 webhook signature verification failed
KindDecodeError Response (or webhook) body could not be parsed as JSON
KindContextCanceled The caller's context.Context was canceled mid-request/retry

Webhooks

http.HandleFunc("/webhooks/paysafe", func(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    signature := r.Header.Get("Signature")

    event, err := client.Webhooks.VerifyAndParse(body, signature, hmacKey)
    if err != nil {
        http.Error(w, "invalid signature", http.StatusUnauthorized)
        return
    }

    switch event.Topic() {
    case paysafe.TopicPaymentHandle:
        log.Println(event.ResourceID, event.PaymentHandleStatus())
    case paysafe.TopicSubscription:
        // handle recurring billing events
    }

    w.WriteHeader(http.StatusOK)
})

Signature verification uses constant-time comparison (crypto/hmac.Equal) to avoid timing attacks. Always verify the signature before trusting Get-derived status over a webhook-derived one when in doubt — re-fetch the resource via client.PaymentHandles.Get / client.Payments.Get etc. for authoritative state.

Telemetry

Attach a hook to observe every outbound request (start, stop, exception/panic):

type loggingHook struct{ paysafe.NoopTelemetryHook }

func (loggingHook) OnStop(ctx context.Context, meta paysafe.TelemetryStopMeta) {
    log.Printf("%s %s -> %d in %s (ok=%v)", meta.Method, meta.Operation, meta.HTTPStatus, meta.Duration, meta.OK)
}

client, err := paysafe.NewClientWithTelemetry(loggingHook{}, paysafe.WithUsername(...), paysafe.WithPassword(...))

Architecture

The module follows Domain-Driven Design boundaries:

paysafe-go/
├── pkg/paysafe/                  # Public API surface: Client facade + re-exported types/options
├── internal/
│   ├── domain/                   # Pure entities, value objects, enums — no I/O
│   │   ├── shared/                 Error, Link
│   │   ├── payment/                 PaymentHandle, Payment, Settlement, Refund, Payout, Verification
│   │   ├── customer/                 Customer Vault profile & saved instruments
│   │   ├── scheduler/                 Plan, Subscription
│   │   ├── application/               Merchant onboarding Application
│   │   ├── webhook/                    Event, Topic classification
│   │   └── vas/                         FxRate, IdentityProfile, BankVerification
│   ├── application/               # Use-case orchestration / repositories — one package per bounded context
│   └── infrastructure/            # Cross-cutting technical concerns
│       ├── config/                  Config, functional options, URL builders
│       ├── httpclient/               Generic typed HTTP transport (retry, auth, JSON)
│       ├── ratelimiter/               Per-credential token bucket
│       └── telemetry/                 Start/stop/exception event hooks
└── examples/                     # Runnable example programs

internal/ is intentionally inaccessible outside this module — the public API is entirely defined by pkg/paysafe, which re-exports domain types as Go type aliases (zero-cost, no conversion needed) so callers never import internal packages directly.

Testing

go test ./...           # unit + httptest-based integration tests
go test -race ./...     # with the race detector
go vet ./...
gofmt -l .              # should print nothing

The test suite covers: config validation, structured error classification, HMAC webhook verification (success/failure/topic routing), token-bucket rate limiting (including refill-over-time), and end-to-end HTTP behavior via httptest — payment handle creation (sync + redirect), structured API error propagation, exponential-backoff retry on transient failures, and rate-limit rejection.

Important operational notes

  • Refunds are addressed by settlement ID, not payment ID. If SettleWithAuth was true, the settlement ID equals the payment ID. Otherwise use the ID returned by Settlements.Create.
  • Always re-verify via Get after a webhook, especially for asynchronous payment methods — webhook delivery is at-least-once but not guaranteed-ordered.
  • Customer Identity (CustomerIdentity.Rerun) should only be called when Decision == DecisionError (a transient provider issue). A DecisionFail result is final — rerunning returns the same result.
  • Plan amount increases should stay below 20% to comply with card network guidelines on recurring billing.
  • Partial Authorization Service (PAS) (AllowPartialAuth/GroupID on CreatePaymentRequest) requires pre-enablement on the merchant account and is only available for Visa/Mastercard on UK/EEA-acquired merchants.

License

MIT — see LICENSE.

Directories

Path Synopsis
examples
basic_payment command
Command basic_payment demonstrates the minimal card-payment flow: create a Payment Handle, then a Payment.
Command basic_payment demonstrates the minimal card-payment flow: create a Payment Handle, then a Payment.
subscription command
Command subscription demonstrates the recurring billing flow: create a Plan, tokenize a customer's card as a multi-use token via the Customer Vault, and subscribe them to the Plan.
Command subscription demonstrates the recurring billing flow: create a Plan, tokenize a customer's card as a multi-use token via the Customer Vault, and subscribe them to the Plan.
webhook_server command
Command webhook_server demonstrates verifying and routing Paysafe webhook notifications using net/http.
Command webhook_server demonstrates verifying and routing Paysafe webhook notifications using net/http.
internal
application/application
Package application implements the application service for the merchant onboarding (Applications API) bounded context.
Package application implements the application service for the merchant onboarding (Applications API) bounded context.
application/customer
Package customer implements the application service for the Customer Vault bounded context: persisted customer profiles, saved payment handles (multi-use tokens), and single-use customer tokens.
Package customer implements the application service for the Customer Vault bounded context: persisted customer profiles, saved payment handles (multi-use tokens), and single-use customer tokens.
application/payment
Package payment implements the application services (use-case orchestration) for the Payments bounded context: payment handles, payments, settlements, refunds, payouts, and verifications.
Package payment implements the application services (use-case orchestration) for the Payments bounded context: payment handles, payments, settlements, refunds, payouts, and verifications.
application/scheduler
Package scheduler implements the application services for the Payment Scheduler bounded context: recurring billing Plans and Subscriptions.
Package scheduler implements the application services for the Payment Scheduler bounded context: recurring billing Plans and Subscriptions.
application/vas
Package vas implements the application services for the Value Added Services bounded context: FX Rates, Customer Identity (KYC), Bank Account Validation, and the Interac Verification Service.
Package vas implements the application services for the Value Added Services bounded context: FX Rates, Customer Identity (KYC), Bank Account Validation, and the Interac Verification Service.
application/webhook
Package webhook implements the application service for verifying and parsing Paysafe webhook notifications.
Package webhook implements the application service for verifying and parsing Paysafe webhook notifications.
domain/application
Package application contains the merchant onboarding bounded context (Applications API), used by platform partners to programmatically onboard sub-merchants in Canada and the United States.
Package application contains the merchant onboarding bounded context (Applications API), used by platform partners to programmatically onboard sub-merchants in Canada and the United States.
domain/customer
Package customer contains the Customer Vault bounded context: persisted customer profiles, saved payment handles (multi-use tokens), and single-use customer tokens (SUCT).
Package customer contains the Customer Vault bounded context: persisted customer profiles, saved payment handles (multi-use tokens), and single-use customer tokens (SUCT).
domain/payment
Package payment contains the Payments bounded context: payment handles, payments, settlements, refunds, payouts, and verifications.
Package payment contains the Payments bounded context: payment handles, payments, settlements, refunds, payouts, and verifications.
domain/scheduler
Package scheduler contains the Payment Scheduler bounded context: recurring billing Plans and customer Subscriptions.
Package scheduler contains the Payment Scheduler bounded context: recurring billing Plans and customer Subscriptions.
domain/shared
Package shared contains domain primitives shared across all Paysafe bounded contexts: the structured Error type, FieldError, and the small set of helpers used to classify HTTP/API failures.
Package shared contains domain primitives shared across all Paysafe bounded contexts: the structured Error type, FieldError, and the small set of helpers used to classify HTTP/API failures.
domain/vas
Package vas contains the Value Added Services bounded context: FX Rates, Customer Identity (KYC), Bank Account Validation, and the Interac Verification Service (VerifiedMe).
Package vas contains the Value Added Services bounded context: FX Rates, Customer Identity (KYC), Bank Account Validation, and the Interac Verification Service (VerifiedMe).
domain/webhook
Package webhook contains the webhook verification and event-parsing bounded context: HMAC-SHA256 signature verification and typed event classification for Paysafe's asynchronous notifications.
Package webhook contains the webhook verification and event-parsing bounded context: HMAC-SHA256 signature verification and typed event classification for Paysafe's asynchronous notifications.
infrastructure/config
Package config holds the Paysafe client configuration: credentials, environment selection, HTTP tuning knobs, and the base-URL builders used by every bounded context's repository implementation.
Package config holds the Paysafe client configuration: credentials, environment selection, HTTP tuning knobs, and the base-URL builders used by every bounded context's repository implementation.
infrastructure/httpclient
Package httpclient implements the internal transport shared by every Paysafe bounded context's repository: Basic Auth, JSON encoding, exponential-backoff retry on transient failures, token-bucket rate limiting, and telemetry span emission around each call.
Package httpclient implements the internal transport shared by every Paysafe bounded context's repository: Basic Auth, JSON encoding, exponential-backoff retry on transient failures, token-bucket rate limiting, and telemetry span emission around each call.
infrastructure/ratelimiter
Package ratelimiter provides a per-credential token-bucket rate limiter guarding outbound Paysafe API calls, mirroring the ExRated-backed limiter in the reference Elixir implementation.
Package ratelimiter provides a per-credential token-bucket rate limiter guarding outbound Paysafe API calls, mirroring the ExRated-backed limiter in the reference Elixir implementation.
infrastructure/telemetry
Package telemetry provides lightweight, dependency-free instrumentation hooks around every outbound Paysafe API call.
Package telemetry provides lightweight, dependency-free instrumentation hooks around every outbound Paysafe API call.
pkg
paysafe
Package paysafe is the public, production-grade Go client for the Paysafe API.
Package paysafe is the public, production-grade Go client for the Paysafe API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL