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 as Analytics.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 sub sub 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 source

  • Zero 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 $.key pattern, 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 export on the role: only the exported factory sub donate() carries is 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).

Air::Plugin::Donate v0.0.1

Stripe donation panel plugin for the Air web framework

Authors

  • librasteve

License

Artistic-2.0

Dependencies

Air

Test Dependencies

Provides

  • Air::Plugin::Donate

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.