From 3c33e89a0c3eb11e85b6a38c0ce44ff895464349 Mon Sep 17 00:00:00 2001 From: Hannes Reinberger <40197225+hreinberger@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:08:38 +0100 Subject: [PATCH] Add Mollie payment integration and enhance form validation --- src/app/components/form/address.tsx | 3 + src/app/components/form/checkoutbutton.tsx | 26 ++++++- src/app/components/form/checkoutform.tsx | 3 +- .../form/methods/componentpaymentmethods.tsx | 52 ++++---------- src/app/layout.tsx | 68 ++++++++++--------- src/app/lib/MollieContext.js | 49 +++++++++++++ src/app/lib/mollie.ts | 3 + src/app/lib/server-actions.ts | 3 +- src/app/lib/validation.ts | 1 + tsconfig.json | 8 ++- 10 files changed, 138 insertions(+), 78 deletions(-) create mode 100644 src/app/lib/MollieContext.js diff --git a/src/app/components/form/address.tsx b/src/app/components/form/address.tsx index e8f329f..3b4990b 100644 --- a/src/app/components/form/address.tsx +++ b/src/app/components/form/address.tsx @@ -134,6 +134,9 @@ export default async function Address() { Austria Netherlands United Kingdom + + Northern Ireland (will error) + diff --git a/src/app/components/form/checkoutbutton.tsx b/src/app/components/form/checkoutbutton.tsx index 5ce1545..9cc1e85 100644 --- a/src/app/components/form/checkoutbutton.tsx +++ b/src/app/components/form/checkoutbutton.tsx @@ -3,6 +3,8 @@ import { Button } from '@radix-ui/themes'; import { useFormStatus } from 'react-dom'; import { checkoutVariant } from '@/app/lib/validation'; +import { useMollie } from '@/app/lib/MollieContext'; +import { createPayment } from '@/app/lib/server-actions'; export default function CheckoutButton({ variant, @@ -12,14 +14,36 @@ export default function CheckoutButton({ // display a loading indicator once the button is clicked const { pending } = useFormStatus(); + // handle form submission when the card component is used + // here we have to first get the card token from mollie + // then we have to append the token to the form and submit it to the createPayment function + + const { mollie } = useMollie(); + const payWithToken = async () => { + const formElement = document.querySelector('form'); + if (!formElement) { + console.error('Form element not found'); + return; + } + const formData = new FormData(formElement); + const { token, error } = await mollie.createToken(); + if (error) { + console.error('Error creating card token:', error); + return; + } + formData.append('cardToken', token); + createPayment(formData); + }; + return ( ); } diff --git a/src/app/components/form/checkoutform.tsx b/src/app/components/form/checkoutform.tsx index c899cc8..94f39dd 100644 --- a/src/app/components/form/checkoutform.tsx +++ b/src/app/components/form/checkoutform.tsx @@ -7,7 +7,6 @@ import { Flex, Grid, Heading } from '@radix-ui/themes'; import React, { useState } from 'react'; // Lib -import { createPayment } from '@/app/lib/server-actions'; import { checkoutVariant } from '@/app/lib/validation'; // Form components @@ -28,7 +27,7 @@ export default function CheckoutForm({ React.useState('hosted'); return ( // The form data is sent to the createPayment function when the form is submitted -
+ (null); - const mollieComponents = useRef(null); - - // Load Mollie script and initialize Mollie object + const { mollie } = useMollie(); useEffect(() => { - let mollie = window.Mollie('pfl_FHTbr2nyYb', { - locale: 'en_US', - testmode: true, - }); - console.debug('Mollie object created'); - console.debug(mollie); - mollieInitialized.current = true; - mollieObject.current = mollie; - - if (mollieInitialized.current) { - const card = document.getElementById('card'); - if (card) { - console.debug('Creating Mollie card component'); - console.debug(mollieObject.current); - const mollie = mollieObject.current; - const cardComponent = mollie.createComponent('card'); - cardComponent.mount(card); - mollieComponents.current = cardComponent; - } + let cardComponent: any; + if (mollie) { + cardComponent = mollie.createComponent('card'); + cardComponent.mount('#card'); } return () => { - console.debug('Unmounting Mollie card component'); - const cardComponent = mollieComponents.current; - cardComponent.unmount(); - mollie = null; - mollieInitialized.current = false; - mollieObject.current = null; - mollieComponents.current = null; + if (cardComponent) { + cardComponent.unmount(); + } }; - }, [mollieInitialized, mollieObject, mollieComponents]); + }, [mollie]); return ( <> @@ -113,7 +85,7 @@ export default function ComponentPaymentMethods() { - 2223 0000 1047 9399 + 2223 0000 1047 9399 diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5c7b739..8f48326 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,6 +7,8 @@ import Footer from '@/app/components/ui/footer.js'; import { Providers } from '@/app/components/ui/providers.jsx'; import Script from 'next/script'; +import { MollieProvider } from './lib/MollieContext'; + export const metadata: Metadata = { title: 'Mollie Demo App', description: 'A demo app for Mollie payments, written in nextJS', @@ -23,40 +25,42 @@ export default function RootLayout({ suppressHydrationWarning > - - - {/* */} - -
- -
-
- {children} -
-
-
-
-
-
-
+ + + + {/* */} + +
+ +
+
+ {children} +
+
+
+
+
+
+
+