Fix PayPal billing flow and mobile admin UX
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-05 10:19:29 +01:00
parent c43327af74
commit 0d7a861875
39 changed files with 1630 additions and 253 deletions

View File

@@ -12,6 +12,7 @@ interface CheckoutState {
error: string | null;
paymentCompleted: boolean;
checkoutSessionId: string | null;
checkoutActionUrl: string | null;
}
interface CheckoutWizardContextType {
@@ -29,6 +30,7 @@ interface CheckoutWizardContextType {
} | null;
paymentCompleted: boolean;
checkoutSessionId: string | null;
checkoutActionUrl: string | null;
selectPackage: (pkg: CheckoutPackage) => void;
setSelectedPackage: (pkg: CheckoutPackage) => void;
setAuthUser: (user: unknown) => void;
@@ -44,6 +46,8 @@ interface CheckoutWizardContextType {
setPaymentCompleted: (completed: boolean) => void;
setCheckoutSessionId: (sessionId: string | null) => void;
clearCheckoutSessionId: () => void;
setCheckoutActionUrl: (url: string | null) => void;
clearCheckoutActionUrl: () => void;
}
const CheckoutWizardContext = createContext<CheckoutWizardContextType | null>(null);
@@ -59,6 +63,7 @@ const initialState: CheckoutState = {
error: null,
paymentCompleted: false,
checkoutSessionId: null,
checkoutActionUrl: null,
};
type CheckoutAction =
@@ -71,7 +76,8 @@ type CheckoutAction =
| { type: 'SET_LOADING'; payload: boolean }
| { type: 'SET_ERROR'; payload: string | null }
| { type: 'SET_PAYMENT_COMPLETED'; payload: boolean }
| { type: 'SET_CHECKOUT_SESSION_ID'; payload: string | null };
| { type: 'SET_CHECKOUT_SESSION_ID'; payload: string | null }
| { type: 'SET_CHECKOUT_ACTION_URL'; payload: string | null };
function checkoutReducer(state: CheckoutState, action: CheckoutAction): CheckoutState {
switch (action.type) {
@@ -109,6 +115,8 @@ function checkoutReducer(state: CheckoutState, action: CheckoutAction): Checkout
return { ...state, paymentCompleted: action.payload };
case 'SET_CHECKOUT_SESSION_ID':
return { ...state, checkoutSessionId: action.payload };
case 'SET_CHECKOUT_ACTION_URL':
return { ...state, checkoutActionUrl: action.payload };
default:
return state;
}
@@ -148,6 +156,7 @@ export function CheckoutWizardProvider({
};
const checkoutSessionStorageKey = 'checkout-session-id';
const checkoutActionStorageKey = 'checkout-action-url';
const [state, dispatch] = useReducer(checkoutReducer, customInitialState);
@@ -174,6 +183,11 @@ export function CheckoutWizardProvider({
if (storedSession) {
dispatch({ type: 'SET_CHECKOUT_SESSION_ID', payload: storedSession });
}
const storedActionUrl = localStorage.getItem(checkoutActionStorageKey);
if (storedActionUrl) {
dispatch({ type: 'SET_CHECKOUT_ACTION_URL', payload: storedActionUrl });
}
}, [initialPackage]);
// Save state to localStorage whenever it changes
@@ -199,6 +213,14 @@ export function CheckoutWizardProvider({
}
}, [state.checkoutSessionId]);
useEffect(() => {
if (state.checkoutActionUrl) {
localStorage.setItem(checkoutActionStorageKey, state.checkoutActionUrl);
} else {
localStorage.removeItem(checkoutActionStorageKey);
}
}, [state.checkoutActionUrl]);
const selectPackage = useCallback((pkg: CheckoutPackage) => {
dispatch({ type: 'SELECT_PACKAGE', payload: pkg });
}, []);
@@ -241,6 +263,7 @@ export function CheckoutWizardProvider({
dispatch({ type: 'SET_ERROR', payload: null });
dispatch({ type: 'SET_PAYMENT_COMPLETED', payload: false });
dispatch({ type: 'SET_CHECKOUT_SESSION_ID', payload: null });
dispatch({ type: 'SET_CHECKOUT_ACTION_URL', payload: null });
}, []);
const setPaymentCompleted = useCallback((completed: boolean) => {
@@ -253,6 +276,15 @@ export function CheckoutWizardProvider({
const clearCheckoutSessionId = useCallback(() => {
dispatch({ type: 'SET_CHECKOUT_SESSION_ID', payload: null });
dispatch({ type: 'SET_CHECKOUT_ACTION_URL', payload: null });
}, []);
const setCheckoutActionUrl = useCallback((url: string | null) => {
dispatch({ type: 'SET_CHECKOUT_ACTION_URL', payload: url });
}, []);
const clearCheckoutActionUrl = useCallback(() => {
dispatch({ type: 'SET_CHECKOUT_ACTION_URL', payload: null });
}, []);
const cancelCheckout = useCallback(() => {
@@ -277,9 +309,10 @@ export function CheckoutWizardProvider({
// State aus localStorage entfernen
localStorage.removeItem('checkout-wizard-state');
localStorage.removeItem(checkoutSessionStorageKey);
localStorage.removeItem(checkoutActionStorageKey);
// Zur Package-Übersicht zurückleiten
window.location.href = '/packages';
}, [state, checkoutSessionStorageKey]);
}, [state, checkoutActionStorageKey, checkoutSessionStorageKey]);
const value: CheckoutWizardContextType = {
state,
@@ -291,6 +324,7 @@ export function CheckoutWizardProvider({
paypalConfig: paypal ?? null,
paymentCompleted: state.paymentCompleted,
checkoutSessionId: state.checkoutSessionId,
checkoutActionUrl: state.checkoutActionUrl,
selectPackage,
setSelectedPackage,
setAuthUser,
@@ -306,6 +340,8 @@ export function CheckoutWizardProvider({
setPaymentCompleted,
setCheckoutSessionId,
clearCheckoutSessionId,
setCheckoutActionUrl,
clearCheckoutActionUrl,
};
return (