diff --git a/README.md b/README.md index bbdb4db3..900fffbb 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ The below code will use Every.org styling, because the `a` tag has the `data-eve ``` +Note that you can use the donate button in our staging environment by using a staging link like: https://staging.every.org/ofsds#/donate + ## Configure To configure your button manually, add `?explicit=1` to the script `src` @@ -69,6 +71,8 @@ Here is an example html file with a manual configuration. ``` +Note that you can use the donate button in our staging environment by adding the `staging: true` parameter to the `create*` calls. + ### Widget #### Configuration options diff --git a/packages/donate-button-v4/src/autoPlayMode.tsx b/packages/donate-button-v4/src/autoPlayMode.tsx index a6c9c365..1249a233 100644 --- a/packages/donate-button-v4/src/autoPlayMode.tsx +++ b/packages/donate-button-v4/src/autoPlayMode.tsx @@ -1,6 +1,7 @@ import {render as preactRender} from 'preact'; import EmbedButton from 'src/components/embed-button'; import {WidgetConfig} from 'src/components/widget/types/WidgetConfig'; +import {BASE_URL, STAGING_BASE_URL} from 'src/constants/url'; import {parseDonateUrl} from 'src/helpers/parseDonateUrl'; import shouldApplyEveryStyleForAllLinks from 'src/helpers/shouldApplyEveryStyleForAllLinks'; import {loadFonts} from 'src/loadFonts'; @@ -114,7 +115,9 @@ function createButton({element, ...options}: CreateButtonProps) { } function findAndReplaceLinks() { - const links = document.querySelectorAll("*[href^='https://www.every.org/']"); + const links = document.querySelectorAll( + `*[href^='${BASE_URL}'], *[href^='${STAGING_BASE_URL}']` + ); links.forEach((link) => { const urlString = link.getAttribute('href'); diff --git a/packages/donate-button-v4/src/components/embed-button/types.ts b/packages/donate-button-v4/src/components/embed-button/types.ts index 089c28ee..4e19db80 100644 --- a/packages/donate-button-v4/src/components/embed-button/types.ts +++ b/packages/donate-button-v4/src/components/embed-button/types.ts @@ -23,6 +23,8 @@ export interface DonateButtonOptions { readonly noExit?: boolean; url?: string; + + staging?: boolean; } export interface EmbedButtonOptions extends DonateButtonOptions { readonly label?: string; diff --git a/packages/donate-button-v4/src/components/widget/api/index.ts b/packages/donate-button-v4/src/components/widget/api/index.ts index 1d6a5b66..afebd55b 100644 --- a/packages/donate-button-v4/src/components/widget/api/index.ts +++ b/packages/donate-button-v4/src/components/widget/api/index.ts @@ -1,7 +1,7 @@ import {DonateFlowCustomization} from 'src/components/widget/types/DonateFlowCustomization'; import {Fundraiser} from 'src/components/widget/types/Fundraiser'; import {Nonprofit} from 'src/components/widget/types/Nonprofit'; -import {BASE_API_URL, BASE_COINGECKO_URL} from 'src/constants/url'; +import {BASE_COINGECKO_URL, getApiUrl} from 'src/constants/url'; type NonprofitResponse = { message: string; @@ -17,9 +17,10 @@ type FundraiserResponse = { }; }; -export async function getNonprofit(nonprofitSlug: string) { +export async function getNonprofit(nonprofitSlug: string, staging?: boolean) { + const apiUrl = getApiUrl(staging); const data: NonprofitResponse = await fetch( - `${BASE_API_URL}/${nonprofitSlug}` + `${apiUrl}/${nonprofitSlug}` ).then(async (response) => response.json()); return data.data.nonprofit; @@ -27,9 +28,11 @@ export async function getNonprofit(nonprofitSlug: string) { export async function getFundraiser( nonprofitSlug: string, - fundraiserSlug: string + fundraiserSlug: string, + staging?: boolean ) { - const url = `${BASE_API_URL}/${nonprofitSlug}/fundraiser/${fundraiserSlug}`; + const apiUrl = getApiUrl(staging); + const url = `${apiUrl}/${nonprofitSlug}/fundraiser/${fundraiserSlug}`; const data: FundraiserResponse = await fetch(url).then(async (response) => response.json() ); @@ -52,8 +55,13 @@ export async function getCoingeckoRate(coingeckoId: string) { return (data as CoingeckoData).market_data.current_price.usd; } -export async function getCustomization(nonprofitId: string, code?: string) { - const url = `${BASE_API_URL}/${nonprofitId}/customization${ +export async function getCustomization( + nonprofitId: string, + code?: string, + staging?: boolean +) { + const apiUrl = getApiUrl(staging); + const url = `${apiUrl}/${nonprofitId}/customization${ code ? `?code=${code}` : '' }`; const response = await fetch(url).then(async (response) => { diff --git a/packages/donate-button-v4/src/components/widget/components/PaymentProcess/CryptoFlow/CryptoSelector.tsx b/packages/donate-button-v4/src/components/widget/components/PaymentProcess/CryptoFlow/CryptoSelector.tsx index b8bd06bf..0c4d696d 100644 --- a/packages/donate-button-v4/src/components/widget/components/PaymentProcess/CryptoFlow/CryptoSelector.tsx +++ b/packages/donate-button-v4/src/components/widget/components/PaymentProcess/CryptoFlow/CryptoSelector.tsx @@ -1,4 +1,5 @@ import {filter as fuzzyFilter} from 'fuzzy'; +import {getSiteUrl} from 'src/constants/url'; import {useRef, useState} from 'preact/hooks'; import {Fragment} from 'preact/jsx-runtime'; import {linkCss} from 'src/components/widget/components/FundraiserLink/styles'; @@ -102,8 +103,9 @@ const CryptoSelectorDropDownItem = ({ const CryptoSupprotLink = () => { const nonprofit = useNonprofitOrError(); - const {primaryColor} = useConfigContext(); - const cryptoSupportBody = `Contents: I would like to make a crypto donation to support https://www.every.org/${nonprofit.primarySlug}.\n\nMy name:\nToken name:\nToken symbol:\nToken quantity:\n\nPlease reply back with an address where I can donate, as this is worth over $100,000 USD.`; + const {primaryColor, staging} = useConfigContext(); + const baseUrl = getSiteUrl(staging); + const cryptoSupportBody = `Contents: I would like to make a crypto donation to support ${baseUrl}${nonprofit.primarySlug}.\n\nMy name:\nToken name:\nToken symbol:\nToken quantity:\n\nPlease reply back with an address where I can donate, as this is worth over $100,000 USD.`; return ( ({ export const FundraiserContextProvider: FunctionalComponent<{ fundraiserSlug?: string; }> = ({children}) => { - const {fundraiserSlug, nonprofitSlug} = useConfigContext(); + const {fundraiserSlug, nonprofitSlug, staging} = useConfigContext(); const [fundraiser, setFundraiser] = useState(FundraiserFetching); @@ -32,12 +32,16 @@ export const FundraiserContextProvider: FunctionalComponent<{ } try { - const response = await getFundraiser(nonprofitSlug, fundraiserSlug); + const response = await getFundraiser( + nonprofitSlug, + fundraiserSlug, + staging + ); setFundraiser(response); } catch { setFundraiser(FundraiserFetchError); } - }, [nonprofitSlug, fundraiserSlug]); + }, [nonprofitSlug, fundraiserSlug, staging]); useEffect(() => { // eslint-disable-next-line @typescript-eslint/no-floating-promises diff --git a/packages/donate-button-v4/src/components/widget/context/NonprofitContext.tsx b/packages/donate-button-v4/src/components/widget/context/NonprofitContext.tsx index fd59c510..9619ae48 100644 --- a/packages/donate-button-v4/src/components/widget/context/NonprofitContext.tsx +++ b/packages/donate-button-v4/src/components/widget/context/NonprofitContext.tsx @@ -30,7 +30,8 @@ export const NonprofitContext = createContext({ export const NonprofitContextProvider: FunctionalComponent<{ nonprofitSlug?: string; code?: string; -}> = ({children, nonprofitSlug, code}) => { + staging?: boolean; +}> = ({children, nonprofitSlug, code, staging}) => { const [nonprofit, setNonprofit] = useState(NonprofitFetching); const [parentNonprofit, setParentNonprofitNonprofit] = @@ -45,7 +46,7 @@ export const NonprofitContextProvider: FunctionalComponent<{ throw new Error('No nonprofit slug provided'); } - const response = await getNonprofit(nonprofitSlug); + const response = await getNonprofit(nonprofitSlug, staging); setNonprofit(response); const parentNonprofitId = @@ -55,7 +56,7 @@ export const NonprofitContextProvider: FunctionalComponent<{ if (parentNonprofitId) { try { - const response = await getNonprofit(parentNonprofitId); + const response = await getNonprofit(parentNonprofitId, staging); setParentNonprofitNonprofit(response); } catch { setParentNonprofitNonprofit(undefined); @@ -66,7 +67,8 @@ export const NonprofitContextProvider: FunctionalComponent<{ try { const customizationResponse = await getCustomization( response.id, - code + code, + staging ); setCustomization(customizationResponse); } catch { @@ -78,7 +80,7 @@ export const NonprofitContextProvider: FunctionalComponent<{ } catch { setNonprofit(NonprofitFetchError); } - }, [nonprofitSlug, code]); + }, [nonprofitSlug, code, staging]); useEffect(() => { // eslint-disable-next-line @typescript-eslint/no-floating-promises diff --git a/packages/donate-button-v4/src/components/widget/context/index.tsx b/packages/donate-button-v4/src/components/widget/context/index.tsx index b364f216..45c3a714 100644 --- a/packages/donate-button-v4/src/components/widget/context/index.tsx +++ b/packages/donate-button-v4/src/components/widget/context/index.tsx @@ -12,6 +12,7 @@ export const ContextProvider: FunctionComponent<{ diff --git a/packages/donate-button-v4/src/components/widget/hooks/useSubmitDonation.ts b/packages/donate-button-v4/src/components/widget/hooks/useSubmitDonation.ts index 490c4213..1dbf996f 100644 --- a/packages/donate-button-v4/src/components/widget/hooks/useSubmitDonation.ts +++ b/packages/donate-button-v4/src/components/widget/hooks/useSubmitDonation.ts @@ -39,7 +39,8 @@ export const useSubmitDonation = () => { webhookToken, redeemGiftCardInFlow, designation, - requireShareInfo + requireShareInfo, + staging } = useConfigContext(); const submitDonation = useCallback( @@ -72,7 +73,8 @@ export const useSubmitDonation = () => { partnerMetadata: config.partnerMetadata, designation, requireShareInfo, - customFieldResponses + customFieldResponses, + staging }; switch (selectedPaymentMethod) { case PaymentMethod.CRYPTO: @@ -156,7 +158,8 @@ export const useSubmitDonation = () => { designation, requireShareInfo, customization, - customFieldValues + customFieldValues, + staging ] ); diff --git a/packages/donate-button-v4/src/components/widget/types/WidgetConfig.ts b/packages/donate-button-v4/src/components/widget/types/WidgetConfig.ts index 6b5ef6a2..1e2a4209 100644 --- a/packages/donate-button-v4/src/components/widget/types/WidgetConfig.ts +++ b/packages/donate-button-v4/src/components/widget/types/WidgetConfig.ts @@ -57,4 +57,6 @@ export interface WidgetConfig { previewMode?: boolean; code?: string; + + staging?: boolean; } diff --git a/packages/donate-button-v4/src/constants/url.ts b/packages/donate-button-v4/src/constants/url.ts index 85defd05..615b8cbc 100644 --- a/packages/donate-button-v4/src/constants/url.ts +++ b/packages/donate-button-v4/src/constants/url.ts @@ -1,5 +1,15 @@ export const BASE_URL = 'https://www.every.org/'; export const BASE_API_URL = 'https://partners.every.org/v0.2/nonprofit'; + +export const STAGING_BASE_URL = 'https://staging.every.org/'; +export const STAGING_API_URL = + 'https://partners-staging.every.org/v0.2/nonprofit'; + +export const getSiteUrl = (staging?: boolean) => + staging ? STAGING_BASE_URL : BASE_URL; +export const getApiUrl = (staging?: boolean) => + staging ? STAGING_API_URL : BASE_API_URL; + export const BASE_CLOUDINARY_URL = 'https://res.cloudinary.com/everydotorg/image/upload/'; export const BASE_COINGECKO_URL = 'https://api.coingecko.com/api/v3'; diff --git a/packages/donate-button-v4/src/helpers/constructDonateUrl.ts b/packages/donate-button-v4/src/helpers/constructDonateUrl.ts index d1dee17c..de426b56 100644 --- a/packages/donate-button-v4/src/helpers/constructDonateUrl.ts +++ b/packages/donate-button-v4/src/helpers/constructDonateUrl.ts @@ -4,7 +4,7 @@ import { DonateUrlParameters, UTM_QUERY_PARAM } from 'src/components/widget/types/UrlParams'; -import {BASE_URL, GIFT_CARD_URL} from 'src/constants/url'; +import {GIFT_CARD_ROUTE, getSiteUrl} from 'src/constants/url'; const UTM_MEDIUM = 'donate-button-0.4'; // Update this if the major version changes const HASH = 'donate'; @@ -23,6 +23,7 @@ interface BaseUrlParams { designation?: string; requireShareInfo?: boolean; customFieldResponses?: string; + staging?: boolean; } interface DonateUrlParams extends BaseUrlParams { @@ -59,9 +60,10 @@ function serializeParams( function getBaseUrl({ fundraiserSlug, - nonprofitSlug -}: Pick) { - let baseUrl = BASE_URL + nonprofitSlug; + nonprofitSlug, + staging +}: Pick) { + let baseUrl = getSiteUrl(staging) + nonprofitSlug; if (fundraiserSlug) { baseUrl += '/f/' + fundraiserSlug; @@ -211,9 +213,10 @@ export function constructGiftCardUrl({ return `${baseUrl}?${parameters}#/${HASH}`; } + const giftCardUrl = getSiteUrl(rest.staging) + GIFT_CARD_ROUTE; const parameters = serializeParams({ nonprofitSlug: rest.nonprofitSlug }); - return `${GIFT_CARD_URL}?${parameters}`; + return `${giftCardUrl}?${parameters}`; } diff --git a/packages/donate-button-v4/src/helpers/parseDonateUrl.ts b/packages/donate-button-v4/src/helpers/parseDonateUrl.ts index 49fc82fd..4b250b00 100644 --- a/packages/donate-button-v4/src/helpers/parseDonateUrl.ts +++ b/packages/donate-button-v4/src/helpers/parseDonateUrl.ts @@ -8,8 +8,10 @@ import { UTM_QUERY_PARAM } from 'src/components/widget/types/UrlParams'; import {WidgetConfig} from 'src/components/widget/types/WidgetConfig'; +import {STAGING_BASE_URL} from 'src/constants/url'; const MAX_AMOUNT_SUGGESTIONS = 5; +const STAGING_HOSTNAME = new URL(STAGING_BASE_URL).hostname; /** * Just for backwards compatibility @@ -121,6 +123,8 @@ export function parseDonateUrl( return; } + const staging = url.hostname === STAGING_HOSTNAME || undefined; + return removeEmptyValues({ fundraiserSlug, nonprofitSlug, @@ -138,6 +142,7 @@ export function parseDonateUrl( requireShareInfo, webhookToken, partnerMetadata, - code + code, + staging }); }