@bunnyapp/components 1.8.0-beta.8 → 1.8.0-beta.9

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.
Files changed (58) hide show
  1. package/dist/cjs/index.js +1015 -943
  2. package/dist/cjs/types/src/components/PaymentForm/DemoPay/DemoPayFormProvider.d.ts +10 -0
  3. package/dist/cjs/types/src/components/PaymentForm/DemoPay/hooks/usePay.d.ts +2 -4
  4. package/dist/cjs/types/src/components/PaymentForm/DemoPay/hooks/useSave.d.ts +1 -5
  5. package/dist/cjs/types/src/components/PaymentForm/PaymentForm.d.ts +40 -9
  6. package/dist/cjs/types/src/components/PaymentForm/Stripe/StripeFormProvider.d.ts +5 -0
  7. package/dist/cjs/types/src/components/PaymentForm/Stripe/hooks/usePay.d.ts +2 -4
  8. package/dist/cjs/types/src/components/PaymentForm/components/CheckoutNoPayment.d.ts +6 -0
  9. package/dist/cjs/types/src/components/PaymentForm/components/Pay.d.ts +5 -0
  10. package/dist/cjs/types/src/components/PaymentForm/components/PaymentMethodDetails/PaymentMethodDetails.d.ts +12 -0
  11. package/dist/cjs/types/src/components/PaymentForm/components/PaymentMethodDetails/components/Cancel.d.ts +1 -0
  12. package/dist/cjs/types/src/components/PaymentForm/components/PaymentMethodDetails/components/Save.d.ts +1 -0
  13. package/dist/cjs/types/src/components/PaymentForm/components/StoredPaymentMethods.d.ts +18 -0
  14. package/dist/cjs/types/src/components/PaymentForm/context/AccountIdContext.d.ts +5 -0
  15. package/dist/cjs/types/src/components/PaymentForm/context/CallbacksContext.d.ts +14 -0
  16. package/dist/cjs/types/src/components/PaymentForm/context/PaymentMethodFormVisibilityContext.d.ts +12 -0
  17. package/dist/cjs/types/src/components/PaymentForm/context/PaymentProvider.d.ts +46 -37
  18. package/dist/cjs/types/src/components/PaymentForm/hooks/usePay.d.ts +14 -0
  19. package/dist/cjs/types/src/components/PaymentForm/hooks/usePaymentButtonText.d.ts +7 -1
  20. package/dist/cjs/types/src/components/PaymentForm/hooks/usePaymentHold.d.ts +18 -0
  21. package/dist/cjs/types/src/components/PaymentForm/hooks/useSave.d.ts +16 -0
  22. package/dist/cjs/types/src/components/PaymentForm/types/PaymentType.d.ts +2 -1
  23. package/dist/cjs/types/src/hooks/usePaymentMethod.d.ts +4 -0
  24. package/dist/esm/index.js +1019 -947
  25. package/dist/esm/types/src/components/PaymentForm/DemoPay/DemoPayFormProvider.d.ts +10 -0
  26. package/dist/esm/types/src/components/PaymentForm/DemoPay/hooks/usePay.d.ts +2 -4
  27. package/dist/esm/types/src/components/PaymentForm/DemoPay/hooks/useSave.d.ts +1 -5
  28. package/dist/esm/types/src/components/PaymentForm/PaymentForm.d.ts +40 -9
  29. package/dist/esm/types/src/components/PaymentForm/Stripe/StripeFormProvider.d.ts +5 -0
  30. package/dist/esm/types/src/components/PaymentForm/Stripe/hooks/usePay.d.ts +2 -4
  31. package/dist/esm/types/src/components/PaymentForm/components/CheckoutNoPayment.d.ts +6 -0
  32. package/dist/esm/types/src/components/PaymentForm/components/Pay.d.ts +5 -0
  33. package/dist/esm/types/src/components/PaymentForm/components/PaymentMethodDetails/PaymentMethodDetails.d.ts +12 -0
  34. package/dist/esm/types/src/components/PaymentForm/components/PaymentMethodDetails/components/Cancel.d.ts +1 -0
  35. package/dist/esm/types/src/components/PaymentForm/components/PaymentMethodDetails/components/Save.d.ts +1 -0
  36. package/dist/esm/types/src/components/PaymentForm/components/StoredPaymentMethods.d.ts +18 -0
  37. package/dist/esm/types/src/components/PaymentForm/context/AccountIdContext.d.ts +5 -0
  38. package/dist/esm/types/src/components/PaymentForm/context/CallbacksContext.d.ts +14 -0
  39. package/dist/esm/types/src/components/PaymentForm/context/PaymentMethodFormVisibilityContext.d.ts +12 -0
  40. package/dist/esm/types/src/components/PaymentForm/context/PaymentProvider.d.ts +46 -37
  41. package/dist/esm/types/src/components/PaymentForm/hooks/usePay.d.ts +14 -0
  42. package/dist/esm/types/src/components/PaymentForm/hooks/usePaymentButtonText.d.ts +7 -1
  43. package/dist/esm/types/src/components/PaymentForm/hooks/usePaymentHold.d.ts +18 -0
  44. package/dist/esm/types/src/components/PaymentForm/hooks/useSave.d.ts +16 -0
  45. package/dist/esm/types/src/components/PaymentForm/types/PaymentType.d.ts +2 -1
  46. package/dist/esm/types/src/hooks/usePaymentMethod.d.ts +4 -0
  47. package/dist/index.d.ts +64 -9
  48. package/package.json +1 -1
  49. package/dist/cjs/types/src/components/PaymentForm/components/CheckoutFooter.d.ts +0 -2
  50. package/dist/cjs/types/src/components/PaymentForm/components/PaymentMethodDetails.d.ts +0 -2
  51. package/dist/cjs/types/src/components/PaymentForm/components/PaymentMethodFooter.d.ts +0 -4
  52. package/dist/cjs/types/src/components/PaymentForm/hooks/useHandlePayment.d.ts +0 -41
  53. package/dist/esm/types/src/components/PaymentForm/components/CheckoutFooter.d.ts +0 -2
  54. package/dist/esm/types/src/components/PaymentForm/components/PaymentMethodDetails.d.ts +0 -2
  55. package/dist/esm/types/src/components/PaymentForm/components/PaymentMethodFooter.d.ts +0 -4
  56. package/dist/esm/types/src/components/PaymentForm/hooks/useHandlePayment.d.ts +0 -41
  57. /package/dist/cjs/types/src/components/PaymentForm/components/{PaymentMethodSelector.d.ts → PaymentMethodDetails/components/PaymentMethodSelector.d.ts} +0 -0
  58. /package/dist/esm/types/src/components/PaymentForm/components/{PaymentMethodSelector.d.ts → PaymentMethodDetails/components/PaymentMethodSelector.d.ts} +0 -0
package/dist/esm/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
- import { useState, useEffect, isValidElement, createContext, useContext, useMemo, createElement, useRef, useCallback, Fragment as Fragment$1 } from 'react';
2
+ import { useState, useEffect, isValidElement, createContext, useContext, useMemo, useCallback, useRef, createElement, Fragment as Fragment$1 } from 'react';
3
3
  import { WarningOutlined, CheckCircleOutlined, CreditCardOutlined, EllipsisOutlined, BankOutlined, DownloadOutlined, SearchOutlined, CheckCircleFilled, LoadingOutlined, InfoCircleOutlined, CloseOutlined } from '@ant-design/icons';
4
- import { notification, theme, ConfigProvider, Tag, Button, Dropdown, Typography, Input, Modal, Checkbox, Skeleton, Collapse, Spin, Form, Tooltip as Tooltip$1, Drawer, Card as Card$1, Divider, Select, Popconfirm, Table, Radio, Space, Switch } from 'antd';
5
- import { QueryClient, MutationCache, QueryCache, QueryClientProvider, useQuery, useMutation, useQueryClient, keepPreviousData, useIsMutating, useIsFetching } from '@tanstack/react-query';
4
+ import { notification, theme, ConfigProvider, Tag, Button, Dropdown, Typography, Skeleton, Input, Modal, Checkbox, Collapse, Spin, Form, Tooltip as Tooltip$1, Drawer, Card as Card$1, Divider, Select, Popconfirm, Table, Radio, Space, Switch } from 'antd';
5
+ import { QueryClient, MutationCache, QueryCache, QueryClientProvider, useQuery, useQueryClient, useMutation, keepPreviousData, useIsMutating, useIsFetching } from '@tanstack/react-query';
6
6
  import request$1 from 'graphql-request';
7
7
  import { RecoilRoot } from 'recoil';
8
8
  import { print } from 'graphql';
9
- import { useElements, useStripe, Elements, PaymentElement } from '@stripe/react-stripe-js';
9
+ import { Elements, useElements, useStripe, PaymentElement } from '@stripe/react-stripe-js';
10
10
  import { loadStripe } from '@stripe/stripe-js/pure/index.js';
11
11
  import styled, { styled as styled$1 } from 'styled-components';
12
12
  import { Markup } from 'interweave';
@@ -1281,7 +1281,7 @@ const DEFAULT_CONFIG = {
1281
1281
  };
1282
1282
 
1283
1283
  // This will be replaced at build time by rollup-plugin-replace
1284
- const PACKAGE_VERSION = '1.8.0-beta.7';
1284
+ const PACKAGE_VERSION = '1.8.0-beta.8';
1285
1285
  const createRequestHeaders = (token) => {
1286
1286
  const headers = createClientDevHeaders({ token });
1287
1287
  // Add the components version header
@@ -19843,60 +19843,6 @@ const PaymentForm_PaymentMethodsFragment = t(`
19843
19843
  }
19844
19844
  `, [MiniCreditCard_PaymentMethodFragment]);
19845
19845
 
19846
- const query$9 = t(`
19847
- query PaymentMethods($accountId: ID) {
19848
- paymentMethods(accountId: $accountId) {
19849
- nodes {
19850
- id
19851
- pluginId
19852
- accountId
19853
- expirationDate
19854
- plugin {
19855
- guid
19856
- id
19857
- }
19858
- state
19859
- metadata {
19860
- issuer
19861
- identifier
19862
- kind
19863
- description
19864
- icon
19865
- type
19866
- }
19867
- isDefault
19868
- ...PaymentForm_PaymentMethodsFragment
19869
- }
19870
- }
19871
- }
19872
- `, [PaymentForm_PaymentMethodsFragment]);
19873
- const getPaymentMethods = async ({ apiHost, token, accountId, }) => {
19874
- var _a, _b, _c;
19875
- const response = await execute(query$9, { apiHost, token }, { accountId });
19876
- // Filter out null values that are technically possible due to api schema
19877
- return (_c = (_b = (_a = response === null || response === void 0 ? void 0 : response.paymentMethods) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b.filter(paymentMethod => paymentMethod !== null)) !== null && _c !== void 0 ? _c : [];
19878
- };
19879
- const usePaymentMethod = ({ accountId, enabled = true, }) => {
19880
- // Context
19881
- const { apiHost } = useContext(BunnyContext);
19882
- const token = useToken();
19883
- // Query
19884
- const { data, isLoading } = useQuery({
19885
- queryKey: QueryKeyFactory.accountPaymentMethodsKey({
19886
- accountId,
19887
- token,
19888
- }),
19889
- queryFn: () => getPaymentMethods({ apiHost, token, accountId }),
19890
- staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
19891
- enabled,
19892
- });
19893
- return {
19894
- paymentMethods: data,
19895
- defaultPaymentMethod: data === null || data === void 0 ? void 0 : data.find(paymentMethod => paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.isDefault),
19896
- isLoading,
19897
- };
19898
- };
19899
-
19900
19846
  const mutation$i = t(`
19901
19847
  query PaymentPlugins($accountId: ID) {
19902
19848
  paymentPlugins(accountId: $accountId) {
@@ -19952,63 +19898,53 @@ const usePaymentPlugins = (accountId) => {
19952
19898
  };
19953
19899
  };
19954
19900
 
19955
- /**
19956
- * Creates a context and provider for a state value
19957
- * @param useValue - A function that returns the state value
19958
- * @returns A tuple containing the provider and the context value
19959
- * @example take a look at how this is used in the quoteIdContext in the QuantityDrawer
19960
- */
19961
- const createStateContext = (useValue) => {
19962
- const StateContext = createContext(null);
19963
- const StateProvider = ({ initialValue, children, }) => jsxs(StateContext.Provider, { value: useValue(initialValue), children: [" ", children, " "] });
19964
- const useContextState = () => {
19965
- const value = useContext(StateContext);
19966
- if (value === null) {
19967
- throw new Error('Provider missing');
19968
- }
19969
- return value;
19970
- };
19971
- return [StateProvider, useContextState];
19972
- };
19901
+ const [PaymentFormCallbacksProvider, usePaymentFormCallbacks] = createValueContext();
19973
19902
 
19974
- // Contexts
19975
- const useSelectedPluginIdState = (id) => useState(id);
19976
- const [SelectedPluginIdProvider, useSelectedPluginId] = createStateContext(useSelectedPluginIdState);
19977
- const [SelectedPluginDataProvider, useSelectedPlugin] = createValueContext(); // TODO: type the response to PluginData
19978
- /**
19979
- * SelectedPluginProvider manages the selected payment plugin state.
19980
- *
19981
- * Data Flow Pattern:
19982
- * - Children components should set the plugin ID using `setSelectedPluginId` from `useSelectedPluginId`
19983
- * - This provider then derives the `selectedPlugin` from that ID and provides it via `useSelectedPlugin`
19984
- * - This ensures that data flows only downward: children control the ID, and the provider computes and provides the data
19985
- */
19986
- function SelectedPluginProvider({ children, accountId, }) {
19987
- return (jsx(SelectedPluginIdProvider, { initialValue: undefined, children: jsx(Content, { accountId: accountId, children: children }) }));
19988
- }
19989
- function Content({ children, accountId }) {
19990
- // Context
19991
- const { paymentPlugins } = usePaymentPlugins(accountId);
19992
- const { defaultPaymentMethod } = usePaymentMethod({ accountId });
19993
- const [selectedPluginId, setSelectedPluginId] = useSelectedPluginId();
19994
- // Derived state
19995
- const selectedPlugin = useMemo(() => {
19996
- return paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.find(plugin => plugin.id === selectedPluginId);
19997
- }, [paymentPlugins, selectedPluginId]);
19903
+ // Used for Signup component. Signup uses an apiClient token which api can't infer an account from, so accountId must be used
19904
+ const [AccountIdProvider, useAccountId] = createValueContext();
19905
+
19906
+ // Automatically sets the default payment method if there is none currently set
19907
+ const useAutoSetDefaultPaymentMethod = ({ handleSetDefault, setDefaultPaymentMethodLoading, enabled = true, }) => {
19908
+ const accountId = useAccountId();
19909
+ const token = useToken();
19910
+ const queryClient = useQueryClient();
19911
+ const { paymentMethods, defaultPaymentMethod } = usePaymentMethod({
19912
+ accountId,
19913
+ });
19914
+ // Set the default payment method on the cache. Prevents 'handleSetDefault' from being called too many times.
19915
+ function setDefaultPaymentMethodOnCache(targetPaymentMethod) {
19916
+ let cachedPaymentMethods = queryClient.getQueryData(QueryKeyFactory.accountPaymentMethodsKey({
19917
+ accountId,
19918
+ token,
19919
+ }));
19920
+ if (cachedPaymentMethods) {
19921
+ for (const paymentMethod of cachedPaymentMethods) {
19922
+ paymentMethod.isDefault = paymentMethod.id === targetPaymentMethod.id;
19923
+ }
19924
+ queryClient.setQueryData(QueryKeyFactory.accountPaymentMethodsKey({
19925
+ accountId,
19926
+ token,
19927
+ }), cachedPaymentMethods);
19928
+ }
19929
+ }
19998
19930
  useEffect(() => {
19999
- var _a, _b;
20000
- if (selectedPluginId) {
19931
+ var _a;
19932
+ if (setDefaultPaymentMethodLoading || !enabled)
20001
19933
  return;
19934
+ if ((paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) && (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) > 0 && !defaultPaymentMethod) {
19935
+ handleSetDefault((_a = paymentMethods[0]) === null || _a === void 0 ? void 0 : _a.id);
19936
+ setDefaultPaymentMethodOnCache(paymentMethods[0]);
20002
19937
  }
20003
- if (defaultPaymentMethod) {
20004
- setSelectedPluginId((_a = defaultPaymentMethod.plugin) === null || _a === void 0 ? void 0 : _a.id);
20005
- }
20006
- else {
20007
- setSelectedPluginId((_b = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins[0]) === null || _b === void 0 ? void 0 : _b.id);
20008
- }
20009
- }, [defaultPaymentMethod, paymentPlugins]);
20010
- return jsx(SelectedPluginDataProvider, { value: selectedPlugin, children: children });
20011
- }
19938
+ }, [
19939
+ paymentMethods,
19940
+ defaultPaymentMethod,
19941
+ handleSetDefault,
19942
+ setDefaultPaymentMethodLoading,
19943
+ queryClient,
19944
+ accountId,
19945
+ token,
19946
+ ]);
19947
+ };
20012
19948
 
20013
19949
  const request = ({ method = 'GET', apiHost, endpoint, body, headers, token, }) => {
20014
19950
  const { apiEndpoint } = DEFAULT_CONFIG;
@@ -20053,222 +19989,232 @@ const invokePlugin = async ({ method, payload, plugin, token, apiHost, component
20053
19989
  throw new Error(error.message);
20054
19990
  };
20055
19991
 
20056
- const DIGIT_REGEX = /\d/;
20057
- const isDigit = (char) => DIGIT_REGEX.test(char);
20058
- const isDeletion = (char) => [8, 46].includes(char); // Backspace or Delete
20059
- const isNavigation = (char) => [9, 37, 38, 39, 40].includes(char); // Tab, Arrows
20060
- const isModifier = (char) => [16, 17, 18].includes(char); // Shift, Ctrl, Alt
20061
- const isValidKey = (code) => isDeletion(code) || isNavigation(code) || isModifier(code);
20062
- const isValidExpiry = (expiry) => {
20063
- const month = parseInt(expiry.substring(0, 2), 10);
20064
- const year = parseInt(expiry.substring(2, 4), 10) + 2000;
20065
- if (isNaN(month) || isNaN(year))
20066
- return false;
20067
- if (month < 1 || month > 12)
20068
- return false;
20069
- return true;
20070
- };
20071
- const isCardExpired = (expiry) => {
20072
- const month = parseInt(expiry.substring(0, 2), 10);
20073
- const year = parseInt(expiry.substring(2, 4), 10) + 2000;
20074
- const expiryDate = new Date(year, month, 1);
20075
- return expiryDate < new Date();
20076
- };
20077
- const formatCardExpiry = (cardExpiry) => {
20078
- if (cardExpiry.length <= 2)
20079
- return cardExpiry;
20080
- return cardExpiry.substring(0, 2) + '/' + cardExpiry.substring(2);
20081
- };
20082
- // removes spaces from a credit card number
20083
- const unformatCardNumber = (cardNumber) => {
20084
- const cardNumberArray = cardNumber.split('');
20085
- const unformattedCardNumberArray = cardNumberArray.filter(character => character !== ' ');
20086
- return unformattedCardNumberArray.join('');
20087
- };
20088
- const storePayment = async (options, plugin, apiHost, accountId) => {
20089
- const { testCreditCardNumber, testCreditCardCvc, testCreditCardExpirationDate, token } = options;
20090
- const response = await invokePlugin({
20091
- method: 'store_payment_method',
20092
- plugin,
20093
- payload: {
20094
- test_credit_card_number: testCreditCardNumber,
20095
- test_credit_card_cvc: testCreditCardCvc,
20096
- test_credit_card_expiration_date: testCreditCardExpirationDate,
20097
- account_id: accountId,
20098
- },
20099
- token,
20100
- apiHost,
20101
- });
20102
- if ((response === null || response === void 0 ? void 0 : response.status) !== 'success')
20103
- throw new Error(response === null || response === void 0 ? void 0 : response.message);
20104
- return response;
20105
- };
20106
-
20107
- function useSave$1({ onSaveSuccess, onSaveError, accountId, }) {
20108
- const [isSaving, setIsSaving] = useState(false);
20109
- const { apiHost } = useContext(BunnyContext);
19992
+ function useSetDefaultPaymentMethod(onError, onSuccess) {
19993
+ // Context
19994
+ const accountId = useAccountId();
20110
19995
  const token = useToken();
20111
- const plugin = useSelectedPlugin();
20112
- const save = async (cardDetails) => {
20113
- try {
20114
- setIsSaving(true);
20115
- const response = await storePayment({
20116
- testCreditCardNumber: unformatCardNumber(cardDetails.number),
20117
- testCreditCardCvc: cardDetails.cvc.toString(),
20118
- testCreditCardExpirationDate: cardDetails.expiry,
20119
- token,
20120
- }, plugin, apiHost, accountId);
20121
- const paymentMethodId = response.payload[0].id;
20122
- if (response.status !== 'success')
20123
- throw new Error(response === null || response === void 0 ? void 0 : response.message);
20124
- onSaveSuccess === null || onSaveSuccess === void 0 ? void 0 : onSaveSuccess({
20125
- pluginPaymentResponse: {
20126
- plugin,
20127
- token: response.token,
20128
- savePaymentMethod: true,
20129
- },
20130
- savedPaymentMethodResponse: {
20131
- paymentMethodId,
20132
- },
20133
- });
20134
- return paymentMethodId;
20135
- }
20136
- catch (error) {
20137
- onSaveError === null || onSaveError === void 0 ? void 0 : onSaveError(error);
20138
- }
20139
- finally {
20140
- setIsSaving(false);
19996
+ const { apiHost } = useContext(BunnyContext);
19997
+ const { paymentPlugins } = usePaymentPlugins(accountId);
19998
+ const { paymentMethods } = usePaymentMethod({ accountId });
19999
+ // Hooks
20000
+ const showErrorNotification = useErrorNotification();
20001
+ const showSuccessNotification = useSuccessNotification();
20002
+ const queryClient = useQueryClient();
20003
+ // State
20004
+ const [loading, setLoading] = useState(false);
20005
+ const setDefaultPaymentMethod = useCallback(async (paymentMethodId) => {
20006
+ const paymentMethod = paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.find(paymentMethod => (paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.id) === paymentMethodId);
20007
+ if (paymentMethod === undefined) {
20008
+ showErrorNotification('Payment method not found', 'Error setting default payment method');
20009
+ return;
20141
20010
  }
20142
- return undefined;
20143
- };
20144
- return { save, isSaving };
20145
- }
20146
-
20147
- const MUTATION$4 = `
20148
- mutation checkout(
20149
- $invoiceId: ID,
20150
- $quoteId: ID,
20151
- $paymentMethodId: ID,
20152
- $paymentMethodData: CheckoutPaymentMethodAttributes
20153
- ) {
20154
- checkout(
20155
- invoiceId: $invoiceId,
20156
- quoteId: $quoteId,
20157
- paymentMethodId: $paymentMethodId,
20158
- paymentMethodData: $paymentMethodData
20159
- ) {
20160
- invoice {
20161
- id
20162
- state
20163
- amount
20164
- amountDue
20165
- }
20166
- payment {
20167
- id
20168
- state
20169
- amount
20170
- }
20171
- paymentApplication {
20172
- id
20173
- invoiceId
20174
- paymentId
20175
- }
20176
- transaction {
20011
+ const plugin = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.find(paymentPlugin => { var _a; return String(paymentPlugin.id) === ((_a = paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.plugin) === null || _a === void 0 ? void 0 : _a.id); }); // TODO: type the response to PluginData
20012
+ if (plugin) {
20013
+ setLoading(true);
20014
+ await invokePlugin({
20015
+ plugin: plugin,
20016
+ method: 'assign_default_payment_method',
20017
+ payload: {
20018
+ payment_method_id: paymentMethod.id,
20019
+ account_id: accountId,
20020
+ },
20021
+ token,
20022
+ apiHost,
20023
+ })
20024
+ .then(() => {
20025
+ var _a;
20026
+ showSuccessNotification(`Payment method ${(_a = paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.metadata) === null || _a === void 0 ? void 0 : _a.identifier} was set as default`, 'Success');
20027
+ onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess();
20028
+ queryClient.invalidateQueries({
20029
+ queryKey: QueryKeyFactory.accountPaymentMethodsKey({
20030
+ token,
20031
+ accountId,
20032
+ }),
20033
+ });
20034
+ })
20035
+ .catch((error) => {
20036
+ setLoading(false);
20037
+ showErrorNotification(error.message, 'Error removing payment method');
20038
+ })
20039
+ .finally(() => {
20040
+ setLoading(false);
20041
+ });
20042
+ }
20043
+ else {
20044
+ const availablePlugins = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.map((plugin) => { var _a, _b, _c; return (_c = (_b = (_a = plugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name; });
20045
+ onError === null || onError === void 0 ? void 0 : onError(`Either the payment method was not found or the plugin was not present. Available plugins: ${availablePlugins === null || availablePlugins === void 0 ? void 0 : availablePlugins.join(', ')}`);
20046
+ }
20047
+ }, [paymentPlugins, token, apiHost, queryClient]);
20048
+ return { setDefaultPaymentMethod, loading };
20049
+ }
20050
+
20051
+ function useRemovePaymentMethod(onPaymentMethodRemoved, onError) {
20052
+ const queryClient = useQueryClient();
20053
+ const showErrorNotification = useErrorNotification();
20054
+ const showSuccessNotification = useSuccessNotification();
20055
+ const accountId = useAccountId();
20056
+ const { paymentPlugins } = usePaymentPlugins(accountId);
20057
+ const token = useToken();
20058
+ const { apiHost } = useContext(BunnyContext);
20059
+ const removePaymentMethod = useCallback(async (data) => {
20060
+ const plugin = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.find(paymentPlugin => { var _a; return String(paymentPlugin.id) === String((_a = data === null || data === void 0 ? void 0 : data.plugin) === null || _a === void 0 ? void 0 : _a.id); });
20061
+ if (data && plugin) {
20062
+ await invokePlugin({
20063
+ plugin: plugin, // TODO: type the plugin
20064
+ method: 'remove_payment_method',
20065
+ payload: {
20066
+ payment_method_id: data.id,
20067
+ account_id: accountId,
20068
+ },
20069
+ token,
20070
+ apiHost,
20071
+ })
20072
+ .then(() => {
20073
+ showSuccessNotification('Payment method was removed', 'Success');
20074
+ queryClient.invalidateQueries({
20075
+ queryKey: QueryKeyFactory.accountPaymentMethodsKey({
20076
+ token,
20077
+ accountId,
20078
+ }),
20079
+ });
20080
+ onPaymentMethodRemoved === null || onPaymentMethodRemoved === void 0 ? void 0 : onPaymentMethodRemoved(data);
20081
+ })
20082
+ .catch((error) => {
20083
+ showErrorNotification(error.message, 'Error removing payment method');
20084
+ });
20085
+ }
20086
+ else {
20087
+ const availablePlugins = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.map(plugin => {
20088
+ var _a, _b;
20089
+ const components = plugin.components;
20090
+ return (_b = (_a = components === null || components === void 0 ? void 0 : components.frontend) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.name;
20091
+ });
20092
+ onError === null || onError === void 0 ? void 0 : onError(`Either the payment method was not found or the plugin was not present. Available plugins: ${availablePlugins === null || availablePlugins === void 0 ? void 0 : availablePlugins.join(', ')}`);
20093
+ }
20094
+ }, [paymentPlugins, token, apiHost, queryClient, onPaymentMethodRemoved, accountId, onError]);
20095
+ return removePaymentMethod;
20096
+ }
20097
+
20098
+ const StoredPaymentMethods_PaymentMethodsFragment = t(`
20099
+ fragment StoredPaymentMethods_PaymentMethodsFragment on PaymentMethod {
20100
+ ...MiniCreditCard_PaymentMethodFragment
20101
+ id
20102
+ plugin {
20177
20103
  id
20178
- amount
20179
20104
  }
20180
20105
  }
20181
- }
20182
- `;
20183
- const checkout = async ({ quoteId, invoiceId, paymentMethodId, paymentMethodData, token, apiHost, }) => {
20184
- const mutationVars = {
20185
- quoteId,
20186
- invoiceId,
20187
- paymentMethodId,
20188
- };
20189
- if (paymentMethodData) {
20190
- mutationVars.paymentMethodData = {
20191
- ...paymentMethodData,
20192
- metadata: paymentMethodData.metadata,
20193
- };
20106
+ `, [MiniCreditCard_PaymentMethodFragment]);
20107
+ const showErrorNotification$d = useErrorNotification();
20108
+ function StoredPaymentMethods(props = {}) {
20109
+ const { className } = props;
20110
+ const accountId = useAccountId();
20111
+ const { paymentMethods, isLoading: isPaymentMethodLoading } = usePaymentMethod({ accountId });
20112
+ const { paymentPlugins } = usePaymentPlugins(accountId);
20113
+ const { onSetDefaultPaymentMethod, onPaymentMethodRemoved } = usePaymentFormCallbacks();
20114
+ // Custom hooks
20115
+ const { setDefaultPaymentMethod: handleSetDefault, loading: setDefaultPaymentMethodLoading } = useSetDefaultPaymentMethod(message => {
20116
+ showErrorNotification$d(message, 'Error setting default payment method');
20117
+ }, () => {
20118
+ onSetDefaultPaymentMethod === null || onSetDefaultPaymentMethod === void 0 ? void 0 : onSetDefaultPaymentMethod();
20119
+ });
20120
+ useAutoSetDefaultPaymentMethod({
20121
+ handleSetDefault,
20122
+ setDefaultPaymentMethodLoading,
20123
+ enabled: !!paymentPlugins,
20124
+ });
20125
+ const handleRemovePaymentMethod = useRemovePaymentMethod(onPaymentMethodRemoved, message => {
20126
+ showErrorNotification$d(message, 'Error removing payment method');
20127
+ });
20128
+ if (isPaymentMethodLoading) {
20129
+ return jsx(Skeleton, { active: true, className: "bunny-p-4" });
20194
20130
  }
20195
- const response = await gqlRequest({
20196
- query: MUTATION$4,
20197
- token,
20198
- vars: mutationVars,
20199
- apiHost: apiHost,
20131
+ return (jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-2 ${className}`, children: [paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.map(maskedPaymentMethod => {
20132
+ const paymentMethod = readFragment(StoredPaymentMethods_PaymentMethodsFragment, maskedPaymentMethod);
20133
+ return (jsx(MiniCreditCard, { onClickRemove: () => handleRemovePaymentMethod(paymentMethod), onClickSetDefault: () => handleSetDefault(paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.id), paymentMethod: paymentMethod, id: `payment-method-${paymentMethod.id}` }, paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.id));
20134
+ }), (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) === 0 && jsx(MiniCreditCard, {})] }));
20135
+ }
20136
+
20137
+ const query$9 = t(`
20138
+ query PaymentMethods($accountId: ID) {
20139
+ paymentMethods(accountId: $accountId) {
20140
+ nodes {
20141
+ id
20142
+ pluginId
20143
+ accountId
20144
+ expirationDate
20145
+ plugin {
20146
+ guid
20147
+ id
20148
+ }
20149
+ state
20150
+ metadata {
20151
+ issuer
20152
+ identifier
20153
+ kind
20154
+ description
20155
+ icon
20156
+ type
20157
+ }
20158
+ isDefault
20159
+ ...PaymentForm_PaymentMethodsFragment
20160
+ ...StoredPaymentMethods_PaymentMethodsFragment
20161
+ }
20162
+ }
20163
+ }
20164
+ `, [PaymentForm_PaymentMethodsFragment, StoredPaymentMethods_PaymentMethodsFragment]);
20165
+ const getPaymentMethods = async ({ apiHost, token, accountId, }) => {
20166
+ var _a, _b, _c;
20167
+ const response = await execute(query$9, { apiHost, token }, { accountId });
20168
+ // Filter out null values that are technically possible due to api schema
20169
+ return (_c = (_b = (_a = response === null || response === void 0 ? void 0 : response.paymentMethods) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b.filter(paymentMethod => paymentMethod !== null)) !== null && _c !== void 0 ? _c : [];
20170
+ };
20171
+ const usePaymentMethod = ({ accountId, enabled = true, }) => {
20172
+ // Context
20173
+ const { apiHost } = useContext(BunnyContext);
20174
+ const token = useToken();
20175
+ // Query
20176
+ const { data, isLoading } = useQuery({
20177
+ queryKey: QueryKeyFactory.accountPaymentMethodsKey({
20178
+ accountId,
20179
+ token,
20180
+ }),
20181
+ queryFn: () => getPaymentMethods({ apiHost, token, accountId }),
20182
+ staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
20183
+ enabled,
20200
20184
  });
20201
- const { errors } = response === null || response === void 0 ? void 0 : response.checkout;
20202
- if (errors)
20203
- throw errors;
20204
20185
  return {
20205
- savePaymentMethod: paymentMethodData === null || paymentMethodData === void 0 ? void 0 : paymentMethodData.savePaymentMethod,
20186
+ paymentMethods: data,
20187
+ defaultPaymentMethod: data === null || data === void 0 ? void 0 : data.find(paymentMethod => paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.isDefault),
20188
+ isLoading,
20206
20189
  };
20207
20190
  };
20208
20191
 
20209
- var dayjs_min$1 = {exports: {}};
20210
-
20211
- var dayjs_min = dayjs_min$1.exports;
20212
-
20213
- var hasRequiredDayjs_min;
20214
-
20215
- function requireDayjs_min () {
20216
- if (hasRequiredDayjs_min) return dayjs_min$1.exports;
20217
- hasRequiredDayjs_min = 1;
20218
- (function (module, exports) {
20219
- !function(t,e){module.exports=e();}(dayjs_min,(function(){var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return "["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return !r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return (e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return -t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,c),s=n-i<0,u=e.clone().add(r+(s?-1:1),c);return +(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return {M:c,y:h,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:f}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p="$isDayjsObject",S=function(t){return t instanceof _||!(!t||!t[p])},w=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else {var a=e.name;D[a]=e,i=a;}return !r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0;}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init();},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds();},m.$utils=function(){return b},m.isValid=function(){return !(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<O(t)},m.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!b.u(e)||e,f=b.p(t),l=function(t,e){var i=b.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return b.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(f){case h:return r?l(1,0):l(31,11);case c:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,!1)},m.$set=function(t,e){var n,o=b.p(t),f="set"+(this.$u?"UTC":""),l=(n={},n[a]=f+"Date",n[d]=f+"Date",n[c]=f+"Month",n[h]=f+"FullYear",n[u]=f+"Hours",n[s]=f+"Minutes",n[i]=f+"Seconds",n[r]=f+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===c||o===h){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d;}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[b.p(t)]()},m.add=function(r,f){var d,l=this;r=Number(r);var $=b.p(f),y=function(t){var e=O(l);return b.w(e.date(e.date()+Math.round(t*r)),l)};if($===c)return this.set(c,this.$M+r);if($===h)return this.set(h,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return b.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=b.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,c=n.months,f=n.meridiem,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},d=function(t){return b.s(s%12||12,t,"0")},$=f||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r};return r.replace(y,(function(t,r){return r||function(t){switch(t){case"YY":return String(e.$y).slice(-2);case"YYYY":return b.s(e.$y,4,"0");case"M":return a+1;case"MM":return b.s(a+1,2,"0");case"MMM":return h(n.monthsShort,a,c,3);case"MMMM":return h(c,a);case"D":return e.$D;case"DD":return b.s(e.$D,2,"0");case"d":return String(e.$W);case"dd":return h(n.weekdaysMin,e.$W,o,2);case"ddd":return h(n.weekdaysShort,e.$W,o,3);case"dddd":return o[e.$W];case"H":return String(s);case"HH":return b.s(s,2,"0");case"h":return d(1);case"hh":return d(2);case"a":return $(s,u,!0);case"A":return $(s,u,!1);case"m":return String(u);case"mm":return b.s(u,2,"0");case"s":return String(e.$s);case"ss":return b.s(e.$s,2,"0");case"SSS":return b.s(e.$ms,3,"0");case"Z":return i}return null}(t)||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=this,M=b.p(d),m=O(r),v=(m.utcOffset()-this.utcOffset())*e,g=this-m,D=function(){return b.m(y,m)};switch(M){case h:$=D()/12;break;case c:$=D();break;case f:$=D()/3;break;case o:$=(g-v)/6048e5;break;case a:$=(g-v)/864e5;break;case u:$=g/n;break;case s:$=g/e;break;case i:$=g/t;break;default:$=g;}return l?$:b.a($)},m.daysInMonth=function(){return this.endOf(c).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=w(t,e,!0);return r&&(n.$L=r),n},m.clone=function(){return b.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),k=_.prototype;return O.prototype=k,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",c],["$y",h],["$D",d]].forEach((function(t){k[t[1]]=function(e){return this.$g(e,t[0],t[1])};})),O.extend=function(t,e){return t.$i||(t(e,_,O),t.$i=!0),O},O.locale=w,O.isDayjs=S,O.unix=function(t){return O(1e3*t)},O.en=D[g],O.Ls=D,O.p={},O}));
20220
- } (dayjs_min$1));
20221
- return dayjs_min$1.exports;
20222
- }
20223
-
20224
- var dayjs_minExports = requireDayjs_min();
20225
- var dayjs = /*@__PURE__*/getDefaultExportFromCjs(dayjs_minExports);
20226
-
20227
- var localizedFormat$2 = {exports: {}};
20228
-
20229
- var localizedFormat$1 = localizedFormat$2.exports;
20230
-
20231
- var hasRequiredLocalizedFormat;
20232
-
20233
- function requireLocalizedFormat () {
20234
- if (hasRequiredLocalizedFormat) return localizedFormat$2.exports;
20235
- hasRequiredLocalizedFormat = 1;
20236
- (function (module, exports) {
20237
- !function(e,t){module.exports=t();}(localizedFormat$1,(function(){var e={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"};return function(t,o,n){var r=o.prototype,i=r.format;n.en.formats=e,r.format=function(t){void 0===t&&(t="YYYY-MM-DDTHH:mm:ssZ");var o=this.$locale().formats,n=function(t,o){return t.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(t,n,r){var i=r&&r.toUpperCase();return n||o[r]||e[r]||o[i].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,(function(e,t,o){return t||o.slice(1)}))}))}(t,void 0===o?{}:o);return i.call(this,n)};}}));
20238
- } (localizedFormat$2));
20239
- return localizedFormat$2.exports;
20240
- }
20241
-
20242
- var localizedFormatExports = requireLocalizedFormat();
20243
- var localizedFormat = /*@__PURE__*/getDefaultExportFromCjs(localizedFormatExports);
20244
-
20245
- dayjs.extend(localizedFormat);
20246
- const formatCurrency = (value, currencyIsoCode, decimals = 2) => {
20247
- if (value !== 0 && !value)
20248
- return '';
20249
- const currencyValue = typeof value === 'string' ? parseFloat(value) : value;
20250
- if (isNaN(currencyValue))
20192
+ /**
20193
+ * Creates a context and provider for a state value
20194
+ * @param useValue - A function that returns the state value
20195
+ * @returns A tuple containing the provider and the context value
20196
+ * @example take a look at how this is used in the quoteIdContext in the QuantityDrawer
20197
+ */
20198
+ const createStateContext = (useValue) => {
20199
+ const StateContext = createContext(null);
20200
+ const StateProvider = ({ initialValue, children, }) => jsxs(StateContext.Provider, { value: useValue(initialValue), children: [" ", children, " "] });
20201
+ const useContextState = () => {
20202
+ const value = useContext(StateContext);
20203
+ if (value === null) {
20204
+ throw new Error('Provider missing');
20205
+ }
20251
20206
  return value;
20252
- const localeOptions = {
20253
- minimumFractionDigits: decimals,
20254
- maximumFractionDigits: decimals,
20255
20207
  };
20256
- localeOptions.style = 'currency';
20257
- localeOptions.currency = currencyIsoCode;
20258
- return currencyValue.toLocaleString(navigator.language, localeOptions);
20208
+ return [StateProvider, useContextState];
20259
20209
  };
20260
20210
 
20261
- const getQuoteAmountDue_QuoteFragment = t(`
20262
- fragment getQuoteAmountDue_QuoteFragment on Quote {
20263
- amountDue
20264
- amount
20265
- }
20266
- `, []);
20267
- const getQuoteAmountDue = (maskedQuote) => {
20268
- var _a;
20269
- const quote = readFragment(getQuoteAmountDue_QuoteFragment, maskedQuote);
20270
- return (_a = quote.amountDue) !== null && _a !== void 0 ? _a : quote.amount;
20211
+ const defaultCardDetails = {
20212
+ number: '4242424242424242',
20213
+ expiry: '',
20214
+ cvc: '',
20271
20215
  };
20216
+ const useDemoPayCardDetailsState = (initialValue) => useState(initialValue !== null && initialValue !== void 0 ? initialValue : defaultCardDetails);
20217
+ const [DemoPayFormProvider, useDemoPayCardDetails] = createStateContext(useDemoPayCardDetailsState);
20272
20218
 
20273
20219
  const { useToken: useAntdToken$1 } = theme;
20274
20220
  const createPaymentMethod = async ({ paymentMethodId, plugin, token, apiHost, accountId, }) => {
@@ -20410,6 +20356,277 @@ const useStripePlugin = (plugin, currencyId, accountId) => {
20410
20356
  return { stripe, options };
20411
20357
  };
20412
20358
 
20359
+ // Contexts
20360
+ const useSelectedPluginIdState = (id) => useState(id);
20361
+ const [SelectedPluginIdProvider, useSelectedPluginId] = createStateContext(useSelectedPluginIdState);
20362
+ const [SelectedPluginDataProvider, useSelectedPlugin] = createValueContext(); // TODO: type the response to PluginData
20363
+ /**
20364
+ * SelectedPluginProvider manages the selected payment plugin state.
20365
+ *
20366
+ * Data Flow Pattern:
20367
+ * - Children components should set the plugin ID using `setSelectedPluginId` from `useSelectedPluginId`
20368
+ * - This provider then derives the `selectedPlugin` from that ID and provides it via `useSelectedPlugin`
20369
+ * - This ensures that data flows only downward: children control the ID, and the provider computes and provides the data
20370
+ */
20371
+ function SelectedPluginProvider({ children, accountId, }) {
20372
+ return (jsx(SelectedPluginIdProvider, { initialValue: undefined, children: jsx(Content, { accountId: accountId, children: children }) }));
20373
+ }
20374
+ function Content({ children, accountId }) {
20375
+ // Context
20376
+ const { paymentPlugins } = usePaymentPlugins(accountId);
20377
+ const { defaultPaymentMethod } = usePaymentMethod({ accountId });
20378
+ const [selectedPluginId, setSelectedPluginId] = useSelectedPluginId();
20379
+ // Derived state
20380
+ const selectedPlugin = useMemo(() => {
20381
+ return paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.find(plugin => plugin.id === selectedPluginId);
20382
+ }, [paymentPlugins, selectedPluginId]);
20383
+ useEffect(() => {
20384
+ var _a, _b;
20385
+ if (selectedPluginId) {
20386
+ return;
20387
+ }
20388
+ if (defaultPaymentMethod) {
20389
+ setSelectedPluginId((_a = defaultPaymentMethod.plugin) === null || _a === void 0 ? void 0 : _a.id);
20390
+ }
20391
+ else {
20392
+ setSelectedPluginId((_b = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins[0]) === null || _b === void 0 ? void 0 : _b.id);
20393
+ }
20394
+ }, [defaultPaymentMethod, paymentPlugins]);
20395
+ return jsx(SelectedPluginDataProvider, { value: selectedPlugin, children: children });
20396
+ }
20397
+
20398
+ function StripeFormProvider({ children, currencyId, accountId, }) {
20399
+ const plugin = useSelectedPlugin();
20400
+ const { stripe, options } = useStripePlugin(plugin, currencyId, accountId);
20401
+ return (jsx(Elements, { options: options, stripe: stripe, children: children }));
20402
+ }
20403
+
20404
+ const MUTATION$4 = `
20405
+ mutation checkout(
20406
+ $invoiceId: ID,
20407
+ $quoteId: ID,
20408
+ $paymentMethodId: ID,
20409
+ $paymentMethodData: CheckoutPaymentMethodAttributes
20410
+ ) {
20411
+ checkout(
20412
+ invoiceId: $invoiceId,
20413
+ quoteId: $quoteId,
20414
+ paymentMethodId: $paymentMethodId,
20415
+ paymentMethodData: $paymentMethodData
20416
+ ) {
20417
+ invoice {
20418
+ id
20419
+ state
20420
+ amount
20421
+ amountDue
20422
+ }
20423
+ payment {
20424
+ id
20425
+ state
20426
+ amount
20427
+ }
20428
+ paymentApplication {
20429
+ id
20430
+ invoiceId
20431
+ paymentId
20432
+ }
20433
+ transaction {
20434
+ id
20435
+ amount
20436
+ }
20437
+ }
20438
+ }
20439
+ `;
20440
+ const checkout = async ({ quoteId, invoiceId, paymentMethodId, paymentMethodData, token, apiHost, }) => {
20441
+ const mutationVars = {
20442
+ quoteId,
20443
+ invoiceId,
20444
+ paymentMethodId,
20445
+ };
20446
+ if (paymentMethodData) {
20447
+ mutationVars.paymentMethodData = {
20448
+ ...paymentMethodData,
20449
+ metadata: paymentMethodData.metadata,
20450
+ };
20451
+ }
20452
+ const response = await gqlRequest({
20453
+ query: MUTATION$4,
20454
+ token,
20455
+ vars: mutationVars,
20456
+ apiHost: apiHost,
20457
+ });
20458
+ const { errors } = response === null || response === void 0 ? void 0 : response.checkout;
20459
+ if (errors)
20460
+ throw errors;
20461
+ return {
20462
+ savePaymentMethod: paymentMethodData === null || paymentMethodData === void 0 ? void 0 : paymentMethodData.savePaymentMethod,
20463
+ };
20464
+ };
20465
+
20466
+ const getQuoteAmountDue_QuoteFragment = t(`
20467
+ fragment getQuoteAmountDue_QuoteFragment on Quote {
20468
+ amountDue
20469
+ amount
20470
+ }
20471
+ `, []);
20472
+ const getQuoteAmountDue = (maskedQuote) => {
20473
+ var _a;
20474
+ const quote = readFragment(getQuoteAmountDue_QuoteFragment, maskedQuote);
20475
+ return (_a = quote.amountDue) !== null && _a !== void 0 ? _a : quote.amount;
20476
+ };
20477
+
20478
+ var dayjs_min$1 = {exports: {}};
20479
+
20480
+ var dayjs_min = dayjs_min$1.exports;
20481
+
20482
+ var hasRequiredDayjs_min;
20483
+
20484
+ function requireDayjs_min () {
20485
+ if (hasRequiredDayjs_min) return dayjs_min$1.exports;
20486
+ hasRequiredDayjs_min = 1;
20487
+ (function (module, exports) {
20488
+ !function(t,e){module.exports=e();}(dayjs_min,(function(){var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return "["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return !r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return (e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return -t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,c),s=n-i<0,u=e.clone().add(r+(s?-1:1),c);return +(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return {M:c,y:h,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:f}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p="$isDayjsObject",S=function(t){return t instanceof _||!(!t||!t[p])},w=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else {var a=e.name;D[a]=e,i=a;}return !r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0;}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init();},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds();},m.$utils=function(){return b},m.isValid=function(){return !(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<O(t)},m.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!b.u(e)||e,f=b.p(t),l=function(t,e){var i=b.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return b.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(f){case h:return r?l(1,0):l(31,11);case c:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,!1)},m.$set=function(t,e){var n,o=b.p(t),f="set"+(this.$u?"UTC":""),l=(n={},n[a]=f+"Date",n[d]=f+"Date",n[c]=f+"Month",n[h]=f+"FullYear",n[u]=f+"Hours",n[s]=f+"Minutes",n[i]=f+"Seconds",n[r]=f+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===c||o===h){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d;}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[b.p(t)]()},m.add=function(r,f){var d,l=this;r=Number(r);var $=b.p(f),y=function(t){var e=O(l);return b.w(e.date(e.date()+Math.round(t*r)),l)};if($===c)return this.set(c,this.$M+r);if($===h)return this.set(h,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return b.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=b.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,c=n.months,f=n.meridiem,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},d=function(t){return b.s(s%12||12,t,"0")},$=f||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r};return r.replace(y,(function(t,r){return r||function(t){switch(t){case"YY":return String(e.$y).slice(-2);case"YYYY":return b.s(e.$y,4,"0");case"M":return a+1;case"MM":return b.s(a+1,2,"0");case"MMM":return h(n.monthsShort,a,c,3);case"MMMM":return h(c,a);case"D":return e.$D;case"DD":return b.s(e.$D,2,"0");case"d":return String(e.$W);case"dd":return h(n.weekdaysMin,e.$W,o,2);case"ddd":return h(n.weekdaysShort,e.$W,o,3);case"dddd":return o[e.$W];case"H":return String(s);case"HH":return b.s(s,2,"0");case"h":return d(1);case"hh":return d(2);case"a":return $(s,u,!0);case"A":return $(s,u,!1);case"m":return String(u);case"mm":return b.s(u,2,"0");case"s":return String(e.$s);case"ss":return b.s(e.$s,2,"0");case"SSS":return b.s(e.$ms,3,"0");case"Z":return i}return null}(t)||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=this,M=b.p(d),m=O(r),v=(m.utcOffset()-this.utcOffset())*e,g=this-m,D=function(){return b.m(y,m)};switch(M){case h:$=D()/12;break;case c:$=D();break;case f:$=D()/3;break;case o:$=(g-v)/6048e5;break;case a:$=(g-v)/864e5;break;case u:$=g/n;break;case s:$=g/e;break;case i:$=g/t;break;default:$=g;}return l?$:b.a($)},m.daysInMonth=function(){return this.endOf(c).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=w(t,e,!0);return r&&(n.$L=r),n},m.clone=function(){return b.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),k=_.prototype;return O.prototype=k,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",c],["$y",h],["$D",d]].forEach((function(t){k[t[1]]=function(e){return this.$g(e,t[0],t[1])};})),O.extend=function(t,e){return t.$i||(t(e,_,O),t.$i=!0),O},O.locale=w,O.isDayjs=S,O.unix=function(t){return O(1e3*t)},O.en=D[g],O.Ls=D,O.p={},O}));
20489
+ } (dayjs_min$1));
20490
+ return dayjs_min$1.exports;
20491
+ }
20492
+
20493
+ var dayjs_minExports = requireDayjs_min();
20494
+ var dayjs = /*@__PURE__*/getDefaultExportFromCjs(dayjs_minExports);
20495
+
20496
+ var localizedFormat$2 = {exports: {}};
20497
+
20498
+ var localizedFormat$1 = localizedFormat$2.exports;
20499
+
20500
+ var hasRequiredLocalizedFormat;
20501
+
20502
+ function requireLocalizedFormat () {
20503
+ if (hasRequiredLocalizedFormat) return localizedFormat$2.exports;
20504
+ hasRequiredLocalizedFormat = 1;
20505
+ (function (module, exports) {
20506
+ !function(e,t){module.exports=t();}(localizedFormat$1,(function(){var e={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"};return function(t,o,n){var r=o.prototype,i=r.format;n.en.formats=e,r.format=function(t){void 0===t&&(t="YYYY-MM-DDTHH:mm:ssZ");var o=this.$locale().formats,n=function(t,o){return t.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(t,n,r){var i=r&&r.toUpperCase();return n||o[r]||e[r]||o[i].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,(function(e,t,o){return t||o.slice(1)}))}))}(t,void 0===o?{}:o);return i.call(this,n)};}}));
20507
+ } (localizedFormat$2));
20508
+ return localizedFormat$2.exports;
20509
+ }
20510
+
20511
+ var localizedFormatExports = requireLocalizedFormat();
20512
+ var localizedFormat = /*@__PURE__*/getDefaultExportFromCjs(localizedFormatExports);
20513
+
20514
+ dayjs.extend(localizedFormat);
20515
+ const formatCurrency = (value, currencyIsoCode, decimals = 2) => {
20516
+ if (value !== 0 && !value)
20517
+ return '';
20518
+ const currencyValue = typeof value === 'string' ? parseFloat(value) : value;
20519
+ if (isNaN(currencyValue))
20520
+ return value;
20521
+ const localeOptions = {
20522
+ minimumFractionDigits: decimals,
20523
+ maximumFractionDigits: decimals,
20524
+ };
20525
+ localeOptions.style = 'currency';
20526
+ localeOptions.currency = currencyIsoCode;
20527
+ return currencyValue.toLocaleString(navigator.language, localeOptions);
20528
+ };
20529
+
20530
+ var PaymentType;
20531
+ (function (PaymentType) {
20532
+ PaymentType["PAY"] = "PAY";
20533
+ PaymentType["APPROVE_HOLD"] = "APPROVE_HOLD";
20534
+ PaymentType["CHECKOUT_NO_PAYMENT"] = "CHECKOUT_NO_PAYMENT";
20535
+ PaymentType["SAVE"] = "SAVE";
20536
+ })(PaymentType || (PaymentType = {}));
20537
+
20538
+ // Contexts
20539
+ // TODO: use useSelector to get the values from the context so we don't have unnecessary re-renders
20540
+ const [PayableProvider, usePayable] = createValueContext();
20541
+ const PaymentProvider_QuoteFragment = t(`
20542
+ fragment PaymentProvider_QuoteFragment on Quote {
20543
+ id
20544
+ currencyId
20545
+ ...getQuoteAmountDue_QuoteFragment
20546
+ }
20547
+ `, [getQuoteAmountDue_QuoteFragment]);
20548
+ const PaymentProvider_InvoiceFragment = t(`
20549
+ fragment PaymentProvider_InvoiceFragment on Invoice {
20550
+ id
20551
+ currencyId
20552
+ amountDue
20553
+ }
20554
+ `);
20555
+ function PaymentProvider({ children, quote: maskedQuote, invoice: maskedInvoice, paymentHold, }) {
20556
+ var _a, _b;
20557
+ // Read fragments
20558
+ const quote = readFragment(PaymentProvider_QuoteFragment, maskedQuote);
20559
+ const invoice = readFragment(PaymentProvider_InvoiceFragment, maskedInvoice);
20560
+ // State
20561
+ const [isPaid, setIsPaid] = useState(false);
20562
+ const getAmountDue = () => {
20563
+ return quote ? getQuoteAmountDue(quote) : invoice === null || invoice === void 0 ? void 0 : invoice.amountDue;
20564
+ };
20565
+ const formattedAmountDue = () => {
20566
+ var _a;
20567
+ const amountDue = getAmountDue();
20568
+ const currencyId = (_a = quote === null || quote === void 0 ? void 0 : quote.currencyId) !== null && _a !== void 0 ? _a : invoice === null || invoice === void 0 ? void 0 : invoice.currencyId;
20569
+ return amountDue !== undefined && currencyId !== undefined
20570
+ ? formatCurrency(amountDue, currencyId).toString()
20571
+ : undefined;
20572
+ };
20573
+ // Validation checks
20574
+ if (quote !== undefined && invoice !== undefined && paymentHold !== undefined) {
20575
+ throw new Error('Either quote or invoice or paymentHold must be provided, not all');
20576
+ }
20577
+ function getPaymentType() {
20578
+ if (getAmountDue() === undefined)
20579
+ return PaymentType.SAVE;
20580
+ if (getAmountDue() === 0)
20581
+ return PaymentType.CHECKOUT_NO_PAYMENT;
20582
+ return paymentHold
20583
+ ? PaymentType.APPROVE_HOLD
20584
+ : PaymentType.PAY;
20585
+ }
20586
+ const paymentType = getPaymentType();
20587
+ const amountDue = getAmountDue();
20588
+ const formattedAmountDueValue = formattedAmountDue();
20589
+ return (jsx(PayableProvider, { value: {
20590
+ quoteId: (_a = quote === null || quote === void 0 ? void 0 : quote.id) !== null && _a !== void 0 ? _a : undefined,
20591
+ invoiceId: (_b = invoice === null || invoice === void 0 ? void 0 : invoice.id) !== null && _b !== void 0 ? _b : undefined,
20592
+ paymentHold,
20593
+ paymentType,
20594
+ amountDue: amountDue !== null && amountDue !== void 0 ? amountDue : undefined,
20595
+ formattedAmountDue: formattedAmountDueValue,
20596
+ isPaid,
20597
+ setIsPaid
20598
+ }, children: children }));
20599
+ }
20600
+
20601
+ function CheckoutNoPayment(props = {}) {
20602
+ const { className, disabled } = props;
20603
+ const { formattedAmountDue, quoteId } = usePayable();
20604
+ const token = useToken();
20605
+ const { apiHost } = useContext(BunnyContext);
20606
+ const handleAllErrorFormats = useAllErrorFormats$1();
20607
+ const { onPaymentSuccess } = usePaymentFormCallbacks();
20608
+ const { mutate: checkoutNoPayment, isPending } = useMutation({
20609
+ mutationFn: async () => {
20610
+ const paymentRequired = formattedAmountDue ? parseFloat(formattedAmountDue) > 0 : true;
20611
+ if (!quoteId)
20612
+ throw new Error('Quote ID is required');
20613
+ if (paymentRequired)
20614
+ throw new Error('Payment is required');
20615
+ return await checkout({ quoteId, token, apiHost });
20616
+ },
20617
+ onSuccess: () => {
20618
+ onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess({});
20619
+ },
20620
+ onError: error => {
20621
+ handleAllErrorFormats(error);
20622
+ },
20623
+ });
20624
+ return (jsx("div", { className: className, children: jsx(Button, { className: "bunny-w-full", loading: isPending, disabled: isPending || disabled, onClick: () => checkoutNoPayment(), type: "primary", children: isPending ? 'Processing...' : 'Complete Order' }) }));
20625
+ }
20626
+
20627
+ const useShowPaymentDetailsState = (isOpen = false) => useState(isOpen);
20628
+ const [ShowPaymentDetailsProvider, useShowPaymentDetails] = createStateContext(useShowPaymentDetailsState);
20629
+
20413
20630
  function useApproveHold$1({ onApproveHoldSuccess, onApproveHoldError, }) {
20414
20631
  // Hooks
20415
20632
  const [isApprovingHold, setIsApprovingHold] = useState(false);
@@ -20442,47 +20659,6 @@ function useApproveHold$1({ onApproveHoldSuccess, onApproveHoldError, }) {
20442
20659
  return { approveHold, isApprovingHold };
20443
20660
  }
20444
20661
 
20445
- // Used for Signup component. Signup needs a custom checkout function
20446
- const [CustomCheckoutFunctionProvider, useCustomCheckoutFunction] = createValueContext();
20447
-
20448
- const showErrorNotification$a = useErrorNotification();
20449
- function usePay$1({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, }) {
20450
- const { apiHost } = useContext(BunnyContext);
20451
- const customCheckoutFunction = useCustomCheckoutFunction();
20452
- const token = useToken();
20453
- const [isPaying, setIsPaying] = useState(false);
20454
- const pay = async (paymentMethodId) => {
20455
- try {
20456
- setIsPaying(true);
20457
- if (customCheckoutFunction) {
20458
- if (!(plugin === null || plugin === void 0 ? void 0 : plugin.id)) {
20459
- showErrorNotification$a('Plugin ID is required');
20460
- return;
20461
- }
20462
- const response = await customCheckoutFunction(plugin === null || plugin === void 0 ? void 0 : plugin.id.toString(), paymentMethodId.toString());
20463
- onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20464
- }
20465
- else {
20466
- const response = await checkout({
20467
- invoiceId: invoice === null || invoice === void 0 ? void 0 : invoice.id,
20468
- quoteId,
20469
- paymentMethodId,
20470
- token,
20471
- apiHost,
20472
- });
20473
- onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20474
- }
20475
- }
20476
- catch (error) {
20477
- onPaymentError === null || onPaymentError === void 0 ? void 0 : onPaymentError(error);
20478
- }
20479
- finally {
20480
- setIsPaying(false);
20481
- }
20482
- };
20483
- return { pay, isPaying };
20484
- }
20485
-
20486
20662
  function useApproveHold({ onApproveHoldSuccess, onApproveHoldError, }) {
20487
20663
  // Hooks
20488
20664
  const [isApprovingHold, setIsApprovingHold] = useState(false);
@@ -20515,209 +20691,147 @@ function useApproveHold({ onApproveHoldSuccess, onApproveHoldError, }) {
20515
20691
  return { approveHold, isApprovingHold };
20516
20692
  }
20517
20693
 
20518
- const showErrorNotification$9 = useErrorNotification();
20519
- function usePay({ onPaymentSuccess, onPaymentError, quoteId, invoice, plugin, }) {
20520
- // Hooks
20521
- const { apiHost } = useContext(BunnyContext);
20522
- const customCheckoutFunction = useCustomCheckoutFunction();
20523
- const token = useToken();
20524
- const [isPaying, setIsPaying] = useState(false);
20525
- const pay = async (paymentMethodId) => {
20526
- try {
20527
- setIsPaying(true);
20528
- if (customCheckoutFunction) {
20529
- if (!(plugin === null || plugin === void 0 ? void 0 : plugin.id)) {
20530
- showErrorNotification$9('Plugin ID is required');
20531
- return;
20532
- }
20533
- const response = await customCheckoutFunction(plugin === null || plugin === void 0 ? void 0 : plugin.id.toString(), paymentMethodId.toString());
20534
- onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20535
- }
20536
- else {
20537
- if (!quoteId && !(invoice === null || invoice === void 0 ? void 0 : invoice.id)) {
20538
- showErrorNotification$9('Quote ID or Invoice ID is required');
20539
- return;
20540
- }
20541
- const response = await checkout({
20542
- invoiceId: invoice === null || invoice === void 0 ? void 0 : invoice.id,
20543
- quoteId,
20544
- paymentMethodId,
20545
- token,
20546
- apiHost,
20547
- });
20548
- onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20549
- }
20550
- }
20551
- catch (error) {
20552
- onPaymentError === null || onPaymentError === void 0 ? void 0 : onPaymentError(error);
20553
- }
20554
- finally {
20555
- setIsPaying(false);
20556
- }
20557
- };
20558
- return { pay, isPaying };
20559
- }
20560
-
20561
- var PaymentType;
20562
- (function (PaymentType) {
20563
- PaymentType["PAY"] = "PAY";
20564
- PaymentType["APPROVE_HOLD"] = "APPROVE_HOLD";
20565
- PaymentType["CHECKOUT_NO_PAYMENT"] = "CHECKOUT_NO_PAYMENT";
20566
- })(PaymentType || (PaymentType = {}));
20567
-
20568
- const handleAllErrorFormats$3 = useAllErrorFormats();
20569
- const showErrorNotification$8 = useErrorNotification();
20570
- const useHandlePayment_QuoteFragment = t(`
20571
- fragment useHandlePayment_QuoteFragment on Quote {
20572
- id
20573
- amount
20574
- currencyId
20575
- ...getQuoteAmountDue_QuoteFragment
20576
- }
20577
- `, [getQuoteAmountDue_QuoteFragment]);
20578
- const useHandlePayment = ({ quote: maskedQuote, invoice, onPaymentSuccess, onPaymentHoldSuccess, paymentHoldPrecondition, plugin, paymentHoldOptions, accountId, }) => {
20579
- // Read fragments
20580
- const quote = readFragment(useHandlePayment_QuoteFragment, maskedQuote);
20581
- // Context
20582
- const { defaultPaymentMethod } = usePaymentMethod({ accountId });
20583
- const { apiHost } = useContext(BunnyContext);
20584
- const token = useToken();
20585
- // State
20586
- const [isPaid, setIsPaid] = useState(false);
20587
- const getAmountDue = () => {
20588
- return quote ? getQuoteAmountDue(quote) : invoice === null || invoice === void 0 ? void 0 : invoice.amountDue;
20589
- };
20590
- const formattedAmountDue = () => {
20591
- var _a;
20592
- const amountDue = getAmountDue();
20593
- const currencyId = (_a = quote === null || quote === void 0 ? void 0 : quote.currencyId) !== null && _a !== void 0 ? _a : invoice === null || invoice === void 0 ? void 0 : invoice.currencyId;
20594
- return amountDue !== undefined && currencyId !== undefined ? formatCurrency(amountDue, currencyId).toString() : undefined;
20595
- };
20596
- // Validation checks
20597
- if (quote !== undefined && invoice !== undefined) {
20598
- throw new Error('Either quote or invoice must be provided, not both');
20599
- }
20600
- // Payment hooks
20601
- const { pay: payDemoPay, isPaying: isPayingDemoPay } = usePay$1({
20602
- onPaymentSuccess: handlePaymentSuccess,
20603
- onPaymentError: error => {
20604
- handleAllErrorFormats$3(error);
20605
- },
20606
- quoteId: quote === null || quote === void 0 ? void 0 : quote.id,
20607
- invoice: invoice,
20608
- plugin,
20609
- });
20610
- const { pay: payStripe, isPaying: isPayingStripe } = usePay({
20611
- onPaymentSuccess: handlePaymentSuccess,
20612
- onPaymentError: error => {
20613
- handleAllErrorFormats$3(error);
20614
- },
20615
- quoteId: quote === null || quote === void 0 ? void 0 : quote.id,
20616
- invoice: invoice,
20617
- plugin,
20618
- });
20619
- // Approve hold hooks
20694
+ const showErrorNotification$c = useErrorNotification();
20695
+ function usePaymentHold({ plugin, onPaymentHoldSuccess, onPaymentHoldError, }) {
20696
+ var _a, _b, _c;
20697
+ const pluginName = (_c = (_b = (_a = plugin === null || plugin === void 0 ? void 0 : plugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name;
20620
20698
  const { approveHold: approveHoldStripe, isApprovingHold: isApprovingHoldStripe } = useApproveHold({
20621
- onApproveHoldSuccess: response => {
20622
- onPaymentHoldSuccess === null || onPaymentHoldSuccess === void 0 ? void 0 : onPaymentHoldSuccess(response);
20623
- },
20624
- onApproveHoldError: error => {
20625
- handleAllErrorFormats$3(error);
20626
- },
20699
+ onApproveHoldSuccess: onPaymentHoldSuccess,
20700
+ onApproveHoldError: onPaymentHoldError,
20627
20701
  });
20628
20702
  const { approveHold: approveHoldDemoPay, isApprovingHold: isApprovingHoldDemoPay } = useApproveHold$1({
20629
- onApproveHoldSuccess: response => {
20630
- onPaymentHoldSuccess === null || onPaymentHoldSuccess === void 0 ? void 0 : onPaymentHoldSuccess(response);
20631
- },
20632
- onApproveHoldError: error => {
20633
- handleAllErrorFormats$3(error);
20634
- },
20635
- });
20636
- // Mutations
20637
- const { mutate: checkoutNoPayment, isPending: isCheckingOutNoPayment } = useMutation({
20638
- mutationFn: async () => {
20639
- const amountDue = formattedAmountDue();
20640
- const paymentRequired = amountDue ? parseFloat(amountDue) > 0 : true;
20641
- if (!(quote === null || quote === void 0 ? void 0 : quote.id))
20642
- throw new Error('Quote ID is required');
20643
- if (paymentRequired)
20644
- throw new Error('Payment is required');
20645
- return await checkout({ quoteId: quote.id, token, apiHost });
20646
- },
20647
- onSuccess: () => {
20648
- onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess({});
20649
- },
20650
- onError: error => {
20651
- handleAllErrorFormats$3(error);
20652
- },
20703
+ onApproveHoldSuccess: onPaymentHoldSuccess,
20704
+ onApproveHoldError: onPaymentHoldError,
20653
20705
  });
20654
- const handleApproveHold = async (overridePaymentMethodId) => {
20655
- var _a, _b, _c, _d;
20656
- if (paymentHoldPrecondition) {
20657
- const allowed = await paymentHoldPrecondition();
20658
- if (!allowed)
20659
- return;
20660
- }
20661
- const paymentMethodId = overridePaymentMethodId !== null && overridePaymentMethodId !== void 0 ? overridePaymentMethodId : defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id;
20706
+ const approveHold = async ({ quote, paymentMethodId, }) => {
20707
+ if (!pluginName)
20708
+ return showErrorNotification$c('plugin name is required');
20662
20709
  if (!plugin)
20663
- return showErrorNotification$8('plugin is undefined');
20664
- if (!(quote === null || quote === void 0 ? void 0 : quote.id) || !(quote === null || quote === void 0 ? void 0 : quote.amount))
20665
- return showErrorNotification$8('quote id and amount are required to approve hold');
20666
- if (!paymentMethodId)
20667
- return;
20668
- switch ((_b = (_a = plugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name) {
20710
+ return showErrorNotification$c('plugin is required');
20711
+ switch (pluginName) {
20669
20712
  case 'StripePayment':
20670
- await approveHoldStripe({
20671
- plugin,
20672
- quote: { id: quote.id, amount: quote.amount },
20673
- paymentMethodId,
20674
- });
20675
- break;
20713
+ await approveHoldStripe({ plugin, quote, paymentMethodId });
20714
+ return;
20676
20715
  case 'DemoPayPayment':
20677
- await approveHoldDemoPay({
20716
+ await approveHoldDemoPay({ plugin, quote, paymentMethodId });
20717
+ return;
20718
+ default:
20719
+ showErrorNotification$c(`Payment holds are not supported by ${pluginName}`);
20720
+ return;
20721
+ }
20722
+ };
20723
+ return {
20724
+ approveHold,
20725
+ isApprovingHold: isApprovingHoldStripe || isApprovingHoldDemoPay,
20726
+ };
20727
+ }
20728
+
20729
+ const DIGIT_REGEX = /\d/;
20730
+ const isDigit = (char) => DIGIT_REGEX.test(char);
20731
+ const isDeletion = (char) => [8, 46].includes(char); // Backspace or Delete
20732
+ const isNavigation = (char) => [9, 37, 38, 39, 40].includes(char); // Tab, Arrows
20733
+ const isModifier = (char) => [16, 17, 18].includes(char); // Shift, Ctrl, Alt
20734
+ const isValidKey = (code) => isDeletion(code) || isNavigation(code) || isModifier(code);
20735
+ const isValidExpiry = (expiry) => {
20736
+ const month = parseInt(expiry.substring(0, 2), 10);
20737
+ const year = parseInt(expiry.substring(2, 4), 10) + 2000;
20738
+ if (isNaN(month) || isNaN(year))
20739
+ return false;
20740
+ if (month < 1 || month > 12)
20741
+ return false;
20742
+ return true;
20743
+ };
20744
+ const isCardExpired = (expiry) => {
20745
+ const month = parseInt(expiry.substring(0, 2), 10);
20746
+ const year = parseInt(expiry.substring(2, 4), 10) + 2000;
20747
+ const expiryDate = new Date(year, month, 1);
20748
+ return expiryDate < new Date();
20749
+ };
20750
+ const formatCardExpiry = (cardExpiry) => {
20751
+ if (cardExpiry.length <= 2)
20752
+ return cardExpiry;
20753
+ return cardExpiry.substring(0, 2) + '/' + cardExpiry.substring(2);
20754
+ };
20755
+ // removes spaces from a credit card number
20756
+ const unformatCardNumber = (cardNumber) => {
20757
+ const cardNumberArray = cardNumber.split('');
20758
+ const unformattedCardNumberArray = cardNumberArray.filter(character => character !== ' ');
20759
+ return unformattedCardNumberArray.join('');
20760
+ };
20761
+ const storePayment = async (options, plugin, apiHost, accountId) => {
20762
+ const { testCreditCardNumber, testCreditCardCvc, testCreditCardExpirationDate, token } = options;
20763
+ const response = await invokePlugin({
20764
+ method: 'store_payment_method',
20765
+ plugin,
20766
+ payload: {
20767
+ test_credit_card_number: testCreditCardNumber,
20768
+ test_credit_card_cvc: testCreditCardCvc,
20769
+ test_credit_card_expiration_date: testCreditCardExpirationDate,
20770
+ account_id: accountId,
20771
+ },
20772
+ token,
20773
+ apiHost,
20774
+ });
20775
+ if ((response === null || response === void 0 ? void 0 : response.status) !== 'success')
20776
+ throw new Error(response === null || response === void 0 ? void 0 : response.message);
20777
+ return response;
20778
+ };
20779
+
20780
+ const TEST_CARD = '4242424242424242';
20781
+ function useSave$2({ onSaveSuccess, onSaveError, accountId, }) {
20782
+ const [isSaving, setIsSaving] = useState(false);
20783
+ const { apiHost } = useContext(BunnyContext);
20784
+ const token = useToken();
20785
+ const plugin = useSelectedPlugin();
20786
+ const [cardDetails, setCardDetails] = useDemoPayCardDetails();
20787
+ const save = async () => {
20788
+ try {
20789
+ validateCardDetails();
20790
+ setIsSaving(true);
20791
+ const response = await storePayment({
20792
+ testCreditCardNumber: unformatCardNumber(cardDetails.number),
20793
+ testCreditCardCvc: cardDetails.cvc.toString(),
20794
+ testCreditCardExpirationDate: cardDetails.expiry,
20795
+ token,
20796
+ }, plugin, apiHost, accountId);
20797
+ const paymentMethodId = response.payload[0].id;
20798
+ if (response.status !== 'success')
20799
+ throw new Error(response === null || response === void 0 ? void 0 : response.message);
20800
+ onSaveSuccess === null || onSaveSuccess === void 0 ? void 0 : onSaveSuccess({
20801
+ pluginPaymentResponse: {
20678
20802
  plugin,
20679
- quote: { id: quote.id, amount: quote.amount },
20803
+ token: response.token,
20804
+ savePaymentMethod: true,
20805
+ },
20806
+ savedPaymentMethodResponse: {
20680
20807
  paymentMethodId,
20681
- });
20682
- break;
20683
- default:
20684
- showErrorNotification$8(`Payment holds are not supported by ${(_d = (_c = plugin.components) === null || _c === void 0 ? void 0 : _c.frontend) === null || _d === void 0 ? void 0 : _d[0].name}`);
20685
- break;
20808
+ },
20809
+ });
20810
+ setCardDetails({ number: cardDetails.number, expiry: '', cvc: '' });
20811
+ return paymentMethodId;
20686
20812
  }
20687
- };
20688
- function handlePaymentSuccess(response) {
20689
- setIsPaid(true);
20690
- onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20691
- }
20692
- const handlePayment = async (overridePaymentMethodId) => {
20693
- var _a, _b;
20694
- if (!plugin)
20695
- return;
20696
- switch ((_b = (_a = plugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name) {
20697
- case 'StripePayment':
20698
- await payStripe(overridePaymentMethodId);
20699
- break;
20700
- case 'DemoPayPayment':
20701
- await payDemoPay(overridePaymentMethodId);
20702
- break;
20813
+ catch (error) {
20814
+ onSaveError === null || onSaveError === void 0 ? void 0 : onSaveError(error);
20703
20815
  }
20816
+ finally {
20817
+ setIsSaving(false);
20818
+ }
20819
+ return undefined;
20704
20820
  };
20705
- return {
20706
- handleApproveHold,
20707
- handlePayment,
20708
- handleCheckoutNoPayment: checkoutNoPayment,
20709
- isPaying: isCheckingOutNoPayment ||
20710
- isPayingDemoPay ||
20711
- isPayingStripe ||
20712
- isApprovingHoldDemoPay ||
20713
- isApprovingHoldStripe,
20714
- isPaid,
20715
- paymentType: paymentHoldOptions.payToAccept ? PaymentType.APPROVE_HOLD : getAmountDue() === 0 ? PaymentType.CHECKOUT_NO_PAYMENT : PaymentType.PAY,
20716
- formattedAmountDue: formattedAmountDue(),
20717
- };
20718
- };
20821
+ function validateCardDetails() {
20822
+ if (cardDetails.number !== TEST_CARD)
20823
+ throw new Error('Only the card number 4242 4242 4242 4242 will be accepted.');
20824
+ if (!isValidExpiry(cardDetails.expiry))
20825
+ throw new Error('Invalid expiry date');
20826
+ if (isCardExpired(cardDetails.expiry))
20827
+ throw new Error('Card is expired');
20828
+ if (cardDetails.cvc.length !== 3)
20829
+ throw new Error('Invalid CVC');
20830
+ }
20831
+ return { save, isSaving };
20832
+ }
20719
20833
 
20720
- function useSave({ onSaveSuccess, onSaveError, accountId, }) {
20834
+ function useSave$1({ onSaveSuccess, onSaveError, accountId, }) {
20721
20835
  // Hooks
20722
20836
  const [isSaving, setIsSaving] = useState(false);
20723
20837
  const elements = useElements();
@@ -20793,169 +20907,176 @@ function useSave({ onSaveSuccess, onSaveError, accountId, }) {
20793
20907
  return { save, createStripePaymentMethodId, isSaving };
20794
20908
  }
20795
20909
 
20796
- const useShowPaymentDetailsState = (isOpen = false) => useState(isOpen);
20797
- const [ShowPaymentDetailsProvider, useShowPaymentDetails] = createStateContext(useShowPaymentDetailsState);
20798
-
20799
- const handleAllErrorFormats$2 = useAllErrorFormats();
20800
- const showErrorNotification$7 = useErrorNotification();
20801
- // Contexts
20802
- const [FormattedAmountDueProvider, useFormattedAmountDue] = createValueContext();
20803
- const [PaymentTypeProvider, usePaymentType] = createValueContext();
20804
- const [IsPayingProvider, useIsPaying] = createValueContext();
20805
- const [HandlePaymentFormSubmitProvider, useHandlePaymentFormSubmit] = createValueContext();
20806
- const [IsSavingProvider, useIsSaving] = createValueContext();
20807
- // Used for PaymentMethodFooter to disable pay button if payment is already successful
20808
- const [IsPaidProvider, useIsPaid] = createValueContext();
20809
- // Used for Signup component. Signup uses an apiClient token which api can't infer an account from, so accountId must be used
20810
- const [AccountIdProvider, useAccountId] = createValueContext();
20811
- // Optional external control to disable the pay button (e.g. while quote is updating)
20812
- const [IsPayButtonDisabledProvider, useIsPayButtonDisabled] = createValueContext();
20813
- const PaymentProvider_QuoteFragment = t(`
20814
- fragment PaymentProvider_QuoteFragment on Quote {
20815
- id
20816
- ...useHandlePayment_QuoteFragment
20817
- }
20818
- `, [useHandlePayment_QuoteFragment]);
20819
- function PaymentProvider({ children, accountId, quote: maskedQuote, invoice, onPaymentSuccess, onPaymentHoldSuccess, paymentHoldPrecondition, paymentHoldOptions, onSavePaymentMethod, }) {
20910
+ const handleAllErrorFormats$3 = useAllErrorFormats();
20911
+ const showErrorNotification$b = useErrorNotification();
20912
+ function useSave({ accountId, onSaveSuccess, onSaveError, invalidatePaymentMethodsQuery = true, }) {
20820
20913
  var _a, _b, _c;
20821
- // Read fragments
20822
- const quote = readFragment(PaymentProvider_QuoteFragment, maskedQuote);
20823
- // Context
20824
- const [showPaymentMethodForm, setShowPaymentMethodForm] = useShowPaymentDetails();
20825
20914
  const selectedPlugin = useSelectedPlugin();
20826
- // Hooks
20827
20915
  const queryClient = useQueryClient();
20828
20916
  const token = useToken();
20829
- const { handlePayment, handleApproveHold, handleCheckoutNoPayment, isPaying, isPaid, paymentType, formattedAmountDue, } = useHandlePayment({
20830
- quote,
20831
- invoice,
20832
- onPaymentSuccess: (response) => {
20833
- // TODO: type the response to PaymentSuccessResponse
20834
- // Hide payment method selector and form
20835
- setShowPaymentMethodForm(false);
20836
- onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20837
- },
20838
- onPaymentHoldSuccess,
20839
- paymentHoldPrecondition,
20840
- plugin: selectedPlugin, // TODO: type the response to PluginData
20841
- paymentHoldOptions,
20917
+ const onSaveErrorFinal = onSaveError !== null && onSaveError !== void 0 ? onSaveError : handleAllErrorFormats$3;
20918
+ // Derived state
20919
+ const pluginName = (_c = (_b = (_a = selectedPlugin === null || selectedPlugin === void 0 ? void 0 : selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name;
20920
+ const { save: saveDemoPay, isSaving: isSavingDemoPay } = useSave$2({
20842
20921
  accountId,
20843
- });
20844
- const { defaultPaymentMethod } = usePaymentMethod({ accountId });
20845
- const { save: saveDemoPay, isSaving: isSavingDemoPay } = useSave$1({
20846
- onSaveSuccess: (response) => {
20847
- onSavePaymentMethod === null || onSavePaymentMethod === void 0 ? void 0 : onSavePaymentMethod(response);
20848
- queryClient.invalidateQueries({
20849
- queryKey: QueryKeyFactory.accountPaymentMethodsKey({
20850
- accountId,
20851
- token,
20852
- }),
20853
- });
20854
- setShowPaymentMethodForm(false);
20922
+ onSaveSuccess: response => {
20923
+ onSaveSuccess === null || onSaveSuccess === void 0 ? void 0 : onSaveSuccess(response);
20924
+ if (invalidatePaymentMethodsQuery) {
20925
+ queryClient.invalidateQueries({
20926
+ queryKey: QueryKeyFactory.accountPaymentMethodsKey({ accountId, token }),
20927
+ });
20928
+ }
20855
20929
  },
20856
- onSaveError: handleAllErrorFormats$2,
20857
- accountId,
20930
+ onSaveError: onSaveErrorFinal,
20858
20931
  });
20859
- const { save: saveStripe, isSaving: isSavingStripe } = useSave({
20860
- onSaveSuccess: async (response) => {
20861
- onSavePaymentMethod === null || onSavePaymentMethod === void 0 ? void 0 : onSavePaymentMethod(response);
20862
- queryClient.invalidateQueries({
20863
- queryKey: QueryKeyFactory.accountPaymentMethodsKey({
20864
- accountId,
20865
- token,
20866
- }),
20867
- });
20868
- setShowPaymentMethodForm(false);
20869
- },
20870
- onSaveError: handleAllErrorFormats$2,
20932
+ const { save: saveStripe, isSaving: isSavingStripe } = useSave$1({
20871
20933
  accountId,
20934
+ onSaveSuccess: response => {
20935
+ onSaveSuccess === null || onSaveSuccess === void 0 ? void 0 : onSaveSuccess(response);
20936
+ if (invalidatePaymentMethodsQuery) {
20937
+ queryClient.invalidateQueries({
20938
+ queryKey: QueryKeyFactory.accountPaymentMethodsKey({ accountId, token }),
20939
+ });
20940
+ }
20941
+ },
20942
+ onSaveError: onSaveErrorFinal,
20872
20943
  });
20873
- const pluginName = (_c = (_b = (_a = selectedPlugin === null || selectedPlugin === void 0 ? void 0 : selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name;
20874
- const handleSave = async (demoPayCardDetails) => {
20875
- var _a, _b;
20876
- if (pluginName === 'StripePayment') {
20944
+ const save = async () => {
20945
+ var _a, _b, _c;
20946
+ if (pluginName === 'StripePayment')
20877
20947
  return await saveStripe();
20878
- }
20879
- else if (pluginName === 'DemoPayPayment') {
20880
- return await saveDemoPay(demoPayCardDetails);
20881
- }
20882
- else {
20883
- showErrorNotification$7(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20884
- return undefined;
20885
- }
20948
+ if (pluginName === 'DemoPayPayment')
20949
+ return await saveDemoPay();
20950
+ showErrorNotification$b(`Can not find form for plugin ${(_c = (_b = (_a = selectedPlugin === null || selectedPlugin === void 0 ? void 0 : selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name}`);
20951
+ return undefined;
20886
20952
  };
20887
- const handleApproveHoldFlow = async (savedPaymentMethodId) => {
20888
- var _a, _b;
20889
- if (pluginName === 'StripePayment') {
20890
- await handleApproveHold(savedPaymentMethodId);
20953
+ return { save, isSaving: isSavingDemoPay || isSavingStripe };
20954
+ }
20955
+
20956
+ // Used for Signup component. Signup needs a custom checkout function
20957
+ const [CustomCheckoutFunctionProvider, useCustomCheckoutFunction] = createValueContext();
20958
+
20959
+ const showErrorNotification$a = useErrorNotification();
20960
+ function usePay$2({ onPaymentSuccess, onPaymentError, quoteId, invoiceId, plugin, }) {
20961
+ const { apiHost } = useContext(BunnyContext);
20962
+ const customCheckoutFunction = useCustomCheckoutFunction();
20963
+ const token = useToken();
20964
+ const [isPaying, setIsPaying] = useState(false);
20965
+ const pay = async (paymentMethodId) => {
20966
+ try {
20967
+ setIsPaying(true);
20968
+ if (customCheckoutFunction) {
20969
+ if (!(plugin === null || plugin === void 0 ? void 0 : plugin.id)) {
20970
+ showErrorNotification$a('Plugin ID is required');
20971
+ return;
20972
+ }
20973
+ const response = await customCheckoutFunction(plugin === null || plugin === void 0 ? void 0 : plugin.id.toString(), paymentMethodId.toString());
20974
+ onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20975
+ }
20976
+ else {
20977
+ const response = await checkout({
20978
+ invoiceId,
20979
+ quoteId,
20980
+ paymentMethodId,
20981
+ token,
20982
+ apiHost,
20983
+ });
20984
+ onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20985
+ }
20891
20986
  }
20892
- else if (pluginName === 'DemoPayPayment') {
20893
- await handleApproveHold(savedPaymentMethodId);
20987
+ catch (error) {
20988
+ onPaymentError === null || onPaymentError === void 0 ? void 0 : onPaymentError(error);
20894
20989
  }
20895
- else {
20896
- showErrorNotification$7(`Can not find form for plugin ${(_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name}`);
20990
+ finally {
20991
+ setIsPaying(false);
20897
20992
  }
20898
20993
  };
20899
- const handlePaymentFormSubmit = async (demoPayCardDetails) => {
20900
- if (paymentType === PaymentType.CHECKOUT_NO_PAYMENT) {
20901
- handleCheckoutNoPayment();
20902
- return;
20903
- }
20904
- if (paymentType === PaymentType.APPROVE_HOLD) {
20905
- // If payment method form is shown, save payment method first, then approve hold
20906
- const savedPaymentMethodId = showPaymentMethodForm
20907
- ? await handleSave(demoPayCardDetails)
20908
- : undefined;
20909
- await handleApproveHoldFlow(savedPaymentMethodId);
20910
- return;
20911
- }
20912
- if (formattedAmountDue === undefined) {
20913
- // If amount due is undefined, we need to save a payment method only
20914
- await handleSave(demoPayCardDetails);
20915
- return;
20916
- }
20917
- if (paymentType === PaymentType.PAY) {
20918
- if (showPaymentMethodForm) {
20919
- // If paying and showing payment method form, save payment method first, then pay (1 click for user convenience)
20920
- const savedPaymentMethodId = await handleSave(demoPayCardDetails);
20921
- if (savedPaymentMethodId) {
20922
- await handlePayment(savedPaymentMethodId);
20994
+ return { pay, isPaying };
20995
+ }
20996
+
20997
+ const showErrorNotification$9 = useErrorNotification();
20998
+ function usePay$1({ onPaymentSuccess, onPaymentError, quoteId, invoiceId, plugin, }) {
20999
+ // Hooks
21000
+ const { apiHost } = useContext(BunnyContext);
21001
+ const customCheckoutFunction = useCustomCheckoutFunction();
21002
+ const token = useToken();
21003
+ const [isPaying, setIsPaying] = useState(false);
21004
+ const pay = async (paymentMethodId) => {
21005
+ try {
21006
+ setIsPaying(true);
21007
+ if (customCheckoutFunction) {
21008
+ if (!(plugin === null || plugin === void 0 ? void 0 : plugin.id)) {
21009
+ showErrorNotification$9('Plugin ID is required');
21010
+ return;
20923
21011
  }
21012
+ const response = await customCheckoutFunction(plugin === null || plugin === void 0 ? void 0 : plugin.id.toString(), paymentMethodId.toString());
21013
+ onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20924
21014
  }
20925
21015
  else {
20926
- if (!(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id)) {
20927
- showErrorNotification$7('Default payment method ID is undefined');
21016
+ if (!quoteId && !invoiceId) {
21017
+ showErrorNotification$9('Quote ID or Invoice ID is required');
20928
21018
  return;
20929
21019
  }
20930
- await handlePayment(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id);
21020
+ const response = await checkout({
21021
+ invoiceId,
21022
+ quoteId,
21023
+ paymentMethodId,
21024
+ token,
21025
+ apiHost,
21026
+ });
21027
+ onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
20931
21028
  }
20932
- return;
21029
+ }
21030
+ catch (error) {
21031
+ onPaymentError === null || onPaymentError === void 0 ? void 0 : onPaymentError(error);
21032
+ }
21033
+ finally {
21034
+ setIsPaying(false);
20933
21035
  }
20934
21036
  };
20935
- const providers = [
20936
- [FormattedAmountDueProvider, { value: formattedAmountDue }],
20937
- [PaymentTypeProvider, { value: paymentType }],
20938
- [IsPayingProvider, { value: isPaying }],
20939
- [HandlePaymentFormSubmitProvider, { value: handlePaymentFormSubmit }],
20940
- [IsSavingProvider, { value: isSavingDemoPay || isSavingStripe }],
20941
- [IsPaidProvider, { value: isPaid }],
20942
- [AccountIdProvider, { value: accountId }],
20943
- ];
20944
- return (jsx(Fragment, { children: providers.reduceRight((acc, [Provider, props]) => createElement(Provider, props, acc), children) }));
21037
+ return { pay, isPaying };
20945
21038
  }
20946
- function StripeWrapper({ children, currencyId, accountId, }) {
20947
- const plugin = useSelectedPlugin();
20948
- const { stripe, options } = useStripePlugin(plugin, currencyId, accountId);
20949
- return (jsx(Elements, { options: options, stripe: stripe, children: children }));
21039
+
21040
+ const showErrorNotification$8 = useErrorNotification();
21041
+ function usePay({ plugin, quoteId, invoiceId, onPaymentSuccess, onPaymentError, }) {
21042
+ var _a, _b, _c;
21043
+ const pluginName = (_c = (_b = (_a = plugin === null || plugin === void 0 ? void 0 : plugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name;
21044
+ const { pay: payStripe, isPaying: isPayingStripe } = usePay$1({
21045
+ onPaymentSuccess,
21046
+ onPaymentError,
21047
+ quoteId,
21048
+ invoiceId,
21049
+ plugin,
21050
+ });
21051
+ const { pay: payDemoPay, isPaying: isPayingDemoPay } = usePay$2({
21052
+ onPaymentSuccess,
21053
+ onPaymentError,
21054
+ quoteId,
21055
+ invoiceId,
21056
+ plugin,
21057
+ });
21058
+ const pay = async (paymentMethodId) => {
21059
+ if (!pluginName)
21060
+ return showErrorNotification$8('plugin name is required');
21061
+ switch (pluginName) {
21062
+ case 'StripePayment':
21063
+ await payStripe(paymentMethodId);
21064
+ return;
21065
+ case 'DemoPayPayment':
21066
+ await payDemoPay(paymentMethodId);
21067
+ return;
21068
+ default:
21069
+ showErrorNotification$8(`Payments are not supported by ${pluginName}`);
21070
+ return;
21071
+ }
21072
+ };
21073
+ return { pay, isPaying: isPayingStripe || isPayingDemoPay };
20950
21074
  }
20951
21075
 
20952
- function usePaymentButtonText() {
20953
- const formattedAmountDue = useFormattedAmountDue();
20954
- const paymentType = usePaymentType();
20955
- const isPaid = useIsPaid();
20956
- const isSaving = useIsSaving();
20957
- const isPaying = useIsPaying();
20958
- const isProcessing = isSaving || isPaying;
21076
+ function usePaymentButtonText(props = {}) {
21077
+ const { isSaving, isPaying, isApprovingHold } = props;
21078
+ const { formattedAmountDue, paymentType, isPaid } = usePayable();
21079
+ const isProcessing = isSaving || isPaying || isApprovingHold;
20959
21080
  // If payment is already successful, show the amount paid
20960
21081
  if (isPaid) {
20961
21082
  return 'Paid ' + (formattedAmountDue !== null && formattedAmountDue !== void 0 ? formattedAmountDue : '');
@@ -20970,34 +21091,95 @@ function usePaymentButtonText() {
20970
21091
  return paymentTypeText + ' ' + (formattedAmountDue !== null && formattedAmountDue !== void 0 ? formattedAmountDue : '');
20971
21092
  }
20972
21093
 
20973
- const CheckoutFooter = () => {
20974
- const isPaid = useIsPaid();
20975
- const isMobile = useIsMobile();
20976
- const isPaying = useIsPaying();
20977
- const isPayButtonDisabled = useIsPayButtonDisabled();
20978
- const paymentButtonText = usePaymentButtonText();
20979
- const onPaymentFormSubmit = useHandlePaymentFormSubmit();
20980
- return (jsx(Button, { className: "bunny-w-full", disabled: isPaying || isPaid || isPayButtonDisabled, onClick: () => onPaymentFormSubmit(), size: isMobile ? 'large' : 'middle', type: "primary", children: paymentButtonText }));
20981
- };
20982
-
21094
+ const handleAllErrorFormats$2 = useAllErrorFormats();
21095
+ const showErrorNotification$7 = useErrorNotification();
21096
+ function Pay(props = {}) {
21097
+ const { disabled } = props;
21098
+ const isMobile = useIsMobile();
21099
+ const { onPaymentSuccess, onPaymentHoldSuccess, onSavePaymentMethod, paymentHoldPrecondition } = usePaymentFormCallbacks();
21100
+ const selectedPlugin = useSelectedPlugin();
21101
+ const { quoteId, invoiceId, paymentHold, paymentType, setIsPaid } = usePayable();
21102
+ const accountId = useAccountId();
21103
+ const { defaultPaymentMethod } = usePaymentMethod({ accountId });
21104
+ const [showPaymentDetailsForm, setShowPaymentDetailsForm] = useShowPaymentDetails();
21105
+ const { save, isSaving } = useSave({
21106
+ accountId,
21107
+ onSaveSuccess: (response) => {
21108
+ onSavePaymentMethod === null || onSavePaymentMethod === void 0 ? void 0 : onSavePaymentMethod(response);
21109
+ },
21110
+ onSaveError: handleAllErrorFormats$2,
21111
+ invalidatePaymentMethodsQuery: true,
21112
+ });
21113
+ const { pay, isPaying } = usePay({
21114
+ plugin: selectedPlugin,
21115
+ quoteId,
21116
+ invoiceId,
21117
+ onPaymentSuccess: (response) => {
21118
+ setIsPaid(true);
21119
+ setShowPaymentDetailsForm(false);
21120
+ onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
21121
+ },
21122
+ onPaymentError: error => handleAllErrorFormats$2(error),
21123
+ });
21124
+ const { approveHold, isApprovingHold } = usePaymentHold({
21125
+ plugin: selectedPlugin,
21126
+ onPaymentHoldSuccess: onPaymentHoldSuccess,
21127
+ onPaymentHoldError: error => handleAllErrorFormats$2(error),
21128
+ });
21129
+ const paymentButtonText = usePaymentButtonText({ isSaving, isPaying, isApprovingHold });
21130
+ async function handlePayment() {
21131
+ if (paymentType === PaymentType.APPROVE_HOLD) {
21132
+ if (paymentHoldPrecondition) {
21133
+ const allowed = await paymentHoldPrecondition();
21134
+ if (!allowed)
21135
+ return;
21136
+ }
21137
+ if (!paymentHold) {
21138
+ showErrorNotification$7('Payment hold is undefined');
21139
+ return;
21140
+ }
21141
+ // If payment details form is shown, save payment method first, then approve hold
21142
+ if (showPaymentDetailsForm) {
21143
+ const savedPaymentMethodId = await save();
21144
+ if (!savedPaymentMethodId) {
21145
+ showErrorNotification$7('Saved payment method ID is undefined');
21146
+ return;
21147
+ }
21148
+ await approveHold({ quote: { id: paymentHold.quoteId, amount: paymentHold.amountToHold }, paymentMethodId: savedPaymentMethodId });
21149
+ return;
21150
+ }
21151
+ if (!(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id)) {
21152
+ showErrorNotification$7('Default payment method ID is undefined');
21153
+ return;
21154
+ }
21155
+ await approveHold({ quote: { id: paymentHold.quoteId, amount: paymentHold.amountToHold }, paymentMethodId: defaultPaymentMethod.id });
21156
+ return;
21157
+ }
21158
+ if (paymentType === PaymentType.PAY) {
21159
+ if (showPaymentDetailsForm) {
21160
+ // If paying and showing payment method form, save payment method first, then pay (1 click for user convenience)
21161
+ const savedPaymentMethodId = await save();
21162
+ if (savedPaymentMethodId) {
21163
+ await pay(savedPaymentMethodId);
21164
+ }
21165
+ }
21166
+ else {
21167
+ if (!(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id)) {
21168
+ showErrorNotification$7('Default payment method ID is undefined');
21169
+ return;
21170
+ }
21171
+ await pay(defaultPaymentMethod === null || defaultPaymentMethod === void 0 ? void 0 : defaultPaymentMethod.id);
21172
+ }
21173
+ return;
21174
+ }
21175
+ }
21176
+ return (jsx(Button, { className: "bunny-w-full", disabled: isPaying || isApprovingHold || isSaving || disabled, loading: isPaying || isApprovingHold || isSaving, onClick: handlePayment, size: isMobile ? 'large' : 'middle', type: "primary", children: paymentButtonText }));
21177
+ }
21178
+
20983
21179
  const defaultStyled = typeof styled === "function" ? styled : styled.default;
20984
21180
  // !!! This is a workaround to avoid the issue with styled-components not supporting ESM
20985
21181
  // Workaround from https://github.com/styled-components/styled-components/issues/3437#issuecomment-1103085056
20986
21182
 
20987
- function PaymentMethodFooter({ onSubmit, noPadding, }) {
20988
- const isPaying = useIsPaying();
20989
- const isSaving = useIsSaving();
20990
- const formattedAmountDue = useFormattedAmountDue();
20991
- const paymentButtonText = usePaymentButtonText();
20992
- const isMobile = useIsMobile();
20993
- const [, setShowPaymentMethodForm] = useShowPaymentDetails();
20994
- // If amount due is undefined, show the save button
20995
- const payableAvailable = formattedAmountDue !== undefined;
20996
- const saveButtonText = isSaving ? 'Saving' : 'Save';
20997
- const buttonText = payableAvailable ? paymentButtonText : saveButtonText;
20998
- return (jsxs("div", { className: `bunny-flex bunny-justify-end bunny-gap-2 ${noPadding ? '' : 'pt-6'}`, children: [jsx(Button, { className: "bunny-w-full", size: isMobile ? 'large' : 'middle', onClick: () => setShowPaymentMethodForm(false), children: "Cancel" }), jsx(Button, { className: "bunny-w-full", disabled: isSaving || isPaying, loading: isSaving || isPaying, onClick: onSubmit, size: isMobile ? 'large' : 'middle', type: "primary", children: buttonText })] }));
20999
- }
21000
-
21001
21183
  const DemoPayCardCvc = ({ autoFocus, onChange, placeholder, value, }) => {
21002
21184
  const onKeyPress = (event) => {
21003
21185
  if (!isDigit(event.key) && !isValidKey(event.keyCode))
@@ -21037,46 +21219,19 @@ const DemoPayExpiry = ({ autoFocus, onChange, placeholder, value, }) => {
21037
21219
  };
21038
21220
 
21039
21221
  const { Text: Text$C } = Typography;
21040
- const handleAllErrorFormats$1 = useAllErrorFormats();
21041
- const TEST_CARD = '4242424242424242';
21042
21222
  const DemoPayForm = () => {
21043
- // Context
21044
21223
  const { darkMode } = useContext(BunnyContext);
21045
- const onPaymentFormSubmit = useHandlePaymentFormSubmit();
21046
- const [cardDetails, setCardDetails] = useState({
21047
- number: '4242424242424242',
21048
- expiry: '',
21049
- cvc: '',
21050
- });
21051
- function onSubmit() {
21052
- try {
21053
- validateCardDetails();
21054
- onPaymentFormSubmit(cardDetails);
21055
- }
21056
- catch (error) {
21057
- handleAllErrorFormats$1(error);
21058
- }
21059
- }
21060
- function validateCardDetails() {
21061
- if (cardDetails.number !== TEST_CARD)
21062
- throw new Error('Only the card number 4242 4242 4242 4242 will be accepted.');
21063
- if (!isValidExpiry(cardDetails.expiry))
21064
- throw new Error('Invalid expiry date');
21065
- if (isCardExpired(cardDetails.expiry))
21066
- throw new Error('Card is expired');
21067
- if (cardDetails.cvc.length !== 3)
21068
- throw new Error('Invalid CVC');
21069
- }
21224
+ const [cardDetails, setCardDetails] = useDemoPayCardDetails();
21070
21225
  const onCardNumberChange = (number) => {
21071
- setCardDetails({ ...cardDetails, number });
21226
+ setCardDetails(prev => ({ ...prev, number }));
21072
21227
  };
21073
21228
  const onCardExpiryChange = (expiry) => {
21074
- setCardDetails({ ...cardDetails, expiry });
21229
+ setCardDetails(prev => ({ ...prev, expiry }));
21075
21230
  };
21076
21231
  const onCardCvcChange = (cvc) => {
21077
- setCardDetails({ ...cardDetails, cvc });
21232
+ setCardDetails(prev => ({ ...prev, cvc }));
21078
21233
  };
21079
- return (jsxs("div", { className: "bunny-flex bunny-flex-col bunny-gap-2", children: [jsxs(StyledInputs, { className: "bunny-flex bunny-flex-col bunny-gap-2", darkMode: darkMode !== null && darkMode !== void 0 ? darkMode : false, children: [jsx(DemoPayCardNumber, { onChange: onCardNumberChange, value: cardDetails.number }), jsxs("div", { className: "bunny-flex bunny-gap-2", children: [jsx(DemoPayExpiry, { onChange: onCardExpiryChange, value: cardDetails.expiry }), jsx(DemoPayCardCvc, { onChange: onCardCvcChange, value: cardDetails.cvc })] })] }), jsx(Text$C, { children: "DemoPay is for testing only." }), jsx(PaymentMethodFooter, { onSubmit: onSubmit })] }));
21234
+ return (jsxs("div", { className: "bunny-flex bunny-flex-col bunny-gap-2", children: [jsxs(StyledInputs, { className: "bunny-flex bunny-flex-col bunny-gap-2", darkMode: darkMode !== null && darkMode !== void 0 ? darkMode : false, children: [jsx(DemoPayCardNumber, { onChange: onCardNumberChange, value: cardDetails.number }), jsxs("div", { className: "bunny-flex bunny-gap-2", children: [jsx(DemoPayExpiry, { onChange: onCardExpiryChange, value: cardDetails.expiry }), jsx(DemoPayCardCvc, { onChange: onCardCvcChange, value: cardDetails.cvc })] })] }), jsx(Text$C, { children: "DemoPay is for testing only." })] }));
21080
21235
  };
21081
21236
  const StyledInputs = defaultStyled.div `
21082
21237
  .ant-input {
@@ -21088,8 +21243,6 @@ const StyledInputs = defaultStyled.div `
21088
21243
  `;
21089
21244
 
21090
21245
  const StripeForm = () => {
21091
- // Context
21092
- const onPaymentFormSubmit = useHandlePaymentFormSubmit();
21093
21246
  // State
21094
21247
  const [isIdealWarningOpen, setIsIdealWarningOpen] = useState(false);
21095
21248
  const idealWarningHasShown = useRef(false);
@@ -21101,25 +21254,19 @@ const StripeForm = () => {
21101
21254
  };
21102
21255
  return (jsxs("form", { children: [jsx(PaymentElement, { options: {
21103
21256
  layout: { type: 'accordion' },
21104
- }, onChange: onChange }), jsx(PaymentMethodFooter, { onSubmit: onPaymentFormSubmit }), jsx(IdealWarningModal, { isOpen: isIdealWarningOpen, onClose: () => setIsIdealWarningOpen(false) })] }));
21257
+ }, onChange: onChange }), jsx(IdealWarningModal, { isOpen: isIdealWarningOpen, onClose: () => setIsIdealWarningOpen(false) })] }));
21105
21258
  };
21106
21259
  function IdealWarningModal({ isOpen, onClose }) {
21107
21260
  return (jsx(Modal, { open: isOpen, onCancel: onClose, footer: jsx(Button, { type: "primary", onClick: onClose, children: "Ok" }), children: jsx("div", { className: "bunny-p-4", children: "iDEAL will charge a 1 cent fee when storing this payment method. You will be refunded later." }) }));
21108
21261
  }
21109
21262
 
21110
- const PaymentMethodDetails = () => {
21111
- var _a, _b;
21112
- const selectedPlugin = useSelectedPlugin();
21113
- switch ((_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name) {
21114
- case 'StripePayment':
21115
- return jsx(StripeForm, {});
21116
- case 'DemoPayPayment':
21117
- return jsx(DemoPayForm, {});
21118
- default:
21119
- console.warn('Can not find form for plugin', selectedPlugin);
21120
- return jsx(Fragment, {});
21121
- }
21122
- };
21263
+ const [PaymentMethodFormVisibilityProvider, usePaymentMethodFormVisibility] = createValueContext();
21264
+
21265
+ function Cancel() {
21266
+ const isMobile = useIsMobile();
21267
+ const [, setShowPaymentMethodForm] = useShowPaymentDetails();
21268
+ return (jsx(Button, { className: "bunny-w-full", size: isMobile ? 'large' : 'middle', onClick: () => setShowPaymentMethodForm(false), children: "Cancel" }));
21269
+ }
21123
21270
 
21124
21271
  const CardIcon = ({ className }) => {
21125
21272
  return (jsxs("svg", { className: className, width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsx("path", { d: "M15 3.75H3C2.17157 3.75 1.5 4.42157 1.5 5.25V12.75C1.5 13.5784 2.17157 14.25 3 14.25H15C15.8284 14.25 16.5 13.5784 16.5 12.75V5.25C16.5 4.42157 15.8284 3.75 15 3.75Z", stroke: SLATE_400, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), jsx("path", { d: "M1.5 7.5H16.5", stroke: SLATE_400, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }));
@@ -21154,48 +21301,72 @@ const PaymentOptionContainer = defaultStyled.div `
21154
21301
  }
21155
21302
  `;
21156
21303
 
21157
- // Automatically sets the default payment method if there is none currently set
21158
- const useAutoSetDefaultPaymentMethod = ({ handleSetDefault, setDefaultPaymentMethodLoading, enabled = true, }) => {
21304
+ const handleAllErrorFormats$1 = useAllErrorFormats();
21305
+ function Save() {
21159
21306
  const accountId = useAccountId();
21160
- const token = useToken();
21161
- const queryClient = useQueryClient();
21162
- const { paymentMethods, defaultPaymentMethod } = usePaymentMethod({
21307
+ const isMobile = useIsMobile();
21308
+ const { onSavePaymentMethod } = usePaymentFormCallbacks();
21309
+ const [_, setShowPaymentDetailsForm] = useShowPaymentDetails();
21310
+ const { save: handleSave, isSaving } = useSave({
21163
21311
  accountId,
21312
+ onSaveSuccess: (response) => {
21313
+ onSavePaymentMethod === null || onSavePaymentMethod === void 0 ? void 0 : onSavePaymentMethod(response);
21314
+ setShowPaymentDetailsForm(false);
21315
+ },
21316
+ onSaveError: handleAllErrorFormats$1,
21317
+ invalidatePaymentMethodsQuery: true,
21164
21318
  });
21165
- // Set the default payment method on the cache. Prevents 'handleSetDefault' from being called too many times.
21166
- function setDefaultPaymentMethodOnCache(targetPaymentMethod) {
21167
- let cachedPaymentMethods = queryClient.getQueryData(QueryKeyFactory.accountPaymentMethodsKey({
21168
- accountId,
21169
- token,
21170
- }));
21171
- if (cachedPaymentMethods) {
21172
- for (const paymentMethod of cachedPaymentMethods) {
21173
- paymentMethod.isDefault = paymentMethod.id === targetPaymentMethod.id;
21174
- }
21175
- queryClient.setQueryData(QueryKeyFactory.accountPaymentMethodsKey({
21176
- accountId,
21177
- token,
21178
- }), cachedPaymentMethods);
21319
+ const saveButtonText = isSaving ? 'Saving' : 'Save';
21320
+ return (jsx(Button, { className: "bunny-w-full", disabled: isSaving, loading: isSaving, onClick: handleSave, size: isMobile ? 'large' : 'middle', type: "primary", children: saveButtonText }));
21321
+ }
21322
+
21323
+ const showErrorNotification$6 = useErrorNotification();
21324
+ function PaymentMethodDetailsRoot(props = {}) {
21325
+ var _a, _b;
21326
+ const { footer } = props;
21327
+ const { showPaymentMethodForm, setShowPaymentMethodForm } = usePaymentMethodFormVisibility();
21328
+ const selectedPlugin = useSelectedPlugin();
21329
+ const accountId = useAccountId();
21330
+ const { paymentMethods } = usePaymentMethod({ accountId });
21331
+ const { paymentPlugins } = usePaymentPlugins(accountId);
21332
+ function handleClickAddPaymentMethod() {
21333
+ if ((paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.length) === 0) {
21334
+ showErrorNotification$6('No payment plugins available', 'Error adding payment method');
21335
+ }
21336
+ else {
21337
+ setShowPaymentMethodForm(true);
21179
21338
  }
21180
21339
  }
21340
+ // Automatically show the payment method form if there are no payment methods
21181
21341
  useEffect(() => {
21182
- var _a;
21183
- if (setDefaultPaymentMethodLoading || !enabled)
21184
- return;
21185
- if ((paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) && (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) > 0 && !defaultPaymentMethod) {
21186
- handleSetDefault((_a = paymentMethods[0]) === null || _a === void 0 ? void 0 : _a.id);
21187
- setDefaultPaymentMethodOnCache(paymentMethods[0]);
21342
+ if ((paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) === 0) {
21343
+ setShowPaymentMethodForm(true);
21188
21344
  }
21189
- }, [
21190
- paymentMethods,
21191
- defaultPaymentMethod,
21192
- handleSetDefault,
21193
- setDefaultPaymentMethodLoading,
21194
- queryClient,
21195
- accountId,
21196
- token,
21197
- ]);
21345
+ }, [paymentMethods]);
21346
+ return (jsx("div", { className: "bunny-flex bunny-flex-col", children: jsx(Collapse, { bordered: false, activeKey: showPaymentMethodForm ? '1' : undefined, ghost: true, collapsible: "disabled", destroyOnHidden: true, items: [
21347
+ {
21348
+ key: '1',
21349
+ showArrow: false,
21350
+ label: !showPaymentMethodForm ? (jsx("div", { className: "bunny-pt-2", children: jsx(Button, { onClick: handleClickAddPaymentMethod, type: "default", className: "bunny-w-full", id: "addPaymentMethod", children: "Add payment method" }) })) : null,
21351
+ children: (jsxs("div", { className: "bunny-flex bunny-flex-col bunny-gap-2 bunny-mt-2", children: [jsx(PaymentMethodSelector, {}), selectedPlugin && (jsx("div", { className: "bunny-flex bunny-flex-col", children: getPaymentMethodForm((_b = (_a = selectedPlugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0].name) })), footer] })),
21352
+ },
21353
+ ] }) }));
21354
+ }
21355
+ const getPaymentMethodForm = (pluginName) => {
21356
+ switch (pluginName) {
21357
+ case 'StripePayment':
21358
+ return jsx(StripeForm, {});
21359
+ case 'DemoPayPayment':
21360
+ return jsx(DemoPayForm, {});
21361
+ default:
21362
+ console.warn('Can not find form for plugin', pluginName);
21363
+ return jsx(Fragment, {});
21364
+ }
21198
21365
  };
21366
+ const PaymentMethodDetails = Object.assign(PaymentMethodDetailsRoot, {
21367
+ Cancel,
21368
+ Save
21369
+ });
21199
21370
 
21200
21371
  const query$8 = t(`
21201
21372
  query GetCurrentUserData {
@@ -21253,113 +21424,6 @@ function usePayableCurrency({ payableCurrencyId, accountCurrencyId }) {
21253
21424
  return { currencyId, isCurrencyIdLoading: isCurrentUserDataLoading };
21254
21425
  }
21255
21426
 
21256
- function useRemovePaymentMethod(onPaymentMethodRemoved, onError) {
21257
- const queryClient = useQueryClient();
21258
- const showErrorNotification = useErrorNotification();
21259
- const showSuccessNotification = useSuccessNotification();
21260
- const accountId = useAccountId();
21261
- const { paymentPlugins } = usePaymentPlugins(accountId);
21262
- const token = useToken();
21263
- const { apiHost } = useContext(BunnyContext);
21264
- const removePaymentMethod = useCallback(async (data) => {
21265
- const plugin = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.find(paymentPlugin => { var _a; return String(paymentPlugin.id) === String((_a = data === null || data === void 0 ? void 0 : data.plugin) === null || _a === void 0 ? void 0 : _a.id); });
21266
- if (data && plugin) {
21267
- await invokePlugin({
21268
- plugin: plugin, // TODO: type the plugin
21269
- method: 'remove_payment_method',
21270
- payload: {
21271
- payment_method_id: data.id,
21272
- account_id: accountId,
21273
- },
21274
- token,
21275
- apiHost,
21276
- })
21277
- .then(() => {
21278
- showSuccessNotification('Payment method was removed', 'Success');
21279
- queryClient.invalidateQueries({
21280
- queryKey: QueryKeyFactory.accountPaymentMethodsKey({
21281
- token,
21282
- accountId,
21283
- }),
21284
- });
21285
- onPaymentMethodRemoved === null || onPaymentMethodRemoved === void 0 ? void 0 : onPaymentMethodRemoved(data);
21286
- })
21287
- .catch((error) => {
21288
- showErrorNotification(error.message, 'Error removing payment method');
21289
- });
21290
- }
21291
- else {
21292
- const availablePlugins = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.map(plugin => {
21293
- var _a, _b;
21294
- const components = plugin.components;
21295
- return (_b = (_a = components === null || components === void 0 ? void 0 : components.frontend) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.name;
21296
- });
21297
- onError === null || onError === void 0 ? void 0 : onError(`Either the payment method was not found or the plugin was not present. Available plugins: ${availablePlugins === null || availablePlugins === void 0 ? void 0 : availablePlugins.join(', ')}`);
21298
- }
21299
- }, [paymentPlugins, token, apiHost, queryClient, onPaymentMethodRemoved, accountId, onError]);
21300
- return removePaymentMethod;
21301
- }
21302
-
21303
- function useSetDefaultPaymentMethod(onError, onSuccess) {
21304
- // Context
21305
- const accountId = useAccountId();
21306
- const token = useToken();
21307
- const { apiHost } = useContext(BunnyContext);
21308
- const { paymentPlugins } = usePaymentPlugins(accountId);
21309
- const { paymentMethods } = usePaymentMethod({ accountId });
21310
- // Hooks
21311
- const showErrorNotification = useErrorNotification();
21312
- const showSuccessNotification = useSuccessNotification();
21313
- const queryClient = useQueryClient();
21314
- // State
21315
- const [loading, setLoading] = useState(false);
21316
- const setDefaultPaymentMethod = useCallback(async (paymentMethodId) => {
21317
- const paymentMethod = paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.find(paymentMethod => (paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.id) === paymentMethodId);
21318
- if (paymentMethod === undefined) {
21319
- showErrorNotification('Payment method not found', 'Error setting default payment method');
21320
- return;
21321
- }
21322
- const plugin = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.find(paymentPlugin => { var _a; return String(paymentPlugin.id) === ((_a = paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.plugin) === null || _a === void 0 ? void 0 : _a.id); }); // TODO: type the response to PluginData
21323
- if (plugin) {
21324
- setLoading(true);
21325
- await invokePlugin({
21326
- plugin: plugin,
21327
- method: 'assign_default_payment_method',
21328
- payload: {
21329
- payment_method_id: paymentMethod.id,
21330
- account_id: accountId,
21331
- },
21332
- token,
21333
- apiHost,
21334
- })
21335
- .then(() => {
21336
- var _a;
21337
- showSuccessNotification(`Payment method ${(_a = paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.metadata) === null || _a === void 0 ? void 0 : _a.identifier} was set as default`, 'Success');
21338
- onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess();
21339
- queryClient.invalidateQueries({
21340
- queryKey: QueryKeyFactory.accountPaymentMethodsKey({
21341
- token,
21342
- accountId,
21343
- }),
21344
- });
21345
- })
21346
- .catch((error) => {
21347
- setLoading(false);
21348
- showErrorNotification(error.message, 'Error removing payment method');
21349
- })
21350
- .finally(() => {
21351
- setLoading(false);
21352
- });
21353
- }
21354
- else {
21355
- const availablePlugins = paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.map((plugin) => { var _a, _b, _c; return (_c = (_b = (_a = plugin.components) === null || _a === void 0 ? void 0 : _a.frontend) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name; });
21356
- onError === null || onError === void 0 ? void 0 : onError(`Either the payment method was not found or the plugin was not present. Available plugins: ${availablePlugins === null || availablePlugins === void 0 ? void 0 : availablePlugins.join(', ')}`);
21357
- }
21358
- }, [paymentPlugins, token, apiHost, queryClient]);
21359
- return { setDefaultPaymentMethod, loading };
21360
- }
21361
-
21362
- const showErrorNotification$6 = useErrorNotification();
21363
21427
  const PaymentForm_QuoteFragment = t(`
21364
21428
  fragment PaymentForm_QuoteFragment on Quote {
21365
21429
  id
@@ -21367,10 +21431,18 @@ const PaymentForm_QuoteFragment = t(`
21367
21431
  ...PaymentProvider_QuoteFragment
21368
21432
  }
21369
21433
  `, [PaymentProvider_QuoteFragment]);
21370
- function PaymentForm({ currencyId: accountCurrencyId, invoice, quote: maskedQuote, onPaymentSuccess, onPaymentHoldSuccess, paymentHoldPrecondition, accountId, onSavePaymentMethod, onPaymentMethodRemoved, onSetDefaultPaymentMethod, overrideToken, customCheckoutFunction, paymentHoldOptions, disablePayButton, }) {
21434
+ const PaymentForm_InvoiceFragment = t(`
21435
+ fragment PaymentForm_InvoiceFragment on Invoice {
21436
+ id
21437
+ currencyId
21438
+ ...PaymentProvider_InvoiceFragment
21439
+ }
21440
+ `, [PaymentProvider_InvoiceFragment]);
21441
+ function PaymentFormRoot({ currencyId: accountCurrencyId, invoice: maskedInvoice, quote: maskedQuote, paymentHold, onPaymentSuccess, onPaymentHoldSuccess, paymentHoldPrecondition, accountId, onSavePaymentMethod, onPaymentMethodRemoved, onSetDefaultPaymentMethod, overrideToken, customCheckoutFunction, children, }) {
21371
21442
  var _a;
21372
21443
  // Read fragments
21373
21444
  const quote = readFragment(PaymentForm_QuoteFragment, maskedQuote);
21445
+ const invoice = readFragment(PaymentForm_InvoiceFragment, maskedInvoice);
21374
21446
  const { currencyId, isCurrencyIdLoading } = usePayableCurrency({
21375
21447
  payableCurrencyId: (_a = quote === null || quote === void 0 ? void 0 : quote.currencyId) !== null && _a !== void 0 ? _a : invoice === null || invoice === void 0 ? void 0 : invoice.currencyId,
21376
21448
  accountCurrencyId,
@@ -21387,66 +21459,66 @@ function PaymentForm({ currencyId: accountCurrencyId, invoice, quote: maskedQuot
21387
21459
  const providers = [
21388
21460
  [ShowPaymentDetailsProvider, { value: undefined }],
21389
21461
  [CustomCheckoutFunctionProvider, { value: customCheckoutFunction }],
21390
- [IsPayButtonDisabledProvider, { value: disablePayButton }],
21462
+ [AccountIdProvider, { value: accountId }],
21391
21463
  ];
21392
- return (jsx(Fragment, { children: providers.reduceRight((acc, [Provider, props]) => createElement(Provider, props, acc), jsx(OverrideTokenContext.Provider, { value: { overrideToken }, children: jsx(SelectedPluginProvider, { accountId: accountId, children: jsx(StripeWrapper, { currencyId: currencyId, accountId: accountId, children: jsx(PaymentProvider, { quote: quote, invoice: invoice, accountId: accountId, onPaymentHoldSuccess: onPaymentHoldSuccess, paymentHoldPrecondition: paymentHoldPrecondition, onPaymentSuccess: onPaymentSuccess, paymentHoldOptions: paymentHoldOptions !== null && paymentHoldOptions !== void 0 ? paymentHoldOptions : {}, onSavePaymentMethod: onSavePaymentMethod, children: jsx(PaymentFormContent, { onPaymentMethodRemoved: onPaymentMethodRemoved, onSetDefaultPaymentMethod: onSetDefaultPaymentMethod }) }) }) }) })) }));
21464
+ return (jsx(Fragment, { children: providers.reduceRight((acc, [Provider, props]) => createElement(Provider, props, acc), jsx(OverrideTokenContext.Provider, { value: { overrideToken }, children: jsx(SelectedPluginProvider, { accountId: accountId, children: jsx(StripeFormProvider, { currencyId: currencyId, accountId: accountId, children: jsx(DemoPayFormProvider, { children: jsx(PaymentFormCallbacksProvider, { value: {
21465
+ onPaymentSuccess,
21466
+ onSetDefaultPaymentMethod,
21467
+ onPaymentMethodRemoved,
21468
+ onSavePaymentMethod,
21469
+ onPaymentHoldSuccess,
21470
+ paymentHoldPrecondition,
21471
+ }, children: jsx(PaymentProvider, { quote: quote, invoice: invoice, paymentHold: paymentHold, children: jsx(PaymentFormContent, { children: children }) }) }) }) }) }) })) }));
21472
+ }
21473
+ // Default layout that uses PaymentFormRoot under the hood when no children are provided.
21474
+ function PaymentFormDefault(props) {
21475
+ console.log('PaymentFormDefault', props);
21476
+ return jsx(PaymentFormRoot, { ...props, children: ({ hasPaymentMethods, isPaymentMethodDetailsOpen, isPayableAvailable, amountDue }) => {
21477
+ const showPayButton = amountDue !== undefined && amountDue > 0 && !isPaymentMethodDetailsOpen && hasPaymentMethods;
21478
+ const checkoutNoPaymentButton = amountDue !== undefined && amountDue === 0;
21479
+ console.log('props.disablePayButton', props.disablePayButton);
21480
+ if (checkoutNoPaymentButton) {
21481
+ return jsx(PaymentForm.CheckoutNoPayment, { className: "bunny-px-4", disabled: props.disablePayButton });
21482
+ }
21483
+ return (jsxs(Fragment, { children: [jsx(PaymentForm.StoredPaymentMethods, { className: "bunny-px-4" }), jsx(PaymentForm.PaymentMethodDetails, { footer: jsxs("div", { className: "bunny-flex bunny-w-full bunny-gap-2", children: [jsx(PaymentForm.PaymentMethodDetails.Cancel, {}), isPayableAvailable && jsx(PaymentForm.Pay, { disabled: props.disablePayButton }), !isPayableAvailable && jsx(PaymentForm.PaymentMethodDetails.Save, {})] }) }), showPayButton && (jsx("div", { className: "bunny-px-4", children: jsx(PaymentForm.Pay, { disabled: props.disablePayButton }) }))] }));
21484
+ } });
21393
21485
  }
21394
- function PaymentFormContent({ onPaymentMethodRemoved, onSetDefaultPaymentMethod, }) {
21486
+ function PaymentFormContent({ children, }) {
21395
21487
  // Context
21396
21488
  const [showPaymentMethodForm, setShowPaymentMethodForm] = useShowPaymentDetails();
21397
- const paymentType = usePaymentType();
21398
- const isPaying = useIsPaying();
21399
- const formattedAmountDue = useFormattedAmountDue();
21400
- const selectedPlugin = useSelectedPlugin();
21401
- const onPaymentFormSubmit = useHandlePaymentFormSubmit();
21402
21489
  const accountId = useAccountId();
21490
+ const { quoteId, invoiceId, amountDue } = usePayable();
21403
21491
  // Hooks
21404
- const { paymentPlugins } = usePaymentPlugins(accountId);
21405
- const { paymentMethods: maskedPaymentMethods, isLoading: isPaymentMethodLoading } = usePaymentMethod({ accountId });
21406
- // Read fragments
21407
- const paymentMethods = useMemo(() => maskedPaymentMethods === null || maskedPaymentMethods === void 0 ? void 0 : maskedPaymentMethods.map(paymentMethod => readFragment(PaymentForm_PaymentMethodsFragment, paymentMethod)), [maskedPaymentMethods]);
21492
+ const { paymentMethods } = usePaymentMethod({ accountId });
21408
21493
  // Derived state
21409
21494
  const hasPaymentMethods = (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) && (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) > 0;
21410
- // Custom hooks
21411
- const { setDefaultPaymentMethod: handleSetDefault, loading: setDefaultPaymentMethodLoading } = useSetDefaultPaymentMethod(message => {
21412
- showErrorNotification$6(message, 'Error setting default payment method');
21413
- }, () => {
21414
- onSetDefaultPaymentMethod === null || onSetDefaultPaymentMethod === void 0 ? void 0 : onSetDefaultPaymentMethod();
21415
- });
21416
- useAutoSetDefaultPaymentMethod({
21417
- handleSetDefault,
21418
- setDefaultPaymentMethodLoading,
21419
- enabled: !!paymentPlugins,
21420
- });
21421
- const onClickRemove = useRemovePaymentMethod(onPaymentMethodRemoved, message => {
21422
- showErrorNotification$6(message, 'Error removing payment method');
21423
- });
21424
- function handleClickAddPaymentMethod() {
21425
- if ((paymentPlugins === null || paymentPlugins === void 0 ? void 0 : paymentPlugins.length) === 0) {
21426
- showErrorNotification$6('No payment plugins available', 'Error adding payment method');
21427
- }
21428
- else {
21429
- setShowPaymentMethodForm(true);
21430
- }
21431
- }
21432
- // Automatically show the payment method form if there are no payment methods
21433
- useEffect(() => {
21434
- if ((paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) === 0) {
21435
- setShowPaymentMethodForm(true);
21436
- }
21437
- }, [paymentMethods]);
21438
- if (isPaymentMethodLoading) {
21439
- return jsx(Skeleton, { active: true, className: "bunny-p-4" });
21495
+ const isPayableAvailable = quoteId !== undefined || invoiceId !== undefined;
21496
+ const renderState = {
21497
+ hasPaymentMethods: !!hasPaymentMethods,
21498
+ isPaymentMethodDetailsOpen: showPaymentMethodForm,
21499
+ isPayableAvailable,
21500
+ amountDue,
21501
+ };
21502
+ const renderedChildren = typeof children === 'function' ? children(renderState) : children;
21503
+ return (jsx(PaymentMethodFormVisibilityProvider, { value: {
21504
+ showPaymentMethodForm,
21505
+ setShowPaymentMethodForm,
21506
+ }, children: jsx("div", { className: "bunny-flex bunny-flex-col bunny-w-full", children: renderedChildren }) }));
21507
+ }
21508
+ // Public component: if children are passed, use PaymentFormRoot directly (render-prop / custom layout).
21509
+ // If no children are passed, fall back to PaymentFormDefault which uses PaymentFormRoot under the hood.
21510
+ function PaymentFormInternal(props) {
21511
+ if (props.children != null) {
21512
+ return jsx(PaymentFormRoot, { ...props });
21440
21513
  }
21441
- return (jsx("div", { className: "bunny-flex bunny-flex-col bunny-gap-0 bunny-w-full", children: paymentType === PaymentType.PAY || paymentType === PaymentType.APPROVE_HOLD ? (jsxs(Fragment, { children: [jsxs("div", { className: "bunny-flex bunny-flex-col bunny-gap-2 bunny-px-4", children: [paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.map(paymentMethod => (jsx(MiniCreditCard, { onClickRemove: () => onClickRemove(paymentMethod), onClickSetDefault: () => handleSetDefault(paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.id), paymentMethod: paymentMethod, id: `payment-method-${paymentMethod.id}` }, paymentMethod === null || paymentMethod === void 0 ? void 0 : paymentMethod.id))), (paymentMethods === null || paymentMethods === void 0 ? void 0 : paymentMethods.length) === 0 && jsx(MiniCreditCard, {})] }), jsx(Collapse, { bordered: false, activeKey: showPaymentMethodForm ? '1' : undefined, ghost: true, collapsible: "disabled", destroyOnHidden: true, items: [
21442
- {
21443
- key: '1',
21444
- showArrow: false,
21445
- label: !showPaymentMethodForm ? (jsx("div", { className: "bunny-pt-2", children: jsx(Button, { onClick: handleClickAddPaymentMethod, type: "default", className: "bunny-w-full", id: "addPaymentMethod", children: "Add payment method" }) })) : null,
21446
- children: (jsxs("div", { className: "bunny-flex bunny-flex-col bunny-gap-2 bunny-mt-2", children: [jsx(PaymentMethodSelector, {}), selectedPlugin && (jsx("div", { className: "bunny-flex bunny-flex-col", children: jsx(PaymentMethodDetails, {}) }))] })),
21447
- },
21448
- ] }), formattedAmountDue !== undefined && !showPaymentMethodForm && hasPaymentMethods ? (jsx("div", { className: "bunny-px-4", children: jsx(CheckoutFooter, {}) })) : null] })) : (jsx(Button, { style: { margin: '0 16px' }, loading: isPaying, onClick: () => onPaymentFormSubmit(), type: "primary", children: isPaying ? 'Processing...' : 'Complete Order' })) }));
21514
+ return jsx(PaymentFormDefault, { ...props });
21449
21515
  }
21516
+ const PaymentForm = Object.assign(PaymentFormInternal, {
21517
+ StoredPaymentMethods,
21518
+ Pay,
21519
+ CheckoutNoPayment,
21520
+ PaymentMethodDetails,
21521
+ });
21450
21522
 
21451
21523
  const useAjax = (onError) => {
21452
21524
  return async (url, method, callback, token, bodyData) => {
@@ -21940,8 +22012,8 @@ const PaymentHoldModal = ({ visible, setVisible, formattedQuote: maskedFormatted
21940
22012
  const [form] = Form.useForm();
21941
22013
  return (jsx(StyledModal$2, { centered: true, onCancel: () => {
21942
22014
  setVisible(false);
21943
- }, footer: null, open: visible, width: 800, className: 'bunny-flex bunny-flex-row bunny-gap-4', children: jsxs("div", { className: "bunny-flex bunny-flex-row", children: [jsxs("div", { className: "bunny-flex bunny-flex-col bunny-w-1/2", children: [jsxs("div", { className: "bunny-mt-5 bunny-mx-4", children: [jsx(Title$2, { className: "bunny-mt-0", level: 5, children: "Pay and sign" }), jsxs(Text$z, { className: "bunny-bt-2 bunny-text-sm/5 bunny-text-gray-500", children: ["To accept this quote, approve a payment hold for", ' ', formatCurrency(formattedQuote.amount, formattedQuote.currency || 'null'), ". This amount will be charged to your payment method once the quote is signed."] })] }), jsx("div", { className: "bunny-p-4", children: noSigningPlugins ? jsx(NoSigningPluginsForm, { isVisible: visible, formattedQuote: formattedQuote, form: form }) : null })] }), jsx(VerticalDivider, { className: "bunny-m-4" }), jsx("div", { className: "bunny-mb-3 bunny-w-1/2 bunny-pt-6", children: jsx(PaymentForm, { quote: formattedQuote.quote, paymentHoldOptions: {
21944
- payToAccept: true,
22015
+ }, footer: null, open: visible, width: 900, className: 'bunny-flex bunny-flex-row bunny-gap-4', children: jsxs("div", { className: "bunny-flex bunny-flex-row", children: [jsxs("div", { className: "bunny-flex bunny-flex-col bunny-w-1/2", children: [jsxs("div", { className: "bunny-mt-5 bunny-mx-4", children: [jsx(Title$2, { className: "bunny-mt-0", level: 5, children: "Pay and sign" }), jsxs(Text$z, { className: "bunny-bt-2 bunny-text-sm/5 bunny-text-gray-500", children: ["To accept this quote, approve a payment hold for", ' ', formatCurrency(formattedQuote.amount, formattedQuote.currency || 'null'), ". This amount will be charged to your payment method once the quote is signed."] })] }), jsx("div", { className: "bunny-p-4 bunny-pb-6", children: noSigningPlugins ? jsx(NoSigningPluginsForm, { isVisible: visible, formattedQuote: formattedQuote, form: form }) : null })] }), jsx(VerticalDivider, { className: "bunny-m-4" }), jsx("div", { className: "bunny-mb-3 bunny-w-1/2 bunny-pt-6", children: jsx(PaymentForm, { quote: formattedQuote.quote, paymentHold: {
22016
+ quoteId: formattedQuote.quote.id, // TODO: fix hack. We assume the quote id is always present, but may not be the case
21945
22017
  amountToHold: formattedQuote.amount,
21946
22018
  }, onPaymentSuccess: () => {
21947
22019
  setVisible(false);
@@ -24132,7 +24204,7 @@ const QuoteCheckout = ({ account, onSuccess, onFail, quote: maskedQuote, taxatio
24132
24204
  removeCoupon(couponCode);
24133
24205
  }
24134
24206
  });
24135
- }, children: "Remove coupon(s)" }))] })) : (jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-2 bunny-px-4 ${isMobile ? 'bunny-shadow-padding-x' : ''}`, children: [jsx(Button, { onClick: handleCheckoutNoPayment, type: "primary", children: isSaving ? 'Processing...' : 'Complete order' }), jsx(Text$m, { className: "bunny-text-xs bunny-text-slate-500", children: "No payment is required" })] })) }));
24207
+ }, children: "Remove coupon(s)" }))] })) : (jsxs("div", { className: `bunny-flex bunny-flex-col bunny-gap-2 bunny-px-4 ${isMobile ? 'bunny-shadow-padding-x' : ''}`, children: [jsx(Button, { onClick: handleCheckoutNoPayment, type: "primary", disabled: isFinalizingQuote, loading: isSaving || isFinalizingQuote, children: isSaving ? 'Processing...' : 'Complete order' }), jsx(Text$m, { className: "bunny-text-xs bunny-text-slate-500", children: "No payment is required" })] })) }));
24136
24208
  };
24137
24209
  const PaymentFormWrapper = ({ children, setMaxHeight, className, }) => {
24138
24210
  const isMobile = useIsMobile();