Payment and booking coupling
Why it matters
Without bidirectional references between the booking record and the payment provider transaction, any webhook processing failure becomes an irrecoverable data loss: you cannot look up the booking from the payment event, and you cannot look up the payment from the booking record. CWE-706 (use of incorrectly-resolved name or reference) and CWE-841 both apply. This is the root cause of "customer was charged but has no booking" incidents that require manual database forensics: the payment provider has the charge, the application has the booking, but there is no programmatic link between them. Every refund, rebooking, and dispute resolution becomes a manual operation.
Severity rationale
Critical because without bidirectional coupling, any webhook failure produces orphaned charges that cannot be programmatically reconciled, requiring manual intervention per incident.
Remediation
Add a paymentIntentId column to the Booking model and pass the booking ID in the payment provider's metadata. Add this in prisma/schema.prisma and your checkout handler.
// schema.prisma
model Booking {
id String @id @default(cuid())
paymentIntentId String? // Stripe PaymentIntent ID
}
// In checkout handler
const intent = await stripe.paymentIntents.create({
amount,
metadata: { bookingId: booking.id }, // payment -> booking
});
await db.booking.update({
where: { id: booking.id },
data: { paymentIntentId: intent.id }, // booking -> payment
});
Detection
-
ID:
payment-booking-coupling -
Label: Payment and booking coupling
-
Severity:
critical -
What to look for: Count all links between booking and payment records. The Booking model must store the payment provider's transaction/intent ID (e.g.,
paymentIntentIdfor Stripe,orderIdfor PayPal). The Payment/Transaction record must store the Booking ID (typically inmetadatafor Stripe, or a separatepaymentstable withbookingIdforeign key). Verify bidirectional links exist: booking-to-payment AND payment-to-booking. Enumerate: "X of 2 bidirectional links present: [booking->payment: field, payment->booking: field]." -
Pass criteria: Both directions of the link must exist — at least 2 of 2 bidirectional references required. (1) Booking model has a field storing the payment provider's ID (e.g.,
paymentIntentId,stripeSessionId,paypalOrderId). (2) Payment provider metadata or a local payments table stores the booking ID. Report:"2 of 2 bidirectional links present: booking.paymentIntentId -> Stripe, Stripe metadata.bookingId -> booking."Report even on pass:"Bidirectional link: booking.[field] <-> [payment provider metadata/table].[field] in [schema file]." -
Fail criteria: No link between the two in either or both directions. Payments and bookings are orphaned and cannot be correlated. Also fails if only 1 direction exists (booking stores payment ID but payment does not store booking ID, or vice versa).
-
Skip (N/A) when: No payment integration (no Stripe, PayPal, Square, or other payment SDK in
package.jsondependencies). -
Detail on fail:
"Booking model lacks a paymentIntentId or paymentId field. If a webhook fails, there is no way to recover which payment corresponds to which booking." -
Cross-reference: The confirmation-record check in Booking Creation verifies the booking ID is passed to the payment provider at creation time.
-
Cross-reference: The refund-logic check in this category needs this link to identify which payment to refund.
-
Cross-reference: The retry-mechanism check in this category needs consistent IDs for idempotency.
-
Remediation: Add bidirectional references in your schema (e.g.,
prisma/schema.prisma) and payment creation code.model Booking { id String @id paymentIntentId String? // Link to Stripe PaymentIntent // ... other fields } // On payment intent creation, include booking ID in metadata const paymentIntent = await stripe.paymentIntents.create({ amount, metadata: { bookingId: booking.id } }); // In webhook, extract booking ID from metadata const bookingId = event.data.object.metadata.bookingId;
External references
- cwe · CWE-706 — Use of Incorrectly-Resolved Name or Reference
- cwe · CWE-841 — Improper Enforcement of Behavioral Workflow
- iso-25010:2011 · functional-suitability.functional-correctness
Taxons
History
- 2026-04-18·v1.0.0·Initial import from booking-flow-lifecycle·automated