75
common/resources/client/billing/checkout/paypal/checkout-paypal-done.tsx
Executable file
75
common/resources/client/billing/checkout/paypal/checkout-paypal-done.tsx
Executable file
@@ -0,0 +1,75 @@
|
||||
import {CheckoutLayout} from '../checkout-layout';
|
||||
import {useParams, useSearchParams} from 'react-router-dom';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {message} from '@common/i18n/message';
|
||||
import {CheckoutProductSummary} from '../checkout-product-summary';
|
||||
import {
|
||||
BillingRedirectMessage,
|
||||
BillingRedirectMessageConfig,
|
||||
} from '../../billing-redirect-message';
|
||||
import {apiClient} from '@common/http/query-client';
|
||||
import {useBootstrapData} from '@common/core/bootstrap-data/bootstrap-data-context';
|
||||
|
||||
export function CheckoutPaypalDone() {
|
||||
const {invalidateBootstrapData} = useBootstrapData();
|
||||
const {productId, priceId} = useParams();
|
||||
const [params] = useSearchParams();
|
||||
|
||||
const [messageConfig, setMessageConfig] =
|
||||
useState<BillingRedirectMessageConfig>();
|
||||
|
||||
useEffect(() => {
|
||||
const subscriptionId = params.get('subscriptionId');
|
||||
const status = params.get('status');
|
||||
if (subscriptionId && status === 'success') {
|
||||
storeSubscriptionDetailsLocally(subscriptionId).then(() => {
|
||||
setMessageConfig(
|
||||
getRedirectMessageConfig('success', productId, priceId),
|
||||
);
|
||||
window.location.href = '/billing';
|
||||
});
|
||||
} else {
|
||||
setMessageConfig(getRedirectMessageConfig(status, productId, priceId));
|
||||
}
|
||||
}, [priceId, productId, params, invalidateBootstrapData]);
|
||||
|
||||
return (
|
||||
<CheckoutLayout>
|
||||
<BillingRedirectMessage config={messageConfig} />
|
||||
<CheckoutProductSummary showBillingLine={false} />
|
||||
</CheckoutLayout>
|
||||
);
|
||||
}
|
||||
|
||||
function getRedirectMessageConfig(
|
||||
status?: 'success' | 'error' | string | null,
|
||||
productId?: string,
|
||||
priceId?: string,
|
||||
): BillingRedirectMessageConfig {
|
||||
switch (status) {
|
||||
case 'success':
|
||||
return {
|
||||
message: message('Subscription successful!'),
|
||||
status: 'success',
|
||||
buttonLabel: message('Return to site'),
|
||||
link: '/billing',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
message: message('Something went wrong. Please try again.'),
|
||||
status: 'error',
|
||||
buttonLabel: message('Go back'),
|
||||
link: errorLink(productId, priceId),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function errorLink(productId?: string, priceId?: string): string {
|
||||
return productId && priceId ? `/checkout/${productId}/${priceId}` : '/';
|
||||
}
|
||||
|
||||
function storeSubscriptionDetailsLocally(subscriptionId: string) {
|
||||
return apiClient.post('billing/paypal/store-subscription-details-locally', {
|
||||
paypal_subscription_id: subscriptionId,
|
||||
});
|
||||
}
|
||||
85
common/resources/client/billing/checkout/paypal/use-paypal.ts
Executable file
85
common/resources/client/billing/checkout/paypal/use-paypal.ts
Executable file
@@ -0,0 +1,85 @@
|
||||
import {useEffect, useRef, useState} from 'react';
|
||||
import {loadScript} from '@paypal/paypal-js';
|
||||
import {useProducts} from '@common/billing/pricing-table/use-products';
|
||||
import {useSettings} from '@common/core/settings/use-settings';
|
||||
|
||||
interface UsePaypalProps {
|
||||
productId?: string;
|
||||
priceId?: string;
|
||||
}
|
||||
export function usePaypal({productId, priceId}: UsePaypalProps) {
|
||||
const {data} = useProducts();
|
||||
const paypalLoadStarted = useRef<boolean>(false);
|
||||
const paypalButtonsRendered = useRef<boolean>(false);
|
||||
const [paypalIsLoaded, setPaypalIsLoaded] = useState(false);
|
||||
const paypalElementRef = useRef<HTMLDivElement>(null);
|
||||
const {
|
||||
base_url,
|
||||
billing: {
|
||||
stripe: {enable: stripeEnabled},
|
||||
paypal: {enable: paypalEnabled, public_key},
|
||||
},
|
||||
} = useSettings();
|
||||
|
||||
useEffect(() => {
|
||||
if (!paypalEnabled || !public_key || paypalLoadStarted.current) return;
|
||||
loadScript({
|
||||
clientId: public_key,
|
||||
intent: 'subscription',
|
||||
vault: true,
|
||||
disableFunding: stripeEnabled ? 'card' : undefined,
|
||||
}).then(() => {
|
||||
setPaypalIsLoaded(true);
|
||||
});
|
||||
paypalLoadStarted.current = true;
|
||||
}, [public_key, paypalEnabled, stripeEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!paypalIsLoaded ||
|
||||
!window.paypal?.Buttons ||
|
||||
!paypalElementRef.current ||
|
||||
!data?.products.length ||
|
||||
!productId ||
|
||||
!priceId ||
|
||||
paypalButtonsRendered.current
|
||||
)
|
||||
return;
|
||||
|
||||
const product = data.products.find(p => p.id === parseInt(productId));
|
||||
const price = product?.prices.find(p => p.id === parseInt(priceId));
|
||||
|
||||
window.paypal
|
||||
.Buttons({
|
||||
style: {
|
||||
label: 'pay',
|
||||
},
|
||||
createSubscription: (data, actions) => {
|
||||
return actions.subscription.create({
|
||||
application_context: {
|
||||
shipping_preference: 'NO_SHIPPING',
|
||||
},
|
||||
plan_id: price?.paypal_id!,
|
||||
});
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
actions.redirect(
|
||||
`${base_url}/checkout/${productId}/${priceId}/paypal/done?subscriptionId=${data.subscriptionID}&status=success`
|
||||
);
|
||||
return Promise.resolve();
|
||||
},
|
||||
onError: e => {
|
||||
location.href = `${base_url}/checkout/${productId}/${priceId}/paypal/done?status=error`;
|
||||
},
|
||||
})
|
||||
.render(paypalElementRef.current)
|
||||
.then(() => {
|
||||
paypalButtonsRendered.current = true;
|
||||
});
|
||||
}, [productId, priceId, data, paypalIsLoaded, base_url]);
|
||||
|
||||
return {
|
||||
paypalElementRef,
|
||||
stripeIsEnabled: public_key != null && paypalEnabled,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user