From 9a236e7152ae80da98d1aac08a179336c69a9b6f Mon Sep 17 00:00:00 2001 From: Jaren Goldberg Date: Fri, 29 Sep 2023 18:28:00 -0400 Subject: [PATCH] fixes --- components/CheckoutForm.tsx | 23 +++++------ components/PaymentStatus.tsx | 38 +++++++++--------- components/ProductCard.tsx | 4 +- lib/hooks/useLocalStorage.tsx | 62 ----------------------------- pages/admin/evenementen/create.tsx | 3 +- pages/admin/sponsors/create.tsx | 1 + pages/api/stripe/customer.ts | 2 + pages/api/stripe/payment_success.ts | 22 +++++----- pages/{checkout.tsx => kassa.tsx} | 11 +++-- pages/webshop.tsx | 10 ++--- src/mail/mail.ts | 4 +- src/models/Order.ts | 4 +- 12 files changed, 64 insertions(+), 120 deletions(-) delete mode 100644 lib/hooks/useLocalStorage.tsx rename pages/{checkout.tsx => kassa.tsx} (77%) diff --git a/components/CheckoutForm.tsx b/components/CheckoutForm.tsx index aee1d5e..6507125 100644 --- a/components/CheckoutForm.tsx +++ b/components/CheckoutForm.tsx @@ -1,9 +1,8 @@ -import { BaseSyntheticEvent, FC, useEffect, useState } from 'react'; +import { BaseSyntheticEvent, FC, useState } from 'react'; import { PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js'; import { FieldValues, useForm } from 'react-hook-form'; import { MoonLoader } from 'react-spinners'; -import Image from 'next/image'; import { useRouter } from 'next/router'; import { CloseIcon } from './Icons'; @@ -26,17 +25,15 @@ const CheckoutForm: FC = ({ paymentIntent }) => { const nameSubmit = (data: FieldValues, e: BaseSyntheticEvent): void => { e.preventDefault(); - console.log(paymentIntent, 'customer'); setCustomer(prevCustomer => ({ ...prevCustomer, customer: { name: data.fullName, email: data.email, payment_intent: paymentIntent, - } + }, })); - window.localStorage.setItem('customer', `{'name':'${data.fullName}','email':'${data.email}'}`); setStep(2); }; @@ -101,40 +98,40 @@ const CheckoutForm: FC = ({ paymentIntent }) => { {step === 1 && (
nameSubmit(data, event!))}> -
)} diff --git a/components/PaymentStatus.tsx b/components/PaymentStatus.tsx index 844713f..671301d 100644 --- a/components/PaymentStatus.tsx +++ b/components/PaymentStatus.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, FC } from 'react'; import { useStripe } from '@stripe/react-stripe-js'; import { useRouter } from 'next/router'; import OrderCartItem from './OrderCartItem'; +import Cookies from 'js-cookie'; interface PaymentData { amount: number, @@ -28,6 +29,7 @@ const PaymentStatus: FC = () => { name: '', }, }); + const [cart, setCart] = useState([]); const [message, setMessage] = useState(''); const [redirect, setRedirect] = useState(false); @@ -44,20 +46,20 @@ const PaymentStatus: FC = () => { if (!clientSecret) return setRedirect(true); if (window === undefined) return setRedirect(true); - if (!window.localStorage.getItem('cart')) return setRedirect(true); + if (!Cookies.get('cart')) return setRedirect(true); stripe .retrievePaymentIntent(clientSecret) .then(({ paymentIntent }) => { switch (paymentIntent?.status) { case 'succeeded': - setMessage('Success! Payment received.'); + setMessage('Succes! Betaling ontvangen.'); fetch('/api/stripe/payment_success', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ paymentIntent: paymentIntent.id, items: window.localStorage.getItem('cart') }), + body: JSON.stringify({ paymentIntent: paymentIntent.id, items: Cookies.get('cart') }), }).then((req) => { - if (req.ok) window.localStorage.setItem('cart', '[]'); + if (req.ok) Cookies.set('cart', '[]'); return req.json(); }).then(data => { setData({ @@ -73,16 +75,16 @@ const PaymentStatus: FC = () => { break; case 'processing': - setMessage('Payment processing. We\'ll update you when payment is received.'); + setMessage('Verwerking van betaling. We houden je op de hoogte wanneer de betaling is ontvangen.'); break; case 'requires_payment_method': - setMessage('Payment failed. Please try another payment method.'); + setMessage('Betaling mislukt. Probeer een andere betaalmethode.'); setRedirect(true); break; default: - setMessage('Something went wrong.'); + setMessage('Er ging iets mis.'); setRedirect(true); break; } @@ -95,12 +97,12 @@ const PaymentStatus: FC = () => {
-

{message || 'It looks like your checkout expired.'}

+

{message || 'Het lijkt erop dat je kassa is verlopen.'}

@@ -110,8 +112,8 @@ const PaymentStatus: FC = () => { {!redirect && (
-

{message}

-

Thank you for ordering, {data.customer.name}!

+

{message}

+

Bedankt voor uw bestelling!

A confirmation email has been sent to {data.customer.email}.

@@ -126,19 +128,19 @@ const PaymentStatus: FC = () => { scope='col' className='px-6 py-3 text-xs font-bold text-left text-gray-500 uppercase ' > - Name + Product @@ -157,7 +159,7 @@ const PaymentStatus: FC = () => {
- Amount + Bedrag - Price + Kosten
-

Total

+

Totale

€{(data.amount / 100).toFixed(2)}

@@ -167,8 +169,8 @@ const PaymentStatus: FC = () => {
-

Payment Details

-

Paid with {data.type}

+

Betalingsgegevens

+

Betaald met {data.type}

**** **** **** {data.last4}

diff --git a/components/ProductCard.tsx b/components/ProductCard.tsx index 67f4306..81b6d7d 100644 --- a/components/ProductCard.tsx +++ b/components/ProductCard.tsx @@ -65,9 +65,9 @@ const ProductCard: FC = ({ product, cart, setCart }) => { <>
{product.name} -

{product.name}

+

{product.name}

{product.price.toLocaleString('de')} EUR

-

{product.stock.toLocaleString('de')} left in stock!

+

Nog {product.stock.toLocaleString('de')} op voorraad!

diff --git a/lib/hooks/useLocalStorage.tsx b/lib/hooks/useLocalStorage.tsx deleted file mode 100644 index d1d6b43..0000000 --- a/lib/hooks/useLocalStorage.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Dispatch, SetStateAction, useEffect, useState } from 'react'; -import { DisplayProduct } from '../../components/ProductCard'; - -export const useLocalStorage = (key: string, initialValue: T): readonly [T, Dispatch>, (value: T) => void, () => string | null] => { - const [storedValue, setStoredValue] = useState(() => { - if (typeof window === 'undefined') { - return initialValue; - } - try { - const item = window.localStorage.getItem(key); - if (!item) { - window.localStorage.setItem(key, JSON.stringify(initialValue)); - return initialValue; - } - return JSON.parse(item); - } catch (error) { - console.log(error); - return initialValue; - } - }); - - const getStorage = (): string | null => { - if (typeof window === 'undefined') { - return null; - } - - return window.localStorage.getItem(key); - }; - - const addValue = (value: T): T | number | undefined => { - if (storedValue.constructor !== Array) { - return value; - } - try { - const index = (storedValue as []).findIndex((o: DisplayProduct) => o.name === (value as DisplayProduct).name); - - if (index !== -1) { - // if ((value as DisplayProduct).stock === 0) return undefined; - // if ((value as DisplayProduct).amount + (storedValue as DisplayProduct[])[index].amount > (value as DisplayProduct).stock) return undefined; - if ((value as DisplayProduct).amount + (storedValue as DisplayProduct[])[index].amount < 0) return undefined; - setStoredValue((storedValue as DisplayProduct[]).filter(p => p.name !== (value as DisplayProduct).name) as SetStateAction); - const amount = (storedValue as DisplayProduct[])[index].amount + (value as DisplayProduct).amount; - // @ts-ignore - setStoredValue(arr => [...arr, { id: value.id, name: value.name, price: value.price * amount, amount, stock: value.stock, imageURL: value.imageURL }]); - return (value as DisplayProduct).amount + (storedValue as DisplayProduct[])[index].amount; - } - - // @ts-ignore - setStoredValue(arr => [...arr, value]); - return (value as DisplayProduct).amount; - - } catch (error) { - console.log(error); - } - }; - - useEffect(() => { - window.localStorage.setItem(key, JSON.stringify(storedValue)); - }, [key, storedValue]); - - return [initialValue, setStoredValue, addValue, getStorage] as const; -}; \ No newline at end of file diff --git a/pages/admin/evenementen/create.tsx b/pages/admin/evenementen/create.tsx index f93e3f7..5789d63 100644 --- a/pages/admin/evenementen/create.tsx +++ b/pages/admin/evenementen/create.tsx @@ -22,11 +22,10 @@ const AdminCreateProducts: NextPage<{ user: { email: string, has2faEnabled: bool const router = useRouter(); const [selected, setSelected] = useState(); - console.log(selected); const onSubmit = async (data: FieldValues, event?: BaseSyntheticEvent): Promise => { event?.preventDefault(); - console.log(data); + try { const req = await fetch('/api/events', { method: 'POST', diff --git a/pages/admin/sponsors/create.tsx b/pages/admin/sponsors/create.tsx index 6de12af..7915471 100644 --- a/pages/admin/sponsors/create.tsx +++ b/pages/admin/sponsors/create.tsx @@ -25,6 +25,7 @@ const AdminCreateSponsors: NextPage<{ user: { email: string, has2faEnabled: bool url: data.url, }), }); + if (req.ok) { console.log('ok'); } else if (req.status === 401) { diff --git a/pages/api/stripe/customer.ts b/pages/api/stripe/customer.ts index 86fecf9..eee78fa 100644 --- a/pages/api/stripe/customer.ts +++ b/pages/api/stripe/customer.ts @@ -28,6 +28,8 @@ export default async function customerHandler( case 'POST': { const { customer } = req.body.customer; + console.log(customer); + if (!customer.payment_intent || !customer.email || !customer.name || !customer.name.match(/^[a-zA-Z]+\s[a-zA-Z]+\s?$/)) { res.status(StatusCodes.BAD_REQUEST).json({ error: true, diff --git a/pages/api/stripe/payment_success.ts b/pages/api/stripe/payment_success.ts index a4f8d39..33fffdb 100644 --- a/pages/api/stripe/payment_success.ts +++ b/pages/api/stripe/payment_success.ts @@ -4,7 +4,8 @@ import { ResponseData } from '../../../src/types'; import dbConnect from '../../../src/util/dbConnect'; import { Stripe } from 'stripe'; import { mail } from '../../../src/mail'; -import { IProduct, Order, Product } from '../../../src/models'; +import { Order, Product } from '../../../src/models'; +import { DisplayProduct } from '../../../components/ProductCard'; const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY, { apiVersion: '2022-11-15', @@ -12,17 +13,15 @@ const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY, { dbConnect(); -const parseCart = async (items: string): Promise => { - const cart = []; - const parsedItems = JSON.parse(items); +const parseCart = async (items: string): Promise => { + const parsedItems = JSON.parse(items) as DisplayProduct[]; - for (const item of parsedItems) { - const product = await Product.findOne({ _id: item.id }); + for (let i = 0; i < parsedItems.length; i++) { + const product = await Product.findOne({ _id: parsedItems[i]._id }); - if (product!.stock < item.amount) item.amount = product!.stock; - - cart.push(item); + if (product!.stock < parsedItems[i].amount) parsedItems[i].amount = product!.stock; } + return parsedItems; }; @@ -45,15 +44,18 @@ interface PaymentIntent { export default async function paymentHandler( req: Omit & { body: Body; }, - res: NextApiResponse>, + res: NextApiResponse>, ): Promise { switch (req.method) { case 'POST': { const paymentIntent = await stripe.paymentIntents.retrieve(req.body.paymentIntent); + switch (paymentIntent?.status) { case 'succeeded': { + console.log(paymentIntent.customer); const customer = await stripe.customers.retrieve(paymentIntent.customer as string) as Stripe.Customer; const cart = await parseCart(req.body.items); + const paymentMethod = await stripe.paymentMethods.retrieve(paymentIntent.payment_method as string); if (!customer) { diff --git a/pages/checkout.tsx b/pages/kassa.tsx similarity index 77% rename from pages/checkout.tsx rename to pages/kassa.tsx index 04feaf2..3a00e22 100644 --- a/pages/checkout.tsx +++ b/pages/kassa.tsx @@ -30,16 +30,19 @@ const Checkout: NextPage = () => { }).catch(console.error); }, []); - return ( <> - {useMetaData('Checkout', 'Checkout', '/checkout')} + {useMetaData('Kassa', 'Kassa', '/kassa')} - {clientSecret && ( + {clientSecret.length ? ( - )} + ) : + (
+

Je winkelwagentje is leeg.

+

Maak een selectie in de .

+
)}
); diff --git a/pages/webshop.tsx b/pages/webshop.tsx index b988978..dab8d3b 100644 --- a/pages/webshop.tsx +++ b/pages/webshop.tsx @@ -37,7 +37,7 @@ const Webshop: NextPage = ({ data }) => {

Webshop

{data.length ? ( -
+
{data.map((product, i) => { return ( = ({ data }) => { )}
-

Summary

+

Samenvatting

{cart.map((product, i) => { return ( @@ -63,9 +63,9 @@ const Webshop: NextPage = ({ data }) => { })} {cart.length > 0 ? <>
-

Total: {cart.reduce((a, b) => a + b.price * b.amount, 0).toLocaleString('de')} EUR

- -

Checkout

+

Totale: {cart.reduce((a, b) => a + b.price * b.amount, 0).toLocaleString('de')} EUR

+ +

Kassa

diff --git a/src/mail/mail.ts b/src/mail/mail.ts index 1f705e3..c32ca1f 100644 --- a/src/mail/mail.ts +++ b/src/mail/mail.ts @@ -1,5 +1,5 @@ import { ServerClient } from 'postmark'; -import { IProduct } from '../models'; +import { DisplayProduct } from '../../components/ProductCard'; export class Mail { private readonly postmark: ServerClient; @@ -8,7 +8,7 @@ export class Mail { this.postmark = new ServerClient(process.env.NEXT_POSTMARK_API_KEY); } - public async sendConfirmationMail(to: string, orderId: string, name: string, total: number, cart: IProduct[], type: string, card: { last4: string, brand: string } | null): Promise { + public async sendConfirmationMail(to: string, orderId: string, name: string, total: number, cart: DisplayProduct[], type: string, card: { last4: string, brand: string; } | null): Promise { for (let i = 0; i < cart.length; i++) { cart[i].price /= 100; } diff --git a/src/models/Order.ts b/src/models/Order.ts index 828b3dc..9efe0d2 100644 --- a/src/models/Order.ts +++ b/src/models/Order.ts @@ -1,6 +1,6 @@ import { Schema, Model, default as mongoose, HydratedDocument } from 'mongoose'; import { ProductDocument } from './Product'; -import { IProduct } from './Product'; +import { DisplayProduct } from '../../components/ProductCard'; export interface IOrder { _id: string; @@ -9,7 +9,7 @@ export interface IOrder { lastName: string; issuedAt: number; receivedAt: number | null; - products: IProduct[]; + products: DisplayProduct[]; delivered: boolean; emailSent: boolean; }