Files
Webklar-Kundenbereich/scripts/stripe-products.mjs
T

104 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Stripe-Preise je Produkt (Wartungs-Abo, Hosting-Abo, E-Mail-Zusatzspeicher): legt fehlende
* Preise an (ensurePrice), haelt die Wartungs-Produktbeschreibung bei Stripe auf dem Katalogtext
* (server/config.js) und passt die Kundenportal-Konfiguration an ("WEBklar – Abo verwalten").
* Idempotent: vorhandene Preise werden ueber lookup_key gefunden, nicht neu angelegt.
*
* Usage (im Container):
* node scripts/stripe-products.mjs --pruefen (nur anzeigen, was fehlt oder abweicht)
* node scripts/stripe-products.mjs (anlegen/anpassen)
*/
import 'dotenv/config'
import { config } from '../server/config.js'
import { ensurePortalConfiguration, ensurePrice, getStripe, PORTAL_HEADLINE } from '../server/services/stripeBilling.js'
import { PRODUCT_KEYS } from '../server/services/products.js'
const KEYS = [...PRODUCT_KEYS, 'mailStorage'] // ['wartung', 'hosting', 'mailStorage']
function catalogEntry(key) {
return key === 'mailStorage' ? config.mailStorage : config.products[key]
}
async function findPrice(lookupKey) {
const { data } = await getStripe().prices.list({ lookup_keys: [lookupKey], active: true, limit: 1 })
return data[0] || null
}
async function checkPrice(key) {
const catalog = catalogEntry(key)
const price = await findPrice(catalog.lookupKey)
if (!price) return { ok: false, note: `Preis ${catalog.lookupKey} fehlt bei Stripe` }
const mismatches = []
if (price.unit_amount !== catalog.amountCents) mismatches.push(`Betrag ${price.unit_amount} statt ${catalog.amountCents}`)
if (price.recurring?.interval !== 'month') mismatches.push(`recurring.interval "${price.recurring?.interval}" statt "month"`)
if (price.tax_behavior !== 'inclusive') mismatches.push(`tax_behavior "${price.tax_behavior}" statt "inclusive"`)
return { ok: mismatches.length === 0, note: mismatches.length ? mismatches.join('; ') : `OK (${price.id})` }
}
async function checkWartungDescription() {
const price = await findPrice(config.products.wartung.lookupKey)
if (!price) return { ok: false, note: 'Preis fehlt, Produkt kann nicht geprüft werden' }
const productId = typeof price.product === 'string' ? price.product : price.product.id
const product = await getStripe().products.retrieve(productId)
const ok = product.description === config.products.wartung.description
return { ok, note: ok ? 'OK' : 'Produktbeschreibung weicht vom Katalogtext ab' }
}
async function checkPortalConfig() {
const { data } = await getStripe().billingPortal.configurations.list({ active: true, limit: 20 })
const own = data.find((c) => c.metadata?.webklar === 'wartungs-abo')
if (!own) return { ok: false, note: 'Konfiguration fehlt bei Stripe' }
const ok = own.business_profile?.headline === PORTAL_HEADLINE
return { ok, note: ok ? 'OK' : `Überschrift "${own.business_profile?.headline}" statt "${PORTAL_HEADLINE}"` }
}
async function pruefen() {
let allOk = true
for (const key of KEYS) {
const r = await checkPrice(key)
allOk = allOk && r.ok
console.log(`${r.ok ? '✅' : '❌'} Preis ${key}: ${r.note}`)
}
const desc = await checkWartungDescription()
allOk = allOk && desc.ok
console.log(`${desc.ok ? '✅' : '❌'} Wartung-Produktbeschreibung: ${desc.note}`)
const portal = await checkPortalConfig()
allOk = allOk && portal.ok
console.log(`${portal.ok ? '✅' : '❌'} Kundenportal-Konfiguration: ${portal.note}`)
if (!allOk) {
console.log('')
console.log('Zum Anlegen/Anpassen: node scripts/stripe-products.mjs')
}
}
async function anlegen() {
for (const key of KEYS) {
const priceId = await ensurePrice(key)
console.log(`✅ Preis ${key}: ${priceId}`)
}
const price = await findPrice(config.products.wartung.lookupKey)
const productId = typeof price.product === 'string' ? price.product : price.product.id
await getStripe().products.update(productId, { description: config.products.wartung.description })
console.log('✅ Wartung-Produktbeschreibung auf Katalogtext gesetzt')
const configurationId = await ensurePortalConfiguration()
console.log(`✅ Kundenportal-Konfiguration: ${configurationId}`)
}
async function main() {
if (!config.stripe.secretKey) {
console.error('❌ STRIPE_SECRET_KEY fehlt')
process.exit(1)
}
const checkOnly = process.argv.includes('--pruefen')
console.log(checkOnly ? 'Modus: --pruefen (nur lesen, keine Änderungen)' : 'Modus: anlegen/anpassen')
console.log('')
if (checkOnly) await pruefen()
else await anlegen()
}
main().catch((err) => {
console.error('❌', err.message)
process.exit(1)
})