Skip to content
cccskills
FreePublished on GitHub

payment-integration

Payment processing integration with Stripe and PayPal for Flutter

The English summary is being prepared. Showing the original description.

Works withStripe
See how to install

Files included (1)

  • SKILL.md8.6 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Skill 17: Payment Integration

Overview

This skill provides comprehensive payment processing capabilities for Flutter applications using Stripe and PayPal. Supports one-time payments, subscriptions, and PCI-compliant payment flows.

Features

  • Stripe integration with Payment Intents
  • PayPal checkout integration
  • PCI DSS compliance (sensitive data never touches your server)
  • Subscription management
  • Apple Pay and Google Pay support
  • Payment method storage
  • Webhook handling

Installation

Dependencies

dependencies:
  flutter_stripe: ^10.0.0
  paypal_sdk: ^1.0.0
  pay: ^1.1.2  # For Apple Pay / Google Pay

Stripe Integration

1. Setup

import 'package:flutter_stripe/flutter_stripe.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Stripe
  Stripe.publishableKey = 'pk_test_your_key_here';
  await Stripe.instance.applySettings();

  runApp(MyApp());
}

2. Payment Service

import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class PaymentService {
  final String backendUrl;

  PaymentService({required this.backendUrl});

  /// Create a payment intent on your backend
  Future<Map<String, dynamic>> createPaymentIntent(
    double amount,
    String currency,
  ) async {
    final response = await http.post(
      Uri.parse('$backendUrl/create-payment-intent'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({
        'amount': (amount * 100).toInt(), // Stripe uses cents
        'currency': currency,
      }),
    );

    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception('Failed to create payment intent');
    }
  }

  /// Present payment sheet
  Future<bool> presentPaymentSheet(String clientSecret) async {
    try {
      // Initialize payment sheet
      await Stripe.instance.initPaymentSheet(
        paymentSheetParameters: SetupPaymentSheetParameters(
          paymentIntentClientSecret: clientSecret,
          merchantDisplayName: '{{PROJECT_NAME}}',
          style: ThemeMode.system,
        ),
      );

      // Display payment sheet
      await Stripe.instance.presentPaymentSheet();

      return true;
    } on StripeException catch (e) {
      if (e.error.code == FailureCode.Canceled) {
        return false; // User canceled
      }
      rethrow;
    }
  }

  /// Create and confirm payment
  Future<bool> makePayment(double amount, String currency) async {
    try {
      // 1. Create payment intent
      final paymentIntent = await createPaymentIntent(amount, currency);

      // 2. Present payment sheet
      final success = await presentPaymentSheet(paymentIntent['clientSecret']);

      return success;
    } catch (e) {
      print('Payment error: $e');
      return false;
    }
  }
}

3. Payment Provider

import 'package:flutter_riverpod/flutter_riverpod.dart';

class PaymentState {
  final bool isProcessing;
  final bool paymentSuccess;
  final String? error;

  PaymentState({
    this.isProcessing = false,
    this.paymentSuccess = false,
    this.error,
  });

  PaymentState copyWith({
    bool? isProcessing,
    bool? paymentSuccess,
    String? error,
  }) {
    return PaymentState(
      isProcessing: isProcessing ?? this.isProcessing,
      paymentSuccess: paymentSuccess ?? this.paymentSuccess,
      error: error,
    );
  }
}

class PaymentNotifier extends StateNotifier<PaymentState> {
  final PaymentService _paymentService;

  PaymentNotifier(this._paymentService) : super(PaymentState());

  Future<void> processPayment(double amount, String currency) async {
    state = state.copyWith(isProcessing: true, error: null);

    try {
      final success = await _paymentService.makePayment(amount, currency);

      state = state.copyWith(
        isProcessing: false,
        paymentSuccess: success,
        error: success ? null : 'Payment was cancelled or failed',
      );
    } catch (e) {
      state = state.copyWith(
        isProcessing: false,
        paymentSuccess: false,
        error: e.toString(),
      );
    }
  }
}

final paymentProvider = StateNotifierProvider<PaymentNotifier, PaymentState>(
  (ref) => PaymentNotifier(PaymentService(backendUrl: 'https://your-api.com')),
);

4. Payment UI

class PaymentPage extends ConsumerWidget {
  const PaymentPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final paymentState = ref.watch(paymentProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('Payment')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Text(
              'Total: \$20.00',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const SizedBox(height: 32),
            if (paymentState.isProcessing)
              const CircularProgressIndicator()
            else
              ElevatedButton(
                onPressed: () {
                  ref.read(paymentProvider.notifier)
                      .processPayment(20.00, 'USD');
                },
                child: const Text('Pay Now'),
              ),
            if (paymentState.error != null)
              Text(
                paymentState.error!,
                style: const TextStyle(color: Colors.red),
              ),
            if (paymentState.paymentSuccess)
              const Text(
                'Payment successful!',
                style: TextStyle(color: Colors.green),
              ),
          ],
        ),
      ),
    );
  }
}

Subscription Management

class SubscriptionService {
  final String backendUrl;

  SubscriptionService({required this.backendUrl});

  Future<Map<String, dynamic>> createSubscription(
    String customerId,
    String priceId,
  ) async {
    final response = await http.post(
      Uri.parse('$backendUrl/create-subscription'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({
        'customerId': customerId,
        'priceId': priceId,
      }),
    );

    return jsonDecode(response.body);
  }

  Future<void> cancelSubscription(String subscriptionId) async {
    await http.post(
      Uri.parse('$backendUrl/cancel-subscription'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({'subscriptionId': subscriptionId}),
    );
  }
}

Apple Pay / Google Pay

import 'package:pay/pay.dart';

class DigitalWalletPayment extends StatelessWidget {
  final double amount;
  final String currency;

  const DigitalWalletPayment({
    super.key,
    required this.amount,
    required this.currency,
  });

  @override
  Widget build(BuildContext context) {
    return GooglePayButton(
      paymentConfigurationAsset: 'google_pay_config.json',
      paymentItems: [
        PaymentItem(
          label: 'Total',
          amount: amount.toStringAsFixed(2),
          status: PaymentItemStatus.final_price,
        ),
      ],
      type: GooglePayButtonType.pay,
      onPaymentResult: (result) {
        // Handle payment result
        print('Payment result: $result');
      },
    );
  }
}

Security Best Practices

  1. Never store card data - Use Stripe tokens only
  2. Backend validation - Always verify payments on server
  3. HTTPS only - All API calls must use HTTPS
  4. PCI Compliance - Don't handle raw card numbers
  5. Webhook verification - Verify Stripe signatures

Backend Requirements

Your backend must implement:

// Node.js/Express example
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.post('/create-payment-intent', async (req, res) => {
  const { amount, currency } = req.body;

  const paymentIntent = await stripe.paymentIntents.create({
    amount,
    currency,
    automatic_payment_methods: { enabled: true },
  });

  res.json({ clientSecret: paymentIntent.client_secret });
});

app.post('/webhook', async (req, res) => {
  const sig = req.headers['stripe-signature'];
  const event = stripe.webhooks.constructEvent(
    req.body,
    sig,
    process.env.STRIPE_WEBHOOK_SECRET
  );

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      // Fulfill the purchase
      break;
    case 'payment_intent.payment_failed':
      // Notify user
      break;
  }

  res.json({ received: true });
});

Testing

Use Stripe test cards:

  • 4242 4242 4242 4242 - Success
  • 4000 0000 0000 9995 - Declined
  • 4000 0000 0000 3220 - Requires 3D Secure

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Machine Learning integration for Flutter using TensorFlow Lite and Firebase ML

The English summary is being prepared. Showing the original description.

HabitaNexus/monorepo3Updated Oct 15, 2026

Google Maps and Mapbox integration for Flutter applications

The English summary is being prepared. Showing the original description.

HabitaNexus/monorepo3Updated Oct 15, 2026

Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.

The English summary is being prepared. Showing the original description.

HabitaNexus/monorepo3Updated Oct 15, 2026

Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.

The English summary is being prepared. Showing the original description.

HabitaNexus/monorepo3Updated Oct 15, 2026

Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.

The English summary is being prepared. Showing the original description.

HabitaNexus/monorepo3Updated Oct 15, 2026

Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.

The English summary is being prepared. Showing the original description.

HabitaNexus/monorepo3Updated Oct 15, 2026

See all skills by HabitaNexus

Report a problem with this skill