Paywallo Flutter SDK

Tests Dart 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 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.