paystack

package module
v1.0.6 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2025 License: MIT Imports: 31 Imported by: 0

README

Paystack Go SDK

A comprehensive, production-ready Go SDK for the Paystack API. Built with idiomatic Go patterns, robust error handling, and extensive test coverage.

Features

  • 🚀 Complete API Coverage - All Paystack endpoints with typed request/response models
  • 🛡️ Type Safety - Strongly typed with custom JSON handling for API quirks
  • 🔧 Flexible Configuration - Custom timeouts, headers, HTTP clients, and base URLs
  • 📝 Fluent Builders - Chainable request builders for clean, readable code
  • 🔐 Webhook Validation - HMAC signature validation with typed event parsing
  • Thoroughly Tested - Comprehensive tests with real JSON fixtures
  • 🌐 Context Aware - All operations support context.Context for cancellation
  • 📚 Rich Documentation - Detailed examples for every package and endpoint

Table of Contents

Installation

go get github.com/huysamen/paystack-go

Requirements: Go 1.21+

Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    paystack "github.com/huysamen/paystack-go"
    "github.com/huysamen/paystack-go/api/transactions"
)

func main() {
    // Create a client with your secret key
    client := paystack.DefaultClient("sk_test_your_secret_key")
    
    ctx := context.Background()
    
    // Verify a transaction
    result, err := client.Transactions.Verify(ctx, "your_transaction_reference")
    if err != nil {
        log.Fatal("HTTP error:", err)
    }
    
    // Check if the transaction was successful
    if err := result.Err(); err != nil {
        log.Fatal("Paystack error:", err)
    }
    
    fmt.Printf("Transaction Status: %s\n", result.Data.Status.String())
    fmt.Printf("Amount: %d kobo\n", result.Data.Amount.Int64())
}

Configuration

Basic Configuration
// Simple client with default settings
client := paystack.DefaultClient("sk_test_your_secret_key")
Advanced Configuration
import (
    "time"
    "net/http"
    paystack "github.com/huysamen/paystack-go"
)

cfg := paystack.NewConfig("sk_test_your_secret_key").
    WithTimeout(30*time.Second).
    WithDefaultHeaders(map[string]string{
        "X-Application": "my-app",
        "X-Version": "1.0.0",
    }).
    WithUserAgentSuffix("MyApp/1.0.0")

// Optional: Use your own HTTP client
customClient := &http.Client{
    Timeout: 45 * time.Second,
    // Add custom transport, proxy settings, etc.
}
cfg = cfg.WithHTTPClient(customClient)

client := paystack.NewClient(cfg)
Configuration Options
Option Description Example
WithTimeout Request timeout 30*time.Second
WithDefaultHeaders Headers added to every request map[string]string{"X-App": "myapp"}
WithUserAgentSuffix Suffix for User-Agent header "MyApp/1.0.0"
WithHTTPClient Custom HTTP client Custom transport, proxy, etc.
WithBaseURL Override API base URL For testing/staging environments

Making Requests

All API calls follow a consistent pattern using fluent request builders:

List Operations with Pagination
import "github.com/huysamen/paystack-go/api/transactions"

// Build query with filters
query := transactions.NewListRequestBuilder().
    PerPage(50).
    Page(1).
    Status("success").
    From(time.Now().Add(-30*24*time.Hour)).
    To(time.Now()).
    Build()

result, err := client.Transactions.List(ctx, *query)
if err != nil {
    // Handle error
}

for _, txn := range result.Data {
    fmt.Printf("Transaction: %s - %d kobo\n", 
        txn.Reference.String(), 
        txn.Amount.Int64())
}
Create Operations
import "github.com/huysamen/paystack-go/api/customers"

request := customers.NewCreateRequestBuilder().
    Email("customer@example.com").
    FirstName("John").
    LastName("Doe").
    Phone("+2348012345678").
    Build()

result, err := client.Customers.Create(ctx, *request)
Context and Cancellation

All operations accept context.Context for timeout and cancellation:

// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

result, err := client.Transactions.Verify(ctx, "ref_123")

Error Handling

The SDK provides two levels of error handling:

1. HTTP/Network Errors
result, err := client.Transactions.Verify(ctx, "ref_123")
if err != nil {
    // Network error, timeout, invalid response, etc.
    log.Fatal("Request failed:", err)
}
2. Paystack API Errors
// Method 1: Check status manually
if !result.Status.Bool() {
    log.Fatal("Paystack error:", result.Message.String())
}

// Method 2: Use convenience method
if err := result.Err(); err != nil {
    log.Fatal("Paystack error:", err)
}
Complete Error Handling Pattern
result, err := client.Transactions.Verify(ctx, "ref_123")
if err != nil {
    return fmt.Errorf("transaction verification failed: %w", err)
}

if err := result.Err(); err != nil {
    return fmt.Errorf("paystack error: %w", err)
}

// Success - use result.Data
transaction := result.Data

Types and JSON Handling

Paystack's API can return inconsistent JSON shapes. The SDK handles this gracefully:

Custom Data Types

The SDK provides custom types in types/data that handle JSON variations:

import "github.com/huysamen/paystack-go/types/data"

// Non-nullable types (convert null to zero value)
var amount data.Int      // Handles: 1000, "1000", null (becomes 0)
var email data.String    // Handles: "test@example.com", null (becomes "")
var active data.Bool     // Handles: true, "true", 1, null (becomes false)

// Nullable types (preserve null state)
var description data.NullString  // Tracks if value was null
var updatedAt data.NullTime      // Can be null or valid time

if description.Valid {
    fmt.Println("Description:", description.String())
} else {
    fmt.Println("No description provided")
}
Metadata Handling
import "github.com/huysamen/paystack-go/types"

// Metadata is flexible and handles various JSON shapes
var meta types.Metadata

// Can handle: null, "", 0, {}, {"key": "value"}
// Only valid objects set Valid=true
if meta.Valid {
    if value, exists := meta.Metadata["custom_field"]; exists {
        fmt.Println("Custom field:", value)
    }
}
Response Variants

Some endpoints return different shapes for the same entity. The SDK handles this with specialized response types:

// When creating a subscription, API returns numeric IDs
createResult, _ := client.Subscriptions.Create(ctx, request)
customerID := createResult.Data.Customer.Int64()  // Numeric ID

// When fetching a subscription, API returns full objects
fetchResult, _ := client.Subscriptions.Fetch(ctx, subscriptionID)
customerEmail := fetchResult.Data.Customer.Email.String()  // Full object

Webhooks

Signature Validation
import "github.com/huysamen/paystack-go/api/webhook"

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    validator := webhook.NewValidator("sk_live_your_secret_key")
    
    event, err := validator.ValidateRequest(r)
    if err != nil {
        http.Error(w, "Invalid signature", http.StatusBadRequest)
        return
    }
    
    // Process the event
    handleEvent(event)
    
    w.WriteHeader(http.StatusOK)
}
Event Processing
import "github.com/huysamen/paystack-go/api/webhook"

func handleEvent(event *webhook.Event) {
    switch event.Event {
    case webhook.EventChargeSuccess:
        data, err := event.AsChargeSuccess()
        if err != nil {
            log.Printf("Failed to parse charge.success: %v", err)
            return
        }
        
        fmt.Printf("Payment successful: %s for %d kobo\n", 
            data.Reference.String(), 
            data.Amount.Int64())
            
    case webhook.EventTransferSuccess:
        data, err := event.AsTransferSuccess()
        if err != nil {
            log.Printf("Failed to parse transfer.success: %v", err)
            return
        }
        
        fmt.Printf("Transfer completed: %s\n", data.TransferCode.String())
        
    case webhook.EventSubscriptionCreate:
        data, err := event.AsSubscriptionCreate()
        if err != nil {
            log.Printf("Failed to parse subscription.create: %v", err)
            return
        }
        
        fmt.Printf("New subscription: %s\n", data.SubscriptionCode.String())
        
    default:
        log.Printf("Unhandled event: %s", event.Event)
    }
}

API Packages

Each API surface has its own package with detailed documentation and examples:

Core APIs
Payment Methods
Transfers and Payouts
Products and Inventory
Account Management
Transaction Management
Bulk Operations
Point of Sale
Integration and Utilities
Webhooks
  • Webhooks - Event validation and processing

Testing

The SDK includes comprehensive tests with real JSON fixtures:

# Run all tests
go test ./...

# Run tests with verbose output
go test -v ./...

# Run tests for a specific package
go test ./api/transactions

# Run tests with coverage
go test -cover ./...
Test Structure
  • Unit tests for every API package and webhook parsing
  • JSON fixtures in resources/examples/ directory
  • Builder tests to verify request construction
  • Response tests to verify JSON unmarshaling

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request
Development Setup
git clone https://github.com/huysamen/paystack-go.git
cd paystack-go
go mod download
go test ./...

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.


Need help? Check the individual package READMEs for detailed examples, or review the test files for comprehensive usage patterns.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	Transactions            *transactions.Client
	Plans                   *plans.Client
	Products                *products.Client
	PaymentPages            *paymentpages.Client
	PaymentRequests         *paymentrequests.Client
	Customers               *customers.Client
	Subscriptions           *subscriptions.Client
	Transfers               *transfers.Client
	TransferControl         *transferscontrol.Client
	TransferRecipients      *transferrecipients.Client
	BulkCharges             *bulkcharges.Client
	Charges                 *charge.Client
	Disputes                *disputes.Client
	Refunds                 *refunds.Client
	Subaccounts             *subaccounts.Client
	Settlements             *settlements.Client
	Miscellaneous           *miscellaneous.Client
	Verification            *verification.Client
	TransactionSplits       *transactionsplits.Client
	Terminal                *terminal.Client
	VirtualTerminal         *virtualterminal.Client
	DirectDebit             *directdebit.Client
	DedicatedVirtualAccount *dedicatedvirtualaccounts.Client
	ApplePay                *applepay.Client
	Integration             *integration.Client
}

func DefaultClient

func DefaultClient(secretKey string) *Client

DefaultClient creates a new client with default configuration

func NewClient

func NewClient(config *Config) *Client

NewClient creates a new Paystack client with the given configuration

type Config

type Config struct {
	// SecretKey is the Paystack secret key
	SecretKey string

	// Environment determines which API endpoint to use
	Environment Environment

	// HTTPClient is the HTTP client to use for requests
	// If nil, a default client will be created
	HTTPClient *http.Client

	// Timeout for HTTP requests
	Timeout time.Duration

	// BaseURL allows overriding the API base URL (useful for testing)
	BaseURL string

	// DefaultHeaders are added to every request. Values here override SDK defaults
	// if the same header is set (e.g. Accept, User-Agent).
	DefaultHeaders map[string]string

	// UserAgentSuffix allows applications to append an identifier to the default
	// SDK user agent, e.g. "myapp/1.2.3".
	UserAgentSuffix string
}

Config holds configuration for the Paystack client

func NewConfig

func NewConfig(secretKey string) *Config

NewConfig creates a new configuration with sensible defaults

func (*Config) GetBaseURL

func (c *Config) GetBaseURL() string

GetBaseURL returns the appropriate base URL for the environment

func (*Config) WithBaseURL

func (c *Config) WithBaseURL(baseURL string) *Config

WithBaseURL sets a custom base URL

func (*Config) WithDefaultHeaders

func (c *Config) WithDefaultHeaders(headers map[string]string) *Config

WithDefaultHeaders sets default headers to be added to every request

func (*Config) WithEnvironment

func (c *Config) WithEnvironment(env Environment) *Config

WithEnvironment sets the environment

func (*Config) WithHTTPClient

func (c *Config) WithHTTPClient(client *http.Client) *Config

WithHTTPClient sets a custom HTTP client

func (*Config) WithTimeout

func (c *Config) WithTimeout(timeout time.Duration) *Config

WithTimeout sets the request timeout

func (*Config) WithUserAgentSuffix

func (c *Config) WithUserAgentSuffix(suffix string) *Config

WithUserAgentSuffix sets an optional suffix appended to the SDK User-Agent

type Environment

type Environment string

Environment represents the Paystack API environment

const (
	// EnvironmentProduction uses the live Paystack API
	EnvironmentProduction Environment = "production"
	// EnvironmentSandbox uses the test Paystack API (not officially supported by Paystack, but useful for testing)
	EnvironmentSandbox Environment = "sandbox"
)

Directories

Path Synopsis
api
Package optional provides utility functions for implementing functional options pattern across the Paystack Go SDK.
Package optional provides utility functions for implementing functional options pattern across the Paystack Go SDK.

Jump to

Keyboard shortcuts

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