From d89218be15f390863982986a59e9a22e387789ab Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Fri, 27 Mar 2026 18:29:12 -0400 Subject: [PATCH] feat(leads): implement Stripe payment component for exhibit licenses Full implementation of ae_comp__exhibit_payment.svelte (was a 9-line stub). Reads Stripe config from $ae_loc.site_cfg_json per-event. License tier selector (1/3/6/10 users) uses {#key} remount pattern to work around stripe-buy-button web component ignoring attribute changes after mount. Three states: paid confirmation (priority=true), not-configured hint, payment form. client_reference_id=exhibit_id ties payments to booth records. TypeScript declaration for stripe-buy-button added to app.d.ts via svelte/elements augmentation. exhibit_id prop wired in +page.svelte and ae_tab__manage.svelte. Co-Authored-By: Claude Sonnet 4.6 --- .../MODULE__AE_Events_Exhibitor_Leads.md | 7 + documentation/TODO__Agents.md | 12 +- src/app.d.ts | 12 ++ .../leads/exhibit/[exhibit_id]/+page.svelte | 2 +- .../ae_comp__exhibit_payment.svelte | 182 +++++++++++++++++- .../[exhibit_id]/ae_tab__manage.svelte | 2 +- 6 files changed, 206 insertions(+), 11 deletions(-) diff --git a/documentation/MODULE__AE_Events_Exhibitor_Leads.md b/documentation/MODULE__AE_Events_Exhibitor_Leads.md index 73888d4c..f8d81239 100644 --- a/documentation/MODULE__AE_Events_Exhibitor_Leads.md +++ b/documentation/MODULE__AE_Events_Exhibitor_Leads.md @@ -260,3 +260,10 @@ Guard in [ae_tab__manage.svelte](src/routes/events/[event_id]/(leads)/leads/exhi - Export endpoint: `GET /v3/action/event_exhibit/{id}/tracking_export` — requires `leads_api_access` - Custom questions are stored per-exhibit in `leads_custom_questions_json` (not global) - The exhibitor landing page link format: `/events/[event_id]/leads/exhibit/[exhibit_exhibit_id]/` + + +## Old Files for Reference + +@backups/legacy/events_leads_v2/exhibit/[slug]/+page.svelte +@backups/legacy/events_leads_v2/exhibit/[slug]/leads_manage.svelte +@backups/legacy/events_leads_v2/exhibit/[slug]/leads_payment.svelte \ No newline at end of file diff --git a/documentation/TODO__Agents.md b/documentation/TODO__Agents.md index 3e78e009..c657cd7e 100644 --- a/documentation/TODO__Agents.md +++ b/documentation/TODO__Agents.md @@ -48,7 +48,7 @@ Full audit: `src/routes/events/[event_id]/(leads)/` and `src/lib/ae_events/ae_ev - Exhibit search/landing (`/leads/`) — SWR, local + API search, sort - Exhibit detail page — 4-tab layout, sticky header with Add/List toggle, auto-refresh timer - Tab 1 (Start): sign-in via shared passcode OR licensed user (email + passcode) -- Tab 2 (Add): QR scan (rapid vs. qualify mode) + manual badge search; duplicate detection on both +- Tab 2 (Add): QR scan (confirm mode — replaced rapid/qualify) + manual badge search; duplicate/re-enable detection on both - Tab 3 (List): SWR lead list, licensee filter (All / My Leads), sort options, export button - Tab 4 (Manage): admin tools, booth profile edit, passcode, license mgmt, custom questions config, app settings (refresh interval, clear IDB/localStorage, reload) - Lead detail page: view/edit custom question responses, exhibitor notes (TipTap), priority/enable flags @@ -64,8 +64,12 @@ Full audit: `src/routes/events/[event_id]/(leads)/` and `src/lib/ae_events/ae_ev Opt-in model: `allow_tracking` must be explicitly `true` on the badge. Also added `allow_tracking` and `agree_to_tc` to `ae_EventBadge` in `ae_types.ts`. **Demo note:** ensure test badges have `allow_tracking = true` or no one can be added. -- [ ] **Payment component** — `ae_comp__exhibit_payment.svelte` is a stub (Stripe placeholder only); - omit from demo or hide the payment tab via "Show Payment Tab" toggle in Manage settings +- [x] **Payment component** — `ae_comp__exhibit_payment.svelte` fully implemented (2026-03-27). + Reads Stripe config from `$ae_loc.site_cfg_json` (`stripe_publishable_key`, `stripe_btn_1/3/6/10_license`). + License tier selector (1/3/6/10 users) with `{#key}` remount pattern for Stripe web component. + 3 states: paid confirmation (priority=true), admin setup hint / "contact organizer" (no Stripe config), + payment form. `client_reference_id=exhibit_id`. TypeScript declaration in `app.d.ts`. + Stripe keys verified visible in `$ae_loc.site_cfg_json` on dev/demo site. Keys need validity check in Stripe dashboard. - [ ] **End-to-end smoke test** — sign in with shared passcode, scan/search a badge, add a lead, view detail, add notes/responses, export CSV; verify on mobile (Chrome/Safari PWA) - [x] **Install prompt** — PWA install nudge implemented (2026-03-16). `pwa_install.svelte.ts` @@ -81,7 +85,7 @@ Full audit: `src/routes/events/[event_id]/(leads)/` and `src/lib/ae_events/ae_ev - [x] **Remote deploy script:** `aether_container_env/deploy.sh` — SSH-triggered from workstation via `npm run deploy:remote:test/prod`. Handles git pull (ff-only) + docker build + restart. Tested and working on test env. (2026-03-25) - [x] **`.env.default` cleanup:** Removed 16 dead variables, added missing `AE_NETWORK_NAME`/`CONTAINER_DOZZLE`/`AE_DOZZLE_PORT`, parameterized all container names (`CONTAINER_MARIADB`, `CONTAINER_PMA`, `CONTAINER_AE_OPS`) with `:-default` fallbacks in compose. ("Dozzle" = log viewer container.) (2026-03-26) - [ ] **Prod deploy:** Run `npm run deploy:remote:prod` (off-peak). Prerequisites: both repos pushed to Bitbucket ✓; verify `.env.prod` exists in `/srv/apps/prod_aether_app_sveltekit/` on Linode before running. -- [ ] **Bitbucket → API token migration:** Bitbucket is deprecating app passwords — creation disabled 2025-09-09, existing passwords expire 2026-06-09. Migrate git remotes on workstation + Linode to use API tokens before then. See [Bitbucket API tokens docs](https://support.atlassian.com/bitbucket-cloud/docs/api-tokens/). +- [x] **Bitbucket → SSH migration:** Switched all three repos (`aether_app_sveltekit`, `aether_container_env`, `aether_api_fastapi`) to SSH remotes (`git@bitbucket.org`) on workstation. App passwords deprecated — SSH unaffected. (2026-03-27) - [ ] **Branch strategy cleanup:** All environments (test, prod, bak) currently pull from same branches. `deploy.sh` defaults are `ae_app_3x_llm` / `development` — acceptable for now but should establish proper branch separation (e.g. `main`/`master` for prod). - [ ] **Tier 2 deploy (Gitea webhook):** Push-triggered deploys via Gitea webhook → listener on Linode → `deploy.sh`. Deferred until Gitea usage is more established. diff --git a/src/app.d.ts b/src/app.d.ts index 25e7c631..9b354164 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -22,3 +22,15 @@ declare global { // eslint-disable-next-line no-var var native_app: any; } + +// Stripe Buy Button web component — needed so Svelte templates accept the element without TS errors. +declare module 'svelte/elements' { + interface IntrinsicElements { + 'stripe-buy-button': { + 'buy-button-id': string; + 'publishable-key': string; + 'client-reference-id'?: string; + [attr: string]: any; + }; + } +} diff --git a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/+page.svelte b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/+page.svelte index afec9504..60b6f128 100644 --- a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/+page.svelte +++ b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/+page.svelte @@ -478,7 +478,7 @@ function toggle_manage_tab() { {:else if active_tab === 'payment'}
- +
{:else if active_tab === 'list'}
diff --git a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_comp__exhibit_payment.svelte b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_comp__exhibit_payment.svelte index 32ac2e07..f457ba5a 100644 --- a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_comp__exhibit_payment.svelte +++ b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_comp__exhibit_payment.svelte @@ -1,11 +1,183 @@ -
-

Payment & Licensing

-

Placeholder for Stripe integration.

+
+ {#if $lq__exhibit_obj?.priority} + +
+
+ +

Marked as Paid

+
+

+ Thank you for your payment. You have purchased + {$lq__exhibit_obj?.license_max ?? 0} user license(s) + for lead retrieval at this event. +

+ {#if ($lq__exhibit_obj?.leads_device_sm_qty ?? 0) > 0} +

+ Rental device(s): {$lq__exhibit_obj?.leads_device_sm_qty}. + Pick them up at onsite registration. +

+ {:else} +

+ No rental devices. Use your own device(s) with this service. +

+ {/if} +
+ + {:else if !is_stripe_configured} + + {#if $ae_loc.administrator_access} +
+ +
+

Stripe not configured for this site.

+

+ Add stripe_publishable_key, + stripe_btn_1_license, + stripe_btn_3_license, + stripe_btn_6_license, and + stripe_btn_10_license + to Site Config JSON to enable payment. +

+
+
+ {:else} +

+ Online payment is not available at this time. Please contact the event organizer. +

+ {/if} + + {:else} + +
+
+ +

Purchase Licenses

+
+ +
+

+ Each person from your booth who will scan attendee badges needs their own user + license. You can use your own smartphone, tablet, or laptop — rental devices are + not required. +

+
+ + +
+ +

One license per team member who will scan badges.

+
+ + +
+ {#if stripe_publishable_key && btn_payment_id} + + {#key btn_payment_id} + + + {/key} +

+ Payment processed securely via Stripe. Verify quantities on the checkout + page. +

+
+ Note: Payment confirmation may take up to 2 business days + to reflect in your account status. Contact + exhibits@oneskyit.com + with questions. +
+ {:else} +

+ No payment button is configured for this tier. Contact the event organizer. +

+ {/if} +
+
+ {/if}
diff --git a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte index 6efc52c3..d44d2056 100644 --- a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte +++ b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte @@ -428,7 +428,7 @@ function handle_signout() { {#if show_billing}
- +
{/if}