Integrate with Encore
Add billing and payments to your Encore application.
Install
encore app create --example=hello-world myapp
cd myapp
go get github.com/commet-labs/commet-goConfigure
Store secrets using Encore's secret manager instead of environment variables:
encore secret set --type dev,local,pr,prod CommetAPIKey
encore secret set --type dev,local,pr,prod CommetWebhookSecretpackage billing
import (
commet "github.com/commet-labs/commet-go"
)
var secrets struct {
CommetAPIKey string
CommetWebhookSecret string
}
var client *commet.Client
func initClient() error {
var err error
client, err = commet.New(
secrets.CommetAPIKey,
commet.WithEnvironment(commet.Sandbox),
)
return err
}Subscribe
import "context"
type SubscribeParams struct {
Email string `json:"email"`
ExternalID string `json:"external_id"`
}
type SubscribeResponse struct {
CheckoutURL string `json:"checkout_url"`
}
//encore:api public method=POST path=/billing/subscribe
func Subscribe(ctx context.Context, req *SubscribeParams) (*SubscribeResponse, error) {
if err := initClient(); err != nil {
return nil, err
}
_, err := client.Customers.Create(ctx, &commet.CreateCustomerParams{
Email: req.Email,
ExternalID: req.ExternalID,
})
if err != nil {
return nil, err
}
subscription, err := client.Subscriptions.Create(ctx, &commet.CreateSubscriptionParams{
ExternalID: req.ExternalID,
PlanCode: "pro",
})
if err != nil {
return nil, err
}
return &SubscribeResponse{
CheckoutURL: subscription.Data["checkout_url"].(string),
}, nil
}Check Access
type SubscriptionResponse struct {
Status string `json:"status"`
}
//encore:api public method=GET path=/billing/subscription/:externalID
func GetSubscription(ctx context.Context, externalID string) (*SubscriptionResponse, error) {
if err := initClient(); err != nil {
return nil, err
}
sub, err := client.Subscriptions.Get(ctx, externalID)
if err != nil {
return nil, err
}
return &SubscriptionResponse{
Status: sub.Data["status"].(string),
}, nil
}
type FeatureResponse struct {
Allowed bool `json:"allowed"`
}
//encore:api public method=GET path=/billing/features/:feature/:externalID
func CheckFeature(ctx context.Context, feature string, externalID string) (*FeatureResponse, error) {
if err := initClient(); err != nil {
return nil, err
}
result, err := client.Features.Check(ctx, feature, externalID)
if err != nil {
return nil, err
}
return &FeatureResponse{
Allowed: result.Data["allowed"].(bool),
}, nil
}Track Usage
func intPtr(i int) *int { return &i }
type UsageParams struct {
ExternalID string `json:"external_id"`
}
type UsageResponse struct {
Tracked bool `json:"tracked"`
}
//encore:api public method=POST path=/billing/usage
func TrackUsage(ctx context.Context, req *UsageParams) (*UsageResponse, error) {
if err := initClient(); err != nil {
return nil, err
}
_, err := client.Usage.Track(ctx, &commet.TrackUsageParams{
ExternalID: req.ExternalID,
Feature: "api_calls",
Value: intPtr(1),
})
if err != nil {
return nil, err
}
return &UsageResponse{Tracked: true}, nil
}Usage is aggregated and billed at end of period.
Webhooks
package billing
import (
"net/http"
commet "github.com/commet-labs/commet-go"
)
//encore:api public raw method=POST path=/webhooks/commet
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"),
secrets.CommetWebhookSecret,
)
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}`))
}Run
encore runRelated
How is this guide?