@flopay/react 0.5.16 → 0.5.18

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
@@ -101,7 +101,7 @@ function FloPayProvider({
101
101
 
102
102
  // src/flopay-checkout.tsx
103
103
  import React7, { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
104
- import { PaymentAPI as PaymentAPI2 } from "@flopay/js";
104
+ import { PaymentAPI as PaymentAPI3 } from "@flopay/js";
105
105
  import { SDK_VERSION, FloPayError as FloPayError4, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
106
106
 
107
107
  // src/card-button-content.tsx
@@ -248,6 +248,7 @@ import {
248
248
  isAVSFieldVisible,
249
249
  getStateFromPostalCode
250
250
  } from "@flopay/shared";
251
+ import { PaymentAPI } from "@flopay/js";
251
252
  import { forwardRef, useCallback as useCallback2, useContext as useContext3, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
252
253
 
253
254
  // src/hooks.ts
@@ -1449,53 +1450,47 @@ function SplitCardFormInner({
1449
1450
  const resolvedCompletionPaymentMethodId = overrides?.completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
1450
1451
  const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
1451
1452
  try {
1452
- const response = await fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
1453
- method: "POST",
1454
- headers: {
1455
- "Content-Type": "application/json",
1456
- "x-user-id": effectiveAccount.userId ?? ""
1453
+ const api = new PaymentAPI(baseUrl);
1454
+ const response = await api.processPayment(effectiveAccount.userId ?? "", {
1455
+ sessionId: effectiveSessionId,
1456
+ tokenizedData: requestTokenizedBody,
1457
+ accountData: {
1458
+ userId: effectiveAccount.userId ?? "",
1459
+ email: effectiveAccount.email ?? "",
1460
+ firstName: effectiveAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
1461
+ lastName: effectiveAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
1462
+ ...avsConfig ? (() => {
1463
+ const c = selectedCountryRef.current;
1464
+ const stateVisible = isAVSFieldVisible(avsConfig.state, c);
1465
+ const line1Visible = isAVSFieldVisible(avsConfig.address_line_1, c);
1466
+ const zipVisible = isAVSFieldVisible(avsConfig.postal_code, c);
1467
+ const derivedState = line1Visible && !stateVisible && zipVisible ? getStateFromPostalCode(c, zipCodeRef.current ?? "") : null;
1468
+ const stateValue2 = stateVisible ? stateRef.current : derivedState;
1469
+ return {
1470
+ country: c,
1471
+ ...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
1472
+ ...isAVSFieldVisible(avsConfig.city, c) && cityRef.current ? { city: cityRef.current } : {},
1473
+ ...stateValue2 ? { state: stateValue2 } : {},
1474
+ ...line1Visible && addressLine1Ref.current ? { addressLine1: addressLine1Ref.current } : {},
1475
+ ...isAVSFieldVisible(avsConfig.address_line_2, c) && addressLine2Ref.current ? { addressLine2: addressLine2Ref.current } : {}
1476
+ };
1477
+ })() : {}
1457
1478
  },
1458
- body: JSON.stringify({
1459
- sessionId: effectiveSessionId,
1460
- tokenizedData: requestTokenizedBody,
1461
- accountData: {
1462
- userId: effectiveAccount.userId ?? "",
1463
- email: effectiveAccount.email ?? "",
1464
- firstName: effectiveAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
1465
- lastName: effectiveAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
1466
- ...avsConfig ? (() => {
1467
- const c = selectedCountryRef.current;
1468
- const stateVisible = isAVSFieldVisible(avsConfig.state, c);
1469
- const line1Visible = isAVSFieldVisible(avsConfig.address_line_1, c);
1470
- const zipVisible = isAVSFieldVisible(avsConfig.postal_code, c);
1471
- const derivedState = line1Visible && !stateVisible && zipVisible ? getStateFromPostalCode(c, zipCodeRef.current ?? "") : null;
1472
- const stateValue2 = stateVisible ? stateRef.current : derivedState;
1473
- return {
1474
- country: c,
1475
- ...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
1476
- ...isAVSFieldVisible(avsConfig.city, c) && cityRef.current ? { city: cityRef.current } : {},
1477
- ...stateValue2 ? { state: stateValue2 } : {},
1478
- ...line1Visible && addressLine1Ref.current ? { addressLine1: addressLine1Ref.current } : {},
1479
- ...isAVSFieldVisible(avsConfig.address_line_2, c) && addressLine2Ref.current ? { addressLine2: addressLine2Ref.current } : {}
1480
- };
1481
- })() : {}
1482
- },
1483
- chv,
1484
- // Checkout analytics metadata
1485
- avsCheck: avsCheckProp ?? false,
1486
- checkoutType: checkoutTypeProp,
1487
- checkoutLayout: checkoutLayoutProp,
1488
- // Resolved exposure: which fields were actually shown for the active country.
1489
- // Country-scoped rules (e.g. ['US', 'CA']) are flattened to booleans here.
1490
- avsConfig: avsConfig ? {
1491
- country: isAVSFieldVisible(avsConfig.country, selectedCountryRef.current),
1492
- postal_code: isAVSFieldVisible(avsConfig.postal_code, selectedCountryRef.current),
1493
- address_line_1: isAVSFieldVisible(avsConfig.address_line_1, selectedCountryRef.current),
1494
- address_line_2: isAVSFieldVisible(avsConfig.address_line_2, selectedCountryRef.current),
1495
- city: isAVSFieldVisible(avsConfig.city, selectedCountryRef.current),
1496
- state: isAVSFieldVisible(avsConfig.state, selectedCountryRef.current)
1497
- } : void 0
1498
- })
1479
+ chv,
1480
+ // Checkout analytics metadata
1481
+ avsCheck: avsCheckProp ?? false,
1482
+ checkoutType: checkoutTypeProp,
1483
+ checkoutLayout: checkoutLayoutProp,
1484
+ // Resolved exposure: which fields were actually shown for the active country.
1485
+ // Country-scoped rules (e.g. ['US', 'CA']) are flattened to booleans here.
1486
+ avsConfig: avsConfig ? {
1487
+ country: isAVSFieldVisible(avsConfig.country, selectedCountryRef.current),
1488
+ postal_code: isAVSFieldVisible(avsConfig.postal_code, selectedCountryRef.current),
1489
+ address_line_1: isAVSFieldVisible(avsConfig.address_line_1, selectedCountryRef.current),
1490
+ address_line_2: isAVSFieldVisible(avsConfig.address_line_2, selectedCountryRef.current),
1491
+ city: isAVSFieldVisible(avsConfig.city, selectedCountryRef.current),
1492
+ state: isAVSFieldVisible(avsConfig.state, selectedCountryRef.current)
1493
+ } : void 0
1499
1494
  });
1500
1495
  if (response.ok) {
1501
1496
  markSessionRecentlyCompleted(effectiveSessionId);
@@ -2398,7 +2393,7 @@ function SplitCardFormInner({
2398
2393
  }
2399
2394
 
2400
2395
  // src/saved-payment-flow.ts
2401
- import { loadFloPay, PaymentAPI } from "@flopay/js";
2396
+ import { loadFloPay, PaymentAPI as PaymentAPI2 } from "@flopay/js";
2402
2397
  import { FloPayError as FloPayError3 } from "@flopay/shared";
2403
2398
  var DEFAULT_SAVED_PAYMENT_DECLINE_METHOD = "card";
2404
2399
  function getRedirectResultFromCheckoutProcessError(error) {
@@ -2506,7 +2501,7 @@ async function recover3DSRedirectResult({
2506
2501
  return null;
2507
2502
  }
2508
2503
  try {
2509
- const api = new PaymentAPI(billingApiUrl);
2504
+ const api = new PaymentAPI2(billingApiUrl);
2510
2505
  const unified = await api.getUnifiedCheckoutSession(sessionId);
2511
2506
  const refreshedToken = unified.provider === "stripe" ? unified.data.stripe?.clientSecret : void 0;
2512
2507
  if (isStripePaymentIntentClientSecret(refreshedToken)) {
@@ -2534,25 +2529,19 @@ async function processSavedPaymentForMode({
2534
2529
  const lastName = session.customer?.lastName ?? session.accountData?.lastName ?? "";
2535
2530
  const country = session.customer?.country ?? session.accountData?.country ?? void 0;
2536
2531
  const zip = session.customer?.zip ?? session.accountData?.zip ?? void 0;
2537
- const response = await retryOnceOnFetchFailure(() => fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
2538
- method: "POST",
2539
- headers: {
2540
- "Content-Type": "application/json",
2541
- "x-user-id": customerId
2532
+ const api = new PaymentAPI2(baseUrl);
2533
+ const response = await retryOnceOnFetchFailure(() => api.processPayment(customerId, {
2534
+ sessionId: resolvedSessionId,
2535
+ tokenizedData,
2536
+ accountData: {
2537
+ userId: customerId,
2538
+ email: customerEmail,
2539
+ firstName,
2540
+ lastName,
2541
+ country,
2542
+ zip
2542
2543
  },
2543
- body: JSON.stringify({
2544
- sessionId: resolvedSessionId,
2545
- tokenizedData,
2546
- accountData: {
2547
- userId: customerId,
2548
- email: customerEmail,
2549
- firstName,
2550
- lastName,
2551
- country,
2552
- zip
2553
- },
2554
- returnUrl: returnUrl ?? resolveSavedPaymentReturnUrl(session)
2555
- })
2544
+ returnUrl: returnUrl ?? resolveSavedPaymentReturnUrl(session)
2556
2545
  }));
2557
2546
  if (response.ok) {
2558
2547
  return {
@@ -2625,7 +2614,7 @@ async function processSavedPaymentWithIntent({
2625
2614
  { checkoutMethod: "card" }
2626
2615
  );
2627
2616
  }
2628
- const api = new PaymentAPI(billingApiUrl);
2617
+ const api = new PaymentAPI2(billingApiUrl);
2629
2618
  const intentResponse = await retryOnceOnFetchFailure(() => api.createPaymentIntent(
2630
2619
  sessionId,
2631
2620
  customerEmail,
@@ -3132,6 +3121,17 @@ function FloPayCheckout({
3132
3121
  if (redirectResult.type === "paypal_redirect_required") {
3133
3122
  clearPayPalResumeState();
3134
3123
  }
3124
+ } else if (options?.initialAutoProcessingPending) {
3125
+ const api = new PaymentAPI3(resolvedBillingUrl);
3126
+ const completed = await api.waitForCheckoutSessionCompletion(options.initialAutoProcessingPending.sessionId, {
3127
+ initialDelayMs: options.initialAutoProcessingPending.retryAfterMs
3128
+ });
3129
+ if (completed.data.session?.status !== "complete") {
3130
+ throw new FloPayError4("Automatic payment failed. Please try again.", "api_error", {
3131
+ code: completed.data.session?.status === "expired" ? "checkout_session_expired" : "checkout_processing_timeout"
3132
+ });
3133
+ }
3134
+ paymentResult = { status: "succeeded" };
3135
3135
  } else if (options?.initialAutoProcessingError) {
3136
3136
  throw checkoutProcessErrorToFloPayError(
3137
3137
  options.initialAutoProcessingError,
@@ -3274,7 +3274,7 @@ function FloPayCheckout({
3274
3274
  const paymentMethodId = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
3275
3275
  let finalResultStatus = resultStatus;
3276
3276
  if (resumeState.sessionId) {
3277
- const resumeApi = new PaymentAPI2(resolvedBillingUrl);
3277
+ const resumeApi = new PaymentAPI3(resolvedBillingUrl);
3278
3278
  const resumeSessionResult = await resumeApi.getUnifiedCheckoutSession(resumeState.sessionId);
3279
3279
  const resumeSession = resumeSessionResult.data.session;
3280
3280
  if (resumeSession && resumeSession.status !== "complete") {
@@ -3368,7 +3368,7 @@ function FloPayCheckout({
3368
3368
  setModeOverlayStatus(null);
3369
3369
  }, [createSessionHash, initialErrorMessage, sessionIdProp]);
3370
3370
  async function resolveInlineSession(params, cacheKey) {
3371
- const api = new PaymentAPI2(resolvedBillingUrl);
3371
+ const api = new PaymentAPI3(resolvedBillingUrl);
3372
3372
  let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
3373
3373
  let realResult = null;
3374
3374
  if (sid) {
@@ -3520,6 +3520,7 @@ function FloPayCheckout({
3520
3520
  fallbackToFull: true,
3521
3521
  fromCreateSession: true,
3522
3522
  initialAutoProcessingError: resolved.result.autoProcessingError,
3523
+ initialAutoProcessingPending: resolved.result.autoProcessingPending,
3523
3524
  autoProcessingAttempted: resolved.result.autoProcessingAttempted,
3524
3525
  sessionId: resolved.sid || resolvedSession.id
3525
3526
  });
@@ -3558,7 +3559,7 @@ function FloPayCheckout({
3558
3559
  setIsLoading(true);
3559
3560
  async function init() {
3560
3561
  try {
3561
- const api = new PaymentAPI2(resolvedBillingUrl);
3562
+ const api = new PaymentAPI3(resolvedBillingUrl);
3562
3563
  const result = await api.getUnifiedCheckoutSession(activeSessionId);
3563
3564
  if (cancelled) return;
3564
3565
  setUnified(result);
@@ -3572,6 +3573,11 @@ function FloPayCheckout({
3572
3573
  onSessionCompletedRef.current?.(sess.successUrl ?? "");
3573
3574
  return;
3574
3575
  }
3576
+ if (sess.status === "expired") {
3577
+ throw new FloPayError4("Checkout session has expired.", "api_error", {
3578
+ code: "checkout_session_expired"
3579
+ });
3580
+ }
3575
3581
  const effectiveMode = checkoutModeProp ?? sess.checkoutMode ?? "full";
3576
3582
  setCurrentMode(effectiveMode);
3577
3583
  const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
@@ -3743,8 +3749,10 @@ function FloPayCheckout({
3743
3749
  ] });
3744
3750
  }
3745
3751
  if (loadError) {
3746
- if (errorNode) return /* @__PURE__ */ jsx7(Fragment3, { children: errorNode(loadError) });
3747
- return /* @__PURE__ */ jsx7(
3752
+ if (errorNode) {
3753
+ return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
3754
+ }
3755
+ return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(
3748
3756
  "div",
3749
3757
  {
3750
3758
  style: {
@@ -3755,7 +3763,7 @@ function FloPayCheckout({
3755
3763
  },
3756
3764
  children: loadError.message
3757
3765
  }
3758
- );
3766
+ ) });
3759
3767
  }
3760
3768
  if (shouldShowInterimButtons) {
3761
3769
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
@@ -4117,6 +4125,7 @@ function InterimButtonsView({
4117
4125
  }
4118
4126
 
4119
4127
  // src/checkout-form.tsx
4128
+ import { PaymentAPI as PaymentAPI4 } from "@flopay/js";
4120
4129
  import { FloPayError as FloPayError5 } from "@flopay/shared";
4121
4130
  import { forwardRef as forwardRef2, useCallback as useCallback4, useEffect as useEffect6, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
4122
4131
  import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
@@ -4179,23 +4188,17 @@ function CheckoutFormInner({
4179
4188
  const resolvedCompletionPaymentMethodId = completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
4180
4189
  const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
4181
4190
  try {
4182
- const response = await fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
4183
- method: "POST",
4184
- headers: {
4185
- "Content-Type": "application/json",
4186
- "x-user-id": userId ?? ""
4191
+ const api = new PaymentAPI4(baseUrl);
4192
+ const response = await api.processPayment(userId ?? "", {
4193
+ sessionId,
4194
+ tokenizedData: requestTokenizedBody,
4195
+ accountData: {
4196
+ userId: userId ?? "",
4197
+ email: email ?? "",
4198
+ firstName: firstName ?? "",
4199
+ lastName: lastName ?? ""
4187
4200
  },
4188
- body: JSON.stringify({
4189
- sessionId,
4190
- tokenizedData: requestTokenizedBody,
4191
- accountData: {
4192
- userId: userId ?? "",
4193
- email: email ?? "",
4194
- firstName: firstName ?? "",
4195
- lastName: lastName ?? ""
4196
- },
4197
- chv
4198
- })
4201
+ chv
4199
4202
  });
4200
4203
  if (response.ok) {
4201
4204
  onComplete?.({
@@ -4463,6 +4466,7 @@ function CheckoutFormInner({
4463
4466
  }
4464
4467
 
4465
4468
  // src/paypal-button.tsx
4469
+ import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
4466
4470
  import { FloPayError as FloPayError6 } from "@flopay/shared";
4467
4471
  import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef4, useState as useState6 } from "react";
4468
4472
  import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
@@ -4489,23 +4493,17 @@ function PayPalButton({
4489
4493
  const processPaymentInternal = useCallback5(
4490
4494
  async (tokenizedBody) => {
4491
4495
  try {
4492
- const response = await fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
4493
- method: "POST",
4494
- headers: {
4495
- "Content-Type": "application/json",
4496
- "x-user-id": userId ?? ""
4496
+ const api = new PaymentAPI5(baseUrl);
4497
+ const response = await api.processPayment(userId ?? "", {
4498
+ sessionId,
4499
+ tokenizedData: tokenizedBody,
4500
+ accountData: {
4501
+ userId: userId ?? "",
4502
+ email: email ?? "",
4503
+ firstName: firstName ?? "",
4504
+ lastName: lastName ?? ""
4497
4505
  },
4498
- body: JSON.stringify({
4499
- sessionId,
4500
- tokenizedData: tokenizedBody,
4501
- accountData: {
4502
- userId: userId ?? "",
4503
- email: email ?? "",
4504
- firstName: firstName ?? "",
4505
- lastName: lastName ?? ""
4506
- },
4507
- chv
4508
- })
4506
+ chv
4509
4507
  });
4510
4508
  if (response.ok) {
4511
4509
  onComplete?.();
@@ -4657,7 +4655,7 @@ function PayPalButton({
4657
4655
 
4658
4656
  // src/automatic-payment-button.tsx
4659
4657
  import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef5, useState as useState7 } from "react";
4660
- import { PaymentAPI as PaymentAPI3 } from "@flopay/js";
4658
+ import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
4661
4659
  import { FloPayError as FloPayError7, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3 } from "@flopay/shared";
4662
4660
  import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
4663
4661
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
@@ -4894,11 +4892,39 @@ function FloPayAutomaticPaymentButton({
4894
4892
  });
4895
4893
  return;
4896
4894
  }
4895
+ if (session.status === "expired") {
4896
+ throw new FloPayError7("Checkout session has expired.", "api_error", {
4897
+ code: "checkout_session_expired"
4898
+ });
4899
+ }
4897
4900
  try {
4898
4901
  let paymentResult = null;
4899
4902
  const redirectResult = getRedirectResultFromCheckoutProcessError(apiResult.autoProcessingError);
4900
4903
  const shouldTreatCreateSessionFlowAsServerAutoAttempt = options?.fromCreateSession && (apiResult.autoProcessingAttempted === true || !!apiResult.autoProcessingError || !!redirectResult);
4901
4904
  const shouldRetryPayPalClientSide = checkoutMethod === "paypal" && automaticPaymentToken?.isPaypal === true && options?.fromCreateSession === true;
4905
+ if (apiResult.autoProcessingPending) {
4906
+ const api = new PaymentAPI6(resolvedBillingUrl);
4907
+ const completed = await api.waitForCheckoutSessionCompletion(apiResult.autoProcessingPending.sessionId, {
4908
+ initialDelayMs: apiResult.autoProcessingPending.retryAfterMs
4909
+ });
4910
+ const completedSession = completed.data.session;
4911
+ if (!completedSession || completedSession.status !== "complete") {
4912
+ throw new FloPayError7("Automatic payment failed. Please try again.", "api_error", {
4913
+ code: completedSession?.status === "expired" ? "checkout_session_expired" : "checkout_processing_timeout"
4914
+ });
4915
+ }
4916
+ await showSuccess({
4917
+ result: {
4918
+ status: "succeeded",
4919
+ paymentMethodId: paymentMethodId ?? void 0,
4920
+ checkoutMethod
4921
+ },
4922
+ session: completedSession,
4923
+ sessionId: completedSession.id || resolvedSessionId,
4924
+ autoCompleted: false
4925
+ });
4926
+ return;
4927
+ }
4902
4928
  if (redirectResult) {
4903
4929
  const {
4904
4930
  publishableKey,
@@ -5109,7 +5135,7 @@ function FloPayAutomaticPaymentButton({
5109
5135
  setOverlayError(null);
5110
5136
  setOverlayStatus("processing");
5111
5137
  try {
5112
- const api = new PaymentAPI3(resolvedBillingUrl);
5138
+ const api = new PaymentAPI6(resolvedBillingUrl);
5113
5139
  if (sessionId) {
5114
5140
  const result = await api.getUnifiedCheckoutSession(sessionId);
5115
5141
  await processResolvedSession(result, sessionId);
@@ -5239,7 +5265,7 @@ function FloPayAutomaticPaymentButton({
5239
5265
  { checkoutMethod: "paypal" }
5240
5266
  );
5241
5267
  }
5242
- const api = new PaymentAPI3(resolvedBillingUrl);
5268
+ const api = new PaymentAPI6(resolvedBillingUrl);
5243
5269
  const sessionResult = await api.getUnifiedCheckoutSession(resumeState.sessionId);
5244
5270
  let resumeSession = sessionResult.data.session;
5245
5271
  if (!resumeSession) {