savers_app_sdk 1.0.4 copy "savers_app_sdk: ^1.0.4" to clipboard
savers_app_sdk: ^1.0.4 copied to clipboard

Flutter SDK for the Savers hosted app: WebView bridge, native maps/dialer/browser, device/session helpers, and encrypted URL generation.

Savers App SDK (Flutter) #

Flutter SDK that bridges host-app features (maps, dialer, browser, device/session, encrypted URL generation) and hosts the Savers merchant web app in a dual-WebView UI (HostedAppComponent).

Package name: savers_app_sdk

Features #

  • SaversAppSDK.initialized — store API key, AES-256 key, program ref code, auth mode, optional hosted environment
  • generateUrl — AES-GCM encrypted qP payload for testm.saversapp.com (sandbox) or m.saversapp.com (prod)
  • HostedAppComponent — header + two WebViews (hosted Hub, then Travel portal)
  • WebView HOST actions: maps, dial pad, external browser, session id, end session
  • Device ID, session, and location helpers
  • Navigation close via global navigationKey (END_SESSION)

Getting Started #

dependencies:
  savers_app_sdk:
    path: ../SaversFlutterLibrary   # or your published package name

The SDK depends on webview_flutter. After adding the package, run flutter pub get.

Platform Setup #

Android AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

iOS Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>Location access is used to show nearby offers.</string>
<key>LSApplicationQueriesSchemes</key>
<array>
  <string>tel</string>
  <string>telprompt</string>
</array>

Quick Start #

1. Initialize the SDK #

initialized is unchanged except for an optional environment. Existing callers keep working; omitted environment defaults to production (https://m.saversapp.com/).

import 'package:savers_app_sdk/savers_app_sdk.dart';

await SaversAppSDK.initialized(
  apiKey: 'YOUR_API_KEY',
  encryptionKey: 'BASE64_256_BIT_ENCRYPTION_KEY', // base64 of 32 bytes
  pRefCode: 'PROGRAM_REF_CODE',
  authMode: 'EMAIL', // 'EMAIL' | 'PHONE'
  environment: SaversSdkHostedEnvironment.sandbox, // optional: sandbox | prod
);
environment generateUrl host
SaversSdkHostedEnvironment.sandbox https://testm.saversapp.com/
SaversSdkHostedEnvironment.prod (or omitted) https://m.saversapp.com/

Wire navigationKey on your MaterialApp so END_SESSION can pop the current route:

MaterialApp(
  navigatorKey: navigationKey,
  home: const HomeScreen(),
);

2. Generate URL #

final url = await generateUrl(
  UrlInput(
    profile: Profile(
      userId: 'USER_ID',
      firstname: 'Jane',
      lastname: 'Doe',
      email: 'jane@example.com',
      phone: '+15555550100', // required when authType is phone
      pv: '1',
      ev: '1',
    ),
    authType: AuthType.email, // phone | email | username
    screen: Screen(name: 'Explore'),
  ),
);
// https://testm.saversapp.com/?pRefCode=...&qP=...  (sandbox)

Profile rules:

  • userId and email are mandatory
  • phone is mandatory when authType is AuthType.phone
  • username is mandatory when authType is AuthType.username
  • pv / ev are optional ('0' or '1')
  • When screen.name is OfrDetails, screen.attributes must be non-empty

Nonce is fetched automatically from profile.userId + pRefCode. Coordinates from setLocationCoordinates are included in deviceInfo.location when set.

qP encryption matches React Native AesGcmCrypto / hosted web decrypt:

  • AES-256-GCM
  • inner hex(iv):base64(content):hex(tag)
  • outer standard base64 (so the page can atob(qP))

3. HostedAppComponent (dual WebView) #

Do not change initialized / generateUrl for this UI. Pass the generated URL into HostedAppComponent.

HostedAppComponent(
  saversAppUrl: generatedUrl,
  travelHeaderTopInset: TravelPortalHeaderInsets.safeArea,
  onSaversSdkMessage: (raw, postBack) {
    try {
      final msg = jsonDecode(raw);
      if (msg is Map && msg['action'] == 'END_SESSION') {
        Navigator.of(context).maybePop();
        return;
      }
    } catch (_) {}
    handleWebMessage(raw, postBack: postBack);
  },
);

Flow:

  1. Load: WebView 1 opens saversAppUrl. Header and WebView 2 stay hidden.
  2. Hub → Travel tile: hosted app posts an envelope with the travel URL.
  3. SDK processes open_travel, hides WebView 1, shows header + WebView 2 with that URL.
  4. Header Back (native, no close Post Message required): hide WebView 2, show WebView 1 (still loaded), inject Hub navigation { type: 'NAVIGATE', route: 'Hub' }.

Travel Post Message (from Hub):

{
  "target": "SDK",
  "from": "savers",
  "action": "open_travel",
  "payload": { "url": "https://sandbox.travelercashback.com/sso" }
}

Optional: prepare_travel (show header early), close_travel (same as native Back), relay between surfaces. Payload may include branding (logo, backIconColor, loaderColor).

HostedAppController.closeSession() asks the Savers WebView to run Cognito closeSession.

Native helpers #

await openMap(37.7749, -122.4194, 'San Francisco');
await openDialPad('+1234567890');
await openBrowser('https://www.example.com');

await setSessionId('123456789');
await setLocationCoordinates(37.7749, -122.4194);

final id = await getDeviceId();
final apiKey = await getApiKey();

WebView HOST messages #

Use handleWebMessage for HOST actions that are not dual-WebView envelopes.

handleWebMessage(raw, postBack: (data) { /* optional reply to the page */ });

Supported action values:

Action Payload
OPEN_MAP { lat, lng, label? }
SHOW_DIAL_PAD { number? }
MERCHANT_PORTAL_REDIRECT { url }
SESSION_ID { sessionId }
END_SESSION none — pops via navigationKey

From a page inside HostedAppComponent, post JSON the same way as React Native (window.ReactNativeWebView.postMessage). The Flutter WebView injects a shim onto the SaversPostMessage channel.

Example App #

cd example
flutter pub get
flutter run

Demo notes:

  • Init uses sandbox credentials and SaversSdkHostedEnvironment.sandbox.
  • Open in Browser / Open in WebView sit on the Generated URL card. WebView opens HostedAppComponent.
  • After encryption or init changes, use hot restart (not only hot reload).

Notes #

  • Location permissions are required on Android and iOS for coordinate enrichment.
  • encryptionKey must be base64 of exactly 32 bytes (AES-256).
  • pRefCode is required before generateUrl.
0
likes
0
points
454
downloads

Publisher

verified publishersaversapp.com

Weekly Downloads

Flutter SDK for the Savers hosted app: WebView bridge, native maps/dialer/browser, device/session helpers, and encrypted URL generation.

Homepage

License

unknown (license)

Dependencies

connectivity_plus, cryptography, device_info_plus, dio, flutter, flutter_secure_storage, geolocator, hive_ce, shared_preferences, url_launcher, uuid, webview_flutter

More

Packages that depend on savers_app_sdk