@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.
- package/LICENSE +115 -0
- package/dist/bondAdapter.d.ts +18 -0
- package/dist/bondAdapter.d.ts.map +1 -0
- package/dist/bondAdapter.js +406 -0
- package/dist/bondAdapter.js.map +1 -0
- package/dist/browser-guard.d.ts +2 -0
- package/dist/browser-guard.d.ts.map +1 -0
- package/dist/browser-guard.js +19 -0
- package/dist/browser-guard.js.map +1 -0
- package/dist/connect.d.ts +252 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +229 -0
- package/dist/connect.js.map +1 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +204 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +413 -0
- package/dist/provider.js.map +1 -0
- package/dist/secrets.d.ts +16 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +32 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work.
|
|
38
|
+
|
|
39
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
40
|
+
form, that is based on (or derived from) the Work and for which the
|
|
41
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
42
|
+
represent, as a whole, an original work of authorship.
|
|
43
|
+
|
|
44
|
+
"Contribution" shall mean any work of authorship, including the
|
|
45
|
+
original version of the Work and any modifications or additions
|
|
46
|
+
to that Work, that is intentionally submitted to the Licensor for
|
|
47
|
+
inclusion in the Work by the copyright owner or by an individual or
|
|
48
|
+
Legal Entity authorized to submit on behalf of the copyright owner.
|
|
49
|
+
|
|
50
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
51
|
+
on behalf of whom a Contribution has been received by the Licensor and
|
|
52
|
+
subsequently incorporated within the Work.
|
|
53
|
+
|
|
54
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
55
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
56
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
57
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
58
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
59
|
+
Work and such Derivative Works in Source or Object form.
|
|
60
|
+
|
|
61
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
62
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
63
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
64
|
+
patent license to make, have made, use, offer to sell, sell, import,
|
|
65
|
+
and otherwise transfer the Work.
|
|
66
|
+
|
|
67
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
68
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
69
|
+
modifications, and in Source or Object form, provided that You
|
|
70
|
+
meet the following conditions:
|
|
71
|
+
|
|
72
|
+
(a) You must give any other recipients of the Work or
|
|
73
|
+
Derivative Works a copy of this License; and
|
|
74
|
+
|
|
75
|
+
(b) You must cause any modified files to carry prominent notices
|
|
76
|
+
stating that You changed the files; and
|
|
77
|
+
|
|
78
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
79
|
+
that You distribute, all copyright, patent, trademark, and
|
|
80
|
+
attribution notices from the Source form of the Work,
|
|
81
|
+
excluding those notices that do not pertain to any part of
|
|
82
|
+
the Derivative Works; and
|
|
83
|
+
|
|
84
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
85
|
+
distribution, then any Derivative Works that You distribute must
|
|
86
|
+
include a readable copy of the attribution notices contained
|
|
87
|
+
within such NOTICE file.
|
|
88
|
+
|
|
89
|
+
5. Submission of Contributions.
|
|
90
|
+
|
|
91
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
92
|
+
names, trademarks, service marks, or product names of the Licensor.
|
|
93
|
+
|
|
94
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
95
|
+
agreed to in writing, Licensor provides the Work on an "AS IS" BASIS,
|
|
96
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
|
|
97
|
+
|
|
98
|
+
8. Limitation of Liability. In no event and under no legal theory shall
|
|
99
|
+
any Contributor be liable to You for damages.
|
|
100
|
+
|
|
101
|
+
9. Accepting Warranty or Additional Liability.
|
|
102
|
+
|
|
103
|
+
Copyright 2026 Molecule Dev, Inc.
|
|
104
|
+
|
|
105
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
106
|
+
you may not use this file except in compliance with the License.
|
|
107
|
+
You may obtain a copy of the License at
|
|
108
|
+
|
|
109
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
110
|
+
|
|
111
|
+
Unless required by applicable law or agreed to in writing, software
|
|
112
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
113
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
114
|
+
See the License for the specific language governing permissions and
|
|
115
|
+
limitations under the License.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bond adapter for the Stripe payment provider.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the Stripe SDK functions into a PaymentProvider-compatible interface
|
|
5
|
+
* for use with the molecule.dev bond system.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { type PaymentProvider } from '@molecule/api-payments';
|
|
10
|
+
/**
|
|
11
|
+
* PaymentProvider-compatible adapter for Stripe.
|
|
12
|
+
*
|
|
13
|
+
* Wraps the Stripe SDK functions into a `PaymentProvider`-compatible object
|
|
14
|
+
* for use with the molecule bond system. Supports subscription verification,
|
|
15
|
+
* webhook handling, plan upgrades/downgrades, and cancellation.
|
|
16
|
+
*/
|
|
17
|
+
export declare const paymentProvider: PaymentProvider;
|
|
18
|
+
//# sourceMappingURL=bondAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bondAdapter.d.ts","sourceRoot":"","sources":["../src/bondAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,EAGL,KAAK,eAAe,EAOrB,MAAM,wBAAwB,CAAA;AA6C/B;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,eA8Z7B,CAAA"}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bond adapter for the Stripe payment provider.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the Stripe SDK functions into a PaymentProvider-compatible interface
|
|
5
|
+
* for use with the molecule.dev bond system.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import crypto from 'crypto';
|
|
10
|
+
import { getLogger } from '@molecule/api-bond';
|
|
11
|
+
const logger = getLogger();
|
|
12
|
+
import { get } from '@molecule/api-bond';
|
|
13
|
+
import { isConfigNotConfiguredError, } from '@molecule/api-payments';
|
|
14
|
+
import { createCheckoutSession, createPortalSession as stripeCreatePortalSession, createSetupIntent as stripeCreateSetupIntent, detachPaymentMethod as stripeDetachPaymentMethod, getCheckoutSession, getSubscription, normalizeSubscription, normalizeSubscriptionStatus, retrievePaymentMethod as stripeRetrievePaymentMethod, updateSubscription as stripeUpdateSubscription, verifyWebhookSignature, } from './provider.js';
|
|
15
|
+
/**
|
|
16
|
+
* Maps Stripe webhook event types (e.g. `customer.subscription.created`) to
|
|
17
|
+
* simplified event type strings (e.g. `created`, `renewed`, `canceled`).
|
|
18
|
+
* @param eventType - The raw Stripe webhook event type string.
|
|
19
|
+
* @returns A simplified event type like `'created'`, `'renewed'`, `'canceled'`, `'paused'`, `'expired'`, or `'trial_ending'`. Falls through to the raw type for unrecognized events.
|
|
20
|
+
*/
|
|
21
|
+
const mapStripeEventType = (eventType) => {
|
|
22
|
+
switch (eventType) {
|
|
23
|
+
case 'customer.subscription.created':
|
|
24
|
+
return 'created';
|
|
25
|
+
case 'customer.subscription.updated':
|
|
26
|
+
return 'renewed';
|
|
27
|
+
case 'customer.subscription.deleted':
|
|
28
|
+
return 'canceled';
|
|
29
|
+
case 'customer.subscription.paused':
|
|
30
|
+
return 'paused';
|
|
31
|
+
case 'customer.subscription.resumed':
|
|
32
|
+
return 'renewed';
|
|
33
|
+
case 'customer.subscription.pending_update_applied':
|
|
34
|
+
return 'renewed';
|
|
35
|
+
case 'customer.subscription.pending_update_expired':
|
|
36
|
+
return 'expired';
|
|
37
|
+
case 'customer.subscription.trial_will_end':
|
|
38
|
+
return 'trial_ending';
|
|
39
|
+
default:
|
|
40
|
+
return eventType;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* PaymentProvider-compatible adapter for Stripe.
|
|
45
|
+
*
|
|
46
|
+
* Wraps the Stripe SDK functions into a `PaymentProvider`-compatible object
|
|
47
|
+
* for use with the molecule bond system. Supports subscription verification,
|
|
48
|
+
* webhook handling, plan upgrades/downgrades, and cancellation.
|
|
49
|
+
*/
|
|
50
|
+
export const paymentProvider = {
|
|
51
|
+
providerName: 'stripe',
|
|
52
|
+
verifyFlow: 'subscription',
|
|
53
|
+
notificationFlow: 'webhook',
|
|
54
|
+
/**
|
|
55
|
+
* Verifies a Stripe subscription by ID and returns a VerifiedSubscription.
|
|
56
|
+
*
|
|
57
|
+
* Accepts either a Subscription ID (`sub_xxx`) or a Checkout Session ID (`cs_xxx`).
|
|
58
|
+
* If a Checkout Session ID is provided, it retrieves the session first to extract
|
|
59
|
+
* the associated subscription ID.
|
|
60
|
+
* @param subscriptionId - The Stripe subscription ID (`sub_xxx`) or checkout session ID (`cs_xxx`).
|
|
61
|
+
* @returns The verified subscription with product, expiry, and renewal info, or `null` on failure.
|
|
62
|
+
*/
|
|
63
|
+
async verifySubscription(subscriptionId) {
|
|
64
|
+
try {
|
|
65
|
+
// A Checkout Session ID (`cs_...`) only arrives via the per-user
|
|
66
|
+
// post-checkout redirect (success_url), which is unguessable and bound to
|
|
67
|
+
// the user who started checkout. A bare `sub_...` id, by contrast, can be
|
|
68
|
+
// a value the caller merely learned, so the handler requires an ownership
|
|
69
|
+
// match for it. Surface which path this was so `verifyPayment` can enforce
|
|
70
|
+
// that distinction.
|
|
71
|
+
const viaCheckoutSession = subscriptionId.startsWith('cs_');
|
|
72
|
+
// If this is a Checkout Session ID (from post-checkout redirect), resolve to subscription
|
|
73
|
+
let resolvedSubscriptionId = subscriptionId;
|
|
74
|
+
if (viaCheckoutSession) {
|
|
75
|
+
const session = await getCheckoutSession(subscriptionId);
|
|
76
|
+
if (!session.subscription) {
|
|
77
|
+
logger.error('Stripe checkout session has no subscription', { sessionId: subscriptionId });
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
resolvedSubscriptionId = session.subscription;
|
|
81
|
+
}
|
|
82
|
+
const subscription = await getSubscription(resolvedSubscriptionId);
|
|
83
|
+
if (!subscription) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const normalized = normalizeSubscription(subscription);
|
|
87
|
+
// Gate the grant on subscription status: only an active/trialing
|
|
88
|
+
// subscription confers entitlement. A past_due/unpaid/canceled/incomplete
|
|
89
|
+
// subscription must NOT re-grant the plan on demand.
|
|
90
|
+
if (!normalized.isActive) {
|
|
91
|
+
logger.info(`Stripe verifySubscription: subscription ${normalized.subscriptionId} is not active (status=${normalized.status}) — rejecting`);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
// Defense in depth: also reject if the current period has already elapsed
|
|
95
|
+
// (when known). `isActive` already covers Stripe's authoritative status,
|
|
96
|
+
// but an out-of-date period end is never a valid grant.
|
|
97
|
+
if (normalized.currentPeriodEnd && normalized.currentPeriodEnd <= Date.now()) {
|
|
98
|
+
logger.info(`Stripe verifySubscription: subscription ${normalized.subscriptionId} period has ended — rejecting`);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// Apps register their plan catalogue with PRICE ids (the env-configured
|
|
102
|
+
// `STRIPE_<APP>_…` values checkout is started with), while the
|
|
103
|
+
// subscription reports the parent PRODUCT id — surface both so plan
|
|
104
|
+
// resolution can match either.
|
|
105
|
+
const rawItems = normalized.rawData?.items;
|
|
106
|
+
const verifiedPriceId = rawItems?.data?.[0]?.price?.id;
|
|
107
|
+
return {
|
|
108
|
+
productId: normalized.productId,
|
|
109
|
+
priceId: verifiedPriceId ? String(verifiedPriceId) : undefined,
|
|
110
|
+
transactionId: normalized.subscriptionId,
|
|
111
|
+
expiresAt: normalized.currentPeriodEnd
|
|
112
|
+
? new Date(normalized.currentPeriodEnd).toISOString()
|
|
113
|
+
: undefined,
|
|
114
|
+
autoRenews: normalized.willRenew,
|
|
115
|
+
data: {
|
|
116
|
+
customerId: typeof normalized.rawData?.customer === 'string'
|
|
117
|
+
? normalized.rawData.customer
|
|
118
|
+
: undefined,
|
|
119
|
+
viaCheckoutSession,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
// A missing STRIPE_SECRET_KEY (getClient()) is a DIFFERENT failure than
|
|
125
|
+
// "no active subscription" — rethrow so the resource handler's catch can
|
|
126
|
+
// surface the actionable 503 instead of a generic 400. Swallowing it into
|
|
127
|
+
// the same `null` a genuine verification failure returns makes an
|
|
128
|
+
// operator's forgotten env var indistinguishable from "this subscription
|
|
129
|
+
// isn't valid."
|
|
130
|
+
if (isConfigNotConfiguredError(error)) {
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
logger.error('Stripe bondAdapter verifySubscription error:', error);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
/**
|
|
138
|
+
* Handles a Stripe webhook event by verifying the signature and parsing the payload.
|
|
139
|
+
*
|
|
140
|
+
* Expects an express-like request object with `body` (raw body as Buffer or string)
|
|
141
|
+
* and `headers` containing `stripe-signature`.
|
|
142
|
+
* @param req - An Express-like request object with `body` (or `rawBody`) and `headers['stripe-signature']`.
|
|
143
|
+
* @returns A parsed webhook event with simplified type and subscription details, or `null` on failure.
|
|
144
|
+
*/
|
|
145
|
+
async handleWebhookEvent(req) {
|
|
146
|
+
try {
|
|
147
|
+
const request = req;
|
|
148
|
+
const rawBody = request.rawBody || request.body;
|
|
149
|
+
const signature = request.headers['stripe-signature'];
|
|
150
|
+
if (!rawBody || !signature) {
|
|
151
|
+
logger.error('Stripe bondAdapter handleWebhookEvent: missing body or stripe-signature.');
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const event = verifyWebhookSignature(rawBody, Array.isArray(signature) ? signature[0] : signature);
|
|
155
|
+
// Only process subscription-related events.
|
|
156
|
+
if (!event.type.startsWith('customer.subscription.')) {
|
|
157
|
+
return {
|
|
158
|
+
type: event.type,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const subscriptionData = event.data.object;
|
|
162
|
+
// Extract the product AND price IDs from the subscription items — apps
|
|
163
|
+
// register plans by PRICE id (env-configured), so plan resolution needs
|
|
164
|
+
// both candidates.
|
|
165
|
+
const items = subscriptionData.items;
|
|
166
|
+
const productId = items?.data?.[0]?.price?.product
|
|
167
|
+
? String(items.data[0].price.product)
|
|
168
|
+
: undefined;
|
|
169
|
+
const priceId = items?.data?.[0]?.price?.id ? String(items.data[0].price.id) : undefined;
|
|
170
|
+
// Get period end from the subscription item (Stripe API 2025+ moved it
|
|
171
|
+
// there). Webhook payload shape follows the API version PINNED ON THE
|
|
172
|
+
// WEBHOOK ENDPOINT — older endpoints still send it top-level on the
|
|
173
|
+
// subscription, so fall back to that rather than silently dropping the
|
|
174
|
+
// expiry (a missing expiresAt reads downstream as "never expires").
|
|
175
|
+
const subscriptionItem = (items?.data?.[0] ?? {});
|
|
176
|
+
const periodEnd = (subscriptionItem.current_period_end ??
|
|
177
|
+
subscriptionData.current_period_end);
|
|
178
|
+
const expiresAt = periodEnd ? new Date(periodEnd * 1000).toISOString() : undefined;
|
|
179
|
+
const autoRenews = event.type !== 'customer.subscription.deleted' &&
|
|
180
|
+
!subscriptionData.canceled_at &&
|
|
181
|
+
!subscriptionData.cancel_at_period_end;
|
|
182
|
+
// Surface the subscription status so the notification handler can enforce
|
|
183
|
+
// the SAME entitlement gate as `verifySubscription`. `isActive` is derived
|
|
184
|
+
// with the identical rule used by `normalizeSubscription` (active/trialing
|
|
185
|
+
// only). Without this, a renewal-payment failure (Stripe fires a signed
|
|
186
|
+
// `customer.subscription.updated` with status=past_due and an advanced
|
|
187
|
+
// current_period_end) would extend premium for an unpaid cycle.
|
|
188
|
+
const rawStatus = typeof subscriptionData.status === 'string' ? subscriptionData.status : undefined;
|
|
189
|
+
const status = normalizeSubscriptionStatus(rawStatus);
|
|
190
|
+
const isActive = rawStatus === 'active' || rawStatus === 'trialing';
|
|
191
|
+
return {
|
|
192
|
+
type: mapStripeEventType(event.type),
|
|
193
|
+
subscription: {
|
|
194
|
+
customerId: subscriptionData.customer ? String(subscriptionData.customer) : undefined,
|
|
195
|
+
productId,
|
|
196
|
+
priceId,
|
|
197
|
+
expiresAt,
|
|
198
|
+
autoRenews,
|
|
199
|
+
status,
|
|
200
|
+
isActive,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
logger.error('Stripe bondAdapter handleWebhookEvent error:', error);
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
/**
|
|
210
|
+
* Updates or creates a Stripe subscription for a user.
|
|
211
|
+
*
|
|
212
|
+
* 1. Looks up the user's existing Stripe subscription via PaymentRecordService.
|
|
213
|
+
* 2. If found, updates the subscription to the new product/price.
|
|
214
|
+
* 3. If not found, creates a Stripe Checkout session and returns the checkout URL.
|
|
215
|
+
*
|
|
216
|
+
* Handles subscription updates and new checkout session creation.
|
|
217
|
+
* @param params - The update parameters.
|
|
218
|
+
* @param params.userId - The molecule user ID to update the subscription for.
|
|
219
|
+
* @param params.newProductId - The Stripe Price ID of the new plan to switch to.
|
|
220
|
+
* @param params.previousProductId - The Stripe Price ID of the current plan (unused, for logging context).
|
|
221
|
+
* @returns An object with `updated: true` if the existing subscription was changed, or `checkoutUrl` if a new Checkout session was created.
|
|
222
|
+
*/
|
|
223
|
+
async updateSubscription(params) {
|
|
224
|
+
try {
|
|
225
|
+
const records = get('paymentRecords');
|
|
226
|
+
// Try to find an existing Stripe subscription for this user.
|
|
227
|
+
if (records) {
|
|
228
|
+
const payment = await records.findByUserId(params.userId, 'stripe');
|
|
229
|
+
const subscriptionId = payment?.data?.subscriptionId;
|
|
230
|
+
if (subscriptionId) {
|
|
231
|
+
// Existing subscription found — update it with the new price.
|
|
232
|
+
const subscription = await getSubscription(subscriptionId);
|
|
233
|
+
if (subscription?.items?.data?.[0]?.id) {
|
|
234
|
+
const updatedSubscription = await stripeUpdateSubscription(subscriptionId, {
|
|
235
|
+
items: [
|
|
236
|
+
{
|
|
237
|
+
id: subscription.items.data[0].id,
|
|
238
|
+
price: params.newProductId,
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
});
|
|
242
|
+
if (updatedSubscription) {
|
|
243
|
+
const subscriptionItem = updatedSubscription.items?.data?.[0];
|
|
244
|
+
const periodEnd = subscriptionItem
|
|
245
|
+
?.current_period_end;
|
|
246
|
+
const expiresAt = periodEnd ? new Date(periodEnd * 1000).toISOString() : undefined;
|
|
247
|
+
return {
|
|
248
|
+
updated: true,
|
|
249
|
+
subscription: {
|
|
250
|
+
expiresAt,
|
|
251
|
+
autoRenews: !updatedSubscription.cancel_at_period_end,
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// No existing subscription — create a Stripe Checkout session.
|
|
259
|
+
// Stripe rejects empty/relative success_url and cancel_url with
|
|
260
|
+
// `code: 'url_invalid'`. When neither API_ORIGIN/APP_ORIGIN nor
|
|
261
|
+
// ORIGIN is set (typical in dev / smoke-app spin-ups), fall back to
|
|
262
|
+
// `http://localhost:${PORT}` so checkout still works locally.
|
|
263
|
+
const apiPort = Number(process.env.PORT) || 4000;
|
|
264
|
+
const fallbackApiOrigin = `http://localhost:${apiPort}`;
|
|
265
|
+
// Conventional dev frontend lives on apiPort - 1000 (e.g. 4030 → 3030).
|
|
266
|
+
// Mirrors the polish-v2 dispatcher's port pairing.
|
|
267
|
+
const fallbackAppOrigin = `http://localhost:${apiPort - 1000}`;
|
|
268
|
+
const apiOrigin = process.env.API_ORIGIN || process.env.ORIGIN || fallbackApiOrigin;
|
|
269
|
+
const appOrigin = process.env.APP_ORIGIN || process.env.ORIGIN || fallbackAppOrigin;
|
|
270
|
+
if (apiOrigin === fallbackApiOrigin || appOrigin === fallbackAppOrigin) {
|
|
271
|
+
// Silent localhost redirect URLs in a deployed app strand the user on
|
|
272
|
+
// localhost after paying — make the fallback visible.
|
|
273
|
+
logger.warn(`Stripe checkout: API_ORIGIN/APP_ORIGIN/ORIGIN not set — falling back to ${fallbackApiOrigin} / ${fallbackAppOrigin} for redirect URLs (fine in dev, wrong in production).`);
|
|
274
|
+
}
|
|
275
|
+
// Generate idempotency key from userId + priceId + timestamp window (5-min buckets)
|
|
276
|
+
// This prevents duplicate checkout sessions if the user double-clicks or retries
|
|
277
|
+
const timeWindow = Math.floor(Date.now() / (5 * 60 * 1000));
|
|
278
|
+
const idempotencyKey = crypto
|
|
279
|
+
.createHash('sha256')
|
|
280
|
+
.update(`checkout:${params.userId}:${params.newProductId}:${timeWindow}`)
|
|
281
|
+
.digest('hex');
|
|
282
|
+
const session = await createCheckoutSession({
|
|
283
|
+
priceId: params.newProductId,
|
|
284
|
+
successUrl: `${apiOrigin}/api/users/${params.userId}/verify-payment/stripe?subscriptionId={CHECKOUT_SESSION_ID}`,
|
|
285
|
+
cancelUrl: appOrigin,
|
|
286
|
+
idempotencyKey,
|
|
287
|
+
});
|
|
288
|
+
if (session?.id && session?.url) {
|
|
289
|
+
return { updated: false, checkoutUrl: session.url };
|
|
290
|
+
}
|
|
291
|
+
return { updated: false };
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
// See the matching comment in verifySubscription: a missing secret must
|
|
295
|
+
// reach the caller as its real 503, not be flattened into the same
|
|
296
|
+
// `{ updated: false }` a genuine update failure (e.g. Stripe declining
|
|
297
|
+
// the card) returns.
|
|
298
|
+
if (isConfigNotConfiguredError(error)) {
|
|
299
|
+
throw error;
|
|
300
|
+
}
|
|
301
|
+
logger.error('Stripe bondAdapter updateSubscription error:', error);
|
|
302
|
+
return { updated: false };
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
/**
|
|
306
|
+
* Creates a Stripe SetupIntent so the frontend can confirm a card off-session.
|
|
307
|
+
*
|
|
308
|
+
* @param params - SetupIntent parameters (optional Stripe customer ID, metadata, idempotency key).
|
|
309
|
+
* @returns The SetupIntent ID, client secret, and customer ID.
|
|
310
|
+
*/
|
|
311
|
+
async createSetupIntent(params) {
|
|
312
|
+
return stripeCreateSetupIntent({
|
|
313
|
+
customerId: params.customerId,
|
|
314
|
+
metadata: params.metadata,
|
|
315
|
+
idempotencyKey: params.idempotencyKey,
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
/**
|
|
319
|
+
* Retrieves a saved Stripe payment method and returns normalized card metadata.
|
|
320
|
+
*
|
|
321
|
+
* @param providerPaymentMethodId - The Stripe payment method ID (`pm_...`).
|
|
322
|
+
* @returns Card brand/last4/exp, or `null` on failure.
|
|
323
|
+
*/
|
|
324
|
+
async getPaymentMethod(providerPaymentMethodId) {
|
|
325
|
+
return stripeRetrievePaymentMethod(providerPaymentMethodId);
|
|
326
|
+
},
|
|
327
|
+
/**
|
|
328
|
+
* Detaches a Stripe saved payment method from its customer.
|
|
329
|
+
*
|
|
330
|
+
* @param providerPaymentMethodId - The Stripe payment method ID (`pm_...`).
|
|
331
|
+
* @returns `true` on success, `false` on provider error.
|
|
332
|
+
*/
|
|
333
|
+
async detachPaymentMethod(providerPaymentMethodId) {
|
|
334
|
+
return stripeDetachPaymentMethod(providerPaymentMethodId);
|
|
335
|
+
},
|
|
336
|
+
/**
|
|
337
|
+
* Cancels an existing Stripe subscription for a user.
|
|
338
|
+
*
|
|
339
|
+
* Looks up the subscription via PaymentRecordService, then sets
|
|
340
|
+
* `cancel_at_period_end` to `true` so it expires at the end of the current billing cycle.
|
|
341
|
+
* @param params - The cancellation parameters.
|
|
342
|
+
* @param params.userId - The molecule user ID whose subscription should be canceled.
|
|
343
|
+
* @returns `true` if the subscription was successfully set to cancel at period end, `false` otherwise.
|
|
344
|
+
*/
|
|
345
|
+
async cancelSubscription(params) {
|
|
346
|
+
try {
|
|
347
|
+
const records = get('paymentRecords');
|
|
348
|
+
if (!records)
|
|
349
|
+
return false;
|
|
350
|
+
const payment = await records.findByUserId(params.userId, 'stripe');
|
|
351
|
+
const subscriptionId = payment?.data?.subscriptionId;
|
|
352
|
+
if (!subscriptionId)
|
|
353
|
+
return false;
|
|
354
|
+
const subscription = await getSubscription(subscriptionId);
|
|
355
|
+
if (!subscription)
|
|
356
|
+
return false;
|
|
357
|
+
const updated = await stripeUpdateSubscription(subscriptionId, {
|
|
358
|
+
cancel_at_period_end: true,
|
|
359
|
+
});
|
|
360
|
+
return !!updated;
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
// See the matching comment in verifySubscription.
|
|
364
|
+
if (isConfigNotConfiguredError(error)) {
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
logger.error('Stripe bondAdapter cancelSubscription error:', error);
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
/**
|
|
372
|
+
* Creates a Stripe Billing Portal session for the user so they can manage
|
|
373
|
+
* payment methods, cancel, and view invoices in Stripe's hosted portal.
|
|
374
|
+
* Resolves the user's Stripe customer ID from their payment record (stored
|
|
375
|
+
* at checkout/verify time).
|
|
376
|
+
*
|
|
377
|
+
* @param params - The portal parameters.
|
|
378
|
+
* @param params.userId - The molecule user ID to open the portal for.
|
|
379
|
+
* @param params.returnUrl - URL Stripe returns the user to on exit.
|
|
380
|
+
* @returns The portal session `{ id, url }`, or `null` when the user has no
|
|
381
|
+
* Stripe customer record or Stripe rejects the request.
|
|
382
|
+
*/
|
|
383
|
+
async createPortalSession(params) {
|
|
384
|
+
try {
|
|
385
|
+
const records = get('paymentRecords');
|
|
386
|
+
if (!records)
|
|
387
|
+
return null;
|
|
388
|
+
const payment = await records.findByUserId(params.userId, 'stripe');
|
|
389
|
+
const customerId = payment?.data?.customerId;
|
|
390
|
+
if (!customerId)
|
|
391
|
+
return null;
|
|
392
|
+
return await stripeCreatePortalSession({
|
|
393
|
+
customerId,
|
|
394
|
+
returnUrl: params.returnUrl,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
if (isConfigNotConfiguredError(error)) {
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
401
|
+
logger.error('Stripe bondAdapter createPortalSession error:', error);
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
//# sourceMappingURL=bondAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bondAdapter.js","sourceRoot":"","sources":["../src/bondAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-guard.d.ts","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser guard — `@molecule/api-payments-stripe` is SERVER-ONLY.
|
|
3
|
+
*
|
|
4
|
+
* Generated by scripts/gen-browser-guards.mjs (workspace root) — edit THAT, not this.
|
|
5
|
+
* Evaluating a server package in a browser bundle is always an import-graph mistake
|
|
6
|
+
* (node APIs, secrets); without this guard it surfaces as a cryptic downstream crash
|
|
7
|
+
* ("Buffer is not defined") far from the culprit. Throwing here names the package and
|
|
8
|
+
* the fix at the exact moment the client bundle evaluates it. jsdom tests and SSR are
|
|
9
|
+
* unaffected: the throw requires browser globals AND the absence of a node runtime.
|
|
10
|
+
*/
|
|
11
|
+
const g = globalThis;
|
|
12
|
+
if (g.window !== undefined && g.document !== undefined && !g.process?.versions?.node) {
|
|
13
|
+
throw new Error('@molecule/api-payments-stripe is SERVER-ONLY: it was bundled into browser/client code. Import it only ' +
|
|
14
|
+
'from server code (a server route/function or your API), or dynamic-import it inside ' +
|
|
15
|
+
'the server handler — never from components or shared client modules, and never ' +
|
|
16
|
+
'polyfill Buffer/process to silence this.');
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=browser-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-guard.js","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,GAAG,UAIT,CAAA;AACD,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrF,MAAM,IAAI,KAAK,CACb,wGAAwG;QACtG,sFAAsF;QACtF,iFAAiF;QACjF,0CAA0C,CAC7C,CAAA;AACH,CAAC"}
|