@proxze/kyc-react-native
v0.1.0
Published
Proxze Enterprise Address KYC SDK for React Native — passive geofence night verification, consent, evidence upload.
Downloads
95
Maintainers
Readme
@proxze/kyc-react-native
Proxze Enterprise Address KYC SDK. Embeds passive geofence-based address
verification into a bank's React Native app: NDPR consent, night-presence
tracking (12AM–5AM, 3 consecutive nights), utility-bill upload and optional
daily video KYC — against proxze-kyc-service's /v2/sdk/* surface.
Install
yarn add @proxze/kyc-react-native
# recommended peers
yarn add react-native-background-geolocation @react-native-async-storage/async-storagereact-native-background-geolocation (licensed, Transistorsoft) powers the
default location adapter — Google Play Services fused location + Geofencing API
on Android, CoreLocation region monitoring on iOS. It is an optional peer:
a bank with its own location stack can implement the LocationAdapter
interface instead and skip the dependency entirely.
@react-native-async-storage/async-storage persists the offline event queue.
Also optional — without it the queue lives in memory only (events survive
connectivity loss but not a process kill).
Security model
The app only ever holds a session token (pst_…) scoped to one
verification. The bank's pxz_live_… API key stays on the bank's backend:
- Bank backend calls
POST /v2/kyc/initiatewith its API key. - It hands the returned
session_tokento the app (its own auth channel). - The app drives this SDK with that token; the token can only report evidence for its own session and dies when the verification completes.
Quickstart
import { useProxzeKyc } from '@proxze/kyc-react-native';
function AddressVerification({ sessionToken }: { sessionToken: string }) {
const { session, state, loading, kyc } = useProxzeKyc({
baseUrl: 'https://api.proxze.com',
sessionToken,
});
if (loading || !session) return <Spinner />;
switch (state) {
case 'awaiting_consent':
// NDPR: show your consent copy, then:
return <ConsentScreen onAccept={() => kyc.grantConsent('1.0.0')} />;
case 'awaiting_address_pin':
// The declared address geocoded too coarsely — ask the customer to
// drop a pin at home (map screen), then:
return <PinScreen onConfirm={(p) => kyc.confirmAddress(p)} />;
case 'awaiting_permissions':
return (
<ExplainerScreen
onContinue={async () => {
const permission = await kyc.startTracking();
if (permission.grade === 'denied') {
// Explain why tracking is needed and re-prompt / open settings.
} else if (!permission.precise) {
// Approximate location can't confirm a 100m fence —
// walk the user to settings to enable Precise Location.
} else if (permission.grade === 'granted_when_in_use') {
// Warn: overnight confirmation unlikely without "Always allow".
}
}}
/>
);
case 'tracking':
return (
<ProgressScreen
nights={session.requirements.nights}
onUploadBill={(file) => kyc.uploadUtilityBill(file, 'electricity_bill')}
/>
);
case 'completed':
return <DoneScreen status={session.status} />;
}
}Imperative use without React is the same machine: new ProxzeKycSession({...}).
How night tracking works
The server evaluates nights in hybrid mode and this SDK feeds both models:
- Geofence transitions (
enter/exit/dwell) come from the OS and survive app termination and reboots (stopOnTerminate: false,startOnBoot: true). - Pings are sampled on the 30-minute marks inside the 12AM–5AM window (Africa/Lagos), where the OS allows. iOS will not run a 30-minute background timer — that's expected; the transitions plus the morning check (one in-fence sample in the 3 hours after 05:00) satisfy the server's arrived-and-stayed strategy.
- Everything is queued offline-first and flushed in batches; the server
dedupes on
clientEventId, so replays are harmless. - Android's mock-location flag rides along on every event; spoofed nights are flagged server-side.
Evidence uploads
// Compulsory utility bill (photo or PDF, ≤10MB, ≤3 months old):
await kyc.uploadUtilityBill(
{ uri: photo.uri, name: 'bill.jpg', type: 'image/jpeg' },
'electricity_bill',
);
// Daily geo-tagged video, only when the integration requires it:
await kyc.uploadVideo(
{ uri: rec.uri, name: 'day1.mp4', type: 'video/mp4' },
{
dayNumber: 1,
durationSec: 22,
lat, lng, accuracyM,
recordedAt: new Date().toISOString(),
},
);The bill response carries the OCR/match outcome (status, matchStatus,
rejectionReason, canRetry) — one re-upload is allowed after a rejection.
Permissions — the full matrix
startTracking() requests permissions and returns what was actually granted
({ grade, precise }); kyc.diagnostics() re-checks at any time. The SDK
starts tracking on anything better than an outright denial — degraded signals
still feed the server's hybrid evaluator, which routes unclear nights to
review instead of failing the customer — but the app should tell the customer
what each state means for their verification:
| State | Effect | App should |
| --- | --- | --- |
| granted_always + precise | Full night tracking | Nothing — happy path |
| granted_when_in_use | Fence events only while app foregrounded; nights mostly inconclusive | Prompt to upgrade to "Allow all the time" (Android takes the user to settings for this on 11+) |
| precise: false | Approximate fixes are km-wide; a 100m fence can never confirm | Walk the user to settings → enable Precise Location |
| denied | No tracking at all | Explain, re-prompt, or offer to cancel the verification |
Platform notes the host app owns:
- Android 10+:
ACCESS_BACKGROUND_LOCATIONis a separate grant; on 11+ the system never shows an "always" button in the dialog — the user must pick it in settings. Google Play also requires an in-app prominent disclosure before the permission prompt; the consent screen you show atawaiting_consentis the natural place. - Android 12+: users can grant approximate-only — that's the
precise: falsestate above. - Android 13+: add
POST_NOTIFICATIONS; the background-geolocation foreground-service notification needs it. - Battery optimization: aggressive OEMs (Tecno/Infinix/Xiaomi — a large
share of Nigerian handsets) kill background services. The Transistorsoft lib
ships
DeviceSettingshelpers to request an exemption; wire them into your "tracking health" screen for best confirmation rates. - iOS: "Always" authorization is granted provisionally and can be
downgraded silently by the user; re-check with
diagnostics()when the app foregrounds. iOS 14+'s Precise Location toggle maps toprecise: false.
Android (AndroidManifest.xml):
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />iOS (Info.plist):
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Your bank uses your location at night to confirm you live at your registered address.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your bank uses your location to confirm your address.</string>
<key>UIBackgroundModes</key>
<array><string>location</string></array>Follow react-native-background-geolocation's own setup guide for its native
configuration and license key.
Custom location stack
import { LocationAdapter, ProxzeKycSession } from '@proxze/kyc-react-native';
class MyAdapter implements LocationAdapter { /* ... */ }
const kyc = new ProxzeKycSession({
baseUrl,
sessionToken,
adapter: new MyAdapter(),
});Build
yarn build # tsc → dist/
yarn test # queue + night-window unit testsLicense
Proprietary — © Sage Grey Technologies Limited. Use is permitted only for integrating with Proxze services; modification and redistribution are not. See LICENSE.
