@topy-ai/maggie 0.7.38 → 0.7.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README-zh-TW.md +55 -4
  2. package/README.md +65 -7
  3. package/bin/maggie.js +10 -2
  4. package/bundled-contracts/maggie-design/css-utility-evidence-v1.schema.json +10 -0
  5. package/bundled-contracts/maggie-design/dashboard-surface-v1.schema.json +11 -0
  6. package/bundled-contracts/maggie-design/progressive-enhancement-v1.schema.json +12 -0
  7. package/bundled-contracts/maggie-design/sample-surface-v1.schema.json +13 -0
  8. package/bundled-contracts/maggie-seo/privacy-origin-evidence-v1.schema.json +11 -0
  9. package/bundled-contracts/maggiedash/booking-access-v1.json +5 -4
  10. package/bundled-contracts/maggiedash/booking-customer-surface-v1.json +12 -1
  11. package/bundled-contracts/maggiedash/booking-email-templates-v1.json +2 -0
  12. package/bundled-contracts/maggiedash/booking-host-adapter-v1.json +14 -2
  13. package/bundled-contracts/maggiedash/booking-runtime.v1.json +41 -0
  14. package/bundled-contracts/maggiedash/execution-board.json +526 -26
  15. package/bundled-contracts/maggiedash/host-capabilities-v1.schema.json +10 -0
  16. package/bundled-contracts/maggiedash/site-structure-v1.schema.json +13 -0
  17. package/bundled-references/maggiedash-booking/ARCHITECTURE.md +218 -0
  18. package/bundled-references/maggiedash-booking/CURRENT-STATE.md +92 -0
  19. package/bundled-references/maggiedash-booking/DATA-FLOW.md +143 -0
  20. package/bundled-references/maggiedash-booking/DATA-MODEL.md +367 -0
  21. package/bundled-references/maggiedash-booking/DECISIONS.md +94 -0
  22. package/bundled-references/maggiedash-booking/EXECUTION-BOARD.json +2387 -0
  23. package/bundled-references/maggiedash-booking/HOST-ADAPTER.md +314 -0
  24. package/bundled-references/maggiedash-booking/ORAWELLNESS-INTEGRATION-AUDIT.md +227 -0
  25. package/bundled-references/maggiedash-booking/PAYMENT-GATEWAY.md +267 -0
  26. package/bundled-references/maggiedash-booking/PRD.md +228 -0
  27. package/bundled-references/maggiedash-booking/PROGRESS.md +2434 -0
  28. package/bundled-references/maggiedash-booking/QA-TEST-PLAN.md +235 -0
  29. package/bundled-references/maggiedash-booking/README.md +271 -0
  30. package/bundled-references/maggiedash-booking/RUNTIME-OPERATIONS.md +152 -0
  31. package/bundled-references/maggiedash-booking/SECURITY-COMPLIANCE.md +158 -0
  32. package/bundled-references/maggiedash-booking/SKILLS-AND-CLI.md +542 -0
  33. package/bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md +129 -0
  34. package/bundled-references/maggiedash-booking/TASK-RUNBOOK.md +107 -0
  35. package/bundled-references/maggiedash-booking/TASKS.md +137 -0
  36. package/bundled-references/maggiedash-booking/USER-JOURNEYS.md +224 -0
  37. package/bundled-references/maggiedash-booking/diagrams/booking-dfd.excalidraw +1 -0
  38. package/bundled-references/maggiedash-booking/diagrams/booking-dfd.mmd +16 -0
  39. package/bundled-references/maggiedash-booking/diagrams/booking-dfd.png +0 -0
  40. package/bundled-references/maggiedash-booking/diagrams/booking-dfd.svg +1 -0
  41. package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.mmd +19 -0
  42. package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.png +0 -0
  43. package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.svg +1 -0
  44. package/bundled-references/maggiedash-booking/diagrams/manager-journey.excalidraw +1 -0
  45. package/bundled-references/maggiedash-booking/diagrams/manager-journey.mmd +11 -0
  46. package/bundled-references/maggiedash-booking/diagrams/manager-journey.png +0 -0
  47. package/bundled-references/maggiedash-booking/diagrams/manager-journey.svg +1 -0
  48. package/bundled-references/maggiedash-booking/diagrams/payment-sequence.mmd +20 -0
  49. package/bundled-references/maggiedash-booking/diagrams/payment-sequence.png +0 -0
  50. package/bundled-references/maggiedash-booking/diagrams/payment-sequence.svg +1 -0
  51. package/bundled-references/maggiedash-booking/diagrams/system-context.excalidraw +1 -0
  52. package/bundled-references/maggiedash-booking/diagrams/system-context.mmd +10 -0
  53. package/bundled-references/maggiedash-booking/diagrams/system-context.png +0 -0
  54. package/bundled-references/maggiedash-booking/diagrams/system-context.svg +1 -0
  55. package/bundled-skills/maggie-blog-bootstrap/SKILL.md +16 -0
  56. package/bundled-skills/maggie-booking/SKILL.md +97 -18
  57. package/bundled-skills/maggie-design/SKILL.md +35 -0
  58. package/bundled-skills/maggie-seo-geo/SKILL.md +12 -0
  59. package/bundled-skills/maggie-service-booking/SKILL.md +14 -0
  60. package/bundled-tools/clis/maggie_booking.py +161 -26
  61. package/bundled-tools/clis/maggie_contracts.py +282 -0
  62. package/bundled-tools/clis/maggie_dash.py +22 -6
  63. package/bundled-tools/clis/maggie_design.py +29 -12
  64. package/bundled-tools/clis/maggie_service_booking.py +50 -1
  65. package/bundled-tools/clis/site_audit.py +49 -0
  66. package/package.json +1 -1
@@ -0,0 +1,267 @@
1
+ # Payment gateway architecture
2
+
3
+ The booking domain depends on a provider-neutral payment port. Stripe is the
4
+ first implementation because the current product already uses it, but the
5
+ booking service must not import Stripe types or encode Stripe state names.
6
+
7
+ The dated Stripe resource/event audit is in
8
+ [`STRIPE-INTEGRATION.md`](./STRIPE-INTEGRATION.md). It is deliberately separate
9
+ from this provider-neutral design so a host can upgrade Stripe without making
10
+ the booking domain depend on Stripe object names. The installable matrix lives
11
+ in the MaggieDash
12
+ [`stripe-booking-capabilities-v1.json`](../../bundled-contracts/maggiedash/stripe-booking-capabilities-v1.json)
13
+ contract.
14
+
15
+ ## 1. Design rule
16
+
17
+ ```text
18
+ Booking service
19
+ |
20
+ v
21
+ PaymentGateway port
22
+ |
23
+ +---+-------------------+
24
+ | |
25
+ Stripe adapter Future adapter
26
+ ```
27
+
28
+ The local database owns payment attempts, transaction projections, refund
29
+ limits, and the relationship between payment and booking. The gateway owns the
30
+ external payment lifecycle and returns normalized facts.
31
+
32
+ ## 2. Implemented port boundary
33
+
34
+ MaggieDash now ships the provider-neutral orchestration in
35
+ `backend/booking/payment-gateway.mjs` and a deterministic fake implementation
36
+ in `backend/booking/fake-payment-gateway.mjs`. The fake gateway supports
37
+ `success`, `action_required`, `delayed`, and `failure` modes, plus refund and
38
+ reconciliation observations. It is for tests only; it never stores card data or
39
+ provider secrets.
40
+
41
+ ```bash
42
+ npm run booking:payment:test
43
+ ```
44
+
45
+ ## 3. Port shape
46
+
47
+ This TypeScript shape is a contract sketch for the implementation. Keep it in a
48
+ shared contract package when code work begins.
49
+
50
+ ```ts
51
+ type Money = {
52
+ amountMinor: number;
53
+ currency: string;
54
+ };
55
+
56
+ type PaymentCapabilities = {
57
+ onlinePayment: boolean;
58
+ delayedCapture: boolean;
59
+ savedPaymentMethod: boolean;
60
+ offSessionCharge: boolean;
61
+ refunds: boolean;
62
+ partialRefunds: boolean;
63
+ terminal: boolean;
64
+ connectedAccounts: boolean;
65
+ webhookVerification: boolean;
66
+ };
67
+
68
+ type PaymentGateway = {
69
+ id: string;
70
+ capabilities(): Promise<PaymentCapabilities>;
71
+ health(): Promise<{ configured: boolean; capabilities: string[]; reason?: string }>;
72
+ createPayment(input: CreatePaymentInput): Promise<PaymentAction>;
73
+ getPayment(input: GetPaymentInput): Promise<PaymentSnapshot>;
74
+ cancelPayment(input: CancelPaymentInput): Promise<PaymentSnapshot>;
75
+ refundPayment(input: RefundPaymentInput): Promise<RefundSnapshot>;
76
+ savePaymentMethod?(input: SavePaymentMethodInput): Promise<SavedMethodSnapshot>;
77
+ chargeSavedMethod?(input: ChargeSavedMethodInput): Promise<PaymentAction>;
78
+ createTerminalPayment?(input: TerminalPaymentInput): Promise<PaymentAction>;
79
+ verifyWebhook(input: VerifyWebhookInput): Promise<VerifiedPaymentEvent>;
80
+ };
81
+ ```
82
+
83
+ Every input carries `tenantId`, `bookingId` or another local aggregate ID,
84
+ `correlationId`, a scope-specific idempotency key, money, and a purpose. The
85
+ adapter maps local IDs to provider metadata without exposing internal secrets.
86
+
87
+ ## 4. Service payment policies and normalized purposes
88
+
89
+ Each service variant may declare one provider-neutral payment policy:
90
+
91
+ | Policy | Initial due | Booking after initial capture | Remaining balance |
92
+ |---|---:|---|---:|
93
+ | `full` | 100% | confirmed when captured | zero after capture |
94
+ | `deposit` | configured amount or percentage | confirmed after deposit capture | total minus captured amount |
95
+ | `pay_later` | zero online | confirmed without online payment | payable at the appointment |
96
+ | `no_payment` | zero | confirmed without payment | zero |
97
+
98
+ The policy is stored on the booking as a snapshot. A payment attempt is labelled
99
+ with `deposit`, `balance`, `full`, or `adjustment`; the worker and payment
100
+ repository aggregate successful attempts and pending/successful refunds into
101
+ `paidMinor`, `balanceDueMinor`, and `paymentStatus`. A later balance attempt
102
+ is rejected when it exceeds the current balance. Idempotency replay is resolved
103
+ before mutable balance validation, so retries return the original provider
104
+ action safely.
105
+
106
+ ```text
107
+ deposit
108
+ balance
109
+ full
110
+ adjustment
111
+ no_show_fee
112
+ package_purchase
113
+ membership
114
+ gift_card
115
+ ```
116
+
117
+ The first booking release implements `deposit`, `balance`, `full`, and
118
+ `adjustment`. The additional purposes remain documented extension points so
119
+ later features do not fork the payment model.
120
+
121
+ Treatment package balances do not use `package_purchase` as a booking payment
122
+ attempt. A customer package purchase is its own payment aggregate: the public
123
+ checkout creates a pending purchase, Stripe/webhooks reconcile it, and a
124
+ succeeded purchase atomically creates the customer entitlement. Managers can
125
+ also grant an entitlement manually; redemption remains atomic and no money is
126
+ implied by that redemption. Package refunds are blocked after any visit has
127
+ been redeemed and cancel an unredeemed entitlement when the provider refund is
128
+ accepted.
129
+
130
+ ## 5. Stripe adapter responsibilities
131
+
132
+ The `StripePaymentGateway` should:
133
+
134
+ - create or reuse the configured Stripe customer mapping server-side;
135
+ - create a provider payment object for the normalized amount and currency;
136
+ - pass a local idempotency key to each mutating Stripe request;
137
+ - return `action_required`, `processing`, `paid`, or `failed` without exposing
138
+ raw provider objects to the browser;
139
+ - verify webhook signatures before parsing an event;
140
+ - map provider events into normalized events;
141
+ - make refund and partial refund requests idempotent;
142
+ - support a connected-account context only when the tenant's payment account
143
+ configuration explicitly enables it;
144
+ - report capability and health separately from booking configuration;
145
+ - never store card numbers, CVC, or full payment method payloads.
146
+
147
+ For new flows, the adapter should use Checkout Sessions or PaymentIntents. A
148
+ SetupIntent is appropriate only when the customer has explicitly consented to a
149
+ future charge. The adapter must pin the account/server API version, test
150
+ upgrades in a sandbox, and avoid introducing new code on the legacy Charges
151
+ API. Stripe's effective API version is account-scoped; see the official
152
+ [versioning guidance](https://docs.stripe.com/api/versioning).
153
+
154
+ The host owns secret loading. The dashboard receives only a client-safe action
155
+ token or URL where the chosen payment flow needs one.
156
+
157
+ ## 6. Cancellation settlement and Resend hand-off
158
+
159
+ An allowed cancellation is a two-stage operation:
160
+
161
+ 1. Booking commits the lifecycle transition and releases allocations.
162
+ 2. The PostgreSQL settlement seam reads captured attempts and prior refunds,
163
+ calculates the policy-safe full/deposit amount, and calls the injected
164
+ provider with a booking/payment-attempt idempotency key.
165
+
166
+ The booking remains cancelled if Stripe is unavailable. The result is bounded
167
+ as `manual_required`, so operators can retry from the payment/refund workspace
168
+ without treating a provider outage as a successful refund. The host queues the
169
+ `payment.refunded` intent into the existing tenant-scoped notification outbox
170
+ only when the provider returns `pending` or `succeeded`; the Resend worker owns
171
+ delivery, retry, and dead-letter handling.
172
+
173
+ No-show fee calculation is deliberately separate from charging. The local
174
+ implementation exposes an explicit manager/finance adjustment route that
175
+ requires saved-payment-method consent, provider capability checks, and
176
+ confirmation. A status transition never charges automatically; pending or
177
+ failed provider results remain `manual_required`/pending and do not queue a
178
+ success notification.
179
+
180
+ MaggieDash's `backend/booking/stripe-adapter.mjs` implements this boundary
181
+ without importing the Stripe SDK. The host passes `createClient(secret,
182
+ {apiVersion})`, `getSecret`, `getWebhookSecret`, and optional connected-account
183
+ scope. The adapter exposes normalized payment/read/refund/webhook operations;
184
+ it never serializes the secret or raw webhook body into a response.
185
+
186
+ ## 6. Payment and booking are separate
187
+
188
+ Examples of valid combinations:
189
+
190
+ | Booking status | Payment status | Meaning |
191
+ |---|---|---|
192
+ | confirmed | not_required | Manager or policy allows no payment |
193
+ | confirmed | partially_paid | Deposit captured, balance due |
194
+ | pending_payment | action_required | Customer must complete authentication |
195
+ | confirmed | refund_pending | Booking cancelled, provider refund is not final |
196
+ | cancelled | refunded | Booking released and money returned |
197
+ | completed | paid | Full visit and payment complete |
198
+
199
+ No provider status should be copied into the booking status field.
200
+
201
+ ## 7. Webhook and reconciliation flow
202
+
203
+ 1. Receive the provider request on a server-only endpoint.
204
+ 2. Resolve the provider account and verify the signature with the correct
205
+ secret.
206
+ 3. Extract provider event ID and reject or acknowledge duplicates using the
207
+ unique event record.
208
+ 4. Map the event to a local payment attempt or store it as unmatched for safe
209
+ review.
210
+ 5. Apply the normalized payment projection in a transaction.
211
+ 6. Re-evaluate the associated booking/hold transition with current state and
212
+ policy.
213
+ 7. Append payment, booking, audit, and outbox events.
214
+ 8. Return a bounded success response; retryable failures remain visible to the
215
+ worker.
216
+
217
+ The customer return URL can request a provider read, but it cannot mark a
218
+ payment paid without this reconciliation path.
219
+
220
+ ## 8. Refund rules
221
+
222
+ - The local captured total is the upper bound for all refunds.
223
+ - A refund request creates a local `refund_pending` record before the provider
224
+ call when the operation is asynchronous.
225
+ - The same refund idempotency key returns the original refund record.
226
+ - A provider rejection is recorded as `refund_failed` with a safe reason.
227
+ - Partial refunds preserve the remaining refundable amount.
228
+ - Cancellation policy, reason, actor, and policy version are stored locally.
229
+ - A no-show transition returns a bounded `review_required` fee preview from the
230
+ booking-scoped policy. It never creates a refund item or calls the provider;
231
+ charging a no-show fee requires a separate explicit manager/finance action,
232
+ customer consent, a saved provider payment method, and verified provider
233
+ off-session capability.
234
+
235
+ ## 9. Future provider onboarding
236
+
237
+ Adding a provider requires:
238
+
239
+ 1. Implement the port without changing booking tables or transitions.
240
+ 2. Declare capabilities and configuration names.
241
+ 3. Add success, missing-credentials, timeout, duplicate, out-of-order event,
242
+ refund, and unsupported-capability fixtures.
243
+ 4. Run provider contract and conformance tests.
244
+ 5. Add a redacted health report and operator-facing setup errors.
245
+ 6. Add a migration only if the provider needs a new generic external reference,
246
+ never a provider-specific booking column.
247
+
248
+ The first candidate after Stripe can be Square if merchant demand requires it.
249
+ Provider choice does not change the local availability or booking engine.
250
+
251
+ ## 10. Configuration shape
252
+
253
+ ```json
254
+ {
255
+ "payment": {
256
+ "defaultProvider": "stripe",
257
+ "mode": "test",
258
+ "accountScope": "tenant",
259
+ "capturePolicy": "automatic",
260
+ "capabilitiesRequired": ["onlinePayment", "refunds"]
261
+ }
262
+ }
263
+ ```
264
+
265
+ This is metadata only. Actual secrets use server runtime configuration, secret
266
+ manager references, or encrypted host storage. No secret belongs in this JSON,
267
+ the dashboard bundle, a fixture, or an issue report.
@@ -0,0 +1,228 @@
1
+ # Booking workspace PRD
2
+
3
+ Status: local implementation delivered; real-host release evidence pending
4
+ Owner: MaggieDash / ai-cmo-skills
5
+ Target route: `/_maggie/booking`
6
+ Authentication: host-owned email/password session
7
+ First payment adapter: Stripe
8
+ First release posture: single business or small multi-location tenant, provider-neutral core
9
+
10
+ ## 1. Product summary
11
+
12
+ MaggieDash currently gives a store manager an admin workspace for site content,
13
+ services, SEO, activity, navigation, media, and AI-assisted operations. This
14
+ project adds a separate Booking workspace where a manager can configure the
15
+ business, staff, services, resources, availability, payment rules, bookings,
16
+ customers, notifications, and reports.
17
+
18
+ The product must support a real booking lifecycle rather than only linking to a
19
+ third-party booking page. It must prevent double booking under concurrent
20
+ requests, reconcile asynchronous payment events, and retain an audit trail for
21
+ every material change.
22
+
23
+ ## 2. Problem
24
+
25
+ Store managers currently need to stitch together service pages, a booking
26
+ provider, a payment dashboard, a calendar, email tools, and spreadsheets. This
27
+ creates four user-visible failures:
28
+
29
+ 1. The published availability does not match staff, resource, or closure rules.
30
+ 2. A customer pays but the booking is not confirmed, or two customers pay for
31
+ the same slot.
32
+ 3. A manager cannot see the operational day in one place.
33
+ 4. Adding a second payment provider would require changing booking business
34
+ logic and UI code.
35
+
36
+ ## 3. Goals
37
+
38
+ - Give a manager one authenticated workspace at `/_maggie/booking`.
39
+ - Provide a correct service, staff, resource, schedule, availability, hold,
40
+ booking, payment, and customer model.
41
+ - Make the payment provider replaceable through a stable internal port.
42
+ - Keep Stripe-specific objects behind the Stripe adapter.
43
+ - Make all writes idempotent, auditable, and safe to retry.
44
+ - Expose provider capabilities honestly. Unsupported operations fail closed.
45
+ - Provide ai-cmo-skills contracts, fixtures, validators, and release gates for
46
+ host integration.
47
+ - Preserve the existing `/_maggie/admin` content and operations workspace.
48
+
49
+ ## 4. Non-goals for the first release
50
+
51
+ - Payroll, commissions, inventory, accounting, or tax filing.
52
+ - A marketplace, platform-led merchant-of-record flow, or public discovery
53
+ network.
54
+ - Memberships, loyalty, gift-card ledgers, or advanced waitlist optimization
55
+ in the first production slice. The current release includes treatment
56
+ packages, customer entitlements, package checkout/refunds, package
57
+ redemption, cancellation-safe credit restoration, a tenant-scoped waitlist
58
+ queue, and a bounded priority/FIFO matcher with manual conversion to a real
59
+ booking.
60
+ - Replacing the host's authentication, database connection, deployment, or
61
+ media storage.
62
+ - Storing card numbers, CVC, or other payment credentials in the host database.
63
+
64
+ These remain documented extension points, not hidden commitments.
65
+
66
+ ## 5. Personas
67
+
68
+ | Persona | Job to be done | Success signal |
69
+ |---|---|---|
70
+ | Owner | Configure the business and understand revenue | Can open a day, see bookings and payment status, and audit changes |
71
+ | Store manager | Run the day and resolve exceptions | Can create, move, cancel, refund, check in, and complete a booking safely |
72
+ | Staff member | See assigned work and update visit status | Sees only permitted locations and can check in/out without payment access |
73
+ | Customer | Find a valid slot and pay or reserve it | Receives one clear outcome with no duplicate charge or booking |
74
+ | Finance/support operator | Reconcile money and recover failures | Can trace a booking to gateway attempts, refunds, webhooks, and events |
75
+ | Host developer | Integrate MaggieDash with a project | Has typed routes, fixtures, conformance evidence, and deterministic gates |
76
+
77
+ ## 6. MVP user outcomes
78
+
79
+ ### Manager setup
80
+
81
+ The manager creates a location, timezone, service, service variant, staff member,
82
+ staff-service eligibility, working hours, and optional resource requirement. The
83
+ workspace reports whether the configuration is bookable and names the missing
84
+ dependency when it is not.
85
+
86
+ ### Customer booking
87
+
88
+ The customer selects a service and location, receives slots calculated from
89
+ the availability engine, creates a short-lived hold, completes the configured
90
+ payment or deposit step, and receives a confirmation only after the payment
91
+ state is verified.
92
+
93
+ ### Daily operations
94
+
95
+ The manager views a day or week calendar, filters by staff/location/status,
96
+ opens a booking, records check-in and completion, takes an outstanding payment,
97
+ reschedules, cancels, or starts a refund according to policy.
98
+
99
+ ### Payment recovery
100
+
101
+ If a gateway redirect is interrupted, the customer refreshes or returns later,
102
+ or a webhook is delayed, the booking remains in an explicit pending state. A
103
+ reconciliation job can safely retry the provider read without creating another
104
+ booking or charge.
105
+
106
+ The same recovery rule applies when the provider call fails after a hold has
107
+ already been converted: the customer checkout idempotency key must replay the
108
+ conversion, reuse the valid customer token, and retry the provider operation
109
+ without creating a second booking.
110
+
111
+ ## 7. Functional requirements
112
+
113
+ ### Configuration
114
+
115
+ | ID | Requirement | Acceptance condition |
116
+ |---|---|---|
117
+ | BK-CFG-01 | A tenant can manage one or more locations | Every location has an IANA timezone, locale, currency, and active flag |
118
+ | BK-CFG-02 | A tenant can manage services and variants | Duration, price, currency, buffers, booking window, and status are explicit |
119
+ | BK-CFG-03 | A tenant can assign staff to services and locations | Ineligible staff never appear in valid slot results |
120
+ | BK-CFG-04 | A tenant can configure resources | A slot is unavailable when any required resource conflicts |
121
+ | BK-CFG-05 | A tenant can configure opening hours, shifts, closures, time off, and blocks | DST, overnight hours, and exceptions have deterministic tests |
122
+ | BK-CFG-06 | A tenant can configure payment policy | Full payment, deposit, pay-later, no-show protection, and no-payment are distinct |
123
+ | BK-CFG-07 | A tenant can configure cancellation/refund policy | The policy version is copied to each booking at creation |
124
+
125
+ Implementation note: BK-CFG-07 is implemented as a normalized catalogue policy
126
+ with a booking-time snapshot and customer cancellation-cutoff enforcement.
127
+ Refund execution is wired through the host provider boundary. A no-show
128
+ transition remains review-only; a separate manager/finance action can charge a
129
+ policy-derived fee only when the customer consented, a saved payment method
130
+ exists, and Stripe off-session capability is verified. Live migration, provider
131
+ acceptance, and delivery evidence remain host/provider release work.
132
+
133
+ ### Availability and booking
134
+
135
+ | ID | Requirement | Acceptance condition |
136
+ |---|---|---|
137
+ | BK-BOOK-01 | Calculate availability from all applicable constraints | Results include a traceable reason for exclusion in diagnostics mode |
138
+ | BK-BOOK-02 | Create a first-class booking hold | Hold has owner, expiry, scope, idempotency key, and release reason |
139
+ | BK-BOOK-03 | Prevent double booking at the database boundary | Concurrent requests yield at most one active allocation |
140
+ | BK-BOOK-04 | Create multi-service bookings with segments | Each segment has its own staff/resource allocation and time range |
141
+ | BK-BOOK-05 | Support manager-created bookings | Manager can bypass customer payment only when policy and permission allow it |
142
+ | BK-BOOK-06 | Support reschedule without destructive replacement | Original allocation and actor/reason remain in event history |
143
+ | BK-BOOK-07 | Support cancellation, no-show, check-in, in-progress, and completion | Invalid transitions are rejected and visible to the operator |
144
+
145
+ ### Payments
146
+
147
+ | ID | Requirement | Acceptance condition |
148
+ |---|---|---|
149
+ | BK-PAY-01 | Use a provider-neutral payment port | Booking code imports no Stripe SDK or provider object |
150
+ | BK-PAY-02 | Support payment attempts and transactions separately | A retry never overwrites the prior attempt |
151
+ | BK-PAY-03 | Reconcile verified provider events | Duplicate and out-of-order events are harmless |
152
+ | BK-PAY-04 | Support deposits and outstanding balances | Booking and payment statuses remain independent |
153
+ | BK-PAY-05 | Support refunds and partial refunds | Refund totals cannot exceed captured amount; allowed cancellation policy settlements call the provider after the lifecycle write with idempotency and a manual-review fallback |
154
+ | BK-PAY-06 | Expose provider capability flags | UI hides or disables unsupported operations with a reason |
155
+ | BK-PAY-07 | Support future providers without schema forks | Provider-specific IDs live in an external-object table or metadata map |
156
+
157
+ The local payment slice also supports Stripe Customer mapping, saved payment
158
+ methods for ordinary PaymentIntents, setup-only SetupIntents, and an explicit
159
+ consented no-show fee adjustment. It does not automatically charge when a
160
+ booking changes to `no_show`.
161
+
162
+ ### Public customer boundary
163
+
164
+ | ID | Requirement | Acceptance condition |
165
+ |---|---|---|
166
+ | BK-CUSTOMER-01 | Expose public catalogue and availability safely | Public DTOs omit tenant, customer, provider, and credential internals and responses are no-store/noindex |
167
+ | BK-CUSTOMER-02 | Create a short-lived customer hold | Request is tenant-bound, idempotent, validates email and booking-update consent, and returns an expiry without exposing internal allocation data |
168
+ | BK-CUSTOMER-03 | Start checkout and recover payment returns | Payment return state is informational; only verified webhook/provider read changes payment truth |
169
+ | BK-CUSTOMER-04 | Queue customer lifecycle notifications | Confirmation, cancellation, reschedule, reminder, and accepted/pending refund intents use the allowlisted outbox templates and are not duplicated on idempotent retries |
170
+ | BK-CUSTOMER-05 | Fail closed at the public host boundary | Host supplies origin/CSRF, rate-limit, bot, persistence, and opaque return-token controls |
171
+
172
+ ### Workspace and reporting
173
+
174
+ | ID | Requirement | Acceptance condition |
175
+ |---|---|---|
176
+ | BK-UI-01 | Serve a dedicated booking workspace | Route is authenticated, noindex, and visually consistent with MaggieDash |
177
+ | BK-UI-02 | Provide overview, calendar, bookings, customers, services, staff, resources, payments, settings, and audit screens | Every screen has loading, empty, error, and permission states |
178
+ | BK-UI-03 | Preserve filters in URL state | Reloading or sharing a permitted URL keeps the view context |
179
+ | BK-UI-04 | Keep one scroll owner in dialogs | Long booking, refund, and configuration dialogs do not create nested page scroll |
180
+ | BK-UI-05 | Surface pending states honestly | No UI says “confirmed” or “paid” from a redirect alone |
181
+ | BK-UI-06 | Show audit history for material changes | Actor, reason, correlation ID, before/after summary, and timestamp are visible |
182
+
183
+ ## 8. Non-functional requirements
184
+
185
+ | Area | Target |
186
+ |---|---|
187
+ | Correctness | No confirmed double allocations under the supported concurrency test |
188
+ | Idempotency | Repeating a request with the same key returns the original result |
189
+ | Availability | Manager read APIs degrade to a safe read-only state when optional adapters fail |
190
+ | Performance | P95 availability query under 500 ms for the launch dataset; measure with a fixed fixture |
191
+ | Security | No secret or payment credential in HTML, props, browser environment, logs, or audit payloads |
192
+ | Privacy | Customer data is tenant-scoped, minimized, exportable, and deletable according to policy |
193
+ | Accessibility | Keyboard navigation, focus management, labels, contrast, and reduced motion are tested |
194
+ | Time | UTC instants in storage; venue timezone required for wall-clock input |
195
+ | Observability | Correlation ID through request, booking event, payment attempt, webhook, and notification |
196
+ | Recovery | Failed webhooks, notifications, and provider reads are retryable without duplicate business effects |
197
+
198
+ ## 9. Success metrics
199
+
200
+ - At least 99.99% of accepted booking requests result in one and only one
201
+ allocation or one explicit failure outcome.
202
+ - Zero payment-success records without a matching verified provider event or
203
+ provider reconciliation result.
204
+ - Zero cross-tenant reads in automated authorization tests.
205
+ - 100% of production payment webhooks have signature, idempotency, and replay
206
+ evidence.
207
+ - A new payment adapter can pass the provider contract without changing booking
208
+ tables or booking state transitions.
209
+ - A store manager can complete daily open, operate, and close flows without
210
+ leaving the booking workspace for normal operations.
211
+
212
+ ## 10. Release acceptance
213
+
214
+ The MVP is not releasable until:
215
+
216
+ 1. Host adapter conformance passes for every required booking endpoint.
217
+ 2. Email/password login and role checks pass against a real host session.
218
+ 3. Availability, hold expiry, database conflict, and DST tests pass.
219
+ 4. Stripe test-mode payment, failed payment, duplicate webhook, delayed
220
+ webhook, refund, no-show adjustment, and reconciliation tests pass against
221
+ the real host account.
222
+ 5. Browser tests pass at desktop and mobile widths with no console errors.
223
+ 6. Security review confirms no card data or raw provider credentials reach the
224
+ public browser bundle. If the optional dashboard-managed Stripe path is
225
+ enabled, only owner/manager HTTPS submissions are accepted, verified before
226
+ persistence, stored as authenticated ciphertext, and never returned.
227
+ 7. A sanitized runtime evidence bundle is stored and the release gate reports
228
+ the exact build, schema, adapter, and fixture revisions.