@rubytech/taskmaster 1.19.0 → 1.19.1

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.
@@ -12,6 +12,11 @@ Before doing anything else:
12
12
  1. Read `SOUL.md` — this is who you are
13
13
  2. Read `IDENTITY.md` — your role and boundaries
14
14
  3. Check memory for recent bookings, driver issues, and any pending follow-ups
15
+ 4. Ensure the stall-detection cron job is configured — call `cron` with action: `list` and check for a job named "Ride Booking Stall Check". If it does not exist, create it:
16
+ - action: `add`, name: `Ride Booking Stall Check`, schedule: `*/10 * * * *` (every 10 minutes)
17
+ - sessionTarget: `isolated`, wakeMode: `next-heartbeat`
18
+ - message: `Run the ride booking stall check.`
19
+ - deliver: false (operator does not need a notification unless a stall is found)
15
20
 
16
21
  ## Tools
17
22
 
@@ -64,61 +69,124 @@ response_rate: 0
64
69
 
65
70
  ## Ride Dispatch Processing
66
71
 
67
- Messages prefixed with `[System: Ride Dispatch — ...]` are automated dispatches from the ride-dispatch hook. They represent privileged operations that the public agent cannot perform (it has no `contact_lookup` or `message` tools). Process each dispatch type as instructed.
72
+ Messages prefixed with `[System: Ride Dispatch — ...]` are automated dispatches from the ride-dispatch hook. Each message contains fully prescriptive steps follow them exactly. The steps in this section are a reference summary; the message body is authoritative.
73
+
74
+ ### Shared File Locations
75
+
76
+ All cross-agent state flows through structured files. Never rely on session memory to recover data another agent should provide.
77
+
78
+ | File | Written by | Read by |
79
+ |------|-----------|---------|
80
+ | `shared/dispatch/{jobId}-trip-request.md` | Public agent | Hook → Admin |
81
+ | `shared/dispatch/{jobId}-booking-confirm.md` | Public agent | Hook → Admin |
82
+ | `shared/active-negotiations/{phone-digits}.md` | Admin | Hook (driver reply routing) |
83
+ | `shared/offers/{jobId}.md` | Admin | Public agent (when tourist selects) |
84
+ | `shared/bookings/{jobId}.md` | Admin | Admin (post-payment) |
85
+ | `shared/state/{tourist-phone}-active.md` | Public agent | Public agent (job ID lookup) |
68
86
 
69
87
  ### Trip Request
70
88
 
71
- When you receive `[System: Ride Dispatch — Trip Request]`:
89
+ When you receive `[System: Ride Dispatch — Trip Request]`, the message body contains step-by-step instructions. Summary:
72
90
 
73
- 1. Call `contact_lookup` to get the driver roster
74
- 2. For each driver, call `memory_get` on `drivers/{name}.md` to check their status
75
- 3. Select up to 3 idle drivers, preferring those with route history for the requested route
91
+ 1. `contact_lookup` get all drivers
92
+ 2. `memory_get` on `drivers/{name}.md` for each → check `status` field
93
+ 3. Select up to 3 with `status: idle`, preferring route history matches
76
94
  4. For each selected driver:
77
- - Update their status to `awaiting_response` via `memory_write`
78
- - Write `shared/active-negotiations/{driver-phone-digits}.md` with `job_id`, `driver_name`, and `contacted_at`
79
- 5. Message each driver in Swahili via the `message` tool with route details, pickup time, passengers, and job ID
80
- 6. When driver replies arrive (dispatched as `[System: Ride Dispatch Driver Reply]`), compile offers
81
- 7. Message the tourist at `tourist_phone` using the `message` tool with `accountId` from this dispatch
82
- - Include up to 3 offers: fare, vehicle type, driver rating, estimated journey time
83
- - Do NOT reveal driver name, phone, or plate those are gated by payment
84
- - Cross-agent echo will relay the WhatsApp message to the tourist's active session automatically
85
- - Do NOT write a dispatch file for the offers — message directly
95
+ - `memory_write` to `drivers/{name}.md` set `status: awaiting_response`, `current_booking: {jobId}`
96
+ - `memory_write` to `shared/active-negotiations/{phone-digits}.md`
97
+ 5. `message` each driver in Swahili fare enquiry, not confirmed job (exact text in dispatch)
98
+ 6. `message` the tourist acknowledged, waiting for quotes (exact text in dispatch)
99
+ 7. On each driver reply: record quote, then when all in (or after 5 min):
100
+ - `memory_write` to `shared/offers/{jobId}.md` (prescribed format below)
101
+ - `message` the tourist with formatted options (exact text in dispatch)
102
+
103
+ **Offers file format** (`shared/offers/{jobId}.md`):
104
+
105
+ ```
106
+ # Offers: BGL-XXXX
107
+ job_id: BGL-XXXX
108
+ status: ready
109
+ tourist_phone: [tourist phone]
110
+ offer_1_driver: [name]
111
+ offer_1_phone: [digits only, no +]
112
+ offer_1_vehicle: [vehicle type]
113
+ offer_1_rating: [rating]
114
+ offer_1_fare: [USD amount as number]
115
+ offer_1_duration: [estimated minutes as number]
116
+ offer_2_driver: [if applicable]
117
+ offer_2_phone: [if applicable]
118
+ offer_2_vehicle: [if applicable]
119
+ offer_2_rating: [if applicable]
120
+ offer_2_fare: [if applicable]
121
+ offer_2_duration: [if applicable]
122
+ [offer_3_* fields if applicable]
123
+ ```
124
+
125
+ Sort by fare ascending. Omit fields for offers you don't have.
86
126
 
87
127
  ### Booking Confirmation
88
128
 
89
- When you receive `[System: Ride Dispatch — Booking Confirmation]`:
129
+ When you receive `[System: Ride Dispatch — Booking Confirmation]`, the message body contains step-by-step instructions. Summary:
130
+
131
+ 1. Load the `stripe` skill. Create a Checkout Session for $2.00 USD:
132
+ - `success_url: https://zanzibar.beagle.taxi/booking-confirmed`
133
+ - `cancel_url: https://zanzibar.beagle.taxi/booking-cancelled`
134
+ - `metadata: booking_id, tourist_phone, account_id`
135
+ 2. `memory_write` to `shared/bookings/{jobId}.md` (prescribed format below)
136
+ 3. `message` the tourist with the payment link (exact text in dispatch)
137
+ 4. Clear `shared/active-negotiations/{phone-digits}.md` for unselected drivers; reset their `drivers/{name}.md` status to `idle`
90
138
 
91
- 1. Load the `stripe` skill and generate a Checkout Session for the booking fee
92
- - Set metadata: `booking_id`, `tourist_phone`, `account_id` (for webhook routing)
93
- 2. Message the tourist at `tourist_phone` using the `message` tool with the payment link and booking terms
94
- - Cross-agent echo relays the message to the tourist's active session automatically
95
- 3. Record booking details in `shared/bookings/{job-id}.md` via `memory_write`
96
- - Include `tourist_phone` for post-payment messaging
97
- 4. Clear `shared/active-negotiations/{phone}.md` for drivers NOT selected
139
+ **Booking record format** (`shared/bookings/{jobId}.md`):
140
+
141
+ ```
142
+ # Booking: BGL-XXXX
143
+ status: payment_pending
144
+ tourist_phone: [phone]
145
+ tourist_name: [name]
146
+ driver_name: [name]
147
+ driver_phone: [digits only]
148
+ vehicle: [vehicle type]
149
+ plate: [plate]
150
+ pickup: [location]
151
+ destination: [location]
152
+ date: [date]
153
+ time: [time]
154
+ fare: [USD amount]
155
+ payment_url: [Stripe URL]
156
+ created_at: [timestamp]
157
+ pin:
158
+ ```
98
159
 
99
160
  ### Payment Confirmed
100
161
 
101
- When you receive `[System: Ride Dispatch — Payment Confirmed]`:
162
+ When you receive `[System: Ride Dispatch — Payment Confirmed]`, the message body contains step-by-step instructions. Summary:
102
163
 
103
- 1. Read the booking record at `shared/bookings/{job-id}.md` for driver details and `tourist_session_key`
104
- 2. Generate the pickup PIN and QR code (see `references/pin-qr.md`)
105
- 3. Message the tourist at `tourist_phone` (from the booking record) using the `message` tool with driver details and pickup PIN
106
- - Cross-agent echo relays to the tourist's active session automatically
107
- 4. Message the driver with passenger name, pickup time/location, fare, and QR code URL
108
- 5. Update the booking record status to `confirmed`
109
- 6. Clear the active negotiation file for the confirmed driver
164
+ 1. `memory_get` on `shared/bookings/{bookingId}.md` get all driver and tourist details
165
+ 2. Generate a random 4-digit PIN (1000–9999)
166
+ 3. QR code URL: `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=PIN%3A+{pin}`
167
+ 4. `message` the tourist with driver details and PIN (exact text in dispatch)
168
+ 5. `message` the driver with the QR code image and booking details (exact text in dispatch)
169
+ 6. `memory_write` to `shared/bookings/{bookingId}.md` set `status: confirmed`, set `pin: {pin}`
170
+ 7. `memory_write` to `shared/active-negotiations/{driver-phone-digits}.md` empty content
110
171
 
111
172
  ### Driver Reply
112
173
 
113
- When you receive `[System: Ride Dispatch — Driver Reply]`:
174
+ When you receive `[System: Ride Dispatch — Driver Reply]`, the message body contains step-by-step instructions. Summary:
175
+
176
+ **If quoting a fare:**
177
+ 1. `contact_lookup` → match driver by phone digits → get name and vehicle
178
+ 2. `memory_write` to `drivers/{name}.md` — record quote under `## Quotes`
179
+ 3. Count active negotiations for this job — if all replied or 5 min elapsed:
180
+ - Write `shared/offers/{jobId}.md` with all quotes (format above)
181
+ - `message` the tourist with formatted options (exact text in dispatch — read `tourist_phone` from the offers file)
114
182
 
115
- 1. If the driver is quoting a fare: record it in their memory profile (`memory_write` on `drivers/{name}.md`)
116
- 2. When all expected quotes are received (or after a reasonable wait): compile the offers and message the tourist directly using the `message` tool at the `tourist_phone` from the trip-request earlier in this session do NOT write a dispatch file
117
- 3. If the driver is declining: update their status in memory to `idle` and delete their `shared/active-negotiations/{phone-digits}.md` file
183
+ **If declining:**
184
+ 1. `memory_write` to `drivers/{name}.md` — set `status: idle`, `current_booking: null`
185
+ 2. `memory_write` to `shared/active-negotiations/{phone-digits}.md` — empty content
118
186
 
119
187
  ### Active Negotiation Index
120
188
 
121
- When contacting drivers, write `shared/active-negotiations/{phone-digits}.md` for each:
189
+ `shared/active-negotiations/{phone-digits}.md` format:
122
190
 
123
191
  ```
124
192
  job_id: BGL-XXXX
@@ -126,17 +194,65 @@ driver_name: [name]
126
194
  contacted_at: [timestamp]
127
195
  ```
128
196
 
129
- Clear these files when:
130
- - A driver declines or is not selected
131
- - The booking is confirmed (keep only the selected driver's file until pickup completes)
132
- - A negotiation expires with no response
133
-
134
- This index enables the hook to route driver WhatsApp replies to the correct ride session without requiring drivers to include job IDs in their messages.
197
+ The hook uses this file to route incoming driver WhatsApp replies to the correct admin ride session, without requiring drivers to include the job ID in their messages. Clear by writing empty content (not deletion) when a driver declines, is not selected, or after the booking is confirmed.
135
198
 
136
199
  ---
137
200
 
138
201
  ## Operational Focus Areas
139
202
 
203
+ ### Ride Booking Stall Check
204
+
205
+ When you receive `Run the ride booking stall check.` (from the stall-detection cron), execute every step below.
206
+
207
+ **STEP 1 — Scan for trip requests without booking records**
208
+ Call `memory_search` on `shared/dispatch/` for all files matching `*-trip-request.md`.
209
+ For each job ID found, check if `shared/bookings/{jobId}.md` exists via `memory_get`.
210
+ If the booking record exists (status: payment_pending or confirmed), this job is active — skip it.
211
+ If no booking record, the job may be stalled — proceed to STEP 2.
212
+
213
+ **STEP 2 — Classify the stall**
214
+ For each stalled job ID:
215
+
216
+ a. Read `shared/dispatch/{jobId}-trip-request.md` to get `tourist_phone`, `pickup`, `destination`, `date`, `time`, `created_at` (or use file modification time).
217
+
218
+ b. If the trip request is less than 15 minutes old: skip — it may still be in progress.
219
+
220
+ c. Check if `shared/offers/{jobId}.md` exists via `memory_get`.
221
+
222
+ **STEP 3 — Recover (offers file exists)**
223
+ If `shared/offers/{jobId}.md` exists, the tourist likely confirmed but the booking-confirm dispatch was never written. Recover automatically:
224
+ - Read the offers file to get `tourist_phone` and all offer fields.
225
+ - Assume the tourist selected Option 1 (the lowest-fare option — already sorted ascending).
226
+ - Write `shared/dispatch/{jobId}-booking-confirm.md` via `memory_write`:
227
+ ```
228
+ # Dispatch: Booking Confirmation
229
+ job_id: {jobId}
230
+ phase: booking-confirm
231
+ tourist_phone: {tourist_phone from offers file}
232
+ tourist_name: unknown
233
+ selected_offer: Option 1 (auto-recovered)
234
+ driver_name: {offer_1_driver}
235
+ driver_phone: {offer_1_phone}
236
+ vehicle: {offer_1_vehicle}
237
+ fare: {offer_1_fare}
238
+ account_id: beagle-zanzibar
239
+ ```
240
+ - This triggers the hook, which dispatches the booking confirmation to you in the ride session for that job.
241
+
242
+ **STEP 4 — Alert (no offers file, job >30 minutes old)**
243
+ If `shared/offers/{jobId}.md` does NOT exist and the trip request is >30 minutes old, the negotiation stalled before drivers responded.
244
+ - Include this text in your response output (do NOT use the `message` tool — this is an internal alert for the operator, not an outbound WhatsApp message):
245
+ `STALL ALERT [{jobId}]: No driver quotes received for {pickup} → {destination} at {time}. Tourist: {tourist_phone}. Please investigate or retry.`
246
+ - The cron job posts your response to the operator's control panel session automatically.
247
+
248
+ If the job is between 15 and 30 minutes old with no offers file: no action needed — negotiations can take time.
249
+
250
+ **STEP 5 — Log outcome**
251
+ Write a brief note via `memory_write` to `shared/stall-checks/{date}.md`:
252
+ ```
253
+ {timestamp}: Checked {N} stalled jobs. Recovered: {list of jobIds}. Alerted: {list of jobIds}. No action needed: {list of jobIds}.
254
+ ```
255
+
140
256
  ### Bookings
141
257
  - Booking records live at `bookings/{job-id}.md` in memory. Each tracks: route, fare, driver, status, timestamps, ratings, and any substitution flag.
142
258
  - Summarise recent booking activity: confirmed, completed, cancelled, no-shows
@@ -89,33 +89,52 @@ passengers: [count]
89
89
  luggage: [description]
90
90
  special_requests: [any requests or "none"]
91
91
  fare_estimate: [range from knowledge base or "unknown"]
92
- account_id: [your account ID]
92
+ account_id: beagle-zanzibar
93
93
  ```
94
94
 
95
+ The `account_id` must be `beagle-zanzibar` — the WhatsApp account ID. Do not use your agent ID here.
96
+
97
+ Then write a second file via `memory_write` to `shared/state/{tourist-phone}-active.md`:
98
+
99
+ ```
100
+ job_id: BGL-XXXX
101
+ tourist_phone: [tourist phone]
102
+ ```
103
+
104
+ This gives you a reliable, direct-read source for the job ID when the tourist confirms. Do not rely on memory search to recover the job ID later.
105
+
95
106
  ### Step 5 — Notify the tourist
96
107
 
97
108
  After writing the dispatch file, tell the tourist: "I'm reaching out to our drivers now. I'll have quotes for you shortly." Do not send this before writing the dispatch file.
98
109
 
99
110
  ### Step 6 — Present offers
100
111
 
101
- When driver offers appear in your conversation as a `[System: Ride Dispatch Driver Offers]` message, present up to 3 competing offers to the tourist: fare, driver rating, vehicle type, estimated journey time. No driver personal details at this stage — name, phone, and plate are gated by payment. See `references/ride-matching.md` for formatting.
112
+ When the operations agent messages the tourist with driver quotes, the message is echoed into your conversation. It will include the job ID (e.g. `[BGL-0002]`) and up to 3 numbered options. Present the options to the tourist clearly: fare, vehicle type, driver rating, estimated journey time. No driver personal details — name, phone, and plate are gated by payment. See `references/ride-matching.md` for formatting.
102
113
 
103
114
  ### Step 7 — Confirm booking
104
115
 
105
- When the tourist chooses an offer, confirm the details and write a booking confirmation dispatch via `memory_write` to `shared/dispatch/{job-id}-booking-confirm.md`:
116
+ When the tourist chooses an offer:
117
+
118
+ 1. Call `memory_get` on `shared/state/{tourist-phone}-active.md` to get the job ID. Do not use `memory_search` — this file was written at Step 4 and contains the job ID directly.
119
+ 2. Call `memory_get` on `shared/offers/{job-id}.md` to get the full offer details (driver name, driver phone, vehicle, rating, fare, duration). This file was written by the operations agent after collecting driver quotes.
120
+ 3. Write the booking confirmation dispatch via `memory_write` to `shared/dispatch/{job-id}-booking-confirm.md`:
106
121
 
107
122
  ```
108
123
  # Dispatch: Booking Confirmation
109
124
  job_id: BGL-XXXX
110
125
  phase: booking-confirm
111
126
  tourist_phone: [same phone used in trip-request]
112
- tourist_name: [name]
113
- driver_name: [selected driver name from offer]
114
- driver_phone: [selected driver phone from offer]
115
- fare: [agreed fare]
116
- account_id: [your account ID]
127
+ tourist_name: [name if known]
128
+ selected_offer: Option 1
129
+ driver_name: [offer_1_driver from shared/offers/{job-id}.md]
130
+ driver_phone: [offer_1_phone from shared/offers/{job-id}.md]
131
+ vehicle: [offer_1_vehicle from shared/offers/{job-id}.md]
132
+ fare: [offer_1_fare from shared/offers/{job-id}.md]
133
+ account_id: beagle-zanzibar
117
134
  ```
118
135
 
136
+ Substitute the correct offer number (1, 2, or 3) based on the tourist's selection. All field values come directly from the offers file — do not invent or guess any driver details.
137
+
119
138
  ### Step 8 — Payment
120
139
 
121
140
  The operations agent will generate a Stripe payment link and relay it to your conversation as a `[System: Ride Dispatch — Payment Link]` message. Present the payment link and terms to the tourist. If the tourist has questions about payment, explain the booking fee. Payment confirmation is automatic via Stripe webhook — the tourist does not need to tell you they've paid.
@@ -0,0 +1,20 @@
1
+ {
2
+ "templateId": "beagle-zanzibar-stall-check",
3
+ "name": "Ride Booking Stall Check",
4
+ "description": "Monitors active ride negotiations every 10 minutes. Automatically recovers stalled bookings where the tourist confirmed but the dispatch was missed. Alerts the operator if driver quotes are not received within 30 minutes.",
5
+ "enabled": true,
6
+ "agentId": "beagle-zanzibar-admin",
7
+ "schedule": { "kind": "cron", "expr": "*/10 * * * *" },
8
+ "sessionTarget": "isolated",
9
+ "wakeMode": "next-heartbeat",
10
+ "payload": {
11
+ "kind": "agentTurn",
12
+ "message": "Run the ride booking stall check.",
13
+ "deliver": false,
14
+ "bestEffortDeliver": false
15
+ },
16
+ "isolation": {
17
+ "postToMainMode": "summary",
18
+ "postToMainPrefix": "Stall Check"
19
+ }
20
+ }
@@ -2,7 +2,12 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- Each confirmed booking gets a unique 4-digit pickup PIN. The tourist holds the PIN. The driver receives a QR code image that encodes it. At pickup, the tourist scans the QR code or asks the driver to read the PIN aloud — both parties should see the same number.
5
+ Each confirmed booking gets a unique 4-digit pickup PIN. The tourist receives the PIN via WhatsApp. The driver receives a QR code image encoding it. At pickup, the tourist either:
6
+
7
+ - Scans the driver's QR code — phone camera shows the PIN inline, tourist confirms it matches their message (works offline)
8
+ - Or simply asks the driver to read the number aloud — driver reads it from the caption on their WhatsApp message
9
+
10
+ Both methods require no internet and no app.
6
11
 
7
12
  ## Generating the PIN
8
13
 
@@ -16,16 +21,24 @@ pin: 4827
16
21
 
17
22
  ## Generating the QR Code Image
18
23
 
19
- Construct the QR image URL from the PIN:
24
+ Encode the PIN as labelled plain text — NOT a URL. Plain text QR codes display inline in the phone camera without opening any app or requiring internet.
25
+
26
+ Format:
27
+
28
+ ```
29
+ PIN: {pin}
30
+ ```
31
+
32
+ QR image URL:
20
33
 
21
34
  ```
22
- https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={pin}
35
+ https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=PIN%3A+{pin}
23
36
  ```
24
37
 
25
38
  Example — PIN 4827:
26
- `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=4827`
39
+ `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=PIN%3A+4827`
27
40
 
28
- This URL returns a PNG image of the QR code. No separate API call is needed to generate it.
41
+ This URL returns a PNG image. When the tourist scans it, their phone camera displays `PIN: 4827` immediately — no browser, no internet needed.
29
42
 
30
43
  ## Sending to the Driver
31
44
 
@@ -35,17 +48,17 @@ Use the `message` tool to send the QR code as an inline WhatsApp image:
35
48
  action: send
36
49
  channel: whatsapp
37
50
  target: {driver_phone_number}
38
- media: https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={pin}
39
- caption: [BGL-XXXX] Your passenger's verification code. Show this QR code at pickupyour passenger will scan it to confirm you're the right driver.
51
+ media: https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=PIN%3A+{pin}
52
+ caption: [BGL-XXXX] Your pickup PIN is {pin}. When you meet your passenger, tell them this number. Say: "Your PIN is {pin}." They will confirm it matches theirs. The QR code above is a backup they can scan it instead if they prefer.
40
53
  ```
41
54
 
42
- The driver opens WhatsApp, sees the QR code image inline, and shows their phone screen to the tourist at pickup.
55
+ The PIN is included in the caption so the driver can read it aloud if the tourist prefers that.
43
56
 
44
57
  ## Sending to the Tourist
45
58
 
46
59
  Send the PIN with an explanation alongside the other driver details (in the main conversation):
47
60
 
48
- > "Your driver verification PIN is **{pin}**. When your driver arrives, ask them to show their QR code — scan it with your phone camera and it should display your PIN. Or ask the driver to read the number aloud. If it matches, you've got the right driver. Works offline — no internet needed."
61
+ > "Your pickup PIN is **{pin}**. When your driver arrives, either scan their QR code (your camera will show the number instantly) or just ask them to read it out — it should match. If it does, you're in the right car."
49
62
 
50
63
  ## Timing
51
64
 
@@ -34,38 +34,48 @@ line_items[0][price_data][product_data][description]={route} on {date}
34
34
  line_items[0][quantity]=1
35
35
  metadata[booking_id]={job_id}
36
36
  metadata[tourist_phone]={tourist_phone}
37
+ metadata[account_id]={whatsapp_account_id}
37
38
  metadata[negotiated_fare]={fare_usd}
39
+ success_url=https://zanzibar.beagle.taxi/booking-confirmed
40
+ cancel_url=https://zanzibar.beagle.taxi/booking-cancelled
38
41
  ```
39
42
 
40
- The Stripe secret key is the `stripe` key stored in your API key config (`sk_test_...` for test mode, `sk_live_...` for production).
43
+ The `stripe` API key from `api_keys` provides the secret key.
41
44
 
42
- The response includes a `url` field — this is the hosted checkout page to send the tourist.
45
+ The response includes a `url` field — the hosted checkout page to send the tourist.
46
+
47
+ ## Metadata Fields
48
+
49
+ | Field | Purpose |
50
+ |-------|---------|
51
+ | `booking_id` | Links payment to the booking record (`BGL-XXXX`) |
52
+ | `tourist_phone` | For post-payment messaging (E.164 format) |
53
+ | `account_id` | WhatsApp account ID for delivery routing |
54
+ | `negotiated_fare` | Full fare amount (paid to driver, not via Stripe) |
55
+
56
+ These metadata fields are returned in webhook events, enabling the gateway to route the payment confirmation to the correct booking workflow.
43
57
 
44
58
  ## Sending the Link
45
59
 
46
- Send the tourist the checkout URL with context:
60
+ Message the tourist with the checkout URL:
47
61
 
48
62
  > "To confirm your booking, pay the $[fee] booking fee here: [url]
49
63
  > This locks in your ride. You pay $[fare] directly to your driver at the end of the journey."
50
64
 
51
- ## Verifying Payment
52
-
53
- Before releasing driver details (Phase 5), verify the session is paid:
65
+ ## Adaptive Pricing (Local Currency Display)
54
66
 
55
- ```
56
- GET https://api.stripe.com/v1/checkout/sessions/{session_id}
57
- Authorization: Bearer {stripe_secret_key}
58
- ```
67
+ Stripe's adaptive pricing is enabled by default. Tourists in the UK, EU, or other non-USD regions will see the booking fee in their local currency (e.g. £1.50 instead of $2.00 — the FX equivalent). The amount you receive is always the USD equivalent after conversion.
59
68
 
60
- Check `payment_status == "paid"`. If not yet paid, wait for the tourist to say they've paid then verify. Do not take their word alone; always confirm with the API before proceeding.
69
+ This is intentional and improves the tourist experience no action needed. If the operator wants to force USD for all customers, they can disable it: Stripe Dashboard Settings Adaptive pricing off.
61
70
 
62
71
  ## What to Store in the Booking Record
63
72
 
64
- Add these fields to `bookings/{job-id}.md` when the session is created and confirmed:
73
+ Add to `shared/bookings/{job-id}.md`:
65
74
 
66
75
  ```
67
76
  stripe_session_id: cs_...
68
- booking_fee_usd: 2.50
69
- payment_status: pending → paid (update when confirmed)
70
- paid_at: [timestamp from session]
77
+ booking_fee_usd: X.XX
78
+ payment_status: pending
71
79
  ```
80
+
81
+ Update `payment_status` to `paid` and add `paid_at` timestamp when the webhook confirms payment.