Issue #28: Stripe Payment Link Integration¶
Summary¶
Implemented Stripe Connect integration to enable service providers to accept online payments from their customers through payment links. The system uses Stripe Express accounts for each service provider, allowing them to connect their Stripe account, generate payment links for invoices, and receive payments directly.
What Was Implemented¶
1. Database Schema¶
File: database/migrations/002_add_stripe_integration.sql
Added Stripe-related fields to existing tables:
service_providers table:
- stripe_account_id (TEXT) - Stripe Connect account ID
- stripe_account_status (ENUM) - Account status: disconnected, pending, connected
- stripe_onboarding_complete (BOOLEAN) - Whether onboarding is finished
documents table:
- stripe_payment_link (TEXT) - Generated payment link URL
New ENUM type:
- stripe_account_status_enum with values: disconnected, pending, connected
2. Backend Services (API)¶
StripeService¶
File: apps/api/src/services/StripeService.ts
Complete Stripe Connect integration service:
- connectAccount(serviceProviderId) - Create/connect Stripe Express account
- getConnectionStatus(serviceProviderId) - Check account connection status
- disconnectAccount(serviceProviderId) - Remove Stripe connection
- generatePaymentLink(invoiceId, serviceProviderId) - Create payment link for invoice
- handleWebhook(payload, signature) - Process Stripe webhook events
- handleCheckoutSessionCompleted(event) - Mark invoice as paid
- handleAccountUpdated(event) - Update service provider status
- handlePaymentSucceeded(event) - Backup payment confirmation handler
Key Features: - Stripe Connect Express account creation - OAuth-based onboarding flow - Payment link generation with invoice metadata - Webhook signature verification for security - Automatic invoice status updates on payment - Account status synchronization
API Routes¶
File: apps/api/src/routes/stripe.ts
New endpoints:
POST /stripe/connect - Start Stripe account connection
GET /stripe/status - Check connection status
DELETE /stripe/disconnect - Disconnect Stripe account
POST /stripe/payment-link/:id - Generate payment link for invoice
POST /stripe/webhook - Stripe webhook handler (public)
Security:
- All endpoints require authentication except /stripe/webhook
- Webhook endpoint verifies Stripe signature
- Service provider isolation (users only access their own data)
3. Schemas & Types¶
Modified: schemas/src/ServiceProviderSchema.ts
Added Stripe-related fields:
stripe_account_id: z.string().nullable().optional()
stripe_account_status: StripeAccountStatusSchema.default("disconnected")
stripe_onboarding_complete: z.boolean().default(false)
Modified: schemas/src/DocumentSchema.ts
Added payment link field:
stripe_payment_link: z.string().nullable().optional()
Modified: schemas/src/EventSchema.ts
Added Stripe-related event constants:
- EVENT_STRIPE_CONNECTED - Account successfully connected
- EVENT_STRIPE_DISCONNECTED - Account disconnected
- EVENT_PAYMENT_LINK_GENERATED - Payment link created
- EVENT_PAYMENT_LINK_FAILED - Payment link generation failed
4. Frontend Integration¶
useStripe Composable¶
File: apps/web/composables/useStripe.ts
Frontend service for Stripe operations:
- connectStripe() - Initiate connection flow
- getConnectionStatus() - Fetch current status
- disconnectStripe() - Remove connection
- generatePaymentLink(invoiceId) - Create payment link
- copyPaymentLink(url) - Copy link to clipboard with toast notification
Features: - Event bus integration for user feedback - Loading state management - Error handling with user-friendly messages - Clipboard API integration
Settings Page¶
Modified: apps/web/pages/member/settings.vue
Added Payments tab with: - Connection status display - "Connect Stripe Account" button - "Disconnect" button when connected - Visual status indicators (disconnected/pending/connected) - Onboarding status information
Invoice Detail Page¶
Modified: apps/web/pages/member/feature/invoicing/[id].vue
Added payment link functionality: - "Generate Payment Link" button (when Stripe connected) - Display existing payment link - "Copy Link" button with clipboard integration - Status-based UI (only show for paid/unpaid invoices)
5. Webhook Integration¶
File: apps/api/src/services/StripeService.ts
Handles three webhook events:
checkout.session.completed:
- Triggered when customer completes payment
- Updates invoice status to "paid"
- Creates payment record in database
- Uses invoice metadata to identify correct invoice
account.updated:
- Triggered when service provider's account status changes
- Updates stripe_account_status based on:
- charges_enabled - Can accept payments
- payouts_enabled - Can receive payouts
- requirements.currently_due - Outstanding requirements
- Sets stripe_onboarding_complete when fully verified
payment_intent.succeeded:
- Backup event for payment confirmation
- Prevents duplicate payment records
- Ensures payment is captured even if checkout.session.completed fails
6. Environment Configuration¶
File: apps/api/.env.example
New environment variables:
# Stripe Configuration
STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret
STRIPE_CONNECT_CLIENT_ID=ca_your_client_id
7. Documentation¶
File: docs/stripe-workflow.md
Comprehensive documentation covering: - Architecture overview (platform → service providers → customers) - Payment flow diagrams - Stripe Connect concepts - Revenue models (platform fees, subscriptions) - Security & compliance - Webhook setup guide (production & development) - Testing procedures - Troubleshooting guide - FAQ section
File: README.md
Updated with: - Stripe setup instructions - Environment variable configuration - Database migration commands - Feature overview
Architecture¶
┌─────────────────────────────────────────────────────────┐
│ MEISTER BILL PLATFORM │
│ (Main Stripe Account - You) │
└─────────────────────────┬───────────────────────────────┘
│ Stripe Connect
│ (Express Accounts)
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ SERVICE │ │ SERVICE │ │ SERVICE │
│ PROVIDER 1 │ │ PROVIDER 2 │ │ PROVIDER 3 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
│ Payment Links │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ END CUSTOMER │ │ END CUSTOMER │ │ END CUSTOMER │
└───────────────┘ └───────────────┘ └───────────────┘
Payment Flow¶
- Service Provider Connects Stripe:
- Goes to Settings → Payments tab
- Clicks "Connect Stripe Account"
- Redirected to Stripe onboarding
- Completes business verification
-
Returns to Meister Bill with connected status
-
Generate Payment Link:
- Service provider creates/views invoice
- Clicks "Generate Payment Link"
- API creates Stripe Payment Link on their connected account
- Link stored in
documents.stripe_payment_link -
Service provider can copy and share link
-
Customer Pays Invoice:
- Customer clicks payment link
- Redirected to Stripe Checkout (hosted by Stripe)
- Enters payment details
- Completes payment
-
Money goes directly to service provider's Stripe account
-
Webhook Updates Invoice:
- Stripe sends
checkout.session.completedwebhook - API verifies signature
- Updates invoice status to "paid"
- Creates payment record
- Service provider sees updated status
Data Flow¶
┌─────────────────────────────────────────────────────────┐
│ 1. Service Provider → Settings → Connect Stripe │
└─────────────────┬───────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 2. API → StripeService.connectAccount() │
│ - Create Stripe Express account │
│ - Generate account link │
│ - Update service_providers.stripe_account_id │
└─────────────────┬───────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 3. User → Stripe Onboarding │
│ - Provide business details │
│ - Add bank account │
│ - Complete verification │
└─────────────────┬───────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 4. Stripe → Webhook → account.updated │
│ - Update stripe_account_status │
│ - Set stripe_onboarding_complete = true │
└─────────────────┬───────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 5. Service Provider → Generate Payment Link │
│ - API creates Stripe Payment Link │
│ - Store link in documents.stripe_payment_link │
└─────────────────┬───────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 6. Customer → Clicks Link → Stripe Checkout │
│ - Enters card details │
│ - Completes payment │
└─────────────────┬───────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 7. Stripe → Webhook → checkout.session.completed │
│ - Update invoice status to "paid" │
│ - Create payment record │
└─────────────────────────────────────────────────────────┘
Why Stripe Connect?¶
Benefits: - ✅ Service providers don't need their own Stripe account - ✅ Platform maintains control over onboarding - ✅ Simplified compliance (Stripe handles most) - ✅ Direct payments to service providers - ✅ No PCI compliance burden - ✅ Automatic fraud prevention - ✅ Multi-currency support (135+ currencies) - ✅ Multiple payment methods (card, Apple Pay, Google Pay, etc.)
Why Express Accounts? - Fast onboarding (5-10 minutes) - Stripe handles payout management - Service providers get their own Stripe Dashboard - Platform doesn't handle sensitive payment data - Easy to implement with minimal code
Security Features¶
- Webhook Signature Verification:
- All webhook events verified using
STRIPE_WEBHOOK_SECRET - Prevents unauthorized/fake webhook requests
-
Protects against replay attacks
-
Service Provider Isolation:
- Users can only access their own Stripe accounts
- User ID validation on all endpoints
-
Database-level foreign key constraints
-
Payment Link Metadata:
- Invoice ID, service provider ID embedded in payment link
- Ensures correct invoice is updated on payment
-
Prevents cross-account payment attribution
-
No Card Data Storage:
- All payment data handled by Stripe
- Meister Bill never sees card numbers
- PCI compliance handled by Stripe
Testing¶
Unit Tests¶
File: apps/api/src/services/StripeService.test.ts
Test coverage: - ✅ Account connection flow - ✅ Payment link generation - ✅ Webhook event handling - ✅ Status updates - ✅ Error handling
Manual Testing¶
- Development Setup: ```bash # Install Stripe CLI brew install stripe/stripe-cli/stripe
# Login stripe login
# Forward webhooks to local API stripe listen --forward-to http://localhost:3002/stripe/webhook
# Copy webhook secret to .env STRIPE_WEBHOOK_SECRET=whsec_... ```
- Test Payment Flow: ```bash # Start API pnpm --filter @meisterbill/api dev
# Start Web pnpm --filter @meisterbill/web dev
# Test with card: 4242 4242 4242 4242 # Expiry: any future date # CVC: any 3 digits ```
- Trigger Test Events:
bash stripe trigger checkout.session.completed stripe trigger account.updated stripe trigger payment_intent.succeeded
Files Changed¶
New Files¶
apps/api/src/services/StripeService.tsapps/api/src/services/StripeService.test.tsapps/api/src/routes/stripe.tsapps/web/composables/useStripe.tsdatabase/migrations/002_add_stripe_integration.sqldocs/stripe-workflow.mddocs/implementation-issue-28.md(this file)
Modified Files¶
apps/api/package.json- Addedstripedependencyapps/api/.env.example- Added Stripe environment variablesapps/api/src/index.ts- Registered/striperoutesapps/web/pages/member/settings.vue- Added Payments tabapps/web/pages/member/feature/invoicing/[id].vue- Added payment link UIschemas/src/ServiceProviderSchema.ts- Added Stripe fieldsschemas/src/DocumentSchema.ts- Added payment link fieldschemas/src/EventSchema.ts- Added Stripe eventsREADME.md- Added Stripe setup documentationdocs/meister-bill-features-catalogue.md- Updated feature list
Deployment¶
Production Setup¶
- Create Stripe Account:
- Sign up at stripe.com
-
Get API keys from Dashboard
-
Configure Webhook:
bash # Add webhook endpoint in Stripe Dashboard URL: https://api.meister-bill.com/stripe/webhook Events: checkout.session.completed, account.updated, payment_intent.succeeded -
Set Environment Variables:
bash fly secrets set STRIPE_SECRET_KEY=sk_live_... --app meisterbill-api fly secrets set STRIPE_WEBHOOK_SECRET=whsec_... --app meisterbill-api fly secrets set STRIPE_CONNECT_CLIENT_ID=ca_... --app meisterbill-api -
Run Migration:
bash psql $DATABASE_URL < database/migrations/002_add_stripe_integration.sql
Future Enhancements¶
- Application Fees:
- Take percentage of transactions for platform revenue
-
Add
application_fee_percentto payment link creation -
Express Dashboard Access:
- Add link to service provider's Stripe Dashboard
-
Use
stripe.accounts.createLoginLink() -
Payment Analytics:
- Track total payments processed
- Show payment success rate
-
Display average invoice amount
-
Refund Management:
- Allow service providers to issue refunds
- Update invoice status accordingly
-
Track refund history
-
Recurring Payments:
- Support subscription-based invoicing
- Automatic payment collection
-
Dunning management
-
Multi-Currency Pricing:
- Dynamic currency conversion
- Show prices in customer's currency
- Automatic settlement currency handling
Completion Status¶
✅ All tasks completed: - [x] Database migration with Stripe fields - [x] StripeService implementation - [x] Payment link generation - [x] Webhook handling - [x] Frontend UI (Settings + Invoice pages) - [x] Composable for Stripe operations - [x] Unit tests - [x] Documentation - [x] Environment configuration
Issue #28 is complete and deployed to production.