@mathrunet/masamune_cloudflare_purchase_stripe 3.2.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/LICENSE +21 -0
  3. package/README.md +174 -0
  4. package/dist/functions.d.ts +44 -0
  5. package/dist/functions.js +47 -0
  6. package/dist/functions.js.map +1 -0
  7. package/dist/index.d.ts +25 -0
  8. package/dist/index.js +42 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/lib/meter/d1_usage_event_store.d.ts +128 -0
  11. package/dist/lib/meter/d1_usage_event_store.js +78 -0
  12. package/dist/lib/meter/d1_usage_event_store.js.map +1 -0
  13. package/dist/lib/meter/flush_meter.d.ts +19 -0
  14. package/dist/lib/meter/flush_meter.js +38 -0
  15. package/dist/lib/meter/flush_meter.js.map +1 -0
  16. package/dist/lib/meter/interface.d.ts +109 -0
  17. package/dist/lib/meter/interface.js +3 -0
  18. package/dist/lib/meter/interface.js.map +1 -0
  19. package/dist/lib/meter/kv_usage_buffer.d.ts +56 -0
  20. package/dist/lib/meter/kv_usage_buffer.js +50 -0
  21. package/dist/lib/meter/kv_usage_buffer.js.map +1 -0
  22. package/dist/lib/meter/record_usage.d.ts +29 -0
  23. package/dist/lib/meter/record_usage.js +72 -0
  24. package/dist/lib/meter/record_usage.js.map +1 -0
  25. package/dist/lib/meter/stripe_meter_client.d.ts +76 -0
  26. package/dist/lib/meter/stripe_meter_client.js +74 -0
  27. package/dist/lib/meter/stripe_meter_client.js.map +1 -0
  28. package/dist/lib/options.d.ts +179 -0
  29. package/dist/lib/options.js +132 -0
  30. package/dist/lib/options.js.map +1 -0
  31. package/dist/lib/purchase/d1_purchase_store.d.ts +78 -0
  32. package/dist/lib/purchase/d1_purchase_store.js +194 -0
  33. package/dist/lib/purchase/d1_purchase_store.js.map +1 -0
  34. package/dist/lib/purchase/helpers.d.ts +36 -0
  35. package/dist/lib/purchase/helpers.js +64 -0
  36. package/dist/lib/purchase/helpers.js.map +1 -0
  37. package/dist/lib/purchase/interface.d.ts +182 -0
  38. package/dist/lib/purchase/interface.js +27 -0
  39. package/dist/lib/purchase/interface.js.map +1 -0
  40. package/dist/lib/purchase/sync_payment.d.ts +15 -0
  41. package/dist/lib/purchase/sync_payment.js +79 -0
  42. package/dist/lib/purchase/sync_payment.js.map +1 -0
  43. package/dist/lib/stripe_client.d.ts +55 -0
  44. package/dist/lib/stripe_client.js +50 -0
  45. package/dist/lib/stripe_client.js.map +1 -0
  46. package/dist/meter.d.ts +21 -0
  47. package/dist/meter.js +38 -0
  48. package/dist/meter.js.map +1 -0
  49. package/dist/purchase.d.ts +58 -0
  50. package/dist/purchase.js +48 -0
  51. package/dist/purchase.js.map +1 -0
  52. package/dist/workers/stripe.d.ts +1 -0
  53. package/dist/workers/stripe.js +861 -0
  54. package/dist/workers/stripe.js.map +1 -0
  55. package/dist/workers/stripe_webhook.d.ts +1 -0
  56. package/dist/workers/stripe_webhook.js +384 -0
  57. package/dist/workers/stripe_webhook.js.map +1 -0
  58. package/dist/workers/stripe_webhook_connect.d.ts +1 -0
  59. package/dist/workers/stripe_webhook_connect.js +97 -0
  60. package/dist/workers/stripe_webhook_connect.js.map +1 -0
  61. package/dist/workers/stripe_webhook_secure.d.ts +1 -0
  62. package/dist/workers/stripe_webhook_secure.js +82 -0
  63. package/dist/workers/stripe_webhook_secure.js.map +1 -0
  64. package/jest.config.json +19 -0
  65. package/package.json +56 -0
  66. package/src/functions.ts +48 -0
  67. package/src/index.ts +25 -0
  68. package/src/lib/meter/d1_usage_event_store.ts +176 -0
  69. package/src/lib/meter/flush_meter.ts +45 -0
  70. package/src/lib/meter/interface.ts +118 -0
  71. package/src/lib/meter/kv_usage_buffer.ts +79 -0
  72. package/src/lib/meter/record_usage.ts +87 -0
  73. package/src/lib/meter/stripe_meter_client.ts +117 -0
  74. package/src/lib/options.ts +299 -0
  75. package/src/lib/purchase/d1_purchase_store.ts +268 -0
  76. package/src/lib/purchase/helpers.ts +77 -0
  77. package/src/lib/purchase/interface.ts +215 -0
  78. package/src/lib/purchase/sync_payment.ts +95 -0
  79. package/src/lib/stripe_client.ts +76 -0
  80. package/src/meter.ts +21 -0
  81. package/src/purchase.ts +66 -0
  82. package/src/workers/stripe.ts +881 -0
  83. package/src/workers/stripe_webhook.ts +414 -0
  84. package/src/workers/stripe_webhook_connect.ts +107 -0
  85. package/src/workers/stripe_webhook_secure.ts +91 -0
  86. package/test/helpers/mem_store.ts +97 -0
  87. package/test/meter.test.ts +217 -0
  88. package/test/purchase_store.test.ts +154 -0
  89. package/test/stripe.test.ts +146 -0
  90. package/test/stripe_webhook.test.ts +174 -0
  91. package/tsconfig.json +14 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 3.0.0
2
+
3
+ - **FEAT**(masamune_cloudflare_purchase_stripe): initial release with Stripe Meter (metered billing) library for Cloudflare Workers.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 mathru (https://mathru.net)
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,174 @@
1
+ <p align="center">
2
+ <a href="https://mathru.net">
3
+ <img width="240px" src="https://raw.githubusercontent.com/mathrunet/node_masamune/main/.github/images/icon.png" alt="Masamune logo" style="border-radius: 32px"><br/>
4
+ </a>
5
+ <h1 align="center">Stripe for Cloudflare Workers</h1>
6
+ </p>
7
+
8
+ <p align="center">
9
+ <a href="https://github.com/mathrunet">
10
+ <img src="https://img.shields.io/static/v1?label=GitHub&message=Follow&logo=GitHub&color=333333&link=https://github.com/mathrunet" alt="Follow on GitHub" />
11
+ </a>
12
+ <a href="https://x.com/mathru">
13
+ <img src="https://img.shields.io/static/v1?label=@mathru&message=Follow&logo=X&color=0F1419&link=https://x.com/mathru" alt="Follow on X" />
14
+ </a>
15
+ <a href="https://www.youtube.com/c/mathrunetchannel">
16
+ <img src="https://img.shields.io/static/v1?label=YouTube&message=Follow&logo=YouTube&color=FF0000&link=https://www.youtube.com/c/mathrunetchannel" alt="Follow on YouTube" />
17
+ </a>
18
+ <a href="https://github.com/invertase/melos">
19
+ <img src="https://img.shields.io/static/v1?label=maintained%20with&message=melos&color=FF1493&link=https://github.com/invertase/melos" alt="Maintained with Melos" />
20
+ </a>
21
+ </p>
22
+
23
+ <p align="center">
24
+ <a href="https://github.com/sponsors/mathrunet"><img src="https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=ff69b4&link=https://github.com/sponsors/mathrunet" alt="GitHub Sponsor" /></a>
25
+ </p>
26
+
27
+ ---
28
+
29
+ [[GitHub]](https://github.com/mathrunet) | [[YouTube]](https://www.youtube.com/c/mathrunetchannel) | [[Packages]](https://pub.dev/publishers/mathru.net/packages) | [[X]](https://x.com/mathru) | [[LinkedIn]](https://www.linkedin.com/in/mathrunet/) | [[mathru.net]](https://mathru.net)
30
+
31
+ ---
32
+
33
+ Masamune framework plugin package for billing with Stripe on Cloudflare Workers.
34
+
35
+ # Installation
36
+
37
+ Install the following package:
38
+
39
+ ```bash
40
+ npm install @mathrunet/masamune_cloudflare_purchase_stripe
41
+ ```
42
+
43
+ # Implementation
44
+
45
+ ## Features
46
+
47
+ Provides payment actions equivalent to `@mathrunet/masamune_firebase_purchase_stripe` and a Stripe Meter library for usage-based billing. All operations use the `stripe` SDK configured for Workers with `createFetchHttpClient`.
48
+
49
+ ### Payment Endpoints (Functions)
50
+
51
+ - `stripe` — payment actions selected by `mode` (POST with a JSON body)
52
+ - Connect: `create_account` / `delete_account` / `get_account` / `dashboard_account`
53
+ - Customers and payment methods: `create_customer_and_payment` / `set_customer_default_payment` / `delete_payment` / `delete_customer`
54
+ - Authorization: `authorization` / `confirm_authorization`
55
+ - Payments: `create_purchase` / `confirm_purchase` / `capture_purchase` / `refresh_purchase` / `cancel_purchase` / `refund_purchase`
56
+ - Subscriptions: `create_subscription` / `delete_subscription`
57
+ - `stripeWebhook` — receives Stripe webhooks (signature verification required). Processes PaymentIntent, payment method, customer, Checkout, and subscription events (creation/update/trial end/deletion) and synchronizes them to the store.
58
+ - `stripeWebhookConnect` — Stripe Connect webhook (`account.updated`)
59
+ - `stripeWebhookSecure` — redirect webhook after 3D Secure authentication
60
+
61
+ Data is stored through `StripePurchaseStore` instead of Firestore. The default is D1's `D1StripePurchaseStore` (`stripe_users` / `stripe_payments` / `stripe_purchases`). Table names are configurable, and custom storage can be substituted by implementing `StripePurchaseStore`.
62
+
63
+ ```typescript
64
+ import { Hono } from "hono";
65
+ import { Functions } from "@mathrunet/masamune_cloudflare_purchase_stripe";
66
+
67
+ const app = new Hono();
68
+ app.route("/stripe/webhook", Functions.stripeWebhook().build());
69
+ app.route("/stripe", Functions.stripe().build());
70
+ ```
71
+
72
+ To keep Worker bundles small, import builders directly from the `dist/purchase` entry point (root imports include re-exports of the entire Masamune framework).
73
+
74
+ ```typescript
75
+ import { Hono } from "hono";
76
+ import {
77
+ buildStripeWebhook,
78
+ D1StripePurchaseStore,
79
+ } from "@mathrunet/masamune_cloudflare_purchase_stripe/dist/purchase";
80
+
81
+ const app = buildStripeWebhook(new Hono(), {
82
+ secretKey: env.STRIPE_SECRET_KEY,
83
+ webhookSecret: env.STRIPE_WEBHOOK_SECRET,
84
+ store: () => new D1StripePurchaseStore(env.DB),
85
+ }, {});
86
+ ```
87
+
88
+ ### D1 Schema (Default)
89
+
90
+ ```sql
91
+ CREATE TABLE stripe_users (
92
+ user_id TEXT PRIMARY KEY,
93
+ customer_id TEXT,
94
+ account_id TEXT,
95
+ data TEXT NOT NULL DEFAULT '{}',
96
+ created_at INTEGER NOT NULL,
97
+ updated_at INTEGER NOT NULL
98
+ );
99
+ CREATE INDEX idx_stripe_users_customer ON stripe_users(customer_id);
100
+ CREATE INDEX idx_stripe_users_account ON stripe_users(account_id);
101
+
102
+ CREATE TABLE stripe_payments (
103
+ user_id TEXT NOT NULL,
104
+ payment_id TEXT NOT NULL,
105
+ data TEXT NOT NULL DEFAULT '{}',
106
+ created_at INTEGER NOT NULL,
107
+ updated_at INTEGER NOT NULL,
108
+ PRIMARY KEY (user_id, payment_id)
109
+ );
110
+
111
+ CREATE TABLE stripe_purchases (
112
+ order_id TEXT PRIMARY KEY,
113
+ user_id TEXT,
114
+ purchase_id TEXT,
115
+ subscription_id TEXT,
116
+ data TEXT NOT NULL DEFAULT '{}',
117
+ created_at INTEGER NOT NULL,
118
+ updated_at INTEGER NOT NULL
119
+ );
120
+ CREATE INDEX idx_stripe_purchases_user ON stripe_purchases(user_id);
121
+ CREATE INDEX idx_stripe_purchases_purchase ON stripe_purchases(purchase_id);
122
+ CREATE INDEX idx_stripe_purchases_subscription ON stripe_purchases(subscription_id);
123
+ ```
124
+
125
+ ### Stripe Meter Library (Usage-Based Billing)
126
+
127
+ - `recordUsage` — records usage events. Inserts them into D1 (the source of truth) and flushes to Stripe when the KV buffer is full (default 10 units) or the time threshold is reached (default 60 seconds).
128
+ - `flushStripeMeter` — aggregates unsent events by customer and sends batches to the Stripe Meter Event API (`/v1/billing/meter_events`, basil or later). Each batch uses a unique identifier for idempotency. Failed events remain unsent and are retried on the next run.
129
+ - `D1UsageEventStore` / `KVUsageBuffer` — bundled D1 and KV adapters. Table names, column names, and key prefixes are configurable. Substitute custom storage by implementing `StripeUsageEventStore` / `StripeUsageBuffer`.
130
+ - `StripeMeterClient` — uses the `stripe` SDK with a fetch HTTP client. Tests can inject `fetch` or `stripeClient` stubs.
131
+
132
+ To keep Worker bundles small when using only the Meter library, import from the `dist/meter` entry point (root imports include re-exports of the entire Masamune framework).
133
+
134
+ ```typescript
135
+ import {
136
+ D1UsageEventStore,
137
+ KVUsageBuffer,
138
+ StripeMeterClient,
139
+ recordUsage,
140
+ } from "@mathrunet/masamune_cloudflare_purchase_stripe/dist/meter";
141
+
142
+ // For example, at the end of request handling (waitUntil recommended)
143
+ await recordUsage({
144
+ store: new D1UsageEventStore(env.DB),
145
+ buffer: new KVUsageBuffer(env.USAGE_BUFFER),
146
+ client: new StripeMeterClient({
147
+ secretKey: env.PURCHASE_STRIPE_SECRETKEY,
148
+ eventName: env.PURCHASE_STRIPE_METER_EVENT_NAME ?? "my_meter",
149
+ }),
150
+ customerKey: apiKey.id,
151
+ stripeCustomerId: apiKey.stripe_customer_id,
152
+ endpoint: "decide",
153
+ units: 1,
154
+ });
155
+ ```
156
+
157
+ If the secret key or the customer's Stripe customer ID is missing, sending is skipped and usage is recorded only in D1.
158
+
159
+ ## Environment Variables
160
+
161
+ - `PURCHASE_STRIPE_SECRETKEY` — Stripe secret key (Worker secret)
162
+ - `PURCHASE_STRIPE_METER_EVENT_NAME` — Stripe Meter event name
163
+ - `PURCHASE_STRIPE_WEBHOOKSECRET` — signing secret for the main webhook (Worker secret)
164
+ - `PURCHASE_STRIPE_WEBHOOKCONNECTSECRET` — signing secret for the Connect webhook (Worker secret)
165
+ - `PURCHASE_STRIPE_EMAILPROVIDER` — 3D Secure email provider (default `sendgrid`)
166
+ - `MAIL_SENDGRID_APIKEY` — SendGrid API key (required only for 3D Secure email; Worker secret)
167
+
168
+ The Firebase implementation's email lookup through `admin.auth()` is replaced by an injected `options.resolveUserEmail`, the user document's `email` field, or the default payment method's billing details.
169
+
170
+ # GitHub Sponsors
171
+
172
+ Sponsors are always welcome. Thank you for your support!
173
+
174
+ [https://github.com/sponsors/mathrunet](https://github.com/sponsors/mathrunet)
@@ -0,0 +1,44 @@
1
+ import { WorkersData } from "@mathrunet/masamune_cloudflare";
2
+ import { StripePurchaseWorkersOptions } from "./lib/options";
3
+ /**
4
+ * Define a list of applicable Functions for CloudflareWorkers.
5
+ *
6
+ * Endpoints have parity with `@mathrunet/masamune_firebase_purchase_stripe`:
7
+ * `stripe` handles mode-based actions (`create_customer_and_payment`,
8
+ * `create_purchase`, `create_subscription`, etc.) and the webhooks receive
9
+ * events from Stripe. The metered billing library is provided separately in
10
+ * `lib/meter`.
11
+ *
12
+ * CloudflareWorkers用の適用可能なFunctionの一覧を定義します。
13
+ *
14
+ * エンドポイントは`@mathrunet/masamune_firebase_purchase_stripe`と同等です。
15
+ * `stripe`は`mode`分岐のアクション(`create_customer_and_payment`、
16
+ * `create_purchase`、`create_subscription`など)を処理し、各WebhookはStripeから
17
+ * のイベントを受信します。従量課金ライブラリは`lib/meter`で別途提供します。
18
+ */
19
+ export declare const Functions: {
20
+ /**
21
+ * Performs various Stripe processes (mode-based actions).
22
+ *
23
+ * Stripeの各種処理(mode分岐のアクション)を実行します。
24
+ */
25
+ readonly stripe: (options?: StripePurchaseWorkersOptions) => WorkersData;
26
+ /**
27
+ * Receives and processes webhooks from Stripe.
28
+ *
29
+ * StripeからのWebhookを受け取り処理を行います。
30
+ */
31
+ readonly stripeWebhook: (options?: StripePurchaseWorkersOptions) => WorkersData;
32
+ /**
33
+ * Receives and processes webhooks for Stripe Connect.
34
+ *
35
+ * Stripe Connect用のWebhookを受信して処理します。
36
+ */
37
+ readonly stripeWebhookConnect: (options?: StripePurchaseWorkersOptions) => WorkersData;
38
+ /**
39
+ * Webhook for proper redirection when 3D Secure authentication is required.
40
+ *
41
+ * 3Dセキュア認証が必要な場合、適切なリダイレクトを行うためのWebhookです。
42
+ */
43
+ readonly stripeWebhookSecure: (options?: StripePurchaseWorkersOptions) => WorkersData;
44
+ };
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Functions = void 0;
4
+ const masamune_cloudflare_1 = require("@mathrunet/masamune_cloudflare");
5
+ /**
6
+ * Define a list of applicable Functions for CloudflareWorkers.
7
+ *
8
+ * Endpoints have parity with `@mathrunet/masamune_firebase_purchase_stripe`:
9
+ * `stripe` handles mode-based actions (`create_customer_and_payment`,
10
+ * `create_purchase`, `create_subscription`, etc.) and the webhooks receive
11
+ * events from Stripe. The metered billing library is provided separately in
12
+ * `lib/meter`.
13
+ *
14
+ * CloudflareWorkers用の適用可能なFunctionの一覧を定義します。
15
+ *
16
+ * エンドポイントは`@mathrunet/masamune_firebase_purchase_stripe`と同等です。
17
+ * `stripe`は`mode`分岐のアクション(`create_customer_and_payment`、
18
+ * `create_purchase`、`create_subscription`など)を処理し、各WebhookはStripeから
19
+ * のイベントを受信します。従量課金ライブラリは`lib/meter`で別途提供します。
20
+ */
21
+ exports.Functions = {
22
+ /**
23
+ * Performs various Stripe processes (mode-based actions).
24
+ *
25
+ * Stripeの各種処理(mode分岐のアクション)を実行します。
26
+ */
27
+ stripe: (options = {}) => new masamune_cloudflare_1.WorkersData({ path: "/stripe", func: require("./workers/stripe"), options: options }),
28
+ /**
29
+ * Receives and processes webhooks from Stripe.
30
+ *
31
+ * StripeからのWebhookを受け取り処理を行います。
32
+ */
33
+ stripeWebhook: (options = {}) => new masamune_cloudflare_1.WorkersData({ path: "/stripe/webhook", func: require("./workers/stripe_webhook"), options: options }),
34
+ /**
35
+ * Receives and processes webhooks for Stripe Connect.
36
+ *
37
+ * Stripe Connect用のWebhookを受信して処理します。
38
+ */
39
+ stripeWebhookConnect: (options = {}) => new masamune_cloudflare_1.WorkersData({ path: "/stripe/webhook/connect", func: require("./workers/stripe_webhook_connect"), options: options }),
40
+ /**
41
+ * Webhook for proper redirection when 3D Secure authentication is required.
42
+ *
43
+ * 3Dセキュア認証が必要な場合、適切なリダイレクトを行うためのWebhookです。
44
+ */
45
+ stripeWebhookSecure: (options = {}) => new masamune_cloudflare_1.WorkersData({ path: "/stripe/webhook/secure", func: require("./workers/stripe_webhook_secure"), options: options }),
46
+ };
47
+ //# sourceMappingURL=functions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functions.js","sourceRoot":"","sources":["../src/functions.ts"],"names":[],"mappings":";;;AAAA,wEAA6D;AAG7D;;;;;;;;;;;;;;;GAeG;AACU,QAAA,SAAS,GAAG;IACrB;;;;OAIG;IACH,MAAM,EAAE,CAAC,UAAwC,EAAE,EAAE,EAAE,CAAC,IAAI,iCAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,kBAAkB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAEjJ;;;;OAIG;IACH,aAAa,EAAE,CAAC,UAAwC,EAAE,EAAE,EAAE,CAAC,IAAI,iCAAW,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,OAAO,CAAC,0BAA0B,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAExK;;;;OAIG;IACH,oBAAoB,EAAE,CAAC,UAAwC,EAAE,EAAE,EAAE,CAAC,IAAI,iCAAW,CAAC,EAAE,IAAI,EAAE,yBAAyB,EAAE,IAAI,EAAE,OAAO,CAAC,kCAAkC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAE/L;;;;OAIG;IACH,mBAAmB,EAAE,CAAC,UAAwC,EAAE,EAAE,EAAE,CAAC,IAAI,iCAAW,CAAC,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,OAAO,CAAC,iCAAiC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;CACtL,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) 2026 mathru. All rights reserved.
3
+ *
4
+ * Masamune framework plugin package for billing with Stripe on Cloudflare Workers.
5
+ *
6
+ * To use, import * as m from "@mathrunet/masamune_cloudflare_purchase_stripe";
7
+ *
8
+ * [mathru.net]: https://mathru.net
9
+ * [YouTube]: https://www.youtube.com/c/mathrunetchannel
10
+ */
11
+ export * from "@mathrunet/masamune";
12
+ export * from "@mathrunet/masamune_cloudflare";
13
+ export * from "./functions";
14
+ export * from "./lib/options";
15
+ export * from "./lib/stripe_client";
16
+ export * from "./lib/purchase/interface";
17
+ export * from "./lib/purchase/d1_purchase_store";
18
+ export * from "./lib/purchase/helpers";
19
+ export * from "./lib/purchase/sync_payment";
20
+ export * from "./lib/meter/interface";
21
+ export * from "./lib/meter/stripe_meter_client";
22
+ export * from "./lib/meter/flush_meter";
23
+ export * from "./lib/meter/record_usage";
24
+ export * from "./lib/meter/d1_usage_event_store";
25
+ export * from "./lib/meter/kv_usage_buffer";
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * Copyright (c) 2026 mathru. All rights reserved.
19
+ *
20
+ * Masamune framework plugin package for billing with Stripe on Cloudflare Workers.
21
+ *
22
+ * To use, import * as m from "@mathrunet/masamune_cloudflare_purchase_stripe";
23
+ *
24
+ * [mathru.net]: https://mathru.net
25
+ * [YouTube]: https://www.youtube.com/c/mathrunetchannel
26
+ */
27
+ __exportStar(require("@mathrunet/masamune"), exports);
28
+ __exportStar(require("@mathrunet/masamune_cloudflare"), exports);
29
+ __exportStar(require("./functions"), exports);
30
+ __exportStar(require("./lib/options"), exports);
31
+ __exportStar(require("./lib/stripe_client"), exports);
32
+ __exportStar(require("./lib/purchase/interface"), exports);
33
+ __exportStar(require("./lib/purchase/d1_purchase_store"), exports);
34
+ __exportStar(require("./lib/purchase/helpers"), exports);
35
+ __exportStar(require("./lib/purchase/sync_payment"), exports);
36
+ __exportStar(require("./lib/meter/interface"), exports);
37
+ __exportStar(require("./lib/meter/stripe_meter_client"), exports);
38
+ __exportStar(require("./lib/meter/flush_meter"), exports);
39
+ __exportStar(require("./lib/meter/record_usage"), exports);
40
+ __exportStar(require("./lib/meter/d1_usage_event_store"), exports);
41
+ __exportStar(require("./lib/meter/kv_usage_buffer"), exports);
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;GASG;AACH,sDAAoC;AACpC,iEAA+C;AAC/C,8CAA4B;AAC5B,gDAA8B;AAC9B,sDAAoC;AACpC,2DAAyC;AACzC,mEAAiD;AACjD,yDAAuC;AACvC,8DAA4C;AAC5C,wDAAsC;AACtC,kEAAgD;AAChD,0DAAwC;AACxC,2DAAyC;AACzC,mEAAiD;AACjD,8DAA4C"}
@@ -0,0 +1,128 @@
1
+ import { StripeUsageEvent, StripeUsageEventStore } from "./interface";
2
+ /**
3
+ * Minimal structural interface for a D1-like SQL database.
4
+ *
5
+ * Cloudflare's `D1Database` satisfies this without depending on
6
+ * `@cloudflare/workers-types`.
7
+ *
8
+ * D1相当のSQLデータベースの最小の構造的インターフェース。
9
+ *
10
+ * `@cloudflare/workers-types`に依存せずCloudflareの`D1Database`が適合します。
11
+ */
12
+ export interface SqlDatabaseLike {
13
+ /**
14
+ * Prepare a SQL statement.
15
+ *
16
+ * SQLステートメントを準備します。
17
+ */
18
+ prepare(sql: string): {
19
+ bind(...args: unknown[]): {
20
+ run(): Promise<unknown>;
21
+ all<T = Record<string, unknown>>(): Promise<{
22
+ results?: T[] | undefined;
23
+ }>;
24
+ };
25
+ };
26
+ }
27
+ /**
28
+ * Column mapping options for [D1UsageEventStore]. Defaults follow the
29
+ * `usage_events` schema (id / api_key_id / endpoint / units / decision_source /
30
+ * created_at / flushed_to_stripe).
31
+ *
32
+ * [D1UsageEventStore]のカラムマッピングオプション。デフォルトは`usage_events`
33
+ * スキーマ(id / api_key_id / endpoint / units / decision_source / created_at /
34
+ * flushed_to_stripe)に従います。
35
+ */
36
+ export interface D1UsageEventStoreOptions {
37
+ /**
38
+ * Table name. Defaults to `usage_events`.
39
+ *
40
+ * テーブル名。デフォルトは`usage_events`。
41
+ */
42
+ table?: string | undefined;
43
+ /**
44
+ * Primary key column. Defaults to `id`.
45
+ *
46
+ * 主キーのカラム。デフォルトは`id`。
47
+ */
48
+ idColumn?: string | undefined;
49
+ /**
50
+ * Customer key column. Defaults to `api_key_id`.
51
+ *
52
+ * 顧客キーのカラム。デフォルトは`api_key_id`。
53
+ */
54
+ customerKeyColumn?: string | undefined;
55
+ /**
56
+ * Endpoint column. Defaults to `endpoint`.
57
+ *
58
+ * エンドポイントのカラム。デフォルトは`endpoint`。
59
+ */
60
+ endpointColumn?: string | undefined;
61
+ /**
62
+ * Units column. Defaults to `units`.
63
+ *
64
+ * ユニット数のカラム。デフォルトは`units`。
65
+ */
66
+ unitsColumn?: string | undefined;
67
+ /**
68
+ * Source label column. Defaults to `decision_source`.
69
+ *
70
+ * ソースラベルのカラム。デフォルトは`decision_source`。
71
+ */
72
+ sourceColumn?: string | undefined;
73
+ /**
74
+ * Creation time column. Defaults to `created_at`.
75
+ *
76
+ * 作成時刻のカラム。デフォルトは`created_at`。
77
+ */
78
+ createdAtColumn?: string | undefined;
79
+ /**
80
+ * Flushed flag column (0/1). Defaults to `flushed_to_stripe`.
81
+ *
82
+ * 送信済みフラグ(0/1)のカラム。デフォルトは`flushed_to_stripe`。
83
+ */
84
+ flushedColumn?: string | undefined;
85
+ }
86
+ /**
87
+ * [StripeUsageEventStore] backed by Cloudflare D1 (or any compatible SQL database).
88
+ *
89
+ * Cloudflare D1(または互換SQLデータベース)を用いた[StripeUsageEventStore]。
90
+ */
91
+ export declare class D1UsageEventStore implements StripeUsageEventStore {
92
+ /**
93
+ * [StripeUsageEventStore] backed by Cloudflare D1 (or any compatible SQL database).
94
+ *
95
+ * Cloudflare D1(または互換SQLデータベース)を用いた[StripeUsageEventStore]。
96
+ */
97
+ constructor(db: SqlDatabaseLike, options?: D1UsageEventStoreOptions);
98
+ private readonly _db;
99
+ private readonly _table;
100
+ private readonly _id;
101
+ private readonly _customerKey;
102
+ private readonly _endpoint;
103
+ private readonly _units;
104
+ private readonly _source;
105
+ private readonly _createdAt;
106
+ private readonly _flushed;
107
+ /**
108
+ * Persist a usage event with `flushed = 0`.
109
+ *
110
+ * 使用量イベントを未送信状態で永続化します。
111
+ */
112
+ insert(event: StripeUsageEvent): Promise<void>;
113
+ /**
114
+ * List events not yet flushed to Stripe for the given customer key.
115
+ *
116
+ * 指定した顧客キーのStripe未送信イベントを一覧します。
117
+ */
118
+ listPending(customerKey: string): Promise<{
119
+ id: string;
120
+ units: number;
121
+ }[]>;
122
+ /**
123
+ * Mark the given events as flushed to Stripe.
124
+ *
125
+ * 指定したイベントをStripe送信済みとして記録します。
126
+ */
127
+ markFlushed(ids: string[]): Promise<void>;
128
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.D1UsageEventStore = void 0;
4
+ /**
5
+ * [StripeUsageEventStore] backed by Cloudflare D1 (or any compatible SQL database).
6
+ *
7
+ * Cloudflare D1(または互換SQLデータベース)を用いた[StripeUsageEventStore]。
8
+ */
9
+ class D1UsageEventStore {
10
+ /**
11
+ * [StripeUsageEventStore] backed by Cloudflare D1 (or any compatible SQL database).
12
+ *
13
+ * Cloudflare D1(または互換SQLデータベース)を用いた[StripeUsageEventStore]。
14
+ */
15
+ constructor(db, options = {}) {
16
+ this._db = db;
17
+ this._table = options.table ?? "usage_events";
18
+ this._id = options.idColumn ?? "id";
19
+ this._customerKey = options.customerKeyColumn ?? "api_key_id";
20
+ this._endpoint = options.endpointColumn ?? "endpoint";
21
+ this._units = options.unitsColumn ?? "units";
22
+ this._source = options.sourceColumn ?? "decision_source";
23
+ this._createdAt = options.createdAtColumn ?? "created_at";
24
+ this._flushed = options.flushedColumn ?? "flushed_to_stripe";
25
+ }
26
+ _db;
27
+ _table;
28
+ _id;
29
+ _customerKey;
30
+ _endpoint;
31
+ _units;
32
+ _source;
33
+ _createdAt;
34
+ _flushed;
35
+ /**
36
+ * Persist a usage event with `flushed = 0`.
37
+ *
38
+ * 使用量イベントを未送信状態で永続化します。
39
+ */
40
+ async insert(event) {
41
+ await this._db
42
+ .prepare(`INSERT INTO ${this._table}
43
+ (${this._id}, ${this._customerKey}, ${this._endpoint}, ${this._units}, ${this._source}, ${this._createdAt}, ${this._flushed})
44
+ VALUES (?, ?, ?, ?, ?, ?, 0)`)
45
+ .bind(event.id, event.customerKey, event.endpoint, event.units, event.source, event.createdAt)
46
+ .run();
47
+ }
48
+ /**
49
+ * List events not yet flushed to Stripe for the given customer key.
50
+ *
51
+ * 指定した顧客キーのStripe未送信イベントを一覧します。
52
+ */
53
+ async listPending(customerKey) {
54
+ const pending = await this._db
55
+ .prepare(`SELECT ${this._id} AS id, ${this._units} AS units FROM ${this._table}
56
+ WHERE ${this._customerKey} = ? AND ${this._flushed} = 0`)
57
+ .bind(customerKey)
58
+ .all();
59
+ return pending.results ?? [];
60
+ }
61
+ /**
62
+ * Mark the given events as flushed to Stripe.
63
+ *
64
+ * 指定したイベントをStripe送信済みとして記録します。
65
+ */
66
+ async markFlushed(ids) {
67
+ if (ids.length <= 0) {
68
+ return;
69
+ }
70
+ const placeholders = ids.map(() => "?").join(",");
71
+ await this._db
72
+ .prepare(`UPDATE ${this._table} SET ${this._flushed} = 1 WHERE ${this._id} IN (${placeholders})`)
73
+ .bind(...ids)
74
+ .run();
75
+ }
76
+ }
77
+ exports.D1UsageEventStore = D1UsageEventStore;
78
+ //# sourceMappingURL=d1_usage_event_store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d1_usage_event_store.js","sourceRoot":"","sources":["../../../src/lib/meter/d1_usage_event_store.ts"],"names":[],"mappings":";;;AA6FA;;;;GAIG;AACH,MAAa,iBAAiB;IAC1B;;;;OAIG;IACH,YAAY,EAAmB,EAAE,UAAoC,EAAE;QACnE,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,cAAc,CAAC;QAC9C,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACpC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,iBAAiB,IAAI,YAAY,CAAC;QAC9D,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,cAAc,IAAI,UAAU,CAAC;QACtD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,YAAY,IAAI,iBAAiB,CAAC;QACzD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,eAAe,IAAI,YAAY,CAAC;QAC1D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,aAAa,IAAI,mBAAmB,CAAC;IACjE,CAAC;IAEgB,GAAG,CAAkB;IACrB,MAAM,CAAS;IACf,GAAG,CAAS;IACZ,YAAY,CAAS;IACrB,SAAS,CAAS;IAClB,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,UAAU,CAAS;IACnB,QAAQ,CAAS;IAElC;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,KAAuB;QAChC,MAAM,IAAI,CAAC,GAAG;aACT,OAAO,CACJ,eAAe,IAAI,CAAC,MAAM;qBACrB,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,QAAQ;8CAC/F,CACjC;aACA,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC;aAC7F,GAAG,EAAE,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,WAAmB;QACjC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG;aACzB,OAAO,CACJ,UAAU,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,MAAM,kBAAkB,IAAI,CAAC,MAAM;yBAC5D,IAAI,CAAC,YAAY,YAAY,IAAI,CAAC,QAAQ,MAAM,CAC5D;aACA,IAAI,CAAC,WAAW,CAAC;aACjB,GAAG,EAAiC,CAAC;QAC1C,OAAO,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,GAAa;QAC3B,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAClB,OAAO;QACX,CAAC;QACD,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,IAAI,CAAC,GAAG;aACT,OAAO,CACJ,UAAU,IAAI,CAAC,MAAM,QAAQ,IAAI,CAAC,QAAQ,cAAc,IAAI,CAAC,GAAG,QAAQ,YAAY,GAAG,CAC1F;aACA,IAAI,CAAC,GAAG,GAAG,CAAC;aACZ,GAAG,EAAE,CAAC;IACf,CAAC;CACJ;AA7ED,8CA6EC"}
@@ -0,0 +1,19 @@
1
+ import { StripeUsageEventStore } from "./interface";
2
+ import { StripeMeterClient } from "./stripe_meter_client";
3
+ /**
4
+ * Flush pending usage events for one customer to the Stripe Meter Event API.
5
+ *
6
+ * Returns `true` when there was nothing to send or the flush succeeded, and
7
+ * `false` when Stripe rejected the batch (events stay pending for a retry).
8
+ *
9
+ * 顧客1件分の未送信使用量イベントをStripe Meter Event APIへフラッシュします。
10
+ *
11
+ * 送信対象がない、または送信に成功した場合は`true`、Stripeがバッチを拒否した
12
+ * 場合は`false`を返します(イベントは未送信のまま残り、次回再送されます)。
13
+ */
14
+ export declare function flushStripeMeter(options: {
15
+ store: StripeUsageEventStore;
16
+ client: StripeMeterClient;
17
+ customerKey: string;
18
+ stripeCustomerId: string | null | undefined;
19
+ }): Promise<boolean>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flushStripeMeter = flushStripeMeter;
4
+ /**
5
+ * Flush pending usage events for one customer to the Stripe Meter Event API.
6
+ *
7
+ * Returns `true` when there was nothing to send or the flush succeeded, and
8
+ * `false` when Stripe rejected the batch (events stay pending for a retry).
9
+ *
10
+ * 顧客1件分の未送信使用量イベントをStripe Meter Event APIへフラッシュします。
11
+ *
12
+ * 送信対象がない、または送信に成功した場合は`true`、Stripeがバッチを拒否した
13
+ * 場合は`false`を返します(イベントは未送信のまま残り、次回再送されます)。
14
+ */
15
+ async function flushStripeMeter(options) {
16
+ if (!options.client.enabled || !options.stripeCustomerId) {
17
+ return true;
18
+ }
19
+ const rows = await options.store.listPending(options.customerKey);
20
+ const total = rows.reduce((sum, row) => sum + row.units, 0);
21
+ if (total <= 0) {
22
+ return true;
23
+ }
24
+ // 時間窓ベースだと同一窓内の別バッチが Stripe の冪等性で捨てられ過小計上になる。
25
+ // 送信成功後に flushed を確定するため、identifier はバッチごとに一意でよい。
26
+ const identifier = `${options.customerKey}:${crypto.randomUUID()}`;
27
+ const sent = await options.client.sendMeterEvent({
28
+ stripeCustomerId: options.stripeCustomerId,
29
+ value: total,
30
+ identifier: identifier,
31
+ });
32
+ if (!sent) {
33
+ return false;
34
+ }
35
+ await options.store.markFlushed(rows.map((row) => row.id));
36
+ return true;
37
+ }
38
+ //# sourceMappingURL=flush_meter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flush_meter.js","sourceRoot":"","sources":["../../../src/lib/meter/flush_meter.ts"],"names":[],"mappings":";;AAcA,4CA8BC;AAzCD;;;;;;;;;;GAUG;AACI,KAAK,UAAU,gBAAgB,CAAC,OAKtC;IACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,8CAA8C;IAC9C,kDAAkD;IAClD,MAAM,UAAU,GAAG,GAAG,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;IACnE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC;QAC7C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,KAAK,EAAE,KAAK;QACZ,UAAU,EAAE,UAAU;KACzB,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC;AAChB,CAAC"}