Stripe: Katalog Terminplaner-Einrichtung (149 €) und Checkout je Funktion aus dem Register
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c584d220c8
commit
98fa03aad7
6 files changed
+238
-13
No files matched your search
@@ -106,6 +106,16 @@ const newsSetup = {
|
||||
priceNickname: 'News-Einrichtung 30 €',
|
||||
}
|
||||
|
||||
// Terminplaner: einmalige Einrichtung (Einmalpreis, kein Abo); der Terminplaner selbst laeuft, solange das Wartungs-Abo laeuft
|
||||
const termineSetup = {
|
||||
key: 'termineSetup',
|
||||
name: 'Terminplaner – Einrichtung',
|
||||
amountCents: 14900,
|
||||
lookupKey: 'webklar_termine_einrichtung_14900',
|
||||
oneTime: true,
|
||||
priceNickname: 'Terminplaner-Einrichtung 149 €',
|
||||
}
|
||||
|
||||
// Ticketsystem (WOMS); eigene Konstante, weil auch die Enable-Banking-Rueckkehradresse darauf aufbaut
|
||||
const womsBaseUrl = (process.env.WOMS_PUBLIC_URL || 'https://ticket.webklar.com').replace(/\/$/, '')
|
||||
|
||||
@@ -313,6 +323,7 @@ export const config = {
|
||||
products,
|
||||
mailStorage,
|
||||
newsSetup,
|
||||
termineSetup,
|
||||
ustHinweis,
|
||||
// Register der kaufbaren Funktionen (siehe oben)
|
||||
features,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
updateDocument,
|
||||
} from './appwriteAdmin.js'
|
||||
import { lateStartAt } from './deadlines.js'
|
||||
import { featureConfig } from './featureRegistry.js'
|
||||
import { creditMinutesForInvoice, describeItems, getProduct, priceIdsOfInvoice, productKeyForLookupKey } from './products.js'
|
||||
|
||||
/**
|
||||
@@ -51,19 +52,26 @@ export function isStripeConfigured() {
|
||||
}
|
||||
|
||||
// Produkt-Metadaten "webklar" je Katalog-Schluessel (F09/Produces: ensurePrice)
|
||||
const PRODUCT_METADATA = { wartung: 'wartungs-abo', hosting: 'hosting-abo', mailStorage: 'mail-speicher', newsSetup: 'news-einrichtung' }
|
||||
const PRODUCT_METADATA = {
|
||||
wartung: 'wartungs-abo',
|
||||
hosting: 'hosting-abo',
|
||||
mailStorage: 'mail-speicher',
|
||||
newsSetup: 'news-einrichtung',
|
||||
termineSetup: 'termine-einrichtung',
|
||||
}
|
||||
|
||||
/** Katalogeintrag zum Schluessel: die beiden Abos aus products.js, der Zusatzspeicher aus config.mailStorage. */
|
||||
/** Katalogeintrag zum Schluessel: die beiden Abos aus products.js, Zusatzspeicher und Einrichtungen aus config. */
|
||||
function catalogFor(key) {
|
||||
if (key === 'mailStorage') return config.mailStorage
|
||||
if (key === 'newsSetup') return config.newsSetup
|
||||
if (key === 'termineSetup') return config.termineSetup
|
||||
return getProduct(key)
|
||||
}
|
||||
|
||||
const priceIdCache = new Map()
|
||||
/**
|
||||
* Preis per lookup_key holen oder (einmalig) samt Produkt anlegen. key: 'wartung' | 'hosting' |
|
||||
* 'mailStorage' | 'newsSetup' (Einmalpreis). Ersetzt das fruehere ensureAboPrice (nur Wartung); lookup_keys werden bei Stripe
|
||||
* 'mailStorage' | 'newsSetup' | 'termineSetup' (Einmalpreise). Ersetzt das fruehere ensureAboPrice (nur Wartung); lookup_keys werden bei Stripe
|
||||
* nie uebertragen, ein neuer Preis bekaeme also ohnehin einen neuen lookup_key im Katalog.
|
||||
*/
|
||||
export async function ensurePrice(key) {
|
||||
@@ -639,14 +647,26 @@ export async function createAboCheckout(customer, consent, productKey, offer = n
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkout fuer die einmalige Einrichtung einer Funktion (News-Pinnwand, 30 € brutto, kein Abo). Die Sitzung
|
||||
* verfaellt nach 24 Stunden (Stripe-Hoechstwert; fuenf Minuten Luft fuer die Laufzeit des Aufrufs), die
|
||||
* Rechnung erzeugt Stripe selbst. Nur die Zuordnung steht in den Metadaten, der Zustimmungswortlaut nicht.
|
||||
* Checkout fuer die einmalige Einrichtung einer Funktion aus dem Register config.features (News-Pinnwand 30 €,
|
||||
* Terminplaner 149 €; Einmalpreis, kein Abo). Preis, Rechnungstext und Rueckkehradresse kommen aus dem Register-Eintrag
|
||||
* zu `key`; ein unbekannter Schluessel ist ein Fehler 400, bevor Stripe angesprochen wird. Die Sitzung verfaellt nach
|
||||
* 24 Stunden (Stripe-Hoechstwert; fuenf Minuten Luft fuer die Laufzeit des Aufrufs), die Rechnung erzeugt Stripe
|
||||
* selbst und traegt featureKey und projectId in ihren Metadaten. Nur die Zuordnung steht in den Metadaten der Sitzung,
|
||||
* der Zustimmungswortlaut nicht.
|
||||
* @returns {Promise<{ id: string, url: string }>}
|
||||
*/
|
||||
export async function createFeatureCheckout(customer, { projectId, key = 'news', consentAt }) {
|
||||
const feature = featureConfig(key)
|
||||
if (!feature) {
|
||||
const err = new Error('Unbekannte Funktion.')
|
||||
err.status = 400
|
||||
throw err
|
||||
}
|
||||
const stripe = getStripe()
|
||||
const [priceId, stripeCustomerId] = await Promise.all([ensurePrice('newsSetup'), getOrCreateStripeCustomer(customer)])
|
||||
const [priceId, stripeCustomerId] = await Promise.all([ensurePrice(feature.product), getOrCreateStripeCustomer(customer)])
|
||||
// Rueckkehr ins Dashboard: bei Funktionen mit returnProjekt (Terminplaner) mit dem Projekt, damit die Seite es oeffnet
|
||||
const projekt = feature.returnProjekt ? encodeURIComponent(projectId) : ''
|
||||
const returnUrl = `${config.publicBaseUrl}/dashboard.html#${feature.returnHash}`
|
||||
const params = {
|
||||
mode: 'payment',
|
||||
customer: stripeCustomerId,
|
||||
@@ -658,12 +678,16 @@ export async function createFeatureCheckout(customer, { projectId, key = 'news',
|
||||
customer_update: { address: 'auto', name: 'auto' },
|
||||
invoice_creation: {
|
||||
enabled: true,
|
||||
invoice_data: { description: config.newsSetup.name, footer: config.stripe.invoiceFooter },
|
||||
invoice_data: {
|
||||
description: catalogFor(feature.product).name,
|
||||
footer: config.stripe.invoiceFooter,
|
||||
metadata: { featureKey: key, projectId },
|
||||
},
|
||||
},
|
||||
metadata: { customerId: customer.$id, projectId, featureKey: key, ...(consentAt ? { consentAt } : {}) },
|
||||
expires_at: Math.floor(Date.now() / 1000) + 24 * 60 * 60 - 5 * 60,
|
||||
success_url: `${config.publicBaseUrl}/dashboard.html#news?bestellt=1`,
|
||||
cancel_url: `${config.publicBaseUrl}/dashboard.html#news`,
|
||||
success_url: `${returnUrl}?bestellt=1${projekt ? `&projekt=${projekt}` : ''}`,
|
||||
cancel_url: `${returnUrl}${projekt ? `?projekt=${projekt}` : ''}`,
|
||||
}
|
||||
let session
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user