@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
@@ -0,0 +1,174 @@
1
+ import Stripe from "stripe";
2
+ import { Hono } from "hono";
3
+ import { StripePurchaseWorkersOptions } from "../src/lib/options";
4
+ import { MemoryPurchaseStore } from "./helpers/mem_store";
5
+
6
+ const buildWebhook = require("../src/workers/stripe_webhook") as (
7
+ hono: Hono,
8
+ options: StripePurchaseWorkersOptions,
9
+ data: { [key: string]: any },
10
+ ) => Hono;
11
+
12
+ const WEBHOOK_SECRET = "whsec_test_secret";
13
+ const stripeForSigning = new Stripe("sk_test_dummy", {
14
+ apiVersion: "2025-02-24.acacia",
15
+ });
16
+
17
+ function subscriptionEvent(type: string, overrides: { [key: string]: any } = {}): string {
18
+ return JSON.stringify({
19
+ id: "evt_test_1",
20
+ object: "event",
21
+ api_version: "2025-02-24.acacia",
22
+ type,
23
+ data: {
24
+ object: {
25
+ id: "sub_test_1",
26
+ object: "subscription",
27
+ status: "active",
28
+ customer: "cus_test_1",
29
+ current_period_start: Math.floor(Date.now() / 1000) - 60,
30
+ current_period_end: Math.floor(Date.now() / 1000) + 86400 * 30,
31
+ cancel_at: null,
32
+ cancel_at_period_end: false,
33
+ canceled_at: null,
34
+ collection_method: "charge_automatically",
35
+ currency: "jpy",
36
+ default_payment_method: "pm_test_1",
37
+ ended_at: null,
38
+ latest_invoice: "in_test_1",
39
+ quantity: 1,
40
+ start_date: Math.floor(Date.now() / 1000) - 60,
41
+ application: null,
42
+ application_fee_percent: null,
43
+ metadata: {
44
+ userId: "user_1",
45
+ orderId: "order_1",
46
+ },
47
+ plan: {
48
+ id: "price_test_1",
49
+ active: true,
50
+ amount: 500,
51
+ billing_scheme: "per_unit",
52
+ interval: "month",
53
+ interval_count: 1,
54
+ usage_type: "licensed",
55
+ },
56
+ ...overrides,
57
+ },
58
+ },
59
+ });
60
+ }
61
+
62
+ function buildApp(store: MemoryPurchaseStore): Hono {
63
+ return buildWebhook(new Hono(), {
64
+ secretKey: "sk_test_dummy",
65
+ webhookSecret: WEBHOOK_SECRET,
66
+ store: () => store,
67
+ stripeClient: () => stripeForSigning,
68
+ }, {});
69
+ }
70
+
71
+ async function post(app: Hono, payload: string, signature: string | null): Promise<Response> {
72
+ const headers: { [key: string]: string } = {
73
+ "content-type": "application/json",
74
+ };
75
+ if (signature) {
76
+ headers["stripe-signature"] = signature;
77
+ }
78
+ return app.request("/", {
79
+ method: "POST",
80
+ headers,
81
+ body: payload,
82
+ });
83
+ }
84
+
85
+ describe("stripeWebhook", () => {
86
+ it("正しい署名のcustomer.subscription.createdで購入レコードを保存する", async () => {
87
+ const store = new MemoryPurchaseStore();
88
+ const app = buildApp(store);
89
+ const payload = subscriptionEvent("customer.subscription.created");
90
+ const signature = stripeForSigning.webhooks.generateTestHeaderString({
91
+ payload,
92
+ secret: WEBHOOK_SECRET,
93
+ });
94
+
95
+ const res = await post(app, payload, signature);
96
+
97
+ expect(res.status).toBe(200);
98
+ const purchase = await store.getPurchase("order_1");
99
+ expect(purchase).not.toBeNull();
100
+ expect(purchase?.userId).toBe("user_1");
101
+ expect(purchase?.data).toMatchObject({
102
+ subscription: "sub_test_1",
103
+ price_id: "price_test_1",
104
+ amount: 500,
105
+ interval: "month",
106
+ expired: false,
107
+ customer: "cus_test_1",
108
+ });
109
+ expect((await store.findPurchaseBySubscriptionId("sub_test_1"))?.orderId).toBe("order_1");
110
+ });
111
+
112
+ it("activeでないサブスクリプションは保存せず200を返す", async () => {
113
+ const store = new MemoryPurchaseStore();
114
+ const app = buildApp(store);
115
+ const payload = subscriptionEvent("customer.subscription.updated", { status: "incomplete" });
116
+ const signature = stripeForSigning.webhooks.generateTestHeaderString({
117
+ payload,
118
+ secret: WEBHOOK_SECRET,
119
+ });
120
+
121
+ const res = await post(app, payload, signature);
122
+
123
+ expect(res.status).toBe(200);
124
+ expect(await store.getPurchase("order_1")).toBeNull();
125
+ });
126
+
127
+ it("customer.subscription.deletedで購読レコードをexpiredにする", async () => {
128
+ const store = new MemoryPurchaseStore();
129
+ await store.savePurchase("order_1", {
130
+ subscription: "sub_test_1",
131
+ expired: false,
132
+ user: "user_1",
133
+ });
134
+ const app = buildApp(store);
135
+ const payload = subscriptionEvent("customer.subscription.deleted", {
136
+ status: "canceled",
137
+ ended_at: Math.floor(Date.now() / 1000),
138
+ canceled_at: Math.floor(Date.now() / 1000),
139
+ });
140
+ const signature = stripeForSigning.webhooks.generateTestHeaderString({
141
+ payload,
142
+ secret: WEBHOOK_SECRET,
143
+ });
144
+
145
+ const res = await post(app, payload, signature);
146
+
147
+ expect(res.status).toBe(200);
148
+ expect((await store.getPurchase("order_1"))?.data["expired"]).toBe(true);
149
+ });
150
+
151
+ it("署名が不正なら403で何も保存しない", async () => {
152
+ const store = new MemoryPurchaseStore();
153
+ const app = buildApp(store);
154
+ const payload = subscriptionEvent("customer.subscription.created");
155
+ const signature = stripeForSigning.webhooks.generateTestHeaderString({
156
+ payload,
157
+ secret: "whsec_wrong_secret",
158
+ });
159
+
160
+ const res = await post(app, payload, signature);
161
+
162
+ expect(res.status).toBe(403);
163
+ expect(store.purchases.size).toBe(0);
164
+ });
165
+
166
+ it("署名ヘッダがなければ403を返す", async () => {
167
+ const store = new MemoryPurchaseStore();
168
+ const app = buildApp(store);
169
+
170
+ const res = await post(app, subscriptionEvent("customer.subscription.created"), null);
171
+
172
+ expect(res.status).toBe(403);
173
+ });
174
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2022",
4
+ "module": "commonjs",
5
+ "declaration": true,
6
+ "sourceMap": true,
7
+ "outDir": "./dist",
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "strict": true,
12
+ },
13
+ "include": ["src/**/*.ts"]
14
+ }