Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions content/docs/deployment/marketplace-handbook.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ the declared keys below are the ones publish actually sends.
| `readmePath` | A markdown file, relative to the manifest, read at publish time | `--readme-file`, `--readme` |
| `translations` | Per-locale `displayName`, `description`, `tagline`, `readme` and `screenshotCaptions` | — |

An official ObjectStack listing's `manifestId` is `app.objectstack.<app>` (`app.objectstack.hotcrm`, `app.objectstack.connector-github`); a partner listing uses its own reverse domain, `app.<partner>.<app>`.

[HotCRM](https://github.com/objectstack-ai/hotcrm/blob/main/objectstack.manifest.json)
is a worked example for the keys it carries:

Expand Down
112 changes: 112 additions & 0 deletions packages/apps/connector-stripe/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Stripe

Read Stripe customers, charges, invoices and payment intents from your flows.
The app calls the Stripe API (`https://api.stripe.com/`) through a connector
built from Stripe's own OpenAPI document, and exposes eight read operations as
connector actions. Nothing is written to Stripe.

## What it adds

- **A connector, `stripe`.** Its actions are Stripe's own operation ids, a
list and a retrieve for each of four resources:

| Resource | List | Retrieve |
|:---|:---|:---|
| Customers | `GetCustomers` (`GET /v1/customers`) | `GetCustomersCustomer` (`GET /v1/customers/{customer}`) |
| Charges | `GetCharges` (`GET /v1/charges`) | `GetChargesCharge` (`GET /v1/charges/{charge}`) |
| Invoices | `GetInvoices` (`GET /v1/invoices`) | `GetInvoicesInvoice` (`GET /v1/invoices/{invoice}`) |
| Payment intents | `GetPaymentIntents` (`GET /v1/payment_intents`) | `GetPaymentIntentsIntent` (`GET /v1/payment_intents/{intent}`) |

No other Stripe operation is in the package, so none can reach your flows
or agents.
- **Two flows that use it**, ready to run or to copy:
- `stripe_get_customer` takes a `customer_id` (for example `cus_123`) and
returns Stripe's customer object as `customer.body`.
- `stripe_list_open_invoices` takes a `customer_id` and returns that
customer's open invoices, at most ten, as `invoices.body` (the invoices
are in `invoices.body.data`).

The app adds no objects, no sample data and no demo users. An install adds the
connector and the two flows, and nothing else.

## Credentials

The app needs one credential. Until it is set, the `stripe` connector is listed
as disconnected and its flows fail, naming the credential.

| Credential | What it is | Where to get it | Permissions it needs |
|:---|:---|:---|:---|
| `STRIPE_RESTRICTED_KEY` | A Stripe restricted API key (it starts with `rk_live_`, or `rk_test_` in a sandbox), sent to Stripe as `Authorization: Bearer <key>` | Stripe Dashboard → **Developers → API keys** (<https://dashboard.stripe.com/apikeys>) → create a restricted key | **Read** on Customers, Charges, Invoices and PaymentIntents, and no other permission |

Use a restricted key, not your secret key (`sk_…`): a restricted key carries
only the permissions you give it, and this app only reads. A request for a
resource the key has no permission for is refused by Stripe with HTTP 403. The
step does not fail on it: the answer lands as `<nodeId>.status` 403,
`<nodeId>.ok` false and Stripe's error in `<nodeId>.body`, so a flow that
must stop on it checks `ok`.

Set the value in **Setup → Integrations → Connector Credentials**, or in the
install dialog right after installing. An environment variable named
`STRIPE_RESTRICTED_KEY` on the server takes precedence over a stored value.
Try the app with a sandbox key first; a live key reads your live account.

The key is only ever sent to Stripe. It is never stored in the app's metadata.

## Running it

```bash
curl -X POST "$OS_URL/api/v1/automation/stripe_get_customer/trigger" \
-H "Authorization: Bearer $SESSION_TOKEN" -H 'Content-Type: application/json' \
-d '{"params": {"customer_id": "cus_123"}}'
```

To use an answer elsewhere, add a `connector_action` node with
`connectorId: 'stripe'` to your own flow and read `<nodeId>.body` in a later
node. An action takes its path parameters as one `path` object and its query
parameters as one `query` object, for example
`query: { dialect: 'cel', source: '{"customer": record.stripe_customer_id, "limit": dyn(10)}' }`
(a CEL map's values share one type, so a literal beside a field read is
wrapped in `dyn(…)`).

## Limits

- Read only. The package carries Stripe's GET operations for four resources
and nothing else; Stripe's write operations take form-encoded bodies, which
this connector does not send.
- Query parameters are sent as plain `name=value` pairs. Stripe's bracketed
parameters, such as `expand[]` and the `created[gte]` range filters, are
not sent in Stripe's form, so leave them out.
- The response schemas in the packaged document are not expanded (see
Provenance): each response is described as its Stripe object type, without
its fields. The answer itself is Stripe's full JSON.

## Provenance

`openapi/stripe.openapi.json` is generated, not written by hand. It is
Stripe's OpenAPI document, `stripe/openapi` `openapi/spec3.json` at commit
`51610f5f061c9723730582a7638b8cc0e646640d` (OpenAPI 3.0.0, API version
`2026-09-30.endive`, 612 operations), trimmed to the eight operations above by
this repository's offline normalizer:

```bash
pnpm --filter @objectstack/connector-openapi normalize-openapi \
--in https://raw.githubusercontent.com/stripe/openapi/51610f5f061c9723730582a7638b8cc0e646640d/openapi/spec3.json \
--include-pattern '^Get(Customers(Customer)?|Charges(Charge)?|Invoices(Invoice)?|PaymentIntents(Intent)?)$' \
--trim --max-depth 0 \
--out packages/apps/connector-stripe/openapi/stripe.openapi.json
```

It printed 47,094 bytes, 8 operations trimmed from 612, 17 schema references
cut at `--max-depth 0` and none left, and sha256
`3aec4b32c8cda2adeb5abb8393f02c63ecaf02ce78ca8bacd697f20edb4b2ca5`, the hash
of the committed file. The package's tests recompute that hash and fail when
the file and this record disagree.

`--max-depth 0` keeps every parameter schema as Stripe wrote it, since those
are what a flow's input is checked against, and replaces each named response
schema with its type, title and description. At the default depth of 2 the
same eight operations write 2,574,259 bytes.

Re-normalize from a newer commit by running the command with that commit,
then update the commit and the sha256 here in the same change. Stripe's
document is MIT-licensed; its licence notice is `openapi/LICENSE`.
38 changes: 38 additions & 0 deletions packages/apps/connector-stripe/objectstack.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

import { defineStack } from '@objectstack/spec';
import { ConnectorOpenApiPlugin } from '@objectstack/connector-openapi';
import { StripeConnector } from './src/connectors/stripe.connector.js';
import { allFlows } from './src/flows/stripe.flows.js';

/**
* Stripe connector app (read only) — open edition (every key it uses runs on
* the open tier), so it lives in this repository and is validated by its CI
* against the runtime at HEAD. One declarative connector over Stripe's own
* OpenAPI document, two flows that use it; no objects, no secret. The listing
* is `objectstack.manifest.json`, and the README is what the administrator
* reads before installing.
*/
export default defineStack({
manifest: {
id: 'app.objectstack.connector-stripe',
namespace: 'stripe',
version: '0.1.0',
type: 'app',
name: 'Stripe',
description: 'Read Stripe customers, charges, invoices and payment intents from flows, over Stripe\'s OpenAPI document.',
engines: { protocol: '^18' },
},

// `automation` materializes the `connectors:` entry at boot and runs the
// flows; the executor below registers the `openapi` provider with it.
requires: ['automation'],

// The generic `openapi` executor, zero-arg: it contributes the provider
// factory only, which reads the packaged document through the host's
// package-file capability (confined to this package's root).
plugins: [new ConnectorOpenApiPlugin()],

connectors: [StripeConnector],
flows: allFlows,
});
13 changes: 13 additions & 0 deletions packages/apps/connector-stripe/objectstack.manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://schemas.objectstack.dev/template-manifest.json",
"manifestId": "app.objectstack.connector-stripe",
"name": "connector-stripe",
"specVersion": "^18.0.0",
"displayName": "Stripe",
"description": "Read Stripe customers, charges, invoices and payment intents from flows, over Stripe's own OpenAPI document. Read only.",
"category": "finance",
"tags": ["stripe", "openapi", "connector", "payments"],
"license": "Apache-2.0",
"homepageUrl": "https://github.com/objectstack-ai/objectstack/tree/main/packages/apps/connector-stripe",
"readmePath": "README.md"
}
21 changes: 21 additions & 0 deletions packages/apps/connector-stripe/openapi/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License

Copyright (c) 2011- Stripe, Inc. (https://stripe.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Loading
Loading