Complete Email Sortierer implementation with Appwrite and Stripe integration
This commit is contained in:
209
server/index.mjs
Normal file
209
server/index.mjs
Normal file
@@ -0,0 +1,209 @@
|
||||
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}`);
|
||||
});
|
||||
Reference in New Issue
Block a user