Files
reactShop/src/components/profile/OrderProcessingService.js

372 lines
11 KiB
JavaScript

import { isUserLoggedIn } from "../LoginComponent.js";
class OrderProcessingService {
constructor(getContext, setState) {
this.getContext = getContext;
this.setState = setState;
this.verifyTokenHandler = null;
this.verifyTokenTimeout = null;
this.socketHandler = null;
this.paymentCompletionData = null;
}
// Clean up all event listeners and timeouts
cleanup() {
if (this.verifyTokenHandler) {
window.removeEventListener('cart', this.verifyTokenHandler);
this.verifyTokenHandler = null;
}
if (this.verifyTokenTimeout) {
clearTimeout(this.verifyTokenTimeout);
this.verifyTokenTimeout = null;
}
if (this.socketHandler) {
window.removeEventListener('cart', this.socketHandler);
this.socketHandler = null;
}
}
// Handle payment completion from parent component
handlePaymentCompletion(paymentCompletion, onClearPaymentCompletion) {
// Store payment completion data before clearing
this.paymentCompletionData = { ...paymentCompletion };
// Clear payment completion data to prevent duplicates
if (onClearPaymentCompletion) {
onClearPaymentCompletion();
}
// Show payment confirmation immediately but wait for verifyToken to complete
this.setState({
showPaymentConfirmation: true,
cartItems: [] // Clear UI cart immediately
});
// Wait for verifyToken to complete and populate window.cart, then process order
this.waitForVerifyTokenAndProcessOrder();
}
waitForVerifyTokenAndProcessOrder() {
// Check if window.cart is already populated (verifyToken already completed)
if (Array.isArray(window.cart) && window.cart.length > 0) {
if (this.paymentCompletionData && this.paymentCompletionData.paymentType === 'mollie') {
this.processMollieOrderWithCart(window.cart);
} else {
this.processStripeOrderWithCart(window.cart);
}
return;
}
// Listen for cart event which is dispatched after verifyToken completes
this.verifyTokenHandler = () => {
if (Array.isArray(window.cart) && window.cart.length > 0) {
const cartCopy = [...window.cart]; // Copy the cart
// Clear window.cart after copying
window.cart = [];
window.dispatchEvent(new CustomEvent("cart"));
// Process based on payment type
if (this.paymentCompletionData && this.paymentCompletionData.paymentType === 'mollie') {
this.processMollieOrderWithCart(cartCopy);
} else {
this.processStripeOrderWithCart(cartCopy);
}
} else {
this.setState({
completionError: "Cart is empty. Please add items to your cart before placing an order."
});
}
// Clean up listener
if (this.verifyTokenHandler) {
window.removeEventListener('cart', this.verifyTokenHandler);
this.verifyTokenHandler = null;
}
};
window.addEventListener('cart', this.verifyTokenHandler);
// Set up a timeout as fallback (in case verifyToken fails)
this.verifyTokenTimeout = setTimeout(() => {
if (Array.isArray(window.cart) && window.cart.length > 0) {
this.processStripeOrderWithCart([...window.cart]);
window.cart = [];
window.dispatchEvent(new CustomEvent("cart"));
} else {
this.setState({
completionError: "Unable to load cart data. Please refresh the page and try again."
});
}
// Clean up
if (this.verifyTokenHandler) {
window.removeEventListener('cart', this.verifyTokenHandler);
this.verifyTokenHandler = null;
}
}, 5000); // 5 second timeout
}
processStripeOrderWithCart(cartItems) {
// Clear timeout if it exists
if (this.verifyTokenTimeout) {
clearTimeout(this.verifyTokenTimeout);
this.verifyTokenTimeout = null;
}
// Store cart items in state and process order
this.setState({
originalCartItems: cartItems
}, () => {
this.processStripeOrder();
});
}
processMollieOrderWithCart(cartItems) {
// Clear timeout if it exists
if (this.verifyTokenTimeout) {
clearTimeout(this.verifyTokenTimeout);
this.verifyTokenTimeout = null;
}
// Store cart items in state and process order
this.setState({
originalCartItems: cartItems
}, () => {
this.processMollieOrder();
});
}
processStripeOrder() {
// If no original cart items, don't process
if (!this.getState().originalCartItems || this.getState().originalCartItems.length === 0) {
this.setState({ completionError: "Cart is empty. Please add items to your cart before placing an order." });
return;
}
// If socket is ready, process immediately
const { isLoggedIn: isAuthenticated } = isUserLoggedIn();
if (isAuthenticated) {
this.sendStripeOrder();
return;
}
// Wait for socket to be ready
this.socketHandler = () => {
const { isLoggedIn: isAuthenticated } = isUserLoggedIn();
const state = this.getState();
if (isAuthenticated && state.showPaymentConfirmation && !state.isCompletingOrder) {
this.sendStripeOrder();
}
// Clean up
if (this.socketHandler) {
window.removeEventListener('cart', this.socketHandler);
this.socketHandler = null;
}
};
window.addEventListener('cart', this.socketHandler);
}
sendStripeOrder() {
const state = this.getState();
// Don't process if already processing or completed
if (state.isCompletingOrder || state.orderCompleted) {
return;
}
this.setState({ isCompletingOrder: true, completionError: null });
const {
deliveryMethod,
invoiceAddress,
deliveryAddress,
useSameAddress,
originalCartItems,
note,
saveAddressForFuture,
} = state;
const deliveryCost = this.getDeliveryCost();
const orderData = {
items: originalCartItems,
invoiceAddress,
deliveryAddress: useSameAddress ? invoiceAddress : deliveryAddress,
deliveryMethod,
paymentMethod: "stripe",
deliveryCost,
note,
domain: window.location.origin,
stripeData: this.paymentCompletionData ? {
paymentIntent: this.paymentCompletionData.paymentIntent,
paymentIntentClientSecret: this.paymentCompletionData.paymentIntentClientSecret,
redirectStatus: this.paymentCompletionData.redirectStatus,
} : null,
saveAddressForFuture,
};
window.socketManager.emit("issueStripeOrder", orderData, (response) => {
if (response.success) {
this.setState({
isCompletingOrder: false,
orderCompleted: true,
completionError: null,
});
} else {
this.setState({
isCompletingOrder: false,
completionError: response.error || "Failed to complete order. Please try again.",
});
}
});
}
processMollieOrder() {
// For Mollie payments, the backend handles order creation automatically
// when payment is successful. We just need to show success state.
this.setState({
isCompletingOrder: false,
orderCompleted: true,
completionError: null,
});
// Clear the cart since order was created by backend
window.cart = [];
window.dispatchEvent(new CustomEvent("cart"));
}
// Process regular (non-Stripe) orders
processRegularOrder(orderData) {
window.socketManager.emit("issueOrder", orderData, (response) => {
if (response.success) {
// Clear the cart
window.cart = [];
window.dispatchEvent(new CustomEvent("cart"));
// Reset state and navigate to orders tab
this.setState({
isCheckingOut: false,
cartItems: [],
isCompletingOrder: false,
completionError: null,
});
// Call success callback if provided
if (this.onOrderSuccess) {
this.onOrderSuccess();
}
} else {
this.setState({
isCompletingOrder: false,
completionError: response.error || "Failed to complete order. Please try again.",
});
}
});
}
// Create Stripe payment intent
createStripeIntent(totalAmount, loadStripeComponent) {
window.socketManager.emit(
"createStripeIntent",
{ amount: totalAmount },
(response) => {
if (response.success) {
loadStripeComponent(response.client_secret);
} else {
console.error("Error:", response.error);
this.setState({
isCompletingOrder: false,
completionError: response.error || "Failed to create Stripe payment intent. Please try again.",
});
}
}
);
};
// Create Mollie payment intent
createMollieIntent(mollieOrderData) {
window.socketManager.emit(
"createMollieIntent",
mollieOrderData,
(response) => {
if (response.success) {
// Store pending payment info and redirect
localStorage.setItem('pendingPayment', JSON.stringify({
paymentId: response.paymentId,
amount: mollieOrderData.amount,
timestamp: Date.now()
}));
window.location.href = response.checkoutUrl;
} else {
console.error("Error:", response.error);
this.setState({
isCompletingOrder: false,
completionError: response.error || "Failed to create Mollie payment intent. Please try again.",
});
}
}
);
}
// Calculate delivery cost
getDeliveryCost() {
const { deliveryMethod, paymentMethod, cartItems } = this.getState();
let cost = 0;
switch (deliveryMethod) {
case "DHL":
cost = 6.99;
break;
case "DPD":
cost = 4.9;
break;
case "Sperrgut":
cost = 28.99;
break;
case "Abholung":
cost = 0;
break;
default:
cost = 6.99;
}
// Check for free shipping threshold (>= 100€ cart value)
// Free shipping applies to DHL, DPD, and Sperrgut deliveries when cart value >= 100€
if (cartItems && Array.isArray(cartItems) && deliveryMethod !== "Abholung") {
const cartValue = cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
if (cartValue >= 100) {
cost = 0; // Free shipping for orders >= 100€
}
}
// Add onDelivery surcharge if selected (still applies even with free shipping)
if (paymentMethod === "onDelivery") {
cost += 8.99;
}
return cost;
}
// Helper method to get current state (to be overridden by component)
getState() {
throw new Error("getState method must be implemented by the component");
}
// Set callback for order success
setOrderSuccessCallback(callback) {
this.onOrderSuccess = callback;
}
}
export default OrderProcessingService;