@molecule/api-payments-stripe 1.0.0

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.
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Stripe Connect support for the Stripe payment provider.
3
+ *
4
+ * Adds split-payment / payouts helpers on top of the existing
5
+ * `@molecule/api-payments-stripe` bond surface so multi-vendor / two-sided
6
+ * marketplace apps (course-marketplace, food-delivery, multi-vendor-marketplace,
7
+ * rental-marketplace, tutoring-marketplace, venue-booking, crowdfunding,
8
+ * pet-care, telemedicine, etc.) can:
9
+ * - Onboard sellers / drivers / providers (`createConnectedAccount` + `createAccountLink`).
10
+ * - Move funds into connected accounts (`createTransfer`).
11
+ * - Trigger payouts (`createPayout`).
12
+ * - Inspect onboarding/eligibility status (`getAccountStatus`).
13
+ * - Handle Connect-specific webhooks (`processConnectWebhook`).
14
+ *
15
+ * @see https://stripe.com/docs/connect
16
+ *
17
+ * @module
18
+ */
19
+ /**
20
+ * Connected account type.
21
+ *
22
+ * Stripe distinguishes three account types with different
23
+ * onboarding / dashboard responsibilities. See
24
+ * https://stripe.com/docs/connect/accounts.
25
+ */
26
+ export type ConnectedAccountType = 'standard' | 'express' | 'custom';
27
+ /**
28
+ * Subset of Stripe `business_profile` fields commonly set during marketplace
29
+ * onboarding.
30
+ */
31
+ export interface ConnectedAccountBusinessProfile {
32
+ name?: string;
33
+ url?: string;
34
+ productDescription?: string;
35
+ supportEmail?: string;
36
+ supportPhone?: string;
37
+ mcc?: string;
38
+ }
39
+ /**
40
+ * Parameters for creating a connected account.
41
+ */
42
+ export interface CreateConnectedAccountParams {
43
+ /** Stripe account type — `standard`, `express`, or `custom`. */
44
+ type: ConnectedAccountType;
45
+ /** Two-letter ISO country code for the account holder (e.g. `US`, `GB`). */
46
+ country: string;
47
+ /** Email address of the account holder. */
48
+ email: string;
49
+ /** Optional business profile fields (display name, website, MCC, etc.). */
50
+ businessProfile?: ConnectedAccountBusinessProfile;
51
+ /** Optional metadata to attach to the connected account. */
52
+ metadata?: Record<string, string>;
53
+ /** Optional idempotency key for safe retries. */
54
+ idempotencyKey?: string;
55
+ }
56
+ /**
57
+ * Result of creating a connected account.
58
+ */
59
+ export interface CreateConnectedAccountResult {
60
+ /** Stripe connected account ID (`acct_...`). */
61
+ id: string;
62
+ /** Optional onboarding URL (only present when an account link is created in the same flow). */
63
+ accountLinkUrl?: string;
64
+ }
65
+ /**
66
+ * Account-link types — onboarding (first-time) vs update (returning).
67
+ */
68
+ export type AccountLinkType = 'account_onboarding' | 'account_update';
69
+ /**
70
+ * Parameters for creating an account link.
71
+ */
72
+ export interface CreateAccountLinkParams {
73
+ /** Stripe connected account ID (`acct_...`). */
74
+ accountId: string;
75
+ /** URL Stripe redirects the user back to after onboarding completes. */
76
+ returnUrl: string;
77
+ /** URL Stripe redirects the user to if the link expires before completion. */
78
+ refreshUrl: string;
79
+ /** Whether this is a first-time onboarding link or an update link. */
80
+ type: AccountLinkType;
81
+ /** Optional idempotency key for safe retries. */
82
+ idempotencyKey?: string;
83
+ }
84
+ /**
85
+ * Result of creating an account link.
86
+ */
87
+ export interface CreateAccountLinkResult {
88
+ /** Hosted onboarding URL the connected account holder should open. */
89
+ url: string;
90
+ /** Unix timestamp (seconds) when the link expires. */
91
+ expiresAt: number;
92
+ }
93
+ /**
94
+ * Parameters for creating a transfer to a connected account.
95
+ */
96
+ export interface CreateTransferParams {
97
+ /** Amount in the smallest currency unit (e.g. cents for USD). */
98
+ amount: number;
99
+ /** Three-letter ISO currency code, lowercase (e.g. `usd`). */
100
+ currency: string;
101
+ /** Destination connected account ID (`acct_...`). */
102
+ destination: string;
103
+ /** Optional source charge to attach the transfer to (for separate-charges-and-transfers flow). */
104
+ sourceTransaction?: string;
105
+ /** Optional transfer group string (groups related transfers/charges together). */
106
+ transferGroup?: string;
107
+ /** Optional metadata. */
108
+ metadata?: Record<string, string>;
109
+ /** Optional idempotency key for safe retries. */
110
+ idempotencyKey?: string;
111
+ }
112
+ /**
113
+ * Result of creating a transfer.
114
+ */
115
+ export interface CreateTransferResult {
116
+ /** Stripe transfer ID (`tr_...`). */
117
+ id: string;
118
+ /** Amount transferred (smallest currency unit). */
119
+ amount: number;
120
+ /** Three-letter ISO currency code. */
121
+ currency: string;
122
+ /** Destination connected account ID. */
123
+ destination: string;
124
+ /** Transfer group, if set. */
125
+ transferGroup?: string;
126
+ }
127
+ /**
128
+ * Parameters for creating a payout from a connected account's Stripe balance to its bank account.
129
+ */
130
+ export interface CreatePayoutParams {
131
+ /** Connected account ID to issue the payout from (`acct_...`). */
132
+ accountId: string;
133
+ /** Amount in the smallest currency unit (e.g. cents for USD). */
134
+ amount: number;
135
+ /** Three-letter ISO currency code, lowercase (e.g. `usd`). */
136
+ currency: string;
137
+ /** Optional payout method — `standard` or `instant`. */
138
+ method?: 'standard' | 'instant';
139
+ /** Optional metadata. */
140
+ metadata?: Record<string, string>;
141
+ /** Optional idempotency key for safe retries. */
142
+ idempotencyKey?: string;
143
+ }
144
+ /**
145
+ * Result of creating a payout.
146
+ */
147
+ export interface CreatePayoutResult {
148
+ /** Stripe payout ID (`po_...`). */
149
+ id: string;
150
+ /** Amount paid out (smallest currency unit). */
151
+ amount: number;
152
+ /** Three-letter ISO currency code. */
153
+ currency: string;
154
+ /** Payout status (e.g. `pending`, `paid`, `failed`). */
155
+ status: string;
156
+ /** Estimated arrival date (Unix timestamp in seconds). */
157
+ arrivalDate: number;
158
+ }
159
+ /**
160
+ * Connected-account onboarding / payout eligibility status.
161
+ */
162
+ export interface AccountStatus {
163
+ /** Stripe connected account ID. */
164
+ id: string;
165
+ /** Whether the account can accept charges. */
166
+ chargesEnabled: boolean;
167
+ /** Whether the account can receive payouts. */
168
+ payoutsEnabled: boolean;
169
+ /** Whether there are any currently-due requirements (Stripe is blocked on something). */
170
+ requirementsCurrent: boolean;
171
+ /** Stripe connected-account type (`standard`, `express`, `custom`), if known. */
172
+ type?: ConnectedAccountType;
173
+ /** Currently-due requirement IDs from Stripe (empty when nothing is due). */
174
+ currentlyDue: readonly string[];
175
+ }
176
+ /**
177
+ * Connect webhook event types this provider knows how to interpret.
178
+ *
179
+ * `unknown` is returned for any other Stripe event so callers can fall
180
+ * through to the standard subscription webhook handler if needed.
181
+ */
182
+ export type ConnectWebhookEventType = 'account.updated' | 'payout.created' | 'transfer.created' | 'application_fee.refunded' | 'unknown';
183
+ /**
184
+ * Normalized Connect webhook event.
185
+ *
186
+ * Provider-agnostic shape so consumers don't have to import Stripe types
187
+ * to dispatch on event kind.
188
+ */
189
+ export interface ConnectWebhookEvent {
190
+ /** Recognized Connect event type, or `unknown` for unrelated events. */
191
+ type: ConnectWebhookEventType;
192
+ /** Original Stripe event type string (e.g. `account.updated`). */
193
+ rawType: string;
194
+ /** The ID of the primary resource this event is about (account, payout, transfer, fee). */
195
+ resourceId?: string;
196
+ /** The connected account this event applies to, if Stripe sent one. */
197
+ accountId?: string;
198
+ /** Raw event-data object (plain JSON shape — never the live Stripe object). */
199
+ data: Record<string, unknown>;
200
+ }
201
+ /**
202
+ * Creates a Stripe connected account for a marketplace seller / driver / provider.
203
+ *
204
+ * @param params - Connected-account creation parameters.
205
+ * @returns The new account ID.
206
+ */
207
+ export declare const createConnectedAccount: (params: CreateConnectedAccountParams) => Promise<CreateConnectedAccountResult>;
208
+ /**
209
+ * Creates a Stripe account link the connected account holder uses to finish
210
+ * onboarding (or to update payout details).
211
+ *
212
+ * @param params - Account-link creation parameters.
213
+ * @returns The hosted onboarding/update URL and its expiry (Unix timestamp, seconds).
214
+ */
215
+ export declare const createAccountLink: (params: CreateAccountLinkParams) => Promise<CreateAccountLinkResult>;
216
+ /**
217
+ * Transfers funds from the platform balance to a connected account.
218
+ *
219
+ * @param params - Transfer parameters.
220
+ * @returns The created transfer.
221
+ */
222
+ export declare const createTransfer: (params: CreateTransferParams) => Promise<CreateTransferResult>;
223
+ /**
224
+ * Issues a payout from a connected account's Stripe balance to its bank account.
225
+ *
226
+ * Uses Stripe's `Stripe-Account` header to scope the call to the connected account.
227
+ *
228
+ * @param params - Payout parameters.
229
+ * @returns The created payout.
230
+ */
231
+ export declare const createPayout: (params: CreatePayoutParams) => Promise<CreatePayoutResult>;
232
+ /**
233
+ * Looks up a connected account's onboarding / payout status.
234
+ *
235
+ * @param accountId - Stripe connected account ID (`acct_...`).
236
+ * @returns Normalized account status.
237
+ */
238
+ export declare const getAccountStatus: (accountId: string) => Promise<AccountStatus>;
239
+ /**
240
+ * Verifies and normalizes a Stripe Connect webhook event.
241
+ *
242
+ * Reuses the same `STRIPE_WEBHOOK_SECRET` env var as the standard webhook
243
+ * pipeline (and the same signature verification logic) — Connect events
244
+ * arrive on the same webhook endpoint when the platform's webhook is
245
+ * configured to receive Connect events.
246
+ *
247
+ * @param headers - Request headers (looks up `stripe-signature`).
248
+ * @param body - The raw request body (string or Buffer).
249
+ * @returns The verified, normalized Connect webhook event.
250
+ */
251
+ export declare const processConnectWebhook: (headers: Record<string, string | string[] | undefined>, body: string | Buffer) => ConnectWebhookEvent;
252
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Stripe Connect support for the Stripe payment provider.
3
+ *
4
+ * Adds split-payment / payouts helpers on top of the existing
5
+ * `@molecule/api-payments-stripe` bond surface so multi-vendor / two-sided
6
+ * marketplace apps (course-marketplace, food-delivery, multi-vendor-marketplace,
7
+ * rental-marketplace, tutoring-marketplace, venue-booking, crowdfunding,
8
+ * pet-care, telemedicine, etc.) can:
9
+ * - Onboard sellers / drivers / providers (`createConnectedAccount` + `createAccountLink`).
10
+ * - Move funds into connected accounts (`createTransfer`).
11
+ * - Trigger payouts (`createPayout`).
12
+ * - Inspect onboarding/eligibility status (`getAccountStatus`).
13
+ * - Handle Connect-specific webhooks (`processConnectWebhook`).
14
+ *
15
+ * @see https://stripe.com/docs/connect
16
+ *
17
+ * @module
18
+ */
19
+ import { getLogger } from '@molecule/api-bond';
20
+ import { getClient, verifyWebhookSignature } from './provider.js';
21
+ const logger = getLogger();
22
+ /**
23
+ * Maps the in-bond business-profile shape to Stripe SDK params.
24
+ *
25
+ * @param profile - The optional business profile.
26
+ * @returns A Stripe-compatible business_profile object, or `undefined` if no profile was provided.
27
+ */
28
+ const toStripeBusinessProfile = (profile) => {
29
+ if (!profile)
30
+ return undefined;
31
+ const out = {};
32
+ if (profile.name !== undefined)
33
+ out.name = profile.name;
34
+ if (profile.url !== undefined)
35
+ out.url = profile.url;
36
+ if (profile.productDescription !== undefined)
37
+ out.product_description = profile.productDescription;
38
+ if (profile.supportEmail !== undefined)
39
+ out.support_email = profile.supportEmail;
40
+ if (profile.supportPhone !== undefined)
41
+ out.support_phone = profile.supportPhone;
42
+ if (profile.mcc !== undefined)
43
+ out.mcc = profile.mcc;
44
+ return out;
45
+ };
46
+ /**
47
+ * Creates a Stripe connected account for a marketplace seller / driver / provider.
48
+ *
49
+ * @param params - Connected-account creation parameters.
50
+ * @returns The new account ID.
51
+ */
52
+ export const createConnectedAccount = async (params) => {
53
+ try {
54
+ const account = await getClient().accounts.create({
55
+ type: params.type,
56
+ country: params.country,
57
+ email: params.email,
58
+ business_profile: toStripeBusinessProfile(params.businessProfile),
59
+ metadata: params.metadata,
60
+ }, params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : undefined);
61
+ return { id: account.id };
62
+ }
63
+ catch (error) {
64
+ logger.error('Error creating Stripe connected account:', error);
65
+ throw error;
66
+ }
67
+ };
68
+ /**
69
+ * Creates a Stripe account link the connected account holder uses to finish
70
+ * onboarding (or to update payout details).
71
+ *
72
+ * @param params - Account-link creation parameters.
73
+ * @returns The hosted onboarding/update URL and its expiry (Unix timestamp, seconds).
74
+ */
75
+ export const createAccountLink = async (params) => {
76
+ try {
77
+ const link = await getClient().accountLinks.create({
78
+ account: params.accountId,
79
+ return_url: params.returnUrl,
80
+ refresh_url: params.refreshUrl,
81
+ type: params.type,
82
+ }, params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : undefined);
83
+ return { url: link.url, expiresAt: link.expires_at };
84
+ }
85
+ catch (error) {
86
+ logger.error('Error creating Stripe account link:', error);
87
+ throw error;
88
+ }
89
+ };
90
+ /**
91
+ * Transfers funds from the platform balance to a connected account.
92
+ *
93
+ * @param params - Transfer parameters.
94
+ * @returns The created transfer.
95
+ */
96
+ export const createTransfer = async (params) => {
97
+ try {
98
+ const transfer = await getClient().transfers.create({
99
+ amount: params.amount,
100
+ currency: params.currency,
101
+ destination: params.destination,
102
+ source_transaction: params.sourceTransaction,
103
+ transfer_group: params.transferGroup,
104
+ metadata: params.metadata,
105
+ }, params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : undefined);
106
+ return {
107
+ id: transfer.id,
108
+ amount: transfer.amount,
109
+ currency: transfer.currency,
110
+ destination: typeof transfer.destination === 'string'
111
+ ? transfer.destination
112
+ : transfer.destination?.id || '',
113
+ transferGroup: transfer.transfer_group ?? undefined,
114
+ };
115
+ }
116
+ catch (error) {
117
+ logger.error('Error creating Stripe transfer:', error);
118
+ throw error;
119
+ }
120
+ };
121
+ /**
122
+ * Issues a payout from a connected account's Stripe balance to its bank account.
123
+ *
124
+ * Uses Stripe's `Stripe-Account` header to scope the call to the connected account.
125
+ *
126
+ * @param params - Payout parameters.
127
+ * @returns The created payout.
128
+ */
129
+ export const createPayout = async (params) => {
130
+ try {
131
+ const requestOptions = { stripeAccount: params.accountId };
132
+ if (params.idempotencyKey)
133
+ requestOptions.idempotencyKey = params.idempotencyKey;
134
+ const payout = await getClient().payouts.create({
135
+ amount: params.amount,
136
+ currency: params.currency,
137
+ method: params.method,
138
+ metadata: params.metadata,
139
+ }, requestOptions);
140
+ return {
141
+ id: payout.id,
142
+ amount: payout.amount,
143
+ currency: payout.currency,
144
+ status: payout.status,
145
+ arrivalDate: payout.arrival_date,
146
+ };
147
+ }
148
+ catch (error) {
149
+ logger.error('Error creating Stripe payout:', error);
150
+ throw error;
151
+ }
152
+ };
153
+ /**
154
+ * Looks up a connected account's onboarding / payout status.
155
+ *
156
+ * @param accountId - Stripe connected account ID (`acct_...`).
157
+ * @returns Normalized account status.
158
+ */
159
+ export const getAccountStatus = async (accountId) => {
160
+ try {
161
+ const account = await getClient().accounts.retrieve(accountId);
162
+ const currentlyDue = account.requirements?.currently_due ?? [];
163
+ return {
164
+ id: account.id,
165
+ chargesEnabled: account.charges_enabled === true,
166
+ payoutsEnabled: account.payouts_enabled === true,
167
+ requirementsCurrent: currentlyDue.length === 0,
168
+ type: account.type,
169
+ currentlyDue: [...currentlyDue],
170
+ };
171
+ }
172
+ catch (error) {
173
+ logger.error('Error retrieving Stripe connected account:', error);
174
+ throw error;
175
+ }
176
+ };
177
+ /**
178
+ * Maps a Stripe event type to the recognized Connect event type.
179
+ *
180
+ * @param raw - The raw Stripe event type.
181
+ * @returns The recognized event type, or `unknown`.
182
+ */
183
+ const toConnectEventType = (raw) => {
184
+ switch (raw) {
185
+ case 'account.updated':
186
+ case 'payout.created':
187
+ case 'transfer.created':
188
+ case 'application_fee.refunded':
189
+ return raw;
190
+ default:
191
+ return 'unknown';
192
+ }
193
+ };
194
+ /**
195
+ * Verifies and normalizes a Stripe Connect webhook event.
196
+ *
197
+ * Reuses the same `STRIPE_WEBHOOK_SECRET` env var as the standard webhook
198
+ * pipeline (and the same signature verification logic) — Connect events
199
+ * arrive on the same webhook endpoint when the platform's webhook is
200
+ * configured to receive Connect events.
201
+ *
202
+ * @param headers - Request headers (looks up `stripe-signature`).
203
+ * @param body - The raw request body (string or Buffer).
204
+ * @returns The verified, normalized Connect webhook event.
205
+ */
206
+ export const processConnectWebhook = (headers, body) => {
207
+ const sigHeader = headers['stripe-signature'];
208
+ const signature = Array.isArray(sigHeader) ? sigHeader[0] : sigHeader;
209
+ if (!signature) {
210
+ throw new Error('Missing stripe-signature header');
211
+ }
212
+ const verified = verifyWebhookSignature(body, signature);
213
+ const rawType = verified.type;
214
+ const data = verified.data.object;
215
+ const accountIdFromHeader = headers['stripe-account'];
216
+ const accountId = Array.isArray(accountIdFromHeader)
217
+ ? accountIdFromHeader[0]
218
+ : accountIdFromHeader;
219
+ const resourceId = typeof data.id === 'string' ? data.id : undefined;
220
+ const dataAccount = typeof data.account === 'string' ? data.account : undefined;
221
+ return {
222
+ type: toConnectEventType(rawType),
223
+ rawType,
224
+ resourceId,
225
+ accountId: accountId || dataAccount,
226
+ data,
227
+ };
228
+ };
229
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Stripe payment provider for molecule.dev.
3
+ *
4
+ * @see https://www.npmjs.com/package/stripe
5
+ *
6
+ * @remarks
7
+ * Bond this as the payments provider so `@molecule/api-payments`'s `verifySubscription` (and
8
+ * the payment resource) work server-side — don't call the Stripe SDK directly for
9
+ * verification. Env: `STRIPE_SECRET_KEY` + `STRIPE_WEBHOOK_SECRET` are SERVER-ONLY; only the
10
+ * publishable key (`pk_…`) is client-side.
11
+ *
12
+ * **You do NOT need molecule's Express app, the `bond()` wiring, or the UI packages to use
13
+ * this — the functions below are framework-agnostic.** On a non-Express / non-molecule host
14
+ * (Next.js App Router, serverless functions, Hono, Fastify), import them and call them from
15
+ * your OWN route handlers:
16
+ * `import { createCheckoutSession, verifyWebhookSignature, getSubscription } from '@molecule/api-payments-stripe'`.
17
+ * They cover the whole flow — {@link createCheckoutSession} (server-owned `priceId`, so you
18
+ * never take a price/amount from the client), {@link createPortalSession} (hosted Billing
19
+ * Portal for payment-method updates/cancellation/invoices), {@link verifyWebhookSignature},
20
+ * and the subscription getters/updaters — and carry the security contract (config-not-configured
21
+ * errors, normalized status) for free, so reach for these instead of hand-rolling raw
22
+ * `stripe` calls. In a Next.js App Router route, read the RAW webhook body with
23
+ * `await req.text()` and the header with `req.headers.get('stripe-signature')`, then
24
+ * `verifyWebhookSignature(rawBody, signature)` (the `express.raw(...)` note below is the
25
+ * Express-host equivalent). Only `@molecule/api-middleware-billing-routes` (Express glue) and
26
+ * `@molecule/app-billing-react` (molecule UI) are framework-coupled — skip THOSE on such a
27
+ * host, but still use these bond functions underneath.
28
+ *
29
+ * Two things a weak Stripe integration gets wrong:
30
+ *
31
+ * - **The webhook body MUST be RAW for signature verification.** {@link verifyWebhookSignature}
32
+ * (Stripe's `constructEvent`) hashes the exact bytes, so a parsed-then-re-serialized body
33
+ * ALWAYS fails. In a molecule app you need NO special middleware: the always-included
34
+ * `@molecule/api-middleware-body-parser-express` already captures the unparsed body as
35
+ * `req.rawBody` (a string) on EVERY request — just pass `req.rawBody` + the `stripe-signature`
36
+ * header to {@link verifyWebhookSignature}. Do NOT add a route-specific `express.raw(...)` here —
37
+ * it is redundant and fights the global JSON parser (which has already consumed the stream and
38
+ * set `req.rawBody`). (ONLY on a NON-molecule Express host that lacks `req.rawBody` do you mount
39
+ * `express.raw({ type: 'application/json' })` before the JSON parser; on Next.js App Router read
40
+ * the raw body with `await req.text()`.) NEVER act on an unverified webhook body — it is
41
+ * attacker-controlled.
42
+ * - **Webhooks are redelivered — be idempotent.** Stripe retries until it gets a 2xx, so the
43
+ * same `event.id` can arrive twice. Dedupe on it (the payment record's
44
+ * `UNIQUE(platformKey, transactionId)` already blocks a double-grant), and return 2xx once
45
+ * handled so Stripe stops retrying.
46
+ *
47
+ * Create checkout with SERVER-configured price ids ({@link createCheckoutSession}) — never an
48
+ * amount sent by the client.
49
+ *
50
+ * **A missing `STRIPE_SECRET_KEY` is NOT the same as "no active subscription."**
51
+ * `getClient()` throws a tagged config-not-configured error; `verifySubscription`,
52
+ * `updateSubscription`, and `cancelSubscription` on {@link paymentProvider} detect
53
+ * that tag (`isConfigNotConfiguredError` from `@molecule/api-payments`) and
54
+ * RETHROW it instead of swallowing it into the same `null` / `{ updated: false }`
55
+ * / `false` a genuine verification/update failure returns — so a caller (or its
56
+ * own catch block) can tell "the operator forgot to set the secret" apart from
57
+ * "this subscription/card is invalid" and surface the actionable 503 instead of
58
+ * a generic 400/500.
59
+ *
60
+ * @module
61
+ */
62
+ export * from './bondAdapter.js';
63
+ export * from './browser-guard.js';
64
+ export * from './connect.js';
65
+ export * from './provider.js';
66
+ export * from './secrets.js';
67
+ export * from './types.js';
68
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Stripe payment provider for molecule.dev.
3
+ *
4
+ * @see https://www.npmjs.com/package/stripe
5
+ *
6
+ * @remarks
7
+ * Bond this as the payments provider so `@molecule/api-payments`'s `verifySubscription` (and
8
+ * the payment resource) work server-side — don't call the Stripe SDK directly for
9
+ * verification. Env: `STRIPE_SECRET_KEY` + `STRIPE_WEBHOOK_SECRET` are SERVER-ONLY; only the
10
+ * publishable key (`pk_…`) is client-side.
11
+ *
12
+ * **You do NOT need molecule's Express app, the `bond()` wiring, or the UI packages to use
13
+ * this — the functions below are framework-agnostic.** On a non-Express / non-molecule host
14
+ * (Next.js App Router, serverless functions, Hono, Fastify), import them and call them from
15
+ * your OWN route handlers:
16
+ * `import { createCheckoutSession, verifyWebhookSignature, getSubscription } from '@molecule/api-payments-stripe'`.
17
+ * They cover the whole flow — {@link createCheckoutSession} (server-owned `priceId`, so you
18
+ * never take a price/amount from the client), {@link createPortalSession} (hosted Billing
19
+ * Portal for payment-method updates/cancellation/invoices), {@link verifyWebhookSignature},
20
+ * and the subscription getters/updaters — and carry the security contract (config-not-configured
21
+ * errors, normalized status) for free, so reach for these instead of hand-rolling raw
22
+ * `stripe` calls. In a Next.js App Router route, read the RAW webhook body with
23
+ * `await req.text()` and the header with `req.headers.get('stripe-signature')`, then
24
+ * `verifyWebhookSignature(rawBody, signature)` (the `express.raw(...)` note below is the
25
+ * Express-host equivalent). Only `@molecule/api-middleware-billing-routes` (Express glue) and
26
+ * `@molecule/app-billing-react` (molecule UI) are framework-coupled — skip THOSE on such a
27
+ * host, but still use these bond functions underneath.
28
+ *
29
+ * Two things a weak Stripe integration gets wrong:
30
+ *
31
+ * - **The webhook body MUST be RAW for signature verification.** {@link verifyWebhookSignature}
32
+ * (Stripe's `constructEvent`) hashes the exact bytes, so a parsed-then-re-serialized body
33
+ * ALWAYS fails. In a molecule app you need NO special middleware: the always-included
34
+ * `@molecule/api-middleware-body-parser-express` already captures the unparsed body as
35
+ * `req.rawBody` (a string) on EVERY request — just pass `req.rawBody` + the `stripe-signature`
36
+ * header to {@link verifyWebhookSignature}. Do NOT add a route-specific `express.raw(...)` here —
37
+ * it is redundant and fights the global JSON parser (which has already consumed the stream and
38
+ * set `req.rawBody`). (ONLY on a NON-molecule Express host that lacks `req.rawBody` do you mount
39
+ * `express.raw({ type: 'application/json' })` before the JSON parser; on Next.js App Router read
40
+ * the raw body with `await req.text()`.) NEVER act on an unverified webhook body — it is
41
+ * attacker-controlled.
42
+ * - **Webhooks are redelivered — be idempotent.** Stripe retries until it gets a 2xx, so the
43
+ * same `event.id` can arrive twice. Dedupe on it (the payment record's
44
+ * `UNIQUE(platformKey, transactionId)` already blocks a double-grant), and return 2xx once
45
+ * handled so Stripe stops retrying.
46
+ *
47
+ * Create checkout with SERVER-configured price ids ({@link createCheckoutSession}) — never an
48
+ * amount sent by the client.
49
+ *
50
+ * **A missing `STRIPE_SECRET_KEY` is NOT the same as "no active subscription."**
51
+ * `getClient()` throws a tagged config-not-configured error; `verifySubscription`,
52
+ * `updateSubscription`, and `cancelSubscription` on {@link paymentProvider} detect
53
+ * that tag (`isConfigNotConfiguredError` from `@molecule/api-payments`) and
54
+ * RETHROW it instead of swallowing it into the same `null` / `{ updated: false }`
55
+ * / `false` a genuine verification/update failure returns — so a caller (or its
56
+ * own catch block) can tell "the operator forgot to set the secret" apart from
57
+ * "this subscription/card is invalid" and surface the actionable 503 instead of
58
+ * a generic 400/500.
59
+ *
60
+ * @module
61
+ */
62
+ export * from './bondAdapter.js';
63
+ export * from './browser-guard.js';
64
+ export * from './connect.js';
65
+ export * from './provider.js';
66
+ export * from './secrets.js';
67
+ export * from './types.js';
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}