@doujins/payments-ui 0.0.13 → 0.0.14

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
@@ -2520,6 +2520,143 @@ var SolanaPaymentView = ({
2520
2520
  renderBody()
2521
2521
  ] });
2522
2522
  };
2523
+ var useSubscriptionActions = () => {
2524
+ const { services } = usePaymentContext();
2525
+ const ensurePrice = (priceId) => {
2526
+ if (!priceId) {
2527
+ throw new Error("payments-ui: priceId is required for subscription actions");
2528
+ }
2529
+ return priceId;
2530
+ };
2531
+ const subscribeWithCard = React3.useCallback(
2532
+ async ({
2533
+ priceId,
2534
+ processor = "nmi",
2535
+ provider,
2536
+ paymentToken,
2537
+ billing
2538
+ }) => {
2539
+ const payload = {
2540
+ priceId: ensurePrice(priceId),
2541
+ paymentToken,
2542
+ processor,
2543
+ provider,
2544
+ email: billing.email,
2545
+ firstName: billing.firstName,
2546
+ lastName: billing.lastName,
2547
+ address1: billing.address1,
2548
+ city: billing.city,
2549
+ state: billing.stateRegion,
2550
+ zipCode: billing.postalCode,
2551
+ country: billing.country
2552
+ };
2553
+ return services.subscriptions.subscribe("nmi", payload);
2554
+ },
2555
+ [services]
2556
+ );
2557
+ const subscribeWithSavedMethod = React3.useCallback(
2558
+ async ({
2559
+ priceId,
2560
+ processor = "nmi",
2561
+ provider,
2562
+ paymentMethodId,
2563
+ email
2564
+ }) => {
2565
+ const payload = {
2566
+ priceId: ensurePrice(priceId),
2567
+ paymentMethodId,
2568
+ processor,
2569
+ provider,
2570
+ email
2571
+ };
2572
+ return services.subscriptions.subscribe("nmi", payload);
2573
+ },
2574
+ [services]
2575
+ );
2576
+ const subscribeWithCCBill = React3.useCallback(
2577
+ async ({
2578
+ priceId,
2579
+ email,
2580
+ firstName,
2581
+ lastName,
2582
+ zipCode,
2583
+ country,
2584
+ processor = "ccbill"
2585
+ }) => {
2586
+ const payload = {
2587
+ priceId: ensurePrice(priceId),
2588
+ email,
2589
+ firstName,
2590
+ lastName,
2591
+ zipCode,
2592
+ country,
2593
+ processor
2594
+ };
2595
+ return services.subscriptions.subscribe("ccbill", payload);
2596
+ },
2597
+ [services]
2598
+ );
2599
+ const generateFlexFormUrl = React3.useCallback(
2600
+ async ({
2601
+ priceId,
2602
+ firstName,
2603
+ lastName,
2604
+ address1,
2605
+ city,
2606
+ state,
2607
+ zipCode,
2608
+ country
2609
+ }) => {
2610
+ const payload = {
2611
+ price_id: ensurePrice(priceId),
2612
+ first_name: firstName,
2613
+ last_name: lastName,
2614
+ address1,
2615
+ city,
2616
+ state,
2617
+ zip_code: zipCode,
2618
+ country
2619
+ };
2620
+ return services.subscriptions.generateFlexFormUrl(payload);
2621
+ },
2622
+ [services]
2623
+ );
2624
+ return {
2625
+ subscribeWithCard,
2626
+ subscribeWithSavedMethod,
2627
+ subscribeWithCCBill,
2628
+ generateFlexFormUrl
2629
+ };
2630
+ };
2631
+
2632
+ // src/hooks/useAlternativePaymentProvider.ts
2633
+ var useAlternativePaymentProvider = () => {
2634
+ const [isLoading, setIsLoading] = React3.useState(false);
2635
+ const [error, setError] = React3.useState(null);
2636
+ const { generateFlexFormUrl } = useSubscriptionActions();
2637
+ const openFlexForm = React3.useCallback(
2638
+ async (payload) => {
2639
+ setIsLoading(true);
2640
+ setError(null);
2641
+ try {
2642
+ const response = await generateFlexFormUrl(payload);
2643
+ if (response?.iframe_url) {
2644
+ window.location.href = response.iframe_url;
2645
+ } else {
2646
+ throw new Error("Unable to launch payment provider.");
2647
+ }
2648
+ } catch (err) {
2649
+ const message = err instanceof Error ? err.message : "Failed to open payment provider.";
2650
+ setError(message);
2651
+ console.error("[payments-ui] failed to open alternative payment provider", err);
2652
+ } finally {
2653
+ setIsLoading(false);
2654
+ }
2655
+ },
2656
+ [generateFlexFormUrl]
2657
+ );
2658
+ return { openFlexForm, isLoading, error };
2659
+ };
2523
2660
  var PaymentExperience = ({
2524
2661
  priceId,
2525
2662
  usdAmount,
@@ -2528,6 +2665,7 @@ var PaymentExperience = ({
2528
2665
  enableNewCard = true,
2529
2666
  enableStoredMethods = true,
2530
2667
  enableSolanaPay = true,
2668
+ enableAlternativePayments = true,
2531
2669
  onSolanaSuccess,
2532
2670
  onSolanaError,
2533
2671
  initialMode = "cards"
@@ -2544,7 +2682,14 @@ var PaymentExperience = ({
2544
2682
  const [savedError, setSavedError] = React3.useState(null);
2545
2683
  const [newCardStatus, setNewCardStatus] = React3.useState("idle");
2546
2684
  const [newCardError, setNewCardError] = React3.useState(null);
2685
+ const [billingDetails, setBillingDetails] = React3.useState(null);
2686
+ const [alternativePaymentError, setAlternativePaymentError] = React3.useState(null);
2547
2687
  const { notifyStatus, notifySuccess, notifyError } = usePaymentNotifications();
2688
+ const {
2689
+ openFlexForm,
2690
+ isLoading: flexFormLoading,
2691
+ error: flexFormError
2692
+ } = useAlternativePaymentProvider();
2548
2693
  React3.useEffect(() => {
2549
2694
  setActiveTab(showStored ? "saved" : "new");
2550
2695
  }, [showStored]);
@@ -2624,6 +2769,48 @@ var PaymentExperience = ({
2624
2769
  },
2625
2770
  [onSolanaError]
2626
2771
  );
2772
+ const handleAlternativePayment = React3.useCallback(() => {
2773
+ if (!enableAlternativePayments || !priceId) {
2774
+ return;
2775
+ }
2776
+ if (!billingDetails) {
2777
+ setAlternativePaymentError("Enter your billing details first.");
2778
+ return;
2779
+ }
2780
+ const requiredFields = [
2781
+ "firstName",
2782
+ "lastName",
2783
+ "address1",
2784
+ "city",
2785
+ "stateRegion",
2786
+ "postalCode",
2787
+ "country"
2788
+ ];
2789
+ const missingField = requiredFields.find((field) => {
2790
+ const value = billingDetails[field];
2791
+ return typeof value !== "string" || value.trim().length === 0;
2792
+ });
2793
+ if (missingField) {
2794
+ setAlternativePaymentError("Please complete your billing address before continuing.");
2795
+ return;
2796
+ }
2797
+ setAlternativePaymentError(null);
2798
+ openFlexForm({
2799
+ priceId,
2800
+ firstName: billingDetails.firstName,
2801
+ lastName: billingDetails.lastName,
2802
+ address1: billingDetails.address1,
2803
+ city: billingDetails.city,
2804
+ state: billingDetails.stateRegion ?? "",
2805
+ zipCode: billingDetails.postalCode,
2806
+ country: billingDetails.country
2807
+ });
2808
+ }, [
2809
+ billingDetails,
2810
+ enableAlternativePayments,
2811
+ openFlexForm,
2812
+ priceId
2813
+ ]);
2627
2814
  const renderSavedTab = () => {
2628
2815
  if (!showStored) {
2629
2816
  return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Saved payment methods are unavailable right now. Add a new card to get started." });
@@ -2662,7 +2849,8 @@ var PaymentExperience = ({
2662
2849
  submitLabel: "Pay now",
2663
2850
  externalError: newCardError,
2664
2851
  onTokenize: handleNewCardTokenize,
2665
- submitting: newCardStatus === "processing"
2852
+ submitting: newCardStatus === "processing",
2853
+ onBillingChange: setBillingDetails
2666
2854
  }
2667
2855
  );
2668
2856
  };
@@ -2690,6 +2878,19 @@ var PaymentExperience = ({
2690
2878
  ] });
2691
2879
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6 pt-4", children: [
2692
2880
  mode === "cards" && renderCardExperience(),
2881
+ mode === "cards" && enableAlternativePayments && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 text-center text-sm text-muted-foreground", children: [
2882
+ /* @__PURE__ */ jsxRuntime.jsx(
2883
+ "button",
2884
+ {
2885
+ type: "button",
2886
+ className: "text-primary underline-offset-4 hover:underline disabled:opacity-60",
2887
+ onClick: handleAlternativePayment,
2888
+ disabled: flexFormLoading || !priceId,
2889
+ children: flexFormLoading ? "Preparing alternative checkout\u2026" : "Prefer a different processor? Pay with CCBill"
2890
+ }
2891
+ ),
2892
+ (alternativePaymentError || flexFormError) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: alternativePaymentError || flexFormError })
2893
+ ] }),
2693
2894
  mode === "solana" && enableSolanaPay && /* @__PURE__ */ jsxRuntime.jsx(
2694
2895
  SolanaPaymentView,
2695
2896
  {
@@ -2730,114 +2931,6 @@ var SubscriptionSuccessDialog = ({
2730
2931
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsxRuntime.jsx(Button, { className: "w-full", onClick: onClose, children: "Continue exploring" }) })
2731
2932
  ] }) });
2732
2933
  };
2733
- var useSubscriptionActions = () => {
2734
- const { services } = usePaymentContext();
2735
- const ensurePrice = (priceId) => {
2736
- if (!priceId) {
2737
- throw new Error("payments-ui: priceId is required for subscription actions");
2738
- }
2739
- return priceId;
2740
- };
2741
- const subscribeWithCard = React3.useCallback(
2742
- async ({
2743
- priceId,
2744
- processor = "nmi",
2745
- provider,
2746
- paymentToken,
2747
- billing
2748
- }) => {
2749
- const payload = {
2750
- priceId: ensurePrice(priceId),
2751
- paymentToken,
2752
- processor,
2753
- provider,
2754
- email: billing.email,
2755
- firstName: billing.firstName,
2756
- lastName: billing.lastName,
2757
- address1: billing.address1,
2758
- city: billing.city,
2759
- state: billing.stateRegion,
2760
- zipCode: billing.postalCode,
2761
- country: billing.country
2762
- };
2763
- return services.subscriptions.subscribe("nmi", payload);
2764
- },
2765
- [services]
2766
- );
2767
- const subscribeWithSavedMethod = React3.useCallback(
2768
- async ({
2769
- priceId,
2770
- processor = "nmi",
2771
- provider,
2772
- paymentMethodId,
2773
- email
2774
- }) => {
2775
- const payload = {
2776
- priceId: ensurePrice(priceId),
2777
- paymentMethodId,
2778
- processor,
2779
- provider,
2780
- email
2781
- };
2782
- return services.subscriptions.subscribe("nmi", payload);
2783
- },
2784
- [services]
2785
- );
2786
- const subscribeWithCCBill = React3.useCallback(
2787
- async ({
2788
- priceId,
2789
- email,
2790
- firstName,
2791
- lastName,
2792
- zipCode,
2793
- country,
2794
- processor = "ccbill"
2795
- }) => {
2796
- const payload = {
2797
- priceId: ensurePrice(priceId),
2798
- email,
2799
- firstName,
2800
- lastName,
2801
- zipCode,
2802
- country,
2803
- processor
2804
- };
2805
- return services.subscriptions.subscribe("ccbill", payload);
2806
- },
2807
- [services]
2808
- );
2809
- const generateFlexFormUrl = React3.useCallback(
2810
- async ({
2811
- priceId,
2812
- firstName,
2813
- lastName,
2814
- address1,
2815
- city,
2816
- state,
2817
- zipCode,
2818
- country
2819
- }) => {
2820
- const payload = {
2821
- price_id: ensurePrice(priceId),
2822
- first_name: firstName,
2823
- last_name: lastName,
2824
- address1,
2825
- city,
2826
- state,
2827
- zip_code: zipCode,
2828
- country
2829
- };
2830
- return services.subscriptions.generateFlexFormUrl(payload);
2831
- },
2832
- [services]
2833
- );
2834
- return {
2835
- subscribeWithCard,
2836
- subscribeWithSavedMethod,
2837
- subscribeWithCCBill,
2838
- generateFlexFormUrl
2839
- };
2840
- };
2841
2934
  var SubscriptionCheckoutModal = ({
2842
2935
  open,
2843
2936
  onOpenChange,
@@ -4885,33 +4978,6 @@ var usePaymentStatus = (options = {}) => {
4885
4978
  isPending: getConfirmationStatus() === "pending"
4886
4979
  };
4887
4980
  };
4888
- var useAlternativePaymentProvider = () => {
4889
- const [isLoading, setIsLoading] = React3.useState(false);
4890
- const [error, setError] = React3.useState(null);
4891
- const { generateFlexFormUrl } = useSubscriptionActions();
4892
- const openFlexForm = React3.useCallback(
4893
- async (payload) => {
4894
- setIsLoading(true);
4895
- setError(null);
4896
- try {
4897
- const response = await generateFlexFormUrl(payload);
4898
- if (response?.iframe_url) {
4899
- window.location.href = response.iframe_url;
4900
- } else {
4901
- throw new Error("Unable to launch payment provider.");
4902
- }
4903
- } catch (err) {
4904
- const message = err instanceof Error ? err.message : "Failed to open payment provider.";
4905
- setError(message);
4906
- console.error("[payments-ui] failed to open alternative payment provider", err);
4907
- } finally {
4908
- setIsLoading(false);
4909
- }
4910
- },
4911
- [generateFlexFormUrl]
4912
- );
4913
- return { openFlexForm, isLoading, error };
4914
- };
4915
4981
 
4916
4982
  exports.BillingHistory = BillingHistory;
4917
4983
  exports.CancelMembershipDialog = CancelMembershipDialog;