diff --git a/app/(api)/_graphql/mutations/saveInfo.ts b/app/(api)/_graphql/mutations/saveInfo.ts new file mode 100644 index 0000000..953471c --- /dev/null +++ b/app/(api)/_graphql/mutations/saveInfo.ts @@ -0,0 +1,37 @@ +/* +A mutation that I hope works that's meant to save the billing info +so that we don't have to go on Stripe to see it +*/ + +import { gql } from '@apollo/client'; + +export const SAVE_BILLING_INFO = gql` + mutation SaveBillingInfo( + $secret: String! + $id: String! + $name: String! + $address: String! + $address2: String + $city: String! + $state: String! + $zip: String! + $phone: String! + $paymentMethod: String! + ) { + saveBillingInfo( + secret: $secret + id: $id + name: $name + address: $address + address2: $address2 + city: $city + state: $state + zip: $zip + phone: $phone + paymentMethod: $paymentMethod + ) { + success + message + } + } +`; diff --git a/app/(api)/_graphql/queries/getOrders.ts b/app/(api)/_graphql/queries/getOrders.ts new file mode 100644 index 0000000..9d61aa7 --- /dev/null +++ b/app/(api)/_graphql/queries/getOrders.ts @@ -0,0 +1,41 @@ +// Queries for grabbing orders that I hope works T_T + +import { gql } from '@apollo/client'; + +export const GET_ALL_ORDERS = gql` + query GetAllOrders { + orders { + id + secret + name + address + address2 + city + state + zip + phone + paymentMethod + createdAt + updatedAt + } + } +`; + +export const GET_ORDER_BY_ID = gql` + query GetOrderById($id: [ID]) { + order(id: $id) { + id + secret + name + address + address2 + city + state + zip + phone + paymentMethod + createdAt + updatedAt + } + } +`; diff --git a/app/(api)/api/checkout_sessions/get.ts b/app/(api)/api/checkout_sessions/get.ts new file mode 100644 index 0000000..b677283 --- /dev/null +++ b/app/(api)/api/checkout_sessions/get.ts @@ -0,0 +1,29 @@ +import { NextRequest, NextResponse } from 'next/server'; +import stripe from 'stripe'; + +export async function GET(request: NextRequest) { + try { + const auth_stripe = new stripe(process.env.STRIPE_SECRET_KEY!); + const searchParams = request.nextUrl.searchParams; + const session_id = searchParams.get('session_id'); + + const session = await auth_stripe.checkout.sessions.retrieve( + session_id as string + ); + + return NextResponse.json( + { + customer_email: session.customer_details!.email, + }, + { + status: 200, + } + ); + } catch (e) { + const error = e as stripe.StripeRawError; + return NextResponse.json( + { ok: false, body: null, error: error.message }, + { status: 400 } + ); + } +} diff --git a/app/(api)/api/checkout_sessions/post.ts b/app/(api)/api/checkout_sessions/post.ts new file mode 100644 index 0000000..09ea17e --- /dev/null +++ b/app/(api)/api/checkout_sessions/post.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from 'next/server'; +import stripe from 'stripe'; + +export async function POST(request: NextRequest) { + try { + const { total } = await request.json(); + const auth_stripe = new stripe(process.env.STRIPE_SECRET_KEY!); + + // Creates a checkout session from body params. + const session = await auth_stripe.paymentIntents.create({ + amount: total * 100, + currency: 'usd', + payment_method_types: ['card'], + }); + + return NextResponse.json( + { + clientSecret: session.client_secret, + }, + { + status: 200, + } + ); + } catch (e) { + const error = e as stripe.StripeRawError; + return NextResponse.json( + { ok: false, body: null, error: error.message }, + { status: 400 } + ); + } +} diff --git a/app/(api)/api/checkout_sessions/route.ts b/app/(api)/api/checkout_sessions/route.ts new file mode 100644 index 0000000..53f8630 --- /dev/null +++ b/app/(api)/api/checkout_sessions/route.ts @@ -0,0 +1,2 @@ +export { POST } from './post'; +export { GET } from './get'; diff --git a/app/(pages)/checkout/_components/CheckoutCart/CheckoutCart.tsx b/app/(pages)/checkout/_components/CheckoutCart/CheckoutCart.tsx index 8ee0640..55e9cbc 100644 --- a/app/(pages)/checkout/_components/CheckoutCart/CheckoutCart.tsx +++ b/app/(pages)/checkout/_components/CheckoutCart/CheckoutCart.tsx @@ -1,3 +1,6 @@ +// This file could honestly be removed but for now I'll keep it here +// in case it still has some use I might not be seeing + 'use client'; import styles from './CheckoutCart.module.scss'; @@ -8,10 +11,11 @@ export default function CheckoutCart() { if (loading) { return 'loading...'; } + return (
- {JSON.stringify(cart)} -

total cost: ${compute_total()}

+ {/* {JSON.stringify(cart)} */} + {/*

total cost: ${compute_total()}

*/}
); } diff --git a/app/(pages)/checkout/_components/CheckoutForm/DeliveryFormSection/DeliveryFormSection.tsx b/app/(pages)/checkout/_components/CheckoutForm/DeliveryFormSection/DeliveryFormSection.tsx index 974f557..f26047a 100644 --- a/app/(pages)/checkout/_components/CheckoutForm/DeliveryFormSection/DeliveryFormSection.tsx +++ b/app/(pages)/checkout/_components/CheckoutForm/DeliveryFormSection/DeliveryFormSection.tsx @@ -7,10 +7,10 @@ import styles from './DeliveryFormSection.module.scss'; export default function DeliveryFormSection() { return ( -
+

Delivery

- +
); } diff --git a/app/(pages)/checkout/_components/CheckoutForm/DeliveryFormSection/ShippingInfo/ShippingInfo.tsx b/app/(pages)/checkout/_components/CheckoutForm/DeliveryFormSection/ShippingInfo/ShippingInfo.tsx index 8069494..d9759e9 100644 --- a/app/(pages)/checkout/_components/CheckoutForm/DeliveryFormSection/ShippingInfo/ShippingInfo.tsx +++ b/app/(pages)/checkout/_components/CheckoutForm/DeliveryFormSection/ShippingInfo/ShippingInfo.tsx @@ -1,5 +1,6 @@ import InputField from '../InputField/InputField'; import Address from '../Address/Address'; +import PaymentForm from '../../../PaymentForm/PaymentForm'; import { useState } from 'react'; import { GoQuestion } from 'react-icons/go'; @@ -78,6 +79,15 @@ export default function ShippingInfo() { icon={GoQuestion} handleFieldChange={updateShippingField} /> + ); } diff --git a/app/(pages)/checkout/_components/PaymentForm/CardSection.tsx b/app/(pages)/checkout/_components/PaymentForm/CardSection.tsx new file mode 100644 index 0000000..214dc95 --- /dev/null +++ b/app/(pages)/checkout/_components/PaymentForm/CardSection.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { CardElement } from '@stripe/react-stripe-js'; +// import './Styles.css'; +const CARD_ELEMENT_OPTIONS = { + style: { + base: { + color: '#32325d', + fontFamily: '"Helvetica Neue", Helvetica, sans-serif', + fontSmoothing: 'antialiased', + fontSize: '16px', + '::placeholder': { + color: '#aab7c4', + }, + }, + invalid: { + color: '#fa755a', + iconColor: '#fa755a', + }, + }, +}; +function CardSection() { + return ( +
+ +
+ ); +} +export default CardSection; diff --git a/app/(pages)/checkout/_components/PaymentForm/CheckoutForm.tsx b/app/(pages)/checkout/_components/PaymentForm/CheckoutForm.tsx new file mode 100644 index 0000000..324613c --- /dev/null +++ b/app/(pages)/checkout/_components/PaymentForm/CheckoutForm.tsx @@ -0,0 +1,111 @@ +import React from 'react'; +import { useStripe, useElements, CardElement } from '@stripe/react-stripe-js'; +import CardSection from './CardSection'; +import Stripe from 'stripe'; +import { useMutation } from '@apollo/client'; +import { SAVE_BILLING_INFO } from '@graphql/mutations/saveInfo'; + +export default function CheckoutForm({ + secret, + name, + address, + address2, + city, + state, + zip, + phone, +}: { + secret: string; + name: string; + address: string; + address2: string; + city: string; + state: string; + zip: string; + phone: string; +}) { + const stripe = useStripe(); + const elements = useElements(); + const key = process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY; + const payment = new Stripe(`${key}`); + const [saveBillingInfo] = useMutation(SAVE_BILLING_INFO); + + const handleSubmit = async (event: { preventDefault: () => void }) => { + // We don't want to let default form submission happen here, + // which would refresh the page. + event.preventDefault(); + + if (!stripe || !elements) { + // Stripe.js hasn't yet loaded. + // Make sure to disable form submission until Stripe.js has loaded. + return; + } + + const result = await stripe.confirmCardPayment(`${secret}`, { + payment_method: { + card: elements.getElement(CardElement)!, + billing_details: { + name: name, + address: { + city: city, + country: 'US', + line1: address, + line2: address2, + postal_code: zip, + state: state, + }, + phone: phone, + }, + }, + }); + + if (result.error) { + // Show error to your customer (for example, insufficient funds) + console.log(result.error.message); + } else { + // The payment has been processed! + if (result.paymentIntent.status === 'succeeded') { + // Show a success message to your customer + // There's a risk of the customer closing the window before callback + // execution. Set up a webhook or plugin to listen for the + // payment_intent.succeeded event that handles any business critical + // post-payment actions. + const intent = result.paymentIntent.payment_method; + + const paymentMethod = await payment.paymentMethods.retrieve( + `${intent}`, + { + apiKey: `${key}`, + } + ); + console.log('method: ', paymentMethod); + + try { + const { data } = await saveBillingInfo({ + variables: { + secret, + paymentMethod: paymentMethod.id, + name: paymentMethod.billing_details.name, + address: paymentMethod.billing_details.address?.line1, + address2: paymentMethod.billing_details.address?.line2, + city: paymentMethod.billing_details.address?.city, + state: paymentMethod.billing_details.address?.state, + zip: paymentMethod.billing_details.address?.postal_code, + phone: paymentMethod.billing_details.phone, + }, + }); + console.log(data); + } catch (error) { + console.error('Someone sux at graphql routes (error msg): ', error); + } + } + } + }; + + return ( +
+ + + + ); +} diff --git a/app/(pages)/checkout/_components/PaymentForm/PaymentForm.tsx b/app/(pages)/checkout/_components/PaymentForm/PaymentForm.tsx new file mode 100644 index 0000000..b6bff12 --- /dev/null +++ b/app/(pages)/checkout/_components/PaymentForm/PaymentForm.tsx @@ -0,0 +1,74 @@ +'use client'; + +import React, { useState, useEffect, useCallback } from 'react'; +import { loadStripe } from '@stripe/stripe-js'; +import { Elements } from '@stripe/react-stripe-js'; +import CheckoutForm from './CheckoutForm'; +import { useShoppingCart } from '@hooks/useShoppingCart'; + +const stripePromise = loadStripe( + process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY! +); + +export default function PaymentForm({ + name, + address, + address2, + city, + state, + zip, + phone, +}: { + name: string; + address: string; + address2: string; + city: string; + state: string; + zip: string; + phone: string; +}) { + const { loading, cart, compute_total } = useShoppingCart(); + const [clientSecret, setClientSecret] = useState(''); + + const fetchClientSecret = useCallback(async (total: number) => { + try { + const response = await fetch('/api/checkout_sessions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ total }), + }); + const data = await response.json(); + setClientSecret(data.clientSecret); + } catch (error) { + console.error('Error fetching client secret:', error); + } + }, []); + + useEffect(() => { + if (!loading) { + const total = compute_total(); + fetchClientSecret(total); + } + }, [loading, compute_total, fetchClientSecret]); + + return ( +
+ {clientSecret && ( + + + + )} +
+ ); +} diff --git a/app/(pages)/checkout/page.tsx b/app/(pages)/checkout/page.tsx index c11df52..fc9d88b 100644 --- a/app/(pages)/checkout/page.tsx +++ b/app/(pages)/checkout/page.tsx @@ -1,12 +1,12 @@ import styles from './page.module.scss'; import DeliveryFormSection from './_components/CheckoutForm/DeliveryFormSection/DeliveryFormSection'; -import CheckoutCart from './_components/CheckoutCart/CheckoutCart'; export default function Checkout() { return (
- - +
+ +
); } diff --git a/package-lock.json b/package-lock.json index a25bab8..edff300 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,13 +10,16 @@ "dependencies": { "@apollo/client": "^3.9.4", "@apollo/experimental-nextjs-app-support": "^0.8.0", + "@stripe/react-stripe-js": "^2.7.1", + "@stripe/stripe-js": "^3.4.0", "mongodb": "^6.3.0", "next": "14.0.4", "react": "^18", "react-dom": "^18", "react-icons": "^4.12.0", "readline": "^1.3.0", - "sass": "^1.69.5" + "sass": "^1.69.5", + "stripe": "^15.6.0" }, "devDependencies": { "@next/eslint-plugin-next": "^14.0.4", @@ -680,6 +683,27 @@ "integrity": "sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==", "dev": true }, + "node_modules/@stripe/react-stripe-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.7.1.tgz", + "integrity": "sha512-/i13alp27HaSBbMM6kW0jhy8KqdtOL1T/EcRjFjfhvt+CBtMEg8TD7y28W3oZG0+OBDdCyGGnXgNgrKPYQH40g==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "@stripe/stripe-js": "^1.44.1 || ^2.0.0 || ^3.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@stripe/stripe-js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-3.4.0.tgz", + "integrity": "sha512-a2kUP7OrsV0SSIk3UxWa+cnrW+PPIyuCbWIBH8vxfHIqmyeQN/d0lsplZJ2h7MlLsU/sB3EyhNBkhLLT+zHwKw==", + "engines": { + "node": ">=12.16" + } + }, "node_modules/@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", @@ -704,7 +728,6 @@ "version": "20.10.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -1504,14 +1527,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1824,17 +1851,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-lazy-prop": { @@ -2034,6 +2063,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-iterator-helpers": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", @@ -3114,7 +3162,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3153,16 +3200,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3298,7 +3348,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -3359,12 +3408,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3374,7 +3422,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3386,7 +3433,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3413,7 +3459,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -4519,7 +4564,6 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5113,6 +5157,20 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5698,15 +5756,16 @@ "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==" }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5748,14 +5807,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6031,6 +6093,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stripe": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-15.6.0.tgz", + "integrity": "sha512-ARG46eQHMmHspnDpj3QTAH8GyEqtE0nesbzpTtQDT/C9nHvOFYri3mIzHEzArzDcKX7HSleTu2VpYoDZIIH7nA==", + "dependencies": { + "@types/node": ">=8.1.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -6491,8 +6565,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/untildify": { "version": "4.0.0", diff --git a/package.json b/package.json index fbb2e3f..e7088ff 100644 --- a/package.json +++ b/package.json @@ -15,13 +15,16 @@ "dependencies": { "@apollo/client": "^3.9.4", "@apollo/experimental-nextjs-app-support": "^0.8.0", + "@stripe/react-stripe-js": "^2.7.1", + "@stripe/stripe-js": "^3.4.0", "mongodb": "^6.3.0", "next": "14.0.4", "react": "^18", "react-dom": "^18", "react-icons": "^4.12.0", "readline": "^1.3.0", - "sass": "^1.69.5" + "sass": "^1.69.5", + "stripe": "^15.6.0" }, "devDependencies": { "@next/eslint-plugin-next": "^14.0.4", diff --git a/tsconfig.json b/tsconfig.json index 499d252..e81cc2b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,6 +31,6 @@ "@datatypes/*": ["./app/(api)/_types/*"] } }, - "include": ["nextEnv.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["nextEnv.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/(pages)/checkout/_components/PaymentForm/CardSection.tsx"], "exclude": ["node_modules"] } \ No newline at end of file