# Go token broker

The trusted Go package is `github.com/AppVisionOS/cherrysub/sdk/go/iapserver`. Its current SDK version is 0.2.0. It is a server-only bridge: it accepts an `iap_sk_` secret, mints/revokes customer tokens, and reads the current product set. It is not a storefront adapter and must never be compiled into a public app.

## Integrate the package

Use the approved source repository/module distribution. In the preview, source access and package coordinates are not equivalent to a public registry release. Your backend stores the app secret using its normal secret configuration and uses the same CherrySub API deployment as the client.

```go
client, err := iapserver.New(iapserver.Config{
    BaseURL: "https://api-staging.cherrysub.com",
    SecretKey: os.Getenv("CHERRYSUB_SECRET_KEY"),
})
if err != nil {
    // Fail application configuration; never substitute a client key.
}
```

Import `os` and the `iapserver` package in the application file. An empty or wrong-kind key is a configuration error.

## Implement an authenticated token endpoint

The following handler takes your existing verified identity function as a dependency. It does not trust an App User ID from the request body.

```go
package tokenbroker

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

    "github.com/AppVisionOS/cherrysub/sdk/go/iapserver"
)

func TokenHandler(
    client *iapserver.Client,
    authenticatedUserID func(*http.Request) (string, bool),
) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if r.Method != http.MethodPost {
            w.Header().Set("Allow", http.MethodPost)
            http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
            return
        }
        userID, ok := authenticatedUserID(r)
        if !ok || userID == "" {
            http.Error(w, "unauthorized", http.StatusUnauthorized)
            return
        }
        created, err := client.CreateCustomerToken(r.Context(), userID, time.Hour)
        if err != nil {
            http.Error(w, "token service unavailable", http.StatusServiceUnavailable)
            return
        }
        w.Header().Set("Cache-Control", "no-store")
        w.Header().Set("Content-Type", "application/json")
        _ = json.NewEncoder(w).Encode(created.CustomerToken())
    }
}
```

Mount this handler behind your application's authentication, CSRF/session policy, and appropriate rate limiting. The identity function must be backed by verified middleware. `CustomerToken()` exposes only `token` and `expires_at` to the public SDK.

## Revoke on logout when required

Keep the created token's `ID` in your server-side session state if logout should call `RevokeCustomerToken`. Do not return root credentials or the server secret to support logout. Client cache invalidation and server revocation are different operations.

The mint endpoint defaults to one hour and permits five minutes through 24 hours. Choose a bounded lifetime appropriate to your session policy rather than treating a customer token as a permanent API key.

## Read the current product set

```go
productSet, err := client.CurrentProductSet(r.Context())
if err != nil {
    // Handle the typed API or transport error.
}
if productSet == nil {
    // No current presentation has been selected for this app.
}
```

This trusted read needs no customer token. Use a public SDK's customer-token-backed read when returning a presentation directly to an application user.

## Transport and failure behavior

The default client requires HTTPS outside loopback development, refuses redirects, uses a 15-second timeout, and caps response bodies at 1 MiB. Configurable response limits remain bounded. Non-success responses expose a typed `*iapserver.APIError` with status, safe message, and `RetryAfter`.

The SDK does not automatically retry non-idempotent token minting. A failed response can represent an uncertain outcome, so do not create an unbounded retry loop. The client-safe SDKs handle their own one-shot 401 token refresh independently.

Run `go test ./sdk/go/...` from the source checkout and test your broker with missing sessions, forged requested user IDs, mismatched app deployments, expired tokens, and logout. [Authentication](/docs/authentication) · [API reference](/docs/api-reference).
