@topy-ai/maggie 0.7.38 → 0.7.41
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.
- package/README-zh-TW.md +82 -6
- package/README.md +99 -7
- package/bin/maggie.js +30 -3
- package/bundled-contracts/maggie-clone/interaction-state-v1.schema.json +26 -0
- package/bundled-contracts/maggie-content/provenance-v1.schema.json +20 -0
- package/bundled-contracts/maggie-design/brand-kit-v1.schema.json +18 -0
- package/bundled-contracts/maggie-design/browser-interactions-v1.schema.json +27 -0
- package/bundled-contracts/maggie-design/css-utility-evidence-v1.schema.json +10 -0
- package/bundled-contracts/maggie-design/dashboard-surface-v1.schema.json +11 -0
- package/bundled-contracts/maggie-design/progressive-enhancement-v1.schema.json +12 -0
- package/bundled-contracts/maggie-design/sample-surface-v1.schema.json +13 -0
- package/bundled-contracts/maggie-design/style-editing-v1.schema.json +35 -0
- package/bundled-contracts/maggie-media/image-generation-policy-v1.json +28 -0
- package/bundled-contracts/maggie-media/video-generation-policy-v1.json +40 -0
- package/bundled-contracts/maggie-media/video-job-v1.schema.json +20 -0
- package/bundled-contracts/maggie-media/video-playback-evidence-v1.schema.json +15 -0
- package/bundled-contracts/maggie-ops/npm11-preflight-v1.schema.json +17 -0
- package/bundled-contracts/maggie-scaffold/host-scaffold-v1.schema.json +25 -0
- package/bundled-contracts/maggie-seo/gsc-readiness-v1.schema.json +19 -0
- package/bundled-contracts/maggie-seo/privacy-origin-evidence-v1.schema.json +11 -0
- package/bundled-contracts/maggie-service-booking/delivery-provider-default-v1.json +8 -0
- package/bundled-contracts/maggie-service-booking/delivery-provider-v1.schema.json +16 -0
- package/bundled-contracts/maggiedash/booking-access-v1.json +5 -4
- package/bundled-contracts/maggiedash/booking-customer-surface-v1.json +12 -1
- package/bundled-contracts/maggiedash/booking-email-templates-v1.json +2 -0
- package/bundled-contracts/maggiedash/booking-host-adapter-v1.json +14 -2
- package/bundled-contracts/maggiedash/booking-runtime.v1.json +41 -0
- package/bundled-contracts/maggiedash/browser-session-v1.schema.json +18 -0
- package/bundled-contracts/maggiedash/content-overrides-v1.schema.json +19 -0
- package/bundled-contracts/maggiedash/execution-board.json +526 -26
- package/bundled-contracts/maggiedash/host-capabilities-v1.schema.json +10 -0
- package/bundled-contracts/maggiedash/public-session-cache-v1.schema.json +17 -0
- package/bundled-contracts/maggiedash/site-structure-v1.schema.json +13 -0
- package/bundled-references/browser-inspection.md +21 -0
- package/bundled-references/maggiedash-booking/ARCHITECTURE.md +218 -0
- package/bundled-references/maggiedash-booking/CURRENT-STATE.md +92 -0
- package/bundled-references/maggiedash-booking/DATA-FLOW.md +143 -0
- package/bundled-references/maggiedash-booking/DATA-MODEL.md +367 -0
- package/bundled-references/maggiedash-booking/DECISIONS.md +94 -0
- package/bundled-references/maggiedash-booking/EXECUTION-BOARD.json +2387 -0
- package/bundled-references/maggiedash-booking/HOST-ADAPTER.md +314 -0
- package/bundled-references/maggiedash-booking/ORAWELLNESS-INTEGRATION-AUDIT.md +227 -0
- package/bundled-references/maggiedash-booking/PAYMENT-GATEWAY.md +267 -0
- package/bundled-references/maggiedash-booking/PRD.md +228 -0
- package/bundled-references/maggiedash-booking/PROGRESS.md +2434 -0
- package/bundled-references/maggiedash-booking/QA-TEST-PLAN.md +235 -0
- package/bundled-references/maggiedash-booking/README.md +271 -0
- package/bundled-references/maggiedash-booking/RUNTIME-OPERATIONS.md +152 -0
- package/bundled-references/maggiedash-booking/SECURITY-COMPLIANCE.md +158 -0
- package/bundled-references/maggiedash-booking/SKILLS-AND-CLI.md +542 -0
- package/bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md +129 -0
- package/bundled-references/maggiedash-booking/TASK-RUNBOOK.md +107 -0
- package/bundled-references/maggiedash-booking/TASKS.md +137 -0
- package/bundled-references/maggiedash-booking/USER-JOURNEYS.md +224 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-dfd.excalidraw +1 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-dfd.mmd +16 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-dfd.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-dfd.svg +1 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.mmd +19 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.svg +1 -0
- package/bundled-references/maggiedash-booking/diagrams/manager-journey.excalidraw +1 -0
- package/bundled-references/maggiedash-booking/diagrams/manager-journey.mmd +11 -0
- package/bundled-references/maggiedash-booking/diagrams/manager-journey.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/manager-journey.svg +1 -0
- package/bundled-references/maggiedash-booking/diagrams/payment-sequence.mmd +20 -0
- package/bundled-references/maggiedash-booking/diagrams/payment-sequence.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/payment-sequence.svg +1 -0
- package/bundled-references/maggiedash-booking/diagrams/system-context.excalidraw +1 -0
- package/bundled-references/maggiedash-booking/diagrams/system-context.mmd +10 -0
- package/bundled-references/maggiedash-booking/diagrams/system-context.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/system-context.svg +1 -0
- package/bundled-skills/maggie-blog/SKILL.md +12 -0
- package/bundled-skills/maggie-blog-bootstrap/SKILL.md +39 -0
- package/bundled-skills/maggie-booking/SKILL.md +112 -18
- package/bundled-skills/maggie-clone/SKILL.md +13 -0
- package/bundled-skills/maggie-deployment/SKILL.md +6 -0
- package/bundled-skills/maggie-design/SKILL.md +64 -3
- package/bundled-skills/maggie-ops/SKILL.md +12 -0
- package/bundled-skills/maggie-seo-geo/SKILL.md +45 -0
- package/bundled-skills/maggie-service-booking/SKILL.md +14 -0
- package/bundled-tools/clis/maggie_analytics.py +43 -1
- package/bundled-tools/clis/maggie_booking.py +161 -26
- package/bundled-tools/clis/maggie_browser_audit.py +99 -3
- package/bundled-tools/clis/maggie_clone.py +46 -1
- package/bundled-tools/clis/maggie_contracts.py +393 -0
- package/bundled-tools/clis/maggie_dash.py +22 -6
- package/bundled-tools/clis/maggie_design.py +84 -12
- package/bundled-tools/clis/maggie_service_booking.py +50 -1
- package/bundled-tools/clis/maggie_workflows.py +387 -0
- package/bundled-tools/clis/site_audit.py +77 -1
- package/bundled-tools/integrations/analytics.md +14 -0
- package/bundled-tools/runtime/site_baseline.py +3 -0
- package/package.json +1 -1
- package/references/browser-inspection.md +21 -0
|
@@ -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.
|