Paywallo Flutter SDK
Plugin Flutter do Paywallo — paywalls, campaigns, subscriptions, identity, events, flags. Mesma linha de contrato do SDK React Native 2.9.0.
Instalação
dependencies:
paywallo_flutter: ^2.9.0
Uso rápido
import 'package:flutter/material.dart';
import 'package:paywallo_flutter/paywallo.dart';
final navigatorKey = GlobalKey<NavigatorState>();
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final paywallo = await PaywalloPlatform.create();
// Necessário só para o presenter default do paywall (WebView).
paywallo.setNavigatorKey(navigatorKey);
await paywallo.initialize(
const PaywalloInitConfig(appKey: 'pk_live_…'),
);
await paywallo.identify(
const IdentifyOptions(email: 'ada@example.com'),
);
await paywallo.track('home_opened');
if (await paywallo.requireSubscription(paywallPlacement: 'home')) {
// usuário assinante — libera o conteúdo
}
}
apiUrl é opcional e existe só para apontar o SDK a um servidor local. Ele
aceita https://, ou http:// em localhost/127.0.0.1/192.168.x.x;
qualquer outra coisa lança ClientError(CLIENT_INVALID_API_URL) — o SDK
nunca cai em produção em silêncio.
Contrato de rede
Base: https://panel.lucasqueiroga.shop. Todos os endpoints ficam sob o prefixo
/sdk, com uma exceção confirmada (/campaigns/public/primary). São 21
chamadas sobre 20 rotas (/sdk/push-tokens responde a POST e DELETE):
| Método | Rota |
|---|---|
| POST | /sdk/ingest/batch |
| POST | /sdk/identity/identify |
| GET | /sdk/flags/{key} · /sdk/flags/evaluate · /sdk/flags |
| GET | /sdk/conditional-flags/{key} |
| GET | /sdk/paywalls/{placement} |
| GET | /sdk/campaigns/{placement} · /sdk/campaigns/placements |
| GET | /campaigns/public/primary (fora do prefixo /sdk) |
| GET | /sdk/emergency-paywall |
| POST | /sdk/purchases/validate |
| GET | /sdk/purchases/status |
| GET | /sdk/plans · /sdk/plans/all · /sdk/offerings |
| POST / DELETE | /sdk/push-tokens |
| POST | /sdk/attribution/deferred-match/{appKey} |
| POST | /sdk/attribution/install-enrich/{appKey} |
| POST | /sdk/errors |
Headers em toda request:
Content-Type: application/json
X-App-Key: <appKey>
x-sdk-version: 2.9.0
x-sdk-platform: ios | android ← apenas estes dois valores
x-sdk-environment: Production | Sandbox
User-Agent: PaywalloSDK/2.9.0 (<os> <versão>; <modelo>)
x-sdk-user-agent: <mesmo valor>
x-sdk-platform carrega só ios ou android — o servidor não reconhece
mais nada e descarta qualquer outro valor sem erro HTTP, o que derruba em
silêncio a entrega de paywall por plataforma, a resolução do SKU da loja e o
match de atribuição. O User-Agent descreve o dispositivo (SO, versão,
modelo), não o framework: como nos SDKs Swift e Kotlin, o Flutter não se
identifica separadamente na rede.
Estrutura
lib/
├── paywallo_flutter.dart ← barrel
├── paywallo.dart ← re-export de compatibilidade
└── src/
├── paywallo_client.dart ← orquestrador (Paywallo.instance)
├── core/
│ ├── api_client.dart ← as 21 chamadas (20 rotas)
│ ├── api_client_queue.dart ← retry + write-ahead do critical
│ ├── circuit_breaker.dart
│ ├── constants.dart ← base URL, versão, resolveApiUrl
│ ├── event_envelope_v2.dart ← envelope do /sdk/ingest/batch
│ ├── http_client.dart ← retry, Retry-After, guard de http://
│ ├── retry/pending_retry.dart ← backstop durável (só critical)
│ ├── sdk_platform.dart ← ios | android
│ ├── user_agent.dart · uuid.dart · storage.dart · device_info.dart
│ └── errors.dart
├── domains/
│ ├── campaign/ · events/ · flags/ · iap/
│ ├── identity/ ← identity, attribution, install,
│ │ deferred match, install referrer
│ ├── localization/ · notifications/ · offering/ · onboarding/
│ ├── paywall/ · plan/ · session/ · subscription/
├── platform/ ← shared_preferences, secure storage,
│ connectivity, in_app_purchase,
│ device_info, app_links, push nativo
├── widgets/paywall_webview.dart ← WebView + presenter default
└── types/types.dart
Resiliência
Não existe fila offline. Ela foi removida no RN 2.7.0 depois de um incidente em
que o processador em batch re-embrulhava o envelope e derrubou 100% dos
$app_installed; este SDK segue a mesma decisão. O backstop é o PendingRetry,
só para eventos critical: o request é gravado antes do POST, removido só
no 2xx, e reposto byte a byte — nunca mesclado, nunca re-embrulhado.
getOfflineQueueSize(), clearOfflineQueue() e processOfflineQueue() seguem
existindo como no-ops @Deprecated e saem na 3.0.0.
isOnline() é pessimista: estado unknown devolve false.
LGPD / GDPR
deleteUserData() limpa apenas o estado local (identidade, atribuição, fila
de retry, token de push). A rota POST /sdk/identity/delete não existe no
servidor — o apagamento server-side é feito pelo painel.
Push
O firebase_messaging não é dependência do plugin. No Android o token do
FCM é lido por reflexão quando o app já tem o Firebase linkado; no iOS o token
APNs vem da ponte nativa. Se o app quiser gerenciar o token por conta própria,
basta chamar registerPushToken(token) na instância que foi inicializada
(a devolvida por PaywalloPlatform.create()); Paywallo.instance só funciona
se foi ela que passou pelo initialize().
Construindo
flutter pub get
flutter analyze
flutter test
Roadmap
Ver PLAN.md.
Libraries
- paywallo
- paywallo_flutter
- Paywallo Flutter SDK — barrel export.