You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Apple Pay / Google Pay on the Stripe guest checkout #65
The Stripe guest checkout (packages/portal/src/payments/stripe/StripeGuestPayment.tsx) only offers the CardElement. Guests start a session by scanning the station QR code on their phone, where Apple Pay and Google Pay are the payment methods people expect. Typing a card number while standing at the charger is where we expect guests to give up. We operate public AC chargers (KEBA P30, OCPP 1.6) with ad hoc guest charging, and for us wallet payments are close to a requirement.
Checked against v0.1.51 and main (2026-11-13): no wallet support in the portal, and no open issue or PR about it.
Proposal
I would like to add Stripes Express Checkout Element above the card field on the guest checkout (Stripe only):
Elements in deferred mode: mode: 'payment', amount = the pre-auth hold, currency, captureMethod: 'manual', paymentMethodCreation: 'manual'.
On confirm: elements.submit(), then stripe.createPaymentMethod({ elements }), then the existing pay(paymentMethod.id). A wallet yields a card PaymentMethod, so the server-side hold and capture, the 3DS handling and the API stay unchanged
Apple Pay and Google Pay only
The button is hidden when the device offers neither, and the card field stays below with an "or pay by card" divider
The divider string in all six portal locales, plus tests in packages/portal/src/payments/__tests__/stripe.test.tsx
Questions
Would you accept this as a PR against main?
Should registering the payment method domain be a dashboard button (like "Create webhook") in the same PR, or a follow-up?
The Express Checkout Element renders inside js.stripe.com iframes, which the portal CSP already allows. We would verify on real devices and touch nginx-security-headers.conf only if needed - any constraints there we should know about?
Thanks @6lyxt, this is a good addition and the proposal fits the current flow well.
1. Would you accept this as a PR against main?
Yes. The server side needs no change: the guest hold already takes a one-time PaymentMethod id with manual capture and automatic payment methods without redirects (authorizeHold in packages/payments/src/providers/stripe/index.ts), so a wallet card PaymentMethod goes through the same hold, capture and 3DS handling.
A few points to cover:
When a site has a Stripe Connect payout account, the hold is a destination charge with on_behalf_of. The deferred Elements then need the matching onBehalfOf option. Please test at a site with a payout account as well as without one.
Apple Pay and Google Pay only: turn Link and the other wallets off.
The divider in all six portal locales and the tests in stripe.test.tsx, as you planned.
The PR needs the CLA signed and green CI (lint, typecheck, format, unit tests).
2. Domain registration: same PR or a follow-up?
Please include it in the same PR, so operators can use the feature without a manual step in the Stripe dashboard. A good model is the Stripe Create Webhook action: POST /v1/settings/stripe/webhook in packages/api/src/routes/payments.ts and its button on the Stripe settings card in the CSMS. What it needs:
A route that registers the portal domain (from PORTAL_URL) through Stripe's Payment Method Domains API, with the settings.payment:write permission, plus a way to show the current registration status.
Every Stripe call in @evtivity/payments (the only package that calls a payment provider), not in the route.
Connected accounts: when sites have Stripe Connect payout accounts, check Stripe's rules for registering the domain on those accounts and handle them.
An audit row for the registration, any new error codes with their messages in the CSMS locales, and the button's labels in all six CSMS locales, with tests.
The OpenAPI spec and the AI tool catalog regenerated (npm run codegen:ai-tools -w @evtivity/api), since a new route changes them. The new route should not be callable by the AI assistant.
We take care of the website docs.
3. CSP constraints?
No change should be needed: the portal script-src allows https://js.stripe.com, and frame-src allows https://js.stripe.com and every https: source. If a real device shows that something is missing:
The same CSP lives in three places that must stay identical: packages/portal/nginx-security-headers.conf, the Compose dev image and the Helm chart's nginx ConfigMap. web-security-headers.test.ts checks them.
frame-ancestors 'none' must stay.
Please keep each new source as narrow as possible and say in the PR why it is needed.
Activity
evtivity-admin commentedon Nov 13, 2026
Thanks @6lyxt, this is a good addition and the proposal fits the current flow well.
1. Would you accept this as a PR against main?
Yes. The server side needs no change: the guest hold already takes a one-time PaymentMethod id with manual capture and automatic payment methods without redirects (
authorizeHoldinpackages/payments/src/providers/stripe/index.ts), so a wallet card PaymentMethod goes through the same hold, capture and 3DS handling.A few points to cover:
on_behalf_of. The deferred Elements then need the matchingonBehalfOfoption. Please test at a site with a payout account as well as without one.stripe.test.tsx, as you planned.2. Domain registration: same PR or a follow-up?
Please include it in the same PR, so operators can use the feature without a manual step in the Stripe dashboard. A good model is the Stripe Create Webhook action:
POST /v1/settings/stripe/webhookinpackages/api/src/routes/payments.tsand its button on the Stripe settings card in the CSMS. What it needs:PORTAL_URL) through Stripe's Payment Method Domains API, with thesettings.payment:writepermission, plus a way to show the current registration status.@evtivity/payments(the only package that calls a payment provider), not in the route.npm run codegen:ai-tools -w @evtivity/api), since a new route changes them. The new route should not be callable by the AI assistant.We take care of the website docs.
3. CSP constraints?
No change should be needed: the portal
script-srcallowshttps://js.stripe.com, andframe-srcallowshttps://js.stripe.comand everyhttps:source. If a real device shows that something is missing:packages/portal/nginx-security-headers.conf, the Compose dev image and the Helm chart's nginx ConfigMap.web-security-headers.test.tschecks them.frame-ancestors 'none'must stay.Looking forward to the PR.