diff --git a/src/App.js b/src/App.js
index 7de70ab..825152a 100644
--- a/src/App.js
+++ b/src/App.js
@@ -55,6 +55,9 @@ const ChatAssistant = lazy(() => import(/* webpackChunkName: "chat" */ "./compon
const PresseverleihPage = lazy(() => import(/* webpackChunkName: "presseverleih" */ "./pages/PresseverleihPage.js"));
const ThcTestPage = lazy(() => import(/* webpackChunkName: "thc-test" */ "./pages/ThcTestPage.js"));
+// Lazy load payment success page
+const PaymentSuccess = lazy(() => import(/* webpackChunkName: "payment" */ "./components/PaymentSuccess.js"));
+
// Import theme from separate file to reduce main bundle size
import defaultTheme from "./theme.js";
// Lazy load theme customizer for development only
@@ -224,6 +227,9 @@ const AppContent = ({ currentTheme, onThemeChange }) => {
{/* Profile page */}
} />
+ {/* Payment success page for Mollie redirects */}
+ } />
+
{/* Reset password page */}
{
+ try {
+ // Get the stored payment ID from localStorage
+ const pendingPayment = localStorage.getItem('pendingPayment');
+
+ if (!pendingPayment) {
+ console.error('No pending payment found in localStorage');
+ this.setState({
+ redirectUrl: '/profile#cart',
+ processing: false,
+ error: 'No payment information found'
+ });
+ return;
+ }
+
+ let paymentData;
+ try {
+ paymentData = JSON.parse(pendingPayment);
+ // Clear the pending payment data
+ localStorage.removeItem('pendingPayment');
+ } catch (error) {
+ console.error('Error parsing pending payment data:', error);
+ this.setState({
+ redirectUrl: '/profile#cart',
+ processing: false,
+ error: 'Invalid payment data'
+ });
+ return;
+ }
+
+ if (!paymentData.paymentId) {
+ console.error('No payment ID found in stored payment data');
+ this.setState({
+ redirectUrl: '/profile#cart',
+ processing: false,
+ error: 'Missing payment ID'
+ });
+ return;
+ }
+
+ // Check payment status with backend
+ this.checkMolliePaymentStatus(paymentData.paymentId);
+
+ } catch (error) {
+ console.error('Error processing Mollie payment:', error);
+ this.setState({
+ redirectUrl: '/profile#cart',
+ processing: false,
+ error: 'Payment processing failed'
+ });
+ }
+ };
+
+ checkMolliePaymentStatus = (paymentId) => {
+ const { socket } = this.context;
+
+ if (!socket || !socket.connected) {
+ console.error('Socket not connected');
+ this.setState({
+ redirectUrl: '/profile#cart',
+ processing: false,
+ error: 'Connection error'
+ });
+ return;
+ }
+
+ socket.emit('checkMollieIntent', { paymentId }, (response) => {
+ if (response.success) {
+ console.log('Payment Status:', response.payment.status);
+ console.log('Is Paid:', response.payment.isPaid);
+ console.log('Order Created:', response.order.created);
+
+ if (response.order.orderId) {
+ console.log('Order ID:', response.order.orderId);
+ }
+
+ // Build the redirect URL with Mollie completion parameters
+ const profileUrl = new URL('/profile', window.location.origin);
+ profileUrl.searchParams.set('mollieComplete', 'true');
+ profileUrl.searchParams.set('mollie_payment_id', paymentId);
+ profileUrl.searchParams.set('mollie_status', response.payment.status);
+ profileUrl.searchParams.set('mollie_amount', response.payment.amount);
+ profileUrl.searchParams.set('mollie_timestamp', Date.now().toString());
+ profileUrl.searchParams.set('mollie_is_paid', response.payment.isPaid.toString());
+
+ if (response.order.orderId) {
+ profileUrl.searchParams.set('mollie_order_id', response.order.orderId.toString());
+ }
+
+ // Set hash to cart tab
+ profileUrl.hash = '#cart';
+
+ this.setState({
+ redirectUrl: profileUrl.pathname + profileUrl.search + profileUrl.hash,
+ processing: false
+ });
+ } else {
+ console.error('Failed to check payment status:', response.error);
+ this.setState({
+ redirectUrl: '/profile#cart',
+ processing: false,
+ error: response.error || 'Payment verification failed'
+ });
+ }
+ });
+ };
+
+ render() {
+ const { redirectUrl, processing, error } = this.state;
+
+ if (processing) {
+ return (
+
+
+
+ Zahlung wird überprüft...
+
+
+ Bitte warten Sie, während wir Ihre Zahlung bei Mollie überprüfen.
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+ Zahlungsüberprüfung fehlgeschlagen
+
+
+ {error}
+
+
+ Sie werden zu Ihrem Profil weitergeleitet...
+
+
+ );
+ }
+
+ if (redirectUrl) {
+ return ;
+ }
+
+ // Fallback redirect to profile
+ return ;
+ }
+}
+
+export default PaymentSuccess;
\ No newline at end of file
diff --git a/src/components/profile/CartTab.js b/src/components/profile/CartTab.js
index e50c6be..e8f06a4 100644
--- a/src/components/profile/CartTab.js
+++ b/src/components/profile/CartTab.js
@@ -377,34 +377,23 @@ class CartTab extends Component {
(total, item) => total + item.price * item.quantity,
0
);
- const totalAmount = Math.round((subtotal + deliveryCost) * 100); // Convert to cents
+ const totalAmount = Math.round((subtotal + deliveryCost) * 100) / 100;
- if (this.context && this.context.socket && this.context.socket.connected) {
- this.context.socket.emit(
- "createMollieIntent",
- { amount: totalAmount, invoiceId: 'A-34344' },
- (response) => {
- if (response.success) {
-
- localStorage.setItem('pendingPayment', JSON.stringify({
- paymentId: response.paymentId,
- amount: totalAmount,
- timestamp: Date.now()
- }));
- window.location.href = response.checkoutUrl;
-
- } else {
- console.error("Error:", response.error);
- }
- }
- );
- } else {
- console.error("Socket context not available");
- this.setState({
- isCompletingOrder: false,
- completionError: "Cannot connect to server. Please try again later.",
- });
- }
+ // Prepare complete order data for Mollie intent creation
+ const mollieOrderData = {
+ amount: totalAmount,
+ items: cartItems,
+ invoiceAddress,
+ deliveryAddress: useSameAddress ? invoiceAddress : deliveryAddress,
+ deliveryMethod,
+ paymentMethod: "mollie",
+ deliveryCost,
+ note,
+ domain: window.location.origin,
+ saveAddressForFuture,
+ };
+
+ this.orderService.createMollieIntent(mollieOrderData);
return;
}
diff --git a/src/components/profile/OrderProcessingService.js b/src/components/profile/OrderProcessingService.js
index 23ba016..3ee8e7f 100644
--- a/src/components/profile/OrderProcessingService.js
+++ b/src/components/profile/OrderProcessingService.js
@@ -49,18 +49,29 @@ class OrderProcessingService {
waitForVerifyTokenAndProcessOrder() {
// Check if window.cart is already populated (verifyToken already completed)
if (Array.isArray(window.cart) && window.cart.length > 0) {
- this.processStripeOrderWithCart(window.cart);
+ 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) {
- this.processStripeOrderWithCart([...window.cart]); // Copy the cart
+ 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."
@@ -111,6 +122,21 @@ class OrderProcessingService {
});
}
+ 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) {
@@ -205,6 +231,20 @@ class OrderProcessingService {
});
}
+ 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) {
const context = this.getContext();
@@ -271,6 +311,40 @@ class OrderProcessingService {
}
}
+ // Create Mollie payment intent
+ createMollieIntent(mollieOrderData) {
+ const context = this.getContext();
+ if (context && context.socket && context.socket.connected) {
+ context.socket.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.",
+ });
+ }
+ }
+ );
+ } else {
+ console.error("Socket context not available");
+ this.setState({
+ isCompletingOrder: false,
+ completionError: "Cannot connect to server. Please try again later.",
+ });
+ }
+ }
+
// Calculate delivery cost
getDeliveryCost() {
const { deliveryMethod, paymentMethod } = this.getState();
diff --git a/src/config.js b/src/config.js
index eae36db..df32320 100644
--- a/src/config.js
+++ b/src/config.js
@@ -1,5 +1,5 @@
const config = {
- baseUrl: "https://growheads.de",
+ baseUrl: "https://dev.seedheads.de",
apiBaseUrl: "",
googleClientId: "928121624463-jbgfdlgem22scs1k9c87ucg4ffvaik6o.apps.googleusercontent.com",
stripePublishableKey: "pk_test_51R7lltRtpe3h1vwJzIrDb5bcEigTLBHrtqj9SiPX7FOEATSuD6oJmKc8xpNp49ShpGJZb2GShHIUqj4zlSIz4olj00ipOuOAnu",
diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js
index 720a68d..c1848c7 100644
--- a/src/pages/ProfilePage.js
+++ b/src/pages/ProfilePage.js
@@ -28,9 +28,11 @@ const ProfilePage = (props) => {
const [orderIdFromHash, setOrderIdFromHash] = useState(null);
const [paymentCompletion, setPaymentCompletion] = useState(null);
- // @note Check for payment completion parameters from Stripe redirect
+ // @note Check for payment completion parameters from Stripe and Mollie redirects
useEffect(() => {
const urlParams = new URLSearchParams(location.search);
+
+ // Check for Stripe payment completion
const isComplete = urlParams.has('complete');
const paymentIntent = urlParams.get('payment_intent');
const paymentIntentClientSecret = urlParams.get('payment_intent_client_secret');
@@ -38,6 +40,7 @@ const ProfilePage = (props) => {
if (isComplete && paymentIntent && redirectStatus) {
setPaymentCompletion({
+ paymentType: 'stripe',
paymentIntent,
paymentIntentClientSecret,
redirectStatus,
@@ -52,6 +55,39 @@ const ProfilePage = (props) => {
newUrl.searchParams.delete('redirect_status');
window.history.replaceState({}, '', newUrl.toString());
}
+
+ // Check for Mollie payment completion
+ const isMollieComplete = urlParams.has('mollieComplete');
+ const molliePaymentId = urlParams.get('mollie_payment_id');
+ const mollieStatus = urlParams.get('mollie_status');
+ const mollieAmount = urlParams.get('mollie_amount');
+ const mollieTimestamp = urlParams.get('mollie_timestamp');
+ const mollieIsPaid = urlParams.get('mollie_is_paid');
+ const mollieOrderId = urlParams.get('mollie_order_id');
+
+ if (isMollieComplete && molliePaymentId && mollieStatus) {
+ setPaymentCompletion({
+ paymentType: 'mollie',
+ molliePaymentId,
+ mollieStatus,
+ mollieAmount: mollieAmount ? parseFloat(mollieAmount) : null,
+ mollieTimestamp: mollieTimestamp ? parseInt(mollieTimestamp) : null,
+ mollieIsPaid: mollieIsPaid === 'true',
+ mollieOrderId: mollieOrderId ? parseInt(mollieOrderId) : null,
+ isSuccessful: mollieIsPaid === 'true'
+ });
+
+ // Clean up the URL by removing the Mollie payment parameters
+ const newUrl = new URL(window.location);
+ newUrl.searchParams.delete('mollieComplete');
+ newUrl.searchParams.delete('mollie_payment_id');
+ newUrl.searchParams.delete('mollie_status');
+ newUrl.searchParams.delete('mollie_amount');
+ newUrl.searchParams.delete('mollie_timestamp');
+ newUrl.searchParams.delete('mollie_is_paid');
+ newUrl.searchParams.delete('mollie_order_id');
+ window.history.replaceState({}, '', newUrl.toString());
+ }
}, [location.search]);
useEffect(() => {