@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,861 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const http_error_1 = require("@mathrunet/masamune_cloudflare/dist/lib/src/http_error");
4
+ require("@mathrunet/masamune");
5
+ const options_1 = require("../lib/options");
6
+ const helpers_1 = require("../lib/purchase/helpers");
7
+ /**
8
+ * Performs various Stripe processes (parity with the `stripe` function of
9
+ * `@mathrunet/masamune_firebase_purchase_stripe`).
10
+ *
11
+ * POST a JSON body with a `mode` and the parameters of that mode. Purchase and
12
+ * user data are stored through [StripePurchaseStore] (D1 by default).
13
+ *
14
+ * Stripeの各種処理を実行します(`@mathrunet/masamune_firebase_purchase_stripe`の
15
+ * `stripe`ファンクションと同等)。
16
+ *
17
+ * `mode`とそのモードのパラメーターを持つJSONボディをPOSTします。購入・ユーザー
18
+ * データは[StripePurchaseStore](デフォルトはD1)を通して保存されます。
19
+ *
20
+ * @param {string} PURCHASE_STRIPE_SECRETKEY
21
+ * API key (secret key) to connect to Stripe.
22
+ * Stripeへ接続するためのAPIキー(シークレットキー)。
23
+ *
24
+ * @param {string} PURCHASE_STRIPE_EMAILPROVIDER
25
+ * Mail provider when sending mail. (sendgrid)
26
+ * メールを送信する際のメールプロバイダー。(sendgrid)
27
+ *
28
+ * @param {string} mode
29
+ * [required]
30
+ * Specifies the mode in which Stripe will run.
31
+ * Stripeの実行するモードを指定します。
32
+ *
33
+ * - create_account / delete_account / get_account / dashboard_account
34
+ * - create_customer_and_payment / set_customer_default_payment / delete_payment / delete_customer
35
+ * - authorization / confirm_authorization
36
+ * - create_purchase / confirm_purchase / capture_purchase / refresh_purchase / cancel_purchase / refund_purchase
37
+ * - create_subscription / delete_subscription
38
+ */
39
+ module.exports = (hono, options, data) => {
40
+ hono.post("/", async (c) => {
41
+ try {
42
+ const apiKey = (0, options_1.resolveStripeSecretKey)(c, options);
43
+ if (!apiKey) {
44
+ throw new http_error_1.HttpError(500, "The Stripe secret key is not configured.");
45
+ }
46
+ const store = (0, options_1.resolveStripePurchaseStore)(c, options);
47
+ const sendEmail = (0, options_1.resolveStripeEmailSender)(c, options);
48
+ const stripeClient = (0, options_1.resolveStripeClient)(c, options, apiKey);
49
+ const query = await c.req.json().catch(() => ({}));
50
+ switch (query.mode) {
51
+ case "create_account": {
52
+ const userId = query.userId;
53
+ const locale = query.locale;
54
+ const refreshUrl = query.refreshUrl;
55
+ const returnUrl = query.returnUrl;
56
+ if (!locale) {
57
+ throw new http_error_1.HttpError(400, "The locale is empty.");
58
+ }
59
+ if (!userId) {
60
+ throw new http_error_1.HttpError(400, "The user id is empty.");
61
+ }
62
+ const country = locale.split("_")[1];
63
+ const user = await store.getUser(userId);
64
+ if (!user || !user.data["account"]) {
65
+ const account = await stripeClient.accounts.create({
66
+ type: "express",
67
+ country: country ?? "JP",
68
+ });
69
+ await store.saveUser(userId, {
70
+ "@uid": userId,
71
+ "@time": new Date(),
72
+ user: userId,
73
+ account: account.id,
74
+ });
75
+ const endpoint = await stripeClient.accountLinks.create({
76
+ type: "account_onboarding",
77
+ account: account.id,
78
+ refresh_url: refreshUrl,
79
+ return_url: returnUrl,
80
+ });
81
+ return c.json({
82
+ next: "registration",
83
+ endpoint: endpoint.url,
84
+ accountId: account.id,
85
+ });
86
+ }
87
+ else {
88
+ if (user.data["capability"] && user.data["capability"]["transfers"]) {
89
+ return c.json({
90
+ next: "none",
91
+ });
92
+ }
93
+ const res = await stripeClient.accounts.retrieve(user.data["account"]);
94
+ if (res.capabilities && res.capabilities.transfers === "active") {
95
+ await store.saveUser(userId, {
96
+ capability: {
97
+ transfers: true,
98
+ },
99
+ });
100
+ return c.json({
101
+ next: "none",
102
+ });
103
+ }
104
+ else {
105
+ const endpoint = await stripeClient.accountLinks.create({
106
+ type: "account_onboarding",
107
+ account: user.data["account"],
108
+ refresh_url: refreshUrl,
109
+ return_url: returnUrl,
110
+ });
111
+ return c.json({
112
+ next: "registration",
113
+ endpoint: endpoint.url,
114
+ accountId: user.data["account"],
115
+ });
116
+ }
117
+ }
118
+ }
119
+ case "delete_account": {
120
+ const userId = query.userId;
121
+ if (!userId) {
122
+ throw new http_error_1.HttpError(400, "The user id is empty.");
123
+ }
124
+ const user = await store.getUser(userId);
125
+ if (!user || !user.data["account"]) {
126
+ throw new http_error_1.HttpError(404, "Account id is not found.");
127
+ }
128
+ await stripeClient.accounts.del(user.data["account"]);
129
+ await store.saveUser(userId, {
130
+ account: null,
131
+ capability: null,
132
+ });
133
+ return c.json({
134
+ success: true,
135
+ });
136
+ }
137
+ case "get_account": {
138
+ const userId = query.userId;
139
+ if (!userId) {
140
+ throw new http_error_1.HttpError(400, "The user id is empty.");
141
+ }
142
+ const user = await store.getUser(userId);
143
+ if (!user || !user.data["account"]) {
144
+ throw new http_error_1.HttpError(404, "Account id is not found.");
145
+ }
146
+ const res = await stripeClient.accounts.retrieve(user.data["account"]);
147
+ return c.json(res);
148
+ }
149
+ case "dashboard_account": {
150
+ const userId = query.userId;
151
+ if (!userId) {
152
+ throw new http_error_1.HttpError(400, "The user id is empty.");
153
+ }
154
+ const user = await store.getUser(userId);
155
+ if (!user || !user.data["account"]) {
156
+ throw new http_error_1.HttpError(404, "Account id is not found.");
157
+ }
158
+ const res = await stripeClient.accounts.createLoginLink(user.data["account"]);
159
+ return c.json({
160
+ endpoint: res.url,
161
+ });
162
+ }
163
+ case "create_customer_and_payment": {
164
+ const userId = query.userId;
165
+ const successUrl = query.successUrl;
166
+ const cancelUrl = query.cancelUrl;
167
+ if (!userId) {
168
+ throw new http_error_1.HttpError(400, "The user id is empty.");
169
+ }
170
+ const user = await store.getUser(userId);
171
+ if (!user || !user.data["customer"]) {
172
+ let email = options.resolveUserEmail
173
+ ? await options.resolveUserEmail(c, userId)
174
+ : undefined;
175
+ if (!email && user && typeof user.data["email"] === "string") {
176
+ email = user.data["email"];
177
+ }
178
+ const customer = await stripeClient.customers.create({
179
+ metadata: {
180
+ "user_id": userId,
181
+ },
182
+ email: email ?? undefined,
183
+ });
184
+ await store.saveUser(userId, {
185
+ "@uid": userId,
186
+ "@time": new Date(),
187
+ user: userId,
188
+ customer: customer.id,
189
+ });
190
+ const session = await stripeClient.checkout.sessions.create({
191
+ payment_method_types: ["card"],
192
+ mode: "setup",
193
+ customer: customer.id,
194
+ success_url: successUrl,
195
+ cancel_url: cancelUrl,
196
+ metadata: {
197
+ "user_id": userId,
198
+ },
199
+ });
200
+ return c.json({
201
+ endpoint: session.url,
202
+ customerId: customer.id,
203
+ });
204
+ }
205
+ else {
206
+ const session = await stripeClient.checkout.sessions.create({
207
+ payment_method_types: ["card"],
208
+ mode: "setup",
209
+ customer: user.data["customer"],
210
+ success_url: successUrl,
211
+ cancel_url: cancelUrl,
212
+ });
213
+ return c.json({
214
+ endpoint: session.url,
215
+ customerId: user.data["customer"],
216
+ });
217
+ }
218
+ }
219
+ case "set_customer_default_payment": {
220
+ const userId = query.userId;
221
+ const paymentId = query.paymentId;
222
+ if (!paymentId) {
223
+ throw new http_error_1.HttpError(400, "The payment id is empty.");
224
+ }
225
+ if (!userId) {
226
+ throw new http_error_1.HttpError(400, "The user id is empty.");
227
+ }
228
+ const user = await store.getUser(userId);
229
+ if (!user || !user.data["customer"]) {
230
+ throw new http_error_1.HttpError(404, "The customer is empty.");
231
+ }
232
+ const payments = await store.listPayments(userId);
233
+ const payment = payments.find((item) => item.paymentId === paymentId);
234
+ if (!payment || !payment.data["id"]) {
235
+ throw new http_error_1.HttpError(404, "The payment method is empty.");
236
+ }
237
+ await stripeClient.customers.update(user.data["customer"], {
238
+ invoice_settings: {
239
+ default_payment_method: payment.data["id"],
240
+ },
241
+ });
242
+ if (user.data["defaultPayment"] !== payment.data["id"]) {
243
+ await store.saveUser(userId, {
244
+ defaultPayment: payment.data["id"],
245
+ });
246
+ }
247
+ return c.json({
248
+ success: true,
249
+ });
250
+ }
251
+ case "delete_payment": {
252
+ const userId = query.userId;
253
+ const paymentId = query.paymentId;
254
+ if (!paymentId) {
255
+ throw new http_error_1.HttpError(400, "The payment id is empty.");
256
+ }
257
+ if (!userId) {
258
+ throw new http_error_1.HttpError(400, "The user id is empty.");
259
+ }
260
+ const user = await store.getUser(userId);
261
+ if (!user || !user.data["customer"]) {
262
+ throw new http_error_1.HttpError(404, "The customer is empty.");
263
+ }
264
+ const payments = await store.listPayments(userId);
265
+ const payment = payments.find((item) => item.paymentId === paymentId);
266
+ if (!payment || !payment.data["id"]) {
267
+ throw new http_error_1.HttpError(404, "The payment method is empty.");
268
+ }
269
+ await stripeClient.paymentMethods.detach(payment.data["id"]);
270
+ if (user.data["defaultPayment"] === payment.data["id"]) {
271
+ await store.saveUser(userId, {
272
+ defaultPayment: null,
273
+ });
274
+ }
275
+ return c.json({
276
+ success: true,
277
+ });
278
+ }
279
+ case "delete_customer": {
280
+ const userId = query.userId;
281
+ if (!userId) {
282
+ throw new http_error_1.HttpError(400, "The user id is empty.");
283
+ }
284
+ const user = await store.getUser(userId);
285
+ if (!user || !user.data["customer"]) {
286
+ throw new http_error_1.HttpError(404, "Customer id is not found.");
287
+ }
288
+ await stripeClient.customers.del(user.data["customer"]);
289
+ await store.saveUser(userId, {
290
+ customer: null,
291
+ });
292
+ return c.json({
293
+ success: true,
294
+ });
295
+ }
296
+ case "authorization": {
297
+ const amount = parseFloat(query.amount);
298
+ const currency = query.currency ?? "jpy";
299
+ const returnUrl = query.returnUrl;
300
+ const online = query.online == "true" || query.online === true;
301
+ const emailFrom = query.from;
302
+ const emailTitle = query.title;
303
+ const emailContent = query.content;
304
+ const userId = query.userId;
305
+ if (!userId) {
306
+ throw new http_error_1.HttpError(400, "The user id is empty.");
307
+ }
308
+ const user = await store.getUser(userId);
309
+ if (!user || !user.data["customer"]) {
310
+ throw new http_error_1.HttpError(404, "The customer id is not found.");
311
+ }
312
+ const defaultPayment = await (0, helpers_1.resolveDefaultPayment)({ stripeClient, store, user });
313
+ const email = await (0, helpers_1.resolveUserEmail)({
314
+ context: c,
315
+ workersOptions: options,
316
+ stripeClient,
317
+ user,
318
+ defaultPayment,
319
+ });
320
+ const paymentIntent = await stripeClient.paymentIntents.create({
321
+ payment_method_types: ["card"],
322
+ amount: amount,
323
+ confirm: false,
324
+ capture_method: "manual",
325
+ payment_method: defaultPayment,
326
+ description: "",
327
+ customer: user.data["customer"],
328
+ receipt_email: email,
329
+ currency: currency ?? "usd",
330
+ setup_future_usage: "off_session",
331
+ });
332
+ if (!paymentIntent) {
333
+ throw new http_error_1.HttpError(500, "The payment is failed.");
334
+ }
335
+ const confirmedPaymentIntent = await stripeClient.paymentIntents.confirm(paymentIntent.id, {
336
+ return_url: returnUrl,
337
+ });
338
+ const nextActionUrl = confirmedPaymentIntent.next_action?.redirect_to_url?.url ?? "";
339
+ if (nextActionUrl && !online) {
340
+ if (emailFrom && email && emailTitle && emailContent && sendEmail) {
341
+ await sendEmail({
342
+ from: emailFrom,
343
+ to: email,
344
+ subject: emailTitle,
345
+ text: emailContent.replace("{url}", nextActionUrl),
346
+ });
347
+ }
348
+ else {
349
+ throw new http_error_1.HttpError(503, "3D Secure authentication is required, but the user is offline and no email settings have been configured.");
350
+ }
351
+ }
352
+ return c.json({
353
+ url: online ? nextActionUrl : "",
354
+ returnUrl: online ? confirmedPaymentIntent.next_action?.redirect_to_url?.return_url ?? "" : "",
355
+ authorizedId: paymentIntent.id,
356
+ });
357
+ }
358
+ case "confirm_authorization": {
359
+ const authorizedId = query.authorizedId;
360
+ if (!authorizedId) {
361
+ throw new http_error_1.HttpError(400, "The authorized id is empty.");
362
+ }
363
+ await stripeClient.paymentIntents.cancel(authorizedId);
364
+ return c.json({
365
+ success: true,
366
+ });
367
+ }
368
+ case "create_purchase": {
369
+ const amount = parseFloat(query.amount);
370
+ const revenue = parseFloat(query.revenueRatio ?? 0);
371
+ const currency = query.currency ?? "jpy";
372
+ const userId = query.userId;
373
+ const targetUserId = query.targetUserId;
374
+ const orderId = query.orderId;
375
+ const description = query.description;
376
+ const emailFrom = query.emailFrom;
377
+ const emailTitle = query.emailTitle;
378
+ const emailContent = query.emailContent;
379
+ const locale = query.locale;
380
+ if (!orderId) {
381
+ throw new http_error_1.HttpError(400, "The order id is empty.");
382
+ }
383
+ if (!userId) {
384
+ throw new http_error_1.HttpError(400, "The user id is empty.");
385
+ }
386
+ const user = await store.getUser(userId);
387
+ if (!user || !user.data["customer"]) {
388
+ throw new http_error_1.HttpError(404, "The customer id is not found.");
389
+ }
390
+ const defaultPayment = await (0, helpers_1.resolveDefaultPayment)({ stripeClient, store, user });
391
+ const email = await (0, helpers_1.resolveUserEmail)({
392
+ context: c,
393
+ workersOptions: options,
394
+ stripeClient,
395
+ user,
396
+ defaultPayment,
397
+ });
398
+ let paymentIntent;
399
+ let targetAccount;
400
+ if (targetUserId) {
401
+ const target = await store.getUser(targetUserId);
402
+ if (!target || !target.data["account"]) {
403
+ throw new http_error_1.HttpError(404, "The target data is not found.");
404
+ }
405
+ targetAccount = target.data["account"];
406
+ paymentIntent = await stripeClient.paymentIntents.create({
407
+ payment_method_types: ["card"],
408
+ amount: amount,
409
+ confirm: false,
410
+ capture_method: "manual",
411
+ payment_method: defaultPayment,
412
+ description: description,
413
+ customer: user.data["customer"],
414
+ metadata: {
415
+ "order_id": orderId,
416
+ },
417
+ receipt_email: email,
418
+ currency: currency,
419
+ setup_future_usage: "off_session",
420
+ application_fee_amount: amount * revenue,
421
+ transfer_data: {
422
+ destination: target.data["account"],
423
+ },
424
+ });
425
+ }
426
+ else {
427
+ paymentIntent = await stripeClient.paymentIntents.create({
428
+ payment_method_types: ["card"],
429
+ amount: amount,
430
+ confirm: false,
431
+ capture_method: "manual",
432
+ payment_method: defaultPayment,
433
+ description: description,
434
+ customer: user.data["customer"],
435
+ metadata: {
436
+ "order_id": orderId,
437
+ },
438
+ receipt_email: email,
439
+ currency: currency,
440
+ setup_future_usage: "off_session",
441
+ });
442
+ }
443
+ if (!paymentIntent) {
444
+ throw new http_error_1.HttpError(500, "The payment is failed.");
445
+ }
446
+ await store.savePurchase(orderId, {
447
+ "@uid": orderId,
448
+ "@time": new Date(),
449
+ orderId: orderId,
450
+ purchaseId: paymentIntent.id,
451
+ paymentMethodId: defaultPayment,
452
+ confirm: false,
453
+ verify: false,
454
+ capture: false,
455
+ success: false,
456
+ user: userId,
457
+ target: targetUserId ?? undefined,
458
+ nextAction: {
459
+ url: paymentIntent.next_action?.redirect_to_url?.url ?? "",
460
+ returnUrl: paymentIntent.next_action?.redirect_to_url?.return_url ?? "",
461
+ },
462
+ targetAccount: targetAccount ?? undefined,
463
+ customer: user.data["customer"],
464
+ amount: paymentIntent.amount,
465
+ application: paymentIntent.application,
466
+ applicationFeeAmount: paymentIntent.application_fee_amount,
467
+ transferAmount: paymentIntent.transfer_data?.amount ?? 0,
468
+ transferDistination: paymentIntent.transfer_data?.destination ?? "",
469
+ currency: paymentIntent.currency,
470
+ clientSecret: paymentIntent.client_secret,
471
+ createdTime: new Date(paymentIntent.created * 1000),
472
+ updatedTime: new Date(),
473
+ emailFrom: emailFrom,
474
+ emailTo: email,
475
+ emailTitle: emailTitle,
476
+ emailContent: emailContent,
477
+ locale: locale,
478
+ }, { userId });
479
+ return c.json({
480
+ purchaseId: paymentIntent.id,
481
+ });
482
+ }
483
+ case "confirm_purchase": {
484
+ const userId = query.userId;
485
+ const orderId = query.orderId;
486
+ const successUrl = query.successUrl;
487
+ const failureUrl = query.failureUrl;
488
+ let returnUrl = query.returnUrl;
489
+ const online = query.online == "true" || query.online === true;
490
+ if (!orderId) {
491
+ throw new http_error_1.HttpError(400, "The order id is empty.");
492
+ }
493
+ if (!userId) {
494
+ throw new http_error_1.HttpError(400, "The user id is empty.");
495
+ }
496
+ if (!online) {
497
+ returnUrl = returnUrl + "?token=" + await JSON.stringify({
498
+ userId: userId,
499
+ orderId: orderId,
500
+ successUrl: successUrl,
501
+ failureUrl: failureUrl,
502
+ }).encrypt({
503
+ key: apiKey.slice(0, 32),
504
+ ivKey: apiKey.slice(-16),
505
+ });
506
+ }
507
+ const purchase = await store.getPurchase(orderId, userId);
508
+ if (!purchase || !purchase.data["purchaseId"]) {
509
+ throw new http_error_1.HttpError(404, "The purchase data is invalid.");
510
+ }
511
+ const purchaseData = purchase.data;
512
+ if (purchaseData["error"]) {
513
+ throw new http_error_1.HttpError(409, "The purchase data has some errors");
514
+ }
515
+ if (purchaseData["cancel"]) {
516
+ throw new http_error_1.HttpError(409, "The purchase data is already canceled.");
517
+ }
518
+ if (purchaseData["confirm"]) {
519
+ if (purchaseData["verify"]) {
520
+ return c.json({
521
+ url: "",
522
+ returnUrl: "",
523
+ purchaseId: purchaseData["purchaseId"],
524
+ });
525
+ }
526
+ try {
527
+ const paymentIntent = await stripeClient.paymentIntents.retrieve(purchaseData["purchaseId"]);
528
+ const nextActionUrl = paymentIntent.next_action?.redirect_to_url?.url ?? "";
529
+ const update = {};
530
+ if (nextActionUrl) {
531
+ if (!online) {
532
+ if (purchaseData["emailFrom"] && purchaseData["emailTo"] && purchaseData["emailTitle"] && purchaseData["emailContent"] && sendEmail) {
533
+ await sendEmail({
534
+ from: purchaseData["emailFrom"],
535
+ to: purchaseData["emailTo"],
536
+ subject: purchaseData["emailTitle"],
537
+ text: purchaseData["emailContent"].replace("{url}", nextActionUrl),
538
+ });
539
+ }
540
+ else {
541
+ update["error"] = true;
542
+ update["errorMessage"] = "3D Secure authentication is required, but the user is offline and no email settings have been configured.";
543
+ }
544
+ }
545
+ update["nextAction"] = {
546
+ url: nextActionUrl,
547
+ returnUrl: paymentIntent.next_action?.redirect_to_url?.return_url ?? "",
548
+ };
549
+ await store.savePurchase(orderId, update, { userId });
550
+ return c.json({
551
+ url: online ? nextActionUrl : "",
552
+ returnUrl: online ? paymentIntent.next_action?.redirect_to_url?.return_url ?? "" : "",
553
+ purchaseId: purchaseData["purchaseId"],
554
+ });
555
+ }
556
+ else {
557
+ update["verify"] = true;
558
+ update["nextAction"] = null;
559
+ await store.savePurchase(orderId, update, { userId });
560
+ return c.json({
561
+ url: "",
562
+ returnUrl: "",
563
+ purchaseId: purchaseData["purchaseId"],
564
+ });
565
+ }
566
+ }
567
+ catch (err) {
568
+ if (err instanceof http_error_1.HttpError) {
569
+ throw err;
570
+ }
571
+ await store.savePurchase(orderId, {
572
+ error: true,
573
+ errorMessage: "The Purchase confirmation failed. Please replace the billing information and Refresh.",
574
+ }, { userId });
575
+ throw err;
576
+ }
577
+ }
578
+ else {
579
+ try {
580
+ const paymentIntent = await stripeClient.paymentIntents.confirm(purchaseData["purchaseId"], {
581
+ return_url: returnUrl,
582
+ });
583
+ const nextActionUrl = paymentIntent.next_action?.redirect_to_url?.url ?? "";
584
+ const update = {};
585
+ if (nextActionUrl && !online) {
586
+ if (purchaseData["emailFrom"] && purchaseData["emailTo"] && purchaseData["emailTitle"] && purchaseData["emailContent"] && sendEmail) {
587
+ await sendEmail({
588
+ from: purchaseData["emailFrom"],
589
+ to: purchaseData["emailTo"],
590
+ subject: purchaseData["emailTitle"],
591
+ text: purchaseData["emailContent"].replace("{url}", nextActionUrl),
592
+ });
593
+ }
594
+ else {
595
+ update["error"] = true;
596
+ update["errorMessage"] = "3D Secure authentication is required, but the user is offline and no email settings have been configured.";
597
+ }
598
+ }
599
+ update["nextAction"] = {
600
+ url: nextActionUrl,
601
+ returnUrl: paymentIntent.next_action?.redirect_to_url?.return_url ?? "",
602
+ };
603
+ await store.savePurchase(orderId, update, { userId });
604
+ return c.json({
605
+ url: online ? nextActionUrl : "",
606
+ returnUrl: online ? paymentIntent.next_action?.redirect_to_url?.return_url ?? "" : "",
607
+ purchaseId: purchaseData["purchaseId"],
608
+ });
609
+ }
610
+ catch (err) {
611
+ if (err instanceof http_error_1.HttpError) {
612
+ throw err;
613
+ }
614
+ await store.savePurchase(orderId, {
615
+ error: true,
616
+ errorMessage: "The Purchase confirmation failed. Please replace the billing information and Refresh.",
617
+ }, { userId });
618
+ throw err;
619
+ }
620
+ }
621
+ }
622
+ case "capture_purchase": {
623
+ const userId = query.userId;
624
+ const orderId = query.orderId;
625
+ const amount = parseFloat(query.amount ?? 0.0);
626
+ if (!orderId) {
627
+ throw new http_error_1.HttpError(400, "The order id is empty.");
628
+ }
629
+ if (!userId) {
630
+ throw new http_error_1.HttpError(400, "The user id is empty.");
631
+ }
632
+ const purchase = await store.getPurchase(orderId, userId);
633
+ if (!purchase || !purchase.data["purchaseId"]) {
634
+ throw new http_error_1.HttpError(404, "The purchase data is invalid.");
635
+ }
636
+ const purchaseData = purchase.data;
637
+ if (purchaseData["error"]) {
638
+ throw new http_error_1.HttpError(409, "The purchase data has some errors");
639
+ }
640
+ if (purchaseData["cancel"]) {
641
+ throw new http_error_1.HttpError(409, "This purchase data has already been cancelled.");
642
+ }
643
+ if (!purchaseData["confirm"] || !purchaseData["verify"]) {
644
+ throw new http_error_1.HttpError(412, "The purchase data is not confirmed.");
645
+ }
646
+ if (purchaseData["capture"]) {
647
+ return c.json({
648
+ purchaseId: purchaseData["purchaseId"],
649
+ });
650
+ }
651
+ if (purchaseData["amount"] < amount) {
652
+ throw new http_error_1.HttpError(400, "You cannot capture an amount higher than the billing amount already saved.");
653
+ }
654
+ try {
655
+ const paymentIntent = await (amount > 0 ? stripeClient.paymentIntents.capture(purchaseData["purchaseId"], {
656
+ amount_to_capture: amount,
657
+ }) : stripeClient.paymentIntents.capture(purchaseData["purchaseId"]));
658
+ if (paymentIntent.status !== "succeeded") {
659
+ throw new http_error_1.HttpError(409, "The Payment capture failed.");
660
+ }
661
+ return c.json({
662
+ purchaseId: purchaseData["purchaseId"],
663
+ });
664
+ }
665
+ catch (err) {
666
+ await store.savePurchase(orderId, {
667
+ error: true,
668
+ errorMessage: "The Purchase capture failed. Please replace the billing information and Refresh.",
669
+ }, { userId });
670
+ throw err;
671
+ }
672
+ }
673
+ case "refresh_purchase": {
674
+ const orderId = query.orderId;
675
+ const userId = query.userId;
676
+ if (!orderId) {
677
+ throw new http_error_1.HttpError(400, "The order id is empty.");
678
+ }
679
+ if (!userId) {
680
+ throw new http_error_1.HttpError(400, "The user id is empty.");
681
+ }
682
+ const purchase = await store.getPurchase(orderId, userId);
683
+ if (!purchase || !purchase.data["purchaseId"]) {
684
+ throw new http_error_1.HttpError(404, "The purchase data is invalid.");
685
+ }
686
+ const purchaseData = purchase.data;
687
+ if (purchaseData["success"]) {
688
+ throw new http_error_1.HttpError(409, "The payment has already been succeed.");
689
+ }
690
+ if (!purchaseData["error"]) {
691
+ return c.json({
692
+ success: true,
693
+ });
694
+ }
695
+ const user = await store.getUser(userId);
696
+ if (!user || !user.data["customer"]) {
697
+ throw new http_error_1.HttpError(404, "The customer id is not found.");
698
+ }
699
+ const defaultPayment = await (0, helpers_1.resolveDefaultPayment)({ stripeClient, store, user });
700
+ if (defaultPayment === purchaseData["payment_method"]) {
701
+ throw new http_error_1.HttpError(412, "There was no change in the Payment method.");
702
+ }
703
+ await stripeClient.paymentIntents.update(purchaseData["purchaseId"], {
704
+ payment_method: defaultPayment,
705
+ });
706
+ await store.savePurchase(orderId, {
707
+ paymentMethodId: defaultPayment,
708
+ error: null,
709
+ errorMessage: null,
710
+ }, { userId });
711
+ return c.json({
712
+ success: true,
713
+ });
714
+ }
715
+ case "cancel_purchase": {
716
+ const orderId = query.orderId;
717
+ const userId = query.userId;
718
+ if (!orderId) {
719
+ throw new http_error_1.HttpError(400, "The order id is empty.");
720
+ }
721
+ if (!userId) {
722
+ throw new http_error_1.HttpError(400, "The user id is empty.");
723
+ }
724
+ const purchase = await store.getPurchase(orderId, userId);
725
+ if (!purchase || !purchase.data["purchaseId"]) {
726
+ throw new http_error_1.HttpError(404, "The purchase data is invalid.");
727
+ }
728
+ const purchaseData = purchase.data;
729
+ if (purchaseData["cancel"]) {
730
+ return c.json({
731
+ success: true,
732
+ });
733
+ }
734
+ if (purchaseData["capture"] || purchaseData["success"]) {
735
+ throw new http_error_1.HttpError(412, "The payment has already been completed.");
736
+ }
737
+ await stripeClient.paymentIntents.cancel(purchaseData["purchaseId"]);
738
+ await store.savePurchase(orderId, {
739
+ cancel: true,
740
+ error: null,
741
+ errorMessage: null,
742
+ }, { userId });
743
+ return c.json({
744
+ success: true,
745
+ });
746
+ }
747
+ case "refund_purchase": {
748
+ const orderId = query.orderId;
749
+ const userId = query.userId;
750
+ const amount = parseFloat(query.amount ?? 0.0);
751
+ if (!orderId) {
752
+ throw new http_error_1.HttpError(400, "The order id is empty.");
753
+ }
754
+ if (!userId) {
755
+ throw new http_error_1.HttpError(400, "The user id is empty.");
756
+ }
757
+ const purchase = await store.getPurchase(orderId, userId);
758
+ if (!purchase || !purchase.data["purchaseId"]) {
759
+ throw new http_error_1.HttpError(404, "The purchase data is invalid.");
760
+ }
761
+ const purchaseData = purchase.data;
762
+ if (!purchaseData["capture"] || !purchaseData["success"]) {
763
+ throw new http_error_1.HttpError(412, "The payment is not yet in your jurisdiction.");
764
+ }
765
+ if (purchaseData["amount"] < amount) {
766
+ throw new http_error_1.HttpError(400, "The amount to be refunded exceeds the original amount.");
767
+ }
768
+ try {
769
+ if (amount > 0) {
770
+ await stripeClient.refunds.create({
771
+ payment_intent: purchaseData["purchaseId"],
772
+ amount: amount,
773
+ });
774
+ }
775
+ else {
776
+ await stripeClient.refunds.create({
777
+ payment_intent: purchaseData["purchaseId"],
778
+ });
779
+ }
780
+ await store.savePurchase(orderId, {
781
+ refund: true,
782
+ cancel: true,
783
+ }, { userId });
784
+ return c.json({
785
+ success: true,
786
+ });
787
+ }
788
+ catch (err) {
789
+ await store.savePurchase(orderId, {
790
+ error: true,
791
+ errorMessage: "The Purchase confirmation failed. Please replace the billing information and Refresh.",
792
+ }, { userId });
793
+ throw err;
794
+ }
795
+ }
796
+ case "create_subscription": {
797
+ const productId = query.productId;
798
+ const orderId = query.orderId;
799
+ const userId = query.userId;
800
+ const count = query.count ?? 1;
801
+ const successUrl = query.successUrl;
802
+ const cancelUrl = query.cancelUrl;
803
+ if (!orderId) {
804
+ throw new http_error_1.HttpError(400, "The order id is empty.");
805
+ }
806
+ if (!userId) {
807
+ throw new http_error_1.HttpError(400, "The user id is empty.");
808
+ }
809
+ if (!productId) {
810
+ throw new http_error_1.HttpError(400, "The product id is empty.");
811
+ }
812
+ const res = await stripeClient.checkout.sessions.create({
813
+ billing_address_collection: "auto",
814
+ subscription_data: {
815
+ metadata: {
816
+ "userId": userId,
817
+ "orderId": orderId,
818
+ },
819
+ },
820
+ line_items: [
821
+ {
822
+ price: productId,
823
+ quantity: count,
824
+ },
825
+ ],
826
+ mode: "subscription",
827
+ success_url: successUrl,
828
+ cancel_url: cancelUrl,
829
+ });
830
+ return c.json({
831
+ endpoint: res.url,
832
+ });
833
+ }
834
+ case "delete_subscription": {
835
+ const orderId = query.orderId;
836
+ if (!orderId) {
837
+ throw new http_error_1.HttpError(400, "The order id is empty.");
838
+ }
839
+ const purchase = await store.getPurchase(orderId);
840
+ if (!purchase || !purchase.data["subscription"]) {
841
+ throw new http_error_1.HttpError(404, "The orderId data is not found");
842
+ }
843
+ const res = await stripeClient.subscriptions.update(purchase.data["subscription"], {
844
+ cancel_at_period_end: true,
845
+ });
846
+ return c.json({
847
+ success: res.cancel_at_period_end,
848
+ });
849
+ }
850
+ default: {
851
+ throw new http_error_1.HttpError(404, "There is no mode:" + query.mode);
852
+ }
853
+ }
854
+ }
855
+ catch (err) {
856
+ return (0, http_error_1.jsonError)(c, err);
857
+ }
858
+ });
859
+ return hono;
860
+ };
861
+ //# sourceMappingURL=stripe.js.map