apenia_verify 0.0.12
apenia_verify: ^0.0.12 copied to clipboard
Apenia Verify — KYC capture SDK. Native active-liveness selfie + document scan; matching runs server-side in Apenia Verify.
example/lib/main.dart
import 'package:apenia_verify/apenia_verify.dart';
import 'package:flutter/material.dart';
/// Minimal end-to-end integration of the Apenia Verify capture SDK.
///
/// Two things live in the app: the API base URL and your **publishable** key.
/// The secret key (`sk_…`) never touches the handset — your own backend holds
/// it and is the only party that may create an onboarding session.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await ApeniaVerify.initialize(
ApeniaConfig(
appKey: 'pk_YOUR_PUBLISHABLE_KEY',
// Documents capture automatically once detected in the frame; the
// shutter always remains available as a manual override.
documentCaptureMode: DocumentCaptureMode.auto,
// Your backend endpoint that creates the session. It holds the secret
// key and returns { session_id, upload_url, client_token }; the SDK
// calls it with the signed-in user's bearer token.
sessionEndpoint: 'https://your.app/api/verification/session',
authTokenProvider: () async => _currentUserToken(),
),
);
runApp(const ExampleApp());
}
/// The signed-in user's token, from your auth store.
///
/// Read at call time rather than captured once, so a refreshed token is picked
/// up without re-initialising the SDK.
///
/// Your endpoint, server-side, is:
/// POST https://verify.apenia.com/v1/onboarding/sessions
/// X-App-Key: pk_… X-App-Secret: sk_…
/// { "flow": "…", "metadata": { … } }
Future<String> _currentUserToken() async {
throw UnimplementedError('Return the signed-in user\'s bearer token.');
}
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Apenia Verify',
theme: ApeniaTheme.light(),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FilledButton(
onPressed: () => _startVerification(context),
child: const Text('Verify my identity'),
),
),
);
}
void _startVerification(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => ApeniaOnboarding(
// jobId must be stable across retries of the same attempt, so a
// resubmission can't create a duplicate KYC record.
params: PartnerParams(
userId: 'user-123',
jobId: 'job-${DateTime.now().millisecondsSinceEpoch}',
),
onComplete: (verdict) {
Navigator.of(context).pop();
// Branch on the decision, never on the individual scores — a
// strong face-match can still accompany a rejection.
final message = verdict.isApproved
? 'Verified'
: 'Not approved (${verdict.decision ?? verdict.status.name})';
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
},
onExit: () => Navigator.of(context).pop(),
),
),
);
}
}