Skip to content

Add donation platform with Stripe integration and recurring support#72

Open
ghanshyam2005singh wants to merge 2 commits into
alphaonelabs:mainfrom
ghanshyam2005singh:feat/donations-stripe-integration
Open

Add donation platform with Stripe integration and recurring support#72
ghanshyam2005singh wants to merge 2 commits into
alphaonelabs:mainfrom
ghanshyam2005singh:feat/donations-stripe-integration

Conversation

@ghanshyam2005singh

@ghanshyam2005singh ghanshyam2005singh commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

This PR introduces a complete donation system for the platform, including one-time and recurring donations, Stripe payment integration, donation statistics, recent donations feed, and webhook processing.

What is Added

Frontend

Donate Page (public/donate.html)

  • Donation type selection (One-Time / Monthly)
  • Preset donation amounts
  • Custom amount input
  • Optional donor message
  • Community impact section
  • Recent donations feed
  • Mobile-responsive layout
  • Stripe Payment Element integration

Backend

Donation API

Added:

  • GET /api/donations/stats
  • GET /api/donations/recent
  • POST /api/donations/one-time
  • POST /api/donations/monthly
  • GET /api/donations/config
  • POST /api/donations/webhook

Architecture Improvements

Created dedicated donation modules instead of expanding worker.py:

src/donations.py

Business logic layer:

  • payload validation
  • donation record creation
  • display formatting
  • reusable donation helpers

src/api/donations.py

API layer:

  • Stripe PaymentIntent creation
  • Stripe Subscription creation
  • Stripe webhook verification
  • Mailgun thank-you emails
  • donation completion processing

Database

Added:

migrations/0008_add_donations.sql

New donations table with support for:

  • one-time donations
  • recurring donations
  • Stripe identifiers
  • donor information
  • payment status tracking

Stripe Integration

Implemented:

  • PaymentIntent flow for one-time donations
  • Customer + Subscription flow for recurring donations
  • Webhook signature verification
  • Payment status synchronization

Handled events:

  • payment_intent.succeeded
  • payment_intent.payment_failed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.payment_succeeded
  • invoice.payment_failed

Email Notifications

Added Mailgun-powered thank-you emails for successful donations.

Environment Variables Required

STRIPE_SECRET_KEY=
STRIPE_PUBLISHABLE_KEY=
STRIPE_WEBHOOK_SECRET=

MAILGUN_API_KEY=
MAILGUN_DOMAIN=
FROM_EMAIL=

Screenshot

Screencast.from.2026-06-28.14-35-52.mp4

Added a full donation flow with Stripe support for both one-time and monthly contributions.

Key changes:

  • New /donate.html page with preset/custom amounts, donor message, anonymity toggle, community impact stats, recent donations, and a responsive Stripe Payment Element checkout flow.
  • Donation API endpoints for config, stats, recent donations, one-time payments, monthly subscriptions, and Stripe webhooks.
  • New donation business-logic module for validating payloads, converting amounts, building records, and formatting donations for display.
  • New database migration to store donation records, payment status, Stripe identifiers, donor details, and recurring donation data.
  • Worker routing updated to expose the new donation endpoints.
  • Navbar Donate link now points to the new donation page.

Impact:

  • Enables users to donate directly through the site.
  • Supports recurring monthly giving and automated thank-you emails.
  • Adds donation tracking, completion handling, and public-facing donation activity updates.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ghanshyam2005singh, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 47 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0ef66c85-c62e-4712-9066-624abb73e308

📥 Commits

Reviewing files that changed from the base of the PR and between 99185cc and e8d9e9f.

📒 Files selected for processing (4)
  • public/donate.html
  • src/api/donations.py
  • src/donations.py
  • tests/conftest.py

Walkthrough

Adds a complete donation feature: a src/donations.py business logic module with validation and formatting, a src/api/donations.py with Stripe payment/subscription endpoints, webhook handling with Mailgun emails, six routes wired into src/worker.py, a public/donate.html page with a 3-step Stripe Payment Element flow, and a navbar link update.

Donation Feature

Layer / File(s) Summary
Donation domain constants, validation, and formatting
src/donations.py
Defines VALID_DONATION_TYPES, cent min/max limits, currency converters, validate_donation_payload (amount bounds, email regex, message cap), build_donation_record (pending status, anonymous flag), and format_donation_for_display (anonymous masking, cent-to-dollar).
API helpers, Stripe client, and Mailgun email
src/api/donations.py (lines 1–272)
CORS/JSON response helpers, JWT Bearer verification, UUID generation, recursive bracket-notation Stripe form encoder, async Stripe HTTP client, webhook HMAC-SHA256 signature verifier, and Mailgun thank-you email sender with env-based config guards.
Donation API endpoints and webhook handler
src/api/donations.py (lines 276–613)
get_donation_config, get_donation_stats, get_recent_donations; create_donation_intent (PaymentIntent + DB insert); create_subscription_intent (Customer → recurring Price → Subscription → DB insert); handle_donation_webhook with event dispatch covering payment, subscription, and invoice lifecycle, including duplicate-safe monthly donation recording.
Worker routing
src/worker.py
Imports all six donation handlers and registers them under /api/donations/* in _dispatch.
Donation page UI and 3-step Stripe flow
public/donate.html, public/partials/navbar.html
Full two-column markup (type selector, preset/custom amounts, email/message/anonymous toggle, Stripe Payment Element container, success screen, stats/recent-donations panels) plus all client-side JS managing step transitions, form validation, Stripe initialization, payment confirmation, redirect-status handling, and dynamic stats/donations rendering. Navbar "Donate" link updated to /donate.html.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(99, 102, 241, 0.5)
    note over Browser,Stripe: Step 1 – Collect details
    Browser->>API: POST /api/donations/one-time (amount, email)
    API->>Stripe: Create PaymentIntent
    Stripe-->>API: client_secret
    API->>DB: INSERT pending donation row
    API-->>Browser: client_secret
  end
  rect rgba(16, 185, 129, 0.5)
    note over Browser,Stripe: Step 2 – Confirm payment
    Browser->>Stripe: confirmPayment(client_secret)
    Stripe-->>Browser: redirect with redirect_status=succeeded
    Browser->>API: GET /api/donations/stats & /recent
    API->>DB: Query completed donations
    DB-->>API: rows
    API-->>Browser: totals + recent list
  end
  rect rgba(245, 158, 11, 0.5)
    note over Stripe,Mailgun: Async – Webhook
    Stripe->>API: POST /api/donations/webhook
    API->>API: Verify HMAC signature
    API->>DB: Mark donation completed
    API->>Mailgun: Send thank-you email
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~100 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a donation platform with Stripe and recurring donations support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@public/donate.html`:
- Around line 305-308: Gate the payment flow on Stripe being fully initialized
before creating any backend PaymentIntent/Subscription. In the donate page logic
around initStripe(), mountPaymentElement(), and the submit/payment handlers, add
a ready state or awaitable initialization so a fast submit cannot proceed until
stripeInstance and stripeElements exist. If Stripe is not ready, block
submission and surface a clear loading/error state rather than calling the
backend and failing later in mountPaymentElement().
- Around line 46-65: The donation type control in the HTML template is missing
proper radio-group accessibility and loses keyboard focus styling when
`onTypeChange()` swaps classes. Update the donation selector around the donation
type tabs to use `fieldset` and `legend` semantics, and make sure the
active/inactive class strings for `onTypeChange()` preserve `peer-focus-visible`
styles for the radio inputs. Keep the visible tab spans (`tab-one-time`,
`tab-monthly`) focusable via the hidden inputs so keyboard users can clearly see
focus state while navigating the group.
- Around line 105-121: Associate the amount and email validation messages with
their inputs so assistive tech can announce them. Update the custom-amount and
donor-email fields in donate.html to use aria-describedby pointing to
amount-error and email-error, set aria-invalid based on validation state, and
make the error paragraphs live regions with role="alert" or aria-live. Apply the
same accessibility pattern to the other validation fields mentioned in the form
(including the related section around the later inputs) so all submission errors
are exposed consistently.
- Around line 75-94: The preset donation amount buttons are only showing
selection via CSS, so assistive tech cannot tell which one is active. Update the
amount-selection logic for the amount-btn controls in donate.html to set and
clear aria-pressed alongside the class toggles whenever a preset is chosen, so
the current selection is announced consistently.
- Around line 300-324: The `showToast` render path is using `innerHTML` to
inject user-controlled message text, which can expose XSS risk even with manual
escaping. Update `showToast` (and the related recent-donation rendering
referenced by `renderDonation()`) to build DOM nodes with `createElement` and
assign text via `textContent` instead of concatenating HTML strings. Keep the
existing styling/icon logic, but ensure any donation names, messages, or backend
error strings are inserted only as plain text.
- Around line 382-389: The resetToForm() flow leaves selectedType and the
related tab/description state stale after form.reset(), so the UI can look reset
while submission still uses the monthly path. Update resetToForm() to also reset
selectedType back to the default one-time value and refresh the type-dependent
UI in the same place where selectedAmount and updateSummary() are reset,
ensuring backToForm() reflects the cleared monthly state.

In `@src/api/donations.py`:
- Around line 257-262: The _mark_donation helper interpolates the SQL identifier
field directly into the SELECT query, which triggers the S608 risk; constrain
field to an explicit allowlist of known Stripe donation column names before
building the statement, and reject anything else. Keep the query in
_mark_donation and its call sites unchanged except for validating the field
value before the env.DB.prepare call so only approved identifiers can be used.
- Around line 247-250: The logging in the email-sending flow leaks sensitive
data: the success message in the Mailgun send branch prints the donor’s address,
and the failure branch logs the raw provider response body. Update the logging
in the donation email handling code to use a masked or redacted recipient in the
thank-you message, and remove or sanitize the Mailgun response text before
logging it. Keep the changes localized to the email/send logic around the resp
handling so the logs remain useful without exposing PII or provider payloads.
- Around line 120-146: Add idempotency support to the mutating Stripe paths by
extending `_stripe()` to accept and forward an `Idempotency-Key` header when
provided. Update the create-call sites for `POST /payment_intents`,
`/customers`, `/prices`, and `/subscriptions` to generate and pass a stable
per-submission key so retries reuse the same Stripe request. Keep the change
localized to `_stripe` and the Stripe request helpers/callers in `donations.py`.
- Around line 151-166: The webhook verification in _verify_stripe_webhook only
checks the HMAC and currently accepts any timestamp, so add a bounded tolerance
check for the Stripe t= value before validating the signature. Parse the
timestamp in _verify_stripe_webhook, compare it against the current time with an
acceptable age window, and return False when the signed event is too old or too
far in the future. Keep the existing signature comparison logic for the v1
values after the timestamp freshness check.
- Around line 541-552: The subscription webhook handlers in
_on_subscription_created and _on_subscription_updated are overwriting
already-completed donations with lower states. Update the status transition
logic so donation status is monotonic: once _mark_donation has set a record to
completed, later customer.subscription.created or customer.subscription.updated
events should not regress it to pending or cancelled. Use the existing
_mark_donation flow and the subscription status mapping in
_on_subscription_updated to either preserve completed status or store
subscription lifecycle state separately from payment completion.
- Around line 497-516: The webhook handler in the donations flow is swallowing
exceptions and always returning success, which prevents Stripe retries on real
processing failures. Update the event dispatch logic in the webhook function to
let exceptions from handlers like _on_payment_succeeded,
_on_subscription_created, and _on_invoice_paid propagate (or return a 5xx
response) instead of catching everything and returning 200. Keep the normal 200
only for successfully processed events so transient failures can be retried.

In `@src/donations.py`:
- Around line 6-36: Use Decimal-based arithmetic in donations helpers to avoid
float rounding issues and to reject non-finite values early. Update
dollars_to_cents, cents_to_dollars, and validate_donation_payload to parse
amount_raw with Decimal, explicitly reject NaN/Infinity before conversion, and
keep cent calculations exact. While touching the signature in
validate_donation_payload, replace Tuple with the built-in tuple[...] annotation
for the return type.

In `@src/worker.py`:
- Around line 2720-2732: Add explicit dispatcher coverage for the donation route
checks in the request routing block that handles get_donation_config,
get_donation_stats, get_recent_donations, create_donation_intent,
create_subscription_intent, and handle_donation_webhook. Update
tests/test_dispatcher.py with a small route matrix that asserts each
/api/donations/* path and method maps to the correct handler, so manual string
typos or mismatched methods are caught before they become 404s or misroutes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9fcc1c49-3632-47c1-a077-09c3b30e7f41

📥 Commits

Reviewing files that changed from the base of the PR and between 997b01e and 99185cc.

⛔ Files ignored due to path filters (1)
  • migrations/0008_add_donations.sql is excluded by !**/migrations/**
📒 Files selected for processing (6)
  • public/donate.html
  • public/partials/navbar.html
  • src/api/__init__.py
  • src/api/donations.py
  • src/donations.py
  • src/worker.py

Comment thread public/donate.html Outdated
Comment thread public/donate.html Outdated
Comment thread public/donate.html Outdated
Comment thread public/donate.html Outdated
Comment thread public/donate.html
Comment thread src/api/donations.py Outdated
Comment thread src/api/donations.py
Comment thread src/api/donations.py Outdated
Comment thread src/donations.py Outdated
Comment thread src/worker.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant