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