@flopay/react 0.4.4 → 0.4.7

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
@@ -1407,13 +1407,19 @@ function SplitCardFormInner({
1407
1407
  onError?.(submitResult.error);
1408
1408
  return;
1409
1409
  }
1410
- const billingDetails = enableAVS ? {
1411
- name: fullName,
1412
- address: {
1413
- country: selectedCountryRef.current,
1414
- postal_code: zipCodeRef.current
1415
- }
1416
- } : void 0;
1410
+ const billingAddress = {
1411
+ ...(enableAVS ? selectedCountryRef.current : resolvedAccount.country) ? {
1412
+ country: enableAVS ? selectedCountryRef.current : resolvedAccount.country
1413
+ } : {},
1414
+ ...(enableAVS ? zipCodeRef.current.trim() : resolvedAccount.zip) || "" ? {
1415
+ postal_code: enableAVS ? zipCodeRef.current.trim() : resolvedAccount.zip
1416
+ } : {}
1417
+ };
1418
+ const billingDetails = {
1419
+ ...resolvedAccount.email ? { email: resolvedAccount.email } : {},
1420
+ ...fullName.trim() ? { name: fullName.trim() } : {},
1421
+ ...Object.keys(billingAddress).length > 0 ? { address: billingAddress } : {}
1422
+ };
1417
1423
  const pmResult = await flopay.createPaymentMethod(billingDetails);
1418
1424
  if (pmResult.error || !pmResult.paymentMethodId) {
1419
1425
  updateError(pmResult.error?.message ?? "Failed to create payment method.");
@@ -1923,6 +1929,32 @@ function SplitCardFormInner({
1923
1929
  var import_js = require("@flopay/js");
1924
1930
  var import_shared6 = require("@flopay/shared");
1925
1931
  var DEFAULT_SAVED_PAYMENT_DECLINE_METHOD = "card";
1932
+ function getRedirectResultFromCheckoutProcessError(error) {
1933
+ if (!error?.type || !error.threeDSecureToken) {
1934
+ return null;
1935
+ }
1936
+ if (error.type !== "3ds_required" && error.type !== "paypal_redirect_required") {
1937
+ return null;
1938
+ }
1939
+ return {
1940
+ type: error.type,
1941
+ threeDSecureToken: error.threeDSecureToken,
1942
+ paymentMethodId: error.paymentMethodId
1943
+ };
1944
+ }
1945
+ function checkoutProcessErrorToFloPayError(error, fallbackMessage = "Payment failed. Please try again.", options) {
1946
+ const checkoutMethod = options?.checkoutMethod ?? error?.checkoutMethod ?? (error?.type === "paypal_redirect_required" ? "paypal" : "card");
1947
+ return Object.assign(
1948
+ new import_shared6.FloPayError(
1949
+ error?.message ?? fallbackMessage,
1950
+ "api_error",
1951
+ {
1952
+ code: error?.gatewayErrorCode
1953
+ }
1954
+ ),
1955
+ { checkoutMethod }
1956
+ );
1957
+ }
1926
1958
  function resolveSavedPaymentReturnUrl(session) {
1927
1959
  if (typeof window !== "undefined" && window.location.href) {
1928
1960
  return window.location.href;
@@ -2445,6 +2477,7 @@ function FloPayCheckout({
2445
2477
  avsLayout,
2446
2478
  submitLabel,
2447
2479
  className,
2480
+ initialErrorMessage = null,
2448
2481
  children,
2449
2482
  checkoutMode: checkoutModeProp,
2450
2483
  confirmLabel,
@@ -2467,7 +2500,7 @@ function FloPayCheckout({
2467
2500
  const [loadError, setLoadError] = (0, import_react8.useState)(null);
2468
2501
  const [currentMode, setCurrentMode] = (0, import_react8.useState)("full");
2469
2502
  const [confirmProcessing, setConfirmProcessing] = (0, import_react8.useState)(false);
2470
- const [modeError, setModeError] = (0, import_react8.useState)(null);
2503
+ const [modeError, setModeError] = (0, import_react8.useState)(initialErrorMessage);
2471
2504
  const [modeOverlayStatus, setModeOverlayStatus] = (0, import_react8.useState)(null);
2472
2505
  const [modeOverlayError, setModeOverlayError] = (0, import_react8.useState)(null);
2473
2506
  const [createSessionPatch, setCreateSessionPatch] = (0, import_react8.useState)(void 0);
@@ -2514,6 +2547,9 @@ function FloPayCheckout({
2514
2547
  setCreateSessionPatch(void 0);
2515
2548
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
2516
2549
  }, [baseCreateSessionHash]);
2550
+ (0, import_react8.useEffect)(() => {
2551
+ setModeError(initialErrorMessage);
2552
+ }, [initialErrorMessage]);
2517
2553
  const emitDecline = (0, import_react8.useCallback)(
2518
2554
  (method, input, overrides) => {
2519
2555
  onDeclineRef.current?.(buildDeclineEvent(method, input, overrides));
@@ -2527,19 +2563,45 @@ function FloPayCheckout({
2527
2563
  setModeOverlayStatus("processing");
2528
2564
  const activeSessionId2 = options?.sessionId ?? sess.id;
2529
2565
  try {
2530
- const result = await processSavedPaymentForMode({
2531
- billingApiUrl: resolvedBillingUrl,
2532
- sessionId: activeSessionId2,
2533
- session: sess
2534
- });
2535
- const paymentResult = result.type === "success" ? result.result : await handleSavedPaymentRedirectResult(result, {
2536
- flopay: flopayRef.current,
2537
- paypalFlopay: paypalFlopayRef.current,
2538
- attempt3DS: options?.attempt3DS,
2539
- billingApiUrl: resolvedBillingUrl,
2540
- sessionId: activeSessionId2,
2541
- session: sess
2542
- });
2566
+ const redirectResult = getRedirectResultFromCheckoutProcessError(options?.initialAutoProcessingError);
2567
+ let paymentResult;
2568
+ if (redirectResult) {
2569
+ paymentResult = await handleSavedPaymentRedirectResult(redirectResult, {
2570
+ flopay: flopayRef.current,
2571
+ paypalFlopay: paypalFlopayRef.current,
2572
+ attempt3DS: options?.attempt3DS,
2573
+ billingApiUrl: resolvedBillingUrl,
2574
+ sessionId: activeSessionId2,
2575
+ session: sess
2576
+ });
2577
+ } else if (options?.initialAutoProcessingError) {
2578
+ throw checkoutProcessErrorToFloPayError(
2579
+ options.initialAutoProcessingError,
2580
+ "Automatic payment failed. Please try again.",
2581
+ {
2582
+ checkoutMethod: options.initialAutoProcessingError.checkoutMethod
2583
+ }
2584
+ );
2585
+ } else if (options?.fromCreateSession) {
2586
+ if (options?.fallbackToFull) {
2587
+ setCurrentMode("full");
2588
+ }
2589
+ return false;
2590
+ } else {
2591
+ const result = await processSavedPaymentForMode({
2592
+ billingApiUrl: resolvedBillingUrl,
2593
+ sessionId: activeSessionId2,
2594
+ session: sess
2595
+ });
2596
+ paymentResult = result.type === "success" ? result.result : await handleSavedPaymentRedirectResult(result, {
2597
+ flopay: flopayRef.current,
2598
+ paypalFlopay: paypalFlopayRef.current,
2599
+ attempt3DS: options?.attempt3DS,
2600
+ billingApiUrl: resolvedBillingUrl,
2601
+ sessionId: activeSessionId2,
2602
+ session: sess
2603
+ });
2604
+ }
2543
2605
  setModeOverlayStatus("success");
2544
2606
  await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
2545
2607
  onCompleteRef.current?.(paymentResult);
@@ -2606,10 +2668,10 @@ function FloPayCheckout({
2606
2668
  }, [sessionIdProp]);
2607
2669
  (0, import_react8.useEffect)(() => {
2608
2670
  autoCheckoutAttempted.current = false;
2609
- setModeError(null);
2671
+ setModeError(initialErrorMessage);
2610
2672
  setModeOverlayError(null);
2611
2673
  setModeOverlayStatus(null);
2612
- }, [sessionIdProp, createSessionHash]);
2674
+ }, [createSessionHash, initialErrorMessage, sessionIdProp]);
2613
2675
  async function resolveInlineSession(params, cacheKey) {
2614
2676
  const api = new import_js2.PaymentAPI(resolvedBillingUrl);
2615
2677
  let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
@@ -2748,6 +2810,9 @@ function FloPayCheckout({
2748
2810
  await runSavedPaymentFlow(resolvedSession, {
2749
2811
  attempt3DS: true,
2750
2812
  fallbackToFull: true,
2813
+ fromCreateSession: true,
2814
+ initialAutoProcessingError: resolved.result.autoProcessingError,
2815
+ autoProcessingAttempted: resolved.result.autoProcessingAttempted,
2751
2816
  sessionId: resolved.sid || resolvedSession.id
2752
2817
  });
2753
2818
  return;
@@ -3568,7 +3633,11 @@ function CheckoutFormInner({
3568
3633
  onError?.(submitResult.error);
3569
3634
  return;
3570
3635
  }
3571
- const pmResult = await flopay.createPaymentMethod();
3636
+ const fullName = `${firstName ?? ""} ${lastName ?? ""}`.trim();
3637
+ const pmResult = await flopay.createPaymentMethod({
3638
+ ...email ? { email } : {},
3639
+ ...fullName ? { name: fullName } : {}
3640
+ });
3572
3641
  if (pmResult.error || !pmResult.paymentMethodId) {
3573
3642
  updateError(pmResult.error?.message ?? "Failed to create payment method.");
3574
3643
  return;
@@ -4071,7 +4140,7 @@ function FloPayAutomaticPaymentButton({
4071
4140
  setOverlayStatus("error");
4072
4141
  await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
4073
4142
  }, [emitDecline]);
4074
- const processResolvedSession = (0, import_react11.useCallback)(async (apiResult, resolvedSessionId) => {
4143
+ const processResolvedSession = (0, import_react11.useCallback)(async (apiResult, resolvedSessionId, options) => {
4075
4144
  const session = apiResult.data.session ?? null;
4076
4145
  if (!session) {
4077
4146
  throw new import_shared10.FloPayError("No session data returned", "api_error");
@@ -4086,19 +4155,15 @@ function FloPayAutomaticPaymentButton({
4086
4155
  return;
4087
4156
  }
4088
4157
  try {
4089
- const result = await processSavedPaymentForMode({
4090
- billingApiUrl: resolvedBillingUrl,
4091
- sessionId: resolvedSessionId ?? session.id,
4092
- session,
4093
- tokenizedData: automaticPaymentToken
4094
- });
4095
- let paymentResult = result.type === "success" ? result.result : null;
4096
- if (result.type !== "success") {
4158
+ let paymentResult = null;
4159
+ const redirectResult = getRedirectResultFromCheckoutProcessError(apiResult.autoProcessingError);
4160
+ const shouldTreatCreateSessionFlowAsServerAutoAttempt = options?.fromCreateSession && (apiResult.autoProcessingAttempted === true || !!apiResult.autoProcessingError || !!redirectResult);
4161
+ if (redirectResult) {
4097
4162
  const {
4098
4163
  publishableKey,
4099
4164
  paypalPublishableKey
4100
4165
  } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4101
- if (result.type === "paypal_redirect_required") {
4166
+ if (redirectResult.type === "paypal_redirect_required") {
4102
4167
  persistPayPalResumeState({
4103
4168
  sessionId: session.id || resolvedSessionId,
4104
4169
  publishableKey,
@@ -4114,7 +4179,7 @@ function FloPayAutomaticPaymentButton({
4114
4179
  billingApiUrl: resolvedBillingUrl,
4115
4180
  locale
4116
4181
  });
4117
- paymentResult = await handleSavedPaymentRedirectResult(result, {
4182
+ paymentResult = await handleSavedPaymentRedirectResult(redirectResult, {
4118
4183
  flopay,
4119
4184
  paypalFlopay,
4120
4185
  attempt3DS: true,
@@ -4122,9 +4187,66 @@ function FloPayAutomaticPaymentButton({
4122
4187
  sessionId: session.id || resolvedSessionId,
4123
4188
  session
4124
4189
  });
4125
- if (result.type === "paypal_redirect_required") {
4190
+ if (redirectResult.type === "paypal_redirect_required") {
4126
4191
  clearPayPalResumeState();
4127
4192
  }
4193
+ } else if (apiResult.autoProcessingError) {
4194
+ throw checkoutProcessErrorToFloPayError(
4195
+ apiResult.autoProcessingError,
4196
+ "Automatic payment failed. Please try again.",
4197
+ {
4198
+ checkoutMethod: apiResult.autoProcessingError.checkoutMethod
4199
+ }
4200
+ );
4201
+ } else if (shouldTreatCreateSessionFlowAsServerAutoAttempt) {
4202
+ throw checkoutProcessErrorToFloPayError(
4203
+ {
4204
+ type: "unknown",
4205
+ message: "Automatic payment failed. Please try again."
4206
+ },
4207
+ "Automatic payment failed. Please try again."
4208
+ );
4209
+ } else {
4210
+ const result = await processSavedPaymentForMode({
4211
+ billingApiUrl: resolvedBillingUrl,
4212
+ sessionId: resolvedSessionId ?? session.id,
4213
+ session,
4214
+ tokenizedData: automaticPaymentToken
4215
+ });
4216
+ paymentResult = result.type === "success" ? result.result : null;
4217
+ if (result.type !== "success") {
4218
+ const {
4219
+ publishableKey,
4220
+ paypalPublishableKey
4221
+ } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4222
+ if (result.type === "paypal_redirect_required") {
4223
+ persistPayPalResumeState({
4224
+ sessionId: session.id || resolvedSessionId,
4225
+ publishableKey,
4226
+ paypalPublishableKey
4227
+ });
4228
+ }
4229
+ const {
4230
+ flopay,
4231
+ paypalFlopay
4232
+ } = await loadSavedPaymentProviders({
4233
+ publishableKey,
4234
+ paypalPublishableKey,
4235
+ billingApiUrl: resolvedBillingUrl,
4236
+ locale
4237
+ });
4238
+ paymentResult = await handleSavedPaymentRedirectResult(result, {
4239
+ flopay,
4240
+ paypalFlopay,
4241
+ attempt3DS: true,
4242
+ billingApiUrl: resolvedBillingUrl,
4243
+ sessionId: session.id || resolvedSessionId,
4244
+ session
4245
+ });
4246
+ if (result.type === "paypal_redirect_required") {
4247
+ clearPayPalResumeState();
4248
+ }
4249
+ }
4128
4250
  }
4129
4251
  await showSuccess({
4130
4252
  result: paymentResult ? {
@@ -4141,7 +4263,8 @@ function FloPayAutomaticPaymentButton({
4141
4263
  } catch (err) {
4142
4264
  let floPayErr = normalizeSavedPaymentError(err);
4143
4265
  const fallbackSessionId = session.id || resolvedSessionId;
4144
- if (floPayErr.code === "authentication_required" && fallbackSessionId && automaticPaymentToken?.id && automaticPaymentToken.id.startsWith("pm_")) {
4266
+ const shouldTreatCreateSessionFlowAsServerAutoAttempt = options?.fromCreateSession && apiResult.autoProcessingAttempted === true;
4267
+ if (!shouldTreatCreateSessionFlowAsServerAutoAttempt && floPayErr.code === "authentication_required" && fallbackSessionId && automaticPaymentToken?.id && automaticPaymentToken.id.startsWith("pm_")) {
4145
4268
  try {
4146
4269
  const {
4147
4270
  publishableKey,
@@ -4238,7 +4361,9 @@ function FloPayAutomaticPaymentButton({
4238
4361
  }
4239
4362
  try {
4240
4363
  const result = await api.createAndFetchSession(createSessionDraft);
4241
- await processResolvedSession(result, result.data.session?.id ?? null);
4364
+ await processResolvedSession(result, result.data.session?.id ?? null, {
4365
+ fromCreateSession: true
4366
+ });
4242
4367
  } catch (err) {
4243
4368
  if (err instanceof import_shared10.FloPayError && err.code === "session_auto_completed") {
4244
4369
  await showSuccess({
@@ -4454,7 +4579,7 @@ function FloPayAutomaticPaymentButton({
4454
4579
  padding: "1.5rem",
4455
4580
  zIndex: 1100
4456
4581
  },
4457
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4582
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4458
4583
  "div",
4459
4584
  {
4460
4585
  style: {
@@ -4470,48 +4595,21 @@ function FloPayAutomaticPaymentButton({
4470
4595
  flexDirection: "column",
4471
4596
  gap: "1rem"
4472
4597
  },
4473
- children: [
4474
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4475
- "div",
4476
- {
4477
- style: {
4478
- display: "flex",
4479
- gap: "1rem",
4480
- alignItems: "flex-start"
4481
- },
4482
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4483
- "div",
4484
- {
4485
- style: {
4486
- flex: 1,
4487
- background: "#fef2f2",
4488
- color: "#b91c1c",
4489
- border: "1px solid #fecaca",
4490
- borderRadius: "12px",
4491
- padding: "0.75rem 1rem",
4492
- fontSize: "0.9rem",
4493
- lineHeight: 1.5
4494
- },
4495
- children: fallbackSession.errorMessage
4496
- }
4497
- )
4498
- }
4499
- ),
4500
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4501
- FloPayCheckout,
4502
- {
4503
- sessionId: fallbackSession.sessionId,
4504
- checkoutMode: "full",
4505
- billingApiUrl: resolvedBillingUrl,
4506
- fallbackPublishableKey,
4507
- cardTitleContent: null,
4508
- showSecurityFooter: false,
4509
- onComplete: handleFallbackComplete,
4510
- onError: handleFallbackError,
4511
- onDecline: handleFallbackDecline
4512
- }
4513
- )
4514
- ]
4598
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4599
+ FloPayCheckout,
4600
+ {
4601
+ sessionId: fallbackSession.sessionId,
4602
+ checkoutMode: "full",
4603
+ billingApiUrl: resolvedBillingUrl,
4604
+ fallbackPublishableKey,
4605
+ initialErrorMessage: fallbackSession.errorMessage,
4606
+ cardTitleContent: null,
4607
+ showSecurityFooter: false,
4608
+ onComplete: handleFallbackComplete,
4609
+ onError: handleFallbackError,
4610
+ onDecline: handleFallbackDecline
4611
+ }
4612
+ )
4515
4613
  }
4516
4614
  )
4517
4615
  }