savers_app_sdk 1.0.5 copy "savers_app_sdk: ^1.0.5" to clipboard
savers_app_sdk: ^1.0.5 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, OAuth session id, user 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.

On later visits, generateUrl also encrypts the persisted auth session as authSessionId next to oAuthSessionId, so the hosted app can restore login. authSessionId is omitted until a USER_SESSION_ID PostMessage (or setAuthSessionId) has stored a value in local storage.

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 setOAuthSessionId('123456789');
await setAuthSessionId('user-session-123');
await setLocationCoordinates(37.7749, -122.4194);

final id = await getDeviceId();
final apiKey = await getApiKey();
final authSessionId = await getAuthSessionId();
await clearAuthSessionId();

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 }
OAUTH_SESSION_ID { oAuthSessionId }
USER_SESSION_ID { authSessionId } or { sId } — stored under local-storage key authSessionId
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, shared_preferences, url_launcher, uuid, webview_flutter

More

Packages that depend on savers_app_sdk