diff --git a/package-lock.json b/package-lock.json index bf0a7d70..9f7ca79d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -509,6 +509,33 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.14.0.tgz", "integrity": "sha512-sDOAZcYwynHFTbLo6n8kIbLiVF3a3BLkrmehJUyEbT9F+Smbi47kLGS2gG2g0fjBLR/Lr1InPD7kXL7FaTqEkw==" }, + "@paypal/paypal-js": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@paypal/paypal-js/-/paypal-js-3.1.10.tgz", + "integrity": "sha512-NGKXjCOU7993pqtVie0E9YmaGs29Q7vWEQe77DhHeBb5qJT8PmEfhi/dDIyjYTCmqXlLsy+f8aPipYllRC4uNw==", + "requires": { + "promise-polyfill": "^8.2.0" + } + }, + "@paypal/react-paypal-js": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@paypal/react-paypal-js/-/react-paypal-js-6.0.1.tgz", + "integrity": "sha512-lj2RbOMEhcCDyS34+KMQW/2m3o3I1hYS41jtRk8iSKOtYOza0aUJCrRfCe9LiDhSL01NrRSJxC+Z2Fc8DX063Q==", + "requires": { + "@paypal/paypal-js": "^3.1.10", + "@paypal/sdk-constants": "^1.0.103" + } + }, + "@paypal/sdk-constants": { + "version": "1.0.104", + "resolved": "https://registry.npmjs.org/@paypal/sdk-constants/-/sdk-constants-1.0.104.tgz", + "integrity": "sha512-DEB8fhpjWZadJa+EMJUdp5w62l9s75oUG9UjBkEy578E6tw5oJ6qGeKIugcdnT23aht0qz1tQC96O+FuUIrHOQ==", + "requires": { + "cross-domain-utils": "^2.0.10", + "hi-base32": "^0.5.0", + "zalgo-promise": "^1.0.28" + } + }, "@polka/url": { "version": "1.0.0-next.12", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.12.tgz", @@ -1717,6 +1744,14 @@ "sha.js": "^2.4.8" } }, + "cross-domain-utils": { + "version": "2.0.34", + "resolved": "https://registry.npmjs.org/cross-domain-utils/-/cross-domain-utils-2.0.34.tgz", + "integrity": "sha512-ke4PirGRXwEElEmE/7k5aCvCW+EqbgseT7AOObzFfaVnOLuEVN9SjVWoOfS/qAT0rDPn3ggmNDW6mguMBy4HgA==", + "requires": { + "zalgo-promise": "^1.0.11" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3437,6 +3472,11 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "hi-base32": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", + "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -5601,21 +5641,6 @@ "object-assign": "^4.1.1" } }, - "react-async-script-loader": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/react-async-script-loader/-/react-async-script-loader-0.3.0.tgz", - "integrity": "sha1-x0KzyiXgi6Yat+tkNx+BQCdpK4Y=", - "requires": { - "hoist-non-react-statics": "^1.0.3" - }, - "dependencies": { - "hoist-non-react-statics": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", - "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" - } - } - }, "react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -6982,6 +7007,11 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zalgo-promise": { + "version": "1.0.46", + "resolved": "https://registry.npmjs.org/zalgo-promise/-/zalgo-promise-1.0.46.tgz", + "integrity": "sha512-tzPpQRqaQQavxl17TY98nznvmr+judUg3My7ugsUcRDbdqisYOE2z79HNNDgXnyX3eA0mf2bMOJrqHptt00npg==" } } } diff --git a/package.json b/package.json index f6f66f49..6b883705 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@material-ui/core": "^4.11.3", "@material-ui/lab": "^4.0.0-alpha.57", "@next/bundle-analyzer": "^10.1.3", + "@paypal/react-paypal-js": "^6.0.1", "@sentry/browser": "^6.2.5", "@sentry/integrations": "^6.2.5", "@sentry/node": "^6.2.5", @@ -29,7 +30,6 @@ "next-i18next": "^8.1.3", "node-sass": "^5.0.0", "react": "17.0.2", - "react-async-script-loader": "^0.3.0", "react-dom": "17.0.2", "react-hook-form": "6.14.2", "styled-jsx": "^3.4.4", diff --git a/src/Donations/Components/PaymentsForm.tsx b/src/Donations/Components/PaymentsForm.tsx index f979b77b..24868a9a 100644 --- a/src/Donations/Components/PaymentsForm.tsx +++ b/src/Donations/Components/PaymentsForm.tsx @@ -15,12 +15,12 @@ import { import ToggleSwitch from "../../Common/InputTypes/ToggleSwitch"; import CardPayments from "../PaymentMethods/CardPayments"; import SepaPayments from "../PaymentMethods/SepaPayments"; -import PaypalPayments from "../PaymentMethods/PaypalPayments"; import GiroPayPayments from "../PaymentMethods/GiroPayPayments"; import SofortPayments from "../PaymentMethods/SofortPayment"; import TaxDeductionOption from "../Micros/TaxDeductionOption"; import ButtonLoader from "../../Common/ContentLoaders/ButtonLoader"; import { useAuth0 } from "@auth0/auth0-react"; +import NewPaypal from "../PaymentMethods/NewPaypal"; interface Props {} @@ -34,6 +34,7 @@ function PaymentsForm({}: Props): ReactElement { const { isLoading, isAuthenticated, getAccessTokenSilently } = useAuth0(); + const [isDonationLoading, setisDonationLoading] = React.useState(false) const { paymentSetup, country, @@ -52,6 +53,7 @@ function PaymentsForm({}: Props): ReactElement { giftDetails, isTaxDeductible, isDirectDonation, + setDonationUid } = React.useContext(QueryParamContext); React.useEffect(() => { @@ -91,6 +93,7 @@ function PaymentsForm({}: Props): ReactElement { if (!isLoading && isAuthenticated) { token = await getAccessTokenSilently(); } + setisDonationLoading(true) const donation = await createDonationFunction({ isTaxDeductible, country, @@ -113,7 +116,9 @@ function PaymentsForm({}: Props): ReactElement { setdonationID(donation.id); setshouldCreateDonation(false); setisCreatingDonation(false); + setDonationUid(donation.uid) } + setisDonationLoading(false) } // This feature allows the user to show or hide their names in the leaderboard @@ -135,6 +140,10 @@ function PaymentsForm({}: Props): ReactElement { } }, [shouldCreateDonation]); + React.useEffect(()=>{ + setPaymentType("CARD") + },[currency]) + return ready ? ( isPaymentProcessing ? ( @@ -273,13 +282,14 @@ function PaymentsForm({}: Props): ReactElement { aria-labelledby={`scrollable-force-tab-${"Paypal"}`} > {paymentType === "Paypal" && ( - )} diff --git a/src/Donations/PaymentMethods/NewPaypal.tsx b/src/Donations/PaymentMethods/NewPaypal.tsx new file mode 100644 index 00000000..fb076e54 --- /dev/null +++ b/src/Donations/PaymentMethods/NewPaypal.tsx @@ -0,0 +1,105 @@ +import React, { ReactElement } from "react"; +import { + PayPalScriptProvider, + PayPalButtons, + usePayPalScriptReducer, +} from "@paypal/react-paypal-js"; +import { QueryParamContext } from "../../Layout/QueryParamContext"; + +interface Props { + paymentSetup: any; + treeCount: number; + treeCost: number; + currency: string; + donationID: any; + payDonationFunction: Function; + setPaymentError: Function; +} + +function NewPaypal({ + paymentSetup, + treeCount, + treeCost, + currency, + donationID, + payDonationFunction, + setPaymentError +}: Props): ReactElement { + const initialOptions = { + "client-id": paymentSetup?.gateways.paypal.authorization.client_id, + "enable-funding": "venmo,giropay,sofort", + "disable-funding": "card", + currency: currency, + }; + + const { donationUid } = React.useContext(QueryParamContext); + + function createOrder(data, actions) { + return actions.order.create({ + purchase_units: [ + { + amount: { + value: (treeCount * treeCost).toFixed(2), + currency: currency, + }, + invoice_id: `planet-${donationID}`, + custom_id: donationUid, + }, + ], + application_context: { + brand_name: "Plant-for-the-Planet", + }, + }); + } + + function onApprove(data, actions) { + return actions.order.capture().then(function (details) { + // This function shows a transaction success message to your buyer. + data = { + ...data, + type: "sdk", + }; + payDonationFunction("paypal", data); + }); + } + + const onError = (data) => { + setPaymentError(`Your order ${data.orderID} failed due to some error.`) + }; + + const onCancel = (data) => { + setPaymentError('Order was cancelled, please try again') + }; + + return ( + <> + + + + + + ); +} + +function ReloadButton({ currency }: any) { + const [{ isPending, options }, dispatch] = usePayPalScriptReducer(); + + React.useEffect(() => { + dispatch({ + type: "resetOptions", + value: { + ...options, + currency: currency, + }, + }); + }, [currency]); + + return isPending ?
: null; +} + +export default NewPaypal; \ No newline at end of file diff --git a/src/Donations/PaymentMethods/Paypal.jsx b/src/Donations/PaymentMethods/Paypal.jsx deleted file mode 100644 index c2f3d454..00000000 --- a/src/Donations/PaymentMethods/Paypal.jsx +++ /dev/null @@ -1,130 +0,0 @@ -// eslint-disable-next-line no-unused-vars -import React from 'react'; -import ReactDOM from 'react-dom'; -import PropTypes from 'prop-types'; -import scriptLoader from 'react-async-script-loader'; - -class Paypal extends React.Component { - constructor(props) { - super(props); - if (typeof window !== 'undefined') { - window.React = React; - window.ReactDOM = ReactDOM; - } - - this.state = { - showButton: false - }; - } - - componentDidMount() { - const { isScriptLoaded, isScriptLoadSucceed } = this.props; - - if (isScriptLoaded && isScriptLoadSucceed) { - this.setState({ showButton: true }); - } - } - - UNSAFE_componentWillReceiveProps({ isScriptLoaded, isScriptLoadSucceed }) { - const isLoadedButWasntLoadedBefore = - !this.state.showButton && !this.props.isScriptLoaded && isScriptLoaded; - - if (isLoadedButWasntLoadedBefore) { - if (isScriptLoadSucceed) { - this.setState({ showButton: true }); - } - } - } - - render() { - let paypal; - if (typeof window !== 'undefined') { - paypal = window.paypal; - } - const { - amount, - mode, - currency, - onSuccess, - donationId, - clientID - } = this.props; - - const { showButton } = this.state; - - const CLIENT = { - // users client id here - [mode]: clientID - }; - const invoice_number = `planet-${donationId}`; - //debug('invoice we are sending to paypal as donationId:', invoice_number); - const payment = () => { - return paypal.rest.payment.create(mode, CLIENT, { - transactions: [ - { - amount: { - total: Math.round(amount * 100) / 100, - currency - }, - invoice_number: invoice_number - } - ] - }); - }; - - // see https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/customize-button/ - const buttonStyle = { - color: 'silver', // gold | blue | silver | black - shape: 'pill', // pill | rect - label: 'pay', // checkout | credit | pay | buynow | paypal | installment - size: 'large' // small | medium | large | responsive - }; - - const onAuthorize = data => { - onSuccess(data); - }; - - const onError = data => { - onSuccess(data); - }; - - const onCancel = data => { - let error = { - ...data, - type: 'error', - error: { message: 'Transaction cancelled' } - }; - onSuccess(error); - }; - - return ( -
- {showButton && ( - - )} -
- ); - } -} - -Paypal.propTypes = { - amount: PropTypes.number.isRequired, - currency: PropTypes.string.isRequired, - isScriptLoaded: PropTypes.bool, - isScriptLoadSucceed: PropTypes.bool, - mode: PropTypes.string, - onSuccess: PropTypes.func -}; - -export default scriptLoader('https://www.paypalobjects.com/api/checkout.js')( - Paypal -); diff --git a/src/Donations/PaymentMethods/PaypalPayments.tsx b/src/Donations/PaymentMethods/PaypalPayments.tsx deleted file mode 100644 index 818363d5..00000000 --- a/src/Donations/PaymentMethods/PaypalPayments.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { ReactElement } from "react"; -import Paypal from "./Paypal"; -import { paypalCurrencies } from "./../../Utils/paypalCurrencies"; -function PaypalPayments({ - paymentSetup, - treeCount, - treeCost, - currency, - donationID, - payDonationFunction, -}: any): ReactElement { - return ( -
- {paypalCurrencies.includes(currency) && paymentSetup?.gateways.paypal && ( -
- { - payDonationFunction("paypal", data); - }} - amount={treeCost * treeCount} - currency={currency} - donationId={donationID} - mode={ - paymentSetup?.gateways.paypal.isLive ? "production" : "sandbox" - } - clientID={paymentSetup?.gateways.paypal.authorization.client_id} - /> -
- )} -
- ); -} - -export default PaypalPayments; diff --git a/src/Layout/QueryParamContext.tsx b/src/Layout/QueryParamContext.tsx index 2e40c923..69bf5709 100644 --- a/src/Layout/QueryParamContext.tsx +++ b/src/Layout/QueryParamContext.tsx @@ -55,6 +55,8 @@ export const QueryParamContext = React.createContext({ setSelectedProjects: (value: Array) => {}, allProjects: [], allowTaxDeductionChange: true, + donationUid:null, + setDonationUid: (value: string) => "" }); export default function QueryParamProvider({ children }: any) { @@ -78,6 +80,8 @@ export default function QueryParamProvider({ children }: any) { const [isDirectDonation, setisDirectDonation] = React.useState(false); + const [donationUid, setDonationUid] = useState(null) + const [isPaymentOptionsLoading, setIsPaymentOptionsLoading] = React.useState(false); @@ -519,6 +523,8 @@ export default function QueryParamProvider({ children }: any) { setSelectedProjects, allProjects, allowTaxDeductionChange, + donationUid, + setDonationUid }} > {children}