An open API service indexing awesome lists of open source software.

https://github.com/chargebee/chargebee-go

Go library for the Chargebee API.
https://github.com/chargebee/chargebee-go

chargebee go

Last synced: 4 months ago
JSON representation

Go library for the Chargebee API.

Awesome Lists containing this project

README

          

# Chargebee Go Client Library

> [!NOTE]
> [![Join Discord](https://img.shields.io/badge/Discord-Early%20Access-blue?logo=discord&logoColor=white)](https://discord.gg/S3SXDzXHAg)
>
> We are trialing a Discord server for developers building with Chargebee. Limited spots are open on a first-come basis. Join [here](https://discord.gg/gpsNqnhDm2) if interested.

This is the official Go library for integrating with Chargebee
- 📘 For a complete reference of available APIs, check out our [API Documentation](https://apidocs.chargebee.com/docs/api/?lang=go).
- 🧪 To explore and test API capabilities interactively, head over to our [API Explorer](https://api-explorer.chargebee.com).

**Go version**: v3 and v2 of the library require Go v1.3 or higher.

## Library versions
***

The versioning scheme of this library is inspired by [SemVer](https://semver.org/) and the format is `v{MAJOR}.{MINOR}.{PATCH}`. For example, `v3.0.0` and `v2.5.1` are valid library versions.

The following table provides some details for each major version:

| Library major version | Status | Compatible API versions | **Branch** |
|----------------------------|----------|-----------------------------------------------------------------------------------------------------|---------------|
| v3 | Active | [v2](https://apidocs.chargebee.com/docs/api/v2?lang=go) and [v1](https://apidocs.chargebee.com/docs/api/v1?lang=go) | `master` |
| v2 | Inactive | [v2](https://apidocs.chargebee.com/docs/api/v2?lang=go) and [v1](https://apidocs.chargebee.com/docs/api/v1?lang=go) | `chargebee-v2`|

A couple of terms used in the above table are explained below:
- **Status**: The current development status for the library version. An **Active** major version is currently being maintained and continues to get backward-compatible changes.
- **Branch**: The branch in this repository containing the source code for the latest release of the library version. Every version of the library has been [tagged](https://github.com/chargebee/chargebee-go/tags). You can check out the source code for any version using its tag.

🔴 **Attention**: The support for v2 will eventually be discontinued on **December 31st 2023** and will no longer receive any further updates. We strongly recommend [upgrading to v3](https://github.com/chargebee/chargebee-go/wiki/Migration-guide-for-v3) as soon as possible.

**Note:** See the [changelog](CHANGELOG.md) for a history of changes.

## Install the library
***

Install the latest version of the library with the following commands:

### Install v3
``` shell
go get github.com/chargebee/chargebee-go/v3
```

### Install v2
``` shell
go get github.com/chargebee/chargebee-go
```

## Use the library
***

Some examples for using the library are listed below.

### Create a customer and subscription

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

func main() {
chargebee.Configure("{site_api_key}", "{site}")
res, err := subscriptionAction.Create(&subscription.CreateRequestParams{
PlanId: "cbdemo_grow",
BillingCycles: chargebee.Int32(3),
AutoCollection: enum.AutoCollectionOff,
Customer: &subscription.CreateCustomerParams{
Email: "john@user.com",
FirstName: "John",
LastName: "Doe",
Locale: "fr-CA",
Phone: "+1-949-999-9999",
AutoCollection: enum.AutoCollectionOff,
}}).Request()
if err != nil {
panic(err)
}else{
Subscription := res.Subscription
Customer := res.Customer
Invoice := res.Invoice
}
}
```

### Create a subscription with addons, metadata, and coupons

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

func main() {
chargebee.Configure("{site_api_key}", "{site}")
res, err := subscriptionAction.Create(&subscription.CreateRequestParams{
PlanId: "cbdemo_grow",
BillingCycles: chargebee.Int32(3),
AutoCollection: enum.AutoCollectionOff,
Customer: &subscription.CreateCustomerParams{
Email: "john@user.com",
FirstName: "John",
LastName: "Doe",
Locale: "fr-CA",
Phone: "+1-949-999-9999",
AutoCollection: enum.AutoCollectionOff,
},
BillingAddress: &subscription.CreateBillingAddressParams{
FirstName: "John",
LastName: "Doe",
Line1: "PO Box 9999",
City: "Walnut",
State: "California",
Zip: "91789",
Country: "US",
},
MetaData: map[string]interface{}{
"features": map[string]interface{}{
"usage-limit": "5GB",
"speed-within-quota": "2MBbps",
"post-usage-quota": "512kbps",
},
},
Addons: []*subscription.CreateAddonParams{
{
Id: "cbdemo_conciergesupport",
},
{
Id: "cbdemo_additionaluser",
Quantity: chargebee.Int32(2),
},
},
CouponIds: []string{"cbdemo_earlybird"},
}).Request()
if err != nil {
panic(err)
}else{
Subscription := res.Subscription
Customer := res.Customer
Card := res.Card
Invoice := res.Invoice
UnbilledCharges := res.UnbilledCharges
}
}
```

### Create a subscription with custom headers, custom fields and custom context

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

func main() {
chargebee.Configure("{site_api_key}", "{site}")
res, err := subscriptionAction.Create(&subscription.CreateRequestParams{
PlanId: "cbdemo_grow",
}).Headers("chargebee-request-origin-ip", "192.168.1.2").Contexts(ctx).AddParams("cf_gender","Female").Request() // Customer level custom field.
if err != nil {
panic(err)
}else{
Subscription := res.Subscription
Customer := res.Customer
Card := res.Card
Invoice := res.Invoice
UnbilledCharges := res.UnbilledCharges
}
}
```

### Retrieve a filtered list of subscriptions

```go
import (
"fmt"
"github.com/chargebee/chargebee-go/v3"
subscriptionAction "github.com/chargebee/chargebee-go/v3/actions/subscription"
"github.com/chargebee/chargebee-go/v3/filter"
"github.com/chargebee/chargebee-go/v3/models/subscription"
)

func main() {
chargebee.Configure("{site_api_key}", "{site}")
res, err := subscriptionAction.List(&subscription.ListRequestParams{
Limit: chargebee.Int32(5),
Id: &filter.StringFilter{
In: []string{"cbdemo_john-sub", "cbdemo_ricky-sub"},
},
PlanId: &filter.StringFilter{
IsNot: "basic",
},
Status: &filter.EnumFilter{
Is: subscriptionEnum.StatusActive,
},
SortBy: &filter.SortFilter{
Asc: "created_at",
},
}).ListRequest()
if err != nil {
panic(err)
}else{
for i := range res.List {
Subscription := res.List[i].Subscription
Customer := res.List[i].Customer
Card := res.List[i].Card
}
}
}
```

### Create an idempotent request

[Idempotency keys](https://apidocs.chargebee.com/docs/api/idempotency?prod_cat_ver=2) are passed along with request headers to allow a safe retry of POST requests.

```go
import (
"fmt"
"github.com/chargebee/chargebee-go/v3"
customerAction "github.com/chargebee/chargebee-go/v3/actions/customer"
"github.com/chargebee/chargebee-go/v3/models/customer"
)

func main() {
chargebee.Configure("{site_api_key}", "{site}")
res, err := customerAction.Create(&customer.CreateRequestParams{
FirstName: "John",
LastName: "Doe",
Email: "john@test.com",
})
.SetIdempotencyKey("ghggh") // Replace <> with a unique string
.Request()
if err != nil {
fmt.Println(err)
} else {
Customer := res.Customer
fmt.Println(Customer)
}
headerValue := res.GetResponseHeaders() // Retrieves response headers
fmt.Println(headerValue)
idempotencyReplayedValue := res.IsIdempotencyReplayed()// Retrieves idempotency replayed header value
fmt.Println(idempotencyReplayedValue)
}
```
`IsIdempotencyReplayed()` method can be accessed to differentiate between original and replayed requests.

### Handle webhooks

Use the `webhook` package to parse and route webhook payloads from Chargebee.

High-level: route events with callbacks using `WebhookHandler`:

```go
package main

import (
"log"
"net/http"

"github.com/chargebee/chargebee-go/v3/webhook"
)

func main() {
handler := &webhook.WebhookHandler{
// Optional: protect endpoint (e.g., Basic Auth)
RequestValidator: webhook.BasicAuthValidator(func(user, pass string) bool {
return user == "admin" && pass == "secret"
}),
OnError: webhook.BasicAuthErrorHandler, // Optional: standard auth error responses

// Register only the events you care about
OnSubscriptionCreated: func(e webhook.SubscriptionCreatedEvent) error {
log.Printf("Subscription created event %s", e.Id)
return nil
},
OnPaymentSucceeded: func(e webhook.PaymentSucceededEvent) error {
log.Printf("Payment succeeded for customer: %v", e.Content.Customer)
return nil
},
}

http.Handle("/chargebee/webhooks", handler.HTTPHandler())
log.Fatal(http.ListenAndServe(":8080", nil))
}
```

Low-level: parse just the event type and unmarshal yourself:

```go
package main

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

"github.com/chargebee/chargebee-go/v3/enum"
"github.com/chargebee/chargebee-go/v3/webhook"
)

func cbWebhook(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

evtType, err := webhook.ParseEventType(body) // validates api_version too
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

switch evtType {
case enum.EventTypeSubscriptionCreated:
var e webhook.SubscriptionCreatedEvent
if err := json.Unmarshal(body, &e); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// handle e
default:
// ignore or log
}
w.WriteHeader(http.StatusOK)
}
```

#### Unhandled events

By default, if an incoming webhook’s event type is unknown or you have not registered a corresponding handler on `WebhookHandler`, the SDK treats it as an error. When using `HTTPHandler()`, this results in a 500 response unless you provide a custom `OnError` handler.

If you prefer to acknowledge unknown/unregistered events (return 200) and just log them, set `OnUnhandledEvent` to a function that returns `nil`:

```go
import (
"log"
"github.com/chargebee/chargebee-go/v3/enum"
"github.com/chargebee/chargebee-go/v3/webhook"
)

handler := &webhook.WebhookHandler{
OnUnhandledEvent: func(t enum.EventType, body []byte) error {
log.Printf("Ignoring unhandled event: %s", t)
return nil // swallow as OK
},
}
```

## Use the test suite
***
Use [Testify's `require`](https://github.com/stretchr/testify/#require-package) package to run the test suite

```shell
go get github.com/stretchr/testify/require
```

## Handle errors
***

```go
_,err := //Go Library call

if err != nil {
if goErr,ok := err.(*chargebee.Error); ok {

//Identify the type of Error
switch goErr.Type {

case chargebee.PaymentError:
// First check for card parameters entered by the user.
// We recommend you to validate the input at the client side itself to catch simple mistakes.
if goErr.Param == "card[number]" {
// Ask your user to recheck the card number. A better way is to use
// Stripe's https://github.com/stripe/jquery.payment for validating it in the client side itself.
//}else if(goErr.Param == <other card params>){
//Similarly check for other card parameters entered by the user.
//....
} else {
// Verfication or processing failures.
// Provide a standard message to your user to recheck his card details or provide a different card.
// Like 'Sorry,there was a problem when processing your card, please check the details and try again'.
}

case chargebee.InvalidRequestError:
// For coupons you could decide to provide specific messages by using
// the 'api_error_code' attribute in the ex.
if goErr.Param == "coupon" {
if goErr.APIErrorCode == "resource_not_found" {
// Inform user to recheck his coupon code.
} else if goErr.APIErrorCode == "resource_limit_exhausted" {
// Inform user that the coupon code has expired.
} else if goErr.APIErrorCode == "invalid_request" {
// Inform user that the coupon code is not applicable for his plan(/addons).
} else {
// Inform user to recheck his coupon code.
}
} else {
// Since you would have validated all other parameters on your side itself,
// this could probably be a bug in your code. Provide a generic message to your users.
}

case chargebee.OperationFailedError:
// Indicates that the request parameters were right but the request couldn't be completed.
// The reasons might be "api_request_limit_exceeded" or could be due to an issue in ChargeBee side.
// These should occur very rarely and mostly be of temporary nature.
// You could ask your user to retry after some time.
default :
// These are unhandled exceptions (Could be due to a bug in your code or very rarely in client library).
// The errors from ChargeBee such as authentication failures will come here.
// You could ask users contact your support.
}
}
}
```

### Retry Handling

Chargebee's SDK includes built-in retry logic to handle temporary network issues and server-side errors. This feature is **disabled by default** but can be **enabled when needed**.

#### Key features include:

- **Automatic retries for specific HTTP status codes**: Retries are automatically triggered for status codes `500`, `502`, `503`, and `504`.
- **Exponential backoff**: Retry delays increase exponentially to prevent overwhelming the server.
- **Rate limit management**: If a `429 Too Many Requests` response is received with a `Retry-After` header, the SDK waits for the specified duration before retrying.
> *Note: Exponential backoff and max retries do not apply in this case.*
- **Customizable retry behavior**: Retry logic can be configured using the `retryConfig` parameter in the environment configuration.

#### Example: Customizing Retry Logic

You can enable and configure the retry logic by passing a `retryConfig` object when initializing the Chargebee environment:

```go
import (
"fmt"
"github.com/chargebee/chargebee-go/v3"
customerAction "github.com/chargebee/chargebee-go/v3/actions/customer"
"github.com/chargebee/chargebee-go/v3/models/customer"
)

func main() {
chargebee.Configure("{site_api_key}", "{site}")
retryConfig := &chargebee.RetryConfig{
Enabled: true,
MaxRetries: 3,
DelayMs: 500,
RetryOn: map[int]struct{}{500: {}, 503: {}},
}
chargebee.WithRetryConfig(retryConfig)
}

// ... your Chargebee API operations below ...

```

#### Example: Rate Limit retry logic

You can enable and configure the retry logic for rate-limit by passing a `retryConfig` object when initializing the Chargebee environment:

```go
import (
"fmt"
"github.com/chargebee/chargebee-go/v3"
customerAction "github.com/chargebee/chargebee-go/v3/actions/customer"
"github.com/chargebee/chargebee-go/v3/models/customer"
)

func main() {
chargebee.Configure("{site_api_key}", "{site}")
retryConfig := &chargebee.RetryConfig{
Enabled: true,
MaxRetries: 3,
DelayMs: 500,
RetryOn: map[int]struct{}{429: {}},
}
chargebee.WithRetryConfig(retryConfig)
}

// ... your Chargebee API operations below ...

```

## Contribution
***
You may contribute patches to any of the **Active** versions of this library. To do so, raise a PR against the [respective branch](#library-versions).

If you find something amiss, you are welcome to create an [issue](https://github.com/chargebee/chargebee-go/issues).

## API documentation
***

The API documentation for the Go library can be found in our [API reference](https://apidocs.chargebee.com/docs/api?lang=go).

## License
***

See the [LICENSE](LICENSE).

---