@flopay/js 1.4.0 → 1.4.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/dist/index.mjs CHANGED
@@ -2,7 +2,14 @@
2
2
  import { FloPayError as FloPayError6, resolveBillingApiUrl as resolveBillingApiUrl3, SDK_VERSION as SDK_VERSION4 } from "@flopay/shared";
3
3
 
4
4
  // src/stripe-adapter.ts
5
+ import { loadStripe } from "@stripe/stripe-js";
5
6
  import { FloPayError, isSetupIntentClientSecret } from "@flopay/shared";
7
+
8
+ // src/card-three-ds.ts
9
+ var CARD_THREE_DS_ATTEMPT_TTL_MS = 15 * 6e4;
10
+ var MAX_CARD_THREE_DS_ATTEMPTS = 32;
11
+
12
+ // src/stripe-adapter.ts
6
13
  function toStripeElementType(type) {
7
14
  const map = {
8
15
  payment: "payment",
@@ -25,8 +32,6 @@ function toStripeAppearanceTheme(theme) {
25
32
  return "stripe";
26
33
  }
27
34
  }
28
- var CARD_THREE_DS_ATTEMPT_TTL_MS = 15 * 6e4;
29
- var MAX_CARD_THREE_DS_ATTEMPTS = 32;
30
35
  function cardThreeDsNow() {
31
36
  return globalThis.performance?.now() ?? Date.now();
32
37
  }
@@ -53,6 +58,24 @@ function wrapStripeElement(stripeElement) {
53
58
  }
54
59
  };
55
60
  }
61
+ function toStripeBillingDetails(billing) {
62
+ return {
63
+ billing_details: {
64
+ ...billing.email ? { email: billing.email } : {},
65
+ ...billing.name ? { name: billing.name } : {},
66
+ ...billing.address ? {
67
+ address: {
68
+ ...billing.address.country ? { country: billing.address.country } : {},
69
+ ...billing.address.postal_code ? { postal_code: billing.address.postal_code } : {},
70
+ ...billing.address.city ? { city: billing.address.city } : {},
71
+ ...billing.address.line1 ? { line1: billing.address.line1 } : {},
72
+ ...billing.address.line2 ? { line2: billing.address.line2 } : {},
73
+ ...billing.address.state ? { state: billing.address.state } : {}
74
+ }
75
+ } : {}
76
+ }
77
+ };
78
+ }
56
79
  var StripeAdapter = class {
57
80
  constructor() {
58
81
  this.name = "stripe";
@@ -70,16 +93,6 @@ var StripeAdapter = class {
70
93
  if (typeof window === "undefined") {
71
94
  return;
72
95
  }
73
- let loadStripe;
74
- try {
75
- ({ loadStripe } = await import("@stripe/stripe-js"));
76
- } catch {
77
- throw new FloPayError(
78
- "Failed to load @stripe/stripe-js. Reinstall @flopay/js.",
79
- "api_error",
80
- { code: "StripeJsLoadFailed" }
81
- );
82
- }
83
96
  const stripe = await loadStripe(config.publishableKey, {
84
97
  locale: config.locale ?? "auto"
85
98
  });
@@ -191,22 +204,7 @@ var StripeAdapter = class {
191
204
  };
192
205
  }
193
206
  const cardNumberEl = this.elements.getElement("cardNumber");
194
- const stripeBilling = billingDetails ? {
195
- billing_details: {
196
- ...billingDetails.email ? { email: billingDetails.email } : {},
197
- ...billingDetails.name ? { name: billingDetails.name } : {},
198
- ...billingDetails.address ? {
199
- address: {
200
- ...billingDetails.address.country ? { country: billingDetails.address.country } : {},
201
- ...billingDetails.address.postal_code ? { postal_code: billingDetails.address.postal_code } : {},
202
- ...billingDetails.address.city ? { city: billingDetails.address.city } : {},
203
- ...billingDetails.address.line1 ? { line1: billingDetails.address.line1 } : {},
204
- ...billingDetails.address.line2 ? { line2: billingDetails.address.line2 } : {},
205
- ...billingDetails.address.state ? { state: billingDetails.address.state } : {}
206
- }
207
- } : {}
208
- }
209
- } : {};
207
+ const stripeBilling = billingDetails ? toStripeBillingDetails(billingDetails) : {};
210
208
  const { error, paymentMethod } = cardNumberEl ? await this.stripe.createPaymentMethod({
211
209
  type: "card",
212
210
  card: cardNumberEl,
@@ -326,22 +324,7 @@ var StripeAdapter = class {
326
324
  );
327
325
  }
328
326
  const billing = params.billingDetails;
329
- const paymentMethodData = billing ? {
330
- billing_details: {
331
- ...billing.email ? { email: billing.email } : {},
332
- ...billing.name ? { name: billing.name } : {},
333
- ...billing.address ? {
334
- address: {
335
- ...billing.address.country ? { country: billing.address.country } : {},
336
- ...billing.address.postal_code ? { postal_code: billing.address.postal_code } : {},
337
- ...billing.address.city ? { city: billing.address.city } : {},
338
- ...billing.address.line1 ? { line1: billing.address.line1 } : {},
339
- ...billing.address.line2 ? { line2: billing.address.line2 } : {},
340
- ...billing.address.state ? { state: billing.address.state } : {}
341
- }
342
- } : {}
343
- }
344
- } : void 0;
327
+ const paymentMethodData = billing ? toStripeBillingDetails(billing) : void 0;
345
328
  const { error, paymentIntent } = await this.stripe.confirmPayment({
346
329
  elements: this.elements,
347
330
  clientSecret: params.clientSecret,
@@ -576,6 +559,19 @@ import {
576
559
  resolveSessionCurrency
577
560
  } from "@flopay/shared";
578
561
 
562
+ // src/api-error.ts
563
+ function readErrorString(value) {
564
+ return typeof value === "string" && value.trim() ? value : void 0;
565
+ }
566
+ function readErrorMessage(value) {
567
+ if (typeof value === "string") return value.trim() ? value : void 0;
568
+ if (Array.isArray(value)) {
569
+ const joined = value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).join("; ");
570
+ return joined || void 0;
571
+ }
572
+ return void 0;
573
+ }
574
+
579
575
  // src/session-display-cache.ts
580
576
  var STORAGE_KEY_PREFIX = "flopay_session_display:";
581
577
  var DEFAULT_TTL_MS = 60 * 60 * 1e3;
@@ -1006,17 +1002,10 @@ function telemetryFailure(error, fallbackCode) {
1006
1002
  };
1007
1003
  }
1008
1004
  function readString(payload, key) {
1009
- const value = payload?.[key];
1010
- return typeof value === "string" && value.trim() ? value : void 0;
1005
+ return readErrorString(payload?.[key]);
1011
1006
  }
1012
1007
  function readMessage(payload, key) {
1013
- const value = payload?.[key];
1014
- if (typeof value === "string") return value.trim() ? value : void 0;
1015
- if (Array.isArray(value)) {
1016
- const joined = value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).join("; ");
1017
- return joined || void 0;
1018
- }
1019
- return void 0;
1008
+ return readErrorMessage(payload?.[key]);
1020
1009
  }
1021
1010
  function readNumber(payload, key) {
1022
1011
  const value = payload?.[key];
@@ -2855,8 +2844,6 @@ function telemetryProvider(name) {
2855
2844
  if (name === "stripe" || name === "paypal" || name === "pcivault") return name;
2856
2845
  return "other";
2857
2846
  }
2858
- var CARD_THREE_DS_ATTEMPT_TTL_MS2 = 15 * 6e4;
2859
- var MAX_CARD_THREE_DS_ATTEMPTS2 = 32;
2860
2847
  var FloPay = class {
2861
2848
  constructor(provider, config, telemetryReporter) {
2862
2849
  this.currentElements = null;
@@ -3038,7 +3025,7 @@ var FloPay = class {
3038
3025
  trackCardThreeDsAttempt(attemptId, startedAt) {
3039
3026
  this.pruneExpiredCardThreeDsAttempts(startedAt);
3040
3027
  if (this.cardThreeDsStartedAt.has(attemptId)) return false;
3041
- while (this.cardThreeDsStartedAt.size >= MAX_CARD_THREE_DS_ATTEMPTS2) {
3028
+ while (this.cardThreeDsStartedAt.size >= MAX_CARD_THREE_DS_ATTEMPTS) {
3042
3029
  const oldestAttemptId = this.cardThreeDsStartedAt.keys().next().value;
3043
3030
  if (oldestAttemptId === void 0) break;
3044
3031
  this.cardThreeDsStartedAt.delete(oldestAttemptId);
@@ -3054,7 +3041,7 @@ var FloPay = class {
3054
3041
  }
3055
3042
  pruneExpiredCardThreeDsAttempts(now) {
3056
3043
  for (const [attemptId, startedAt] of this.cardThreeDsStartedAt) {
3057
- if (now - startedAt >= CARD_THREE_DS_ATTEMPT_TTL_MS2) {
3044
+ if (now - startedAt >= CARD_THREE_DS_ATTEMPT_TTL_MS) {
3058
3045
  this.cardThreeDsStartedAt.delete(attemptId);
3059
3046
  }
3060
3047
  }
@@ -3494,20 +3481,21 @@ function instanceCacheKey(publishableKey, options) {
3494
3481
  stableCacheValue(options?.appearance ?? null)
3495
3482
  ]);
3496
3483
  }
3484
+ var initializationCache = /* @__PURE__ */ new Map();
3497
3485
  async function loadFloPay(publishableKey, options) {
3498
3486
  if (!publishableKey) {
3499
- const reporter2 = new TelemetryReporter({
3487
+ const reporter = new TelemetryReporter({
3500
3488
  billingApiUrl: resolveBillingApiUrl3(options?.billingApiUrl),
3501
3489
  sdkVersion: SDK_VERSION4,
3502
3490
  enabled: options?.telemetry !== false
3503
3491
  });
3504
- reporter2.error({
3492
+ reporter.error({
3505
3493
  errorCode: "CONFIGURATION_INVALID",
3506
3494
  stage: "sdk_initialize",
3507
3495
  paymentMethodCategory: "unknown"
3508
3496
  });
3509
- void reporter2.flush().catch(() => {
3510
- }).finally(() => reporter2.destroy());
3497
+ void reporter.flush().catch(() => {
3498
+ }).finally(() => reporter.destroy());
3511
3499
  throw new FloPayError6(
3512
3500
  "A publishable key is required to initialize FloPay.",
3513
3501
  "validation_error",
@@ -3525,59 +3513,71 @@ async function loadFloPay(publishableKey, options) {
3525
3513
  }
3526
3514
  return cached;
3527
3515
  }
3516
+ const initializing = initializationCache.get(cacheKey);
3517
+ if (initializing) return initializing;
3528
3518
  const config = {
3529
- publishableKey,
3530
- ...options
3519
+ ...options,
3520
+ publishableKey
3531
3521
  };
3532
- const reporter = new TelemetryReporter({
3533
- billingApiUrl: resolveBillingApiUrl3(config.billingApiUrl),
3534
- sdkVersion: SDK_VERSION4,
3535
- enabled: config.telemetry !== false
3536
- });
3537
- const initializationStarted = reporter.now();
3538
- reporter.log({ name: "sdk.initialize.started", stage: "sdk_initialize" });
3539
- reporter.log({ name: "sdk.cache.miss", stage: "sdk_initialize" });
3540
- reporter.log({
3541
- name: "provider.load.started",
3542
- stage: "provider_load",
3543
- provider: "stripe"
3544
- });
3545
- const adapter = new StripeAdapter();
3546
- try {
3547
- await adapter.initialize(config);
3548
- } catch (error) {
3549
- reporter.error({
3550
- errorCode: "SDK_INITIALIZATION_FAILED",
3522
+ const initialization = (async () => {
3523
+ const reporter = new TelemetryReporter({
3524
+ billingApiUrl: resolveBillingApiUrl3(config.billingApiUrl),
3525
+ sdkVersion: SDK_VERSION4,
3526
+ enabled: config.telemetry !== false
3527
+ });
3528
+ const initializationStarted = reporter.now();
3529
+ reporter.log({ name: "sdk.initialize.started", stage: "sdk_initialize" });
3530
+ reporter.log({ name: "sdk.cache.miss", stage: "sdk_initialize" });
3531
+ reporter.log({
3532
+ name: "provider.load.started",
3533
+ stage: "provider_load",
3534
+ provider: "stripe"
3535
+ });
3536
+ const adapter = new StripeAdapter();
3537
+ try {
3538
+ await adapter.initialize(config);
3539
+ } catch (error) {
3540
+ reporter.error({
3541
+ errorCode: "SDK_INITIALIZATION_FAILED",
3542
+ stage: "sdk_initialize",
3543
+ provider: "stripe",
3544
+ paymentMethodCategory: "unknown"
3545
+ });
3546
+ reporter.destroy();
3547
+ throw error;
3548
+ }
3549
+ reporter.log({ name: "provider.ready", stage: "provider_ready", provider: "stripe" });
3550
+ reporter.log({
3551
+ name: "provider.availability.checked",
3552
+ stage: "provider_ready",
3553
+ provider: "stripe"
3554
+ });
3555
+ reporter.log({ name: "sdk.initialize.ready", stage: "sdk_initialize" });
3556
+ const initializationDuration = reporter.now() - initializationStarted;
3557
+ reporter.performance({
3551
3558
  stage: "sdk_initialize",
3552
- provider: "stripe",
3553
- paymentMethodCategory: "unknown"
3559
+ durationMs: initializationDuration,
3560
+ durationMode: "machine",
3561
+ provider: "stripe"
3554
3562
  });
3555
- reporter.destroy();
3556
- throw error;
3563
+ reporter.performance({
3564
+ stage: "provider_ready",
3565
+ durationMs: initializationDuration,
3566
+ durationMode: "machine",
3567
+ provider: "stripe"
3568
+ });
3569
+ const instance = createInstrumentedFloPay(adapter, config, reporter);
3570
+ instanceCache.set(cacheKey, instance);
3571
+ return instance;
3572
+ })();
3573
+ initializationCache.set(cacheKey, initialization);
3574
+ try {
3575
+ return await initialization;
3576
+ } finally {
3577
+ if (initializationCache.get(cacheKey) === initialization) {
3578
+ initializationCache.delete(cacheKey);
3579
+ }
3557
3580
  }
3558
- reporter.log({ name: "provider.ready", stage: "provider_ready", provider: "stripe" });
3559
- reporter.log({
3560
- name: "provider.availability.checked",
3561
- stage: "provider_ready",
3562
- provider: "stripe"
3563
- });
3564
- reporter.log({ name: "sdk.initialize.ready", stage: "sdk_initialize" });
3565
- const initializationDuration = reporter.now() - initializationStarted;
3566
- reporter.performance({
3567
- stage: "sdk_initialize",
3568
- durationMs: initializationDuration,
3569
- durationMode: "machine",
3570
- provider: "stripe"
3571
- });
3572
- reporter.performance({
3573
- stage: "provider_ready",
3574
- durationMs: initializationDuration,
3575
- durationMode: "machine",
3576
- provider: "stripe"
3577
- });
3578
- const instance = createInstrumentedFloPay(adapter, config, reporter);
3579
- instanceCache.set(cacheKey, instance);
3580
- return instance;
3581
3581
  }
3582
3582
 
3583
3583
  // src/create-checkout-session.ts
@@ -3592,13 +3592,10 @@ import {
3592
3592
  resolveSessionCurrency as resolveSessionCurrency2
3593
3593
  } from "@flopay/shared";
3594
3594
  var MAX_COUPON_CODES = 5;
3595
- function readString2(value) {
3596
- return typeof value === "string" && value.trim() ? value : void 0;
3597
- }
3598
3595
  function buildCheckoutSessionError(status, payload) {
3599
3596
  const nested = payload?.error;
3600
- const code = readString2(payload?.code) ?? readString2(nested?.code) ?? `http_${status}`;
3601
- const message = readString2(payload?.message) ?? readString2(nested?.message) ?? defaultMessageForCode(code, status);
3597
+ const code = readErrorString(payload?.code) ?? readErrorString(nested?.code) ?? `http_${status}`;
3598
+ const message = readErrorString(payload?.message) ?? readErrorString(nested?.message) ?? defaultMessageForCode(code, status);
3602
3599
  return new FloPayError7(message, "api_error", { code, statusCode: status });
3603
3600
  }
3604
3601
  function defaultMessageForCode(code, status) {