@churchapps/apphelper 0.11.0 → 0.13.0

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 (73) hide show
  1. package/dist/donations/components/BankForm.js.map +1 -1
  2. package/dist/donations/components/CardForm.js.map +1 -1
  3. package/dist/donations/components/DonationForm.js.map +1 -1
  4. package/dist/donations/components/KingdomFundingNonAuthDonationInner.d.ts +13 -0
  5. package/dist/donations/components/KingdomFundingNonAuthDonationInner.d.ts.map +1 -0
  6. package/dist/donations/components/KingdomFundingNonAuthDonationInner.js +282 -0
  7. package/dist/donations/components/KingdomFundingNonAuthDonationInner.js.map +1 -0
  8. package/dist/donations/components/KingdomFundingTokenForm.d.ts +19 -0
  9. package/dist/donations/components/KingdomFundingTokenForm.d.ts.map +1 -0
  10. package/dist/donations/components/KingdomFundingTokenForm.js +147 -0
  11. package/dist/donations/components/KingdomFundingTokenForm.js.map +1 -0
  12. package/dist/donations/components/MultiGatewayDonationForm.d.ts.map +1 -1
  13. package/dist/donations/components/MultiGatewayDonationForm.js +104 -108
  14. package/dist/donations/components/MultiGatewayDonationForm.js.map +1 -1
  15. package/dist/donations/components/NonAuthDonation.d.ts.map +1 -1
  16. package/dist/donations/components/NonAuthDonation.js +16 -48
  17. package/dist/donations/components/NonAuthDonation.js.map +1 -1
  18. package/dist/donations/components/PaymentMethods.d.ts.map +1 -1
  19. package/dist/donations/components/PaymentMethods.js +97 -16
  20. package/dist/donations/components/PaymentMethods.js.map +1 -1
  21. package/dist/donations/components/RecurringDonations.d.ts.map +1 -1
  22. package/dist/donations/components/RecurringDonations.js +29 -6
  23. package/dist/donations/components/RecurringDonations.js.map +1 -1
  24. package/dist/donations/components/index.d.ts +3 -0
  25. package/dist/donations/components/index.d.ts.map +1 -1
  26. package/dist/donations/components/index.js +2 -0
  27. package/dist/donations/components/index.js.map +1 -1
  28. package/dist/donations/helpers/DonationHelper.d.ts +2 -2
  29. package/dist/donations/helpers/DonationHelper.d.ts.map +1 -1
  30. package/dist/donations/helpers/DonationHelper.js +1 -1
  31. package/dist/donations/helpers/DonationHelper.js.map +1 -1
  32. package/dist/donations/helpers/DonationInterface.d.ts +1 -1
  33. package/dist/donations/helpers/DonationInterface.d.ts.map +1 -1
  34. package/dist/donations/helpers/Locale.d.ts.map +1 -1
  35. package/dist/donations/helpers/Locale.js +24 -0
  36. package/dist/donations/helpers/Locale.js.map +1 -1
  37. package/dist/donations/helpers/PaymentMethod.d.ts +4 -2
  38. package/dist/donations/helpers/PaymentMethod.d.ts.map +1 -1
  39. package/dist/donations/helpers/StripePaymentMethod.d.ts +1 -1
  40. package/dist/donations/helpers/StripePaymentMethod.d.ts.map +1 -1
  41. package/dist/donations/index.d.ts +1 -0
  42. package/dist/donations/index.d.ts.map +1 -1
  43. package/dist/donations/index.js +1 -0
  44. package/dist/donations/index.js.map +1 -1
  45. package/dist/donations/modals/DonationPreviewModal.d.ts.map +1 -1
  46. package/dist/donations/modals/DonationPreviewModal.js +3 -1
  47. package/dist/donations/modals/DonationPreviewModal.js.map +1 -1
  48. package/dist/donations/providers/KingdomFundingProvider.d.ts +3 -0
  49. package/dist/donations/providers/KingdomFundingProvider.d.ts.map +1 -0
  50. package/dist/donations/providers/KingdomFundingProvider.js +86 -0
  51. package/dist/donations/providers/KingdomFundingProvider.js.map +1 -0
  52. package/dist/donations/providers/PayPalProvider.d.ts +3 -0
  53. package/dist/donations/providers/PayPalProvider.d.ts.map +1 -0
  54. package/dist/donations/providers/PayPalProvider.js +81 -0
  55. package/dist/donations/providers/PayPalProvider.js.map +1 -0
  56. package/dist/donations/providers/StripeProvider.d.ts +34 -0
  57. package/dist/donations/providers/StripeProvider.d.ts.map +1 -0
  58. package/dist/donations/providers/StripeProvider.js +74 -0
  59. package/dist/donations/providers/StripeProvider.js.map +1 -0
  60. package/dist/donations/providers/index.d.ts +3 -0
  61. package/dist/donations/providers/index.d.ts.map +1 -0
  62. package/dist/donations/providers/index.js +6 -0
  63. package/dist/donations/providers/index.js.map +1 -0
  64. package/dist/donations/providers/registry.d.ts +6 -0
  65. package/dist/donations/providers/registry.d.ts.map +1 -0
  66. package/dist/donations/providers/registry.js +36 -0
  67. package/dist/donations/providers/registry.js.map +1 -0
  68. package/dist/donations/providers/types.d.ts +113 -0
  69. package/dist/donations/providers/types.d.ts.map +1 -0
  70. package/dist/donations/providers/types.js +2 -0
  71. package/dist/donations/providers/types.js.map +1 -0
  72. package/dist/public/locales/en.json +24 -0
  73. package/package.json +1 -1
@@ -0,0 +1,147 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState, useCallback, forwardRef, useImperativeHandle } from "react";
4
+ import { Box, CircularProgress, Typography } from "@mui/material";
5
+ import { Locale } from "../helpers";
6
+ // CSS strings for each CardFormStyles field to match MUI outlined TextField
7
+ // accept.blue labelType options: "floating" | "static-left" | "static-top" | "hidden"
8
+ const inputBase = "font-family: 'Roboto','Helvetica','Arial',sans-serif; font-size: 1rem; color: rgba(0,0,0,0.87); border: 1px solid rgba(0,0,0,0.23); border-radius: 4px; padding: 16.5px 14px; background: #fff; outline: none; box-sizing: border-box; transition: border-color 200ms cubic-bezier(0.0,0,0.2,1);";
9
+ const tokenFormStyles = {
10
+ container: "display: flex; flex-wrap: wrap; gap: 12px; width: 100%; align-items: flex-end;",
11
+ card: inputBase + " flex: 1 0 100%; width: 100%;",
12
+ expiryContainer: "display: flex; align-items: flex-end; gap: 4px;",
13
+ expiryMonth: inputBase + " width: 64px; text-align: center;",
14
+ expirySeparator: "font-size: 1.25rem; color: rgba(0,0,0,0.4); padding: 16.5px 0; line-height: 1;",
15
+ expiryYear: inputBase + " width: 64px; text-align: center;",
16
+ cvv2: inputBase + " width: 90px;",
17
+ labels: "font-family: 'Roboto','Helvetica','Arial',sans-serif; font-size: 0.75rem; color: rgba(0,0,0,0.6);",
18
+ labelType: "floating"
19
+ };
20
+ export const KingdomFundingTokenForm = forwardRef(({ tokenizationKey, sandbox = false }, ref) => {
21
+ const containerRef = useRef(null);
22
+ const hostedTokenizationRef = useRef(null);
23
+ const [loading, setLoading] = useState(true);
24
+ const [error, setError] = useState(null);
25
+ const initCalledRef = useRef(false);
26
+ const destroyTokenization = useCallback(() => {
27
+ if (hostedTokenizationRef.current) {
28
+ try {
29
+ // destroy() returns a Promise that often rejects with undefined when the
30
+ // iframe was never fully initialized (e.g. unmount during loading).
31
+ // Swallow both sync throws and the async rejection so it doesn't surface
32
+ // as an Unhandled Promise Rejection in the console.
33
+ const result = hostedTokenizationRef.current.destroy();
34
+ if (result && typeof result.catch === "function") {
35
+ result.catch(() => { });
36
+ }
37
+ }
38
+ catch (_e) { /* ignore */ }
39
+ hostedTokenizationRef.current = null;
40
+ }
41
+ // Clear any leftover iframes in the container
42
+ if (containerRef.current) {
43
+ containerRef.current.innerHTML = "";
44
+ }
45
+ }, []);
46
+ const initTokenization = useCallback(() => {
47
+ if (initCalledRef.current)
48
+ return;
49
+ try {
50
+ const HostedTokenization = window.HostedTokenization;
51
+ if (!HostedTokenization) {
52
+ setError(Locale.label("donation.kingdomFunding.paymentFormNotAvailable"));
53
+ setLoading(false);
54
+ return;
55
+ }
56
+ // Clean up any previous instance first
57
+ destroyTokenization();
58
+ const containerId = "kf-token-container";
59
+ if (containerRef.current) {
60
+ containerRef.current.id = containerId;
61
+ }
62
+ initCalledRef.current = true;
63
+ hostedTokenizationRef.current = new HostedTokenization(tokenizationKey, {
64
+ target: `#${containerId}`,
65
+ styles: tokenFormStyles
66
+ });
67
+ setLoading(false);
68
+ }
69
+ catch (_e) {
70
+ setError(Locale.label("donation.kingdomFunding.failedToInitPaymentForm"));
71
+ setLoading(false);
72
+ }
73
+ }, [tokenizationKey, destroyTokenization]);
74
+ useEffect(() => {
75
+ initCalledRef.current = false;
76
+ if (!tokenizationKey) {
77
+ setError(Locale.label("donation.kingdomFunding.missingTokenizationKey"));
78
+ setLoading(false);
79
+ return;
80
+ }
81
+ const scriptUrl = sandbox
82
+ ? "https://tokenization.sandbox.accept.blue/tokenization/v0.3"
83
+ : "https://tokenization.accept.blue/tokenization/v0.3";
84
+ // Check if script already loaded
85
+ if (window.HostedTokenization) {
86
+ initTokenization();
87
+ return () => { destroyTokenization(); initCalledRef.current = false; };
88
+ }
89
+ const existingScript = document.querySelector(`script[src="${scriptUrl}"]`);
90
+ if (existingScript) {
91
+ const onLoad = () => { initTokenization(); };
92
+ if (window.HostedTokenization) {
93
+ initTokenization();
94
+ }
95
+ else {
96
+ existingScript.addEventListener("load", onLoad);
97
+ }
98
+ return () => {
99
+ existingScript.removeEventListener("load", onLoad);
100
+ destroyTokenization();
101
+ initCalledRef.current = false;
102
+ };
103
+ }
104
+ const script = document.createElement("script");
105
+ script.src = scriptUrl;
106
+ script.async = true;
107
+ script.onload = () => { initTokenization(); };
108
+ script.onerror = () => {
109
+ setError(Locale.label("donation.kingdomFunding.failedToLoadPaymentForm"));
110
+ setLoading(false);
111
+ };
112
+ document.head.appendChild(script);
113
+ return () => {
114
+ destroyTokenization();
115
+ initCalledRef.current = false;
116
+ };
117
+ }, [tokenizationKey, sandbox, initTokenization, destroyTokenization]);
118
+ useImperativeHandle(ref, () => ({
119
+ getNonce: async () => {
120
+ if (!hostedTokenizationRef.current) {
121
+ throw new Error(Locale.label("donation.kingdomFunding.paymentFormNotInitialized"));
122
+ }
123
+ const result = await hostedTokenizationRef.current.getNonceToken();
124
+ if (!result?.nonce) {
125
+ throw new Error(result?.error || Locale.label("donation.kingdomFunding.failedToTokenizeCard"));
126
+ }
127
+ // Normalize expiry year to 4-digit (accept.blue API requires 2020-9999)
128
+ let expYear = result.expiryYear ? Number(result.expiryYear) : 0;
129
+ if (expYear > 0 && expYear < 100)
130
+ expYear += 2000;
131
+ return {
132
+ nonce: result.nonce,
133
+ last4: result.last4 || "",
134
+ cardType: result.cardType || "",
135
+ expiryMonth: result.expiryMonth ? String(result.expiryMonth) : "",
136
+ expiryYear: expYear ? String(expYear) : "",
137
+ maskedCard: result.maskedCard || ""
138
+ };
139
+ }
140
+ }));
141
+ if (error) {
142
+ return _jsx(Typography, { color: "error", sx: { py: 2 }, children: error });
143
+ }
144
+ return (_jsxs(Box, { children: [loading && (_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, py: 2 }, children: [_jsx(CircularProgress, { size: 20 }), _jsx(Typography, { variant: "body2", children: Locale.label("donation.kingdomFunding.loadingPaymentForm") })] })), _jsx("div", { ref: containerRef, style: { minHeight: loading ? 0 : undefined } })] }));
145
+ });
146
+ KingdomFundingTokenForm.displayName = "KingdomFundingTokenForm";
147
+ //# sourceMappingURL=KingdomFundingTokenForm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KingdomFundingTokenForm.js","sourceRoot":"","sources":["../../../src/donations/components/KingdomFundingTokenForm.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"MultiGatewayDonationForm.d.ts","sourceRoot":"","sources":["../../../src/donations/components/MultiGatewayDonationForm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAQhD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAiC,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAwC,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAiB7G,UAAU,KAAK;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CA6jBpD,CAAC"}
1
+ {"version":3,"file":"MultiGatewayDonationForm.d.ts","sourceRoot":"","sources":["../../../src/donations/components/MultiGatewayDonationForm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAOhD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAiC,MAAM,YAAY,CAAC;AAG/F,OAAO,EAAE,eAAe,EAAwC,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAkB7G,UAAU,KAAK;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAijBD,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAKpD,CAAC"}
@@ -4,14 +4,16 @@ import { useCallback, useState, useEffect, useMemo, useRef } from "react";
4
4
  import { useStripe } from "@stripe/react-stripe-js";
5
5
  import { InputBox, ErrorMessages } from "../..";
6
6
  import { FundDonations } from ".";
7
- import { PayPalHostedFields } from "./PayPalHostedFields";
8
7
  import { DonationPreviewModal } from "../modals/DonationPreviewModal";
9
8
  import { ApiHelper, CurrencyHelper, DateHelper } from "@churchapps/helpers";
10
9
  import { Locale, DonationHelper } from "../helpers";
11
- import { Grid, InputLabel, MenuItem, Select, TextField, FormControl, Button, FormControlLabel, Checkbox, FormGroup, Typography, Alert } from "@mui/material";
12
- export const MultiGatewayDonationForm = (props) => {
10
+ import { getPaymentProvider } from "../providers";
11
+ import { Grid, Icon, InputLabel, MenuItem, Select, TextField, FormControl, Button, FormControlLabel, Checkbox, FormGroup, Typography, Alert } from "@mui/material";
12
+ const MultiGatewayDonationInner = (props) => {
13
13
  const stripe = useStripe();
14
14
  const [errorMessage, setErrorMessage] = useState();
15
+ const [saveCard, setSaveCard] = useState(false);
16
+ const [useNewCard, setUseNewCard] = useState(false);
15
17
  const [fundDonations, setFundDonations] = useState();
16
18
  const [funds, setFunds] = useState([]);
17
19
  const [fundsLoaded, setFundsLoaded] = useState(false);
@@ -19,8 +21,9 @@ export const MultiGatewayDonationForm = (props) => {
19
21
  const [transactionFee, setTransactionFee] = useState(0);
20
22
  const [payFee, setPayFee] = useState(0);
21
23
  const [total, setTotal] = useState(0);
22
- const [paymentMethodName, setPaymentMethodName] = useState(props?.paymentMethods?.length > 0 ? `${props.paymentMethods[0].name} ${props.paymentMethods[0].last4 ? `****${props.paymentMethods[0].last4}` : props.paymentMethods[0].email || ''}` : "");
23
- const [selectedGateway, setSelectedGateway] = useState(DonationHelper.normalizeProvider(props?.paymentGateways?.find(g => g.enabled !== false)?.provider || "stripe"));
24
+ const [paymentMethodName, setPaymentMethodName] = useState(props?.paymentMethods?.length > 0 ? `${props.paymentMethods[0].name} ${props.paymentMethods[0].last4 ? `****${props.paymentMethods[0].last4}` : props.paymentMethods[0].email || ""}` : "");
25
+ const [selectedGateway] = useState(DonationHelper.normalizeProvider(props?.paymentGateways?.find(g => g.enabled !== false)?.provider || "stripe"));
26
+ const paymentProvider = useMemo(() => getPaymentProvider(selectedGateway), [selectedGateway]);
24
27
  const selectedGatewayObj = useMemo(() => {
25
28
  return (props.paymentGateways.find((g) => DonationHelper.normalizeProvider(g.provider) === selectedGateway) || null);
26
29
  }, [props.paymentGateways, selectedGateway]);
@@ -28,11 +31,7 @@ export const MultiGatewayDonationForm = (props) => {
28
31
  const [showDonationPreviewModal, setShowDonationPreviewModal] = useState(false);
29
32
  const [interval, setInterval] = useState("one_month");
30
33
  const [gateway, setGateway] = useState(selectedGatewayObj);
31
- const paypalClientId = useMemo(() => {
32
- const gw = props.paymentGateways.find(g => DonationHelper.isProvider(g.provider, "paypal"));
33
- return gw?.publicKey || "";
34
- }, [props.paymentGateways]);
35
- const hostedRef = useRef(null);
34
+ const entryRef = useRef(null);
36
35
  const feeTimeoutRef = useRef(null);
37
36
  const [donation, setDonation] = useState({
38
37
  id: props?.paymentMethods?.length > 0 ? props.paymentMethods[0].id : "",
@@ -110,35 +109,17 @@ export const MultiGatewayDonationForm = (props) => {
110
109
  const d = { ...donation };
111
110
  const value = e.target.value;
112
111
  switch (e.target.name) {
113
- case "gateway":
114
- setSelectedGateway(value);
115
- d.provider = value;
116
- const matchedGateway = props.paymentGateways.find(g => DonationHelper.normalizeProvider(g.provider) === value);
117
- d.gatewayId = matchedGateway?.id;
118
- d.currency = matchedGateway?.currency || "usd";
119
- // Reset payment method when changing gateways
120
- const availableMethods = props.paymentMethods.filter(pm => DonationHelper.normalizeProvider(pm.provider) === value);
121
- if (availableMethods.length > 0) {
122
- d.id = availableMethods[0].id;
123
- d.type = availableMethods[0].type;
124
- setPaymentMethodName(`${availableMethods[0].name} ${availableMethods[0].last4 ? `****${availableMethods[0].last4}` : availableMethods[0].email || ''}`);
125
- }
126
- else {
127
- d.id = "";
128
- if (value === "paypal")
129
- d.type = "paypal";
130
- }
131
- break;
132
- case "method":
112
+ case "method": {
133
113
  d.id = value;
134
114
  const pm = props.paymentMethods.find(pm => pm.id === value);
135
115
  if (pm) {
136
116
  d.type = pm.type;
137
117
  d.provider = DonationHelper.normalizeProvider(pm.provider);
138
118
  d.gatewayId = pm.gatewayId || gateway?.id || selectedGatewayObj?.id;
139
- setPaymentMethodName(`${pm.name} ${pm.last4 ? `****${pm.last4}` : pm.email || ''}`);
119
+ setPaymentMethodName(`${pm.name} ${pm.last4 ? `****${pm.last4}` : pm.email || ""}`);
140
120
  }
141
121
  break;
122
+ }
142
123
  case "type":
143
124
  setDonationType(value);
144
125
  break;
@@ -152,15 +133,16 @@ export const MultiGatewayDonationForm = (props) => {
152
133
  case "notes":
153
134
  d.notes = value;
154
135
  break;
155
- case "transaction-fee":
136
+ case "transaction-fee": {
156
137
  const element = e.target;
157
138
  d.amount = element.checked ? fundsTotal + transactionFee : fundsTotal;
158
139
  const showFee = element.checked ? transactionFee : 0;
159
140
  setTotal(d.amount);
160
141
  setPayFee(showFee);
142
+ }
161
143
  }
162
144
  setDonation(d);
163
- }, [donation, props.paymentMethods, fundsTotal, transactionFee, gateway?.id, props.paymentGateways, selectedGatewayObj?.id]);
145
+ }, [donation, props.paymentMethods, fundsTotal, transactionFee, gateway?.id, selectedGatewayObj?.id]);
164
146
  const handleCancel = useCallback(() => { setDonationType(undefined); }, []);
165
147
  const handleDonationSelect = useCallback((type) => {
166
148
  const dt = donationType === type ? undefined : type;
@@ -168,67 +150,79 @@ export const MultiGatewayDonationForm = (props) => {
168
150
  }, [donationType]);
169
151
  const handleSingleDonationClick = useCallback(() => handleDonationSelect("once"), [handleDonationSelect]);
170
152
  const handleRecurringDonationClick = useCallback(() => handleDonationSelect("recurring"), [handleDonationSelect]);
171
- const makeDonation = useCallback(async (message) => {
172
- let results;
173
- const churchObj = {
153
+ // Snapshot of the gift in flight, in the shape every provider adapter expects.
154
+ const buildContext = useCallback(() => ({
155
+ provider: selectedGateway,
156
+ gatewayId: donation.gatewayId || gateway?.id || selectedGatewayObj?.id,
157
+ churchId: props?.church?.id || "",
158
+ amount: total,
159
+ funds: (donation.funds || []).map((f) => ({ id: f.id, amount: f.amount })),
160
+ person: {
161
+ id: donation.person?.id || "",
162
+ email: donation.person?.email || "",
163
+ name: donation.person?.name || ""
164
+ },
165
+ notes: donation?.notes || "",
166
+ church: {
174
167
  name: props?.church?.name || "",
175
168
  subDomain: props?.church?.subDomain || "",
176
169
  churchURL: typeof window !== "undefined" ? window.location.origin : "",
177
170
  logo: props?.churchLogo || ""
178
- };
179
- // If using PayPal without a saved method, try Hosted Fields
180
- if (selectedGateway === "paypal" && (!donation.id || donation.id === "") && paypalClientId) {
171
+ },
172
+ recurring: donationType === "recurring",
173
+ interval: donation.interval,
174
+ billingCycleAnchor: donation.billing_cycle_anchor,
175
+ saveCard,
176
+ customerId: props.customerId,
177
+ currency: donation.currency
178
+ }), [
179
+ donation, total, donationType, saveCard, selectedGateway, gateway?.id, selectedGatewayObj?.id, props.church?.id, props.church?.name, props.church?.subDomain, props.churchLogo, props.customerId
180
+ ]);
181
+ const makeDonation = useCallback(async (message) => {
182
+ const ctx = buildContext();
183
+ // Capture a new payment if the provider needs one (no saved-card support, or
184
+ // the user is entering a fresh card); otherwise charge the chosen saved method.
185
+ const needsToken = !!paymentProvider.MemberEntry
186
+ && (!paymentProvider.capabilities.savedCard || useNewCard || !donation.id || donation.id === "");
187
+ let token;
188
+ if (needsToken) {
181
189
  try {
182
- const payload = await hostedRef.current?.submit();
183
- const orderId = payload?.orderId || payload?.id || "";
184
- if (orderId) {
185
- // Capture and persist via unified /donate/charge endpoint for PayPal
186
- const compactFunds = (donation.funds || []).map((f) => ({ id: f.id, amount: f.amount }));
187
- results = await ApiHelper.post("/donate/charge", {
188
- provider: "paypal",
189
- gatewayId: selectedGatewayObj?.id,
190
- id: orderId,
191
- churchId: props?.church?.id || "",
192
- amount: total,
193
- funds: compactFunds,
194
- person: donation.person,
195
- notes: donation?.notes || ""
196
- }, "GivingApi");
197
- }
190
+ token = await entryRef.current.tokenize();
198
191
  }
199
192
  catch (e) {
200
- console.warn("Hosted Fields submit failed, falling back to standard flow.", e);
193
+ setShowDonationPreviewModal(false);
194
+ setErrorMessage("Failed to process payment: " + (e.message || "Unknown error"));
195
+ return;
201
196
  }
202
197
  }
203
- // Standard flow (Stripe or saved payment method)
204
- if (!results) {
205
- const payload = {
206
- ...donation,
207
- provider: donation.provider || selectedGateway,
208
- gatewayId: donation.gatewayId || gateway?.id || selectedGatewayObj?.id,
209
- church: churchObj
210
- };
211
- if (donationType === "once")
212
- results = await ApiHelper.post("/donate/charge", payload, "GivingApi");
213
- if (donationType === "recurring")
214
- results = await ApiHelper.post("/donate/subscribe", payload, "GivingApi");
198
+ else {
199
+ token = { id: donation.id, type: donation.type, saved: true };
200
+ }
201
+ const { endpoint, body } = paymentProvider.buildChargeRequest(ctx, token);
202
+ let results = await ApiHelper.post(endpoint, body, "GivingApi");
203
+ // Terminal: a freshly-tokenized payment must never silently retry on a falsy
204
+ // response, or we'd double-charge.
205
+ if (needsToken && !results) {
206
+ setShowDonationPreviewModal(false);
207
+ setErrorMessage(Locale.label("donation.kingdomFunding.unexpectedError"));
208
+ return;
215
209
  }
216
- // Handle 3D Secure authentication if required (Stripe only)
217
- if (selectedGateway === "stripe") {
218
- const threeDSResult = await DonationHelper.handle3DSIfRequired(results, stripe);
219
- if (threeDSResult.requiresAction) {
210
+ if (paymentProvider.finalizeResult) {
211
+ const fin = await paymentProvider.finalizeResult(results, { stripe });
212
+ if (fin.requiresAction) {
220
213
  setShowDonationPreviewModal(false);
221
- if (threeDSResult.success) {
214
+ if (fin.success) {
222
215
  setDonationType(undefined);
223
216
  props.donationSuccess(message);
224
217
  }
225
218
  else {
226
- setErrorMessage(Locale.label("donation.common.error") + ": " + threeDSResult.error);
219
+ setErrorMessage(Locale.label("donation.common.error") + ": " + fin.error);
227
220
  }
228
221
  return;
229
222
  }
223
+ results = fin.result;
230
224
  }
231
- if (results?.status === "succeeded" || results?.status === "pending" || results?.status === "active" || results?.status === "processing" || results?.status === "CREATED") {
225
+ if (results?.status === "succeeded" || results?.status === "pending" || results?.status === "active" || results?.status === "processing" || results?.status === "CREATED" || results?.status === "Approved") {
232
226
  setShowDonationPreviewModal(false);
233
227
  setDonationType(undefined);
234
228
  props.donationSuccess(message);
@@ -237,7 +231,7 @@ export const MultiGatewayDonationForm = (props) => {
237
231
  setShowDonationPreviewModal(false);
238
232
  setErrorMessage(Locale.label("donation.common.error") + ": " + (results?.raw?.message || results?.message));
239
233
  }
240
- }, [donation, donationType, gateway?.id, paypalClientId, props.church?.name, props.church?.subDomain, props.churchLogo, props.donationSuccess, selectedGateway, selectedGatewayObj?.id, total, stripe]);
234
+ }, [buildContext, paymentProvider, donation.id, donation.type, useNewCard, stripe, props.donationSuccess]);
241
235
  const getTransactionFee = useCallback(async (amount, activeGatewayId, provider = "stripe", paymentMethodType) => {
242
236
  if (amount > 0) {
243
237
  try {
@@ -322,7 +316,10 @@ export const MultiGatewayDonationForm = (props) => {
322
316
  };
323
317
  }, []);
324
318
  const availablePaymentMethods = props.paymentMethods.filter(pm => DonationHelper.normalizeProvider(pm.provider) === selectedGateway);
325
- const availableGateways = props.paymentGateways.filter(g => g.enabled !== false);
319
+ // Show the provider's inline entry widget when there's no saved method to use:
320
+ // PayPal (no vault) always; Kingdom Funding when adding a new card or none saved.
321
+ const showInlineEntry = !!paymentProvider.MemberEntry
322
+ && (!paymentProvider.capabilities.savedCard || useNewCard || availablePaymentMethods.length === 0);
326
323
  if (!fundsLoaded) {
327
324
  return _jsx(Alert, { severity: "info", children: "Loading donation settings\u2026" });
328
325
  }
@@ -330,6 +327,7 @@ export const MultiGatewayDonationForm = (props) => {
330
327
  return (_jsx(Alert, { severity: "warning", children: "No donation funds have been configured for this church. Please contact your administrator." }));
331
328
  }
332
329
  else {
330
+ const MemberEntry = paymentProvider.MemberEntry;
333
331
  return (_jsxs(_Fragment, { children: [_jsx(DonationPreviewModal, { show: showDonationPreviewModal, onHide: () => setShowDonationPreviewModal(false), handleDonate: makeDonation, donation: {
334
332
  ...donation,
335
333
  person: {
@@ -337,39 +335,37 @@ export const MultiGatewayDonationForm = (props) => {
337
335
  email: donation.person?.email || "",
338
336
  name: donation.person?.name || ""
339
337
  }
340
- }, donationType: donationType || "", payFee: payFee, paymentMethodName: paymentMethodName, funds: funds }), _jsxs(InputBox, { id: "donation-form", "aria-label": "donation-box", headerIcon: "volunteer_activism", headerText: Locale.label("donation.donationForm.donate"), ariaLabelSave: "save-button", cancelFunction: donationType ? handleCancel : undefined, saveFunction: donationType ? handleSave : undefined, saveText: Locale.label("donation.donationForm.preview"), children: [_jsxs(Grid, { id: "donation-type-selector", container: true, spacing: 3, children: [_jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(Button, { id: "single-donation-button", "aria-label": "single-donation", size: "small", fullWidth: true, style: { minHeight: "50px" }, variant: donationType === "once" ? "contained" : "outlined", onClick: handleSingleDonationClick, children: Locale.label("donation.donationForm.make") }) }), _jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(Button, { id: "recurring-donation-button", "aria-label": "recurring-donation", size: "small", fullWidth: true, style: { minHeight: "50px" }, variant: donationType === "recurring" ? "contained" : "outlined", onClick: handleRecurringDonationClick, children: Locale.label("donation.donationForm.makeRecurring") }) })] }), donationType && (_jsxs("div", { id: "donation-details", style: { marginTop: "20px" }, children: [_jsxs(Grid, { container: true, spacing: 3, children: [availableGateways.length > 1 && (_jsx(Grid, { size: { xs: 12 }, children: _jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: "Payment Provider" }), _jsx(Select, { id: "gateway-select", label: "Payment Provider", name: "gateway", "aria-label": "gateway", value: selectedGateway, onChange: handleChange, children: availableGateways.map((gw) => (_jsx(MenuItem, { value: DonationHelper.normalizeProvider(gw.provider), children: DonationHelper.isProvider(gw.provider, "stripe") ? "Stripe" : "PayPal" }, gw.provider))) })] }) })), selectedGateway !== "paypal" || availablePaymentMethods.length > 0 ? (_jsx(Grid, { size: { xs: 12 }, children: _jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: Locale.label("donation.donationForm.method") }), _jsx(Select, { id: "payment-method-select", label: Locale.label("donation.donationForm.method"), name: "method", "aria-label": "method", value: donation.id, className: "capitalize", onChange: handleChange, children: availablePaymentMethods.map((paymentMethod) => (_jsxs(MenuItem, { value: paymentMethod.id, children: [paymentMethod.name, " ", paymentMethod.last4 ? `****${paymentMethod.last4}` : paymentMethod.email || ''] }, paymentMethod.id))) })] }) })) : (_jsxs(Grid, { size: { xs: 12 }, children: [_jsx(Typography, { variant: "subtitle1", sx: { mb: 1 }, children: "Enter card details (PayPal Hosted Fields)" }), _jsx(PayPalHostedFields, { ref: hostedRef, clientId: paypalClientId, getClientToken: async () => {
341
- try {
342
- const resp = await ApiHelper.post("/donate/client-token", {
343
- churchId: props?.church?.id || "",
344
- provider: "paypal",
345
- gatewayId: selectedGatewayObj?.id || gateway?.id
346
- }, "GivingApi");
347
- const token = resp?.clientToken || resp?.token || resp?.result || resp;
348
- return typeof token === "string" && token.length > 0 ? token : "";
349
- }
350
- catch {
351
- return "";
338
+ }, donationType: donationType || "", payFee: payFee, paymentMethodName: paymentMethodName, funds: funds }), _jsxs(InputBox, { id: "donation-form", "aria-label": "donation-box", headerIcon: "volunteer_activism", headerText: Locale.label("donation.donationForm.donate"), ariaLabelSave: "save-button", cancelFunction: donationType ? handleCancel : undefined, saveFunction: donationType ? handleSave : undefined, saveText: Locale.label("donation.donationForm.preview"), children: [_jsxs(Grid, { id: "donation-type-selector", container: true, spacing: 3, children: [_jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(Button, { id: "single-donation-button", "aria-label": "single-donation", size: "small", fullWidth: true, style: { minHeight: "50px" }, variant: donationType === "once" ? "contained" : "outlined", onClick: handleSingleDonationClick, children: Locale.label("donation.donationForm.make") }) }), _jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(Button, { id: "recurring-donation-button", "aria-label": "recurring-donation", size: "small", fullWidth: true, style: { minHeight: "50px" }, variant: donationType === "recurring" ? "contained" : "outlined", onClick: handleRecurringDonationClick, children: Locale.label("donation.donationForm.makeRecurring") }) })] }), donationType && (_jsxs("div", { id: "donation-details", style: { marginTop: "20px" }, children: [_jsx(Grid, { container: true, spacing: 3, children: !showInlineEntry ? (_jsx(Grid, { size: { xs: 12 }, children: _jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: Locale.label("donation.donationForm.method") }), _jsxs(Select, { id: "payment-method-select", label: Locale.label("donation.donationForm.method"), name: "method", "aria-label": "method", value: donation.id, className: "capitalize", onChange: (e) => {
339
+ if (e.target.value === "__new__") {
340
+ setUseNewCard(true);
341
+ setPaymentMethodName("New card");
342
+ const d = { ...donation };
343
+ d.id = "";
344
+ setDonation(d);
352
345
  }
353
- }, createOrder: async () => {
354
- try {
355
- const fundsPayload = (donation?.funds || [])
356
- .filter((f) => (f.amount || 0) > 0 && f.id)
357
- .map((f) => ({ id: f.id, amount: f.amount || 0 }));
358
- const response = await ApiHelper.post("/donate/create-order", {
359
- churchId: props?.church?.id || "",
360
- provider: "paypal",
361
- gatewayId: selectedGatewayObj?.id || gateway?.id,
362
- amount: total,
363
- currency: (selectedGatewayObj?.currency || gateway?.currency || "USD").toUpperCase(),
364
- funds: fundsPayload,
365
- notes: donation?.notes || ""
366
- }, "GivingApi");
367
- return response?.id || response?.orderId || "";
346
+ else {
347
+ // Switching back to a saved card after picking "Enter new card"
348
+ // must clear useNewCard, otherwise the submit handler still
349
+ // tries to tokenize a card the iframe never collected.
350
+ setUseNewCard(false);
351
+ handleChange(e);
368
352
  }
369
- catch (_e) {
370
- return "";
371
- }
372
- } })] }))] }), donationType === "recurring" && (_jsxs(Grid, { container: true, spacing: 3, style: { marginTop: 10 }, children: [_jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(TextField, { id: "start-date-field", fullWidth: true, name: "date", type: "date", "aria-label": "date", label: Locale.label("donation.donationForm.startDate"), value: DateHelper.formatHtml5Date(new Date(donation.billing_cycle_anchor || Date.now())), onChange: handleChange, onKeyDown: handleKeyDown }) }), _jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: Locale.label("donation.donationForm.frequency") }), _jsxs(Select, { id: "frequency-select", label: Locale.label("donation.donationForm.frequency"), name: "interval", "aria-label": "interval", value: interval, onChange: handleChange, children: [_jsx(MenuItem, { value: "one_week", children: Locale.label("donation.donationForm.weekly") }), _jsx(MenuItem, { value: "two_week", children: Locale.label("donation.donationForm.biWeekly") }), _jsx(MenuItem, { value: "one_month", children: Locale.label("donation.donationForm.monthly") }), _jsx(MenuItem, { value: "three_month", children: Locale.label("donation.donationForm.quarterly") }), _jsx(MenuItem, { value: "one_year", children: Locale.label("donation.donationForm.annually") })] })] }) })] })), _jsxs("div", { id: "fund-selection", className: "form-group", children: [funds && fundDonations && (_jsxs(_Fragment, { children: [_jsx("h4", { children: Locale.label("donation.donationForm.fund") }), _jsx(FundDonations, { fundDonations: fundDonations, funds: funds, updatedFunction: handleFundDonationsChange, currency: gateway?.currency })] })), fundsTotal > 0 && (_jsxs(_Fragment, { children: [(gateway?.payFees === true) ? (_jsxs(Typography, { fontSize: 14, fontStyle: "italic", children: ["*", Locale.label("donation.donationForm.fees").replace("{}", CurrencyHelper.formatCurrencyWithLocale(transactionFee, gateway?.currency || "USD"))] })) : (_jsx(FormGroup, { children: _jsx(FormControlLabel, { control: _jsx(Checkbox, {}), name: "transaction-fee", label: Locale.label("donation.donationForm.cover").replace("{}", CurrencyHelper.formatCurrencyWithLocale(transactionFee, gateway?.currency || "USD")), onChange: handleCheckChange }) })), _jsxs("p", { children: [Locale.label("donation.donationForm.total"), ": ", CurrencyHelper.formatCurrencyWithLocale(total, gateway?.currency || "USD")] })] })), _jsx(TextField, { id: "donation-notes", fullWidth: true, label: "Memo (optional)", multiline: true, "aria-label": "note", name: "notes", value: donation.notes || "", onChange: handleChange, onKeyDown: handleKeyDown })] }), errorMessage && _jsx(ErrorMessages, { errors: [errorMessage] })] }))] })] }));
353
+ }, children: [availablePaymentMethods.map((paymentMethod) => (_jsxs(MenuItem, { value: paymentMethod.id, children: [paymentMethod.name, " ", paymentMethod.last4 ? `****${paymentMethod.last4}` : paymentMethod.email || ""] }, paymentMethod.id))), paymentProvider.capabilities.memberNewCard && (_jsxs(MenuItem, { value: "__new__", children: [_jsx(Icon, { sx: { mr: 1, fontSize: 18 }, children: "add_circle" }), " Enter new card"] }))] })] }) })) : (_jsx(Grid, { size: { xs: 12 }, children: selectedGatewayObj?.publicKey && MemberEntry ? (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "subtitle1", sx: { mb: 1 }, children: Locale.label("donation.kingdomFunding.enterCardDetails") }), _jsx(MemberEntry, { ref: entryRef, gateway: selectedGatewayObj, getContext: buildContext }), paymentProvider.capabilities.savedCard && props.person?.id && (_jsx(FormGroup, { sx: { mt: 1 }, children: _jsx(FormControlLabel, { control: _jsx(Checkbox, { checked: saveCard, onChange: (e) => setSaveCard(e.target.checked) }), label: "Save this card for future donations" }) })), paymentProvider.capabilities.savedCard && availablePaymentMethods.length > 0 && (_jsx(Button, { size: "small", sx: { mt: 1 }, onClick: () => {
354
+ setUseNewCard(false);
355
+ const d = { ...donation };
356
+ d.id = availablePaymentMethods[0].id;
357
+ d.type = availablePaymentMethods[0].type;
358
+ setDonation(d);
359
+ setPaymentMethodName(`${availablePaymentMethods[0].name} ${availablePaymentMethods[0].last4 ? `****${availablePaymentMethods[0].last4}` : ""}`);
360
+ }, children: "Use a saved payment method instead" }))] })) : (_jsx(Alert, { severity: "warning", children: Locale.label("donation.kingdomFunding.gatewayConfigMissing") })) })) }), donationType === "recurring" && (_jsxs(Grid, { container: true, spacing: 3, style: { marginTop: 10 }, children: [_jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(TextField, { id: "start-date-field", fullWidth: true, name: "date", type: "date", "aria-label": "date", label: Locale.label("donation.donationForm.startDate"), value: DateHelper.formatHtml5Date(new Date(donation.billing_cycle_anchor || Date.now())), onChange: handleChange, onKeyDown: handleKeyDown }) }), _jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: Locale.label("donation.donationForm.frequency") }), _jsxs(Select, { id: "frequency-select", label: Locale.label("donation.donationForm.frequency"), name: "interval", "aria-label": "interval", value: interval, onChange: handleChange, children: [_jsx(MenuItem, { value: "one_week", children: Locale.label("donation.donationForm.weekly") }), _jsx(MenuItem, { value: "two_week", children: Locale.label("donation.donationForm.biWeekly") }), _jsx(MenuItem, { value: "one_month", children: Locale.label("donation.donationForm.monthly") }), _jsx(MenuItem, { value: "three_month", children: Locale.label("donation.donationForm.quarterly") }), _jsx(MenuItem, { value: "one_year", children: Locale.label("donation.donationForm.annually") })] })] }) })] })), _jsxs("div", { id: "fund-selection", className: "form-group", children: [funds && fundDonations && (_jsxs(_Fragment, { children: [_jsx("h4", { children: Locale.label("donation.donationForm.fund") }), _jsx(FundDonations, { fundDonations: fundDonations, funds: funds, updatedFunction: handleFundDonationsChange, currency: gateway?.currency })] })), fundsTotal > 0 && (_jsxs(_Fragment, { children: [(gateway?.payFees === true) ? (_jsxs(Typography, { fontSize: 14, fontStyle: "italic", children: ["*", Locale.label("donation.donationForm.fees").replace("{}", CurrencyHelper.formatCurrencyWithLocale(transactionFee, gateway?.currency || "USD"))] })) : (_jsx(FormGroup, { children: _jsx(FormControlLabel, { control: _jsx(Checkbox, {}), name: "transaction-fee", label: Locale.label("donation.donationForm.cover").replace("{}", CurrencyHelper.formatCurrencyWithLocale(transactionFee, gateway?.currency || "USD")), onChange: handleCheckChange }) })), _jsxs("p", { children: [Locale.label("donation.donationForm.total"), ": ", CurrencyHelper.formatCurrencyWithLocale(total, gateway?.currency || "USD")] })] })), _jsx(TextField, { id: "donation-notes", fullWidth: true, label: Locale.label("donation.kingdomFunding.memo"), multiline: true, "aria-label": "note", name: "notes", value: donation.notes || "", onChange: handleChange, onKeyDown: handleKeyDown })] }), errorMessage && _jsx(ErrorMessages, { errors: [errorMessage] })] }))] })] }));
373
361
  }
374
362
  };
363
+ // Public member donation form. Applies the resolved provider's SDK wrapper
364
+ // (Stripe -> <Elements>, so 3DS works) around the provider-agnostic inner form.
365
+ export const MultiGatewayDonationForm = (props) => {
366
+ const provider = getPaymentProvider(props?.paymentGateways?.find(g => g.enabled !== false)?.provider || "stripe");
367
+ const Wrapper = provider.MemberWrapper;
368
+ const inner = _jsx(MultiGatewayDonationInner, { ...props });
369
+ return Wrapper ? _jsx(Wrapper, { stripePromise: props.stripePromise, children: inner }) : inner;
370
+ };
375
371
  //# sourceMappingURL=MultiGatewayDonationForm.js.map