first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View 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,
});
}

View 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,
};
}