ghijk/donation-checkout
ghijk/donation-checkout is a Laravel package for adding donation checkout flows to your application, helping you collect one-time or recurring contributions with configurable amounts and a streamlined checkout experience.
Install the addon and publish its config before touching any templates:
composer require 1stevengrant/donation-checkout
php artisan vendor:publish --tag=donation-checkout-config
Set your Stripe keys in .env (STRIPE_KEY, STRIPE_SECRET, and a webhook secret if you're handling async payment events), then open the published config file — that's the first place to look. As of v2.1.7 it holds the two knobs you're most likely to change on day one: the per-IP rate limits (defaults: 5 requests/minute, 50/day) and create_users (default true).
Your first use case is almost certainly "put a donate button on a Statamic page." Render the addon's form (or point your own <form> at the public donation endpoint), submit against Stripe test keys, and confirm you get a redirect/response you expect. Check that a donation record lands where you expect and — because create_users defaults to true — that a Statamic user was or wasn't created. Decide that now, not after launch.
Treat create_users as an explicit product decision, not a default. Leaving it true means every donor becomes a Statamic user: useful if you plan a donor portal, repeat-giving flow, or per-user receipts, but it pollutes your users collection and can interact badly with role assignment, newsletter sync, and anything that iterates User::all(). Set it to false in config for one-off/anonymous campaigns and flip it per-site if you run several.
Lean on Stripe customer reuse rather than building your own. The addon looks up/creates a Stripe customer by normalised email, so repeat donors accumulate charges against one customer instead of scattering duplicates. This only works if your form's email field is the source of truth — don't let users edit email after submission, and don't pre-fill it from a stale session value.
Mirror the server-side bounds in your frontend. Donor input is now bounded server-side, and emails are normalised before storage/lookup. Your Blade form should carry matching maxlength attributes and client-side validation so donors get feedback before a round trip, rather than a validation redirect after one.
Design your error UX around generic responses. Stripe API failures now return generic messages rather than leaking provider detail. That's correct security-wise but means your JavaScript cannot branch on "card declined" vs "network to Stripe failed." Keep the user-facing copy deliberately vague ("We couldn't process that right now, please try again") and put the real detail in logs/Stripe Dashboard.
Tune rate limits per environment. Override the per-minute/per-day values in config: generous in local and staging so your own testing isn't throttled, tighter in production. If you run campaigns from shared egress (offices, universities, mobile carriers behind CGNAT), consider raising the daily cap — 50/day per IP will bite a legitimate shared-IP audience.
Pin the security floor in CI. The release pins Laravel ≥ 12.69.3, league/commonmark ≥ 2.10.3, and league/flysystem ≥ 3.36.0 via Composer constraints. Add composer audit --locked to your pipeline so a future composer update can't silently walk you back below that floor, and commit composer.lock.
create_users defaults to true, so if you previously relied on donations not creating users, verify the post-upgrade behaviour explicitly and set the flag deliberately.TrustProxies correctly or every donor collapses into a single bucket and you'll throttle the whole site at 50 donations/day.composer update -W in a branch and exercise the checkout flow end to end, since jumping Laravel minors can surface deprecations in unrelated parts of your app.How can I help you explore Laravel packages today?