I have an app that uses Next.js. I attached my braintree.ts file. Also attached my Signup.tsx, where I want my Braintree payment methods to be displayed. And finally I attached my route.ts. My route.ts has the issue: Cannot find module @/utils/braintree/braintree or its corresponding type declarations.ts(2307).
Can you please advise on the best steps to solve this issue?
Signup.tsx
'use client';
import Button from '@/components/ui/Button';
import React, { useEffect, useState } from 'react';
import Link from 'next/link';
import { signUp } from '@/utils/auth-helpers/server';
import { handleRequest } from '@/utils/auth-helpers/client';
import { useRouter } from 'next/navigation';
import Script from 'next/script';
interface SignUpProps {
allowEmail: boolean;
redirectMethod: string;
}
export default function SignUp({ allowEmail, redirectMethod }: SignUpProps) {
const router = redirectMethod === 'client' ? useRouter() : null;
const [isSubmitting, setIsSubmitting] = useState(false);
const [showBraintree, setShowBraintree] = useState(false);
const [braintreeInstance, setBraintreeInstance] = useState<any>(null);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsSubmitting(true);
// Here, instead of calling handleRequest, we'll just show the Braintree UI
setShowBraintree(true);
setIsSubmitting(false);
};
useEffect(() => {
if (showBraintree && (window as any).braintree) {
(window as any).braintree.dropin.create({
authorization: process.env.NEXT_PUBLIC_BRAINTREE_TOKENIZATION_KEY,
container: '#dropin-container'
}, (createErr: any, instance: any) => {
if (createErr) {
console.error(createErr);
return;
}
setBraintreeInstance(instance);
});
}
}, [showBraintree]);
const handlePayment = async () => {
if (!braintreeInstance) {
console.error('Braintree instance not available');
return;
}
try {
const { nonce } = await braintreeInstance.requestPaymentMethod();
console.log('Payment nonce:', nonce);
// Here you would typically send the nonce to your server
// along with the user's email and password to complete the sign-up process
alert('Payment method successfully created! Sign-up would be completed here.');
// After successful sign-up and payment, you might want to redirect the user
if (router) router.push('/dashboard');
} catch (error) {
console.error('Payment error:', error);
alert('Payment failed. Please try again.');
}
};
return (
<div className="my-8">
<form
noValidate={true}
className="mb-4"
onSubmit={(e) => handleSubmit(e)}
>
<div className="grid gap-2">
<div className="grid gap-1">
<label htmlFor="email">Email</label>
<input
id="email"
placeholder="name@example.com"
type="email"
name="email"
autoCapitalize="none"
autoComplete="email"
autoCorrect="off"
className="w-full p-3 rounded-md bg-zinc-800"
/>
<label htmlFor="password">Password</label>
<input
id="password"
placeholder="Password"
type="password"
name="password"
autoComplete="current-password"
className="w-full p-3 rounded-md bg-zinc-800"
/>
</div>
<Button
variant="slim"
type="submit"
className="mt-1"
loading={isSubmitting}
>
Continue to Payment
</Button>
</div>
</form>
{showBraintree && (
<div id="dropin-wrapper">
<div id="dropin-container"></div>
<Button onClick={handlePayment} variant="slim" className="mt-4">
Complete Sign Up and Pay
</Button>
</div>
)}
<p>Already have an account?</p>
<p>
<Link href="/signin/password_signin" className="font-light text-sm">
Sign in with email and password
</Link>
</p>
{allowEmail && (
<p>
<Link href="/signin/email_signin" className="font-light text-sm">
Sign in via magic link
</Link>
</p>
)}
<Script
src="https://js.braintreegateway.com/web/dropin/1.33.0/js/dropin.min.js"
strategy="lazyOnload"
/>
</div>
);
}
route.ts
import Stripe from 'stripe';
import { stripe } from '@/utils/stripe/config';
import {
upsertProductRecord,
upsertPriceRecord,
manageSubscriptionStatusChange,
deleteProductRecord,
deletePriceRecord
} from '@/utils/supabase/admin';
import { gateway } from '@/utils/braintree/braintree';
const relevantEvents = new Set([
'product.created',
'product.updated',
'product.deleted',
'price.created',
'price.updated',
'price.deleted',
'checkout.session.completed',
'customer.subscription.created',
'customer.subscription.updated',
'customer.subscription.deleted'
]);
export async function POST(req: Request) {
if (req.headers.get('Content-Type') === 'application/json') {
const body = await req.json();
const { paymentMethodNonce } = body;
if (!paymentMethodNonce) {
return new Response('Payment method nonce is required', { status: 400 });
}
try {
const result = await gateway.transaction.sale({
amount: '10.00',
paymentMethodNonce: paymentMethodNonce,
options: {
submitForSettlement: true
}
});
if (result.success) {
return new Response(JSON.stringify({ success: true, transaction: result.transaction }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} else {
return new Response(JSON.stringify({ success: false, error: result.message }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
} catch (error) {
console.error('Braintree transaction error:', error);
return new Response('An error occurred while processing the payment', { status: 500 });
}
} else {
const body = await req.text();
const sig = req.headers.get('stripe-signature') as string;
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
let event: Stripe.Event;
try {
if (!sig || !webhookSecret)
return new Response('Webhook secret not found.', { status: 400 });
event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
console.log(`🔔 Webhook received: ${event.type}`);
} catch (err: any) {
console.log(`❌ Error message: ${err.message}`);
return new Response(`Webhook Error: ${err.message}`, { status: 400 });
}
if (relevantEvents.has(event.type)) {
try {
switch (event.type) {
case 'product.created':
case 'product.updated':
await upsertProductRecord(event.data.object as Stripe.Product);
break;
case 'price.created':
case 'price.updated':
await upsertPriceRecord(event.data.object as Stripe.Price);
break;
case 'price.deleted':
await deletePriceRecord(event.data.object as Stripe.Price);
break;
case 'product.deleted':
await deleteProductRecord(event.data.object as Stripe.Product);
break;
case 'customer.subscription.created':
case 'customer.subscription.updated':
case 'customer.subscription.deleted':
const subscription = event.data.object as Stripe.Subscription;
await manageSubscriptionStatusChange(
subscription.id,
subscription.customer as string,
event.type === 'customer.subscription.created'
);
break;
case 'checkout.session.completed':
const checkoutSession = event.data.object as Stripe.Checkout.Session;
if (checkoutSession.mode === 'subscription') {
const subscriptionId = checkoutSession.subscription;
await manageSubscriptionStatusChange(
subscriptionId as string,
checkoutSession.customer as string,
true
);
}
break;
default:
throw new Error('Unhandled relevant event!');
}
} catch (error) {
console.log(error);
return new Response(
'Webhook handler failed. View your Next.js function logs.',
{
status: 400
}
);
}
} else {
return new Response(`Unsupported event type: ${event.type}`, {
status: 400
});
}
return new Response(JSON.stringify({ received: true }));
}
}
braintree.ts
import braintree from 'braintree';
export const gateway = new braintree.BraintreeGateway({
environment: braintree.Environment.Sandbox,
merchantId: process.env.BRAINTREE_MERCHANT_ID!,
publicKey: process.env.BRAINTREE_PUBLIC_KEY!,
privateKey: process.env.BRAINTREE_PRIVATE_KEY!
});
I have an app that uses Next.js. I attached my braintree.ts file. Also attached my Signup.tsx, where I want my Braintree payment methods to be displayed. And finally I attached my route.ts. My route.ts has the issue: Cannot find module @/utils/braintree/braintree or its corresponding type declarations.ts(2307).
Can you please advise on the best steps to solve this issue?
Signup.tsx
route.ts
braintree.ts