Stripe: Testrabatt-Namen, Produktbild, Hosting-Leistungen, Tests für Bestandskunden
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c4607e9c43
commit
7df0b72019
7 files changed
+297
-52
No files matched your search
+37
-10
@@ -6,8 +6,11 @@
|
||||
* 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)
|
||||
* node scripts/stripe-products.mjs --pruefen (nur anzeigen, was fehlt oder abweicht, keine Schreibzugriffe)
|
||||
* node scripts/stripe-products.mjs --anlegen (anlegen/anpassen)
|
||||
*
|
||||
* Fix round 1, Minor 7: genau einer der beiden Schalter ist Pflicht - kein stiller Livegang bei
|
||||
* fehlendem oder falsch geschriebenem Schalter.
|
||||
*/
|
||||
import 'dotenv/config'
|
||||
import { config } from '../server/config.js'
|
||||
@@ -15,6 +18,14 @@ import { ensurePortalConfiguration, ensurePrice, getStripe, PORTAL_HEADLINE } fr
|
||||
import { PRODUCT_KEYS } from '../server/services/products.js'
|
||||
|
||||
const KEYS = [...PRODUCT_KEYS, 'mailStorage'] // ['wartung', 'hosting', 'mailStorage']
|
||||
const USAGE = 'Usage: node scripts/stripe-products.mjs --pruefen | --anlegen'
|
||||
|
||||
export function parseArgs(argv) {
|
||||
const args = argv.slice(2)
|
||||
if (args.length === 1 && args[0] === '--pruefen') return 'pruefen'
|
||||
if (args.length === 1 && args[0] === '--anlegen') return 'anlegen'
|
||||
return null
|
||||
}
|
||||
|
||||
function catalogEntry(key) {
|
||||
return key === 'mailStorage' ? config.mailStorage : config.products[key]
|
||||
@@ -33,6 +44,12 @@ async function checkPrice(key) {
|
||||
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"`)
|
||||
// Important 2: Produkt ohne Bild faellt in --pruefen auf (Checkout zeigt sonst kein Bild)
|
||||
const productId = typeof price.product === 'string' ? price.product : price.product?.id
|
||||
if (productId) {
|
||||
const product = await getStripe().products.retrieve(productId)
|
||||
if (!product.images?.length) mismatches.push('kein Produktbild')
|
||||
}
|
||||
return { ok: mismatches.length === 0, note: mismatches.length ? mismatches.join('; ') : `OK (${price.id})` }
|
||||
}
|
||||
|
||||
@@ -68,7 +85,9 @@ async function pruefen() {
|
||||
console.log(`${portal.ok ? '✅' : '❌'} Kundenportal-Konfiguration: ${portal.note}`)
|
||||
if (!allOk) {
|
||||
console.log('')
|
||||
console.log('Zum Anlegen/Anpassen: node scripts/stripe-products.mjs')
|
||||
console.log('Zum Anlegen/Anpassen: node scripts/stripe-products.mjs --anlegen')
|
||||
// Minor 7: --pruefen bleibt rein lesend, meldet Luecken aber ueber den Exit-Code (fuer Cron/CI)
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,18 +105,26 @@ async function anlegen() {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const mode = parseArgs(process.argv)
|
||||
if (!mode) {
|
||||
console.error(`❌ ${USAGE}`)
|
||||
process.exit(1)
|
||||
}
|
||||
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(mode === 'pruefen' ? 'Modus: --pruefen (nur lesen, keine Änderungen)' : 'Modus: --anlegen (anlegen/anpassen)')
|
||||
console.log('')
|
||||
if (checkOnly) await pruefen()
|
||||
if (mode === 'pruefen') await pruefen()
|
||||
else await anlegen()
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('❌', err.message)
|
||||
process.exit(1)
|
||||
})
|
||||
// Nur beim direkten Aufruf laufen lassen, nicht wenn das Modul importiert wird (z. B. fuer
|
||||
// parseArgs in einem Test) - main() wuerde sonst ueber process.exit den Prozess mitreissen.
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
main().catch((err) => {
|
||||
console.error('❌', err.message)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user