# Adapter Playground Source: https://docs.stellartools.dev/adapter-playground Live interactive demos for every StellarTools adapter. The Adapter Playground lets you test every StellarTools integration end-to-end — no setup required. Each adapter demo is fully wired up and connected to real APIs so you can see the subscription gate, file upload shield, and auth flows in action. Interactive demos for AI SDK, LangChain, BetterAuth, UploadThing, WooCommerce, and Shopify adapters. # Assets Source: https://docs.stellartools.dev/api-reference/assets Query supported Stellar assets An asset represents a Stellar token that StellarTools accepts as payment — USDC, EURC, XLM, or any other asset your organization has enabled. ## The asset object The Stellar asset code, e.g. `"USDC"`, `"XLM"`, `"EURC"`. Human-readable description of the asset, e.g. `"USD Coin"`. The Stellar public key of the canonical issuer for this asset. `null` for native XLM, which has no issuer. This is the authoritative issuer your integration should reference — for example, Circle's address for USDC. During one-time checkouts the DEX routes from any issuer the customer holds automatically, so you never need to resolve issuers yourself. For subscriptions, the smart contract uses this exact issuer. Array of image URLs for the asset logo. *** ## List supported assets `GET /assets` Returns all assets enabled for your account's environment (testnet or mainnet, determined by your API key). ```bash theme={null} curl https://api.stellartools.dev/assets \ -H "x-api-key: YOUR_API_KEY" ``` ### Response ```json theme={null} { "data": [ { "code": "USDC", "description": "USD Coin", "canonicalIssuer": "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN", "images": [] }, { "code": "EURC", "description": "Euro Coin", "canonicalIssuer": "GDHU6WRG4IEQXM5NZ4BMPKOXHW76MZM4Y2IEMFDVXBSDP6SJY4ITNPP", "images": [] }, { "code": "XLM", "description": "Stellar Lumens", "canonicalIssuer": null, "images": [] } ] } ``` This endpoint is useful for dynamically populating payment asset selectors in your storefront or checkout UI, so you always reflect the exact assets your account supports without hardcoding them. # Authentication Source: https://docs.stellartools.dev/api-reference/authentication Authenticating requests to the StellarTools API Every request to the StellarTools API must include your API key in the `x-api-key` header. ```bash theme={null} curl https://api.stellartools.dev/customers \ -H "x-api-key: YOUR_API_KEY" ``` The API key determines the environment your requests run in. Keys prefixed for testnet only interact with testnet data; mainnet keys interact with live data. There is no separate base URL — the key controls it. ## Getting an API key Go to [dashboard.stellartools.dev/api-keys](https://dashboard.stellartools.dev/api-keys) to create and manage your keys. Copy the key immediately after creating it — it will not be shown again. ```bash theme={null} STELLAR_TOOLS_API_KEY=sk_test_... ``` Store it in an environment variable and never expose it in client-side code. ## Errors If the key is missing, the API returns `401`: ```json theme={null} { "error": "Session Token or API Key or App Token required" } ``` If the key is present but invalid or revoked, the API returns `401` as well. Double-check that you are sending the right key for the right environment (testnet vs. mainnet). # Checkouts Source: https://docs.stellartools.dev/api-reference/checkouts Create and manage checkout sessions A checkout session is a short-lived payment page. You create one, redirect the customer to the `payment_url`, and StellarTools handles the rest. On completion, the customer is sent to your `redirect_url`. ## The checkout object Unique identifier. Prefixed with `chk_`. Always `"checkout"`. The customer being charged. For product checkouts. The product being purchased. For direct checkouts. The amount to charge. For direct checkouts. Currency code, e.g. `"USD"`. `open`, `completed`, `expired`, or `failed`. URL to redirect the customer to for payment. URL the customer is sent to after payment. Optional description shown on the checkout page. Arbitrary key-value data. ISO 8601 timestamp. Checkout sessions expire after 24 hours. ISO 8601 timestamp. *** ## Create a checkout `POST /checkout?type=product` or `POST /checkout?type=direct` Use `type=product` to charge for a product you've defined. Use `type=direct` to charge an arbitrary amount without a product. ### Product checkout ```bash theme={null} curl "https://api.stellartools.dev/checkout?type=product" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_01jx...", "product_id": "prod_01jx...", "redirect_url": "https://yourapp.com/success" }' ``` The product to charge for. Existing customer ID. If omitted, provide `customer_email` or `customer_phone` to create one. Email to look up or create a customer. Phone number to look up or create a customer. URL to redirect the customer to after payment. Description shown on the checkout page. Arbitrary key-value data. ### Direct checkout ```bash theme={null} curl "https://api.stellartools.dev/checkout?type=direct" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_01jx...", "amount": 50, "currency_code": "NGN", "redirect_url": "https://yourapp.com/success" }' ``` Amount to charge. Asset code, e.g. `"NGN"`. Same optional fields as product checkout (`customer_id`, `customer_email`, `customer_phone`, `redirect_url`, `description`, `metadata`). *** ## Retrieve a checkout `GET /checkout/{id}` ```bash theme={null} curl https://api.stellartools.dev/checkout/chk_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` # Currencies Source: https://docs.stellartools.dev/api-reference/currencies Query supported fiat currencies StellarTools supports 170 fiat currencies for product pricing and direct checkouts. Prices are stored in each product's native currency and converted to crypto at checkout time using live exchange rates. ## List supported currencies `GET /currencies` Returns the full list of supported ISO 4217 currency codes. ```bash theme={null} curl https://api.stellartools.dev/currencies \ -H "x-api-key: YOUR_API_KEY" ``` ### Response ```json theme={null} { "data": ["AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "..."] } ``` The returned codes map directly to the `currency_code` field on [products](/api-reference/products) and [direct checkouts](/api-reference/checkouts). *** ## Using currency codes When creating a product or direct checkout, pass one of these codes as `currency_code`: ```bash theme={null} curl https://api.stellartools.dev/product \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Pro Plan", "type": "subscription", "currency_code": "EUR", "price_amount_cents": 999, "recurring_period": "month" }' ``` The price is stored as-is in the given currency. At checkout time, StellarTools fetches the live exchange rate and calculates the exact crypto amount the customer must send. *** ## Amount format All amounts in StellarTools (`price_amount_cents`, `amount_cents`) use a *uniform scale of 100*, the base currency amount multiplied by 100, for *every* currency: ``` stored_amount = base_amount × 100 ``` | Display amount | `currency_code` | Stored value | | -------------- | --------------- | ------------ | | \$9.99 | `USD` | `999` | | €25.00 | `EUR` | `2500` | | ₦15,000.00 | `NGN` | `1500000` | | ¥1,000 | `JPY` | `100000` | | 1.50 BHD | `BHD` | `150` | This is *not* the same as ISO 4217 minor units (Stripe's convention). In StellarTools the multiplier is always 100, regardless of how many decimal places the currency uses natively. For example, ¥1,000 is `100000` here, but `1000` in systems that use minor units. A few rules to keep in mind: * Amounts must be *positive integers*. * For zero-decimal currencies (JPY, KRW, VND, and similar), use whole-unit multiples of 100, e.g. ¥1,000 is `100000`, never `100050`. * For three-decimal currencies (BHD, KWD, OMR, and similar), amounts are limited to two decimal places of precision, e.g. `1.234 BHD` cannot be represented and should be rounded to `1.23` (`123`). # Customers Source: https://docs.stellartools.dev/api-reference/customers Create and manage customers A customer represents a person or entity that pays through StellarTools. Customers hold wallets, subscriptions, and payment history. ## The customer object Unique identifier. Prefixed with `cus_`. Always `"customer"`. Email address of the customer. Full name of the customer. Phone number of the customer. URL to the customer's profile image. Stellar wallets linked to this customer. Unique identifier for the wallet. Prefixed with `cwl_`. The Stellar public key. Arbitrary key-value metadata. ISO 8601 timestamp. Arbitrary key-value data you can attach to the customer. ISO 8601 timestamp. ISO 8601 timestamp. *** ## Create a customer `POST /customers` Creates one customer. You can also pass an array to create multiple at once. ```bash theme={null} curl https://api.stellartools.dev/customers \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "jane@example.com", "name": "Jane Smith" }' ``` ### Body Customer's email address. Customer's full name. Customer's phone number. URL to a profile image. Arbitrary key-value string pairs to attach to the customer. *** ## List customers `GET /customers` Returns all customers for your account. ```bash theme={null} curl https://api.stellartools.dev/customers \ -H "x-api-key: YOUR_API_KEY" ``` Returns a [list object](/api-reference/introduction#pagination) containing customer objects. *** ## Retrieve a customer `GET /customers/{customer_id}` ```bash theme={null} curl https://api.stellartools.dev/customers/cus_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` *** ## Update a customer `PUT /customers/{customer_id}` ```bash theme={null} curl -X PUT https://api.stellartools.dev/customers/cus_01jx... \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Jane Doe" }' ``` ### Body New email address. New name. New phone number. New image URL. Merged with existing metadata. *** ## Delete a customer `DELETE /customers/{customer_id}` Returns `null` on success. ```bash theme={null} curl -X DELETE https://api.stellartools.dev/customers/cus_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` # Introduction Source: https://docs.stellartools.dev/api-reference/introduction The StellarTools REST API The StellarTools API lets you manage customers, products, checkouts, subscriptions, payments, refunds, and webhooks over plain HTTP. Every operation maps to a resource with predictable endpoints and consistent response shapes. **If you're building in TypeScript or JavaScript**, the [TypeScript SDK](/integrations/typescript-sdk) is the faster path as it wraps these endpoints with full type safety and handles auth automatically. The raw API is the right choice when you're working in another language, integrating from a backend that can't run Node, or need low-level control. ## Base URL ``` https://api.stellartools.dev ``` ## Responses Returns `200` with the result wrapped in a `data` field. ```json theme={null} { "data": { "id": "cus_01jx...", "object": "customer", ... } } ``` Every resource includes an `object` field identifying its type — e.g. `"customer"`, `"payment"`, `"subscription"`. Returns a non-2xx status with an `error` string. ```json theme={null} { "error": "Customer not found" } ``` ## Pagination List endpoints support cursor-based pagination via query parameters. Maximum number of records to return. Return records after this resource ID (exclusive). Use the last ID in the previous page to paginate forward. Return records before this resource ID (exclusive). Use the first ID in the current page to paginate backward. List responses always have this shape: ```json theme={null} { "object": "list", "data": [...], "has_more": true, "url": "/api/customers" } ``` ## Rate limits There are no rate limits at this time. # Payments Source: https://docs.stellartools.dev/api-reference/payments Retrieve payment records A payment is created automatically when a customer completes a checkout. Payments are confirmed on the Stellar ledger and carry a transaction hash. ## The payment object Unique identifier. Prefixed with `pay_`. Always `"payment"`. The checkout this payment originated from. The customer who made the payment. If this payment is a subscription renewal, the subscription ID. Amount and asset code, e.g. `"50 XLM"`. `pending`, `confirmed`, or `failed`. The Stellar transaction hash. Snapshot of the customer's billing info at the time of payment. `null` if no customer was attached. Customer's email address. Customer's full name. The Stellar wallet used to make the payment. `null` if unavailable. Wallet identifier. Prefixed with `cwl_`. The Stellar public key (G-address). Refunds issued against this payment. Empty array if no refunds exist. Unique identifier for the refund. Prefixed with `ref_`. Refunded amount and asset code, e.g. `"10 XLM"`. The reason provided when the refund was issued. `pending`, `confirmed`, or `failed`. Arbitrary key-value data. ISO 8601 timestamp. *** ## Retrieve a payment `GET /payment/{id}` Fetches a single payment by ID. Before returning, the Stellar network is queried for any `pending` payments and their status is updated accordingly. ```bash theme={null} curl https://api.stellartools.dev/payment/pay_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` Returns a payment object. *** ## List payments `GET /payment` Returns a paginated list of payments for your account, ordered by creation date descending. ```bash theme={null} curl https://api.stellartools.dev/payment \ -H "x-api-key: YOUR_API_KEY" ``` ### Query parameters Filter payments by customer ID. Returns only payments made by this customer. Maximum number of payments to return. Defaults to `10`. Return payments after this payment ID (exclusive). Use the last `id` in the previous page to paginate forward. Returns a [list object](/api-reference/introduction#pagination) containing payment objects. # Customer Portal Source: https://docs.stellartools.dev/api-reference/portal Create portal sessions for customers A customer portal session gives a customer a short-lived authenticated URL to manage their own subscriptions — cancel, view billing history, and update payment methods. ## The portal session object The URL to redirect the customer to. Valid until `expires_at`. The session token embedded in the URL. ISO 8601 timestamp. The session expires shortly after creation. *** ## Create a portal session `POST /customers/{customer_id}/portal` Create a session and redirect the customer to the returned `url`. The URL is single-use and short-lived — generate it on demand, never store it. ```bash theme={null} curl -X POST https://api.stellartools.dev/customers/cus_01jx.../portal \ -H "x-api-key: YOUR_API_KEY" ``` No request body required. # Products Source: https://docs.stellartools.dev/api-reference/products Create and manage products A product defines what you're selling — a one-time item, a recurring subscription, or a metered usage plan. Products are referenced when creating checkouts. ## The product object Unique identifier. Prefixed with `prod_`. Always `"product"`. Display name of the product. Optional description. Array of image URLs. Billing type. One of `one_time`, `subscription`, or `metered`. `active` or `archived`. The asset this product is priced in. The currency code this product is priced in, e.g "NGN" Price in the product's asset. For `subscription` products. One of `day`, `week`, `month`, or `year`. For `metered` products. The unit being measured (e.g. `"token"`, `"request"`). Units consumed per credit. Total credits granted to the customer when this product is purchased. Arbitrary key-value data. `testnet` or `mainnet`. ISO 8601 timestamp. ISO 8601 timestamp. *** ## Create a product `POST /product` ```bash theme={null} curl https://api.stellartools.dev/product \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Pro Plan", "type": "subscription", "asset_id": "ast_01jx...", "price_amount": 10, "recurring_period": "month" }' ``` ### Body Product name. `one_time`, `subscription`, or `metered`. Currency code to charge in, e.g "NGN". Price amount in the asset. Required for `subscription` type. One of `day`, `week`, `month`, `year`. Product description. Array of image URLs. For `metered` products. The unit label. Units consumed per credit. Total credits granted on purchase. Arbitrary key-value data. *** ## Update a product `PUT /product/{id}` ```bash theme={null} curl -X PUT https://api.stellartools.dev/product/prod_01jx... \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Pro Plan v2", "price_amount": 12 }' ``` ### Body New name. New description. New price amount. New billing interval. New image URLs. Merged with existing metadata. *** ## Delete a product `DELETE /product/{id}` ```bash theme={null} curl -X DELETE https://api.stellartools.dev/product/prod_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` # Refunds Source: https://docs.stellartools.dev/api-reference/refunds Issue refunds for payments A refund sends the original payment amount back to the customer's Stellar wallet. Refunds are processed on-chain and reflect a `succeeded` or `failed` status once the transaction settles. ## The refund object Unique identifier. Prefixed with `rf_`. Always `"refund"`. The payment being refunded. The customer receiving the refund. Amount refunded, e.g. `"50 XLM"`. `pending`, `succeeded`, or `failed`. Optional reason for the refund. Stellar public key the refund was sent to. Arbitrary key-value data. ISO 8601 timestamp. *** ## Create a refund `POST /refunds` Refunds the full amount of a payment to the customer's wallet on file. Pass `wallet_address` to override the destination. ```bash theme={null} curl https://api.stellartools.dev/refunds \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "payment_id": "pay_01jx...", "reason": "Customer request" }' ``` ### Body ID of the payment to refund. Reason for the refund. Override the refund destination. Defaults to the wallet that made the original payment. Arbitrary key-value data. # Subscriptions Source: https://docs.stellartools.dev/api-reference/subscriptions Manage recurring billing subscriptions Subscriptions are created automatically when a customer completes a checkout for a `subscription` product. Use these endpoints to retrieve, update, pause, resume, or cancel them. ## The subscription object Unique identifier. Prefixed with `sub_`. Always `"subscription"`. The customer this subscription belongs to. The product being subscribed to. One of `trialing`, `active`, `past_due`, `paused`, or `canceled`. ISO 8601 timestamp. Start of the current billing period. ISO 8601 timestamp. End of the current billing period. If `true`, the subscription will cancel at the end of the current period rather than renewing. ISO 8601 timestamp. Set when the subscription is canceled. ISO 8601 timestamp. Set when the subscription is paused. Number of trial days, if any. Number of consecutive failed renewal attempts. Arbitrary key-value data. ISO 8601 timestamp. ISO 8601 timestamp. *** ## List subscriptions `GET /subscriptions?customer_id={customer_id}` Returns all subscriptions for a customer. ```bash theme={null} curl "https://api.stellartools.dev/subscriptions?customer_id=cus_01jx..." \ -H "x-api-key: YOUR_API_KEY" ``` The customer whose subscriptions to list. *** ## Retrieve a subscription `GET /subscriptions/{id}` Fetches the subscription and syncs its status with the Stellar network before returning. ```bash theme={null} curl https://api.stellartools.dev/subscriptions/sub_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` The response includes `related_resources` (the product and asset) and `last_attempt` (the most recent payment). *** ## Update a subscription `PUT /subscriptions/{id}` ```bash theme={null} curl -X PUT https://api.stellartools.dev/subscriptions/sub_01jx... \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cancel_at_period_end": true }' ``` ### Body Set to `true` to schedule cancellation at the end of the current period, or `false` to undo a scheduled cancellation. Merged with existing metadata. Swap to a different product. *** ## Cancel a subscription `POST /subscriptions/{id}/cancel` Schedules the subscription to cancel at the end of the current billing period. The customer keeps access until `current_period_end`; no further renewals are charged. On-chain cancellation happens automatically when the period ends. ```bash theme={null} curl -X POST https://api.stellartools.dev/subscriptions/sub_01jx.../cancel \ -H "x-api-key: YOUR_API_KEY" ``` Returns the updated subscription object with `cancel_at_period_end: true` and `status` still `active`. To undo a scheduled cancellation before the period ends, use `PUT /subscriptions/{id}` with `{ "cancel_at_period_end": false }`. *** ## Pause a subscription `POST /subscriptions/{id}/pause` ```bash theme={null} curl -X POST https://api.stellartools.dev/subscriptions/sub_01jx.../pause \ -H "x-api-key: YOUR_API_KEY" ``` *** ## Resume a subscription `POST /subscriptions/{id}/resume` ```bash theme={null} curl -X POST https://api.stellartools.dev/subscriptions/sub_01jx.../resume \ -H "x-api-key: YOUR_API_KEY" ``` # Access Source: https://docs.stellartools.dev/api-reference/usage Check whether a customer can access a product The access endpoint tells you whether a customer is entitled to use a product — either because they have an active subscription or because they made a confirmed one-time payment. This is the check the AI SDK, LangChain, and UploadThing adapters run automatically, but you can call it directly from your own server-side logic. ## Check access `GET /customers/{customer_id}/access/{product_id}` Returns whether the customer has access to the product and the reason why. ```bash theme={null} curl https://api.stellartools.dev/customers/cus_01jx.../access/prod_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` ### Response ```json theme={null} { "has_access": true, "grant": { "type": "subscription", "subscription_id": "sub_01jx...", "status": "active" } } ``` ```json theme={null} { "has_access": true, "grant": { "type": "one_time", "payment_id": "pay_01jx..." } } ``` ```json theme={null} { "has_access": false, "grant": null } ``` Whether the customer is entitled to use the product. The reason access was granted. `null` when `has_access` is false. What kind of entitlement granted access. Present when `type` is `"subscription"`. The subscription that is active. Present when `type` is `"subscription"`. The subscription status (`active`, `trialing`, or `paused`). Subscriptions scheduled to cancel remain `active` until `current_period_end`. Present when `type` is `"one_time"`. The confirmed payment that granted access. ## SDK usage The `@stellartools/core` SDK exposes this as `customers.access.verify(customerId, productId)`: ```ts theme={null} import { StellarTools } from "@stellartools/core"; const stellar = new StellarTools({ api_key: process.env.STELLAR_TOOLS_API_KEY! }); const { has_access, grant } = await stellar.customers.access.verify("cus_xxx", "prod_xxx"); if (!has_access) { throw new Error("Customer does not have access to this product"); } // grant.type tells you whether it's a subscription or one-time payment if (grant?.type === "subscription") { console.log("Active subscription:", grant.subscription_id); } ``` # Webhooks Source: https://docs.stellartools.dev/api-reference/webhooks Create and manage webhook endpoints Webhook endpoints receive event notifications from StellarTools when things happen in your account. See the [Webhooks guide](/webhooks) for the full list of event types and how to handle deliveries. ## The webhook object Unique identifier. Prefixed with `wh_`. Always `"webhook"`. A label for this endpoint. The HTTPS URL that receives event POSTs. List of event types this endpoint is subscribed to. When `true`, deliveries to this endpoint are suspended. Optional description. ISO 8601 timestamp. ISO 8601 timestamp. *** ## Create a webhook `POST /webhooks` ```bash theme={null} curl https://api.stellartools.dev/webhooks \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Production handler", "url": "https://yourapp.com/webhooks", "events": ["payment.confirmed", "subscription.canceled"] }' ``` ### Body Label for this endpoint. The HTTPS URL to send events to. Event types to subscribe to. At least one required. See [event types](/webhooks#event-types). Optional description. *** ## Retrieve a webhook `GET /webhooks/{id}` ```bash theme={null} curl https://api.stellartools.dev/webhooks/wh_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` *** ## Update a webhook `PUT /webhooks/{id}` ```bash theme={null} curl -X PUT https://api.stellartools.dev/webhooks/wh_01jx... \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "is_disabled": true }' ``` ### Body New label. New destination URL. Updated event type list. At least one required. Set to `true` to pause deliveries, `false` to resume. New description. *** ## Delete a webhook `DELETE /webhooks/{id}` ```bash theme={null} curl -X DELETE https://api.stellartools.dev/webhooks/wh_01jx... \ -H "x-api-key: YOUR_API_KEY" ``` # Introduction Source: https://docs.stellartools.dev/index Payment infrastructure for the Stellar blockchain StellarTools is a payments platform built on [Stellar](https://stellar.org). You can use it to accept crypto payments, run subscriptions, charge by usage, and pay out to local currencies. Payments settle in a few seconds for fractions of a cent. **What you can build with it:** * One-time and recurring checkout pages * Subscription billing with pause, resume, and cancellation * Customer portals where users manage their own subscriptions and invoices * Global payouts to local currencies (NGN, KES, GHS, and more) ## Get your API key Go to [dashboard.stellartools.dev/api-keys](https://dashboard.stellartools.dev/api-keys) and create a new key. The key encodes your environment (testnet or mainnet), so you do not need to set any base URLs or environment flags. Use testnet keys while building. Switch to a mainnet key when you are ready to go live. ## Install the SDK ```bash theme={null} npm install @stellartools/core ``` ```ts theme={null} import { StellarTools } from "@stellartools/core"; const st = new StellarTools({ api_key: process.env.STELLAR_TOOLS_API_KEY!, }); ``` That is all the setup you need. From here, `st.customers`, `st.checkout`, `st.subscriptions`, and the rest are ready to use. ## Explore Core SDK for customers, checkouts, products, subscriptions, and more. Let Cursor and other agents manage customers, payments, and subscriptions. Adapters for AI SDK, LangChain, MedusaJS, Better Auth, and Uploadthing. Listen to payment, subscription, and customer events in real time. Full REST API reference for all resources. # AI SDK Adapter Source: https://docs.stellartools.dev/integrations/aisdk Gate AI model access to subscribers ## Install ```bash theme={null} npm install @stellartools/aisdk-adapter ai ``` ## Usage ```ts theme={null} import { shield } from "@stellartools/aisdk-adapter"; import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; const result = await generateText({ model: shield(openai("gpt-4o"), { apiKey: process.env.STELLAR_TOOLS_API_KEY!, customerId: "cus_xxx", productId: "prod_xxx", cacheTTL: 120_000, }), prompt: "Write a haiku about Stellar", }); console.log(result.text); ``` Streaming works the same way: ```ts theme={null} import { shield } from "@stellartools/aisdk-adapter"; import { streamText } from "ai"; const { textStream } = await streamText({ model: shield(openai("gpt-4o"), { apiKey: process.env.STELLAR_TOOLS_API_KEY!, customerId: req.user.stellartoolsCustomerId, productId: "prod_xxx", cacheTTL: 120_000, }), messages: [{ role: "user", content: "Hello" }], }); ``` ## Error handling If the customer has no active subscription for the product, `shield` throws a `ShieldError` before the model is ever called. ```ts theme={null} import { shield, ShieldError } from "@stellartools/aisdk-adapter"; try { const result = await generateText({ model: shield(openai("gpt-4o"), config), prompt: "Hello", }); } catch (err) { if (err instanceof ShieldError) { return res.status(403).json({ error: err.message }); } throw err; } ``` ## Config Your StellarTools API key. The StellarTools customer ID to check. The product the customer must have an active subscription to. # Better Auth Adapter Source: https://docs.stellartools.dev/integrations/betterauth Billing plugin for Better Auth apps ## Install ```bash theme={null} npm install @stellartools/betterauth-adapter ``` ## Configure ```ts theme={null} import { stellarTools } from "@stellartools/betterauth-adapter"; import { betterAuth } from "better-auth"; export const auth = betterAuth({ plugins: [ stellarTools({ apiKey: process.env.STELLAR_TOOLS_API_KEY!, createCustomerOnSignUp: true, onCustomerCreated: async (customer) => { console.log("Customer created", customer); }, onSubscriptionCreated: async (subscription) => { console.log("Subscription created", subscription); }, onSubscriptionCanceled: async (subscription) => { console.log("Subscription canceled", subscription); }, }), ], }); ``` The plugin adds `stellartools_customer_id` to the user schema. Run migrations after adding it so your database is up to date. ## Plugin options Your StellarTools API key. Create a StellarTools customer when a user signs up. Defaults to `false`. Called when a customer is created or linked. Called when a subscription is created. Called when a subscription is canceled. Called when a subscription is updated. Called when a checkout is completed. ## Endpoints All endpoints require a valid Better Auth session. ### Customers | Method | Path | Description | | ------ | ------------------------------------- | --------------------------------------- | | `POST` | `/api/auth/stellar/customer/create` | Create or link a StellarTools customer. | | `GET` | `/api/auth/stellar/customer/retrieve` | Get the current user's customer. | | `POST` | `/api/auth/stellar/customer/update` | Update name, email, phone, or metadata. | ### Subscriptions | Method | Path | Description | | ------ | --------------------------------------- | ---------------------------------------- | | `POST` | `/api/auth/stellar/subscription/create` | Create a subscription. | | `GET` | `/api/auth/stellar/subscriptions/list` | List subscriptions for the current user. | ### Refunds | Method | Path | Description | | ------ | --------------------------------- | ------------------------------ | | `POST` | `/api/auth/stellar/refund/create` | Create a refund for a payment. | # LangChain Adapter Source: https://docs.stellartools.dev/integrations/langchain Gate LangChain model access to subscribers ## Install ```bash theme={null} npm install @stellartools/langchain-adapter @langchain/core ``` ## Usage ```ts theme={null} import { HumanMessage } from "@langchain/core/messages"; import { ChatOpenAI } from "@langchain/openai"; import { shield } from "@stellartools/langchain-adapter"; const model = shield(new ChatOpenAI({ model: "gpt-4o" }), { apiKey: process.env.STELLAR_TOOLS_API_KEY!, customerId: "cus_xxx", productId: "prod_xxx", cacheTTL: 120_000, }); const result = await model.invoke([new HumanMessage("Hello")]); ``` Because `shield` returns a standard `Runnable`, it composes naturally with LCEL chains: ```ts theme={null} import { StringOutputParser } from "@langchain/core/output_parsers"; import { ChatPromptTemplate } from "@langchain/core/prompts"; const chain = ChatPromptTemplate.fromMessages([["human", "{question}"]]) .pipe( shield(new ChatOpenAI(), { apiKey: process.env.STELLAR_TOOLS_API_KEY!, customerId: "cus_xxx", productId: "prod_xxx", cacheTTL: 120_000, }) ) .pipe(new StringOutputParser()); const answer = await chain.invoke({ question: "What is Stellar?" }); ``` ## Error handling If the customer has no active subscription for the product, `shield` throws a `ShieldError` before the model is called. ```ts theme={null} import { ShieldError, shield } from "@stellartools/langchain-adapter"; try { const result = await model.invoke([new HumanMessage("Hello")]); } catch (err) { if (err instanceof ShieldError) { return res.status(403).json({ error: err.message }); } throw err; } ``` ## Config Your StellarTools API key. The StellarTools customer ID to check. The product the customer must have an active subscription to. # MCP Server Source: https://docs.stellartools.dev/integrations/mcp Connect AI agents to your StellarTools account StellarTools exposes a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server so agents in Cursor, Claude Desktop, and other MCP clients can work with your customers, products, checkouts, subscriptions, payments, and refunds. You need an API key from [dashboard.stellartools.dev/api-keys](https://dashboard.stellartools.dev/api-keys). ## Endpoint ``` https://api.stellartools.dev/mcp ``` ## Authentication Send your API key on every connection in the `x-api-key` header. StellarTools resolves the key to your organization and environment before any tool runs. ```bash theme={null} x-api-key: sk_test_... ``` If the key is missing or invalid, tool calls return an error result. Use a testnet key while developing and switch to mainnet when you go live. ## Connect in Cursor Add this to your project or user MCP config (`.cursor/mcp.json` or Cursor Settings → MCP): ```json theme={null} { "mcpServers": { "stellartools": { "url": "https://api.stellartools.dev/mcp", "headers": { "x-api-key": "sk_test_your_key_here" } } } } ``` Replace the key with yours. Restart Cursor or reload MCP servers after saving. ## Connect in Claude Desktop In `claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "stellartools": { "url": "https://api.stellartools.dev/mcp", "headers": { "x-api-key": "sk_test_your_key_here" } } } } ``` ## Available tools Tool names and inputs mirror the REST API. Arguments are merged into the shape expected by each route (path params, query, and body fields in one object). | Tool | Description | | ------------------- | ------------------------------------------- | | `create_customers` | Create one or more customers | | `create_product` | Create a product | | `create_refund` | Create a refund | | `delete_checkout` | Delete a checkout (`id`) | | `delete_customer` | Delete a customer (`customerId`) | | `delete_product` | Delete a product (`id`) | | `get_balance` | Stellar account balance for your org wallet | | `get_checkout` | Get a checkout (`id`) | | `get_customer` | Get one customer by ID (`customerId`) | | `get_customers` | List customers for your organization | | `get_payment` | Get one payment (`id`) | | `get_payments` | List payments | | `get_subscriptions` | List subscriptions | | `update_checkout` | Update a checkout (`id` + fields) | | `update_customer` | Update a customer (`customerId` + fields) | | `update_product` | Update a product (`id` + fields) | Successful tool calls return JSON in the MCP text content. For field-level detail on each resource, see the [API Reference](/api-reference/introduction). ## Related * [Authentication](/api-reference/authentication) — API keys and headers * [TypeScript SDK](/integrations/typescript-sdk) — programmatic access without an agent * [API Reference](/api-reference/introduction) — full REST surface # MedusaJS Adapter Source: https://docs.stellartools.dev/integrations/medusajs Stellar as a payment provider in Medusa v2 ## Install ```bash theme={null} npm install @stellartools/medusajs-adapter ``` ## Configure Add the adapter as a payment provider in your Medusa config: ```ts theme={null} import { defineConfig, loadEnv } from "@medusajs/framework/utils"; import { StellarTools } from "@stellartools/medusajs-adapter"; loadEnv(process.env.NODE_ENV || "development", process.cwd()); module.exports = defineConfig({ projectConfig: { databaseUrl: process.env.DATABASE_URL, }, modules: [ { resolve: "@medusajs/payment", options: { providers: [ { resolve: "@stellartools/medusajs-adapter", id: "stellar", options: { api_key: process.env.STELLAR_TOOLS_API_KEY, webhook_secret: process.env.STELLAR_TOOLS_WEBHOOK_SECRET, }, }, ], }, }, ], }); ``` ## Options Your StellarTools API key. Webhook signing secret for verifying incoming events. Enable debug logging. Defaults to `true`. Point your StellarTools webhook at Medusa's provider webhook URL to receive payment events. # TypeScript SDK Source: https://docs.stellartools.dev/integrations/typescript-sdk The core SDK for the StellarTools API The `@stellartools/core` package is the main way to talk to the StellarTools API from TypeScript or JavaScript. It covers customers, checkouts, products, subscriptions, payments, refunds, and credits. ## Install ```bash theme={null} npm install @stellartools/core ``` ## Initialize ```ts theme={null} import { StellarTools } from "@stellartools/core"; const st = new StellarTools({ api_key: process.env.STELLAR_TOOLS_API_KEY!, }); ``` The API key determines whether requests go to testnet or mainnet. No other configuration is needed. ## Customers ```ts theme={null} const customer = await st.customers.create({ email: "jane@example.com", name: "Jane Smith", phone: "+12345678901", }); // customer.id — use this as customerId everywhere else ``` ## Checkouts ```ts theme={null} const checkout = await st.checkout.create({ customer_id: customer.id, product_id: "prod_xxx", redirect_url: "https://yourapp.com/success", }); // Send the customer to checkout.paymentUrl redirect(checkout.payment_url); ``` For a direct amount checkout without a product: ```ts theme={null} const checkout = await st.checkout.create({ customer_d: customer.id, amount: 10, asset_code: "XLM", redirect_url: "https://yourapp.com/success", }); ``` ## Products ```ts theme={null} const product = await st.products.create({ name: "Pro Plan", type: "subscription", asset_code: "XLM", price_amount: 10, recurring_period: "monthly", }); ``` Product types: `one_time`, `subscription`, `metered`. ## Subscriptions ```ts theme={null} // Create const subscription = await st.subscriptions.create({ customer_id: "cust_xxx", product_id: "prod_xxx", }); // Pause, resume, cancel await st.subscriptions.pause(subscription.id); await st.subscriptions.resume(subscription.id); await st.subscriptions.cancel(subscription.id); ``` ## Webhooks ```ts theme={null} const event = st.webhooks.constructEvent( rawBody, req.headers.get("X-StellarTools-Signature")!, process.env.STELLAR_TOOLS_WEBHOOK_SECRET! ); ``` See the [Webhooks](/webhooks) page for all event types and a full handler example. # UploadThing Adapter Source: https://docs.stellartools.dev/integrations/uploadthing Gate file uploads to subscribers ## Install ```bash theme={null} npm install @stellartools/uploadthing-adapter uploadthing ``` ## Usage ```ts theme={null} import { shield } from "@stellartools/uploadthing-adapter"; const f = shield({ apiKey: process.env.STELLAR_TOOLS_API_KEY!, productId: "prod_xxx", }); export const fileRouter = { imageUploader: f({ image: { maxFileSize: "8MB" } }).onUploadComplete(async ({ metadata, file }) => { console.log("Upload complete for:", metadata.customerId); console.log("File URL:", file.ufsUrl); }), }; ``` Pass the customer email on each upload. The adapter looks up the StellarTools customer and checks their subscription before the file transfer starts. ```tsx theme={null} ``` Use the email from checkout for that product. If the customer is not found or has no active subscription, the upload is rejected. `metadata.customerId` and `metadata.productId` are available in `onUploadComplete`. ## Config Your StellarTools API key. The product the customer must have an active subscription to. ## Required headers The customer email to look up and verify access for. # WooCommerce Adapter Source: https://docs.stellartools.dev/integrations/woocommerce Accept Stellar blockchain payments in your WooCommerce store The StellarTools WooCommerce plugin adds Stellar-based payments to any WooCommerce store. Customers pay with USDC, EURC, or XLM through a hosted checkout page. Order status updates automatically when StellarTools confirms payment via webhook. ## Requirements * WordPress 6.0 or later * WooCommerce 8.0 or later * A [StellarTools account](https://dashboard.stellartools.dev/signup) with an API key and webhook secret ## Download the plugin [Download stellartools-woocommerce.zip](https://dashboard.stellartools.dev/~api/integrations/woocommerce/download) ## Installation ### Step 1 Navigate to Add Plugin In your WordPress admin sidebar, go to **Plugins** then click the **Add Plugin** button at the top of the page. Plugins page with Add Plugin button highlighted ### Step 2 Upload and install On the Add Plugins screen, click **Upload Plugin**, select your `stellartools-woocommerce.zip` file, then click **Install Now**. Upload Plugin form showing steps 1, 2, 3 ### Step 3 Activate the plugin After the upload completes, click **Activate Plugin**. Plugin installed successfully with Activate Plugin button StellarTools will now appear in your installed plugins list. Plugins list showing StellarTools as active ## Configuration ### Step 4 Open WooCommerce payment settings In the sidebar go to **WooCommerce → Settings**, click the **Payments** tab, and find StellarTools in the list. Click **Manage** to open the gateway settings. WooCommerce Payments tab with StellarTools active and Manage button highlighted ### Step 5 Enter your credentials Fill in the fields highlighted below and save. Checkout amounts use your WooCommerce store currency (WooCommerce → Settings → General). Customers choose USDC, EURC, or XLM on the StellarTools hosted payment page. StellarTools gateway settings panel Your StellarTools secret key. Test keys begin with `sk_test_`; live keys begin with `sk_live_`. Find yours in the [StellarTools dashboard](https://dashboard.stellartools.dev/api-keys). Leave as `https://api.stellartools.dev` unless you are self-hosting. No trailing slash. Copy the signing secret from your webhook endpoint in the [StellarTools dashboard](https://dashboard.stellartools.dev/webhooks). Your webhook URL is shown inline directly in the settings page copy it from there. WooCommerce status to set when payment is confirmed. Use **Processing** for physical goods that need fulfilment; use **Completed** for digital or virtual products. Writes API requests and webhook events to WooCommerce → Status → Logs. Enable during setup, disable in production. ### Step 6 Register the webhook in StellarTools Copy the webhook URL displayed in the settings panel and add it as a new endpoint in the [StellarTools dashboard](https://dashboard.stellartools.dev/webhooks). Enable at minimum the `payment.confirmed` and `payment.failed` events. The URL uses WordPress plain-permalink REST routing: ``` https:///index.php?rest_route=/stellartools/v1/webhook ``` ## How it works Once configured, **Pay with Stellar (Crypto)** appears as a payment option on the WooCommerce checkout page. WooCommerce checkout page showing Pay with Stellar option selected After clicking **Place Order** the customer is redirected to the StellarTools hosted payment page to complete the payment on the Stellar blockchain. On return they land on the WooCommerce order confirmation page. Order received confirmation page StellarTools fires a `payment.confirmed` webhook to your site. The plugin verifies the signature, resolves the WooCommerce order, and updates the status automatically. WooCommerce orders list showing order marked as Completed ## Webhook events | Event | WooCommerce action | | ------------------- | -------------------------------------------------------------------------------------------------------------- | | `payment.confirmed` | Order moves to Processing or Completed (per your setting), payment ID and transaction hash saved to order meta | | `payment.failed` | Order moves to Failed | # Building Marketplace Apps Source: https://docs.stellartools.dev/marketplace/building-apps Ship an app that runs natively inside the StellarTools dashboard. StellarTools Marketplace Apps are web applications embedded directly into the merchant dashboard via an `iframe`. Your app automatically inherits the merchant's active organization context, theme, and time-range filters — and can react to real-time payment events via webhooks. ## Installation Install the core libraries required to communicate with the host dashboard and use our shared design system. ```bash theme={null} npm install @stellartools/app-sdk @stellartools/core @stellartools/shared-ui ``` ## Styling Import the shared CSS so your app looks native inside the StellarTools dashboard. ```css theme={null} /* globals.css */ @import "tailwindcss"; @import "@stellartools/shared-ui/dist/output.css"; ``` ## Bootstrap your app ### Server-side verification When StellarTools loads your app it appends `st_token` to your `baseUrl`. This token is a signed JWT prefixed with `st_app_` that contains the `AppContext` — the identity, permissions, and decrypted settings for this installation. Because your App Secret must never reach the browser, verification happens in a server action. ```ts theme={null} // app/actions/context.ts "use server"; import { type AppContext } from "@stellartools/app-sdk"; import { APP_TOKEN_PREFIX, STELLARTOOLS_ID, decodeJwt, verifyJwt } from "@stellartools/core"; export async function resolveAppContext(token: string): Promise { if (!token.startsWith(APP_TOKEN_PREFIX)) return null; const rawJwt = token.replace(APP_TOKEN_PREFIX, ""); const verified = verifyJwt(rawJwt, process.env.YOUR_APP_SECRET!, STELLARTOOLS_ID); if (!verified) return null; return decodeJwt(rawJwt); } ``` ### Client provider Reads the token from the URL, calls the server action, and mounts the context for the entire component tree. `StellarAppBootstrap` ensures internal links and fetch calls resolve correctly inside the iframe. ```tsx theme={null} // app/providers.tsx "use client"; import * as React from "react"; import { resolveAppContext } from "@/app/actions/context"; import { type AppContext, StellarAppBootstrap, StellarToolsAppProvider } from "@stellartools/app-sdk"; import { useSearchParams } from "next/navigation"; function AppContextBridge({ children }: { children: React.ReactNode }) { const searchParams = useSearchParams(); const token = searchParams.get("st_token"); const [context, setContext] = React.useState(null); React.useEffect(() => { if (!token) return; resolveAppContext(token).then(setContext); }, [token]); React.useEffect(() => { if (!context) return; document.documentElement.classList.toggle("dark", context.ui.theme === "dark"); }, [context]); if (!context) return null; return ( {children} ); } export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### Routing on settings The root page reads `context.settings` and routes the user — no fetch required. Settings are already decrypted inside the token. ```tsx theme={null} // app/page.tsx "use client"; import { Suspense, useEffect } from "react"; import { useStellarToolsContext } from "@stellartools/app-sdk"; import { useRouter, useSearchParams } from "next/navigation"; function AppRouter() { const router = useRouter(); const searchParams = useSearchParams(); const { settings } = useStellarToolsContext(); useEffect(() => { const qs = searchParams.toString() ? `?${searchParams.toString()}` : ""; router.replace(settings?.connectedApiKey ? `/dashboard${qs}` : `/authentication${qs}`); }, [settings, router, searchParams]); return null; } export default function Page() { return ( ); } ``` ## AppContext reference `useStellarToolsContext()` returns the full `AppContext` decoded from `st_token`. | Field | Type | Description | | --------------- | ------------------------ | ------------------------------------------------------------------------- | | `orgId` | `string` | The active organization | | `env` | `"testnet" \| "mainnet"` | Which network the org is on | | `instId` | `string` | Unique ID for this installation | | `appId` | `string` | Your app's ID | | `scopes` | `AppScope[]` | Scopes granted at install time | | `settings` | `Record` | Decrypted installation settings — read directly, no fetch needed | | `st_token` | `string` | The prefixed `st_app_...` JWT — pass this to server actions and SDK calls | | `ui.theme` | `"light" \| "dark"` | The dashboard's current theme | | `ui.periodDays` | `number` | The selected time window (e.g. `30`) | | `ui.currency` | `string` | The dashboard's display currency | ## Fetching data `useStellarToolsQuery` wraps TanStack Query and automatically scopes cache keys to `orgId + env + periodDays`. Switching organizations or time windows triggers a fresh fetch with no extra config. The fetcher receives the full `AppContext` as its argument — use it to access `settings`, `ui.periodDays`, or anything else from the token. ```tsx theme={null} "use client"; import { getEmailStats } from "@/app/actions"; import { useStellarToolsQuery } from "@stellartools/app-sdk"; export default function DashboardPage() { const { data: stats } = useStellarToolsQuery( ["stats"], async (context) => getEmailStats(context.settings.apiKey, context.ui.periodDays), { enabled: (ctx) => !!ctx.settings.apiKey } ); return
{/* render stats */}
; } ``` ### Resource hydration Apps are webhook consumers and webhook event payloads contain only the primary resource. Use the `st_app_` token to hydrate related objects when you need more detail. ```ts theme={null} import { StellarTools } from "@stellartools/core"; // Retrieve the full customer from a payment event const st = new StellarTools({ api_key: appToken }); const customer = await st.customers.retrieve(event.data.object.customer_id); ``` ## Mutations and saving settings `useStellarToolsMutation` wraps TanStack Query's `useMutation`, injects the `AppContext` as the second argument, and gives you `isPending` for loading states. ### Server action ```ts theme={null} // app/actions/index.ts "use server"; import { StellarTools } from "@stellartools/core"; export const updateSettings = async ( appToken: string, patch: Record ): Promise => { const st = new StellarTools({ api_key: appToken }); await st.appInstallations.updateSettings(patch); }; ``` The patch is merged into the existing settings object. When you call `updateSettings`, the SDK automatically notifies the host dashboard to refresh its state. ### In your component ```tsx theme={null} "use client"; import { Suspense, useState } from "react"; import { updateSettings } from "@/app/actions"; import { useStellarToolsContext, useStellarToolsMutation } from "@stellartools/app-sdk"; import { useSearchParams } from "next/navigation"; function SettingsPanel() { const { settings } = useStellarToolsContext(); const appToken = useSearchParams().get("st_token") ?? ""; const [syncEnabled, setSyncEnabled] = useState(Boolean(settings.customerSyncEnabled)); const { mutate: toggleSync, isPending } = useStellarToolsMutation( async (enabled: boolean) => updateSettings(appToken, { customerSyncEnabled: enabled }), { onMutate: (enabled) => setSyncEnabled(enabled) } ); return ; } export default function Page() { return ( ); } ``` ## First-time setup On the authentication page, validate the user's credentials before saving them. Return `true` on success, or an error string to display in the form. ```ts theme={null} // app/actions/index.ts "use server"; import { StellarTools } from "@stellartools/core"; export const validateAndConnect = async (apiKey: string, appToken: string): Promise => { const { error } = await validateCredentials(apiKey); if (error) return error.message; const resolvedConfig = await resolveConfig(apiKey); const st = new StellarTools({ api_key: appToken }); const result = await st.appInstallations.updateSettings({ apiKey, ...resolvedConfig }); if (result?.error) return result.error; return true; }; ``` ```tsx theme={null} // app/authentication/page.tsx "use client"; import { Suspense } from "react"; import { validateAndConnect } from "@/app/actions"; import { zodResolver } from "@hookform/resolvers/zod"; import { Button, TextAreaField } from "@stellartools/shared-ui"; import { useRouter, useSearchParams } from "next/navigation"; import { Controller, useForm } from "react-hook-form"; import { z } from "zod"; const schema = z.object({ apiKey: z.string().min(1, "API key is required") }); function AuthenticationForm() { const router = useRouter(); const searchParams = useSearchParams(); const appToken = searchParams.get("st_token") ?? ""; const form = useForm({ resolver: zodResolver(schema), defaultValues: { apiKey: "" } }); const { isSubmitting } = form.formState; const onSubmit = async ({ apiKey }: { apiKey: string }) => { const result = await validateAndConnect(apiKey, appToken); if (result !== true) { form.setError("apiKey", { message: result }); return; } const qs = searchParams.toString() ? `?${searchParams.toString()}` : ""; router.push(`/dashboard${qs}`); }; return (
( )} /> ); } export default function AuthenticationPage() { return ( ); } ``` ## Webhooks When a matching event fires, StellarTools POSTs a signed payload to your `webhookUrl`. The body contains both the `event` and the installation's decrypted `settings` — your handler can access merchant config without a database round-trip. ### Verify and handle ```ts theme={null} // app/api/webhook/route.ts import { type WebhookEvent, WebhookSigner } from "@stellartools/core"; import { NextRequest, NextResponse } from "next/server"; type Settings = { apiKey: string; // your app's settings shape }; const wh = new WebhookSigner(); export async function POST(req: NextRequest) { const rawBody = await req.text(); const signature = req.headers.get("x-stellartools-signature") ?? ""; try { wh.constructEvent(rawBody, signature, process.env.WEBHOOK_SECRET!); } catch { return NextResponse.json({ error: "Invalid signature" }, { status: 401 }); } const { event, settings }: { event: WebhookEvent; settings: Settings } = JSON.parse(rawBody); switch (event.type) { case "payment.confirmed": { await handlePayment(event.data.object, settings); break; } case "customer.created": { await handleNewCustomer(event.data.object, settings); break; } } return NextResponse.json({ ok: true }); } ``` Always check that the required setting (template ID, feature flag, etc.) is present before acting. Merchants may install your app before completing setup. ### Available events For the complete list of event types, payloads, and object shapes see [Webhook event types](/webhooks#event-types). | Event | Fires when | `data.object` type | | ------------------------ | -------------------------------------------- | ------------------ | | `payment.confirmed` | A payment is confirmed on-chain | `Payment` | | `payment.pending` | A payment is submitted but not yet confirmed | `Payment` | | `payment.failed` | A payment fails on-chain | `Payment` | | `refund.succeeded` | A refund is issued successfully | `Refund` | | `refund.failed` | A refund could not be processed | `Refund` | | `subscription.created` | A new subscription starts | `Subscription` | | `subscription.updated` | A subscription is modified | `Subscription` | | `subscription.canceled` | A subscription is canceled | `Subscription` | | `checkout.created` | A checkout session opens | `Checkout` | | `customer.created` | A new customer is created | `Customer` | | `customer.updated` | Customer details change | `Customer` | | `customer.deleted` | A customer is deleted | `Customer` | | `payment_method.created` | A wallet is linked to a customer | `CustomerWallet` | | `payment_method.deleted` | A wallet is removed from a customer | `CustomerWallet` | ## The App Manifest The manifest is the source of truth for your app's identity and requirements. Submit it when publishing to the marketplace. ```json theme={null} { "name": "My App", "description": "A short description shown in the marketplace (max 200 chars).", "homepageUrl": "https://myapp.com", "baseUrl": "https://myapp.com/stellartools", "webhookUrl": "https://myapp.com/api/webhooks/stellar", "scopes": ["read:customers", "read:payments"], "sensitiveKeys": ["apiKey", "secretToken"], "version": "1.0.0" } ``` Display name in the marketplace. 2–50 characters. Short description shown on the listing card. Max 200 characters. Your app's public homepage. The URL StellarTools loads inside the iframe. `st_token` is appended as a query param automatically. If provided, StellarTools POSTs signed event payloads here. Omit if you don't use webhooks. Permissions requested at install time. Declare only what your app actually needs. Keys in your settings that require encryption at rest. See [Sensitive keys](#sensitive-keys) below. ### Sensitive keys Add any settings property that holds a secret (API keys, tokens, credentials) to `sensitiveKeys`. StellarTools will: * Encrypt the value with a platform-level master key before writing it to the database. * Decrypt it on-the-fly before delivering it to your app server via the `AppContext` or webhook payload. Your app logic always receives the plain-text value — the database only ever stores ciphertext. ```json theme={null} { "sensitiveKeys": ["apiKey", "webhookSigningSecret"] } ``` ## Scopes Declare only the scopes your app strictly needs. Merchants see these permissions during installation. | Scope | What it grants | | ------------------------ | ---------------------------------------------------------------------- | | `read:customers` | Customer records | | `read:payments` | Payment history and status | | `read:subscriptions` | Subscription lifecycle | | `read:checkouts` | Checkout sessions | | `read:payouts` | Payout records | | `read:refunds` | Refund records | | `read:payment_methods` | Saved payment methods | | `read:products` | Product catalog | | `read:portal` | Customer portal sessions | | `write:app-installation` | Allow the app UI to update its own settings | | `*` | Full access — shown prominently to users during install, use sparingly | ## Environment variables ```env theme={null} YOUR_APP_SECRET=sec_... # App signing secret — used by resolveAppContext NEXT_PUBLIC_APP_URL=https://myapp.com WEBHOOK_SECRET=whsec_... # For verifying incoming webhook signatures ``` ## Deployment Host your app on any provider (Vercel, AWS, etc.). Before submitting, ensure: * `baseUrl` is accessible over HTTPS. * `YOUR_APP_SECRET` is set in your production environment. * Your server correctly strips the `st_app_` prefix before passing the raw JWT to `verifyJwt`. ## Example apps These are fully working marketplace apps you can reference or clone. | App | What it does | | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | [Resend](https://github.com/payrouteshq/stellartools/tree/main/apps/marketplace-apps/resend) | Sends transactional emails on payment, subscription, and refund events using Resend. | | [Loops](https://github.com/payrouteshq/stellartools/tree/main/apps/marketplace-apps/loops) | Syncs customers to a Loops audience and triggers email journeys on StellarTools events. | ## Submit your app App submission is currently in private beta. To list your app in the StellarTools Marketplace, contact [partners@stellartools.dev](partners@stellartools.dev). # Loops Source: https://docs.stellartools.dev/marketplace/loops Send lifecycle emails and trigger workflows from StellarTools payment events. The Loops app connects your StellarTools organization to [Loops](https://loops.so), letting you send transactional emails and trigger automated workflows when things happen — payments confirmed, subscriptions started, refunds processed, and more. With it you can: * Send transactional emails triggered by any StellarTools event * Trigger Loops workflows from payment and subscription events * Auto-sync new customers to your Loops audience and mailing lists * View a live activity log of emails sent inside the dashboard ## Supported events The app fires on any of the following StellarTools events: | Event | When it fires | | ------------------------ | -------------------------------- | | `customer.created` | A new customer is added | | `customer.updated` | A customer's details change | | `customer.deleted` | A customer is removed | | `payment_method.created` | A wallet is linked to a customer | | `payment_method.deleted` | A wallet is removed | | `checkout.created` | A checkout session is opened | | `payment.pending` | A payment is initiated | | `payment.confirmed` | A payment is confirmed on-chain | | `payment.failed` | A payment fails | | `refund.succeeded` | A refund is processed | | `refund.failed` | A refund attempt fails | | `subscription.created` | A subscription starts | | `subscription.updated` | A subscription changes | | `subscription.canceled` | A subscription is canceled | ## Connect your account 1. Open the Loops app from the StellarTools Marketplace. 2. Enter your Loops API key. Find it at [Loops → Settings → API](https://app.loops.so/settings?page=api). 3. Click **Connect**. The app validates the key and activates. ## Transactional emails Open **Notification rules** in the app dashboard. For each event type, you can assign a Loops transactional email. When that event fires in StellarTools, Loops sends the matched email to the customer. Leave an event set to **None** to skip sending for it. ## Loops workflows You can also trigger Loops [workflows](https://loops.so/docs/loop-builder/triggers) directly — no template selection needed. Create a workflow in Loops and set its trigger to the event name with dots replaced by underscores: | StellarTools event | Loops trigger name | | ---------------------- | ---------------------- | | `payment.confirmed` | `payment_confirmed` | | `subscription.created` | `subscription_created` | | `refund.succeeded` | `refund_succeeded` | The workflow fires automatically when that event occurs. ## Contact sync Enable **Contact sync** to automatically add new StellarTools customers to your Loops audience when `customer.created` fires. When contact sync is on, you can also select a **mailing list** to subscribe them to immediately. Toggle it off to stop syncing. ## Disconnect Click **Disconnect** in the top-right of the app dashboard. This removes your API key and pauses all email delivery. ## Examples ### Payment confirmed email Send a receipt when a customer's payment goes through. 1. Create a transactional email in Loops for payment confirmations. 2. In the Loops app, open **Notification rules** and assign it to `payment.confirmed`. ### Subscription welcome email Send a welcome email when a customer starts a subscription. 1. Create a transactional email in Loops. 2. Assign it to `subscription.created` in **Notification rules**. ### Subscription canceled — win-back workflow Trigger a Loops workflow to attempt to win back a customer who canceled. 1. Create a workflow in Loops with trigger `subscription_canceled`. 2. Add your win-back email sequence to the workflow. 3. The workflow fires automatically — no template selection needed in the app. ### Failed payment alert Notify a customer when their payment fails so they can retry. 1. Create a transactional email in Loops for payment failures. 2. Assign it to `payment.failed` in **Notification rules**. # Resend Source: https://docs.stellartools.dev/marketplace/resend Send transactional emails triggered by StellarTools payment events. The Resend app connects your StellarTools organization to [Resend](https://resend.com), letting you send transactional emails when things happen to your customers — payments, subscriptions, refunds, and more. With it you can: * Send emails triggered by any StellarTools event * Auto-sync new customers to a Resend audience * Map each event type to a Resend email template * View a live email log and delivery stats inside the dashboard ## Supported events The app can trigger emails on any of the following StellarTools events: | Event | When it fires | | ------------------------ | -------------------------------- | | `customer.created` | A new customer is added | | `customer.updated` | A customer's details change | | `customer.deleted` | A customer is removed | | `payment_method.created` | A wallet is linked to a customer | | `payment_method.deleted` | A wallet is removed | | `checkout.created` | A checkout session is opened | | `payment.pending` | A payment is initiated | | `payment.confirmed` | A payment is confirmed on-chain | | `payment.failed` | A payment fails | | `refund.succeeded` | A refund is processed | | `refund.failed` | A refund attempt fails | | `subscription.created` | A subscription starts | | `subscription.updated` | A subscription changes | | `subscription.canceled` | A subscription is canceled | ## Connect your account 1. Open the Resend app from the StellarTools Marketplace. 2. Enter your Resend API key (starts with `re_`). The app validates it against your Resend account. 3. Choose a **sending domain**. If you have a verified domain in Resend, you can pick it here and set a custom `from` prefix (e.g. `noreply@yourdomain.com`). Without a verified domain, emails go out via `onboarding@resend.dev`, which can only reach addresses registered in your Resend account. 4. Click **Connect**. The app is now active. To verify a sending domain, go to [resend.com/domains](https://resend.com/domains). ## Notification rules Once connected, open the **Notification rules** section in the app dashboard. For each event type, you can assign a Resend email template. When that event fires in StellarTools, Resend sends the matched template to the customer. Leave an event set to **None** to disable emails for it. ## Contact sync Enable **Contact sync** to automatically add new StellarTools customers to your Resend audience when `customer.created` fires. Toggle it off to stop syncing. ## Disconnect Click **Disconnect** in the top-right of the app dashboard. This removes your API key and pauses all email delivery. ## Examples ### Payment confirmed email Send a receipt when a customer's payment goes through. 1. Create an email template in Resend for payment confirmations. 2. In the Resend app, open **Notification rules** and assign that template to `payment.confirmed`. ### Subscription welcome email Send a welcome email when a customer starts a subscription. 1. Create an email template in Resend. 2. Assign it to `subscription.created` in **Notification rules**. ### Failed payment alert Notify a customer when their payment fails so they can retry. 1. Create an email template in Resend for payment failures. 2. Assign it to `payment.failed` in **Notification rules**. ### Refund confirmation Let customers know their refund was processed. 1. Create a refund confirmation template in Resend. 2. Assign it to `refund.succeeded` in **Notification rules**. # Products Source: https://docs.stellartools.dev/products The two billing models available on StellarTools and when to use each one A product is the thing you are selling. It defines the billing model and price. You create products once and reuse them across checkouts. StellarTools has two product types: one-time and subscription. *** ## One-time The customer pays once. You get the money. Nothing recurs, nothing needs managing afterward. Good for software licenses, digital downloads, access passes, and any purchase that only needs to happen once. Create a checkout with a one-time product and the customer pays the fixed price you set. ```typescript theme={null} const checkout = await st.checkout.create({ type: "product", product_id: "prod_abc123", customer_email: "user@example.com", }); ``` *** ## Subscription The customer is billed on a recurring schedule. You set the price and the billing period: daily, weekly, monthly, yearly, or a custom interval. StellarTools renews the subscription automatically at the end of each period. You can cancel, pause, or resume from the API or from the customer portal. | Period | What it means | | ------- | ----------------------------------------------------- | | daily | Billed every day | | weekly | Billed every 7 days | | monthly | Billed every 30 days | | yearly | Billed every 365 days | | custom | Billed every N days, weeks, or months that you define | When a subscription renews, StellarTools fires a `subscription.renewed` webhook so you know to keep the customer's access active. ```typescript theme={null} // Cancel at end of current period await st.subscriptions.update(subscriptionId, { cancel_at_period_end: true, }); ``` *** ## Checking access Both product types grant the customer access to whatever they paid for. Once they have a confirmed payment or an active subscription, you can verify that access programmatically: ```typescript theme={null} const { has_access, grant } = await st.customers.access.verify("cus_xxx", "prod_xxx"); if (!has_access) { throw new Error("No active subscription or payment found"); } // grant.type is "subscription" or "one_time" ``` The AI SDK, LangChain, and UploadThing adapters run this check automatically — you only call it directly if you're building your own gating logic. *** ## Choosing a type If you charge once, use one-time. If you charge on a fixed schedule, use subscription. You cannot change a product's type after it is created. *** Create, read, and update products via the REST API. Turn a product into a payment link. # Webhooks Source: https://docs.stellartools.dev/webhooks Listen to real-time events from StellarTools Webhooks let your server react to things as they happen. When a payment is confirmed, a subscription is canceled, or a customer is updated, StellarTools sends an HTTP `POST` to your endpoint with a signed JSON payload. Create and manage webhooks at [dashboard.stellartools.dev/webhooks](https://dashboard.stellartools.dev/webhooks). When you create one, pick which events to subscribe to and copy the signing secret to verify deliveries. *** ## Event envelope Every webhook POST has the same top-level shape, regardless of event type. ```json theme={null} { "id": "wh_evt_01jx4k...", "type": "payment.confirmed", "created": "2026-05-01T14:32:00.000Z", "livemode": false, "data": { "object": { "id": "pay_01jx4m...", "checkout_id": "chk_01jx4n...", "customer_id": "cust_01jx2a...", "amount": "10 XLM", "status": "confirmed", "transaction_hash": "a3f92c8d...", "created_at": "2026-05-01T14:31:58.000Z", "metadata": null } } } ``` ### Envelope fields Unique ID for this event. Prefixed with `wh_evt_`. The event type, e.g. `payment.confirmed` or `subscription.canceled`. See the full list below. ISO 8601 timestamp of when the event was created. `true` if the event came from a mainnet (live) API key. `false` for testnet. Contains the event payload. The full resource that triggered the event. Its shape depends on the event type. For `payment.*` events it is a Payment object, for `subscription.*` a Subscription, and so on. Only present on `*.updated` events. Contains the fields that changed, with their **previous** values. Fields that did not change are not included. For example, on a `subscription.updated` event where the status changed from `active` to `paused`: ```json theme={null} "previous_attributes": { "status": "active" } ``` *** ## Event types ### Customer events A new customer was created. A customer's details were updated. Includes `previous_attributes`. A customer was deleted. ### Payment method events A Stellar wallet was linked to a customer. A wallet was removed from a customer. ### Checkout events A new checkout session was created. ### Payment events A payment transaction was submitted to the network but has not yet been confirmed. A payment is confirmed on the Stellar ledger. This is the event to act on when fulfilling an order. A payment could not be confirmed on-chain. ### Refund events A refund was sent to the customer's wallet successfully. A refund could not be processed. ### Subscription events A new subscription was created. A subscription was updated (for example: paused, resumed, or `cancel_at_period_end` was toggled). Includes `previous_attributes`. A subscription was canceled. *** ## Handling events Use `st.webhooks.constructEvent` to verify the signature and get the typed event back. Always verify before acting on the payload. ```ts theme={null} import { StellarTools } from "@stellartools/core"; import { NextRequest, NextResponse } from "next/server"; const st = new StellarTools({ api_key: process.env.STELLAR_TOOLS_API_KEY!, }); export async function POST(req: NextRequest) { const body = await req.text(); const signature = req.headers.get("X-StellarTools-Signature")!; const event = st.webhooks.constructEvent(body, signature, process.env.STELLAR_TOOLS_WEBHOOK_SECRET!); switch (event.type) { case "payment.confirmed": { const payment = event.data.object; // payment.id, payment.customer_id, payment.amount, payment.transaction_hash break; } case "subscription.updated": { const subscription = event.data.object; const changed = event.data.previous_attributes; // check what changed: changed?.status, changed?.cancel_at_period_end, etc. break; } case "subscription.canceled": { const subscription = event.data.object; // revoke access for subscription.customer_id break; } case "refund.succeeded": { const refund = event.data.object; break; } } return NextResponse.json({ received: true }); } ``` If `constructEvent` throws, the signature is invalid or the payload was tampered with. Return a `400` and do not process the event. *** ## Retries If your endpoint returns a non-2xx response, StellarTools will retry the delivery. Check your webhook logs at [dashboard.stellartools.dev/webhooks](https://dashboard.stellartools.dev/webhooks) to see all delivery attempts and manually resend any failed events.