Skip to content

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

  1. Service Provider Connects Stripe:
  2. Goes to Settings → Payments tab
  3. Clicks "Connect Stripe Account"
  4. Redirected to Stripe onboarding
  5. Completes business verification
  6. Returns to Meister Bill with connected status

  7. Generate Payment Link:

  8. Service provider creates/views invoice
  9. Clicks "Generate Payment Link"
  10. API creates Stripe Payment Link on their connected account
  11. Link stored in documents.stripe_payment_link
  12. Service provider can copy and share link

  13. Customer Pays Invoice:

  14. Customer clicks payment link
  15. Redirected to Stripe Checkout (hosted by Stripe)
  16. Enters payment details
  17. Completes payment
  18. Money goes directly to service provider's Stripe account

  19. Webhook Updates Invoice:

  20. Stripe sends checkout.session.completed webhook
  21. API verifies signature
  22. Updates invoice status to "paid"
  23. Creates payment record
  24. 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

  1. Webhook Signature Verification:
  2. All webhook events verified using STRIPE_WEBHOOK_SECRET
  3. Prevents unauthorized/fake webhook requests
  4. Protects against replay attacks

  5. Service Provider Isolation:

  6. Users can only access their own Stripe accounts
  7. User ID validation on all endpoints
  8. Database-level foreign key constraints

  9. Payment Link Metadata:

  10. Invoice ID, service provider ID embedded in payment link
  11. Ensures correct invoice is updated on payment
  12. Prevents cross-account payment attribution

  13. No Card Data Storage:

  14. All payment data handled by Stripe
  15. Meister Bill never sees card numbers
  16. 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

  1. 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_... ```

  1. 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 ```

  1. 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.ts
  • apps/api/src/services/StripeService.test.ts
  • apps/api/src/routes/stripe.ts
  • apps/web/composables/useStripe.ts
  • database/migrations/002_add_stripe_integration.sql
  • docs/stripe-workflow.md
  • docs/implementation-issue-28.md (this file)

Modified Files

  • apps/api/package.json - Added stripe dependency
  • apps/api/.env.example - Added Stripe environment variables
  • apps/api/src/index.ts - Registered /stripe routes
  • apps/web/pages/member/settings.vue - Added Payments tab
  • apps/web/pages/member/feature/invoicing/[id].vue - Added payment link UI
  • schemas/src/ServiceProviderSchema.ts - Added Stripe fields
  • schemas/src/DocumentSchema.ts - Added payment link field
  • schemas/src/EventSchema.ts - Added Stripe events
  • README.md - Added Stripe setup documentation
  • docs/meister-bill-features-catalogue.md - Updated feature list

Deployment

Production Setup

  1. Create Stripe Account:
  2. Sign up at stripe.com
  3. Get API keys from Dashboard

  4. 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

  5. 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

  6. Run Migration: bash psql $DATABASE_URL < database/migrations/002_add_stripe_integration.sql

Future Enhancements

  1. Application Fees:
  2. Take percentage of transactions for platform revenue
  3. Add application_fee_percent to payment link creation

  4. Express Dashboard Access:

  5. Add link to service provider's Stripe Dashboard
  6. Use stripe.accounts.createLoginLink()

  7. Payment Analytics:

  8. Track total payments processed
  9. Show payment success rate
  10. Display average invoice amount

  11. Refund Management:

  12. Allow service providers to issue refunds
  13. Update invoice status accordingly
  14. Track refund history

  15. Recurring Payments:

  16. Support subscription-based invoicing
  17. Automatic payment collection
  18. Dunning management

  19. Multi-Currency Pricing:

  20. Dynamic currency conversion
  21. Show prices in customer's currency
  22. 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.