Aviato / docs
Plugins & integrations

Stripe widgets

Add read-only Stripe customer, subscription and invoice widgets through the TypeScript, Go and Laravel SDKs.

View Markdown

Provider widgets appear beneath the overview on a record page, including custom summaries. Configure them in your SDK. Your browser calls your agent, which resolves the linked record and calls Stripe directly. Provider credentials and responses never pass through Aviato's API, servers, or databases. The ordinary audit trail records the local record read and its outcome, without provider IDs, credentials, or response bodies.

These APIs are included in the upcoming 0.2.0 SDKs and require an agent build with provider-widget support. This documentation tracks the current source; committing these changes does not publish the SDK packages or agent binary. The earlier 0.1.x releases do not provide StripePlugin.

Configure a read-only Stripe key

Create a Stripe restricted API key with Read access to Customers, Subscriptions, and Invoices as needed, with all other permissions set to None. Do not grant any Write permissions. Use a test-mode key with test-mode IDs, or a live-mode key with live-mode IDs. You can further restrict the key to your agent's outbound IP addresses.

Set STRIPE_WIDGET_KEY in the customer agent's environment, through your deployment's secret manager. Never put its value into widget configuration, source control, the dashboard, or an Aviato environment setting. Restart the agent after changing its environment.

The adapter accepts only rk_test_… or rk_live_… keys and makes only GET requests. readOnly: true is your confirmation of the permissions configured in Stripe. It is not an automatic verification of key permissions: the public Stripe API does not provide a supported permission-introspection endpoint for a supplied key. A restricted key can still have write permissions if configured incorrectly. Aviato does not attempt write requests to test the key. Keep this distinction in mind when enforcing your own credential policy.

TypeScript

StripePlugin implements the shared IntegrationPlugin interface. Configure credentials once, add typed widgets, then install the integration with plugin.use():

import { StripePlugin } from "@getaviato/sdk";

const stripe = new StripePlugin({
    credentialEnv: "STRIPE_WIDGET_KEY",
    readOnly: true,
});
const customerId = { field: "stripe_customer_id" };

plugin.use(stripe
    .customer({ collection: "customers", name: "stripe-customer", customerId })
    .subscriptions({ collection: "customers", name: "stripe-subscriptions", customerId })
    .invoices({ collection: "customers", name: "stripe-invoices", customerId }));

To show billing on an order, use collection: "orders" and customerId: { field: "stripe_customer_id", relation: "customer" }. The relation must be a belongsTo relation in your agent schema (introspected or declared using relation hints). This first version supports one relation hop. The binding always comes from your database, never a browser-supplied Stripe ID. Set title for custom copy, or omit it for a localized default.

Single-entity methods distinguish their binding types:

stripe.subscription({
    collection: "subscriptions", name: "stripe-subscription",
    subscriptionId: { field: "stripe_subscription_id" },
});
stripe.invoice({
    collection: "invoices", name: "stripe-invoice",
    invoiceId: { field: "stripe_invoice_id" },
});
plugin.use(stripe);

Passing customerId to .subscription(), or invoiceId to .invoices(), is a TypeScript error. Provider and resource strings are handled by the plugin, not repeated in application code.

Go

stripe, err := aviato.NewStripePlugin(aviato.StripePluginOptions{
    CredentialEnv: "STRIPE_WIDGET_KEY", ReadOnly: true,
})
if err != nil {
    return err
}
stripe.Invoices(aviato.StripeCustomerWidget{
    Collection: "customers", Name: "stripe-invoices", Title: "Stripe invoices",
    CustomerID: aviato.RecordBinding{Field: "stripe_customer_id"},
})
if err := plugin.Use(stripe); err != nil {
    return err
}

StripePlugin satisfies IntegrationPlugin. Customer, Subscriptions, and Invoices accept StripeCustomerWidget. Subscription accepts StripeSubscriptionWidget with SubscriptionID; Invoice accepts StripeInvoiceWidget with InvoiceID. Set Relation: "customer" on RecordBinding to resolve a related record.

Laravel

use Aviato\Laravel\Facades\Aviato;
use Aviato\Laravel\Integrations\RecordBinding;
use Aviato\Laravel\Integrations\StripePlugin;

$stripe = new StripePlugin(credentialEnv: 'STRIPE_WIDGET_KEY', readOnly: true);
$stripe->invoices(
    collection: 'customers',
    name: 'stripe-invoices',
    customerId: new RecordBinding(field: 'stripe_customer_id'),
    title: 'Stripe invoices',
);
Aviato::use($stripe);

StripePlugin implements IntegrationPlugin. The single-entity methods are subscription(..., subscriptionId: new RecordBinding(...)) and invoice(..., invoiceId: new RecordBinding(...)). Use relation: 'customer' on the binding for a related record. The key belongs in the agent's process environment even when your plugin runs inside Laravel.

All SDKs snapshot an integration when it is installed. Later builder changes take effect only when you call use / Use again. Registration is atomic: an invalid declaration installs no widgets from that call. Re-registering a collection/name pair replaces that widget; it does not remove other widgets already registered on the host.

Available widgets

ProviderResourceBinding valueDisplay
stripecustomercus_…Name, email, Stripe ID, creation date
stripesubscriptionscus_…Latest 20 subscriptions, including canceled subscriptions
stripeinvoicescus_…Latest 20 invoices, status, amount due, creation date
stripesubscriptionsub_…One subscription, status, creation and scheduled cancellation dates
stripeinvoicein_…One invoice

Use the corresponding typed method to register each widget. Names are unique within a collection; registering the same name again replaces it. The agent exposes at most 20 valid widgets per collection. Each widget can reference a different credential environment variable, for example for separate Stripe accounts.

The list widgets are bounded previews, not paginated browsers. A notice appears when more records exist. Refresh fetches current data. There are no refund, payment, cancellation, subscription-editing, hosted-invoice, or download actions.

Permissions and failures

Reading a widget requires access to the local record and its binding column. Related bindings also require access to the relation's foreign-key columns, the target record, and the target binding column. A null or empty binding displays an unlinked state. An inaccessible related record reveals no provider data. Provider responses are projected onto a small display-field allowlist; metadata, payment methods, addresses, arbitrary HTML and URLs are excluded.

Read-only refers to provider writes. Access to the provider data follows access to the binding column: users who can read it can read that widget. Restrict the binding column when billing details should only be visible to selected roles.

The agent pins the Stripe API version to 2025-09-30.clover, limits each request to ten seconds and one MiB, rejects redirects, and never forwards raw provider errors. For an unavailable widget, check the agent environment, key permissions, mode, and the configured ID column. No provider data is persisted by the widget system. The browser retains it only while the widget is mounted; HTTP responses use Cache-Control: no-store.

Future providers

Each SDK exposes a common IntegrationPlugin interface with a widgets() / Widgets() method. Provider classes implement that contract and expose their own typed builders. TypeScript implementations return WidgetInput declarations, Go implementations return []WidgetDefinition, and Laravel implementations return IntegrationWidget objects. Applications use StripePlugin; these lower-level declarations are for integration authors.

Serialized definitions separate provider, resource, record binding, and credential reference. The customer-agent provider registry owns each provider's supported resources, fixed outbound endpoints, credential policy, and response projection. All providers reuse record authorization and a versioned, bounded table format with text, status, date, and money cells. Adding a provider does not require new SDK methods or an Aviato-side proxy. Intercom and Zendesk are not included in this release; each needs its own provider implementation and credential-policy review.

Edit on GitHub

On this page