design
Context
The user wants a donation panel similar to donate.codeberg.org. This is much simpler than a full Stripe API integration: no Checkout Session creation, no webhooks, no server-side calls to Stripe at all. Stripe already hosts a permanent payment page per "Payment Link" (https://donate.stripe.com/<id>), created once via the Stripe Dashboard. The plugin just renders a small panel (heading, blurb, Donate button) that links out to that URL.
Precedent driving the design:
Umami pattern (
Air/lib/Air/Base/Tools.rakumod):role Analytics does Tool { has Provider $.provider; has Str $.key; ... }, instantiated asAnalytics.new(:provider(Umami), :key<website-id>). This confirms the "pass an external service id as a plain constructor attribute named$.key" convention mirrored here ā a public identifier, not a credential.Hilite pattern (
Air-Plugin-Hilite/lib/Air/Plugin/Hilite.rakumod):role Air::Plugin::Hilite does Tag { has Str $.code; multi method HTML {...} }plus an exported lowercase factory subsub hilite(*@a,*%h) is export {...}. This is the template for Donate ā a pure rendering widget with no routes, no registration, no Form/Component role.Pico CSS is already the base styling system used throughout Air; Pico's
<article>element renders as a card with zero custom CSS, matching Codeberg's plain-box aesthetic.
Goals / Non-Goals
Goals:
Render a static Pico-styled panel (heading, blurb, button) linking to a Stripe Payment Link
Accept the Payment Link id as a plain constructor attribute
$.key, safe to commit in sourceZero backend logic: no network calls, no env vars, no webhooks
Non-Goals:
Dynamic Stripe Checkout Session creation
Webhook handling / payment-success confirmation
Recurring/monthly donations, custom amount presets ā deferred to the future
Air::Plugin::Pay
Decisions
Static Payment Link over Checkout Session API: mirrors the existing Umami
$.keypattern, keeps the plugin free of secrets and network calls. Rejected the original richer plan (Cro::HTTP::Client, STRIPE_SECRET_KEY env var, HX-Redirect) as overkill per explicit user feedback.Pico
<article>for card styling: no custom CSS/SCSS hooks needed, consistent with sibling plugins.No
is exporton the role: only the exported factory subdonate()carriesis export, per project-wide convention.
Risks / Trade-offs
No control over Stripe's hosted page appearance beyond what the Payment Links dashboard offers ā acceptable, matches Codeberg's own approach.
No webhook/confirmation means the site itself never learns a donation succeeded ā acceptable per user confirmation that Codeberg's box works the same way (target the Stripe page directly, no webhooks).