@verevoir/stripe 0.1.0-alpha.1
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 +21 -0
- package/README.md +86 -0
- package/dist/index.cjs +123 -0
- package/dist/index.d.cts +91 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +96 -0
- package/llms.txt +89 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 verevoir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @verevoir/stripe
|
|
2
|
+
|
|
3
|
+
Stripe payment adapter — checkout sessions, subscription management, webhook handling, and billing portal.
|
|
4
|
+
|
|
5
|
+
Part of [Verevoir](https://verevoir.io) — a database-agnostic application platform.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @verevoir/stripe stripe
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires `stripe` as a peer dependency (^17.0.0 || ^18.0.0).
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import Stripe from 'stripe';
|
|
19
|
+
import { createStripeAdapter } from '@verevoir/stripe';
|
|
20
|
+
|
|
21
|
+
const stripe = new Stripe('sk_test_...');
|
|
22
|
+
const adapter = createStripeAdapter({
|
|
23
|
+
client: stripe,
|
|
24
|
+
webhookSecret: 'whsec_...',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Create a checkout session
|
|
28
|
+
const session = await adapter.createCheckoutSession({
|
|
29
|
+
priceId: 'price_pro_monthly',
|
|
30
|
+
customerEmail: 'alice@example.com',
|
|
31
|
+
successUrl: 'https://app.com/success',
|
|
32
|
+
cancelUrl: 'https://app.com/cancel',
|
|
33
|
+
});
|
|
34
|
+
// Redirect to session.url
|
|
35
|
+
|
|
36
|
+
// Handle webhooks
|
|
37
|
+
const event = adapter.parseWebhookEvent(body, signature);
|
|
38
|
+
if (event.type === 'customer.subscription.updated') {
|
|
39
|
+
// Sync subscription state
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
|
|
45
|
+
### Adapter
|
|
46
|
+
|
|
47
|
+
| Method | Description |
|
|
48
|
+
| --------------------------------------- | ------------------------------------------------- |
|
|
49
|
+
| `createCheckoutSession(options)` | Create a Stripe Checkout session for subscription |
|
|
50
|
+
| `createPortalSession(options)` | Create a Stripe Billing Portal session |
|
|
51
|
+
| `createCustomer(email, metadata?)` | Create a Stripe customer |
|
|
52
|
+
| `cancelSubscription(subscriptionId)` | Cancel a subscription |
|
|
53
|
+
| `parseWebhookEvent(payload, signature)` | Parse and verify a webhook event |
|
|
54
|
+
|
|
55
|
+
### Webhook Events
|
|
56
|
+
|
|
57
|
+
| Event Type | Fields |
|
|
58
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
59
|
+
| `checkout.session.completed` | `customerId`, `subscriptionId`, `metadata` |
|
|
60
|
+
| `customer.subscription.updated` | `subscriptionId`, `status`, `currentPeriodStart`, `currentPeriodEnd`, `priceId`, `cancelAtPeriodEnd` |
|
|
61
|
+
| `customer.subscription.deleted` | `subscriptionId` |
|
|
62
|
+
| `invoice.payment_failed` | `subscriptionId`, `customerId` |
|
|
63
|
+
| `unknown` | `rawType` |
|
|
64
|
+
|
|
65
|
+
## Design
|
|
66
|
+
|
|
67
|
+
- Consumer owns the Stripe client — adapter does not manage API keys
|
|
68
|
+
- Stripe is source of truth for subscription state
|
|
69
|
+
- Focused on subscription billing (not one-off payments)
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
- [Verevoir Packages](https://verevoir.io/packages)
|
|
74
|
+
- [Commerce Guide](https://verevoir.io/docs/commerce)
|
|
75
|
+
|
|
76
|
+
## Development
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
make build # Compile TypeScript
|
|
80
|
+
make test # Run test suite
|
|
81
|
+
make lint # Lint and check formatting
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createStripeAdapter: () => createStripeAdapter
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/adapter.ts
|
|
28
|
+
function createStripeAdapter(options) {
|
|
29
|
+
const { client, webhookSecret } = options;
|
|
30
|
+
return {
|
|
31
|
+
async createCheckoutSession(opts) {
|
|
32
|
+
const params = {
|
|
33
|
+
mode: "subscription",
|
|
34
|
+
line_items: [{ price: opts.priceId, quantity: 1 }],
|
|
35
|
+
success_url: opts.successUrl,
|
|
36
|
+
cancel_url: opts.cancelUrl
|
|
37
|
+
};
|
|
38
|
+
if (opts.customerId) {
|
|
39
|
+
params.customer = opts.customerId;
|
|
40
|
+
} else if (opts.customerEmail) {
|
|
41
|
+
params.customer_email = opts.customerEmail;
|
|
42
|
+
}
|
|
43
|
+
if (opts.metadata) {
|
|
44
|
+
params.metadata = opts.metadata;
|
|
45
|
+
}
|
|
46
|
+
const session = await client.checkout.sessions.create(params);
|
|
47
|
+
return {
|
|
48
|
+
sessionId: session.id,
|
|
49
|
+
url: session.url
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
async createPortalSession(opts) {
|
|
53
|
+
const session = await client.billingPortal.sessions.create({
|
|
54
|
+
customer: opts.customerId,
|
|
55
|
+
return_url: opts.returnUrl
|
|
56
|
+
});
|
|
57
|
+
return { url: session.url };
|
|
58
|
+
},
|
|
59
|
+
async createCustomer(email, metadata) {
|
|
60
|
+
const customer = await client.customers.create({
|
|
61
|
+
email,
|
|
62
|
+
...metadata && { metadata }
|
|
63
|
+
});
|
|
64
|
+
return customer.id;
|
|
65
|
+
},
|
|
66
|
+
async cancelSubscription(subscriptionId) {
|
|
67
|
+
await client.subscriptions.cancel(subscriptionId);
|
|
68
|
+
},
|
|
69
|
+
parseWebhookEvent(payload, signature) {
|
|
70
|
+
const event = client.webhooks.constructEvent(
|
|
71
|
+
payload,
|
|
72
|
+
signature,
|
|
73
|
+
webhookSecret
|
|
74
|
+
);
|
|
75
|
+
switch (event.type) {
|
|
76
|
+
case "checkout.session.completed": {
|
|
77
|
+
const session = event.data.object;
|
|
78
|
+
return {
|
|
79
|
+
type: "checkout.session.completed",
|
|
80
|
+
customerId: session.customer,
|
|
81
|
+
subscriptionId: session.subscription,
|
|
82
|
+
metadata: session.metadata ?? {}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
case "customer.subscription.updated": {
|
|
86
|
+
const sub = event.data.object;
|
|
87
|
+
const item = sub.items.data[0];
|
|
88
|
+
return {
|
|
89
|
+
type: "customer.subscription.updated",
|
|
90
|
+
subscriptionId: sub.id,
|
|
91
|
+
status: sub.status,
|
|
92
|
+
currentPeriodStart: item?.current_period_start ?? 0,
|
|
93
|
+
currentPeriodEnd: item?.current_period_end ?? 0,
|
|
94
|
+
priceId: item?.price?.id ?? "",
|
|
95
|
+
cancelAtPeriodEnd: sub.cancel_at_period_end
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
case "customer.subscription.deleted": {
|
|
99
|
+
const sub = event.data.object;
|
|
100
|
+
return {
|
|
101
|
+
type: "customer.subscription.deleted",
|
|
102
|
+
subscriptionId: sub.id
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
case "invoice.payment_failed": {
|
|
106
|
+
const invoice = event.data.object;
|
|
107
|
+
const subDetails = invoice.parent?.subscription_details;
|
|
108
|
+
return {
|
|
109
|
+
type: "invoice.payment_failed",
|
|
110
|
+
subscriptionId: subDetails?.subscription ? typeof subDetails.subscription === "string" ? subDetails.subscription : subDetails.subscription.id : "",
|
|
111
|
+
customerId: typeof invoice.customer === "string" ? invoice.customer : invoice.customer?.id ?? ""
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
return { type: "unknown", rawType: event.type };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
121
|
+
0 && (module.exports = {
|
|
122
|
+
createStripeAdapter
|
|
123
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import Stripe from 'stripe';
|
|
2
|
+
|
|
3
|
+
/** Structural Money type — compatible with @verevoir/commerce Money. */
|
|
4
|
+
interface Money {
|
|
5
|
+
readonly amount: number;
|
|
6
|
+
readonly currency: string;
|
|
7
|
+
}
|
|
8
|
+
/** A plan that maps to a Stripe Price. */
|
|
9
|
+
interface PlanMapping {
|
|
10
|
+
readonly planId: string;
|
|
11
|
+
readonly stripePriceId: string;
|
|
12
|
+
}
|
|
13
|
+
/** Checkout session configuration. */
|
|
14
|
+
interface CheckoutOptions {
|
|
15
|
+
/** Stripe customer ID. Created automatically if not provided. */
|
|
16
|
+
readonly customerId?: string;
|
|
17
|
+
/** Customer email — used if creating a new Stripe customer. */
|
|
18
|
+
readonly customerEmail?: string;
|
|
19
|
+
/** Stripe Price ID to subscribe to. */
|
|
20
|
+
readonly priceId: string;
|
|
21
|
+
/** URL to redirect on success. */
|
|
22
|
+
readonly successUrl: string;
|
|
23
|
+
/** URL to redirect on cancel. */
|
|
24
|
+
readonly cancelUrl: string;
|
|
25
|
+
/** Optional metadata to attach to the Stripe session. */
|
|
26
|
+
readonly metadata?: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/** Result of creating a checkout session. */
|
|
29
|
+
interface CheckoutSession {
|
|
30
|
+
readonly sessionId: string;
|
|
31
|
+
readonly url: string;
|
|
32
|
+
}
|
|
33
|
+
/** Billing portal configuration. */
|
|
34
|
+
interface PortalOptions {
|
|
35
|
+
readonly customerId: string;
|
|
36
|
+
readonly returnUrl: string;
|
|
37
|
+
}
|
|
38
|
+
/** Result of creating a billing portal session. */
|
|
39
|
+
interface PortalSession {
|
|
40
|
+
readonly url: string;
|
|
41
|
+
}
|
|
42
|
+
/** Parsed webhook event — the subset of Stripe events we handle. */
|
|
43
|
+
type WebhookEvent = {
|
|
44
|
+
type: 'checkout.session.completed';
|
|
45
|
+
customerId: string;
|
|
46
|
+
subscriptionId: string;
|
|
47
|
+
metadata: Record<string, string>;
|
|
48
|
+
} | {
|
|
49
|
+
type: 'customer.subscription.updated';
|
|
50
|
+
subscriptionId: string;
|
|
51
|
+
status: string;
|
|
52
|
+
currentPeriodStart: number;
|
|
53
|
+
currentPeriodEnd: number;
|
|
54
|
+
priceId: string;
|
|
55
|
+
cancelAtPeriodEnd: boolean;
|
|
56
|
+
} | {
|
|
57
|
+
type: 'customer.subscription.deleted';
|
|
58
|
+
subscriptionId: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'invoice.payment_failed';
|
|
61
|
+
subscriptionId: string;
|
|
62
|
+
customerId: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'unknown';
|
|
65
|
+
rawType: string;
|
|
66
|
+
};
|
|
67
|
+
/** The adapter interface — everything a consumer needs from Stripe. */
|
|
68
|
+
interface StripeAdapter {
|
|
69
|
+
createCheckoutSession(options: CheckoutOptions): Promise<CheckoutSession>;
|
|
70
|
+
createPortalSession(options: PortalOptions): Promise<PortalSession>;
|
|
71
|
+
createCustomer(email: string, metadata?: Record<string, string>): Promise<string>;
|
|
72
|
+
cancelSubscription(subscriptionId: string): Promise<void>;
|
|
73
|
+
parseWebhookEvent(payload: string | Buffer, signature: string): WebhookEvent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface CreateStripeAdapterOptions {
|
|
77
|
+
/** Configured Stripe SDK instance. Consumer creates this with their API key. */
|
|
78
|
+
client: Stripe;
|
|
79
|
+
/** Webhook endpoint secret for signature verification. */
|
|
80
|
+
webhookSecret: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a Stripe adapter wrapping the Stripe SDK.
|
|
84
|
+
*
|
|
85
|
+
* The consumer provides a configured Stripe client — this adapter does not
|
|
86
|
+
* manage API keys. It provides the operations needed for subscription billing:
|
|
87
|
+
* checkout, portal, customer management, and webhook parsing.
|
|
88
|
+
*/
|
|
89
|
+
declare function createStripeAdapter(options: CreateStripeAdapterOptions): StripeAdapter;
|
|
90
|
+
|
|
91
|
+
export { type CheckoutOptions, type CheckoutSession, type CreateStripeAdapterOptions, type Money, type PlanMapping, type PortalOptions, type PortalSession, type StripeAdapter, type WebhookEvent, createStripeAdapter };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import Stripe from 'stripe';
|
|
2
|
+
|
|
3
|
+
/** Structural Money type — compatible with @verevoir/commerce Money. */
|
|
4
|
+
interface Money {
|
|
5
|
+
readonly amount: number;
|
|
6
|
+
readonly currency: string;
|
|
7
|
+
}
|
|
8
|
+
/** A plan that maps to a Stripe Price. */
|
|
9
|
+
interface PlanMapping {
|
|
10
|
+
readonly planId: string;
|
|
11
|
+
readonly stripePriceId: string;
|
|
12
|
+
}
|
|
13
|
+
/** Checkout session configuration. */
|
|
14
|
+
interface CheckoutOptions {
|
|
15
|
+
/** Stripe customer ID. Created automatically if not provided. */
|
|
16
|
+
readonly customerId?: string;
|
|
17
|
+
/** Customer email — used if creating a new Stripe customer. */
|
|
18
|
+
readonly customerEmail?: string;
|
|
19
|
+
/** Stripe Price ID to subscribe to. */
|
|
20
|
+
readonly priceId: string;
|
|
21
|
+
/** URL to redirect on success. */
|
|
22
|
+
readonly successUrl: string;
|
|
23
|
+
/** URL to redirect on cancel. */
|
|
24
|
+
readonly cancelUrl: string;
|
|
25
|
+
/** Optional metadata to attach to the Stripe session. */
|
|
26
|
+
readonly metadata?: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/** Result of creating a checkout session. */
|
|
29
|
+
interface CheckoutSession {
|
|
30
|
+
readonly sessionId: string;
|
|
31
|
+
readonly url: string;
|
|
32
|
+
}
|
|
33
|
+
/** Billing portal configuration. */
|
|
34
|
+
interface PortalOptions {
|
|
35
|
+
readonly customerId: string;
|
|
36
|
+
readonly returnUrl: string;
|
|
37
|
+
}
|
|
38
|
+
/** Result of creating a billing portal session. */
|
|
39
|
+
interface PortalSession {
|
|
40
|
+
readonly url: string;
|
|
41
|
+
}
|
|
42
|
+
/** Parsed webhook event — the subset of Stripe events we handle. */
|
|
43
|
+
type WebhookEvent = {
|
|
44
|
+
type: 'checkout.session.completed';
|
|
45
|
+
customerId: string;
|
|
46
|
+
subscriptionId: string;
|
|
47
|
+
metadata: Record<string, string>;
|
|
48
|
+
} | {
|
|
49
|
+
type: 'customer.subscription.updated';
|
|
50
|
+
subscriptionId: string;
|
|
51
|
+
status: string;
|
|
52
|
+
currentPeriodStart: number;
|
|
53
|
+
currentPeriodEnd: number;
|
|
54
|
+
priceId: string;
|
|
55
|
+
cancelAtPeriodEnd: boolean;
|
|
56
|
+
} | {
|
|
57
|
+
type: 'customer.subscription.deleted';
|
|
58
|
+
subscriptionId: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'invoice.payment_failed';
|
|
61
|
+
subscriptionId: string;
|
|
62
|
+
customerId: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'unknown';
|
|
65
|
+
rawType: string;
|
|
66
|
+
};
|
|
67
|
+
/** The adapter interface — everything a consumer needs from Stripe. */
|
|
68
|
+
interface StripeAdapter {
|
|
69
|
+
createCheckoutSession(options: CheckoutOptions): Promise<CheckoutSession>;
|
|
70
|
+
createPortalSession(options: PortalOptions): Promise<PortalSession>;
|
|
71
|
+
createCustomer(email: string, metadata?: Record<string, string>): Promise<string>;
|
|
72
|
+
cancelSubscription(subscriptionId: string): Promise<void>;
|
|
73
|
+
parseWebhookEvent(payload: string | Buffer, signature: string): WebhookEvent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface CreateStripeAdapterOptions {
|
|
77
|
+
/** Configured Stripe SDK instance. Consumer creates this with their API key. */
|
|
78
|
+
client: Stripe;
|
|
79
|
+
/** Webhook endpoint secret for signature verification. */
|
|
80
|
+
webhookSecret: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a Stripe adapter wrapping the Stripe SDK.
|
|
84
|
+
*
|
|
85
|
+
* The consumer provides a configured Stripe client — this adapter does not
|
|
86
|
+
* manage API keys. It provides the operations needed for subscription billing:
|
|
87
|
+
* checkout, portal, customer management, and webhook parsing.
|
|
88
|
+
*/
|
|
89
|
+
declare function createStripeAdapter(options: CreateStripeAdapterOptions): StripeAdapter;
|
|
90
|
+
|
|
91
|
+
export { type CheckoutOptions, type CheckoutSession, type CreateStripeAdapterOptions, type Money, type PlanMapping, type PortalOptions, type PortalSession, type StripeAdapter, type WebhookEvent, createStripeAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// src/adapter.ts
|
|
2
|
+
function createStripeAdapter(options) {
|
|
3
|
+
const { client, webhookSecret } = options;
|
|
4
|
+
return {
|
|
5
|
+
async createCheckoutSession(opts) {
|
|
6
|
+
const params = {
|
|
7
|
+
mode: "subscription",
|
|
8
|
+
line_items: [{ price: opts.priceId, quantity: 1 }],
|
|
9
|
+
success_url: opts.successUrl,
|
|
10
|
+
cancel_url: opts.cancelUrl
|
|
11
|
+
};
|
|
12
|
+
if (opts.customerId) {
|
|
13
|
+
params.customer = opts.customerId;
|
|
14
|
+
} else if (opts.customerEmail) {
|
|
15
|
+
params.customer_email = opts.customerEmail;
|
|
16
|
+
}
|
|
17
|
+
if (opts.metadata) {
|
|
18
|
+
params.metadata = opts.metadata;
|
|
19
|
+
}
|
|
20
|
+
const session = await client.checkout.sessions.create(params);
|
|
21
|
+
return {
|
|
22
|
+
sessionId: session.id,
|
|
23
|
+
url: session.url
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
async createPortalSession(opts) {
|
|
27
|
+
const session = await client.billingPortal.sessions.create({
|
|
28
|
+
customer: opts.customerId,
|
|
29
|
+
return_url: opts.returnUrl
|
|
30
|
+
});
|
|
31
|
+
return { url: session.url };
|
|
32
|
+
},
|
|
33
|
+
async createCustomer(email, metadata) {
|
|
34
|
+
const customer = await client.customers.create({
|
|
35
|
+
email,
|
|
36
|
+
...metadata && { metadata }
|
|
37
|
+
});
|
|
38
|
+
return customer.id;
|
|
39
|
+
},
|
|
40
|
+
async cancelSubscription(subscriptionId) {
|
|
41
|
+
await client.subscriptions.cancel(subscriptionId);
|
|
42
|
+
},
|
|
43
|
+
parseWebhookEvent(payload, signature) {
|
|
44
|
+
const event = client.webhooks.constructEvent(
|
|
45
|
+
payload,
|
|
46
|
+
signature,
|
|
47
|
+
webhookSecret
|
|
48
|
+
);
|
|
49
|
+
switch (event.type) {
|
|
50
|
+
case "checkout.session.completed": {
|
|
51
|
+
const session = event.data.object;
|
|
52
|
+
return {
|
|
53
|
+
type: "checkout.session.completed",
|
|
54
|
+
customerId: session.customer,
|
|
55
|
+
subscriptionId: session.subscription,
|
|
56
|
+
metadata: session.metadata ?? {}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
case "customer.subscription.updated": {
|
|
60
|
+
const sub = event.data.object;
|
|
61
|
+
const item = sub.items.data[0];
|
|
62
|
+
return {
|
|
63
|
+
type: "customer.subscription.updated",
|
|
64
|
+
subscriptionId: sub.id,
|
|
65
|
+
status: sub.status,
|
|
66
|
+
currentPeriodStart: item?.current_period_start ?? 0,
|
|
67
|
+
currentPeriodEnd: item?.current_period_end ?? 0,
|
|
68
|
+
priceId: item?.price?.id ?? "",
|
|
69
|
+
cancelAtPeriodEnd: sub.cancel_at_period_end
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
case "customer.subscription.deleted": {
|
|
73
|
+
const sub = event.data.object;
|
|
74
|
+
return {
|
|
75
|
+
type: "customer.subscription.deleted",
|
|
76
|
+
subscriptionId: sub.id
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
case "invoice.payment_failed": {
|
|
80
|
+
const invoice = event.data.object;
|
|
81
|
+
const subDetails = invoice.parent?.subscription_details;
|
|
82
|
+
return {
|
|
83
|
+
type: "invoice.payment_failed",
|
|
84
|
+
subscriptionId: subDetails?.subscription ? typeof subDetails.subscription === "string" ? subDetails.subscription : subDetails.subscription.id : "",
|
|
85
|
+
customerId: typeof invoice.customer === "string" ? invoice.customer : invoice.customer?.id ?? ""
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
return { type: "unknown", rawType: event.type };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
createStripeAdapter
|
|
96
|
+
};
|
package/llms.txt
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# @verevoir/stripe
|
|
2
|
+
|
|
3
|
+
> Stripe payment adapter — checkout sessions, subscription management, webhook handling, and billing portal. Wraps the Stripe SDK with a focused interface for subscription billing.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @verevoir/stripe stripe
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Core Exports
|
|
12
|
+
|
|
13
|
+
### Stripe Adapter
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import Stripe from 'stripe';
|
|
17
|
+
import { createStripeAdapter } from '@verevoir/stripe';
|
|
18
|
+
|
|
19
|
+
const stripe = new Stripe('sk_test_...');
|
|
20
|
+
const adapter = createStripeAdapter({
|
|
21
|
+
client: stripe,
|
|
22
|
+
webhookSecret: 'whsec_...',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Create checkout session for subscription
|
|
26
|
+
const session = await adapter.createCheckoutSession({
|
|
27
|
+
priceId: 'price_pro_monthly',
|
|
28
|
+
customerEmail: 'alice@example.com',
|
|
29
|
+
successUrl: 'https://app.com/success',
|
|
30
|
+
cancelUrl: 'https://app.com/cancel',
|
|
31
|
+
metadata: { accountId: 'acc_123' },
|
|
32
|
+
});
|
|
33
|
+
// Redirect to session.url
|
|
34
|
+
|
|
35
|
+
// Create billing portal session
|
|
36
|
+
const portal = await adapter.createPortalSession({
|
|
37
|
+
customerId: 'cus_123',
|
|
38
|
+
returnUrl: 'https://app.com/settings',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Create customer
|
|
42
|
+
const customerId = await adapter.createCustomer('alice@example.com', { accountId: 'acc_1' });
|
|
43
|
+
|
|
44
|
+
// Cancel subscription
|
|
45
|
+
await adapter.cancelSubscription('sub_123');
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Webhook Handling
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// In your webhook endpoint
|
|
52
|
+
const event = adapter.parseWebhookEvent(requestBody, stripeSignature);
|
|
53
|
+
|
|
54
|
+
switch (event.type) {
|
|
55
|
+
case 'checkout.session.completed':
|
|
56
|
+
// event.customerId, event.subscriptionId, event.metadata
|
|
57
|
+
break;
|
|
58
|
+
case 'customer.subscription.updated':
|
|
59
|
+
// event.subscriptionId, event.status, event.currentPeriodStart, event.currentPeriodEnd, event.priceId
|
|
60
|
+
break;
|
|
61
|
+
case 'customer.subscription.deleted':
|
|
62
|
+
// event.subscriptionId
|
|
63
|
+
break;
|
|
64
|
+
case 'invoice.payment_failed':
|
|
65
|
+
// event.subscriptionId, event.customerId
|
|
66
|
+
break;
|
|
67
|
+
case 'unknown':
|
|
68
|
+
// event.rawType — unhandled Stripe event
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Types
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
interface StripeAdapter {
|
|
77
|
+
createCheckoutSession(options: CheckoutOptions): Promise<CheckoutSession>;
|
|
78
|
+
createPortalSession(options: PortalOptions): Promise<PortalSession>;
|
|
79
|
+
createCustomer(email: string, metadata?: Record<string, string>): Promise<string>;
|
|
80
|
+
cancelSubscription(subscriptionId: string): Promise<void>;
|
|
81
|
+
parseWebhookEvent(payload: string | Buffer, signature: string): WebhookEvent;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Constraints
|
|
86
|
+
|
|
87
|
+
- Peer dependency: `stripe` (^17.0.0 || ^18.0.0)
|
|
88
|
+
- Consumer provides configured Stripe SDK instance
|
|
89
|
+
- Stripe is source of truth for subscription state
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@verevoir/stripe",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "Stripe payment adapter — checkout, subscriptions, webhooks, and billing portal",
|
|
5
|
+
"homepage": "https://verevoir.io",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"llms.txt"
|
|
21
|
+
],
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+ssh://git@github.com:verevoir/stripe.git"
|
|
25
|
+
},
|
|
26
|
+
"funding": "https://buymeacoffee.com/verevoir",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"prepare": "npm run build",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"lint": "eslint . && prettier --check .",
|
|
32
|
+
"format": "prettier --write ."
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"stripe": "^17.0.0 || ^18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@eslint/js": "^9.39.4",
|
|
39
|
+
"eslint": "^9.39.4",
|
|
40
|
+
"eslint-config-prettier": "^10.1.8",
|
|
41
|
+
"prettier": "^3.8.1",
|
|
42
|
+
"@types/node": "^22.0.0",
|
|
43
|
+
"stripe": "^18.0.0",
|
|
44
|
+
"tsup": "^8.5.1",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"typescript-eslint": "^8.56.1",
|
|
47
|
+
"vitest": "^3.2.4"
|
|
48
|
+
}
|
|
49
|
+
}
|