Build Paystack Subscriptions in Go
To build Paystack subscriptions in Go, create a plan via the API, initialize a transaction with the plan code, handle subscription webhooks in your webhook handler, track status in your database, and provide an endpoint for cancellation.
Subscribe Handler
func subscribeHandler(w http.ResponseWriter, r *http.Request) {
var input struct {
Email string "json:\"email\""
PlanCode string "json:\"plan_code\""
}
json.NewDecoder(r.Body).Decode(&input)
body, _ := json.Marshal(map[string]string{
"email": input.Email,
"plan": input.PlanCode,
"callback_url": "http://localhost:8080/subscription/callback",
})
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")
resp, err := http.DefaultClient.Do(req)
if err != nil {
http.Error(w, "Failed", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
var result PaystackResponse
json.NewDecoder(resp.Body).Decode(&result)
if result.Status {
json.NewEncoder(w).Encode(map[string]string{
"authorization_url": result.Data.AuthorizationURL,
})
return
}
http.Error(w, result.Message, http.StatusBadRequest)
}
Subscription Webhook Events
// In processEvent:
case "subscription.create":
var sub struct {
SubscriptionCode string "json:\"subscription_code\""
EmailToken string "json:\"email_token\""
Customer struct {
Email string "json:\"email\""
} "json:\"customer\""
Plan struct {
PlanCode string "json:\"plan_code\""
} "json:\"plan\""
}
json.Unmarshal(data, &sub)
// Save to database
case "invoice.payment_failed":
var inv struct {
Subscription struct {
SubscriptionCode string "json:\"subscription_code\""
} "json:\"subscription\""
}
json.Unmarshal(data, &inv)
// Update status to past_due
Cancel Subscription
func cancelHandler(w http.ResponseWriter, r *http.Request) {
// Get subscription from database for current user
subCode := "SUB_xxx" // from database
emailToken := "token_xxx" // from database
body, _ := json.Marshal(map[string]string{
"code": subCode,
"token": emailToken,
})
req, _ := http.NewRequest("POST", "https://api.paystack.co/subscription/disable",
bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+paystackSecret)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
// Update database status to cancelled
w.Write([]byte("Subscription cancelled"))
}
Subscription Middleware
func requireSubscription(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Check user subscription status from database
// If not active, return 403
// Otherwise, call next(w, r)
next(w, r)
}
}
// Usage:
http.HandleFunc("/premium", requireSubscription(premiumHandler))
Key Takeaways
- ✓Paystack handles recurring billing. Create plans once and subscribe customers.
- ✓Initialize with a plan parameter instead of amount. Paystack uses the plan price.
- ✓Webhook events drive subscription state. Handle subscription.create and invoice.payment_failed.
- ✓Store subscription_code and email_token for managing subscriptions.
- ✓Go middleware can gate handlers behind subscription checks.
- ✓Use database transactions for safe subscription status updates.
Frequently Asked Questions
- Can I change subscription plans?
- Paystack does not support direct plan changes. Cancel the current subscription and create a new one on the desired plan.
- What happens when a renewal fails?
- Paystack retries the charge. You receive invoice.payment_failed webhooks. Update the status to past_due and notify the customer.
- How do I store the email_token?
- The email_token comes in the subscription.create webhook. Store it in your database alongside the subscription_code. You need both for cancellation.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon