Commet
  • Pricing
Log InTry out
Introduction

Quickstart

Integrate with GoIntegrate with Encore

Learn

Resources

SDK ReferenceError HandlingTestingCLI

Plugins

Better Auth
DocumentationKnowledge BaseBuild with AIAPI ReferenceWebhooks

Integrate with Go

Add billing and payments to your Go application using net/http.

Install

go get github.com/commet-labs/commet-go

Configure

.env
COMMET_API_KEY=ck_sandbox_xxx
COMMET_WEBHOOK_SECRET=whsec_xxx
billing/client.go
package billing

import (
	"log"
	"os"

	commet "github.com/commet-labs/commet-go"
)

var Client *commet.Client

func Init() {
	var err error
	Client, err = commet.New(
		os.Getenv("COMMET_API_KEY"),
		commet.WithEnvironment(commet.Sandbox),
	)
	if err != nil {
		log.Fatal(err)
	}
}

Subscribe

billing/handlers.go
package billing

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

	commet "github.com/commet-labs/commet-go"
)

type subscribeRequest struct {
	Email      string `json:"email"`
	ExternalID string `json:"external_id"`
}

func Subscribe(w http.ResponseWriter, r *http.Request) {
	var req subscribeRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	_, err := Client.Customers.Create(r.Context(), &commet.CreateCustomerParams{
		Email:      req.Email,
		ExternalID: req.ExternalID,
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	subscription, err := Client.Subscriptions.Create(r.Context(), &commet.CreateSubscriptionParams{
		ExternalID: req.ExternalID,
		PlanCode:   "pro",
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{"checkout_url": subscription.Data["checkout_url"]})
}

Check Access

billing/handlers.go
func GetSubscription(w http.ResponseWriter, r *http.Request) {
	externalID := r.PathValue("externalID")

	sub, err := Client.Subscriptions.Get(r.Context(), externalID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{"status": sub.Data["status"]})
}

func CheckFeature(w http.ResponseWriter, r *http.Request) {
	feature := r.PathValue("feature")
	externalID := r.PathValue("externalID")

	result, err := Client.Features.Check(r.Context(), feature, externalID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{"allowed": result.Data["allowed"]})
}

Track Usage

billing/handlers.go
func intPtr(i int) *int { return &i }

type usageRequest struct {
	ExternalID string `json:"external_id"`
}

func TrackUsage(w http.ResponseWriter, r *http.Request) {
	var req usageRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	_, err := Client.Usage.Track(r.Context(), &commet.TrackUsageParams{
		ExternalID: req.ExternalID,
		Feature:    "api_calls",
		Value:      intPtr(1),
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{"tracked": true})
}

Usage is aggregated and billed at end of period.

Customer Portal

billing/handlers.go
func Portal(w http.ResponseWriter, r *http.Request) {
	result, err := Client.Portal.GetURL(r.Context(), &commet.GetPortalURLParams{
		ExternalID: "user_123",
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	http.Redirect(w, r, result.Data["portal_url"].(string), http.StatusTemporaryRedirect)
}

Webhooks

billing/webhooks.go
package billing

import (
	"io"
	"net/http"
	"os"

	commet "github.com/commet-labs/commet-go"
)

func HandleWebhook(w http.ResponseWriter, r *http.Request) {
	rawBody, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Failed to read body", http.StatusBadRequest)
		return
	}

	webhooks := &commet.Webhooks{}
	payload, err := webhooks.VerifyAndParse(
		string(rawBody),
		r.Header.Get("x-commet-signature"),
		os.Getenv("COMMET_WEBHOOK_SECRET"),
	)
	if err != nil {
		http.Error(w, "Invalid signature", http.StatusUnauthorized)
		return
	}

	switch payload["event"] {
	case "subscription.activated":
		// handle activation
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(`{"ok":true}`))
}

Start Server

main.go
package main

import (
	"billing"
	"log"
	"net/http"
)

func main() {
	billing.Init()
	defer billing.Client.Close()

	mux := http.NewServeMux()

	mux.HandleFunc("POST /billing/subscribe", billing.Subscribe)
	mux.HandleFunc("GET /billing/subscription/{externalID}", billing.GetSubscription)
	mux.HandleFunc("GET /billing/features/{feature}/{externalID}", billing.CheckFeature)
	mux.HandleFunc("POST /billing/usage", billing.TrackUsage)
	mux.HandleFunc("GET /billing/portal", billing.Portal)
	mux.HandleFunc("POST /webhooks/commet", billing.HandleWebhook)

	log.Println("Listening on :3000")
	log.Fatal(http.ListenAndServe(":3000", mux))
}

Related

  • Subscriptions
  • Track Usage
  • Customer Portal
  • SDK Reference

How is this guide?

Integrate with Java

Install and configure the Commet Java SDK.

Integrate with Encore

Add billing and payments to your Encore application.

On this page

Install
Configure
Subscribe
Check Access
Track Usage
Customer Portal
Webhooks
Start Server
Related