mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:21:09 +02:00
style: auto-format codebase with Prettier
Applied formatting to 1487+ files using pnpm format:write - TypeScript/JavaScript files - Svelte components - Astro pages - JSON configs - Markdown docs 13 files still need manual review (Astro JSX comments)
This commit is contained in:
parent
0241f5554c
commit
d36b321d9d
3952 changed files with 661498 additions and 739751 deletions
|
|
@ -67,12 +67,13 @@ npm install stripe @stripe/stripe-js @paypal/paypal-js
|
|||
### 3. Erstellung der Frontend-Komponenten
|
||||
|
||||
#### PaymentForm.astro
|
||||
|
||||
```astro
|
||||
---
|
||||
import { useTranslations } from '../utils/i18n';
|
||||
|
||||
interface Props {
|
||||
lang: string;
|
||||
lang: string;
|
||||
}
|
||||
|
||||
const { lang } = Astro.props;
|
||||
|
|
@ -80,69 +81,74 @@ const t = useTranslations(lang);
|
|||
---
|
||||
|
||||
<div class="payment-container">
|
||||
<!-- Zahlungstyp-Auswahl -->
|
||||
<div class="payment-type-selector">
|
||||
<button id="one-time" class="payment-type-btn active">{t('support.onetime')}</button>
|
||||
<button id="recurring" class="payment-type-btn">{t('support.recurring')}</button>
|
||||
</div>
|
||||
|
||||
<!-- Kaffee-Größen -->
|
||||
<div class="coffee-options">
|
||||
<!-- ... -->
|
||||
</div>
|
||||
|
||||
<!-- Zahlungsmethoden -->
|
||||
<div class="payment-buttons">
|
||||
<button id="stripe-button" class="payment-method-btn">
|
||||
<!-- ... -->
|
||||
<span>{t('support.payWithStripe')}</span>
|
||||
</button>
|
||||
<button id="paypal-button" class="payment-method-btn">
|
||||
<!-- ... -->
|
||||
<span>{t('support.payWithPayPal')}</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Zahlungstyp-Auswahl -->
|
||||
<div class="payment-type-selector">
|
||||
<button id="one-time" class="payment-type-btn active">{t('support.onetime')}</button>
|
||||
<button id="recurring" class="payment-type-btn">{t('support.recurring')}</button>
|
||||
</div>
|
||||
|
||||
<!-- Kaffee-Größen -->
|
||||
<div class="coffee-options">
|
||||
<!-- ... -->
|
||||
</div>
|
||||
|
||||
<!-- Zahlungsmethoden -->
|
||||
<div class="payment-buttons">
|
||||
<button id="stripe-button" class="payment-method-btn">
|
||||
<!-- ... -->
|
||||
<span>{t('support.payWithStripe')}</span>
|
||||
</button>
|
||||
<button id="paypal-button" class="payment-method-btn">
|
||||
<!-- ... -->
|
||||
<span>{t('support.payWithPayPal')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
import { loadStripe } from '@stripe/stripe-js';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Stripe-Instanz initialisieren
|
||||
const stripePromise = loadStripe(import.meta.env.PUBLIC_STRIPE_PUBLISHABLE_KEY || 'pk_test_placeholder');
|
||||
|
||||
// Event-Listener für Stripe-Button
|
||||
stripeBtn?.addEventListener('click', async () => {
|
||||
try {
|
||||
// Checkout-Session erstellen
|
||||
const response = await fetch('/.netlify/functions/create-payment-intent', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
amount, isRecurring, priceId, coffeeSize
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Zur Checkout-Seite weiterleiten
|
||||
if (data.url) {
|
||||
window.location.href = data.url;
|
||||
return;
|
||||
}
|
||||
|
||||
// Alternativ: redirectToCheckout verwenden
|
||||
if (data.sessionId || data.id) {
|
||||
const stripe = await stripePromise;
|
||||
await stripe.redirectToCheckout({
|
||||
sessionId: data.sessionId || data.id,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Payment error:', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
import { loadStripe } from '@stripe/stripe-js';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Stripe-Instanz initialisieren
|
||||
const stripePromise = loadStripe(
|
||||
import.meta.env.PUBLIC_STRIPE_PUBLISHABLE_KEY || 'pk_test_placeholder'
|
||||
);
|
||||
|
||||
// Event-Listener für Stripe-Button
|
||||
stripeBtn?.addEventListener('click', async () => {
|
||||
try {
|
||||
// Checkout-Session erstellen
|
||||
const response = await fetch('/.netlify/functions/create-payment-intent', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
amount,
|
||||
isRecurring,
|
||||
priceId,
|
||||
coffeeSize,
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Zur Checkout-Seite weiterleiten
|
||||
if (data.url) {
|
||||
window.location.href = data.url;
|
||||
return;
|
||||
}
|
||||
|
||||
// Alternativ: redirectToCheckout verwenden
|
||||
if (data.sessionId || data.id) {
|
||||
const stripe = await stripePromise;
|
||||
await stripe.redirectToCheckout({
|
||||
sessionId: data.sessionId || data.id,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Payment error:', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
|
|
@ -154,50 +160,52 @@ const t = useTranslations(lang);
|
|||
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
|
||||
|
||||
exports.handler = async (event, context) => {
|
||||
const headers = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "Content-Type",
|
||||
"Access-Control-Allow-Methods": "POST, OPTIONS"
|
||||
};
|
||||
|
||||
try {
|
||||
const { amount, isRecurring, coffeeSize } = JSON.parse(event.body || '{}');
|
||||
const amountInCents = Math.round(amount * 100);
|
||||
|
||||
// Stripe Checkout Session erstellen
|
||||
const session = await stripe.checkout.sessions.create({
|
||||
payment_method_types: ['card'],
|
||||
line_items: [{
|
||||
price_data: {
|
||||
currency: 'eur',
|
||||
product_data: {
|
||||
name: `BaunTown Kaffee - ${coffeeSize || 'Mittlerer Kaffee'}`,
|
||||
description: isRecurring ? 'Monatliche Unterstützung' : 'Einmalige Unterstützung',
|
||||
},
|
||||
unit_amount: amountInCents,
|
||||
recurring: isRecurring ? { interval: 'month' } : undefined,
|
||||
},
|
||||
quantity: 1,
|
||||
}],
|
||||
mode: isRecurring ? 'subscription' : 'payment',
|
||||
success_url: `${process.env.URL || 'https://bauntown.com'}/support-success`,
|
||||
cancel_url: `${process.env.URL || 'https://bauntown.com'}/support-cancel`,
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
url: session.url
|
||||
})
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
statusCode: 500,
|
||||
headers,
|
||||
body: JSON.stringify({ error: error.message })
|
||||
};
|
||||
}
|
||||
const headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
||||
};
|
||||
|
||||
try {
|
||||
const { amount, isRecurring, coffeeSize } = JSON.parse(event.body || '{}');
|
||||
const amountInCents = Math.round(amount * 100);
|
||||
|
||||
// Stripe Checkout Session erstellen
|
||||
const session = await stripe.checkout.sessions.create({
|
||||
payment_method_types: ['card'],
|
||||
line_items: [
|
||||
{
|
||||
price_data: {
|
||||
currency: 'eur',
|
||||
product_data: {
|
||||
name: `BaunTown Kaffee - ${coffeeSize || 'Mittlerer Kaffee'}`,
|
||||
description: isRecurring ? 'Monatliche Unterstützung' : 'Einmalige Unterstützung',
|
||||
},
|
||||
unit_amount: amountInCents,
|
||||
recurring: isRecurring ? { interval: 'month' } : undefined,
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
mode: isRecurring ? 'subscription' : 'payment',
|
||||
success_url: `${process.env.URL || 'https://bauntown.com'}/support-success`,
|
||||
cancel_url: `${process.env.URL || 'https://bauntown.com'}/support-cancel`,
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
url: session.url,
|
||||
}),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
statusCode: 500,
|
||||
headers,
|
||||
body: JSON.stringify({ error: error.message }),
|
||||
};
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
|
@ -265,7 +273,7 @@ export function useTranslations(lang: keyof typeof ui) {
|
|||
- Navigieren Sie zu Ihrem Netlify-Dashboard
|
||||
- Gehen Sie zu "Site settings" > "Environment variables"
|
||||
- Fügen Sie die folgenden Umgebungsvariablen hinzu:
|
||||
- `PUBLIC_STRIPE_PUBLISHABLE_KEY` (muss mit PUBLIC_ beginnen für Frontend-Verwendung)
|
||||
- `PUBLIC_STRIPE_PUBLISHABLE_KEY` (muss mit PUBLIC\_ beginnen für Frontend-Verwendung)
|
||||
- `STRIPE_SECRET_KEY`
|
||||
- `STRIPE_WEBHOOK_SECRET` (optional für Webhook-Verarbeitung)
|
||||
|
||||
|
|
@ -315,9 +323,9 @@ Die Netlify-Function enthält CORS-Header für die Anfrageverarbeitung:
|
|||
|
||||
```javascript
|
||||
const headers = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "Content-Type",
|
||||
"Access-Control-Allow-Methods": "POST, OPTIONS"
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
||||
};
|
||||
```
|
||||
|
||||
|
|
@ -327,15 +335,15 @@ Die Implementation enthält einen Fallback-Mechanismus für Entwicklungs- und Te
|
|||
|
||||
```javascript
|
||||
if (!process.env.STRIPE_SECRET_KEY) {
|
||||
console.log("WARNUNG: STRIPE_SECRET_KEY fehlt - liefere Test-Antwort");
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
url: `${process.env.URL || 'https://bauntown.com'}/support-success?test=true`,
|
||||
message: "Test mode - no Stripe key available"
|
||||
})
|
||||
};
|
||||
console.log('WARNUNG: STRIPE_SECRET_KEY fehlt - liefere Test-Antwort');
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
url: `${process.env.URL || 'https://bauntown.com'}/support-success?test=true`,
|
||||
message: 'Test mode - no Stripe key available',
|
||||
}),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -346,9 +354,10 @@ if (!process.env.STRIPE_SECRET_KEY) {
|
|||
Die Stripe-Integration für das "Buy Me a Coffee"-Feature wurde erfolgreich implementiert und ermöglicht sowohl einmalige als auch wiederkehrende Spenden. Durch die Verwendung von Netlify Functions wird die Sicherheit erhöht, da sensible Daten wie der Stripe Secret Key nicht im Frontend verfügbar sind.
|
||||
|
||||
Die wichtigsten Punkte für eine fehlerfreie Implementierung:
|
||||
|
||||
1. Verwenden Sie `PUBLIC_`-Präfix für Frontend-Umgebungsvariablen in Astro
|
||||
2. Verwenden Sie den Stripe Checkout-Flow für eine einfache und sichere Zahlungsabwicklung
|
||||
3. Implementieren Sie robuste Fehlerbehandlung für Übersetzungen und API-Aufrufe
|
||||
4. Testen Sie sowohl im Entwicklungs- als auch im Produktionsmodus
|
||||
|
||||
Bei Fragen oder Problemen konsultieren Sie die [Stripe-Dokumentation](https://stripe.com/docs/checkout) oder öffnen Sie ein Issue im BaunTown-Repository.
|
||||
Bei Fragen oder Problemen konsultieren Sie die [Stripe-Dokumentation](https://stripe.com/docs/checkout) oder öffnen Sie ein Issue im BaunTown-Repository.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue