PayGate

Go SDK

Official Go SDK for PayGate.

Go SDK

The official Go SDK for PayGate provides a convenient way to access the PayGate API from Go applications.

Requirements: Go 1.18 or later.

Installation

go get github.com/paygate/paygate-go

Quick Start

package main

import (
    "fmt"
    "github.com/paygate/paygate-go"
)

func main() {
    client := paygate.New("sk_test_...")

    // Create a payment
    payment, err := client.Payments.Create(&paygate.PaymentParams{
        Amount:        5000,
        Currency:      "GHS",
        PaymentMethod: "mobile_money",
        Provider:      "mtn",
        Phone:         "0241234567",
    })
    if err != nil {
        panic(err)
    }

    fmt.Println(payment.ID)
}

Configuration

API Key

import (
    "os"
    "github.com/paygate/paygate-go"
)

// Using environment variable (recommended)
client := paygate.New(os.Getenv("PAYGATE_SECRET_KEY"))

// Or pass directly
client := paygate.New("sk_test_...")

Options

client := paygate.New("sk_test_...",
    // API version (defaults to latest)
    paygate.WithAPIVersion("2024-01-01"),

    // Request timeout (default: 30s)
    paygate.WithTimeout(60 * time.Second),

    // Maximum retries for failed requests (default: 3)
    paygate.WithMaxRetries(5),

    // Custom base URL (for testing)
    paygate.WithBaseURL("https://api.sandbox.paygate.com.gh"),

    // Custom HTTP client
    paygate.WithHTTPClient(&http.Client{}),
)

Payments

Create a Payment

payment, err := client.Payments.Create(&paygate.PaymentParams{
    Amount:        5000,
    Currency:      "GHS",
    PaymentMethod: "mobile_money",
    Provider:      "mtn",
    Phone:         "0241234567",
    Description:   "Order #1234",
    Metadata: map[string]string{
        "order_id":       "1234",
        "customer_email": "customer@example.com",
    },
})
if err != nil {
    // Handle error
}

fmt.Println(payment.ID)
fmt.Println(payment.Status) // "pending"

Retrieve a Payment

payment, err := client.Payments.Retrieve("pay_abc123")
if err != nil {
    // Handle error
}

List Payments

// Get a page of payments
params := &paygate.PaymentListParams{
    Limit:  10,
    Status: "succeeded",
}

payments, err := client.Payments.List(params)
if err != nil {
    // Handle error
}

for _, payment := range payments.Data {
    fmt.Println(payment.ID)
}

// Check for more pages
if payments.HasMore {
    params.StartingAfter = payments.Data[len(payments.Data)-1].ID
    nextPage, _ := client.Payments.List(params)
}

Cancel a Payment

payment, err := client.Payments.Cancel("pay_abc123")
if err != nil {
    // Handle error
}

Customers

Create a Customer

customer, err := client.Customers.Create(&paygate.CustomerParams{
    Email: "customer@example.com",
    Name:  "John Doe",
    Phone: "0241234567",
    Metadata: map[string]string{
        "user_id": "123",
    },
})

Update a Customer

customer, err := client.Customers.Update("cus_abc123", &paygate.CustomerParams{
    Name: "Jane Doe",
})

Subscriptions

Create a Subscription

subscription, err := client.Subscriptions.Create(&paygate.SubscriptionParams{
    Customer: "cus_abc123",
    Plan:     "plan_xyz789",
    PaymentMethod: &paygate.PaymentMethodParams{
        Type:     "mobile_money",
        Phone:    "0241234567",
        Provider: "mtn",
    },
})

Cancel a Subscription

// Cancel at period end
_, err := client.Subscriptions.Update("sub_abc123", &paygate.SubscriptionParams{
    CancelAtPeriodEnd: true,
})

// Cancel immediately
_, err := client.Subscriptions.Cancel("sub_abc123")

Webhooks

Verify Webhook Signature

package main

import (
    "encoding/json"
    "io"
    "net/http"

    "github.com/paygate/paygate-go"
    "github.com/paygate/paygate-go/webhook"
)

const endpointSecret = "whsec_..."

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    payload, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Error reading body", http.StatusBadRequest)
        return
    }

    signature := r.Header.Get("X-PayGate-Signature")

    event, err := webhook.ConstructEvent(payload, signature, endpointSecret)
    if err != nil {
        http.Error(w, "Invalid signature", http.StatusBadRequest)
        return
    }

    // Handle the event
    switch event.Type {
    case "payment.succeeded":
        var payment paygate.Payment
        if err := json.Unmarshal(event.Data.Raw, &payment); err != nil {
            http.Error(w, "Error parsing payment", http.StatusBadRequest)
            return
        }
        fmt.Printf("Payment %s succeeded!\n", payment.ID)
        // Fulfill the order

    case "payment.failed":
        fmt.Println("Payment failed")
        // Notify customer

    case "subscription.created":
        fmt.Println("Subscription created")
        // Handle new subscription

    default:
        fmt.Printf("Unhandled event type: %s\n", event.Type)
    }

    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(map[string]bool{"received": true})
}

func main() {
    http.HandleFunc("/webhooks", webhookHandler)
    http.ListenAndServe(":3000", nil)
}

Gin Framework Example

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/paygate/paygate-go/webhook"
)

const endpointSecret = "whsec_..."

func main() {
    r := gin.Default()

    r.POST("/webhooks", func(c *gin.Context) {
        payload, _ := c.GetRawData()
        signature := c.GetHeader("X-PayGate-Signature")

        event, err := webhook.ConstructEvent(payload, signature, endpointSecret)
        if err != nil {
            c.JSON(400, gin.H{"error": "Invalid signature"})
            return
        }

        switch event.Type {
        case "payment.succeeded":
            // Handle payment success
        case "payment.failed":
            // Handle payment failure
        }

        c.JSON(200, gin.H{"received": true})
    })

    r.Run(":3000")
}

Error Handling

import (
    "errors"
    "github.com/paygate/paygate-go"
)

client := paygate.New("sk_test_...")

payment, err := client.Payments.Create(&paygate.PaymentParams{
    Amount:        5000,
    Currency:      "GHS",
    PaymentMethod: "mobile_money",
    Provider:      "mtn",
    Phone:         "0241234567",
})

if err != nil {
    var paygateErr *paygate.Error
    if errors.As(err, &paygateErr) {
        switch paygateErr.Type {
        case paygate.ErrorTypeAuthentication:
            fmt.Println("Check your API key")

        case paygate.ErrorTypeInvalidRequest:
            fmt.Printf("Invalid parameter: %s\n", paygateErr.Param)

        case paygate.ErrorTypePayment:
            switch paygateErr.Code {
            case "insufficient_funds":
                fmt.Println("Please top up your account")
            case "phone_unreachable":
                fmt.Println("Please check your phone")
            default:
                fmt.Println(paygateErr.Message)
            }

        case paygate.ErrorTypeRateLimit:
            time.Sleep(60 * time.Second)
            // Retry

        case paygate.ErrorTypeAPI:
            fmt.Printf("Server error: %s\n", paygateErr.Message)
        }
    } else {
        // Network error or other
        fmt.Printf("Unexpected error: %v\n", err)
    }
    return
}

fmt.Println(payment.ID)

Error Types

TypeDescription
ErrorTypeAuthenticationInvalid API key
ErrorTypeInvalidRequestInvalid parameters
ErrorTypePaymentPayment-specific error
ErrorTypeRateLimitToo many requests
ErrorTypeAPIServer error

Idempotency

Use idempotency keys to safely retry requests:

payment, err := client.Payments.Create(&paygate.PaymentParams{
    Amount:        5000,
    Currency:      "GHS",
    PaymentMethod: "mobile_money",
    Provider:      "mtn",
    Phone:         "0241234567",
}, paygate.WithIdempotencyKey("order_1234_payment"))

Context Support

All methods accept a context for cancellation and timeouts:

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

payment, err := client.Payments.CreateWithContext(ctx, &paygate.PaymentParams{
    Amount:        5000,
    Currency:      "GHS",
    PaymentMethod: "mobile_money",
    Provider:      "mtn",
    Phone:         "0241234567",
})

Pagination with Iterators

// Iterate through all payments
iter := client.Payments.Iter(&paygate.PaymentListParams{
    Status: "succeeded",
})

for iter.Next() {
    payment := iter.Payment()
    fmt.Println(payment.ID)
}

if err := iter.Err(); err != nil {
    // Handle error
}

Invoices

Create an Invoice

invoice, err := client.Invoices.Create(&paygate.InvoiceParams{
    Customer: "cus_abc123",
    Items: []paygate.InvoiceItemParams{
        {Description: "Consulting Services", Amount: 50000},
        {Description: "Development Work", Amount: 150000},
    },
    DueDate: "2024-02-15",
    Metadata: map[string]string{
        "project": "Website Redesign",
    },
})

Send an Invoice

// Send invoice via email/SMS
err := client.Invoices.Send("inv_abc123")

Pay an Invoice

invoice, err := client.Invoices.Pay("inv_abc123", &paygate.InvoicePayParams{
    PaymentMethod: &paygate.PaymentMethodParams{
        Type:     "mobile_money",
        Phone:    "0241234567",
        Provider: "mtn",
    },
})

List Invoices

invoices, err := client.Invoices.List(&paygate.InvoiceListParams{
    Status: "open",
    Limit:  10,
})
for _, inv := range invoices.Data {
    fmt.Printf("%s: %.2f %s\n", inv.ID, float64(inv.AmountDue)/100, inv.Currency)
}

Coupons

Create a Coupon

// Percentage discount
coupon, err := client.Coupons.Create(&paygate.CouponParams{
    Code:             "SAVE20",
    Type:             "percent",
    PercentOff:       20,
    Duration:         "repeating",
    DurationInMonths: 3,
    MaxRedemptions:   100,
})

// Fixed amount discount
coupon, err := client.Coupons.Create(&paygate.CouponParams{
    Code:      "FLAT50",
    Type:      "amount",
    AmountOff: 5000, // 50 GHS in pesewas
    Currency:  "GHS",
    Duration:  "once",
})

Validate a Coupon

validation, err := client.Coupons.Validate("SAVE20")
if err != nil {
    fmt.Println("Coupon is invalid or expired")
    return
}
if validation.Valid {
    fmt.Printf("Discount: %d%%\n", validation.PercentOff)
}

List Coupons

coupons, err := client.Coupons.List(&paygate.CouponListParams{Limit: 10})
for _, coupon := range coupons.Data {
    fmt.Printf("%s: %d/%d redemptions\n", coupon.Code, coupon.TimesRedeemed, coupon.MaxRedemptions)
}

QR Codes

Create a Static QR Code

// Reusable payment QR code
qrCode, err := client.QRCodes.Create(&paygate.QRCodeParams{
    Type:        "static",
    Name:        "Store Counter QR",
    Description: "Scan to pay at checkout",
})
fmt.Printf("QR Image URL: %s\n", qrCode.ImageURL)

Create a Dynamic QR Code

// QR code for specific payment
qrCode, err := client.QRCodes.Create(&paygate.QRCodeParams{
    Type:        "dynamic",
    Amount:      5000,
    Currency:    "GHS",
    Description: "Order #1234",
    ExpiresIn:   3600, // 1 hour
})
fmt.Printf("QR Image URL: %s\n", qrCode.ImageURL)

Retrieve QR Code Status

qrCode, err := client.QRCodes.Retrieve("qr_abc123")
fmt.Printf("Status: %s\n", qrCode.Status)
fmt.Printf("Times Used: %d\n", qrCode.TimesUsed)

Reports

Generate a Report

report, err := client.Reports.Create(&paygate.ReportParams{
    Type: "payments_summary",
    Parameters: map[string]string{
        "start_date": "2024-01-01",
        "end_date":   "2024-01-31",
        "group_by":   "day",
    },
    Format: "csv",
})
fmt.Printf("Report ID: %s\n", report.ID)

Retrieve Report Status

for {
    report, err := client.Reports.Retrieve("report_abc123")
    if err != nil {
        break
    }
    if report.Status == "ready" {
        fmt.Printf("Download URL: %s\n", report.DownloadURL)
        break
    }
    time.Sleep(5 * time.Second)
}

Available Report Types

reportTypes := []string{
    "payments_summary",
    "payouts_summary",
    "settlements",
    "disputes",
    "customers",
    "subscriptions",
    "revenue",
}

Events

List Events

// Get recent events
events, err := client.Events.List(&paygate.EventListParams{Limit: 20})
for _, event := range events.Data {
    fmt.Printf("%s: %s\n", event.Type, event.ID)
}

// Filter by type
paymentEvents, err := client.Events.List(&paygate.EventListParams{
    Type:  "payment.succeeded",
    Limit: 10,
})

Retrieve an Event

event, err := client.Events.Retrieve("evt_abc123")
fmt.Printf("Type: %s\n", event.Type)
fmt.Printf("Data: %v\n", event.Data)

Event Types

Common event types include:

  • payment.created
  • payment.succeeded
  • payment.failed
  • payout.created
  • payout.succeeded
  • customer.created
  • subscription.created
  • subscription.cancelled
  • invoice.created
  • invoice.paid
  • dispute.created
  • dispute.closed

Settings

Settlement Schedule

// Get current settlement schedule
schedule, err := client.Settings.GetSettlementSchedule()
fmt.Printf("Frequency: %s\n", schedule.Frequency)
fmt.Printf("Day of week: %s\n", schedule.DayOfWeek)

// Update settlement schedule
schedule, err = client.Settings.UpdateSettlementSchedule(&paygate.SettlementScheduleParams{
    Frequency: "weekly",
    DayOfWeek: "friday",
})

Transaction Limits

// Get current limits
limits, err := client.Settings.GetTransactionLimits()
fmt.Printf("Min: %d\n", limits.MinAmount)
fmt.Printf("Max: %d\n", limits.MaxAmount)
fmt.Printf("Daily: %d\n", limits.DailyLimit)

// Update limits
limits, err = client.Settings.UpdateTransactionLimits(&paygate.TransactionLimitParams{
    MinAmount:  100,       // 1 GHS
    MaxAmount:  10000000,  // 100,000 GHS
    DailyLimit: 50000000,  // 500,000 GHS
})

Two-Factor Authentication

Setup 2FA

// Generate 2FA secret and QR code
setup, err := client.Auth.Setup2FA()
fmt.Printf("Secret: %s\n", setup.Secret)
fmt.Printf("QR Code URL: %s\n", setup.QRCodeURL)
// Display QR code to user for scanning

Enable 2FA

// Verify and enable 2FA
result, err := client.Auth.Enable2FA(&paygate.Enable2FAParams{
    Code: "123456", // Code from authenticator app
})
if result.Enabled {
    fmt.Println("2FA enabled successfully")
    fmt.Printf("Backup codes: %v\n", result.BackupCodes)
}

Verify 2FA Code

// Verify a 2FA code
result, err := client.Auth.Verify2FA(&paygate.Verify2FAParams{
    Code: "123456",
})
if result.Valid {
    fmt.Println("Code is valid")
}

Disable 2FA

// Disable 2FA (requires current code)
err := client.Auth.Disable2FA(&paygate.Disable2FAParams{
    Code: "123456",
})

Regenerate Backup Codes

// Get new backup codes
result, err := client.Auth.RegenerateBackupCodes(&paygate.RegenerateBackupCodesParams{
    Code: "123456",
})
fmt.Printf("New backup codes: %v\n", result.BackupCodes)