Stripe Connect Integration
Miranda uses Stripe Connect in a platform model with Express connected accounts. Each foundation connects their own Stripe account, and Miranda facilitates the payment flow while collecting a tiered platform fee.
Architecture
Section titled “Architecture”Miranda operates as the platform account (merchant of record). Each foundation is an Express connected account. Donations use destination charges, meaning:
- The PaymentIntent is created on Miranda’s platform account
- Funds are automatically transferred to the foundation’s connected account
- Miranda retains the
application_fee_amountas its transaction fee
Donor → Stripe.js → Miranda Platform Account ↓ PaymentIntent (destination charge) ↓ Foundation's Connected Account (receives funds minus fee) ↓ Miranda retains application_fee_amountStripeConnectService
Section titled “StripeConnectService”The StripeConnectService manages the connected account lifecycle.
Creating an Express account
Section titled “Creating an Express account”public function createExpressAccount(Foundation $foundation): string;Creates a Stripe Express account with the foundation’s ID in metadata. Updates the foundation record with stripe_connect_id and sets stripe_connect_status to pending.
Generating an onboarding link
Section titled “Generating an onboarding link”public function generateOnboardingLink( Foundation $foundation, string $returnUrl, string $refreshUrl,): string;Returns a Stripe Account Link URL that redirects the foundation owner through Stripe’s hosted onboarding flow. After completion, Stripe redirects back to returnUrl.
Checking account status
Section titled “Checking account status”public function getAccountStatus(Foundation $foundation): array;Returns:
{ "account_id": "acct_abc123", "charges_enabled": true, "payouts_enabled": true, "details_submitted": true}A foundation can accept donations once charges_enabled is true.
Retrieving business profile
Section titled “Retrieving business profile”public function getBusinessProfile(Foundation $foundation): array;Returns:
{ "business_name": "Miranda Faith Memorial Foundation", "support_phone": "+15551234567", "business_type": "non_profit"}Disconnecting
Section titled “Disconnecting”public function deleteAccount(Foundation $foundation): void;Deletes the connected account from Stripe. Used when a foundation disconnects their Stripe integration.
Platform fee calculation
Section titled “Platform fee calculation”public function calculatePlatformFee(int $amountInCents, Foundation $foundation): int;Fee rates are tiered by plan (from config/plans.php):
| Plan | Fee rate | Example ($100 donation) |
|---|---|---|
| Starter | 2.0% | $2.00 |
| Growth | 1.5% | $1.50 |
| Pro | 1.0% | $1.00 |
The fee is calculated in cents and rounded to the nearest cent:
$plan = $foundation->settings['plan'] ?? 'starter';$feeRate = config("plans.{$plan}.transaction_fee");return (int) round($amountInCents * $feeRate);Onboarding flow
Section titled “Onboarding flow”The full onboarding sequence from the admin dashboard:
- Initiate: Admin clicks “Connect Stripe” in settings
- API call:
POST /api/stripe/onboardcreates an Express account and returns an onboarding URL - Redirect: Admin is redirected to Stripe’s hosted onboarding
- Complete: After providing business details and bank account, Stripe redirects back
- Callback:
GET /api/stripe/callbackverifies the account status - Ready: Once
charges_enabledis true, the foundation can accept donations
API endpoints
Section titled “API endpoints”| Method | Path | Description |
|---|---|---|
| POST | /api/stripe/onboard | Create Express account and return onboarding URL |
| GET | /api/stripe/callback | Handle return from Stripe onboarding |
| GET | /api/stripe/status | Check connected account status |
| POST | /api/stripe/disconnect | Disconnect Stripe account |
| GET | /api/stripe/account-details | Retrieve business profile from Stripe |
All endpoints require foundation authentication.
Webhook handling
Section titled “Webhook handling”Stripe webhooks are received at:
POST /stripe/webhook(primary)POST /strp/wbhk(alias for Stripe dashboard configuration)
The WebhookController validates the webhook signature and processes events for payment status updates, failed charges, and subscription lifecycle events.
Foundation model fields
Section titled “Foundation model fields”| Field | Type | Description |
|---|---|---|
stripe_connect_id | string? | Stripe connected account ID (e.g., acct_abc123) |
stripe_connect_status | string | pending, active, or disconnected |
Security notes
Section titled “Security notes”- PCI compliance: Miranda operates at SAQ-A level. Card data is collected client-side via Stripe.js/Elements and never touches Miranda’s servers.
- Webhook verification: All Stripe webhooks are validated using the webhook signing secret.
- Platform keys: Stripe API keys are stored in
.envand accessed viaconfig('services.stripe.secret').