Stripe configuration
This guide describes, step by step, how to operate a Stripe account to obtain all the environment variables consumed by the microservices (and the monolith) of Model City. The goal is not just to create the account, but to know how to navigate the Stripe dashboard to extract each concrete value.
Through this guide, the following environment variables will be obtained:
| Variable | Consumed by | Format | Source in Stripe |
|---|---|---|---|
STRIPE_SECRET_KEY | leisure, mobility, monolith and frontend (server-side) | sk_test_… / sk_live_… | Environment secret key |
STRIPE_PUBLISHABLE_KEY | Next.js frontend (baked into the bundle at docker build, mapped to NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY) | pk_test_… / pk_live_… | Environment publishable key |
STRIPE_WEBHOOK_SECRET | leisure (micro) · leisure webhook in the monolith | whsec_… | Event webhook secret |
STRIPE_RESERVATION_WEBHOOK_SECRET | mobility webhook in the monolith | whsec_… | Reservation webhook secret |
STRIPE_PARKING_PRODUCT_ID | Next.js frontend (creates the parking Checkout Session) | prod_… | Parking product ID |
In microservices, leisure and mobility share STRIPE_SECRET_KEY and each
uses its own STRIPE_WEBHOOK_SECRET. In the monolith there is a single
STRIPE_SECRET_KEY and two webhook secrets: STRIPE_WEBHOOK_SECRET (leisure) and
STRIPE_RESERVATION_WEBHOOK_SECRET (mobility) so the same logic can be used in
both ways.
1. Create the account
- Go to
https://dashboard.stripe.com/register. - Enter email, name and password, and verify the confirmation email.
- Set the account country (it determines the currencies and payment methods
available. Project's default currency is
eur). - You don't need to activate the account (the activation/KYC process with tax and bank data) to develop: in test mode you have complete keys and webhooks without activation. Activation is only required to charge real money in live mode.
- On first entry the dashboard opens in test mode by default (Test mode switch, top right). Always work in this mode while developing.
2. Working environments (Test vs Live) and sandbox accounts
In Stripe an "environment" is not created as in other systems: each account ships with two environments with independent sets of keys and webhooks.
- Test mode: keys
sk_test_…/pk_test_…. Payments use test cards (e.g.4242 4242 4242 4242, any future date and CVC). No real money. - Live mode: keys
sk_live_…/pk_live_…. Requires an activated account. Moves real money. - The Test mode switch (top-right corner) toggles between the two. Each mode has its own products, webhooks and keys: a webhook created in test does not exist in live.
- If you need several isolated environments (e.g. dev, staging, demo) without mixing data, create Sandboxes: account selector top menu → Sandboxes → Create sandbox. Each sandbox is an independent test account with its own set of keys and webhooks. It is the recommended way to have a clean environment per deployment.
- For real production, one account (or a Connect account) per city/tenant is recommended; Model City is prepared for one account per deployment.
3. Getting the secret and publishable keys
- Make sure you are in the correct mode (Test for development).
- Go to Developers → API keys (
https://dashboard.stripe.com/test/apikeys). - Copy the Publishable key (
pk_test_…): it is public, baked into the front-end bundle. It goes intoSTRIPE_PUBLISHABLE_KEY. - Under Secret key click Reveal test key and copy the value (
sk_test_…). It goes intoSTRIPE_SECRET_KEY. It is secret: it must only live in the back-end (and the front-end server-side), never in the browser. - If the secret key ever leaks, use Roll key on that same screen to rotate it; the previous value is invalidated.
- In Live mode the steps are identical but the keys start with
pk_live_…/sk_live_…and only appear with an activated account.
4. Create the webhook and get its secret
The back-ends validate each event's signature with
Webhook.constructEvent(payload, sigHeader, webhookSecret). Therefore each
endpoint needs its whsec_… secret. The endpoints are public (they are in the
gateway/monolith permitAll list) precisely because they authenticate by
signature, not by JWT.
4.1. Exposed endpoints
| Topology | Vertical | Public webhook URL |
|---|---|---|
| Microservices | leisure | https://<domain>/api/leisure/stripe/webhook |
| Microservices | mobility | https://<domain>/api/mobility/stripe/webhook |
| Monolith | leisure | https://<domain>/api/leisure/stripe/webhook |
| Monolith | mobility | https://<domain>/api/mobility/stripe/webhook |
4.2. Events to subscribe
Both verticals handle exactly these event types (see StripeWebhookController):
checkout.session.completed→ marks the ticket/reservation as PAID.checkout.session.expired→ marks it CANCELLED.checkout.session.async_payment_failed→ marks it CANCELLED.
4.3. Steps to create the webhook
- Go to Developers → Webhooks (
https://dashboard.stripe.com/test/webhooks). - Click Add endpoint.
- In Endpoint URL enter the vertical's public URL (table 4.1). It must be reachable from the Internet.
- In Select events choose the three events from section 4.2 (or the whole
checkout.session.*family). - Click Add endpoint to create it.
- On the newly created endpoint's screen, Signing secret section, click
Reveal and copy the
whsec_…value:- In microservices: the leisure endpoint's value goes into
STRIPE_WEBHOOK_SECRETof theleisureservice; the mobility endpoint's value goes intoSTRIPE_WEBHOOK_SECRETof themobilityservice (same variable, different value per service). - In the monolith: leisure goes into
STRIPE_WEBHOOK_SECRETand mobility intoSTRIPE_RESERVATION_WEBHOOK_SECRET.
- In microservices: the leisure endpoint's value goes into
- Repeat for the second vertical: two endpoints are created (leisure and mobility), each with its own secret.
4.4. Testing webhooks locally (Stripe CLI)
- Install the CLI:
brew install stripe/stripe-cli/stripe(macOS) and runstripe login. - Forward events to your local back-end:
stripe listen --forward-to http://localhost:8093/stripe/webhook # leisure (micro)stripe listen --forward-to http://localhost:8094/stripe/webhook # mobility (micro)
- The CLI prints an ephemeral
whsec_…secret: use it asSTRIPE_WEBHOOK_SECRETfor the duration of thestripe listensession.
5. Choosing the stripe-java version to match your account's API version
The project pins the dependency in the root BOM (pom.xml) and in the archetypes:
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>24.14.0</version>
</dependency>
The relationship between the library and the Stripe API, and how to check it:
- Each
stripe-javarelease pins a specific Stripe API version. That value is in the constantcom.stripe.Stripe.API_VERSIONand is sent in theStripe-Versionheader of every call, regardless of your account's default version. - Check your account's API version under Developers → Overview (or
Settings → Business → API version): you'll see the default API version (e.g.
2024-06-20) and the version history. - Check the version your library pins: open the release notes at
https://github.com/stripe/stripe-java/releasesfor24.14.0, or just log the value ofcom.stripe.Stripe.API_VERSIONat startup. That is the realStripe-Versionthe back-end will use. - Practical rule of choice: pick a
stripe-javaversion whose pinned API is equal to or compatible with your account's default API version. Since the app's calls always use the library version, what matters is that:- the objects the back-end creates (Product, Price, Checkout Session, Refund) exist as-is in that API version, and
- the shape of the webhook payloads you receive matches what the code
expects (the project uses
checkout.session.*, stable for many versions).
- When you bump the API version in the dashboard (Upgrade button in the
version history), also bump
stripe-javato the release whose pinned API matches the new one, recompile and re-test the webhooks, because payloads may change shape. - To update in the project, just change the version in the
<dependency>of the rootpom.xml(BOM) and in the<stripe.version>property of the archetypes, then runmvn -q -DskipTests package.
6. Create the parking product
For the mobility services and parking, the front-end creates the
Checkout Session using a fixed parking product and returns the
checkoutSessionId to the back-end. That fixed product is what
STRIPE_PARKING_PRODUCT_ID references.
Steps to create the parking product:
- In the correct mode (Test/Live), go to Product catalog → Products
(
https://dashboard.stripe.com/test/products). - Click Add product (or + Create product).
- Fill in:
- Name: e.g.
Parking Model City(name shown at checkout). - Description (optional).
- Name: e.g.
- Under Pricing define the parking price. Since the real amount depends on
duration, one of these two strategies is recommended:
- A recurring price per unit of time, or
- A product with a base price where the front-end computes the final amount
(
unit_amount) when creating the Session. CurrencyEUR, consistent with the back-end'sdefault-currency: eur.
- Click Save product.
- Open the newly created product. Its identifier
prod_…appears at the top. Copy it: that is the value ofSTRIPE_PARKING_PRODUCT_ID.
7. Summary
At the end you should have, for the chosen mode (test or live):
STRIPE_SECRET_KEY=sk_…→ Developers → API keys (step 3).STRIPE_PUBLISHABLE_KEY=pk_…→ Developers → API keys (step 3).STRIPE_WEBHOOK_SECRET=whsec_…of the leisure endpoint (step 4).STRIPE_RESERVATION_WEBHOOK_SECRET=whsec_…of the mobility endpoint (monolith only; in micro the mobility service usesSTRIPE_WEBHOOK_SECRET) (step 4).STRIPE_PARKING_PRODUCT_ID=prod_…of the parking product (step 6).