@flopay/react 0.5.6 → 0.5.11

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
@@ -625,6 +625,39 @@ function resolveTokenizedPaymentMethodId(tokenizedBody) {
625
625
  return void 0;
626
626
  }
627
627
 
628
+ // src/recent-completion.ts
629
+ var STORAGE_KEY_PREFIX = "flopay_recent_completion_";
630
+ var RECENT_COMPLETION_TTL_MS = 60 * 1e3;
631
+ function markSessionRecentlyCompleted(sessionId) {
632
+ if (!sessionId || typeof window === "undefined") return;
633
+ try {
634
+ const payload = {
635
+ expiresAt: Date.now() + RECENT_COMPLETION_TTL_MS
636
+ };
637
+ window.sessionStorage.setItem(STORAGE_KEY_PREFIX + sessionId, JSON.stringify(payload));
638
+ } catch {
639
+ }
640
+ }
641
+ function wasSessionRecentlyCompleted(sessionId) {
642
+ if (!sessionId || typeof window === "undefined") return false;
643
+ try {
644
+ const raw = window.sessionStorage.getItem(STORAGE_KEY_PREFIX + sessionId);
645
+ if (!raw) return false;
646
+ const parsed = JSON.parse(raw);
647
+ if (typeof parsed?.expiresAt !== "number") {
648
+ window.sessionStorage.removeItem(STORAGE_KEY_PREFIX + sessionId);
649
+ return false;
650
+ }
651
+ if (parsed.expiresAt <= Date.now()) {
652
+ window.sessionStorage.removeItem(STORAGE_KEY_PREFIX + sessionId);
653
+ return false;
654
+ }
655
+ return true;
656
+ } catch {
657
+ return false;
658
+ }
659
+ }
660
+
628
661
  // src/split-card-form.tsx
629
662
  var import_shared5 = require("@flopay/shared");
630
663
  var import_jsx_runtime5 = require("react/jsx-runtime");
@@ -756,6 +789,11 @@ function PayPalButtonInner({
756
789
  const redirectStatus = params.get("redirect_status");
757
790
  if (!paymentIntentId || !clientSecret) return;
758
791
  paypalResumeAttempted.current = true;
792
+ const cleanedUrl = new URL(window.location.href);
793
+ cleanedUrl.searchParams.delete("payment_intent");
794
+ cleanedUrl.searchParams.delete("payment_intent_client_secret");
795
+ cleanedUrl.searchParams.delete("redirect_status");
796
+ window.history.replaceState({}, "", cleanedUrl.toString());
759
797
  (async () => {
760
798
  try {
761
799
  setSubmitting(true);
@@ -778,11 +816,6 @@ function PayPalButtonInner({
778
816
  threeDSecureActionResultTokenId: paymentIntent.id,
779
817
  isPaypal: true
780
818
  });
781
- const url = new URL(window.location.href);
782
- url.searchParams.delete("payment_intent");
783
- url.searchParams.delete("payment_intent_client_secret");
784
- url.searchParams.delete("redirect_status");
785
- window.history.replaceState({}, "", url.toString());
786
819
  } else {
787
820
  const message = "PayPal payment was not completed. Please try again.";
788
821
  onErrorChange?.(message);
@@ -1365,6 +1398,7 @@ function SplitCardFormInner({
1365
1398
  })
1366
1399
  });
1367
1400
  if (response.ok) {
1401
+ markSessionRecentlyCompleted(effectiveSessionId);
1368
1402
  setOverlayStatus("success");
1369
1403
  await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_SUCCESS_DELAY_MS));
1370
1404
  onComplete?.({
@@ -3019,6 +3053,7 @@ function FloPayCheckout({
3019
3053
  }
3020
3054
  }
3021
3055
  }
3056
+ if (activeSessionId2) markSessionRecentlyCompleted(activeSessionId2);
3022
3057
  setModeOverlayStatus("success");
3023
3058
  await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
3024
3059
  onCompleteRef.current?.(paymentResult);
@@ -3115,6 +3150,31 @@ function FloPayCheckout({
3115
3150
  );
3116
3151
  }
3117
3152
  const paymentMethodId = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
3153
+ if (resumeState.sessionId) {
3154
+ const resumeApi = new import_js2.PaymentAPI(resolvedBillingUrl);
3155
+ const resumeSessionResult = await resumeApi.getUnifiedCheckoutSession(resumeState.sessionId);
3156
+ const resumeSession = resumeSessionResult.data.session;
3157
+ if (resumeSession && resumeSession.status !== "complete") {
3158
+ const processResult = await processSavedPaymentForMode({
3159
+ billingApiUrl: resolvedBillingUrl,
3160
+ sessionId: resumeState.sessionId,
3161
+ session: resumeSession,
3162
+ tokenizedData: {
3163
+ id: paymentMethodId ?? paymentIntent.id,
3164
+ type: "card",
3165
+ threeDSecureActionResultTokenId: paymentIntent.id,
3166
+ isPaypal: true
3167
+ }
3168
+ });
3169
+ if (processResult.type !== "success") {
3170
+ throw Object.assign(
3171
+ new import_shared7.FloPayError("Failed to finalize PayPal payment.", "api_error"),
3172
+ { checkoutMethod: "paypal" }
3173
+ );
3174
+ }
3175
+ }
3176
+ }
3177
+ if (resumeState.sessionId) markSessionRecentlyCompleted(resumeState.sessionId);
3118
3178
  setModeOverlayStatus("success");
3119
3179
  await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
3120
3180
  onCompleteRef.current?.({
@@ -3149,7 +3209,6 @@ function FloPayCheckout({
3149
3209
  const key = JSON.stringify({
3150
3210
  c: params?.clientId,
3151
3211
  a: {
3152
- u: params?.account?.userId ?? "",
3153
3212
  e: params?.account?.email?.trim().toLowerCase() ?? "",
3154
3213
  country: params?.account?.country ?? ""
3155
3214
  },
@@ -3191,7 +3250,11 @@ function FloPayCheckout({
3191
3250
  if (sid) {
3192
3251
  try {
3193
3252
  realResult = await api.getUnifiedCheckoutSession(sid);
3194
- if (realResult.data.session?.status === "complete") {
3253
+ const status = realResult.data.session?.status;
3254
+ if (status === "complete") {
3255
+ if (wasSessionRecentlyCompleted(sid)) {
3256
+ return { sid, result: realResult };
3257
+ }
3195
3258
  if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
3196
3259
  sid = null;
3197
3260
  realResult = null;
@@ -4856,6 +4919,10 @@ function FloPayAutomaticPaymentButton({
4856
4919
  floPayErr = normalizeSavedPaymentError(intentRecoveryErr);
4857
4920
  }
4858
4921
  }
4922
+ const inResumeWindow = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent_client_secret") && !!readPayPalResumeState2();
4923
+ if (floPayErr.code === "payment_intent_unexpected_state" && inResumeWindow) {
4924
+ return;
4925
+ }
4859
4926
  await showError(floPayErr, {
4860
4927
  emitDecline: true,
4861
4928
  method: floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD
@@ -5027,25 +5094,58 @@ function FloPayAutomaticPaymentButton({
5027
5094
  );
5028
5095
  }
5029
5096
  const resultStatus = mapPayPalIntentStatusToPaymentResult(paymentIntent?.status);
5030
- if (paymentIntent && resultStatus !== "failed") {
5031
- const paymentMethodId2 = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
5032
- await showSuccess({
5033
- result: {
5034
- status: resultStatus,
5035
- paymentIntentId: paymentIntent.id,
5036
- paymentMethodId: paymentMethodId2,
5037
- checkoutMethod: "paypal"
5038
- },
5039
- session: null,
5040
- sessionId: resumeState.sessionId,
5041
- autoCompleted: false
5042
- });
5043
- } else {
5097
+ if (!paymentIntent || resultStatus === "failed") {
5044
5098
  throw Object.assign(
5045
5099
  new import_shared10.FloPayError("PayPal payment was not completed. Please try again.", "api_error"),
5046
5100
  { checkoutMethod: "paypal" }
5047
5101
  );
5048
5102
  }
5103
+ const paymentMethodId2 = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
5104
+ if (!resumeState.sessionId) {
5105
+ throw Object.assign(
5106
+ new import_shared10.FloPayError("Missing session id on PayPal resume.", "api_error"),
5107
+ { checkoutMethod: "paypal" }
5108
+ );
5109
+ }
5110
+ const api = new import_js3.PaymentAPI(resolvedBillingUrl);
5111
+ const sessionResult = await api.getUnifiedCheckoutSession(resumeState.sessionId);
5112
+ const resumeSession = sessionResult.data.session;
5113
+ if (!resumeSession) {
5114
+ throw Object.assign(
5115
+ new import_shared10.FloPayError("Could not load session to capture PayPal payment.", "api_error"),
5116
+ { checkoutMethod: "paypal" }
5117
+ );
5118
+ }
5119
+ if (resumeSession.status !== "complete") {
5120
+ const processResult = await processSavedPaymentForMode({
5121
+ billingApiUrl: resolvedBillingUrl,
5122
+ sessionId: resumeState.sessionId,
5123
+ session: resumeSession,
5124
+ tokenizedData: {
5125
+ id: paymentMethodId2 ?? paymentIntent.id,
5126
+ type: "card",
5127
+ threeDSecureActionResultTokenId: paymentIntent.id,
5128
+ isPaypal: true
5129
+ }
5130
+ });
5131
+ if (processResult.type !== "success") {
5132
+ throw Object.assign(
5133
+ new import_shared10.FloPayError("Failed to finalize PayPal payment.", "api_error"),
5134
+ { checkoutMethod: "paypal" }
5135
+ );
5136
+ }
5137
+ }
5138
+ await showSuccess({
5139
+ result: {
5140
+ status: resultStatus,
5141
+ paymentIntentId: paymentIntent.id,
5142
+ paymentMethodId: paymentMethodId2,
5143
+ checkoutMethod: "paypal"
5144
+ },
5145
+ session: resumeSession,
5146
+ sessionId: resumeState.sessionId,
5147
+ autoCompleted: false
5148
+ });
5049
5149
  } catch (err) {
5050
5150
  const floPayErr = normalizeSavedPaymentError(err);
5051
5151
  await showError(floPayErr, {