Abo: individuelle Angebote (z. B. erste 2 Monate je 25 EUR) mit Stripe-Coupon
- Admin-Tab "Abo-Angebote" und Button "Angebot" in der Kundenliste: Preis, Monate, Gueltigkeit, Hinweis fuer den Kunden, interne Notiz, Vorschau vor dem Anlegen - Je Angebot sofort ein Stripe-Coupon (amount_off, repeating ueber die Angebotsmonate, max. 1 Einloesung); danach automatisch wieder Normalpreis - Kunde ohne Abo: Angebot im Portal, Angebotspreis in der Bestelluebersicht, Coupon im Checkout; Angebot schaltet die Bestellung auch ausserhalb der Pilotliste frei; Bestellung prueft, dass das angezeigte Angebot noch gilt - Laufendes Abo: Coupon sofort am Abo fuer die naechsten Abbuchungen - Testkonten: Testrabatt im Checkout, Angebot per Webhook zusaetzlich am Abo - Angebot in Abo-Metadaten, Vertragsbestaetigung, Admin-Mail und Abo-Karte - Zurueckziehen loescht den Coupon; abgelaufene Angebote nicht mehr bestellbar - Stundenkontingent nach invoice.subtotal: auch voll rabattierte oder per Guthaben bezahlte Monate bringen die Stunde, die Probezeit nicht - setup-abo-collections.mjs: aboOffers, subscriptions.offerId/offerText, Attribut-Abfrage mit Limit (sonst Haenger ab 26 Attributen) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
48b13f4488
commit
bb2f46e191
11 files changed
+856
-30
No files matched your search
@@ -175,6 +175,8 @@ export async function syncSubscription(subscriptionId) {
|
||||
consentIp: meta.consentIp || null,
|
||||
consentUserAgent: meta.consentUserAgent || null,
|
||||
consentTextVersion: meta.consentTextVersion || null,
|
||||
offerId: meta.offerId || null,
|
||||
offerText: meta.offerText || null,
|
||||
})
|
||||
return doc
|
||||
}
|
||||
@@ -243,9 +245,10 @@ export async function grantCreditForInvoice(invoice) {
|
||||
const ref = subscriptionIdOfInvoice(invoice)
|
||||
const subscriptionId = typeof ref === 'string' ? ref : ref?.id
|
||||
if (!subscriptionId) return { skipped: 'keine Abo-Rechnung' }
|
||||
// total statt amount_paid: Eine Monatsgebuehr, die ein Guthaben (Uebernahmegutschrift) deckt,
|
||||
// ist bezahlt, obwohl nichts abgebucht wurde. Nur Probezeit-Rechnungen (0 €) zaehlen nicht.
|
||||
if (!(invoice.total > 0)) return { skipped: 'Betrag 0' }
|
||||
// subtotal (vor Rabatt und Guthaben) statt amount_paid: Auch ein Monat, den ein Angebot oder eine
|
||||
// Uebernahmegutschrift ganz deckt, ist ein bezahlter Leistungsmonat. Nur Probezeit-Rechnungen
|
||||
// haben eine Zwischensumme von 0 € und bringen keine Stunde.
|
||||
if (!(invoice.subtotal > 0)) return { skipped: 'Betrag 0' }
|
||||
|
||||
const sub = await syncSubscription(subscriptionId)
|
||||
if (!sub) return { skipped: 'Abo ohne Kundenzuordnung' }
|
||||
@@ -327,8 +330,10 @@ async function ensureTestCoupon() {
|
||||
* abgegeben ("Zahlungspflichtig bestellen"); hier hinterlegt er die Zahlungsart.
|
||||
* startChoice: 'sofort' (Leistung und erste Abbuchung sofort) oder 'nach_frist'
|
||||
* (15 Tage Probezeit ohne Leistung und ohne Abbuchung, Start nach der Widerrufsfrist).
|
||||
* offer: individuelles Angebot { id, stripeCouponId, text } -> Coupon im Checkout; bei Testkonten
|
||||
* bleibt der Testrabatt im Checkout und das Angebot wird nach dem Abschluss angehaengt (offerDeferred).
|
||||
*/
|
||||
export async function createAboCheckout(customer, consent) {
|
||||
export async function createAboCheckout(customer, consent, offer = null) {
|
||||
const current = await getCurrentSubscription(customer.$id)
|
||||
if (current && ACTIVE_STATUSES.has(current.status)) {
|
||||
const err = new Error('Sie haben das Wartungs-Abo bereits abgeschlossen.')
|
||||
@@ -354,7 +359,13 @@ export async function createAboCheckout(customer, consent) {
|
||||
earlyStartConsentAt: consent.earlyStartConsentAt || '',
|
||||
consentIp: String(consent.consentIp || '').slice(0, 64),
|
||||
consentUserAgent: String(consent.consentUserAgent || '').slice(0, 300),
|
||||
...(offer
|
||||
? { offerId: offer.id, offerText: offer.text.slice(0, 300), ...(testCoupon ? { offerDeferred: '1' } : {}) }
|
||||
: {}),
|
||||
}
|
||||
// Checkout erlaubt nur einen Rabatt: Testkonto -> Testrabatt, sonst der Angebots-Coupon
|
||||
const coupon = testCoupon || offer?.stripeCouponId || null
|
||||
const offerPrice = offer ? ` Preis wie vereinbart: ${offer.text}.` : ''
|
||||
const params = {
|
||||
mode: 'subscription',
|
||||
customer: stripeCustomerId,
|
||||
@@ -370,12 +381,14 @@ export async function createAboCheckout(customer, consent) {
|
||||
...(lateStart ? { trial_period_days: config.abo.lateStartTrialDays } : {}),
|
||||
},
|
||||
metadata: meta,
|
||||
...(testCoupon ? { discounts: [{ coupon: testCoupon }] } : {}),
|
||||
...(coupon ? { discounts: [{ coupon }] } : {}),
|
||||
custom_text: {
|
||||
submit: {
|
||||
message: lateStart
|
||||
? 'Ihre Bestellung haben Sie im Kundenportal abgegeben. Hier hinterlegen Sie die Zahlungsart; mit dem Abschluss kommt der Vertrag zustande. Wie gewählt beginnen wir nach Ablauf der Widerrufsfrist – bis dahin erbringen wir keine Leistungen und buchen nichts ab. Danach 50,00 € monatlich, Mindestlaufzeit 12 Monate, danach monatlich kündbar.'
|
||||
: 'Ihre Bestellung haben Sie im Kundenportal abgegeben. Hier hinterlegen Sie die Zahlungsart; mit der ersten Zahlung kommt der Vertrag zustande. 50,00 € monatlich, Mindestlaufzeit 12 Monate, danach monatlich kündbar.',
|
||||
message: offer
|
||||
? `Ihre Bestellung haben Sie im Kundenportal abgegeben. Hier hinterlegen Sie die Zahlungsart; mit ${lateStart ? 'dem Abschluss' : 'der ersten Zahlung'} kommt der Vertrag zustande.${lateStart ? ' Wie gewählt beginnen wir nach Ablauf der Widerrufsfrist – bis dahin erbringen wir keine Leistungen und buchen nichts ab.' : ''}${offerPrice} Mindestlaufzeit 12 Monate, danach monatlich kündbar.`
|
||||
: lateStart
|
||||
? 'Ihre Bestellung haben Sie im Kundenportal abgegeben. Hier hinterlegen Sie die Zahlungsart; mit dem Abschluss kommt der Vertrag zustande. Wie gewählt beginnen wir nach Ablauf der Widerrufsfrist – bis dahin erbringen wir keine Leistungen und buchen nichts ab. Danach 50,00 € monatlich, Mindestlaufzeit 12 Monate, danach monatlich kündbar.'
|
||||
: 'Ihre Bestellung haben Sie im Kundenportal abgegeben. Hier hinterlegen Sie die Zahlungsart; mit der ersten Zahlung kommt der Vertrag zustande. 50,00 € monatlich, Mindestlaufzeit 12 Monate, danach monatlich kündbar.',
|
||||
},
|
||||
},
|
||||
success_url: `${config.publicBaseUrl}/dashboard.html?abo=success`,
|
||||
|
||||
Reference in new issue
Block a user