The event was written twice per accepted organization invitation. The
backend submitted it, and the browser then re-submitted a copy of the
props that the backend had already put in the response under
`:organization-invitation-audit` (`handle-token :team-invitation` in
`verify-token.cljs`). Both rows carried the same name with different prop
vocabularies, and the browser copy only existed when the browser finished
the flow.
Emit the event from the backend only. It now also carries the three props
that lived in the browser copy: the organization member count before the
add, the add source, and whether the invitee also joined a team. The
origin moves to the event context as `:event-origin`. The response no
longer includes `:organization-invitation-audit`, so the browser stops
emitting the event and `verify-token.cljs` drops its
`app.main.data.event` require.
The `accept-*` events of this command now share one prop vocabulary:
`:profile-id` for the accepting profile, `:invited-by` for the inviter
and `:profile-email` for the email, replacing the mix of
`:user-id`/`:user-who-send-invitation` and `:email`.
Audit consumers of `accept-organization-invitation` now see one row per
acceptance instead of two, and must read the new prop names.
AI-assisted-by: space-bunny-free
When the Stripe checkout fails to start, the subscription page now
shows an inline error in the Business Nitrate card under the CTA
instead of a toast. When the post-payment activation fails, the toast
message is updated to point users to support@penpot.app.
The nitrate-form modal also passed a URI object to
build-nitrate-callback-urls while the underlying append-query-param
relied on lambdaisland's u/parse, which only accepts strings. Switched
to the local u/uri helper so both strings and URI records work, so
failures opened from the modal land on the subscription page.