To accept Paystack payments in Go, create HTTP handlers that call the Paystack REST API using net/http. Initialize a transaction, redirect the user to Paystack checkout, and verify the payment in a callback handler before granting value.
mkdir paystack-go && cd paystack-go
go mod init paystack-go
Set your environment variable:
export PAYSTACK_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxx
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
type InitRequest struct {
Email string "json:\"email\""
Amount int "json:\"amount\""
Reference string "json:\"reference\""
CallbackURL string "json:\"callback_url\""
}
type PaystackResponse struct {
Status bool "json:\"status\""
Message string "json:\"message\""
Data struct {
AuthorizationURL string "json:\"authorization_url\""
AccessCode string "json:\"access_code\""
Reference string "json:\"reference\""
} "json:\"data\""
}
type VerifyResponse struct {
Status bool "json:\"status\""
Data struct {
Status string "json:\"status\""
Amount int "json:\"amount\""
Currency string "json:\"currency\""
Customer struct {
Email string "json:\"email\""
} "json:\"customer\""
Reference string "json:\"reference\""
} "json:\"data\""
}
var paystackSecret = os.Getenv("PAYSTACK_SECRET_KEY")
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
func initializeHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var input struct {
Email string "json:\"email\""
Amount float64 "json:\"amount\""
}
json.NewDecoder(r.Body).Decode(&input)
ref := fmt.Sprintf("order_%d", time.Now().UnixNano())
reqBody := InitRequest{
Email: input.Email,
Amount: int(input.Amount * 100),
Reference: ref,
CallbackURL: "http://localhost:8080/payment/callback",
}
body, _ := json.Marshal(reqBody)
req, _ := http.NewRequest("POST", "https://api.paystack.co/transaction/initialize", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+paystackSecret)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Failed to call Paystack", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
var result PaystackResponse
json.NewDecoder(resp.Body).Decode(&result)
if !result.Status {
http.Error(w, result.Message, http.StatusBadRequest)
return
}
// TODO: Save order to database
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"authorization_url": result.Data.AuthorizationURL,
"reference": result.Data.Reference,
})
}
func callbackHandler(w http.ResponseWriter, r *http.Request) {
reference := r.URL.Query().Get("reference")
if reference == "" {
reference = r.URL.Query().Get("trxref")
}
if reference == "" {
http.Error(w, "Missing reference", http.StatusBadRequest)
return
}
req, _ := http.NewRequest("GET", "https://api.paystack.co/transaction/verify/"+reference, nil)
req.Header.Set("Authorization", "Bearer "+paystackSecret)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Verification failed", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
var result VerifyResponse
json.NewDecoder(resp.Body).Decode(&result)
if result.Status && result.Data.Status == "success" {
// TODO: Verify amount, mark order as paid
fmt.Fprintf(w, "Payment successful! %s %d", result.Data.Currency, result.Data.Amount/100)
return
}
http.Error(w, "Payment not successful", http.StatusBadRequest)
}
func main() {
http.HandleFunc("/api/pay", initializeHandler)
http.HandleFunc("/payment/callback", callbackHandler)
fmt.Println("Server running on :8080")
http.ListenAndServe(":8080", nil)
}
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course