@flopay/react 0.4.8 → 0.4.10

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.cjs CHANGED
@@ -126,13 +126,22 @@ function FloPayProvider({
126
126
  clientSecret: options?.clientSecret,
127
127
  amount: options?.amount,
128
128
  currency: options?.currency,
129
- paymentMethodCreation: options?.paymentMethodCreation
129
+ paymentMethodCreation: options?.paymentMethodCreation,
130
+ setupFutureUsage: options?.setupFutureUsage
130
131
  });
131
132
  setElements(els);
132
133
  return () => {
133
134
  els.destroy();
134
135
  };
135
- }, [flopay, options?.appearance, options?.clientSecret, options?.amount, options?.currency]);
136
+ }, [
137
+ flopay,
138
+ options?.appearance,
139
+ options?.clientSecret,
140
+ options?.amount,
141
+ options?.currency,
142
+ options?.paymentMethodCreation,
143
+ options?.setupFutureUsage
144
+ ]);
136
145
  const resolvedBillingApiUrl = (0, import_shared.resolveBillingApiUrl)(options?.billingApiUrl);
137
146
  const value = (0, import_react2.useMemo)(
138
147
  () => ({ flopay, paypalFlopay, elements, billingApiUrl: resolvedBillingApiUrl }),
@@ -550,6 +559,15 @@ function buildDeclineEvent(method, input, overrides) {
550
559
  ...declineCode ? { declineCode } : {}
551
560
  };
552
561
  }
562
+ function mapPayPalIntentStatusToPaymentResult(status) {
563
+ if (status === "succeeded") {
564
+ return "succeeded";
565
+ }
566
+ if (status === "processing" || status === "requires_capture") {
567
+ return "processing";
568
+ }
569
+ return "failed";
570
+ }
553
571
  function resolveTokenizedPaymentMethodId(tokenizedBody) {
554
572
  const candidates = [
555
573
  tokenizedBody?.id,
@@ -777,7 +795,10 @@ function PayPalButtonInner({
777
795
  const createPM = stripe.createPaymentMethod;
778
796
  const { error: pmError, paymentMethod } = await createPM({ type: "paypal" });
779
797
  if (pmError) {
780
- console.warn("[FloPay] Could not create PayPal PM upfront:", pmError.message);
798
+ const message = pmError.message ?? "PayPal payment failed.";
799
+ onErrorChange?.(message);
800
+ event.paymentFailed({ reason: "fail", message });
801
+ return;
781
802
  }
782
803
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
783
804
  method: "POST",
@@ -786,7 +807,9 @@ function PayPalButtonInner({
786
807
  sessionId: effectiveSessionId,
787
808
  email: effectiveEmail,
788
809
  paymentMethodType: paymentMethod?.id ?? "paypal",
789
- isPaypal: "true"
810
+ isPaypal: "true",
811
+ setupFutureUsage: "off_session",
812
+ setup_future_usage: "off_session"
790
813
  })
791
814
  });
792
815
  if (!intentResponse.ok) throw new Error("Failed to create payment intent");
@@ -822,6 +845,7 @@ function PayPalButtonInner({
822
845
  accountPatch: prepared?.accountPatch,
823
846
  sessionId: effectiveSessionId
824
847
  });
848
+ return;
825
849
  } catch (err) {
826
850
  onErrorChange?.(err instanceof Error ? err.message : "PayPal payment failed. Please try again.");
827
851
  } finally {
@@ -842,6 +866,9 @@ function PayPalButtonInner({
842
866
  },
843
867
  options: {
844
868
  buttonType: { paypal: "paypal" },
869
+ billingAddressRequired: false,
870
+ phoneNumberRequired: false,
871
+ shippingAddressRequired: false,
845
872
  paymentMethods: {
846
873
  applePay: "never",
847
874
  googlePay: "never",
@@ -1121,7 +1148,8 @@ function SplitCardFormInner({
1121
1148
  mode: "payment",
1122
1149
  amount: amountInCents,
1123
1150
  currency: currency.toLowerCase(),
1124
- captureMethod: "manual"
1151
+ captureMethod: "manual",
1152
+ setupFutureUsage: "off_session"
1125
1153
  }), [amountInCents, currency]);
1126
1154
  const updateError = (0, import_react7.useCallback)(
1127
1155
  (err) => {
@@ -1206,6 +1234,8 @@ function SplitCardFormInner({
1206
1234
  updateError(null);
1207
1235
  const effectiveSessionId = overrides?.sessionId ?? sessionId;
1208
1236
  const effectiveAccount = mergeAccountPatch(resolvedAccount, overrides?.accountPatch);
1237
+ const resolvedCompletionPaymentMethodId = overrides?.completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
1238
+ const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
1209
1239
  try {
1210
1240
  const response = await fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
1211
1241
  method: "POST",
@@ -1215,7 +1245,7 @@ function SplitCardFormInner({
1215
1245
  },
1216
1246
  body: JSON.stringify({
1217
1247
  sessionId: effectiveSessionId,
1218
- tokenizedData: tokenizedBody,
1248
+ tokenizedData: requestTokenizedBody,
1219
1249
  accountData: {
1220
1250
  userId: effectiveAccount.userId ?? "",
1221
1251
  email: effectiveAccount.email ?? "",
@@ -1232,7 +1262,8 @@ function SplitCardFormInner({
1232
1262
  onComplete?.({
1233
1263
  status: "succeeded",
1234
1264
  paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
1235
- paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
1265
+ paymentMethodId: resolvedCompletionPaymentMethodId,
1266
+ checkoutMethod: tokenizedBody.isPaypal ? "paypal" : "card"
1236
1267
  });
1237
1268
  return;
1238
1269
  }
@@ -1275,8 +1306,9 @@ function SplitCardFormInner({
1275
1306
  await processPaymentInternal({
1276
1307
  id: result.paymentIntentId,
1277
1308
  type: "card",
1278
- threeDSecureActionResultTokenId: result.paymentIntentId,
1279
- originalPaymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
1309
+ threeDSecureActionResultTokenId: result.paymentIntentId
1310
+ }, {
1311
+ completionPaymentMethodId: resolvedCompletionPaymentMethodId
1280
1312
  });
1281
1313
  }
1282
1314
  } finally {
@@ -2101,7 +2133,8 @@ async function processSavedPaymentForMode({
2101
2133
  result: {
2102
2134
  status: "succeeded",
2103
2135
  paymentIntentId: tokenizedData?.threeDSecureActionResultTokenId,
2104
- paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedData)
2136
+ paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedData),
2137
+ checkoutMethod: tokenizedData ? tokenizedData.isPaypal ? "paypal" : "card" : void 0
2105
2138
  }
2106
2139
  };
2107
2140
  }
@@ -2204,7 +2237,7 @@ async function processSavedPaymentWithIntent({
2204
2237
  if (!confirmResult.paymentIntentId || confirmResult.status !== "succeeded" && confirmResult.status !== "processing" && confirmResult.status !== "requires_capture") {
2205
2238
  throw Object.assign(
2206
2239
  new import_shared6.FloPayError(
2207
- `Payment intent in unexpected status: ${confirmResult.status}`,
2240
+ `Unfortunately, your payment could not be processed. Please try again using a different payment method or contact your bank for assistance. If the issue persists, feel free to reach out to us for support.`,
2208
2241
  "api_error",
2209
2242
  {
2210
2243
  code: confirmResult.status === "requires_action" ? "authentication_required" : void 0
@@ -2220,8 +2253,7 @@ async function processSavedPaymentWithIntent({
2220
2253
  tokenizedData: {
2221
2254
  id: paymentMethodId,
2222
2255
  type: "card",
2223
- threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
2224
- originalPaymentMethodId: paymentMethodId
2256
+ threeDSecureActionResultTokenId: confirmResult.paymentIntentId
2225
2257
  },
2226
2258
  returnUrl
2227
2259
  });
@@ -2237,7 +2269,8 @@ async function processSavedPaymentWithIntent({
2237
2269
  return {
2238
2270
  ...finalResult,
2239
2271
  paymentIntentId: finalResult.paymentIntentId ?? confirmResult.paymentIntentId,
2240
- paymentMethodId: finalResult.paymentMethodId ?? paymentMethodId
2272
+ paymentMethodId: finalResult.paymentMethodId ?? paymentMethodId,
2273
+ checkoutMethod: finalResult.checkoutMethod ?? "card"
2241
2274
  };
2242
2275
  }
2243
2276
  async function handleSavedPaymentRedirectResult(redirectResult, {
@@ -2331,8 +2364,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2331
2364
  tokenizedData: {
2332
2365
  id: paymentIntent.id,
2333
2366
  type: "card",
2334
- threeDSecureActionResultTokenId: paymentIntent.id,
2335
- originalPaymentMethodId: savedPaymentMethodId
2367
+ threeDSecureActionResultTokenId: paymentIntent.id
2336
2368
  },
2337
2369
  returnUrl
2338
2370
  });
@@ -2387,7 +2419,10 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2387
2419
  { checkoutMethod: "paypal" }
2388
2420
  );
2389
2421
  }
2390
- return { status: "succeeded" };
2422
+ return {
2423
+ status: "succeeded",
2424
+ checkoutMethod: "paypal"
2425
+ };
2391
2426
  }
2392
2427
  throw new import_shared6.FloPayError("Unsupported payment redirect state.", "api_error");
2393
2428
  }
@@ -2400,19 +2435,16 @@ function normalizeSavedPaymentError(err) {
2400
2435
  "api_error"
2401
2436
  );
2402
2437
  }
2403
- function resolveSavedPaymentPublishableKeys(unified, fallbackPublishableKey) {
2438
+ function resolveSavedPaymentPublishableKeys(unified) {
2404
2439
  let publishableKey;
2405
2440
  let paypalPublishableKey;
2406
2441
  if (unified.provider === "stripe") {
2407
2442
  publishableKey = unified.data.stripe?.publishableKey;
2408
2443
  paypalPublishableKey = unified.data.stripe?.paypalPublishableKey ?? void 0;
2409
2444
  }
2410
- if (!publishableKey) {
2411
- publishableKey = fallbackPublishableKey;
2412
- }
2413
2445
  if (!publishableKey) {
2414
2446
  throw new import_shared6.FloPayError(
2415
- "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
2447
+ "No publishable key found in the checkout session. Ensure the session includes gatewayData.publishableKey.",
2416
2448
  "validation_error"
2417
2449
  );
2418
2450
  }
@@ -2450,9 +2482,60 @@ async function loadSavedPaymentProviders({
2450
2482
  // src/flopay-checkout.tsx
2451
2483
  var import_jsx_runtime6 = require("react/jsx-runtime");
2452
2484
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
2485
+ var PAYPAL_RESUME_STORAGE_KEY = "flopay_checkout_saved_payment_resume";
2453
2486
  function sleep(ms) {
2454
2487
  return new Promise((resolve) => setTimeout(resolve, ms));
2455
2488
  }
2489
+ function canUseStorage() {
2490
+ return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
2491
+ }
2492
+ function readPayPalResumeState() {
2493
+ if (!canUseStorage()) return null;
2494
+ try {
2495
+ const raw = window.sessionStorage.getItem(PAYPAL_RESUME_STORAGE_KEY);
2496
+ if (!raw) return null;
2497
+ return JSON.parse(raw);
2498
+ } catch {
2499
+ return null;
2500
+ }
2501
+ }
2502
+ function persistPayPalResumeState(state) {
2503
+ if (!canUseStorage()) return;
2504
+ try {
2505
+ window.sessionStorage.setItem(PAYPAL_RESUME_STORAGE_KEY, JSON.stringify(state));
2506
+ } catch (error) {
2507
+ console.warn("[FloPayCheckout] Failed to persist PayPal resume state.", error);
2508
+ }
2509
+ }
2510
+ function clearPayPalResumeState() {
2511
+ if (!canUseStorage()) return;
2512
+ try {
2513
+ window.sessionStorage.removeItem(PAYPAL_RESUME_STORAGE_KEY);
2514
+ } catch (error) {
2515
+ console.warn("[FloPayCheckout] Failed to clear PayPal resume state.", error);
2516
+ }
2517
+ }
2518
+ function clearPayPalRedirectParams() {
2519
+ if (typeof window === "undefined") return;
2520
+ const url = new URL(window.location.href);
2521
+ const keys = ["payment_intent", "payment_intent_client_secret", "redirect_status"];
2522
+ let changed = false;
2523
+ for (const key of keys) {
2524
+ if (url.searchParams.has(key)) {
2525
+ url.searchParams.delete(key);
2526
+ changed = true;
2527
+ }
2528
+ }
2529
+ if (changed) {
2530
+ window.history.replaceState(window.history.state, "", url.toString());
2531
+ }
2532
+ }
2533
+ function replaceCheckoutModeQueryParam(mode) {
2534
+ if (typeof window === "undefined") return;
2535
+ const url = new URL(window.location.href);
2536
+ url.searchParams.set("mode", mode);
2537
+ window.history.replaceState(window.history.state, "", url.toString());
2538
+ }
2456
2539
  function hasInlineSessionPatchData(patch) {
2457
2540
  if (!patch) return false;
2458
2541
  return Boolean(
@@ -2465,7 +2548,6 @@ function FloPayCheckout({
2465
2548
  billingApiUrl,
2466
2549
  appearance,
2467
2550
  locale,
2468
- fallbackPublishableKey,
2469
2551
  loading: loadingNode,
2470
2552
  error: errorNode,
2471
2553
  onComplete,
@@ -2520,6 +2602,8 @@ function FloPayCheckout({
2520
2602
  const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = (0, import_react8.useState)("");
2521
2603
  const [cardBootstrapPending, setCardBootstrapPending] = (0, import_react8.useState)(false);
2522
2604
  const autoCheckoutAttempted = (0, import_react8.useRef)(false);
2605
+ const paypalResumeAttempted = (0, import_react8.useRef)(false);
2606
+ const savedPaymentKeysRef = (0, import_react8.useRef)(null);
2523
2607
  const onCompleteRef = (0, import_react8.useRef)(onComplete);
2524
2608
  onCompleteRef.current = onComplete;
2525
2609
  const onErrorRef = (0, import_react8.useRef)(onError);
@@ -2579,6 +2663,13 @@ function FloPayCheckout({
2579
2663
  const redirectResult = getRedirectResultFromCheckoutProcessError(options?.initialAutoProcessingError);
2580
2664
  let paymentResult;
2581
2665
  if (redirectResult) {
2666
+ if (redirectResult.type === "paypal_redirect_required" && savedPaymentKeysRef.current) {
2667
+ persistPayPalResumeState({
2668
+ sessionId: activeSessionId2,
2669
+ publishableKey: savedPaymentKeysRef.current.publishableKey,
2670
+ paypalPublishableKey: savedPaymentKeysRef.current.paypalPublishableKey
2671
+ });
2672
+ }
2582
2673
  paymentResult = await handleSavedPaymentRedirectResult(redirectResult, {
2583
2674
  flopay: flopayRef.current,
2584
2675
  paypalFlopay: paypalFlopayRef.current,
@@ -2587,6 +2678,9 @@ function FloPayCheckout({
2587
2678
  sessionId: activeSessionId2,
2588
2679
  session: sess
2589
2680
  });
2681
+ if (redirectResult.type === "paypal_redirect_required") {
2682
+ clearPayPalResumeState();
2683
+ }
2590
2684
  } else if (options?.initialAutoProcessingError) {
2591
2685
  throw checkoutProcessErrorToFloPayError(
2592
2686
  options.initialAutoProcessingError,
@@ -2598,6 +2692,7 @@ function FloPayCheckout({
2598
2692
  } else if (options?.fromCreateSession) {
2599
2693
  if (options?.fallbackToFull) {
2600
2694
  setCurrentMode("full");
2695
+ replaceCheckoutModeQueryParam("full");
2601
2696
  }
2602
2697
  return false;
2603
2698
  } else {
@@ -2606,14 +2701,28 @@ function FloPayCheckout({
2606
2701
  sessionId: activeSessionId2,
2607
2702
  session: sess
2608
2703
  });
2609
- paymentResult = result.type === "success" ? result.result : await handleSavedPaymentRedirectResult(result, {
2610
- flopay: flopayRef.current,
2611
- paypalFlopay: paypalFlopayRef.current,
2612
- attempt3DS: options?.attempt3DS,
2613
- billingApiUrl: resolvedBillingUrl,
2614
- sessionId: activeSessionId2,
2615
- session: sess
2616
- });
2704
+ if (result.type === "success") {
2705
+ paymentResult = result.result;
2706
+ } else {
2707
+ if (result.type === "paypal_redirect_required" && savedPaymentKeysRef.current) {
2708
+ persistPayPalResumeState({
2709
+ sessionId: activeSessionId2,
2710
+ publishableKey: savedPaymentKeysRef.current.publishableKey,
2711
+ paypalPublishableKey: savedPaymentKeysRef.current.paypalPublishableKey
2712
+ });
2713
+ }
2714
+ paymentResult = await handleSavedPaymentRedirectResult(result, {
2715
+ flopay: flopayRef.current,
2716
+ paypalFlopay: paypalFlopayRef.current,
2717
+ attempt3DS: options?.attempt3DS,
2718
+ billingApiUrl: resolvedBillingUrl,
2719
+ sessionId: activeSessionId2,
2720
+ session: sess
2721
+ });
2722
+ if (result.type === "paypal_redirect_required") {
2723
+ clearPayPalResumeState();
2724
+ }
2725
+ }
2617
2726
  }
2618
2727
  setModeOverlayStatus("success");
2619
2728
  await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
@@ -2626,6 +2735,7 @@ function FloPayCheckout({
2626
2735
  setModeOverlayError(floPayErr.message);
2627
2736
  if (options?.fallbackToFull) {
2628
2737
  setCurrentMode("full");
2738
+ replaceCheckoutModeQueryParam("full");
2629
2739
  }
2630
2740
  onErrorRef.current?.(floPayErr);
2631
2741
  emitDecline(method, floPayErr, {
@@ -2649,6 +2759,96 @@ function FloPayCheckout({
2649
2759
  resolvedBillingUrl
2650
2760
  ]
2651
2761
  );
2762
+ (0, import_react8.useEffect)(() => {
2763
+ if (typeof window === "undefined" || paypalResumeAttempted.current) {
2764
+ return;
2765
+ }
2766
+ const params = new URLSearchParams(window.location.search);
2767
+ const clientSecret = params.get("payment_intent_client_secret");
2768
+ if (!clientSecret) {
2769
+ return;
2770
+ }
2771
+ const resumeState = readPayPalResumeState();
2772
+ if (!resumeState) {
2773
+ return;
2774
+ }
2775
+ paypalResumeAttempted.current = true;
2776
+ void (async () => {
2777
+ setModeError(null);
2778
+ setModeOverlayError(null);
2779
+ setModeOverlayStatus("processing");
2780
+ setConfirmProcessing(true);
2781
+ try {
2782
+ if (params.get("redirect_status") === "failed") {
2783
+ throw Object.assign(
2784
+ new import_shared7.FloPayError("PayPal payment was declined. Please try again.", "api_error"),
2785
+ { checkoutMethod: "paypal" }
2786
+ );
2787
+ }
2788
+ const {
2789
+ flopay: resumeFlopay,
2790
+ paypalFlopay: resumePaypalFlopay
2791
+ } = await loadSavedPaymentProviders({
2792
+ publishableKey: resumeState.publishableKey,
2793
+ paypalPublishableKey: resumeState.paypalPublishableKey,
2794
+ billingApiUrl: resolvedBillingUrl,
2795
+ locale
2796
+ });
2797
+ const paypalStripe = (resumePaypalFlopay ?? resumeFlopay).getRawProvider();
2798
+ if (!paypalStripe) {
2799
+ throw Object.assign(
2800
+ new import_shared7.FloPayError("PayPal is not available.", "api_error"),
2801
+ { checkoutMethod: "paypal" }
2802
+ );
2803
+ }
2804
+ const { paymentIntent, error } = await paypalStripe.retrievePaymentIntent(clientSecret);
2805
+ if (error) {
2806
+ throw Object.assign(
2807
+ new import_shared7.FloPayError(
2808
+ error.message ?? "Failed to retrieve PayPal payment status.",
2809
+ "api_error",
2810
+ { code: error.code }
2811
+ ),
2812
+ { checkoutMethod: "paypal" }
2813
+ );
2814
+ }
2815
+ const resultStatus = mapPayPalIntentStatusToPaymentResult(paymentIntent?.status);
2816
+ if (!paymentIntent || resultStatus === "failed") {
2817
+ throw Object.assign(
2818
+ new import_shared7.FloPayError("PayPal payment was not completed. Please try again.", "api_error"),
2819
+ { checkoutMethod: "paypal" }
2820
+ );
2821
+ }
2822
+ const paymentMethodId = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
2823
+ setModeOverlayStatus("success");
2824
+ await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
2825
+ onCompleteRef.current?.({
2826
+ status: resultStatus,
2827
+ paymentIntentId: paymentIntent.id,
2828
+ paymentMethodId,
2829
+ checkoutMethod: "paypal"
2830
+ });
2831
+ } catch (err) {
2832
+ const floPayErr = normalizeSavedPaymentError(err);
2833
+ const method = floPayErr.checkoutMethod ?? "paypal";
2834
+ setModeError(floPayErr.message);
2835
+ setModeOverlayError(floPayErr.message);
2836
+ onErrorRef.current?.(floPayErr);
2837
+ emitDecline(method, floPayErr, {
2838
+ code: floPayErr.code,
2839
+ declineCode: floPayErr.declineCode
2840
+ });
2841
+ setModeOverlayStatus("error");
2842
+ await sleep(PROCESSING_OVERLAY_ERROR_DELAY_MS);
2843
+ } finally {
2844
+ clearPayPalResumeState();
2845
+ clearPayPalRedirectParams();
2846
+ setModeOverlayStatus(null);
2847
+ setModeOverlayError(null);
2848
+ setConfirmProcessing(false);
2849
+ }
2850
+ })();
2851
+ }, [emitDecline, locale, normalizeSavedPaymentError, resolvedBillingUrl]);
2652
2852
  const inflightRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
2653
2853
  const initializedHashRef = (0, import_react8.useRef)(null);
2654
2854
  function hashCreateParams(params) {
@@ -2747,7 +2947,7 @@ function FloPayCheckout({
2747
2947
  const {
2748
2948
  publishableKey,
2749
2949
  paypalPublishableKey
2750
- } = resolveSavedPaymentPublishableKeys(realResult, fallbackPublishableKey);
2950
+ } = resolveSavedPaymentPublishableKeys(realResult);
2751
2951
  const {
2752
2952
  flopay: instance,
2753
2953
  paypalFlopay: paypalInstance
@@ -2770,7 +2970,7 @@ function FloPayCheckout({
2770
2970
  }
2771
2971
  return resolved;
2772
2972
  },
2773
- [fallbackPublishableKey, locale, resolvedBillingUrl]
2973
+ [locale, resolvedBillingUrl]
2774
2974
  );
2775
2975
  const handleInlineSessionPatch = (0, import_react8.useCallback)(async (patch) => {
2776
2976
  if (!hasInlineSessionPatchData(patch) || cardBootstrapPending) {
@@ -2818,6 +3018,7 @@ function FloPayCheckout({
2818
3018
  try {
2819
3019
  const resolved = await bootstrapInlineSession();
2820
3020
  const resolvedSession = resolved.result.data.session ?? null;
3021
+ savedPaymentKeysRef.current = resolveSavedPaymentPublishableKeys(resolved.result);
2821
3022
  if (!cancelled && shouldAutoProcessInlineSession && resolvedSession) {
2822
3023
  autoCheckoutAttempted.current = true;
2823
3024
  await runSavedPaymentFlow(resolvedSession, {
@@ -2916,7 +3117,11 @@ function FloPayCheckout({
2916
3117
  const {
2917
3118
  publishableKey,
2918
3119
  paypalPublishableKey
2919
- } = resolveSavedPaymentPublishableKeys(result, fallbackPublishableKey);
3120
+ } = resolveSavedPaymentPublishableKeys(result);
3121
+ savedPaymentKeysRef.current = {
3122
+ publishableKey,
3123
+ paypalPublishableKey
3124
+ };
2920
3125
  const {
2921
3126
  flopay: instance,
2922
3127
  paypalFlopay: paypalInstance
@@ -3469,9 +3674,11 @@ function CheckoutFormInner({
3469
3674
  [onDecline]
3470
3675
  );
3471
3676
  const processPaymentInternal = (0, import_react9.useCallback)(
3472
- async (tokenizedBody) => {
3677
+ async (tokenizedBody, completionPaymentMethodId) => {
3473
3678
  setProcessing(true);
3474
3679
  updateError(null);
3680
+ const resolvedCompletionPaymentMethodId = completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
3681
+ const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
3475
3682
  try {
3476
3683
  const response = await fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
3477
3684
  method: "POST",
@@ -3481,7 +3688,7 @@ function CheckoutFormInner({
3481
3688
  },
3482
3689
  body: JSON.stringify({
3483
3690
  sessionId,
3484
- tokenizedData: tokenizedBody,
3691
+ tokenizedData: requestTokenizedBody,
3485
3692
  accountData: {
3486
3693
  userId: userId ?? "",
3487
3694
  email: email ?? "",
@@ -3495,7 +3702,8 @@ function CheckoutFormInner({
3495
3702
  onComplete?.({
3496
3703
  status: "succeeded",
3497
3704
  paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
3498
- paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
3705
+ paymentMethodId: resolvedCompletionPaymentMethodId,
3706
+ checkoutMethod: tokenizedBody.isPaypal ? "paypal" : "card"
3499
3707
  });
3500
3708
  return;
3501
3709
  }
@@ -3527,9 +3735,8 @@ function CheckoutFormInner({
3527
3735
  await processPaymentInternal({
3528
3736
  id: result.paymentIntentId,
3529
3737
  type: "card",
3530
- threeDSecureActionResultTokenId: result.paymentIntentId,
3531
- originalPaymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
3532
- });
3738
+ threeDSecureActionResultTokenId: result.paymentIntentId
3739
+ }, resolvedCompletionPaymentMethodId);
3533
3740
  }
3534
3741
  } finally {
3535
3742
  setIs3DSActive(false);
@@ -3856,33 +4063,23 @@ function PayPalButton({
3856
4063
  if (!sessionId || !email) {
3857
4064
  throw new import_shared9.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
3858
4065
  }
3859
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3860
- method: "POST",
3861
- headers: { "Content-Type": "application/json" },
3862
- body: JSON.stringify({
3863
- sessionId,
3864
- email,
3865
- paymentMethodType: "paypal",
3866
- isPaypal: "true"
3867
- })
3868
- });
3869
- if (!intentResponse.ok) throw new import_shared9.FloPayError("Failed to create payment intent", "api_error");
3870
- const intentJson = await intentResponse.json();
3871
- const intentClientSecret = intentJson.data?.id;
3872
- if (!intentClientSecret) throw new import_shared9.FloPayError("No client_secret in response", "api_error");
3873
- const result = await flopay.confirmPayment({
3874
- clientSecret: intentClientSecret,
4066
+ const result = await flopay.confirmPayPalPayment({
4067
+ billingApiUrl: baseUrl,
4068
+ sessionId,
4069
+ email,
3875
4070
  returnUrl: window.location.href
3876
4071
  });
3877
- if (result.status === "succeeded" || result.status === "processing") {
4072
+ if (result.error) {
4073
+ onErrorChange?.(result.error.message);
4074
+ return;
4075
+ }
4076
+ if (result.paymentIntentId && (result.status === "succeeded" || result.status === "processing" || result.status === "requires_capture")) {
3878
4077
  dispatchTokenizedBody({
3879
- id: result.paymentIntentId,
4078
+ id: result.paymentMethodId ?? result.paymentIntentId,
3880
4079
  type: "card",
3881
4080
  threeDSecureActionResultTokenId: result.paymentIntentId,
3882
4081
  isPaypal: true
3883
4082
  });
3884
- } else if (result.error) {
3885
- onErrorChange?.(result.error.message);
3886
4083
  }
3887
4084
  } catch (err) {
3888
4085
  onErrorChange?.(err instanceof Error ? err.message : "PayPal payment failed. Please try again.");
@@ -3942,7 +4139,7 @@ var import_js3 = require("@flopay/js");
3942
4139
  var import_shared10 = require("@flopay/shared");
3943
4140
  var import_jsx_runtime9 = require("react/jsx-runtime");
3944
4141
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
3945
- var PAYPAL_RESUME_STORAGE_KEY = "flopay_automatic_payment_button_resume";
4142
+ var PAYPAL_RESUME_STORAGE_KEY2 = "flopay_automatic_payment_button_resume";
3946
4143
  function sleep2(ms) {
3947
4144
  return new Promise((resolve) => setTimeout(resolve, ms));
3948
4145
  }
@@ -3955,42 +4152,44 @@ function coerceError(err, fallbackMessage) {
3955
4152
  "api_error"
3956
4153
  );
3957
4154
  }
3958
- function canUseStorage() {
4155
+ function canUseStorage2() {
3959
4156
  return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
3960
4157
  }
3961
4158
  function shouldShowFallbackCheckout(error, sessionId) {
3962
4159
  if (!sessionId) return false;
3963
4160
  if (error.type === "validation_error") return false;
3964
- if (error.checkoutMethod && error.checkoutMethod !== "card") return false;
4161
+ if (error.checkoutMethod && error.checkoutMethod !== "card" && error.checkoutMethod !== "paypal") {
4162
+ return false;
4163
+ }
3965
4164
  return true;
3966
4165
  }
3967
- function readPayPalResumeState() {
3968
- if (!canUseStorage()) return null;
4166
+ function readPayPalResumeState2() {
4167
+ if (!canUseStorage2()) return null;
3969
4168
  try {
3970
- const raw = window.sessionStorage.getItem(PAYPAL_RESUME_STORAGE_KEY);
4169
+ const raw = window.sessionStorage.getItem(PAYPAL_RESUME_STORAGE_KEY2);
3971
4170
  if (!raw) return null;
3972
4171
  return JSON.parse(raw);
3973
4172
  } catch {
3974
4173
  return null;
3975
4174
  }
3976
4175
  }
3977
- function persistPayPalResumeState(state) {
3978
- if (!canUseStorage()) return;
4176
+ function persistPayPalResumeState2(state) {
4177
+ if (!canUseStorage2()) return;
3979
4178
  try {
3980
- window.sessionStorage.setItem(PAYPAL_RESUME_STORAGE_KEY, JSON.stringify(state));
4179
+ window.sessionStorage.setItem(PAYPAL_RESUME_STORAGE_KEY2, JSON.stringify(state));
3981
4180
  } catch (error) {
3982
4181
  console.warn("[FloPayAutomaticPaymentButton] Failed to persist PayPal resume state.", error);
3983
4182
  }
3984
4183
  }
3985
- function clearPayPalResumeState() {
3986
- if (!canUseStorage()) return;
4184
+ function clearPayPalResumeState2() {
4185
+ if (!canUseStorage2()) return;
3987
4186
  try {
3988
- window.sessionStorage.removeItem(PAYPAL_RESUME_STORAGE_KEY);
4187
+ window.sessionStorage.removeItem(PAYPAL_RESUME_STORAGE_KEY2);
3989
4188
  } catch (error) {
3990
4189
  console.warn("[FloPayAutomaticPaymentButton] Failed to clear PayPal resume state.", error);
3991
4190
  }
3992
4191
  }
3993
- function clearPayPalRedirectParams() {
4192
+ function clearPayPalRedirectParams2() {
3994
4193
  if (typeof window === "undefined") return;
3995
4194
  const url = new URL(window.location.href);
3996
4195
  const keys = ["payment_intent", "payment_intent_client_secret", "redirect_status"];
@@ -4032,6 +4231,7 @@ function FloPayAutomaticPaymentButton({
4032
4231
  sessionId,
4033
4232
  createSession,
4034
4233
  paymentMethodId,
4234
+ checkoutMethod,
4035
4235
  clientId,
4036
4236
  items,
4037
4237
  subscriptions,
@@ -4043,7 +4243,6 @@ function FloPayAutomaticPaymentButton({
4043
4243
  utmMetadata,
4044
4244
  billingApiUrl,
4045
4245
  locale,
4046
- fallbackPublishableKey,
4047
4246
  buttonsTheme,
4048
4247
  buttonsStyles: stylesOverride,
4049
4248
  onSuccess,
@@ -4092,9 +4291,10 @@ function FloPayAutomaticPaymentButton({
4092
4291
  const automaticPaymentToken = (0, import_react11.useMemo)(
4093
4292
  () => paymentMethodId ? {
4094
4293
  id: paymentMethodId,
4095
- type: "card"
4294
+ type: "card",
4295
+ ...checkoutMethod === "paypal" ? { isPaypal: true } : {}
4096
4296
  } : void 0,
4097
- [paymentMethodId]
4297
+ [checkoutMethod, paymentMethodId]
4098
4298
  );
4099
4299
  const isMountedRef = (0, import_react11.useRef)(true);
4100
4300
  const resumeAttemptedRef = (0, import_react11.useRef)(false);
@@ -4176,13 +4376,14 @@ function FloPayAutomaticPaymentButton({
4176
4376
  let paymentResult = null;
4177
4377
  const redirectResult = getRedirectResultFromCheckoutProcessError(apiResult.autoProcessingError);
4178
4378
  const shouldTreatCreateSessionFlowAsServerAutoAttempt = options?.fromCreateSession && (apiResult.autoProcessingAttempted === true || !!apiResult.autoProcessingError || !!redirectResult);
4379
+ const shouldRetryPayPalClientSide = checkoutMethod === "paypal" && automaticPaymentToken?.isPaypal === true && options?.fromCreateSession === true;
4179
4380
  if (redirectResult) {
4180
4381
  const {
4181
4382
  publishableKey,
4182
4383
  paypalPublishableKey
4183
- } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4384
+ } = resolveSavedPaymentPublishableKeys(apiResult);
4184
4385
  if (redirectResult.type === "paypal_redirect_required") {
4185
- persistPayPalResumeState({
4386
+ persistPayPalResumeState2({
4186
4387
  sessionId: session.id || resolvedSessionId,
4187
4388
  publishableKey,
4188
4389
  paypalPublishableKey
@@ -4206,9 +4407,9 @@ function FloPayAutomaticPaymentButton({
4206
4407
  session
4207
4408
  });
4208
4409
  if (redirectResult.type === "paypal_redirect_required") {
4209
- clearPayPalResumeState();
4410
+ clearPayPalResumeState2();
4210
4411
  }
4211
- } else if (apiResult.autoProcessingError) {
4412
+ } else if (apiResult.autoProcessingError && !shouldRetryPayPalClientSide) {
4212
4413
  throw checkoutProcessErrorToFloPayError(
4213
4414
  apiResult.autoProcessingError,
4214
4415
  "Automatic payment failed. Please try again.",
@@ -4216,7 +4417,7 @@ function FloPayAutomaticPaymentButton({
4216
4417
  checkoutMethod: apiResult.autoProcessingError.checkoutMethod
4217
4418
  }
4218
4419
  );
4219
- } else if (shouldTreatCreateSessionFlowAsServerAutoAttempt) {
4420
+ } else if (shouldTreatCreateSessionFlowAsServerAutoAttempt && !shouldRetryPayPalClientSide) {
4220
4421
  throw checkoutProcessErrorToFloPayError(
4221
4422
  {
4222
4423
  type: "unknown",
@@ -4236,9 +4437,9 @@ function FloPayAutomaticPaymentButton({
4236
4437
  const {
4237
4438
  publishableKey,
4238
4439
  paypalPublishableKey
4239
- } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4440
+ } = resolveSavedPaymentPublishableKeys(apiResult);
4240
4441
  if (result.type === "paypal_redirect_required") {
4241
- persistPayPalResumeState({
4442
+ persistPayPalResumeState2({
4242
4443
  sessionId: session.id || resolvedSessionId,
4243
4444
  publishableKey,
4244
4445
  paypalPublishableKey
@@ -4262,17 +4463,19 @@ function FloPayAutomaticPaymentButton({
4262
4463
  session
4263
4464
  });
4264
4465
  if (result.type === "paypal_redirect_required") {
4265
- clearPayPalResumeState();
4466
+ clearPayPalResumeState2();
4266
4467
  }
4267
4468
  }
4268
4469
  }
4269
4470
  await showSuccess({
4270
4471
  result: paymentResult ? {
4271
4472
  ...paymentResult,
4272
- paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
4473
+ paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId,
4474
+ checkoutMethod: paymentResult.checkoutMethod ?? checkoutMethod
4273
4475
  } : {
4274
4476
  status: "succeeded",
4275
- paymentMethodId: paymentMethodId ?? void 0
4477
+ paymentMethodId: paymentMethodId ?? void 0,
4478
+ checkoutMethod
4276
4479
  },
4277
4480
  session,
4278
4481
  sessionId: session.id || resolvedSessionId,
@@ -4287,7 +4490,7 @@ function FloPayAutomaticPaymentButton({
4287
4490
  const {
4288
4491
  publishableKey,
4289
4492
  paypalPublishableKey
4290
- } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4493
+ } = resolveSavedPaymentPublishableKeys(apiResult);
4291
4494
  const {
4292
4495
  flopay
4293
4496
  } = await loadSavedPaymentProviders({
@@ -4306,7 +4509,8 @@ function FloPayAutomaticPaymentButton({
4306
4509
  await showSuccess({
4307
4510
  result: {
4308
4511
  ...paymentResult,
4309
- paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
4512
+ paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId,
4513
+ checkoutMethod: paymentResult.checkoutMethod ?? checkoutMethod
4310
4514
  },
4311
4515
  session,
4312
4516
  sessionId: fallbackSessionId,
@@ -4329,7 +4533,7 @@ function FloPayAutomaticPaymentButton({
4329
4533
  }
4330
4534
  }
4331
4535
  }, [
4332
- fallbackPublishableKey,
4536
+ checkoutMethod,
4333
4537
  locale,
4334
4538
  automaticPaymentToken,
4335
4539
  paymentMethodId,
@@ -4441,7 +4645,7 @@ function FloPayAutomaticPaymentButton({
4441
4645
  if (!clientSecret) {
4442
4646
  return;
4443
4647
  }
4444
- const resumeState = readPayPalResumeState();
4648
+ const resumeState = readPayPalResumeState2();
4445
4649
  if (!resumeState) {
4446
4650
  return;
4447
4651
  }
@@ -4484,9 +4688,16 @@ function FloPayAutomaticPaymentButton({
4484
4688
  { checkoutMethod: "paypal" }
4485
4689
  );
4486
4690
  }
4487
- if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) {
4691
+ const resultStatus = mapPayPalIntentStatusToPaymentResult(paymentIntent?.status);
4692
+ if (paymentIntent && resultStatus !== "failed") {
4693
+ const paymentMethodId2 = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
4488
4694
  await showSuccess({
4489
- result: { status: "succeeded", paymentIntentId: paymentIntent.id },
4695
+ result: {
4696
+ status: resultStatus,
4697
+ paymentIntentId: paymentIntent.id,
4698
+ paymentMethodId: paymentMethodId2,
4699
+ checkoutMethod: "paypal"
4700
+ },
4490
4701
  session: null,
4491
4702
  sessionId: resumeState.sessionId,
4492
4703
  autoCompleted: false
@@ -4504,8 +4715,8 @@ function FloPayAutomaticPaymentButton({
4504
4715
  method: floPayErr.checkoutMethod ?? "paypal"
4505
4716
  });
4506
4717
  } finally {
4507
- clearPayPalResumeState();
4508
- clearPayPalRedirectParams();
4718
+ clearPayPalResumeState2();
4719
+ clearPayPalRedirectParams2();
4509
4720
  if (isMountedRef.current) {
4510
4721
  setOverlayStatus(null);
4511
4722
  setOverlayError(null);
@@ -4619,7 +4830,6 @@ function FloPayAutomaticPaymentButton({
4619
4830
  sessionId: fallbackSession.sessionId,
4620
4831
  checkoutMode: "full",
4621
4832
  billingApiUrl: resolvedBillingUrl,
4622
- fallbackPublishableKey,
4623
4833
  initialErrorMessage: fallbackSession.errorMessage,
4624
4834
  cardTitleContent: null,
4625
4835
  showSecurityFooter: false,