Abo-API: Nachweis-ID nach Appwrite-Regeln, Webhook bei dauerhaften Fehlern, Tests
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
7df0b72019
commit
887e1e54a0
10 files changed
+409
-107
No files matched your search
+19
-6
@@ -5,7 +5,7 @@ import { Router } from 'express'
|
||||
import { config } from '../config.js'
|
||||
import { getSessionCustomerId, requireSession } from '../middleware/session.js'
|
||||
import { getCustomerByEmail, getDocument, updateDocument } from '../services/appwriteAdmin.js'
|
||||
import { listContractEvents, openWithdrawals, recordContractEvent } from '../services/contractEvents.js'
|
||||
import { eventIdFor, listContractEvents, openWithdrawals, recordContractEvent } from '../services/contractEvents.js'
|
||||
import { lateStartAt, withdrawalDeadline } from '../services/deadlines.js'
|
||||
import { getLedger } from '../services/hourLedger.js'
|
||||
import { sendAdminNotice, sendCustomerMail } from '../services/mailer.js'
|
||||
@@ -324,11 +324,12 @@ async function sendPortalCancellationConfirmation(sub, createdUnix) {
|
||||
* Nachweis des Neuabschlusses als Vertragsereignis. Der Vertrag kommt mit dem Abschluss bei Stripe
|
||||
* zustande, die Widerrufsfrist zaehlt ab dann (details.contractAt = startedAt, F04); die Zustimmung
|
||||
* (Fassung, Zeitpunkt, IP, Browser) kommt aus den Abo-Metadaten, die der Spiegel traegt. Feste
|
||||
* Dokument-ID je Abo: eine Webhook-Wiederholung legt kein zweites Ereignis an (F05).
|
||||
* Dokument-ID je Abo (eventIdFor, 36 Zeichen): eine Webhook-Wiederholung legt kein zweites
|
||||
* Ereignis an (F05).
|
||||
*/
|
||||
async function recordAbschluss(sub, session) {
|
||||
return recordContractEvent({
|
||||
id: `abschluss-${sub.$id}`,
|
||||
id: eventIdFor('abschluss', sub.$id),
|
||||
customerId: sub.customerId,
|
||||
subscriptionId: sub.$id,
|
||||
type: 'abschluss',
|
||||
@@ -357,8 +358,20 @@ async function handleStripeEvent(event) {
|
||||
case 'checkout.session.completed': {
|
||||
if (obj.mode !== 'subscription' || !obj.subscription) return
|
||||
const sub = await syncSubscription(obj.subscription)
|
||||
// Ein Fehler hier (etwa Collection fehlt) gibt 500 -> Stripe wiederholt, das Ereignis wird nachgeholt
|
||||
if (sub) await recordAbschluss(sub, obj)
|
||||
// Nachweis: voruebergehend nicht speicherbar (Appwrite nicht erreichbar, 5xx) -> 500, Stripe
|
||||
// wiederholt und der Nachweis kommt nach. Dauerhaft nicht speicherbar (4xx, etwa Collection
|
||||
// oder Attribut fehlt) -> Angebot, T02 und K01 laufen trotzdem, T02 sagt es dem Team; eine
|
||||
// Wiederholung wuerde nichts aendern, der Kunde soll seine Bestaetigung nicht warten lassen.
|
||||
let proofProblem = false
|
||||
if (sub) {
|
||||
try {
|
||||
await recordAbschluss(sub, obj)
|
||||
} catch (err) {
|
||||
if (!err.status || err.status >= 500) throw err
|
||||
console.error(`[abo] Nachweis des Abschlusses ${obj.subscription} nicht gespeichert (${err.status}):`, err.message)
|
||||
proofProblem = true
|
||||
}
|
||||
}
|
||||
const offerProblem = await activateOfferFromCheckout(obj).then(
|
||||
() => false,
|
||||
(e) => {
|
||||
@@ -375,7 +388,7 @@ async function handleStripeEvent(event) {
|
||||
stripe_abo_id: obj.subscription,
|
||||
angebot_text: obj.metadata?.offerText || '',
|
||||
},
|
||||
blocks: { angebot: Boolean(obj.metadata?.offerText), angebot_fehler: offerProblem },
|
||||
blocks: { angebot: Boolean(obj.metadata?.offerText), angebot_fehler: offerProblem, nachweis_fehlt: proofProblem },
|
||||
})
|
||||
await sendContractConfirmation(sub, obj.customer_details?.name).catch((e) =>
|
||||
console.error('[abo] Vertragsbestaetigung fehlgeschlagen:', e.message)
|
||||
|
||||
Reference in new issue
Block a user