Skip to content

Go SDK v4 migration guide

Srinath Sankar edited this page Jan 30, 2026 · 2 revisions

V4 Migration Guide

Since the chargebee-go SDK v4 is a complete rewrite, it fundamentally changes the way the SDK is used. Below are the breaking changes with specific examples of how to perform certain operations before (v3) and after (v4) migrating to the new version.

Minimum required Go version

Because of the use of generics and the slices package, only Go versions >= 1.21 are now supported.

Breaking Changes

Importing required packages

All resources are now available directly in the root package. This makes it much easier for IDEs to suggest/autocomplete all the available symbols in the package without having too many package aliases.

Before (v3)

import (
  "github.com/chargebee/chargebee-go/v3"
  subscriptionAction "github.com/chargebee/chargebee-go/v3/actions/subscription"
  subscriptionEnum "github.com/chargebee/chargebee-go/v3/models/subscription/enum"
  "github.com/chargebee/chargebee-go/v3/models/subscription"
  "github.com/chargebee/chargebee-go/v3/enum"
)

After (v4)

import (
  "github.com/chargebee/chargebee-go/v4"
)

Accessing models, enums and services

Instead of using nested packages to separate all available models, request, response and enums, they are now prefixed with resource name.

Before (v3)

import (
    subscriptionAction "github.com/chargebee/chargebee-go/v3/actions/subscription"
    subscriptionEnum "github.com/chargebee/chargebee-go/v3/models/subscription/enum"
    "github.com/chargebee/chargebee-go/v3/models/subscription"
    "github.com/chargebee/chargebee-go/v3/enum"
)

// Accessing the subscription create method/request
res, err := subscriptionAction.Create(&subscription.CreateRequestParams{...}).Request()

// Global enum
enum.AutoCollectionOff

// Subscription enum
subscriptionEnum.Status

After (v4)

import (
    "github.com/chargebee/chargebee-go/v4"
)

res, err := client.Subscription.Create(&chargebee.SubscriptionCreateRequest{
    Customer: &chargebee.SubscriptionCreateCustomer{...}
})

// Global enum
chargebee.AutoCollectionOff

// Subscription enum
chargebee.SubscriptionStatus

Configuring the Chargebee client

Previously, the SDK was configured globally which meant only one instance of the client could be used at any point. It also exposed non-idiomatic methods to configure addional options like custom HTTP client, retry config, context, etc. Instead, we now expose a chargebee.Client type which encapsulates all required state and methods. This makes it possible to have multiple instances of the client if required with different configurations.

Before (v3)

chargebee.Configure("{site_api_key}", "{site}")

After (v4)

config := &chargebee.ClientConfig{
    SiteName: "{site}",
    ApiKey:   "{site_api_key}",
}
client := chargebee.NewClient(config)

Invoking resource endpoints

Requests are no longer dispatched when the .Request() method is called. Instead, each resource action expects the request object, which is automatically dispatched when the method is called. The distinction between a Request() and ListRequest() also has been done away with.

Before (v3)

res, err := subscriptionAction.Create(&subscription.CreateRequestParams{
    PlanId: "cbdemo_grow",
}).Request()

res, err := subscriptionAction.List(&subscription.ListRequestParams{
    Limit: chargebee.Int32(5),
}).ListRequest()

After (v4)

res, err := client.Subscription.Create(&chargebee.SubscriptionCreateRequest{
    PlanId: "cbdemo_grow",
})

res, err := client.Subscription.List(&chargebee.SubscriptionListRequest{
    Limit: chargebee.Int32(5),
})

Typed responses

Thanks to Go generics, we now have typed response objects for all requests. This makes is easier for IDEs to offer better suggestions and to ensure static type checks pass during development and when compiling the program. This also removes unnecessary noise when trying to identify which fields are actually available in a response without repeated nil checks.

Before (v3)

var result chargebee.Result
result, err = subscriptionAction.Create(&subscription.CreateRequestParams{}).Request()
// result has > 100 fields without a clear indication of which ones are
// actually available for this request
Subscription := result.Subscription
Customer := result.Customer
Invoice := result.Invoice

After (v4)

var res *chargebee.SubscriptionCreateResponse
res, err := client.Subscription.Create(&chargebee.SubscriptionCreateRequest{})
// response is statically typed and contains these exported fields
fmt.Println("%+v", res.Subscription, res.Customer, res.Card, res.Invoice, res.UnbilledCharges)

Accessing custom fields

Before (v3)

// Set custom field in request
req := subscriptionAction.Create(&subscription.CreateRequestParams{
    PlanId: "cbdemo_grow",
})
req.AddParams("cf_gender", "Female")
res, err := req.Request()

// Get custom field from response
res.Subscription.CustomField["cf_gender"]

After (v4)

// Set custom field in request
req := &chargebee.SubscriptionCreateRequest{
    PlanId: "cbdemo_grow",
}
req.AddCustomField("gender", "Female") // with or without the cf_ prefix
res, err := client.Subscription.Create(req)

// Get custom field from response
res.Subscription.CustomFields.Get("gender") // with or without the cf_ prefix

Handling errors

Most panic() calls have been removed in favor of returning an error with the method call. The library will panic only if there is a configuration error and the chargebee.NewClient() cannot instantiate a client with the given site name and API key.

Before (v3)

func main() {
    // guard against panic
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in main", r)
        }
    }()
    if result, err := subscriptionAction.Create(&subscription.CreateRequestParams{...}).Request(); err != nil {
        if goErr, ok := err.(*chargebee.Error); ok {
            // API has returned an error
        }
    }
}

After (v4)

if res, err := client.Subscription.Create(&chargebee.SubscriptionCreateRequest{...}); if err != nil {
    if goErr, ok := err.(*chargebee.Error); ok {
        // API has returned an error
    } else {
        // Other errors
    }
}