diff --git a/src/components/Mollie.js b/src/components/Mollie.js
new file mode 100644
index 0000000..e35de51
--- /dev/null
+++ b/src/components/Mollie.js
@@ -0,0 +1,381 @@
+import React, { Component, useState } from "react";
+import { Button, Box, Typography, CircularProgress } from "@mui/material";
+import config from "../config.js";
+
+// Function to lazy load Mollie script
+const loadMollie = () => {
+ return new Promise((resolve, reject) => {
+ // Check if Mollie is already loaded
+ if (window.Mollie) {
+ resolve(window.Mollie);
+ return;
+ }
+
+ // Create script element
+ const script = document.createElement('script');
+ script.src = 'https://js.mollie.com/v1/mollie.js';
+ script.async = true;
+
+ script.onload = () => {
+ if (window.Mollie) {
+ resolve(window.Mollie);
+ } else {
+ reject(new Error('Mollie failed to load'));
+ }
+ };
+
+ script.onerror = () => {
+ reject(new Error('Failed to load Mollie script'));
+ };
+
+ document.head.appendChild(script);
+ });
+};
+
+const CheckoutForm = ({ mollie }) => {
+ const [errorMessage, setErrorMessage] = useState(null);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ React.useEffect(() => {
+ if (!mollie) return;
+
+ let mountedComponents = {
+ cardNumber: null,
+ cardHolder: null,
+ expiryDate: null,
+ verificationCode: null
+ };
+
+ try {
+ // Create Mollie components
+ const cardNumber = mollie.createComponent('cardNumber');
+ const cardHolder = mollie.createComponent('cardHolder');
+ const expiryDate = mollie.createComponent('expiryDate');
+ const verificationCode = mollie.createComponent('verificationCode');
+
+ // Store references for cleanup
+ mountedComponents = {
+ cardNumber,
+ cardHolder,
+ expiryDate,
+ verificationCode
+ };
+
+ // Mount components
+ cardNumber.mount('#card-number');
+ cardHolder.mount('#card-holder');
+ expiryDate.mount('#expiry-date');
+ verificationCode.mount('#verification-code');
+
+ // Set up error handling
+ cardNumber.addEventListener('change', event => {
+ const errorElement = document.querySelector('#card-number-error');
+ if (errorElement) {
+ if (event.error && event.touched) {
+ errorElement.textContent = event.error;
+ } else {
+ errorElement.textContent = '';
+ }
+ }
+ });
+
+ cardHolder.addEventListener('change', event => {
+ const errorElement = document.querySelector('#card-holder-error');
+ if (errorElement) {
+ if (event.error && event.touched) {
+ errorElement.textContent = event.error;
+ } else {
+ errorElement.textContent = '';
+ }
+ }
+ });
+
+ expiryDate.addEventListener('change', event => {
+ const errorElement = document.querySelector('#expiry-date-error');
+ if (errorElement) {
+ if (event.error && event.touched) {
+ errorElement.textContent = event.error;
+ } else {
+ errorElement.textContent = '';
+ }
+ }
+ });
+
+ verificationCode.addEventListener('change', event => {
+ const errorElement = document.querySelector('#verification-code-error');
+ if (errorElement) {
+ if (event.error && event.touched) {
+ errorElement.textContent = event.error;
+ } else {
+ errorElement.textContent = '';
+ }
+ }
+ });
+
+ // Components are now mounted and ready
+
+ } catch (error) {
+ console.error('Error creating Mollie components:', error);
+ setErrorMessage('Failed to initialize payment form. Please try again.');
+ }
+
+ // Cleanup function
+ return () => {
+ try {
+ if (mountedComponents.cardNumber) mountedComponents.cardNumber.unmount();
+ if (mountedComponents.cardHolder) mountedComponents.cardHolder.unmount();
+ if (mountedComponents.expiryDate) mountedComponents.expiryDate.unmount();
+ if (mountedComponents.verificationCode) mountedComponents.verificationCode.unmount();
+ } catch (error) {
+ console.error('Error cleaning up Mollie components:', error);
+ }
+ };
+ }, [mollie]);
+
+ const handleSubmit = async (event) => {
+ event.preventDefault();
+
+ if (!mollie || isSubmitting) {
+ return;
+ }
+
+ setIsSubmitting(true);
+ setErrorMessage(null);
+
+ try {
+ const { token, error } = await mollie.createToken();
+
+ if (error) {
+ setErrorMessage(error.message || 'Payment failed. Please try again.');
+ setIsSubmitting(false);
+ return;
+ }
+
+ if (token) {
+ // Handle successful token creation
+ // Create a payment completion event similar to Stripe
+ const mollieCompletionData = {
+ mollieToken: token,
+ paymentMethod: 'mollie'
+ };
+
+ // Dispatch a custom event to notify the parent component
+ const completionEvent = new CustomEvent('molliePaymentComplete', {
+ detail: mollieCompletionData
+ });
+ window.dispatchEvent(completionEvent);
+
+ // For now, redirect to profile with completion data
+ const returnUrl = `${window.location.origin}/profile?complete&mollie_token=${token}`;
+ window.location.href = returnUrl;
+ }
+ } catch (error) {
+ console.error('Error creating Mollie token:', error);
+ setErrorMessage('Payment failed. Please try again.');
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+
+
+ Kreditkarte oder Sofortüberweisung
+
+
+
+
+ );
+};
+
+class Mollie extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ mollie: null,
+ loading: true,
+ error: null,
+ };
+ this.molliePromise = loadMollie();
+ }
+
+ componentDidMount() {
+ this.molliePromise
+ .then((MollieClass) => {
+ try {
+ // Initialize Mollie with profile key
+ const mollie = MollieClass(config.mollieProfileKey, {
+ locale: 'de_DE',
+ testmode: true // Set to false for production
+ });
+ this.setState({ mollie, loading: false });
+ } catch (error) {
+ console.error('Error initializing Mollie:', error);
+ this.setState({
+ error: 'Failed to initialize payment system. Please try again.',
+ loading: false
+ });
+ }
+ })
+ .catch((error) => {
+ console.error('Error loading Mollie:', error);
+ this.setState({
+ error: 'Failed to load payment system. Please try again.',
+ loading: false
+ });
+ });
+ }
+
+ render() {
+ const { mollie, loading, error } = this.state;
+
+ if (loading) {
+ return (
+
+
+
+ Zahlungskomponente wird geladen...
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+ {error}
+
+
+
+ );
+ }
+
+ return ;
+ }
+}
+
+export default Mollie;
diff --git a/src/components/profile/CartTab.js b/src/components/profile/CartTab.js
index 4cc6f0e..a579a58 100644
--- a/src/components/profile/CartTab.js
+++ b/src/components/profile/CartTab.js
@@ -51,6 +51,9 @@ class CartTab extends Component {
showStripePayment: false,
StripeComponent: null,
isLoadingStripe: false,
+ showMolliePayment: false,
+ MollieComponent: null,
+ isLoadingMollie: false,
showPaymentConfirmation: false,
orderCompleted: false,
originalCartItems: []
@@ -116,7 +119,7 @@ class CartTab extends Component {
// Determine payment method - respect constraints
let prefillPaymentMethod = template.payment_method || "wire";
const paymentMethodMap = {
- "credit_card": "stripe",
+ "credit_card": "mollie",//stripe
"bank_transfer": "wire",
"cash_on_delivery": "onDelivery",
"cash": "cash"
@@ -319,6 +322,27 @@ class CartTab extends Component {
}
};
+ loadMollieComponent = async () => {
+ this.setState({ isLoadingMollie: true });
+
+ try {
+ const { default: Mollie } = await import("../Mollie.js");
+ this.setState({
+ MollieComponent: Mollie,
+ showMolliePayment: true,
+ isCompletingOrder: false,
+ isLoadingMollie: false,
+ });
+ } catch (error) {
+ console.error("Failed to load Mollie component:", error);
+ this.setState({
+ isCompletingOrder: false,
+ isLoadingMollie: false,
+ completionError: "Failed to load payment component. Please try again.",
+ });
+ }
+ };
+
handleCompleteOrder = () => {
this.setState({ completionError: null }); // Clear previous errors
@@ -363,6 +387,25 @@ class CartTab extends Component {
this.orderService.createStripeIntent(totalAmount, this.loadStripeComponent);
return;
}
+ // Handle Mollie payment differently
+ if (paymentMethod === "mollie") {
+ // Store the cart items used for Mollie payment in sessionStorage for later reference
+ try {
+ sessionStorage.setItem('molliePaymentCart', JSON.stringify(cartItems));
+ } catch (error) {
+ console.error("Failed to store Mollie payment cart:", error);
+ }
+
+ // Calculate total amount for Mollie
+ const subtotal = cartItems.reduce(
+ (total, item) => total + item.price * item.quantity,
+ 0
+ );
+ const totalAmount = Math.round((subtotal + deliveryCost) * 100); // Convert to cents
+
+ this.orderService.createMollieIntent(totalAmount, this.loadMollieComponent);
+ return;
+ }
// Handle regular orders
const orderData = {
@@ -398,6 +441,9 @@ class CartTab extends Component {
showStripePayment,
StripeComponent,
isLoadingStripe,
+ showMolliePayment,
+ MollieComponent,
+ isLoadingMollie,
showPaymentConfirmation,
orderCompleted,
} = this.state;
@@ -434,7 +480,7 @@ class CartTab extends Component {
@@ -442,7 +488,7 @@ class CartTab extends Component {
{cartItems.length > 0 && (
- {isLoadingStripe ? (
+ {isLoadingStripe || isLoadingMollie ? (
Zahlungskomponente wird geladen...
@@ -468,9 +514,29 @@ class CartTab extends Component {
>
+ ) : showMolliePayment && MollieComponent ? (
+ <>
+
+
+
+
+ >
) : (
{
if ((deliveryMethod === "DHL" || deliveryMethod === "DPD") && paymentMethod === "cash") {
- handlePaymentMethodChange({ target: { value: "stripe" } });
+ handlePaymentMethodChange({ target: { value: "mollie" } });
}
}, [deliveryMethod, paymentMethod, handlePaymentMethodChange]);
@@ -42,8 +42,22 @@ const PaymentMethodSelector = ({ paymentMethod, onChange, deliveryMethod, onDeli
description: "Bezahlen Sie per Banküberweisung",
disabled: totalAmount === 0,
},
- {
+ /*{
id: "stripe",
+ name: "Karte oder Sofortüberweisung (Stripe)",
+ description: totalAmount < 0.50 && totalAmount > 0
+ ? "Bezahlen Sie per Karte oder Sofortüberweisung (Mindestbetrag: 0,50 €)"
+ : "Bezahlen Sie per Karte oder Sofortüberweisung",
+ disabled: totalAmount < 0.50 || (deliveryMethod !== "DHL" && deliveryMethod !== "DPD" && deliveryMethod !== "Abholung"),
+ icons: [
+ "/assets/images/giropay.png",
+ "/assets/images/maestro.png",
+ "/assets/images/mastercard.png",
+ "/assets/images/visa_electron.png",
+ ],
+ },*/
+ {
+ id: "mollie",
name: "Karte oder Sofortüberweisung",
description: totalAmount < 0.50 && totalAmount > 0
? "Bezahlen Sie per Karte oder Sofortüberweisung (Mindestbetrag: 0,50 €)"
diff --git a/src/config.js b/src/config.js
index eae36db..99c3682 100644
--- a/src/config.js
+++ b/src/config.js
@@ -3,6 +3,7 @@ const config = {
apiBaseUrl: "",
googleClientId: "928121624463-jbgfdlgem22scs1k9c87ucg4ffvaik6o.apps.googleusercontent.com",
stripePublishableKey: "pk_test_51R7lltRtpe3h1vwJzIrDb5bcEigTLBHrtqj9SiPX7FOEATSuD6oJmKc8xpNp49ShpGJZb2GShHIUqj4zlSIz4olj00ipOuOAnu",
+ mollieProfileKey: "pfl_AtcRTimCff",
// SEO and Business Information
siteName: "Growheads.de",