Specification: Workspace-Owned Stripe Credentials for Roaming Payments
TLDR (Solution Summary)
Move roaming payment ownership from Evenergi-managed Stripe credentials to workspace-owned Stripe credentials configured in Manage Workspace settings and used end-to-end in roaming checkout and payment authorization.
- Research brief + implementation plan + run a Stripe-only best-practice discovery and define the agreed delivery plan (including webhook necessity) + implementation starts from an approved plan.
- Workspace > Roaming access > Advanced configuration + add Stripe credential fields (publishable key, secret key, webhook signing secret) + workspace admins can configure their own payment account for roaming sessions.
- GET/PATCH /api/permissions/workspace/{workspace_id}/ocpi-config + extend OCPI config contract with Stripe credential state + Manage Web can read/write credential configuration and show validation state.
- POST /ocpi/create-payment-intent + resolve Stripe credentials by workspace instead of global environment keys + each paid roaming pre-authorization is created in the workspace's own Stripe account.
- POST /ocpi/stripe/webhook + validate webhook signatures and intent ownership against workspace credential context + authorization/capture state changes only apply to the owning workspace.
- GET /emsp/2.2.1/locations/{location_id}?show_ocpi_cfg=true + expose workspace publishable key in roaming OCPI configuration + Roaming Web initializes Stripe checkout with workspace-specific credentials.
- bf-manage-roaming payment bootstrap (RegionProvider/AppRouter) + replace region-default Stripe key behavior for paid sessions with workspace-configured key behavior + drivers are charged against the customer workspace account rather than the Evenergi account.
- Customer onboarding guide + define customer setup steps for Stripe keys and webhook configuration (if required) + customers can self-serve onboarding with support-ready guidance.
1. Summary
- Problem statement:
- Roaming payments currently use platform-level Stripe credentials, so customers cannot run roaming payments under their own Stripe account ownership, reporting, payout, and compliance controls.
- Goal and success criteria:
- Allow each roaming-enabled workspace to configure and use its own Stripe account credentials for paid roaming sessions.
- Success is measured by:
- 100% of new paid roaming payment intents for configured workspaces being created under that workspace Stripe account.
- 0 paid roaming sessions for configured workspaces using Evenergi default Stripe keys.
- No regression to free-session roaming flows.
- What will be built in this phase:
Research brief + implementation plan + assess Stripe-only best-practice architecture, credential lifecycle, and webhook necessity + implementation is sequenced from approved findings.
Workspace > Roaming access > Advanced configuration + credential fields + system admins save workspace Stripe credentials.
Workspace OCPI Config API + Stripe credential fields and validation state + Manage Web can load and patch credential config safely.
Roaming payment intent creation flow + workspace credential resolution + payment intents are created using workspace-owned Stripe account.
Roaming webhook authorization flow + workspace signature validation and ownership checks + webhook events cannot authorize another workspace's intents.
Roaming location bootstrap contract + workspace publishable key exposure + roaming checkout initializes the correct Stripe account.
Roaming checkout loader + workspace-key-based Stripe initialization and explicit error handling + payment cannot start when credentials are missing/invalid.
Customer onboarding guide + Stripe account setup, key entry, and webhook setup instructions (or explicit no-webhook path) + customers can complete setup with predictable outcomes.
- Scope (in/out) for this phase:
- In:
- Research-first Stripe-only architecture decision and implementation plan.
- Manage Web workspace-level Stripe credential management for roaming.
- Core API contract updates for workspace OCPI config and roaming payment flow.
- Roaming Web checkout key source change from region env to workspace config.
- Customer-facing onboarding documentation for Stripe setup and webhook requirements.
- Out:
- Multi-provider payment support.
- Non-roaming billing flows.
- External partner OCPI settlement changes.
- Finance reconciliation and accounting exports.
- Backlog slicing and story-level acceptance criteria.
- Current baseline (as of March 5, 2026):
bf-manage-roaming chooses Stripe publishable key from region environment variables (VITE_STRIPE_KEY_AU|EU|US|CA) via RegionProvider/getRegionData.
bf-manage-core roaming payment orchestration uses global Stripe environment configuration (STRIPE_SECRET / STRIPE_API_KEY and STRIPE_WH_SECRET) for payment intents and webhook verification.
bf-manage-web roaming advanced configuration currently manages only allow_guest_access, allow_user_registration, and pre_auth_cents through GET/PATCH /api/permissions/workspace/{workspace_id}/ocpi-config.
- Future evolution guardrails:
- This phase explicitly supports Stripe only for roaming payments.
- This phase must follow Stripe credential and webhook security best practices.
- This phase must not block future per-workspace key rotation without downtime.
- This phase must preserve ability to support per-workspace regional key variants later.
- Not in scope:
- Database schema design details, infrastructure implementation details, and code-level API implementation specifics.
2. Users and Use Cases
- Primary personas:
- Workspace system admin configuring roaming payment ownership.
- Roaming driver paying for a charging session.
- BetterFleet support/operations troubleshooting payment configuration issues.
- High-level user stories:
- As a workspace system admin, I want to configure workspace Stripe credentials in roaming settings so roaming payments are owned by my organization.
- As a roaming driver, I want checkout to work consistently so I can start paid sessions without payment-account mismatches.
- As support staff, I want clear ownership and configuration status so I can quickly diagnose payment failures.
- Edge cases and failure modes:
- Workspace enables roaming but has no valid Stripe credentials.
- Workspace rotates keys while payment intents are in progress.
- Webhook arrives with valid Stripe structure but wrong workspace context.
- Workspace has free-session tariffs where Stripe should not be required.
- Workspace submits malformed credentials or credentials from the wrong Stripe mode.
3. Conceptual Model Terms and Decisions
Key Terms
| Term |
Definition |
Notes |
| Workspace Stripe Profile |
The payment credential set assigned to one workspace for roaming payments |
Canonical owner of roaming Stripe account context |
| Publishable Key |
Client-safe key used by roaming checkout to initialize Stripe Elements |
Exposed to roaming UI |
| Secret Key |
Server-side Stripe key used for payment intent and capture operations |
Never returned in plaintext after save |
| Webhook Signing Secret |
Workspace secret used to verify Stripe webhook authenticity |
Server-side verification only |
| Credential Health |
Current validity status of workspace Stripe profile for paid roaming sessions |
Used for configuration UI and roaming preconditions |
| Payment Ownership |
The workspace Stripe account under which intents and charges are created |
Must match workspace operating the charger |
| Roaming Checkout Context |
Combined location, tariff, and OCPI config data used to start checkout |
Includes workspace publishable key in this phase |
Decision Ledger
| ID |
Decision |
Rationale |
Alternatives Rejected |
Implications |
| D-001 |
Stripe credentials are owned and configured per workspace |
Aligns billing ownership and payout control with customer |
Keep global platform keys for all workspaces |
Requires workspace-level config lifecycle |
| D-002 |
Secret credentials are write-only in UI/API reads |
Reduces accidental exposure and leakage risk |
Return full secrets for edit convenience |
Requires masked status UX and explicit rotate action |
| D-003 |
Paid roaming requires valid workspace Stripe profile |
Prevents paid-session startup on broken configuration |
Silent fallback to Evenergi account after workspace setup |
Introduces explicit error handling and admin remediation flow |
| D-004 |
Webhook processing must verify both signature and workspace ownership |
Prevents cross-workspace authorization/capture mutation |
Signature-only validation with no workspace ownership checks |
Requires workspace identifiers in payment intent metadata and verification policy |
| D-005 |
Workspace remains the primary ownership boundary in phase 1 |
Matches existing roaming/access configuration boundaries |
Per-depot credential ownership in this phase |
Keeps first rollout simpler, supports later per-depot extension |
| D-006 |
Roaming payments remain Stripe-only in this initiative |
Keeps scope clear and avoids unnecessary abstraction |
Introduce payment-provider abstraction now |
Faster delivery and lower implementation risk |
4. Domain Model and Eventstorming (Conceptual)
- Bounded context and ubiquitous language:
Workspace Configuration context: workspace roaming settings and payment ownership.
Roaming Checkout context: location/tariff bootstrap and checkout initialization.
Roaming Payment Orchestration context: pre-auth, authorization, capture lifecycle.
- Aggregates and entities (abstract):
Workspace Stripe Profile aggregate: workspace, publishable key, secret credential references, credential health.
OCPI Charging Intent aggregate: authorization reference, payment intent identifier, workspace ownership, payment state.
OCPI Session aggregate: charging telemetry and final payment capture linkage.
- Value objects (if relevant):
StripeCredentialSet, CredentialHealthStatus, PaymentOwnershipRef.
- Domain events and their triggers:
WorkspaceStripeCredentialsUpdated when admin saves credentials.
WorkspaceStripeCredentialsValidated when validation passes.
WorkspaceStripeCredentialsValidationFailed when validation fails.
WorkspacePaymentIntentCreated when paid roaming pre-auth succeeds.
WorkspacePaymentIntentAuthorizationReceived when Stripe webhook confirms capturable funds.
WorkspaceSessionPaymentCaptured when session billing is finalized.
- Commands, actors, and policies:
SaveWorkspaceStripeCredentials (workspace admin).
ValidateWorkspaceStripeCredentials (system policy).
RequestRoamingPreAuth (roaming app on driver action).
ProcessStripeAuthorizationWebhook (system policy on provider event).
- Invariants and business rules enforced:
- A paid roaming session references exactly one owning workspace Stripe profile.
- Secret credentials are never exposed in read responses or logs.
- Webhook authorization/capture state transitions are valid only when workspace ownership matches intent metadata.
- Free sessions remain operational without Stripe credential requirement.
- External systems and integrations:
- Stripe checkout and webhook integration remains shared endpoint-based but workspace-context aware.
- Manage Web and Roaming Web consume Workspace OCPI configuration via existing permissions/location APIs.
Interaction Flow
flowchart LR
Admin["Workspace Admin"] --> Cmd1["Save Workspace Stripe Credentials"]
Cmd1 --> E1["WorkspaceStripeCredentialsUpdated"]
E1 --> P1{"Credentials Valid?"}
P1 -->|"Yes"| E2["WorkspaceStripeCredentialsValidated"]
P1 -->|"No"| E3["WorkspaceStripeCredentialsValidationFailed"]
Driver["Roaming Driver"] --> Cmd2["Request Roaming Pre-Auth"]
Cmd2 --> E4["WorkspacePaymentIntentCreated"]
Stripe["Stripe Webhook"] --> Cmd3["Process Authorization Webhook"]
Cmd3 --> E5["WorkspacePaymentIntentAuthorizationReceived"]
E5 --> E6["WorkspaceSessionPaymentCaptured"]
Event Timeline
timeline
title Workspace-Owned Roaming Payment Timeline
WorkspaceStripeCredentialsUpdated: Admin saves workspace Stripe profile
WorkspaceStripeCredentialsValidated: System marks profile ready for paid roaming
WorkspacePaymentIntentCreated: Driver initiates paid roaming session
WorkspacePaymentIntentAuthorizationReceived: Stripe webhook confirms pre-auth
WorkspaceSessionPaymentCaptured: Final session charge captured
Event Dictionary
WorkspaceStripeCredentialsUpdated: Workspace payment credentials changed | defines payment ownership boundary | workspaceId, credentialVersion, updatedBy | triggers credential validation.
WorkspaceStripeCredentialsValidated: Workspace profile is ready for paid sessions | allows paid roaming starts | workspaceId, validatedAt, mode | enables payment intent creation.
WorkspaceStripeCredentialsValidationFailed: Saved profile cannot be used | blocks paid roaming path | workspaceId, failureCode, validatedAt | triggers admin remediation state.
WorkspacePaymentIntentCreated: Pre-auth intent created in workspace Stripe account | starts payment lifecycle | workspaceId, paymentIntentId, authorizationReference | allows session authorization.
WorkspacePaymentIntentAuthorizationReceived: Stripe confirmed capturable amount | allows bind/start/capture flow | workspaceId, paymentIntentId, eventId | updates charging intent state.
WorkspaceSessionPaymentCaptured: Final charge captured at session completion | closes payment lifecycle | workspaceId, ocpiSessionId, capturedAmount | supports reconciliation/reporting.
5. Requirements and Constraints
- Functional requirements:
FR-001: Manage Web must provide workspace-level Stripe credential inputs in roaming advanced configuration for users with roaming update permission.
FR-002: Workspace OCPI config read/write APIs must support Stripe credential configuration and credential health status for a workspace.
FR-003: Secret key and webhook signing secret must be write-only in API/UI read paths and rotatable without exposing previous values.
FR-004: Credential save must run validation and return explicit success/failure outcomes consumable by UI messaging.
FR-005: Roaming location bootstrap payload must include workspace publishable key (or equivalent client initialization value) for paid sessions.
FR-006: Roaming checkout initialization must use workspace publishable key for paid sessions instead of region-default keys.
FR-007: Payment intent creation for paid roaming sessions must use workspace secret credentials and include workspace ownership metadata.
FR-008: Webhook authorization handling must validate signature and workspace ownership before mutating charging-intent state.
FR-009: System must block paid roaming session start when roaming is enabled but credential health is invalid.
FR-010: Free-session roaming flow must remain available and must not require Stripe credential checks.
FR-011: Credential changes and payment ownership decisions must be auditable by workspace and actor.
FR-012: Error responses for configuration and payment bootstrap failures must be explicit enough for admin/support triage.
FR-013: A research brief and implementation plan must be produced first, covering Stripe best-practice credential handling, webhook strategy, and rollout sequencing.
FR-014: Customer onboarding documentation must describe Stripe account setup, required keys, webhook setup steps (if required), and verification/troubleshooting checks.
- Non-functional requirements:
NFR-001: Stripe secrets must not appear in plaintext in API read responses, client storage, or application logs.
NFR-002: Paid roaming payment-intent creation latency must not regress beyond acceptable current behavior for end users.
NFR-003: Credential updates must propagate to paid session creation paths within one minute.
NFR-004: Webhook processing must be idempotent by event identity to avoid duplicate authorization transitions.
NFR-005: Every paid roaming payment intent must be traceable to workspace ownership in operational diagnostics.
- Constraints and assumptions:
- One Stripe account per workspace in this phase.
- Workspace is the ownership boundary in this phase, not depot-level ownership.
- Existing roaming tariff and user-group behavior remains unchanged.
- Free-session behavior remains independent of Stripe.
- Build item coverage mapping:
Research brief + implementation plan build item -> FR-013.
Workspace > Roaming access > Advanced configuration build item -> FR-001, FR-003, FR-004, FR-012, NFR-001.
GET/PATCH /api/permissions/workspace/{workspace_id}/ocpi-config build item -> FR-002, FR-003, FR-004, FR-011, NFR-001, NFR-003.
POST /ocpi/create-payment-intent build item -> FR-007, FR-009, NFR-002, NFR-005.
POST /ocpi/stripe/webhook build item -> FR-008, FR-011, NFR-004, NFR-005.
GET /emsp/2.2.1/locations/{location_id}?show_ocpi_cfg=true build item -> FR-005, FR-012, NFR-003.
bf-manage-roaming payment bootstrap (RegionProvider/AppRouter) build item -> FR-006, FR-009, FR-010, FR-012, NFR-002.
Customer onboarding guide build item -> FR-014.
- Verification notes:
FR-001-FR-004: Validate via workspace settings API/UI integration checks and permission checks.
FR-005-FR-010: Validate through end-to-end roaming paid/free session flows across configured and misconfigured workspaces.
FR-011-FR-012: Validate through audit/log review and typed error response checks.
FR-013: Validate by review/approval of the research brief and implementation plan before build execution.
FR-014: Validate by walkthrough of onboarding documentation with support/customer-success stakeholders.
NFR-001-NFR-005: Validate through security review, observability dashboards, webhook replay/idempotency checks, and latency benchmarks.
6. Interaction and Flow
- User journey or process steps:
- Workspace admin opens
Workspace > Roaming access > Advanced configuration and saves Stripe credential set.
- System validates credentials and marks workspace credential health.
- Driver starts paid roaming session; roaming app loads location OCPI config and initializes checkout with workspace publishable key.
- Core creates payment intent using workspace secret credentials and records workspace ownership metadata.
- Stripe webhook authorizes intent; core validates ownership and advances charging intent state.
- Session ends and final capture is recorded against workspace-owned Stripe account.
Flowchart: Workspace-Owned Roaming Checkout
flowchart TD
A["Admin saves workspace Stripe credentials"] --> B{"Credentials valid?"}
B -->|"No"| C["Set credential health to invalid and show admin error"]
B -->|"Yes"| D["Set credential health to valid"]
D --> E["Driver requests paid roaming session"]
E --> F["Roaming app reads workspace publishable key"]
F --> G["Core creates payment intent using workspace secret key"]
G --> H["Stripe webhook authorization received"]
H --> I{"Workspace ownership matches intent metadata?"}
I -->|"No"| J["Reject event and log security warning"]
I -->|"Yes"| K["Authorize intent and continue session payment lifecycle"]
Sequence Diagram: End-to-End Ownership Flow
sequenceDiagram
participant Admin as Workspace Admin
participant Web as Manage Web
participant Core as Manage Core
participant Roam as Roaming Web
participant Stripe as Stripe
Admin->>Web: Save workspace Stripe credentials
Web->>Core: PATCH workspace ocpi-config
Core-->>Web: Credential health result
Roam->>Core: GET location with ocpi config
Core-->>Roam: Location + workspace publishable key
Roam->>Core: POST create-payment-intent
Core->>Stripe: Create PaymentIntent (workspace secret key)
Stripe-->>Core: PaymentIntent created
Core-->>Roam: clientSecret + charging_intent
Stripe->>Core: Webhook authorization event
Core-->>Core: Verify signature + workspace ownership
Core-->>Roam: Payment authorized for session flow
7. Non-Technical Implementation Approach
- Approach overview:
- Produce a research-first Stripe-only recommendation and implementation plan before engineering changes.
- Align product, security, and support on workspace credential ownership policy.
- Deliver in thin vertical slices across settings, API contract, and roaming payment runtime.
- Pilot with selected roaming-enabled workspaces before broad rollout.
- Delivery sequencing:
- Phase 0: Research Stripe best practices, decide webhook necessity/responsibilities, and publish approved implementation plan.
- Phase 1: Finalize credential model semantics and error taxonomy.
- Phase 2: Introduce workspace credential management UX and API updates.
- Phase 3: Switch payment-intent and webhook processing to workspace-owned credential resolution.
- Phase 4: Switch roaming checkout bootstrap to workspace key source and remove region-default dependency for paid sessions.
- Phase 5: Publish customer onboarding documentation for keys/webhooks and support troubleshooting.
- Phase 6: Roll out with monitoring, support runbook, and fallback playbook for misconfiguration.
- Design considerations:
- Prioritize secure secret handling and operational diagnosability over rapid rollout breadth.
- Preserve free-session continuity to avoid regressions for non-paid roaming paths.
- Dependencies and prerequisites:
- Security approval for credential handling policy.
- Product decision on workspace credential validation behavior and admin UX states.
- Support/operations alignment on incident response for invalid credentials and webhook failures.
8. Open Questions
- Should phase 1 accept raw Stripe secret keys or require restricted keys only?
- Should paid roaming be hard-blocked when credential health is invalid, or should there be an explicit temporary fallback mode?
- Is one publishable key per workspace sufficient, or is per-country/per-region key configuration required now?
- What is the required key-rotation behavior for in-flight sessions (immediate cutover vs overlap window)?
- Should webhook verification support multiple active signing secrets during rotation?
- What credential validation signal is required at save time (format-only vs provider round-trip)?
- Do customers need to register and manage webhooks in their own Stripe account for this design, or can webhook handling remain fully platform-managed?
9. Appendices
- Affected artifact references (current baseline):
bf-manage-web/src/pages/Settings/Workspace/RoamingAccess/AdvancedConfiguration.tsx
bf-manage-web/src/services/workspace.ts
bf-manage-web/src/interfaces/workspace.ts
bf-manage-core/src/api/permissions.py
bf-manage-core/src/workspace/workspace_service.py
bf-manage-core/src/OCPI/router.py
bf-manage-core/src/OCPI/payments/stripe_gateway.py
bf-manage-core/src/vemo_ocpi/emsp/locations/api_v_2_2_1.py
bf-manage-roaming/src/context/regionContext.tsx
bf-manage-roaming/src/router/AppRouter.tsx
bf-manage-roaming/src/services/locations.ts
bf-manage-roaming/src/types/ocpi.ts