Connecting Stripe for Subscription Billing

AcelleMail's Cashier ships Stripe as the first-class subscription gateway. This guide walks the connection end-to-end: API key configuration, the four webhook events that must be subscribed, the webhook URL location, and test-vs-live mode discipline. Covers both subscription billing (for SaaS operators) and one-off payment use cases.

What this is for

AcelleMail's Cashier (the subscription billing layer) supports Stripe as the first-class payment gateway. PayPal and other gateways were removed in the recent Cashier consolidation — Stripe + Offline (manual) are what's supported today.

This guide walks the connection end-to-end. If you're setting up a full SaaS platform, Stripe is one step in that larger setup; this article covers the Stripe-specific configuration in depth.

Prerequisites

  • An AcelleMail install with the Extended License (required for SaaS / paid-subscription use)
  • A Stripe account (https://dashboard.stripe.com) — start in test mode, switch to live after validating
  • Comfortable with the Stripe Dashboard's basic navigation

Step 1 — Get your Stripe API keys

In Stripe Dashboard:

  1. Click Developers → API keys
  2. Note your Publishable key (pk_test_... in test mode, pk_live_... in live mode)
  3. Click Reveal test key next to "Secret key" to see your Secret key (sk_test_... / sk_live_...)

Test mode first, always. Set up the whole integration in test mode (test cards, test webhooks). Validate every flow end-to-end. THEN switch to live mode — never go straight to live with new gateway code.

Step 2 — Configure Stripe in AcelleMail

In AcelleMail Admin:

  1. Payment Gateways → Stripe → Edit
  2. Paste:
    • Publishable Key — from Step 1
    • Secret Key — from Step 1
    • Modetest (for now)
    • Currency — must match your Stripe account default currency
  3. Save (leave Webhook Secret blank for now — we'll set it after creating the webhook in Step 3)
  4. Click Enable on the Stripe gateway

Disable other gateways you're not using. AcelleMail shows every enabled gateway in the customer's plan-checkout flow. Leaving inactive ones enabled creates confusing UX.

Step 3 — Set up the Stripe webhook

This is the most-skipped step, and without it, renewals + cancellations don't sync back to AcelleMail.

  1. AcelleMail Admin → Payment Gateways → Stripe → after saving, AcelleMail shows the webhook URL you'll use. Format: https://your-acellemail-domain.com/stripe/webhook. Copy it.
  2. Stripe Dashboard → Developers → Webhooks → Add endpoint
  3. Endpoint URL — paste the URL from step 1
  4. Events to send — select these 6 events (each one matters):
Stripe event Why AcelleMail needs it
customer.subscription.created Initial subscription activation — flips customer's plan status to active
customer.subscription.updated Plan changes, status changes (trialing → active)
customer.subscription.deleted Cancellation — downgrades customer to default free plan
invoice.paid Successful renewal — extends customer's billing period
invoice.payment_failed Failed renewal — triggers dunning (retry + customer notification)
payment_intent.succeeded One-off payment confirmation (for non-subscription flows)
  1. After clicking "Add endpoint", Stripe shows you a Signing Secret (whsec_...). Copy it.
  2. Back in AcelleMail → Payment Gateways → Stripe → paste the Signing Secret into the Webhook Secret field
  3. Save

Step 4 — Test the round-trip

In test mode:

  1. Acelle: Admin → Plans → Add new — create a test plan, e.g. Test Plan at $1/month
  2. Log in as a test customer (or use a different browser)
  3. Customer Dashboard → Subscribe to the test plan
  4. Stripe Checkout opens → use test card 4242 4242 4242 4242, any future expiry, any CVC, any ZIP
  5. Click Pay
  6. You should be redirected back to AcelleMail with subscription active

Verify each side:

  • Stripe Dashboard → Customers — new customer with active subscription
  • Stripe Dashboard → Developers → Webhooks → click your endpoint — should show 3 successful POST attempts (customer.subscription.created, invoice.paid, payment_intent.succeeded) within seconds
  • AcelleMail → Customers → your test customer — Plan = Test Plan, Status = Active

If all three are green, the integration is working. Make a refund in Stripe (Test Customer → Refund) to verify the cancellation flow as well.

Step 5 — Switch to live mode

When test mode is fully validated:

  1. Stripe Dashboard → toggle Test mode to Live mode (top-right)
  2. Get your live keys (Developers → API keys)
  3. Create a NEW webhook endpoint in live mode (test webhooks don't carry over) — same URL, same 6 events
  4. Get the live webhook signing secret
  5. AcelleMail → Payment Gateways → Stripe → swap to live keys + live webhook secret
  6. Save + repeat the Step 4 test using a real card (smallest amount — refund it after)
  7. Watch the live webhook deliveries on Stripe Dashboard for the first day to catch any issues

Plan design considerations

In AcelleMail's plan editor:

  • Price + currency — must match the currency of your Stripe account
  • Frequency1 month, 1 year, 3 months, etc. — Stripe creates the right recurring price internally
  • Trial period — e.g. 14 + day — Stripe handles the trial; customers aren't charged until trial end
  • Plans visible — toggle off to hide a plan from the public pricing page (useful for legacy / promo plans)

Tip: create both monthly + annual variants of each plan, with annual at ~17% discount. Annual conversion meaningfully reduces churn — see the Building Your SaaS guide for pricing-ladder advice.

Plan migrations

When you change a plan's price or limits in Stripe:

  • Existing subscribers stay on their old terms until their next renewal
  • New subscribers get the new terms immediately
  • Acelle's subscription:monitor daily task reconciles state — usually within 24 hours

When you change a plan's options JSON (quotas) in Acelle:

  • Changes apply to ALL subscribers (active and new) immediately
  • This is one-way: dropping the customer's quota mid-billing-period is bad UX; bump quotas on new plans only

Refunds + disputes

  • Refund issued in Stripe → triggers charge.refunded event. AcelleMail does NOT auto-cancel the subscription on refund (refunding the latest charge doesn't necessarily end service). To stop service, also cancel the subscription in Stripe.
  • Dispute / chargeback → triggers charge.dispute.created. Manual review required. Acelle doesn't auto-suspend on dispute, but you should suspend manually pending the dispute outcome.

One-off payments (not subscriptions)

For Acelle's sending-credit / email-verification-credit products, Stripe handles one-off Payment Intents (not subscriptions). The webhook event for these is payment_intent.succeeded (you subscribed to it in Step 3).

Common issues

What you see Likely cause Fix
Stripe Checkout opens but customer is never charged Test card declined OR webhook not configured (Acelle doesn't know it succeeded) Use 4242 4242 4242 4242 for testing; verify Stripe Dashboard → Webhooks shows your endpoint with deliveries
Customer paid in Stripe but Acelle subscription status stays "new" Webhook signing secret mismatch — Acelle rejects the webhook Re-copy whsec_... from Stripe to AcelleMail; verify exact match
Renewal happens in Stripe but Acelle doesn't extend the period invoice.paid event missing from webhook subscription Edit webhook in Stripe → Add invoice.paid event
Failed payment doesn't trigger dunning emails invoice.payment_failed missing OR Acelle's mail templates not configured Add the event; verify Admin → System Emails → Payment Failed template exists
Customer downgrades plan but quotas don't drop Acelle's subscription:monitor cron hasn't run yet Wait for the next run (hourly), or trigger manually: php artisan subscription:monitor
404 on the webhook URL Acelle install URL changed OR APP_URL in .env is wrong Check https://your-domain/stripe/webhook is reachable; verify APP_URL
Webhook works in test mode, breaks in live mode Forgot to set up a separate live-mode webhook Create live webhook in Stripe Dashboard live mode; swap signing secret
Stripe says "no such event" Stripe API version mismatch — your account's default API version is too old Stripe Dashboard → Developers → API version → Update to latest
Customer with 2FA on Stripe can't pay 3DS challenge not handled correctly Use Stripe's modern Checkout (default in 2026) which handles 3DS natively

FAQ

Why is PayPal not supported? The current Acelle Cashier (post-refactor) supports Stripe + Offline only. PayPal recurring subscriptions are operationally messier (no webhook for failed payments, currency conversion overhead, no first-class trial support). If you need PayPal, use Offline mode with manual reconciliation, OR set up Stripe's PayPal pass-through (Stripe can charge PayPal as a payment method on their side).

Can I use Stripe Tax? Yes — enable in Stripe Dashboard. Acelle's invoice flow uses Stripe's tax calculation automatically once enabled.

Can I use Stripe Billing's coupons? Yes — configure coupons in Stripe Dashboard; pass the coupon code via the customer's checkout URL. AcelleMail's UI doesn't have a coupon field directly; you'd need to extend the checkout flow (see extending source code).

Charging in multiple currencies? Each AcelleMail plan has one currency. Create parallel plans in each currency (e.g. "Pro $79" and "Pro €72"). Customer's billing currency is locked to the plan they signed up on.

Migrating from another billing system? Export Stripe customer + subscription records via Stripe's CSV export. Acelle has no built-in import for Stripe data; you'd need a one-off script that creates Acelle customer records + links them via stripe_customer_id metadata field. Talk to support before doing this at scale.

GDPR / right-to-erasure? When a customer requests deletion: Stripe's data retention is governed by Stripe's own GDPR processes (you can request deletion via Stripe Dashboard → Customer → Actions → Forget). AcelleMail-side data deletion is via Admin → Customers → Delete.

Is there a TestStripe / Stripe sandbox UI for verification? Use Stripe's test mode (Step 1's pk_test_ / sk_test_ keys). Test cards from https://stripe.com/docs/testing.

Related articles

11 条评论

4 条评论

  1. cmendoza.mx
    wooCommerce integration finally documented properly. Was reverse-engineering the webhook payload before this
  2. bos.devops
    If your webhook receiver is unreliable, point AcelleMail at an intermediate proxy with retry logic (Cloudflare Worker works well)...
    1. admin
      worth adding to the article. PR wlcome if you want to author the addition
    2. admin (已编辑)
      yep, same pattern works for us. thanks for sharing...
  3. akira.tnk88
    Any plans for a native Shopify app? The webhook approach works but a real app integration would be smoother for non-technical users.
    1. admin
      Good question. The campaign:rerun audit writes to laravel.log only when the audit decides to force-resume — pure noop runs are silent. We'll add an info-level heartbeat in a future Acelle release to make it easier to monitor.
    2. admin (已编辑)
      depends on your version. 5.x supports it natively; 4.x needs a config flag set in `.env`. we'll note this caveat in the article on the next pass
  4. sobrien.kw
    Set up the Zapier bridge last week. ~30 minutes from start to working flow. Cleaner than I expected
    1. admin (已编辑)
      thanks for sharing. the pattern you describe is exactly the use case we built that feature for — glad it landed for you.
    2. admin (已编辑)
      solid case study material here. if you're open to it, we'd love to write this up as a blog post — happy to credit you anonymously or otherwise.
    3. admin (已编辑)
      Thanks for the detail — adding the kernel-reboot edge case to the article on the next update.

More in Integrations