@flopay/react 0.1.0 → 0.1.3

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
@@ -985,6 +985,13 @@ function FloPayCheckout({
985
985
  paymentMethodId: json.paymentMethodId
986
986
  };
987
987
  }
988
+ if (json?.gatewayErrorCode === "authentication_required") {
989
+ return {
990
+ type: "3ds_required",
991
+ threeDSecureToken: ""
992
+ // No client secret available
993
+ };
994
+ }
988
995
  throw new FloPayError2(
989
996
  json?.message ?? "Payment failed. Please try again.",
990
997
  "api_error"
@@ -992,6 +999,51 @@ function FloPayCheckout({
992
999
  },
993
1000
  [resolvedBillingUrl, sessionId, onComplete]
994
1001
  );
1002
+ const handleRedirectResult = useCallback2(
1003
+ async (redirectResult, sess, options) => {
1004
+ const stripe = flopayRef.current?.getRawProvider();
1005
+ if (!stripe) return false;
1006
+ if (redirectResult.type === "3ds_required") {
1007
+ if (!options?.attempt3DS || !redirectResult.threeDSecureToken) {
1008
+ setModeError("Your card requires authentication. Please enter your payment details below.");
1009
+ return false;
1010
+ }
1011
+ const { error: nextActionError, paymentIntent } = await stripe.handleNextAction({
1012
+ clientSecret: redirectResult.threeDSecureToken
1013
+ });
1014
+ if (nextActionError) {
1015
+ setModeError(nextActionError.message ?? "3DS authentication failed.");
1016
+ return false;
1017
+ }
1018
+ if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded")) {
1019
+ onComplete?.({ status: "succeeded", paymentIntentId: paymentIntent.id });
1020
+ return true;
1021
+ }
1022
+ return false;
1023
+ }
1024
+ if (redirectResult.type === "paypal_redirect_required") {
1025
+ const confirmParams = {
1026
+ return_url: window.location.href
1027
+ };
1028
+ if (redirectResult.paymentMethodId) {
1029
+ confirmParams["payment_method"] = redirectResult.paymentMethodId;
1030
+ }
1031
+ const { error } = await stripe.confirmPayment({
1032
+ clientSecret: redirectResult.threeDSecureToken,
1033
+ confirmParams,
1034
+ redirect: "if_required"
1035
+ });
1036
+ if (error) {
1037
+ setModeError(error.message ?? "PayPal authorization failed.");
1038
+ return false;
1039
+ }
1040
+ onComplete?.({ status: "succeeded" });
1041
+ return true;
1042
+ }
1043
+ return false;
1044
+ },
1045
+ [resolvedBillingUrl, sessionId, onComplete]
1046
+ );
995
1047
  useEffect4(() => {
996
1048
  let cancelled = false;
997
1049
  setIsLoading(true);
@@ -1026,29 +1078,11 @@ function FloPayCheckout({
1026
1078
  }
1027
1079
  if (cancelled) return;
1028
1080
  await stripeInitPromise;
1029
- if (redirectResult.type === "paypal_redirect_required") {
1030
- const stripeRef = flopayRef.current;
1031
- if (stripeRef) {
1032
- const rawStripe = stripeRef.getRawProvider();
1033
- if (rawStripe) {
1034
- const confirmParams = {
1035
- return_url: window.location.href
1036
- };
1037
- if (redirectResult.paymentMethodId) {
1038
- confirmParams["payment_method"] = redirectResult.paymentMethodId;
1039
- }
1040
- await rawStripe.confirmPayment({
1041
- clientSecret: redirectResult.threeDSecureToken,
1042
- confirmParams,
1043
- redirect: "if_required"
1044
- });
1045
- if (!cancelled) setIsLoading(false);
1046
- return;
1047
- }
1048
- }
1049
- }
1081
+ const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
1050
1082
  if (!cancelled) {
1051
- setCurrentMode("full");
1083
+ if (!handled) {
1084
+ setCurrentMode("full");
1085
+ }
1052
1086
  setIsLoading(false);
1053
1087
  }
1054
1088
  return;
@@ -1102,14 +1136,22 @@ function FloPayCheckout({
1102
1136
  locale,
1103
1137
  checkoutModeProp,
1104
1138
  onSessionCompleted,
1105
- processPaymentForMode
1139
+ processPaymentForMode,
1140
+ handleRedirectResult
1106
1141
  ]);
1107
1142
  const handleConfirmCheckout = useCallback2(async () => {
1108
1143
  if (confirmProcessing || !session) return;
1109
1144
  setConfirmProcessing(true);
1110
1145
  setModeError(null);
1111
1146
  try {
1112
- await processPaymentForMode(session);
1147
+ const redirectResult = await processPaymentForMode(session);
1148
+ if (!redirectResult) {
1149
+ return;
1150
+ }
1151
+ const handled = await handleRedirectResult(redirectResult, session);
1152
+ if (!handled) {
1153
+ setCurrentMode("full");
1154
+ }
1113
1155
  } catch (err) {
1114
1156
  const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(
1115
1157
  err instanceof Error ? err.message : "Payment failed",
@@ -1121,7 +1163,7 @@ function FloPayCheckout({
1121
1163
  } finally {
1122
1164
  setConfirmProcessing(false);
1123
1165
  }
1124
- }, [confirmProcessing, session, processPaymentForMode, onError]);
1166
+ }, [confirmProcessing, session, processPaymentForMode, handleRedirectResult, onError]);
1125
1167
  const providerOptions = useMemo3(() => {
1126
1168
  if (!unified || !session) return void 0;
1127
1169
  const opts = {
@@ -1231,33 +1273,50 @@ function FloPayCheckout({
1231
1273
  )
1232
1274
  ] }) }) });
1233
1275
  }
1234
- return /* @__PURE__ */ jsx4(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx4(FloPayProvider, { flopay, options: providerOptions, children: children ? /* @__PURE__ */ jsx4(
1235
- SessionInjector,
1236
- {
1237
- sessionId,
1238
- billingApiUrl: resolvedBillingUrl,
1239
- session,
1240
- children
1241
- }
1242
- ) : /* @__PURE__ */ jsx4(
1243
- SplitCardForm,
1244
- {
1245
- sessionId,
1246
- email: session?.customer?.email,
1247
- userId: session?.customer?.id,
1248
- firstName: session?.customer?.firstName,
1249
- lastName: session?.customer?.lastName,
1250
- totalAmount: session ? Math.round(buildCheckoutDisplayData(session).total * 100) : 0,
1251
- currency: session?.currency?.toLowerCase() ?? "usd",
1252
- onComplete,
1253
- onError,
1254
- showPayPal,
1255
- showApplePay,
1256
- showGooglePay,
1257
- submitLabel,
1258
- className
1259
- }
1260
- ) }) });
1276
+ return /* @__PURE__ */ jsx4(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs2(FloPayProvider, { flopay, options: providerOptions, children: [
1277
+ modeError && /* @__PURE__ */ jsx4(
1278
+ "div",
1279
+ {
1280
+ style: {
1281
+ padding: "0.75rem 1rem",
1282
+ marginBottom: "0.75rem",
1283
+ backgroundColor: "#FEF3C7",
1284
+ border: "1px solid #F59E0B",
1285
+ borderRadius: "8px",
1286
+ color: "#92400E",
1287
+ fontSize: "0.875rem"
1288
+ },
1289
+ children: modeError
1290
+ }
1291
+ ),
1292
+ children ? /* @__PURE__ */ jsx4(
1293
+ SessionInjector,
1294
+ {
1295
+ sessionId,
1296
+ billingApiUrl: resolvedBillingUrl,
1297
+ session,
1298
+ children
1299
+ }
1300
+ ) : /* @__PURE__ */ jsx4(
1301
+ SplitCardForm,
1302
+ {
1303
+ sessionId,
1304
+ email: session?.customer?.email,
1305
+ userId: session?.customer?.id,
1306
+ firstName: session?.customer?.firstName,
1307
+ lastName: session?.customer?.lastName,
1308
+ totalAmount: session ? Math.round(buildCheckoutDisplayData(session).total * 100) : 0,
1309
+ currency: session?.currency?.toLowerCase() ?? "usd",
1310
+ onComplete,
1311
+ onError,
1312
+ showPayPal,
1313
+ showApplePay,
1314
+ showGooglePay,
1315
+ submitLabel,
1316
+ className
1317
+ }
1318
+ )
1319
+ ] }) });
1261
1320
  }
1262
1321
  function SessionInjector({
1263
1322
  sessionId,