The "Vertical Slice" Boilerplate for robust Flutter payments.
Maintained by Arkbridge Solutions
When Serverpod came out it was a game changer for Flutter developers because now we could build full-stack applications with a single language (Dart). It is now possible to integrate payments in backend and frontend (on 3 different platforms: Web, Android and iOS) in a single codebase and in just a few hours.
This repository is a clean, vertical slice of a production payment flow. It uses:
- Serverpod 3.0 (RC): Leveraging the latest backend architecture
- Stripe Payment Sheet: Native UI for Card, Apple Pay, and Google Pay
- Secure Webhooks: Verifying signatures and handling idempotency
- Database Logging: Storing payment status in Postgres (not in memory)
We follow the "Verify, Don't Trust" principle. The App handles the UI, but the Server handles the logic.
- App: Requests a PaymentIntent from Serverpod backend
- Server: Creates an Intent and logs a pending record in the database
- App: Displays the native Stripe Payment Sheet (mobile) or PaymentElement (web)
- Stripe: Charges the card and fires a webhook to Serverpod
- Server: Validates the webhook signature and updates the database record to
succeeded
- Flutter SDK (Latest Stable)
- Serverpod (Version 3.0+) see serverpod docs
- Docker Desktop (Running)
- Stripe Account & Stripe CLI (For local webhook testing) See stripe docs
Navigate to the server directory and start the infrastructure.
cd stripepod_server
docker compose up --build --detachDuplicate the password template and fill in your secrets.
cp config/passwords.yaml.example config/passwords.yamlEdit passwords.yaml: Add your Database password and Stripe Secret Key (sk_test_...).
To receive webhooks locally, you must tunnel them to your localhost.
Login:
stripe loginStart listening and forward to your Serverpod endpoint:
stripe listen --forward-to http://localhost:8082/webhookCopy the Signing Secret (whsec_...) output by the CLI and add it to your passwords.yaml or environment config.
Apply the database migrations (creates the payment_log table) and start the server.
dart bin/main.dart --apply-migrationsLaunch the Flutter client.
cd stripepod_flutter
flutter runYou will notice we generate a table payments and write to it immediately when an intent is created.
Tip: Never rely on in-memory state for payments. If your server restarts during a transaction, the record is lost. Always write to disk (DB).
The product catalog is hardcoded in the Flutter UI.
Tip: We did this to keep the boilerplate dependency-free. In a real app, you would fetch this from the DB, but for this starter kit, we prioritized readability over complexity.
We used StatefulWidget and ValueNotifier instead of Bloc/Riverpod.
Tip: Dependencies are debt. This boilerplate is designed to be copy-pasted into any architecture without conflicting with your existing state management.
This boilerplate handles the "First Transaction." Scaling to subscriptions, handling proration, and automating DevOps (Terraform/K8s) requires a deeper strategy.
Arkbridge Solutions specializes in Dart Full-Stack & Flutter.
Disclaimer: This code is provided "as is" for educational purposes. Always perform a security audit before deploying financial code to production. Before using this code in production, we recommend you to implement proper authorization of the pay endpoint and app attestation.