Add donation platform with Stripe integration and recurring support#72
Conversation
|
Warning Review limit reached
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 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 configurationConfiguration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughAdds a complete donation feature: a Donation Feature
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
Estimated code review effort🎯 5 (Critical) | ⏱️ ~100 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
migrations/0008_add_donations.sqlis excluded by!**/migrations/**
📒 Files selected for processing (6)
public/donate.htmlpublic/partials/navbar.htmlsrc/api/__init__.pysrc/api/donations.pysrc/donations.pysrc/worker.py
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)Backend
Donation API
Added:
GET /api/donations/statsGET /api/donations/recentPOST /api/donations/one-timePOST /api/donations/monthlyGET /api/donations/configPOST /api/donations/webhookArchitecture Improvements
Created dedicated donation modules instead of expanding
worker.py:src/donations.pyBusiness logic layer:
src/api/donations.pyAPI layer:
Database
Added:
migrations/0008_add_donations.sqlNew donations table with support for:
Stripe Integration
Implemented:
Handled events:
Email Notifications
Added Mailgun-powered thank-you emails for successful donations.
Environment Variables Required
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:
/donate.htmlpage with preset/custom amounts, donor message, anonymity toggle, community impact stats, recent donations, and a responsive Stripe Payment Element checkout flow.Impact: