Flutter Quick Start - Pagamentos Recorrentes¶
Guia rápido de 5 minutos para integrar pagamentos recorrentes no Flutter.
Dois Gateways Disponíveis
O FDPlay suporta Asaas (gateway BR) e Stripe (internacional). Este guia cobre o Asaas. Para Stripe, veja a seção Stripe abaixo ou a documentação completa.
Asaas Quick Start¶
O Asaas é o gateway principal para clientes brasileiros. Os dados do cartão são enviados diretamente ao backend — não é necessária criptografia client-side.
1. Adicionar Dependência (30 segundos)¶
2. Criar Serviço (2 minutos)¶
// lib/services/subscription_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class SubscriptionService {
final String baseUrl = 'https://sua-api.com'; // ← ALTERE AQUI
final String Function() getToken;
SubscriptionService({required this.getToken});
Future<Map<String, dynamic>> subscribe({
required String planId,
required String cardNumber,
required String cardHolder,
required String cardCvv,
required String cardExpMonth,
required String cardExpYear,
required String holderName,
required String holderEmail,
required String holderCpfCnpj,
required String holderPostalCode,
required String holderAddressNumber,
required String holderPhone,
}) async {
// Criar assinatura via Asaas
final response = await http.post(
Uri.parse('$baseUrl/api/v1/asaas/subscribe'),
headers: {
'Authorization': 'Bearer ${getToken()}',
'Content-Type': 'application/json',
},
body: json.encode({
'plan_id': planId,
'credit_card': {
'holderName': cardHolder.toUpperCase(),
'number': cardNumber.replaceAll(' ', ''),
'expiryMonth': cardExpMonth.padLeft(2, '0'),
'expiryYear': cardExpYear,
'ccv': cardCvv,
},
'credit_card_holder_info': {
'name': holderName,
'email': holderEmail,
'cpfCnpj': holderCpfCnpj,
'postalCode': holderPostalCode,
'addressNumber': holderAddressNumber,
'phone': holderPhone,
},
}),
);
if (response.statusCode != 201) {
throw Exception(json.decode(response.body)['detail']);
}
return json.decode(response.body);
}
}
3. Usar no Checkout (1 minuto)¶
// No seu widget de checkout
Future<void> _onSubmitPayment() async {
try {
final service = SubscriptionService(
getToken: () => yourUserToken, // ← JWT do usuário logado
);
final subscription = await service.subscribe(
planId: 'plan-basic',
cardNumber: _cardNumberController.text,
cardHolder: _cardHolderController.text,
cardCvv: _cvvController.text,
cardExpMonth: _expMonthController.text,
cardExpYear: _expYearController.text,
holderName: 'Nome Completo',
holderEmail: 'email@example.com',
holderCpfCnpj: '24971563792',
holderPostalCode: '89223005',
holderAddressNumber: '277',
holderPhone: '47998781877',
);
// Sucesso!
Navigator.pushReplacementNamed(context, '/subscription-success');
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erro: $e')),
);
}
}
Testar¶
Cartões de Teste (Sandbox)¶
| Bandeira | Número | Status |
|---|---|---|
| Visa | 4539620659922097 |
Aprovado |
| Mastercard | 5555666677778884 |
Aprovado |
Dados:
- Titular: JOAO DA SILVA
- Validade: 12/2030
- CVV: 123
Documentação Completa¶
Para exemplos completos, troubleshooting e detalhes:
Importante¶
- Backend transmite dados do cartão ao Asaas via HTTPS (PCI compliant)
- Asaas cobra automaticamente todo mês (modelo Netflix)
- Nunca logue dados do cartão
- Nunca armazene dados do cartão no app
Stripe Quick Start¶
1. Adicionar Dependência (30 segundos)¶
2. Criar Serviço (2 minutos)¶
// lib/services/stripe_subscription_service.dart
import 'dart:convert';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:http/http.dart' as http;
class StripeSubscriptionService {
final String baseUrl = 'https://sua-api.com'; // ← ALTERE AQUI
final String Function() getToken;
StripeSubscriptionService({required this.getToken});
/// Inicializar Stripe SDK (chamar uma vez no app)
/// Busca a publishable key do backend — NUNCA hardcodar.
Future<void> init() async {
final resp = await http.get(Uri.parse('$baseUrl/api/v1/stripe/config'));
if (resp.statusCode != 200) throw Exception('Stripe config unavailable');
final data = json.decode(resp.body);
Stripe.publishableKey = data['publishable_key'];
await Stripe.instance.applySettings();
}
/// Criar assinatura Stripe
Future<Map<String, dynamic>> subscribe({
required String planId,
}) async {
// 1. Tokenizar cartão via Stripe SDK
final pm = await Stripe.instance.createPaymentMethod(
params: PaymentMethodParams.card(
paymentMethodData: PaymentMethodData(),
),
);
// 2. Enviar para backend
final resp = await http.post(
Uri.parse('$baseUrl/api/v1/stripe/subscribe'),
headers: {
'Authorization': 'Bearer ${getToken()}',
'Content-Type': 'application/json',
},
body: json.encode({
'plan_id': planId,
'payment_method_id': pm.id,
}),
);
if (resp.statusCode != 201) {
throw Exception(json.decode(resp.body)['detail']);
}
return json.decode(resp.body);
}
}
3. Usar no Checkout (1 minuto)¶
Future<void> _onSubmitStripe() async {
try {
final service = StripeSubscriptionService(
getToken: () => yourUserToken,
);
final result = await service.subscribe(planId: 'plan-basic');
// Se 3D Secure necessário
if (result['info']['client_secret'] != null) {
await Stripe.instance.confirmPayment(
paymentIntentClientSecret: result['info']['client_secret'],
);
}
Navigator.pushReplacementNamed(context, '/success');
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erro: $e')),
);
}
}
Cartões de Teste Stripe¶
| Número | Cenário |
|---|---|
4242 4242 4242 4242 |
Aprovado |
4000 0025 0000 3155 |
Requer 3D Secure |
4000 0000 0000 9995 |
Recusado |
Dados: Validade futura, CVC qualquer (ex: 123)
Documentação completa: Integração Stripe
Pronto!¶
Com esses passos, você já pode processar pagamentos recorrentes via Asaas ou Stripe.