@doany-ai/sdk 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,6 +46,7 @@ const client = createClient({
46
46
  - `auth` — login, tokens, current user
47
47
  - `users` — user management
48
48
  - `integrations` — built-in integrations
49
+ - `payments` — take card payments through Stripe
49
50
  - `connectors` — third-party connectors
50
51
  - `functions` — invoke backend functions
51
52
  - `agents` — agent conversations
package/dist/client.js CHANGED
@@ -6,6 +6,7 @@ import { createSsoModule } from "./modules/sso.js";
6
6
  import { createConnectorsModule, createUserConnectorsModule, } from "./modules/connectors.js";
7
7
  import { getAccessToken } from "./utils/auth-utils.js";
8
8
  import { createFunctionsModule } from "./modules/functions.js";
9
+ import { createPaymentsModule } from "./modules/payments.js";
9
10
  import { createAgentsModule } from "./modules/agents.js";
10
11
  import { createAiGatewayModule } from "./modules/ai-gateway.js";
11
12
  import { createAppLogsModule } from "./modules/app-logs.js";
@@ -138,6 +139,7 @@ export function createClient(config) {
138
139
  getSocket,
139
140
  }),
140
141
  integrations: createIntegrationsModule(axiosClient, appId),
142
+ payments: createPaymentsModule(axiosClient, appId),
141
143
  connectors: createUserConnectorsModule(axiosClient, appId),
142
144
  auth: userAuthModule,
143
145
  functions: createFunctionsModule(functionsAxiosClient, appId, {
@@ -4,6 +4,7 @@ import type { AuthModule } from "./modules/auth.types.js";
4
4
  import type { SsoModule } from "./modules/sso.types.js";
5
5
  import type { ConnectorsModule, UserConnectorsModule } from "./modules/connectors.types.js";
6
6
  import type { FunctionsModule } from "./modules/functions.types.js";
7
+ import type { PaymentsModule } from "./modules/payments.types.js";
7
8
  import type { AgentsModule } from "./modules/agents.types.js";
8
9
  import type { AiGatewayModule } from "./modules/ai-gateway.types.js";
9
10
  import type { AppLogsModule } from "./modules/app-logs.types.js";
@@ -98,6 +99,8 @@ export interface DoanyClient {
98
99
  functions: FunctionsModule;
99
100
  /** {@link IntegrationsModule | Integrations module} for calling pre-built integration endpoints. */
100
101
  integrations: IntegrationsModule;
102
+ /** {@link PaymentsModule | Payments module} for taking card payments through Stripe. */
103
+ payments: PaymentsModule;
101
104
  /** Cleanup function to disconnect WebSocket connections. Call when you're done with the client. */
102
105
  cleanup: () => void;
103
106
  /**
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./mod
11
11
  export type { AgentsModule, AgentName, AgentNameRegistry, AgentConversation, AgentMessage, AgentMessageReasoning, AgentMessageToolCall, AgentMessageUsage, AgentMessageCustomContext, AgentMessageMetadata, CreateConversationParams, } from "./modules/agents.types.js";
12
12
  export type { AiGatewayModule, AiGatewayConnection, } from "./modules/ai-gateway.types.js";
13
13
  export type { AppLogsModule } from "./modules/app-logs.types.js";
14
+ export type { PaymentsModule, CheckoutLineItem, CreateCheckoutParams, CreateCheckoutResult, CheckoutSession, SubscriptionState, BillingPortalParams, } from "./modules/payments.types.js";
14
15
  export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
15
16
  export type { ConnectorsModule, UserConnectorsModule, } from "./modules/connectors.types.js";
16
17
  export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
@@ -0,0 +1,13 @@
1
+ import { AxiosInstance } from "axios";
2
+ import { PaymentsModule } from "./payments.types";
3
+ /**
4
+ * Creates the payments module for the Doany SDK.
5
+ *
6
+ * Deliberately thin: every decision that could go wrong — which Stripe
7
+ * environment, which account, where the customer is sent back to — is made by
8
+ * the backend from the request's own origin. Nothing here can override it, so
9
+ * generated app code cannot accidentally charge a real card from a preview.
10
+ *
11
+ * @internal
12
+ */
13
+ export declare function createPaymentsModule(axios: AxiosInstance, appId: string): PaymentsModule;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Creates the payments module for the Doany SDK.
3
+ *
4
+ * Deliberately thin: every decision that could go wrong — which Stripe
5
+ * environment, which account, where the customer is sent back to — is made by
6
+ * the backend from the request's own origin. Nothing here can override it, so
7
+ * generated app code cannot accidentally charge a real card from a preview.
8
+ *
9
+ * @internal
10
+ */
11
+ export function createPaymentsModule(axios, appId) {
12
+ // This client's response interceptor resolves requests to the response body.
13
+ // Axios's declared return type does not reflect that, so the results below
14
+ // are cast through `unknown`.
15
+ return {
16
+ async createCheckoutSession(params) {
17
+ const data = await axios.request({
18
+ method: "POST",
19
+ url: `/apps/${appId}/payments/checkout-session`,
20
+ data: params,
21
+ });
22
+ return data;
23
+ },
24
+ async getSubscription(subscriptionId) {
25
+ const data = await axios.request({
26
+ method: "GET",
27
+ url: `/apps/${appId}/payments/subscription/${encodeURIComponent(subscriptionId)}`,
28
+ });
29
+ return data;
30
+ },
31
+ async createBillingPortalSession(params) {
32
+ const data = await axios.request({
33
+ method: "POST",
34
+ url: `/apps/${appId}/payments/billing-portal`,
35
+ data: params,
36
+ });
37
+ return data;
38
+ },
39
+ async getCheckoutSession(sessionId) {
40
+ const data = await axios.request({
41
+ method: "GET",
42
+ url: `/apps/${appId}/payments/checkout-session/${encodeURIComponent(sessionId)}`,
43
+ });
44
+ return data;
45
+ },
46
+ };
47
+ }
@@ -0,0 +1,228 @@
1
+ /**
2
+ * One thing being sold in a checkout.
3
+ *
4
+ * A line item names a record in your own app data — doany keeps your catalog
5
+ * there rather than as objects inside Stripe, which is why switching from test
6
+ * to real payments needs no migration.
7
+ */
8
+ export type CheckoutLineItem = {
9
+ /**
10
+ * The `id` of a record in this app's `Product` entity.
11
+ *
12
+ * The price, name and currency all come from that record — you cannot pass
13
+ * a price. This endpoint is reachable by anyone (a shop has to sell to
14
+ * visitors who never signed in), so an amount taken from the caller would
15
+ * let a buyer set their own.
16
+ */
17
+ product_id: string;
18
+ /** Defaults to 1. */
19
+ quantity?: number;
20
+ };
21
+ export type CreateCheckoutParams = {
22
+ line_items: CheckoutLineItem[];
23
+ /**
24
+ * Three-letter code, lowercase. Defaults to `usd`.
25
+ *
26
+ * A `currency` on the `Product` record wins over this, and a product without
27
+ * one takes this value. All line items in one checkout must end up on the
28
+ * same currency — Stripe puts it on the session, not the line item — or the
29
+ * call is refused with `MIXED_CURRENCY_BASKET`.
30
+ */
31
+ currency?: string;
32
+ /**
33
+ * Where the customer lands after paying, as a path on your own site.
34
+ *
35
+ * Must be a path, not a full URL — the origin is added for you, which is
36
+ * what guarantees a checkout started in the preview comes back to the
37
+ * preview rather than to your published site.
38
+ *
39
+ * `session_id` is appended so the page can confirm the payment.
40
+ */
41
+ success_path?: string;
42
+ /** Where the customer lands if they abandon checkout. Also a path. */
43
+ cancel_path?: string;
44
+ /** Prefills the email field. Skip it and Stripe asks. */
45
+ customer_email?: string;
46
+ /** Your own data, returned with the order. Up to 20 keys. */
47
+ metadata?: Record<string, string>;
48
+ };
49
+ export type CreateCheckoutResult = {
50
+ id: string;
51
+ /** Send the customer here. */
52
+ url: string;
53
+ /** `test` in the preview, `live` on a published site that has gone live. */
54
+ mode: "test" | "live";
55
+ /**
56
+ * What the customer is about to agree to, decided by the catalog.
57
+ *
58
+ * `subscription` when the products carry a `recurring_interval`, `payment`
59
+ * otherwise. Say the right word on the button: "Subscribe" over a one-off
60
+ * charge, or "Buy" over a recurring one, is a complaint waiting to happen.
61
+ */
62
+ billing: "payment" | "subscription";
63
+ /** `month` / `year` / `week` for a subscription, `null` for a one-off. */
64
+ interval: string | null;
65
+ };
66
+ export type SubscriptionState = {
67
+ id: string;
68
+ mode: "test" | "live";
69
+ /** Stripe's own vocabulary: active / trialing / past_due / canceled / unpaid /
70
+ * incomplete / incomplete_expired / paused. */
71
+ status: string;
72
+ /** THE field to gate access on — true for `active` and `trialing`. */
73
+ active: boolean;
74
+ /** Cancelled, but the period they already paid for is still running.
75
+ * Keep access until `current_period_end`. */
76
+ cancel_at_period_end: boolean;
77
+ current_period_end: number | null;
78
+ canceled_at: number | null;
79
+ customer_id: string | null;
80
+ interval: string | null;
81
+ amount: number | null;
82
+ currency: string | null;
83
+ metadata: Record<string, string>;
84
+ };
85
+ export type BillingPortalParams = {
86
+ /** From `getCheckoutSession(...).customer_id`. Store it when they subscribe. */
87
+ customer_id: string;
88
+ /** Where Stripe returns them, as a path on your own site. */
89
+ return_path?: string;
90
+ };
91
+ export type CheckoutSession = {
92
+ id: string;
93
+ mode: "test" | "live";
94
+ /** The one field a success page should branch on. */
95
+ paid: boolean;
96
+ payment_status: string;
97
+ amount_total: number | null;
98
+ currency: string | null;
99
+ customer_email: string | null;
100
+ /**
101
+ * Whether the money came back, in three honest states.
102
+ *
103
+ * Computed from the amounts rather than from Stripe's own `refunded` flag,
104
+ * which is only true for a FULL refund — an app reading that flag keeps
105
+ * serving someone who was refunded most of their money.
106
+ *
107
+ * Re-read this before granting access to anything valuable; a checkout that
108
+ * was `paid` in March can be `full` in April.
109
+ */
110
+ refund_status: "none" | "partial" | "full";
111
+ /** Minor units, same scale as `amount_total`. */
112
+ amount_refunded: number;
113
+ /** The customer went to their bank. Money is already gone, whatever the
114
+ * refund status says. */
115
+ disputed: boolean;
116
+ /** `subscription` when this checkout started a plan. */
117
+ billing: "payment" | "subscription";
118
+ /** Present for a subscription checkout. Store it — it is what reads the
119
+ * plan's state on every later page load. */
120
+ subscription_id: string | null;
121
+ /** Store this too: it is what opens the billing portal so the customer can
122
+ * cancel, without the app being given any power over subscriptions. */
123
+ customer_id: string | null;
124
+ line_items: Array<{
125
+ name: string | null;
126
+ quantity: number | null;
127
+ amount_total: number | null;
128
+ }>;
129
+ metadata: Record<string, string>;
130
+ };
131
+ /**
132
+ * Take card payments on your site.
133
+ *
134
+ * Money goes to the app owner's own Stripe account — doany never holds it and
135
+ * takes no cut. Stripe's usual per-transaction fee applies.
136
+ *
137
+ * ## Prices live in your data, not in your code
138
+ *
139
+ * Sellable things are records in this app's `Product` entity, with the price in
140
+ * an integer `price_cents` field. A checkout names the product; the server
141
+ * looks up what it costs.
142
+ *
143
+ * ## Test and real payments
144
+ *
145
+ * Which one you get is decided by the address the site is served from, not by
146
+ * anything in your code:
147
+ *
148
+ * - **Preview** — test payments. Card `4242 4242 4242 4242` with any future
149
+ * expiry runs the whole flow and charges nothing.
150
+ * - **Published site** — real payments, once the owner has gone live from the
151
+ * Payments panel. Before that, {@linkcode PaymentsModule.createCheckoutSession}
152
+ * fails with `PAYMENTS_NOT_LIVE` rather than quietly taking no money.
153
+ *
154
+ * The same code covers both. There is no key to configure and no mode to set.
155
+ */
156
+ export interface PaymentsModule {
157
+ /**
158
+ * Opens a Stripe checkout and returns the URL to send the customer to.
159
+ *
160
+ * @example Sell one item
161
+ * ```typescript
162
+ * // The price comes from the Product record, not from this call.
163
+ * const { url } = await doany.payments.createCheckoutSession({
164
+ * line_items: [{ product_id: product.id, quantity: 1 }],
165
+ * success_path: '/thanks',
166
+ * cancel_path: '/shop',
167
+ * metadata: { order_id: order.id },
168
+ * });
169
+ * window.location.href = url;
170
+ * ```
171
+ */
172
+ createCheckoutSession(params: CreateCheckoutParams): Promise<CreateCheckoutResult>;
173
+ /**
174
+ * Reads a checkout back — what a success page calls to confirm the payment.
175
+ *
176
+ * Confirm here rather than waiting for a webhook: the customer is already
177
+ * looking at your thank-you page, and the webhook may not have arrived.
178
+ *
179
+ * @example Confirm on the success page, once
180
+ * ```typescript
181
+ * const sessionId = new URLSearchParams(location.search).get('session_id');
182
+ * const session = await doany.payments.getCheckoutSession(sessionId);
183
+ * if (session.paid) {
184
+ * // Key the fulfilment on session.id so a refresh cannot deliver twice.
185
+ * await Order.update(session.metadata.order_id, { status: 'paid' });
186
+ * }
187
+ * ```
188
+ */
189
+ getCheckoutSession(sessionId: string): Promise<CheckoutSession>;
190
+ /**
191
+ * Is this plan still active?
192
+ *
193
+ * Call it on every page that gates something behind a plan. A subscription
194
+ * outlives the checkout that created it and changes without your app doing
195
+ * anything — renewals, a failed card, a cancellation made in the portal — so
196
+ * a value stored at signup goes stale on its own.
197
+ *
198
+ * @example Gate a page
199
+ * ```typescript
200
+ * const plan = await doany.payments.getSubscription(user.subscription_id);
201
+ * if (!plan.active) return <Upgrade />;
202
+ * if (plan.cancel_at_period_end) {
203
+ * // still paid up — keep access, but say when it ends
204
+ * }
205
+ * ```
206
+ */
207
+ getSubscription(subscriptionId: string): Promise<SubscriptionState>;
208
+ /**
209
+ * Opens Stripe's own page for cancelling, switching plan, or fixing a card.
210
+ *
211
+ * This is why there is no `cancelSubscription` here: cancelling happens on
212
+ * Stripe's hosted page, so no endpoint in your app can modify a
213
+ * subscription, and a bug in your code cannot cancel somebody's plan. What
214
+ * the page offers is configured by the app owner in their Stripe dashboard.
215
+ *
216
+ * @example A "Manage billing" button
217
+ * ```typescript
218
+ * const { url } = await doany.payments.createBillingPortalSession({
219
+ * customer_id: user.stripe_customer_id,
220
+ * return_path: '/account',
221
+ * });
222
+ * window.location.href = url;
223
+ * ```
224
+ */
225
+ createBillingPortalSession(params: BillingPortalParams): Promise<{
226
+ url: string;
227
+ }>;
228
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doany-ai/sdk",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "JavaScript SDK for the doany app platform (API-compatible fork of @base44/sdk)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",