@flopay/react 1.2.7 → 1.2.8

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.d.cts CHANGED
@@ -3,7 +3,6 @@ import { FloPay, FloPayElements } from '@flopay/js';
3
3
  import * as _flopay_shared from '@flopay/shared';
4
4
  import { FloPayAppearance, InlineSessionDraft, FloPayError, PaymentResult, DeclineEvent, CheckoutButtonMethod, BeforeButtonClickEvent, InlineSessionPatch, CheckoutMode, CheckoutSession, ElementOptions, ElementChangeEvent, TokenizedBody, AVSFieldConfig, GatewayEnvironment, CheckoutProduct, CheckoutItem, CheckoutSubscription, ThemeId, ButtonsLayoutTheme, ButtonsLayoutStyles } from '@flopay/shared';
5
5
  export { BeforeButtonClickEvent, CheckoutButtonMethod, DeclineEvent, InlineSessionDraft, InlineSessionPatch } from '@flopay/shared';
6
- import * as react_jsx_runtime from 'react/jsx-runtime';
7
6
 
8
7
  /** Props for the `FloPayProvider` component. */
9
8
  interface FloPayProviderProps {
@@ -867,6 +866,6 @@ interface FloPayAutomaticPaymentButtonProps extends Omit<React.ButtonHTMLAttribu
867
866
  onDecline?: (decline: DeclineEvent) => void;
868
867
  children?: React.ReactNode;
869
868
  }
870
- declare function FloPayAutomaticPaymentButton({ sessionId, nonce, createSession, paymentMethodId: _deprecatedPaymentMethodId, checkoutMethod: _deprecatedCheckoutMethod, clientId, products, items, subscriptions, account, successUrl, cancelUrl, couponCodes, tagsData, utmMetadata, billingApiUrl, locale, theme, buttonsTheme, buttonsStyles: stylesOverride, onSuccess, onError, onDecline, children, disabled, type, style, ...buttonProps }: FloPayAutomaticPaymentButtonProps): react_jsx_runtime.JSX.Element;
869
+ declare function FloPayAutomaticPaymentButton({ sessionId, nonce, createSession, paymentMethodId: _deprecatedPaymentMethodId, checkoutMethod: _deprecatedCheckoutMethod, clientId, products, items, subscriptions, account, successUrl, cancelUrl, couponCodes, tagsData, utmMetadata, billingApiUrl, locale, theme, buttonsTheme, buttonsStyles: stylesOverride, onSuccess, onError, onDecline, children, disabled, type, style, ...buttonProps }: FloPayAutomaticPaymentButtonProps): React.JSX.Element;
871
870
 
872
871
  export { AddressElement, CardCvcElement, CardElement, CardExpiryElement, CardNumberElement, CheckoutForm, type CheckoutFormProps, type CheckoutFormRef, type CheckoutState, DirectPayPalButton, type DirectPayPalButtonProps, type ElementComponentProps, FloPayAutomaticPaymentButton, type FloPayAutomaticPaymentButtonProps, type FloPayAutomaticPaymentSuccessEvent, FloPayCheckout, type FloPayCheckoutProps, FloPayProvider, type FloPayProviderProps, PayPalButton, type PayPalButtonProps, PaymentElement, SplitCardForm, type SplitCardFormProps, type SplitCardFormRef, useCheckout, useElements, useFloPay, usePayPalFloPay };
package/dist/index.d.ts CHANGED
@@ -3,7 +3,6 @@ import { FloPay, FloPayElements } from '@flopay/js';
3
3
  import * as _flopay_shared from '@flopay/shared';
4
4
  import { FloPayAppearance, InlineSessionDraft, FloPayError, PaymentResult, DeclineEvent, CheckoutButtonMethod, BeforeButtonClickEvent, InlineSessionPatch, CheckoutMode, CheckoutSession, ElementOptions, ElementChangeEvent, TokenizedBody, AVSFieldConfig, GatewayEnvironment, CheckoutProduct, CheckoutItem, CheckoutSubscription, ThemeId, ButtonsLayoutTheme, ButtonsLayoutStyles } from '@flopay/shared';
5
5
  export { BeforeButtonClickEvent, CheckoutButtonMethod, DeclineEvent, InlineSessionDraft, InlineSessionPatch } from '@flopay/shared';
6
- import * as react_jsx_runtime from 'react/jsx-runtime';
7
6
 
8
7
  /** Props for the `FloPayProvider` component. */
9
8
  interface FloPayProviderProps {
@@ -867,6 +866,6 @@ interface FloPayAutomaticPaymentButtonProps extends Omit<React.ButtonHTMLAttribu
867
866
  onDecline?: (decline: DeclineEvent) => void;
868
867
  children?: React.ReactNode;
869
868
  }
870
- declare function FloPayAutomaticPaymentButton({ sessionId, nonce, createSession, paymentMethodId: _deprecatedPaymentMethodId, checkoutMethod: _deprecatedCheckoutMethod, clientId, products, items, subscriptions, account, successUrl, cancelUrl, couponCodes, tagsData, utmMetadata, billingApiUrl, locale, theme, buttonsTheme, buttonsStyles: stylesOverride, onSuccess, onError, onDecline, children, disabled, type, style, ...buttonProps }: FloPayAutomaticPaymentButtonProps): react_jsx_runtime.JSX.Element;
869
+ declare function FloPayAutomaticPaymentButton({ sessionId, nonce, createSession, paymentMethodId: _deprecatedPaymentMethodId, checkoutMethod: _deprecatedCheckoutMethod, clientId, products, items, subscriptions, account, successUrl, cancelUrl, couponCodes, tagsData, utmMetadata, billingApiUrl, locale, theme, buttonsTheme, buttonsStyles: stylesOverride, onSuccess, onError, onDecline, children, disabled, type, style, ...buttonProps }: FloPayAutomaticPaymentButtonProps): React.JSX.Element;
871
870
 
872
871
  export { AddressElement, CardCvcElement, CardElement, CardExpiryElement, CardNumberElement, CheckoutForm, type CheckoutFormProps, type CheckoutFormRef, type CheckoutState, DirectPayPalButton, type DirectPayPalButtonProps, type ElementComponentProps, FloPayAutomaticPaymentButton, type FloPayAutomaticPaymentButtonProps, type FloPayAutomaticPaymentSuccessEvent, FloPayCheckout, type FloPayCheckoutProps, FloPayProvider, type FloPayProviderProps, PayPalButton, type PayPalButtonProps, PaymentElement, SplitCardForm, type SplitCardFormProps, type SplitCardFormRef, useCheckout, useElements, useFloPay, usePayPalFloPay };
package/dist/index.mjs CHANGED
@@ -436,7 +436,7 @@ function ProcessingOverlay({
436
436
  }
437
437
 
438
438
  // src/checkout-utils.ts
439
- import { FloPayError } from "@flopay/shared";
439
+ import { FloPayError, isSetupIntentClientSecret } from "@flopay/shared";
440
440
  function mergeInlineSessionPatches(base, patch) {
441
441
  if (!patch) return base;
442
442
  if (!base) return patch;
@@ -605,6 +605,18 @@ function resolvePaymentIntentPaymentMethodId(paymentIntent) {
605
605
  }
606
606
  async function retrievePaymentIntentFromProvider(provider, clientSecret) {
607
607
  const retriever = provider;
608
+ if (isSetupIntentClientSecret(clientSecret)) {
609
+ if (!retriever?.retrieveSetupIntent) {
610
+ return null;
611
+ }
612
+ const { setupIntent, error: error2 } = await retriever.retrieveSetupIntent(clientSecret);
613
+ if (error2) {
614
+ throw new FloPayError(error2.message ?? "Failed to retrieve setup intent.", "api_error", {
615
+ ...error2.code ? { code: error2.code } : {}
616
+ });
617
+ }
618
+ return setupIntent ?? null;
619
+ }
608
620
  if (!retriever?.retrievePaymentIntent) {
609
621
  return null;
610
622
  }
@@ -616,6 +628,12 @@ async function retrievePaymentIntentFromProvider(provider, clientSecret) {
616
628
  }
617
629
  return paymentIntent ?? null;
618
630
  }
631
+ function resolveWalletElementsMode(amountInMinorUnits) {
632
+ if (typeof amountInMinorUnits !== "number" || !Number.isFinite(amountInMinorUnits) || amountInMinorUnits <= 0) {
633
+ return { mode: "setup" };
634
+ }
635
+ return { mode: "payment", amount: amountInMinorUnits };
636
+ }
619
637
  function resolveTokenizedPaymentMethodId(tokenizedBody) {
620
638
  const candidates = [
621
639
  tokenizedBody?.id,
@@ -1240,7 +1258,7 @@ ${debugLines.join("\n")}`
1240
1258
  }
1241
1259
 
1242
1260
  // src/split-card-form.tsx
1243
- import { FloPayError as FloPayError3, resolveTheme } from "@flopay/shared";
1261
+ import { FloPayError as FloPayError3, isSetupIntentClientSecret as isSetupIntentClientSecret2, resolveTheme } from "@flopay/shared";
1244
1262
  import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1245
1263
  var STRIPE_RESUME_KEY = "flopay_stripe_resume";
1246
1264
  var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
@@ -1733,10 +1751,7 @@ function WalletButtonInner({
1733
1751
  const intentJson = await intentResponse.json();
1734
1752
  const intentClientSecret = intentJson.data?.id;
1735
1753
  if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
1736
- const { error: confirmError, paymentIntent } = await stripe.confirmCardPayment(
1737
- intentClientSecret,
1738
- { payment_method: paymentMethod.id }
1739
- );
1754
+ const { error: confirmError, intentId } = isSetupIntentClientSecret2(intentClientSecret) ? await stripe.confirmCardSetup(intentClientSecret, { payment_method: paymentMethod.id }).then((r) => ({ error: r.error, intentId: r.setupIntent?.id })) : await stripe.confirmCardPayment(intentClientSecret, { payment_method: paymentMethod.id }).then((r) => ({ error: r.error, intentId: r.paymentIntent?.id }));
1740
1755
  if (confirmError) {
1741
1756
  const message = confirmError.message ?? "Wallet payment failed.";
1742
1757
  onErrorChange?.(message);
@@ -1748,7 +1763,7 @@ function WalletButtonInner({
1748
1763
  onTokenizedBody({
1749
1764
  id: paymentMethod.id,
1750
1765
  type: "card",
1751
- threeDSecureActionResultTokenId: paymentIntent?.id
1766
+ threeDSecureActionResultTokenId: intentId
1752
1767
  }, {
1753
1768
  accountPatch: prepared?.accountPatch,
1754
1769
  sessionId: effectiveSessionId,
@@ -2611,14 +2626,17 @@ function SplitCardFormInner({
2611
2626
  }
2612
2627
  };
2613
2628
  }, [appearance]);
2614
- const walletOptions = useMemo3(() => ({
2615
- mode: "payment",
2616
- amount: amountInCents,
2617
- currency: currency.toLowerCase(),
2618
- paymentMethodCreation: "manual",
2619
- captureMethod: "manual",
2620
- ...stripeAppearanceProp
2621
- }), [amountInCents, currency, stripeAppearanceProp]);
2629
+ const walletOptions = useMemo3(() => {
2630
+ const modeOptions = resolveWalletElementsMode(totalAmount);
2631
+ return {
2632
+ ...modeOptions,
2633
+ currency: currency.toLowerCase(),
2634
+ paymentMethodCreation: "manual",
2635
+ // captureMethod only applies to payment mode; Stripe rejects it in setup.
2636
+ ...modeOptions.mode === "payment" ? { captureMethod: "manual" } : {},
2637
+ ...stripeAppearanceProp
2638
+ };
2639
+ }, [totalAmount, currency, stripeAppearanceProp]);
2622
2640
  const paypalOptions = useMemo3(() => ({
2623
2641
  mode: "payment",
2624
2642
  amount: amountInCents,
@@ -4288,8 +4306,28 @@ function SplitCardFormInner({
4288
4306
 
4289
4307
  // src/saved-payment-flow.ts
4290
4308
  import { loadFloPay, PaymentAPI as PaymentAPI3 } from "@flopay/js";
4291
- import { FloPayError as FloPayError4 } from "@flopay/shared";
4309
+ import { FloPayError as FloPayError4, isSetupIntentClientSecret as isSetupIntentClientSecret3 } from "@flopay/shared";
4292
4310
  var DEFAULT_SAVED_PAYMENT_DECLINE_METHOD = "card";
4311
+ async function retrieveResumeIntent(stripe, clientSecret, isSetupIntent) {
4312
+ if (isSetupIntent) {
4313
+ if (typeof stripe.retrieveSetupIntent !== "function") return null;
4314
+ const { setupIntent, error: error2 } = await stripe.retrieveSetupIntent(clientSecret);
4315
+ return { error: error2, intent: setupIntent ?? null };
4316
+ }
4317
+ if (typeof stripe.retrievePaymentIntent !== "function") return null;
4318
+ const { paymentIntent, error } = await stripe.retrievePaymentIntent(clientSecret);
4319
+ return { error, intent: paymentIntent ?? null };
4320
+ }
4321
+ async function confirmResumeIntent(stripe, clientSecret, data, isSetupIntent) {
4322
+ if (isSetupIntent) {
4323
+ if (typeof stripe.confirmCardSetup !== "function") return null;
4324
+ const { setupIntent, error: error2 } = await stripe.confirmCardSetup(clientSecret, data);
4325
+ return { error: error2, intent: setupIntent ?? null };
4326
+ }
4327
+ if (typeof stripe.confirmCardPayment !== "function") return null;
4328
+ const { paymentIntent, error } = await stripe.confirmCardPayment(clientSecret, data);
4329
+ return { error, intent: paymentIntent ?? null };
4330
+ }
4293
4331
  function getRedirectResultFromCheckoutProcessError(error) {
4294
4332
  if (!error?.type || !error.threeDSecureToken) {
4295
4333
  return null;
@@ -4525,64 +4563,66 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
4525
4563
  { checkoutMethod: "card" }
4526
4564
  );
4527
4565
  }
4566
+ const isSetupIntent = isSetupIntentClientSecret3(redirectResult.threeDSecureToken);
4528
4567
  let paymentIntent = null;
4529
4568
  let savedPaymentMethodId = redirectResult.paymentMethodId;
4530
- if (typeof stripe.retrievePaymentIntent === "function") {
4531
- const { paymentIntent: existingPaymentIntent, error: retrieveError } = await stripe.retrievePaymentIntent(
4532
- redirectResult.threeDSecureToken
4533
- );
4534
- if (retrieveError) {
4569
+ const retrieved = await retrieveResumeIntent(stripe, redirectResult.threeDSecureToken, isSetupIntent);
4570
+ if (retrieved) {
4571
+ if (retrieved.error) {
4535
4572
  throw Object.assign(
4536
4573
  new FloPayError4(
4537
- retrieveError.message ?? "Failed to retrieve 3DS payment status.",
4574
+ retrieved.error.message ?? `Failed to retrieve 3DS ${isSetupIntent ? "setup" : "payment"} status.`,
4538
4575
  "api_error",
4539
- { code: retrieveError.code }
4576
+ { code: retrieved.error.code }
4540
4577
  ),
4541
4578
  { checkoutMethod: "card" }
4542
4579
  );
4543
4580
  }
4544
- if (!savedPaymentMethodId && existingPaymentIntent?.payment_method) {
4545
- if (typeof existingPaymentIntent.payment_method === "string") {
4546
- savedPaymentMethodId = existingPaymentIntent.payment_method;
4547
- } else if ("id" in existingPaymentIntent.payment_method) {
4548
- savedPaymentMethodId = existingPaymentIntent.payment_method.id;
4581
+ const existingIntent = retrieved.intent;
4582
+ if (!savedPaymentMethodId && existingIntent?.payment_method) {
4583
+ if (typeof existingIntent.payment_method === "string") {
4584
+ savedPaymentMethodId = existingIntent.payment_method;
4585
+ } else if ("id" in existingIntent.payment_method) {
4586
+ savedPaymentMethodId = existingIntent.payment_method.id ?? void 0;
4549
4587
  }
4550
4588
  }
4551
4589
  }
4552
- if (savedPaymentMethodId && typeof stripe.confirmCardPayment === "function") {
4553
- const { error: confirmError, paymentIntent: confirmedPaymentIntent } = await stripe.confirmCardPayment(
4554
- redirectResult.threeDSecureToken,
4555
- {
4556
- payment_method: savedPaymentMethodId,
4557
- return_url: returnUrl ?? resolveSavedPaymentReturnUrl(session) ?? window.location.href
4558
- }
4559
- );
4560
- if (confirmError) {
4590
+ const confirmed = savedPaymentMethodId ? await confirmResumeIntent(
4591
+ stripe,
4592
+ redirectResult.threeDSecureToken,
4593
+ {
4594
+ payment_method: savedPaymentMethodId,
4595
+ return_url: returnUrl ?? resolveSavedPaymentReturnUrl(session) ?? window.location.href
4596
+ },
4597
+ isSetupIntent
4598
+ ) : null;
4599
+ if (confirmed) {
4600
+ if (confirmed.error) {
4561
4601
  throw Object.assign(
4562
4602
  new FloPayError4(
4563
- confirmError.message ?? "3DS authentication failed.",
4603
+ confirmed.error.message ?? "3DS authentication failed.",
4564
4604
  "api_error",
4565
- { code: confirmError.code }
4605
+ { code: confirmed.error.code }
4566
4606
  ),
4567
4607
  { checkoutMethod: "card" }
4568
4608
  );
4569
4609
  }
4570
- paymentIntent = confirmedPaymentIntent ?? null;
4610
+ paymentIntent = confirmed.intent;
4571
4611
  } else {
4572
- const { error: nextActionError, paymentIntent: nextActionPaymentIntent } = await stripe.handleNextAction({
4612
+ const nextAction = await stripe.handleNextAction({
4573
4613
  clientSecret: redirectResult.threeDSecureToken
4574
4614
  });
4575
- if (nextActionError) {
4615
+ if (nextAction.error) {
4576
4616
  throw Object.assign(
4577
4617
  new FloPayError4(
4578
- nextActionError.message ?? "3DS authentication failed.",
4618
+ nextAction.error.message ?? "3DS authentication failed.",
4579
4619
  "api_error",
4580
- { code: nextActionError.code }
4620
+ { code: nextAction.error.code }
4581
4621
  ),
4582
4622
  { checkoutMethod: "card" }
4583
4623
  );
4584
4624
  }
4585
- paymentIntent = nextActionPaymentIntent ?? null;
4625
+ paymentIntent = (isSetupIntent ? nextAction.setupIntent : nextAction.paymentIntent) ?? null;
4586
4626
  }
4587
4627
  if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) {
4588
4628
  const followUp = await processSavedPaymentForMode({