stripe_smart_checkout 0.1.1
stripe_smart_checkout: ^0.1.1 copied to clipboard
Modern Flutter package for Stripe Checkout integration with support for one-time payments and subscriptions. Clean architecture, type-safe, cross-platform.
π Stripe Smart Checkout #
A modern, production-ready Flutter package for seamless Stripe Checkout integration with clean architecture principles. Built with best practices, type safety, and comprehensive error handling.
β¨ Features #
- π― Simple Integration - Get started with just a few lines of code
- π³ One-Time Payments - Support for single payment transactions
- π Subscriptions - Full support for recurring payments with flexible billing intervals
- π Trial Periods - Optional free trial periods for subscriptions
- ποΈ Clean Architecture - Built with separation of concerns and SOLID principles
- π Type Safe - Full type safety with Freezed and JSON serialization
- β‘ WebView Support - Integrated checkout experience with WebView
- π External Browser - Option to open checkout in default browser
- π± Cross-Platform - Works on iOS, Android, Web, macOS, Windows, and Linux
- π‘οΈ Error Handling - Comprehensive exception handling with detailed error messages
- π Real-time Callbacks - Success and error callbacks for payment status
- π§ͺ Test Mode Ready - Easy switching between test and production modes
- π¦ Zero Native Code - Pure Dart implementation, no platform-specific setup needed
π Table of Contents #
- Installation
- Getting Started
- Payment Types
- Configuration
- Usage Examples
- API Reference
- Error Handling
- Testing
- Example App
- License
π¦ Installation #
Add this to your package's pubspec.yaml file:
dependencies:
stripe_smart_checkout: ^0.1.0
Then run:
flutter pub get
π Getting Started #
1. Get Your Stripe API Keys #
Get your API keys from the Stripe Dashboard:
- Test mode keys: Use for development (starts with
pk_test_andsk_test_) - Live mode keys: Use for production (starts with
pk_live_andsk_live_)
β οΈ Security Warning: Never commit your secret keys to version control. Use environment variables or secure configuration management.
2. Import the Package #
import 'package:stripe_smart_checkout/stripe_smart_checkout.dart';
3. Quick Start - One-Time Payment #
import 'package:flutter/material.dart';
import 'package:stripe_smart_checkout/stripe_smart_checkout.dart';
class CheckoutScreen extends StatelessWidget {
const CheckoutScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Checkout')),
body: Center(
child: ElevatedButton(
onPressed: () => _startCheckout(context),
child: const Text('Pay Now'),
),
),
);
}
void _startCheckout(BuildContext context) {
// Configure Stripe
const config = StripeCheckoutConfig(
publicKey: 'pk_test_your_key_here',
secretKey: 'sk_test_your_key_here',
isTestMode: true,
);
// Navigate to checkout
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => StripeCheckoutWidget(
config: config,
amount: 49.99,
currency: 'usd',
mode: CheckoutMode.payment, // One-time payment
items: [
const CheckoutItem.payment(
name: 'Flutter Course',
price: 49.99,
quantity: 1,
description: 'Advanced Flutter Development',
),
],
customerEmail: '[email protected]',
onSuccess: (session) {
print('Payment successful! Session ID: ${session.id}');
Navigator.of(context).pop();
// Handle successful payment
},
onError: (error) {
print('Payment failed: ${error.message}');
// Handle payment error
},
),
),
);
}
}
π³ Payment Types #
This package supports both one-time payments and recurring subscriptions with a unified, easy-to-use API.
One-Time Payments #
Perfect for selling products, courses, digital downloads, or any single-transaction purchase.
Basic One-Time Payment
StripeCheckoutWidget(
config: config,
amount: 99.99,
currency: 'usd',
mode: CheckoutMode.payment, // Specify payment mode
items: [
const CheckoutItem.payment(
name: 'Premium Course Bundle',
price: 99.99,
quantity: 1,
description: 'Complete Flutter & Dart Course Bundle',
),
],
customerEmail: '[email protected]',
onSuccess: (session) {
// Payment completed successfully
print('Order ID: ${session.id}');
print('Amount paid: \$${session.amount}');
},
onError: (error) {
print('Payment failed: ${error.message}');
},
)
Multiple Items (Shopping Cart)
StripeCheckoutWidget(
config: config,
amount: 179.97,
currency: 'usd',
mode: CheckoutMode.payment,
items: [
const CheckoutItem.payment(
name: 'Flutter Course',
price: 49.99,
quantity: 1,
),
const CheckoutItem.payment(
name: 'Clean Architecture Book',
price: 29.99,
quantity: 2,
),
const CheckoutItem.payment(
name: 'Premium Laptop',
price: 1499.99,
quantity: 1,
),
],
onSuccess: (session) {
// Process order fulfillment
fulfillOrder(session.id);
},
)
Subscription Payments #
Create recurring revenue with flexible subscription billing - monthly, yearly, weekly, or daily intervals.
Monthly Subscription
StripeCheckoutWidget(
config: config,
amount: 29.99,
currency: 'usd',
mode: CheckoutMode.subscription, // Subscription mode
items: [
const CheckoutItem.subscription(
name: 'Pro Plan',
price: 29.99,
description: 'Monthly subscription with all pro features',
billingInterval: BillingInterval.month,
),
],
customerEmail: user.email,
onSuccess: (session) {
// Subscription created successfully
await activateSubscription(
userId: user.id,
sessionId: session.id,
plan: 'pro',
);
},
)
Annual Subscription with Free Trial
StripeCheckoutWidget(
config: config,
amount: 299.99,
currency: 'usd',
mode: CheckoutMode.subscription,
items: [
const CheckoutItem.subscription(
name: 'Premium Annual Plan',
price: 299.99,
description: 'Annual subscription - Save 20%!',
billingInterval: BillingInterval.year,
trialPeriodDays: 14, // 14-day free trial
),
],
onSuccess: (session) {
// User gets 14 days free, then charged annually
print('Trial ends: ${DateTime.now().add(Duration(days: 14))}');
},
)
Weekly Subscription
StripeCheckoutWidget(
config: config,
amount: 9.99,
currency: 'usd',
mode: CheckoutMode.subscription,
items: [
const CheckoutItem.subscription(
name: 'Starter Plan',
price: 9.99,
billingInterval: BillingInterval.week,
trialPeriodDays: 7, // 7-day free trial
),
],
)
Daily Subscription
StripeCheckoutWidget(
config: config,
amount: 1.99,
currency: 'usd',
mode: CheckoutMode.subscription,
items: [
const CheckoutItem.subscription(
name: 'Daily Access Pass',
price: 1.99,
billingInterval: BillingInterval.day,
),
],
)
Custom Billing Intervals
Charge every N intervals (e.g., every 3 months, every 6 weeks):
const CheckoutItem.subscription(
name: 'Quarterly Plan',
price: 79.99,
billingInterval: BillingInterval.month,
intervalCount: 3, // Bill every 3 months
)
Using Existing Stripe Price IDs
If you've pre-created prices in the Stripe Dashboard:
const CheckoutItem.subscription(
name: 'Enterprise Plan',
price: 999.99,
priceId: 'price_1234567890', // Your Stripe Price ID
)
Choosing Between Payment Types #
Use this decision guide:
| Use Case | Payment Type | Example |
|---|---|---|
| One-time purchase | CheckoutMode.payment |
Courses, ebooks, physical products |
| Monthly service | CheckoutMode.subscription |
SaaS apps, memberships |
| Annual membership | CheckoutMode.subscription |
Yearly plans with savings |
| Pay-per-use | CheckoutMode.payment |
Pay as you go, credits |
| Recurring donations | CheckoutMode.subscription |
Monthly supporter programs |
Dynamic Payment Type Selection #
Automatically determine payment type based on product:
void checkout(Product product) {
final isSubscription = product.type == ProductType.subscription;
final mode = isSubscription
? CheckoutMode.subscription
: CheckoutMode.payment;
final CheckoutItem item;
if (isSubscription) {
// Convert billing period to interval
BillingInterval interval = BillingInterval.month;
if (product.billingPeriod == 'week') {
interval = BillingInterval.week;
} else if (product.billingPeriod == 'year') {
interval = BillingInterval.year;
}
item = CheckoutItem.subscription(
name: product.name,
price: product.price,
description: product.description,
billingInterval: interval,
trialPeriodDays: product.trialDays,
);
} else {
item = CheckoutItem.payment(
name: product.name,
price: product.price,
quantity: quantity,
description: product.description,
);
}
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => StripeCheckoutWidget(
config: config,
amount: product.price,
currency: 'usd',
items: [item],
mode: mode,
onSuccess: (session) {
if (isSubscription) {
handleSubscriptionSuccess(session);
} else {
handlePaymentSuccess(session);
}
},
),
),
);
}
βοΈ Configuration #
StripeCheckoutConfig #
Configure your Stripe integration:
const config = StripeCheckoutConfig(
publicKey: 'pk_test_...', // Your Stripe publishable key
secretKey: 'sk_test_...', // Your Stripe secret key
isTestMode: true, // Use test mode (default: false)
);
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
publicKey |
String |
β Yes | Your Stripe publishable key |
secretKey |
String |
β Yes | Your Stripe secret key |
isTestMode |
bool |
β No | Enable test mode (default: false) |
CheckoutItem #
Define items for checkout. Use the appropriate named constructor based on payment type:
For One-Time Payments
const item = CheckoutItem.payment(
name: 'Product Name',
price: 29.99,
quantity: 2,
description: 'Optional description',
);
Payment Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
String |
β Yes | Product or service name |
price |
double |
β Yes | Price per unit |
quantity |
int |
β No | Quantity (default: 1) |
description |
String? |
β No | Optional description |
For Subscriptions
const item = CheckoutItem.subscription(
name: 'Subscription Plan',
price: 29.99,
billingInterval: BillingInterval.month,
intervalCount: 1,
trialPeriodDays: 14,
description: 'Optional description',
priceId: 'price_123', // Optional: use existing Stripe Price
);
Subscription Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
String |
β Yes | Subscription plan name |
price |
double |
β Yes | Recurring price amount |
billingInterval |
BillingInterval |
β No | Billing frequency (default: month) |
intervalCount |
int |
β No | Interval multiplier (default: 1) |
trialPeriodDays |
int? |
β No | Free trial days (optional) |
description |
String? |
β No | Optional description |
priceId |
String? |
β No | Existing Stripe Price ID (optional) |
Billing Intervals:
BillingInterval.day- Daily billingBillingInterval.week- Weekly billingBillingInterval.month- Monthly billingBillingInterval.year- Annual billing
π‘ Usage Examples #
E-Commerce: Single Product #
StripeCheckoutWidget(
config: config,
amount: 99.99,
currency: 'usd',
mode: CheckoutMode.payment,
items: [
const CheckoutItem.payment(
name: 'Wireless Headphones',
price: 99.99,
quantity: 1,
description: 'Premium noise-cancelling headphones',
),
],
customerEmail: '[email protected]',
onSuccess: (session) {
// Payment successful - fulfill order
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Order Confirmed!'),
content: Text('Order ID: ${session.id}\nAmount: \$${session.amount}'),
),
);
},
onError: (error) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Payment failed: ${error.message}'),
backgroundColor: Colors.red,
),
);
},
)
E-Commerce: Shopping Cart #
StripeCheckoutWidget(
config: config,
amount: 179.97,
currency: 'usd',
mode: CheckoutMode.payment,
items: [
const CheckoutItem.payment(
name: 'T-Shirt',
price: 29.99,
quantity: 2,
description: 'Cotton t-shirt - Size M',
),
const CheckoutItem.payment(
name: 'Jeans',
price: 59.99,
quantity: 1,
description: 'Blue denim jeans - Size 32',
),
const CheckoutItem.payment(
name: 'Shipping',
price: 9.99,
quantity: 1,
description: 'Express shipping',
),
],
customerEmail: '[email protected]',
onSuccess: (session) async {
// Clear cart and fulfill order
await clearCart();
await createOrder(session.id);
Navigator.pushReplacementNamed(context, '/order-success');
},
)
SaaS: Monthly Subscription #
StripeCheckoutWidget(
config: config,
amount: 29.99,
currency: 'usd',
mode: CheckoutMode.subscription,
items: [
const CheckoutItem.subscription(
name: 'Pro Plan',
price: 29.99,
description: 'Full access to all pro features',
billingInterval: BillingInterval.month,
),
],
customerEmail: user.email,
onSuccess: (session) async {
// Activate user's subscription
await activateSubscription(
userId: user.id,
sessionId: session.id,
plan: 'pro',
);
Navigator.pushReplacementNamed(context, '/subscription-active');
},
onError: (error) {
showErrorDialog(context, 'Unable to start subscription');
},
)
SaaS: Annual Plan with Trial #
StripeCheckoutWidget(
config: config,
amount: 299.99,
currency: 'usd',
mode: CheckoutMode.subscription,
items: [
const CheckoutItem.subscription(
name: 'Premium Annual',
price: 299.99,
description: 'Save 20% with annual billing!',
billingInterval: BillingInterval.year,
trialPeriodDays: 30, // 30-day free trial
),
],
customerEmail: user.email,
onSuccess: (session) async {
await activateTrialSubscription(
userId: user.id,
sessionId: session.id,
trialEndsAt: DateTime.now().add(Duration(days: 30)),
);
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Trial Started!'),
content: Text('Your 30-day free trial has begun. '
'You\'ll be charged \$299.99 on ${_formatDate(30.days.fromNow)}'),
),
);
},
)
Different Currency Support #
// EUR checkout
StripeCheckoutWidget(
config: config,
amount: 49.99,
currency: 'eur',
items: [...],
)
// GBP checkout
StripeCheckoutWidget(
config: config,
amount: 39.99,
currency: 'gbp',
items: [...],
)
// JPY checkout (no decimal places)
StripeCheckoutWidget(
config: config,
amount: 5000,
currency: 'jpy',
items: [...],
)
Opening Checkout in External Browser #
The widget includes a button to open the checkout in the device's default browser:
// The open-in-browser button is automatically available in the app bar
// Users can tap it to continue checkout in their preferred browser
π API Reference #
StripeCheckoutWidget #
Main widget for displaying the Stripe Checkout interface.
StripeCheckoutWidget({
required StripeCheckoutConfig config,
required double amount,
required String currency,
required List<CheckoutItem> items,
CheckoutMode mode = CheckoutMode.payment,
String? customerEmail,
void Function(CheckoutSession)? onSuccess,
void Function(StripeCheckoutException)? onError,
})
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
config |
StripeCheckoutConfig |
β Yes | Stripe configuration |
amount |
double |
β Yes | Total amount to charge |
currency |
String |
β Yes | Three-letter ISO currency code (e.g., 'usd', 'eur') |
items |
List<CheckoutItem> |
β Yes | List of items being purchased |
mode |
CheckoutMode |
β No | Payment or subscription mode (default: payment) |
customerEmail |
String? |
β No | Pre-fill customer email |
onSuccess |
Function(CheckoutSession)? |
β No | Called when payment succeeds |
onError |
Function(StripeCheckoutException)? |
β No | Called when payment fails |
Checkout Modes:
CheckoutMode.payment- One-time payment (default)CheckoutMode.subscription- Recurring subscription
CheckoutSession #
Represents a completed checkout session.
class CheckoutSession {
final String id;
final String url;
final String status;
final bool paid;
final double amount;
final String currency;
final String? customerEmail;
}
StripeCheckoutRepository #
For advanced use cases, you can use the repository directly:
final repository = StripeCheckoutRepositoryImpl(config: config);
// Create one-time payment session
final paymentSession = await repository.createCheckoutSession(
amount: 49.99,
currency: 'usd',
items: [
CheckoutItem.payment(
name: 'Course',
price: 49.99,
quantity: 1,
),
],
mode: CheckoutMode.payment,
customerEmail: '[email protected]',
);
// Create subscription session
final subscriptionSession = await repository.createCheckoutSession(
amount: 29.99,
currency: 'usd',
items: [
CheckoutItem.subscription(
name: 'Pro Plan',
price: 29.99,
billingInterval: BillingInterval.month,
trialPeriodDays: 14,
),
],
mode: CheckoutMode.subscription,
customerEmail: '[email protected]',
);
// Retrieve session
final session = await repository.retrieveCheckoutSession(sessionId);
// Verify payment
final isPaid = await repository.verifyPayment(sessionId);
π¨ Error Handling #
The package provides comprehensive error handling with specific exception types:
Exception Types #
try {
// Checkout code
} on StripePaymentException catch (e) {
// Payment-related errors (declined cards, insufficient funds, etc.)
print('Payment error: ${e.message}');
print('Error code: ${e.errorCode}');
} on StripeNetworkException catch (e) {
// Network connectivity issues
print('Network error: ${e.message}');
} on StripeCheckoutException catch (e) {
// General checkout errors
print('Checkout error: ${e.message}');
}
Common Error Scenarios #
1. Payment Declined
onError: (error) {
if (error is StripePaymentException) {
if (error.errorCode == 'card_declined') {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Card Declined'),
content: const Text('Your card was declined. Please try another payment method.'),
),
);
}
}
}
2. Network Issues
onError: (error) {
if (error is StripeNetworkException) {
showSnackBar(
'Network error. Please check your connection and try again.',
);
}
}
3. Invalid Configuration
// Validate keys before use
final publicKeyError = StripeCheckoutConfig.validatePublicKey(publicKey);
final secretKeyError = StripeCheckoutConfig.validateSecretKey(secretKey);
if (publicKeyError != null) {
print('Invalid public key: $publicKeyError');
}
if (secretKeyError != null) {
print('Invalid secret key: $secretKeyError');
}
π§ͺ Testing #
Test Mode #
Always use test mode during development:
const config = StripeCheckoutConfig(
publicKey: 'pk_test_...',
secretKey: 'sk_test_...',
isTestMode: true, // Enable test mode
);
Test Cards #
Use Stripe's test cards for testing:
| Card Number | Description |
|---|---|
4242 4242 4242 4242 |
Successful payment |
4000 0000 0000 9995 |
Declined payment |
4000 0000 0000 0002 |
Card declined |
4000 0025 0000 3155 |
Requires authentication |
More test cards: Stripe Testing Guide
Unit Testing #
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:stripe_smart_checkout/stripe_smart_checkout.dart';
void main() {
group('StripeCheckoutConfig', () {
test('validates public key format', () {
expect(
StripeCheckoutConfig.validatePublicKey('invalid'),
'Invalid Stripe public key format',
);
expect(
StripeCheckoutConfig.validatePublicKey('pk_test_valid'),
null,
);
});
test('validates secret key format', () {
expect(
StripeCheckoutConfig.validateSecretKey('invalid'),
'Invalid Stripe secret key format',
);
expect(
StripeCheckoutConfig.validateSecretKey('sk_test_valid'),
null,
);
});
});
}
π± Example App #
A complete, simplified single-page example app is included in the example folder, demonstrating:
- π± Single-Page Interface - All products visible at once in a clean, scrollable layout
- π³ One-Time Payments - 3 example products (course, book, laptop)
- π Subscription Plans - 3 subscription tiers (weekly, monthly, annual)
- π― Direct Checkout - Tap any product to start checkout immediately
- β Success/Error Handling - Professional dialogs with detailed information
- π§ͺ Test Cards Reference - Built-in test card information for easy testing
- π¨ Clean UI - Material Design with color-coded product cards
- π¦ Minimal Dependencies - Only requires
flutterandstripe_smart_checkout
Example Structure #
The example is now a single-file application (example/lib/main.dart) with ~580 lines that showcases the complete integration without complex navigation or state management. Perfect for understanding the package quickly!
Example Products Included #
One-Time Payments (Blue Cards):
- Flutter Course - $49.99
- Clean Architecture Book - $29.99
- Premium Development Laptop - $1,499.99
Subscription Plans (Purple Cards):
- Starter Plan - $9.99/week with 7-day trial
- Pro Plan - $29.99/month
- Premium Plan - $299.99/year with 14-day trial
Running the Example #
cd example
flutter pub get
flutter run
Setup: Replace the API keys in lib/main.dart:39-42 with your Stripe test keys:
_stripeConfig = const StripeCheckoutConfig(
publicKey: 'pk_test_YOUR_KEY_HERE',
secretKey: 'sk_test_YOUR_KEY_HERE',
isTestMode: true,
);
What You'll See #
- Package Info Card - Setup instructions and quick start guide
- One-Time Payments Section - 3 products for single transactions
- Subscription Plans Section - 3 plans with different billing intervals
- Test Cards Section - Reference for Stripe test cards
Tap any product β Enter payment details β See success dialog with session info!
ποΈ Architecture #
This package follows clean architecture principles:
lib/
βββ src/
β βββ core/ # Core functionality
β β βββ stripe_checkout_config.dart
β β βββ stripe_checkout_exceptions.dart
β βββ domain/ # Business logic layer
β β βββ entities/
β β β βββ checkout_session.dart
β β βββ repositories/
β β βββ stripe_checkout_repository.dart
β βββ data/ # Data layer
β β βββ repositories/
β β βββ stripe_checkout_repository_impl.dart
β βββ presentation/ # UI layer
β βββ stripe_checkout_widget.dart
βββ stripe_smart_checkout.dart
Design Principles #
- Separation of Concerns: Clear boundaries between layers
- Dependency Inversion: High-level modules don't depend on low-level modules
- Single Responsibility: Each class has one reason to change
- Open/Closed: Open for extension, closed for modification
- Type Safety: Full type safety with Freezed and JSON serialization
π License #
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2025 SnippetCoder
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
π Acknowledgments #
- Stripe for their excellent payment platform
- Flutter team for the amazing framework
- All contributors who helped improve this package
π Support #
- π§ Email: [email protected]
- π Official Youtube Channel: Official Youtube Channel
πΊοΈ Roadmap #
- β β Subscriptions with trial periods - COMPLETED
- β β Flexible billing intervals (day, week, month, year) - COMPLETED
- β Implement Apple Pay and Google Pay
- β Add support for Stripe Connect
- β Implement refund functionality
- β Add customer portal for managing subscriptions
- β Support for multiple currencies in a single checkout
- β Enhanced analytics and reporting
- β Webhooks integration helper
- β Subscription management (upgrade, downgrade, cancel)
- β Proration handling for subscription changes
- β Coupon and discount code support
Made with β€οΈ by SnippetCoder