210 lines
6.5 KiB
JavaScript
210 lines
6.5 KiB
JavaScript
import 'dotenv/config';
|
|
import express from 'express';
|
|
import { Client, Databases, Query } from 'node-appwrite';
|
|
import Stripe from 'stripe';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
const requiredEnvVars = [
|
|
'APPWRITE_ENDPOINT',
|
|
'APPWRITE_PROJECT_ID',
|
|
'APPWRITE_API_KEY',
|
|
'APPWRITE_DATABASE_ID',
|
|
'STRIPE_SECRET_KEY',
|
|
'STRIPE_WEBHOOK_SECRET'
|
|
];
|
|
|
|
for (const envVar of requiredEnvVars) {
|
|
if (!process.env[envVar]) {
|
|
console.error(`Error: Missing required environment variable: ${envVar}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
const app = express();
|
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
|
|
|
|
const client = new Client()
|
|
.setEndpoint(process.env.APPWRITE_ENDPOINT)
|
|
.setProject(process.env.APPWRITE_PROJECT_ID)
|
|
.setKey(process.env.APPWRITE_API_KEY);
|
|
|
|
const databases = new Databases(client);
|
|
|
|
app.use(express.static(join(__dirname, '..', 'public')));
|
|
app.use('/api', express.json());
|
|
|
|
app.post('/stripe/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
const sig = req.headers['stripe-signature'];
|
|
|
|
try {
|
|
const event = stripe.webhooks.constructEvent(
|
|
req.body,
|
|
sig,
|
|
process.env.STRIPE_WEBHOOK_SECRET
|
|
);
|
|
|
|
if (event.type === 'checkout.session.completed') {
|
|
const session = event.data.object;
|
|
const submissionId = session.metadata.submissionId;
|
|
|
|
if (submissionId) {
|
|
await databases.updateDocument(
|
|
process.env.APPWRITE_DATABASE_ID,
|
|
'submissions',
|
|
submissionId,
|
|
{ status: 'paid' }
|
|
);
|
|
|
|
await databases.createDocument(
|
|
process.env.APPWRITE_DATABASE_ID,
|
|
'orders',
|
|
'unique()',
|
|
{
|
|
submissionId: submissionId,
|
|
orderDataJson: JSON.stringify(session)
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
res.json({ received: true });
|
|
} catch (err) {
|
|
console.error('Webhook error:', err.message);
|
|
res.status(400).send(`Webhook Error: ${err.message}`);
|
|
}
|
|
});
|
|
|
|
app.get('/api/questions', async (req, res) => {
|
|
try {
|
|
const { productSlug } = req.query;
|
|
|
|
const productsResponse = await databases.listDocuments(
|
|
process.env.APPWRITE_DATABASE_ID,
|
|
'products',
|
|
[Query.equal('slug', productSlug), Query.equal('isActive', true)]
|
|
);
|
|
|
|
if (productsResponse.documents.length === 0) {
|
|
return res.status(404).json({ error: 'Product not found' });
|
|
}
|
|
|
|
const product = productsResponse.documents[0];
|
|
|
|
const questionsResponse = await databases.listDocuments(
|
|
process.env.APPWRITE_DATABASE_ID,
|
|
'questions',
|
|
[
|
|
Query.equal('productId', product.$id),
|
|
Query.equal('isActive', true),
|
|
Query.orderAsc('step'),
|
|
Query.orderAsc('order')
|
|
]
|
|
);
|
|
|
|
res.json(questionsResponse.documents);
|
|
} catch (error) {
|
|
console.error('Error fetching questions:', error);
|
|
res.status(500).json({ error: 'Failed to fetch questions' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/submissions', async (req, res) => {
|
|
try {
|
|
const { productSlug, answers } = req.body;
|
|
|
|
const productsResponse = await databases.listDocuments(
|
|
process.env.APPWRITE_DATABASE_ID,
|
|
'products',
|
|
[Query.equal('slug', productSlug)]
|
|
);
|
|
|
|
if (productsResponse.documents.length === 0) {
|
|
return res.status(404).json({ error: 'Product not found' });
|
|
}
|
|
|
|
const product = productsResponse.documents[0];
|
|
|
|
const submission = await databases.createDocument(
|
|
process.env.APPWRITE_DATABASE_ID,
|
|
'submissions',
|
|
'unique()',
|
|
{
|
|
productId: product.$id,
|
|
status: 'draft',
|
|
customerEmail: answers.email || null,
|
|
customerName: answers.name || null,
|
|
finalSummaryJson: JSON.stringify(answers),
|
|
priceCents: product.priceCents,
|
|
currency: product.currency
|
|
}
|
|
);
|
|
|
|
await databases.createDocument(
|
|
process.env.APPWRITE_DATABASE_ID,
|
|
'answers',
|
|
'unique()',
|
|
{
|
|
submissionId: submission.$id,
|
|
answersJson: JSON.stringify(answers)
|
|
}
|
|
);
|
|
|
|
res.json({ submissionId: submission.$id });
|
|
} catch (error) {
|
|
console.error('Error creating submission:', error);
|
|
res.status(500).json({ error: 'Failed to create submission' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/checkout', async (req, res) => {
|
|
try {
|
|
const { submissionId } = req.body;
|
|
|
|
if (!submissionId) {
|
|
return res.status(400).json({ error: 'Missing submissionId' });
|
|
}
|
|
|
|
const submission = await databases.getDocument(
|
|
process.env.APPWRITE_DATABASE_ID,
|
|
'submissions',
|
|
submissionId
|
|
);
|
|
|
|
const session = await stripe.checkout.sessions.create({
|
|
payment_method_types: ['card'],
|
|
line_items: [
|
|
{
|
|
price_data: {
|
|
currency: submission.currency,
|
|
product_data: {
|
|
name: 'Email Sortierer Service',
|
|
},
|
|
unit_amount: submission.priceCents,
|
|
},
|
|
quantity: 1,
|
|
},
|
|
],
|
|
mode: 'payment',
|
|
success_url: `${process.env.BASE_URL || 'http://localhost:3000'}/success.html`,
|
|
cancel_url: `${process.env.BASE_URL || 'http://localhost:3000'}/cancel.html`,
|
|
metadata: {
|
|
submissionId: submissionId
|
|
}
|
|
});
|
|
|
|
res.json({ url: session.url });
|
|
} catch (error) {
|
|
console.error('Error creating checkout session:', error);
|
|
res.status(500).json({ error: 'Failed to create checkout session' });
|
|
}
|
|
});
|
|
|
|
const PORT = process.env.PORT || 3000;
|
|
app.listen(PORT, () => {
|
|
console.log(`Server running on port ${PORT}`);
|
|
});
|