expo-pngme-sdk
Version:
Expo SDK for Pngme financial insights - Android only, optimized for managed workflow
481 lines (391 loc) • 11.2 kB
Markdown
# Expo Pngme SDK
A native Expo module for integrating Pngme's financial insights SDK into your Expo managed workflow apps. This SDK enables credit scoring and financial analysis through SMS data collection on Android devices.
## 🎯 Designed for Expo Managed Workflow
This SDK is specifically optimized for **Expo Managed Workflow** projects. No ejecting required!
## 📱 Platform Support
| Platform | Support |
|----------|---------|
| Android | ✅ Full Support |
| iOS | ❌ Not Supported |
| Web | ⚠️ Development Stub |
## 🚀 Installation
### Step 1: Install the Package
```bash
npm install expo-pngme-sdk
# or
yarn add expo-pngme-sdk
# or
expo install expo-pngme-sdk
```
### Step 2: Add to App Config
In your `app.json` or `app.config.js`, add the plugin:
```json
{
"expo": {
"plugins": [
"expo-pngme-sdk"
]
}
}
```
Or with custom configuration:
```json
{
"expo": {
"plugins": [
[
"expo-pngme-sdk",
{
"pngmeSdkVersion": "7.0.6",
"pngmeMavenUrl": "https://jitpack.io"
}
]
]
}
}
```
### Step 3: Create a Development Build
**⚠️ Important**: This SDK does NOT work with Expo Go due to native module requirements.
```bash
# Using EAS Build (recommended)
eas build --profile development --platform android
# Or using local build
expo run:android
## 💻 Usage
### Basic Implementation
```typescript
import PngmeSDK from 'expo-pngme-sdk';
const initializePngme = async () => {
try {
await PngmeSDK.go({
clientKey: 'YOUR_PNGME_CLIENT_KEY', // Required
externalId: 'user_123', // Required: Your user ID
companyName: 'Your Company', // Required
firstName: 'John', // Optional
lastName: 'Doe', // Optional
email: 'john@example.com', // Optional
phoneNumber: '254712345678', // Optional
});
console.log('Pngme SDK initialized successfully');
} catch (error) {
console.error('Failed to initialize:', error);
}
};
```
### Custom Dialog Styling
```typescript
await PngmeSDK.go({
clientKey: 'YOUR_CLIENT_KEY',
externalId: 'user_123',
companyName: 'Your Company',
dialogStyle: {
// Colors
primaryColor: '#007AFF',
backgroundColor: '#FFFFFF',
buttonBackgroundColor: '#007AFF',
buttonTextColor: '#FFFFFF',
// Text
customTitle: 'Enable Financial Insights',
customButtonText: 'Get Started',
// Sizing
titleTextSize: 24,
bodyTextSize: 16,
buttonCornerRadius: 8,
// URLs
privacyPolicyUrl: 'https://yourcompany.com/privacy',
eulaUrl: 'https://yourcompany.com/terms',
}
});
```
### Event Handling
```typescript
import { useEffect } from 'react';
import PngmeSDK from 'expo-pngme-sdk';
export default function App() {
useEffect(() => {
const subscriptions = [
PngmeSDK.addListener('onPermissionGranted', (event) => {
console.log('Permission granted:', event.status);
}),
PngmeSDK.addListener('onSignInComplete', (event) => {
console.log('User ID:', event.userId);
}),
PngmeSDK.addListener('onError', (event) => {
console.error('Error:', event.error);
}),
];
return () => {
subscriptions.forEach(sub => sub.remove());
};
}, []);
// ... rest of your app
}
```
### Check Permission Status
```typescript
const checkStatus = async () => {
const isGranted = await PngmeSDK.isPermissionGranted();
const status = await PngmeSDK.getPermissionStatus();
console.log('SMS Permission:', status.smsPermission);
console.log('Terms Accepted:', status.termsAccepted);
};
```
## 📖 API Reference
### Methods
| Method | Description | Returns |
|--------|-------------|---------|
| `go(config)` | Initialize SDK and show dialog | `Promise<void>` |
| `isPermissionGranted()` | Check if permissions granted | `Promise<boolean>` |
| `getUserUuid()` | Get Pngme user ID | `Promise<string \| null>` |
| `setDefaultStyle(style)` | Set default dialog style | `Promise<void>` |
| `clearDefaultStyle()` | Clear default style | `Promise<void>` |
| `getPermissionStatus()` | Get detailed permission status | `Promise<PngmePermissionStatus>` |
### Events
| Event | Payload | Description |
|-------|---------|-------------|
| `onPermissionGranted` | `{ status }` | Permissions granted |
| `onPermissionDenied` | `{ status }` | Permissions denied |
| `onDialogDismissed` | `{ completed }` | Dialog closed |
| `onSignInComplete` | `{ userId }` | User authenticated |
| `onSmsUploadStarted` | `{ timestamp }` | SMS sync started |
| `onSmsUploadComplete` | `{ success, count? }` | SMS sync finished |
| `onError` | `{ error, code }` | Error occurred |
### Types
```typescript
interface PngmeConfig {
clientKey: string; // Your Pngme API key
externalId: string; // Your internal user ID
companyName: string; // Your company name
firstName?: string;
lastName?: string;
email?: string;
phoneNumber?: string;
dialogStyle?: PngmeDialogStyle;
}
interface PngmeDialogStyle {
// Colors (hex strings)
primaryColor?: string;
backgroundColor?: string;
textColor?: string;
buttonBackgroundColor?: string;
buttonTextColor?: string;
linkTextColor?: string;
// Icon colors
closeIconColor?: string;
smsIconColor?: string;
privacyIconColor?: string;
// Text sizes (sp units)
titleTextSize?: number;
bodyTextSize?: number;
buttonTextSize?: number;
// Custom text
customTitle?: string;
customSmsDescription?: string;
customPrivacyDescription?: string;
customButtonText?: string;
// Button styling
buttonCornerRadius?: number;
buttonElevation?: number;
// URLs
privacyPolicyUrl?: string;
eulaUrl?: string;
// Layout
contentPadding?: number;
}
interface PngmePermissionStatus {
smsPermission: 'granted' | 'denied' | 'never_asked';
termsAccepted: boolean;
}
```
## 🎨 Complete Example App
```typescript
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
Text,
View,
Button,
TextInput,
ScrollView,
Alert,
} from 'react-native';
import PngmeSDK from 'expo-pngme-sdk';
export default function App() {
const [clientKey, setClientKey] = useState('');
const [userId, setUserId] = useState('');
const [status, setStatus] = useState<any>(null);
useEffect(() => {
checkStatus();
// Set up event listeners
const listeners = [
PngmeSDK.addListener('onPermissionGranted', (event) => {
Alert.alert('Success', 'Permissions granted!');
setStatus(event.status);
}),
PngmeSDK.addListener('onSignInComplete', (event) => {
setUserId(event.userId);
}),
PngmeSDK.addListener('onError', (event) => {
Alert.alert('Error', event.error);
}),
];
return () => listeners.forEach(l => l.remove());
}, []);
const checkStatus = async () => {
const status = await PngmeSDK.getPermissionStatus();
setStatus(status);
};
const initialize = async () => {
if (!clientKey) {
Alert.alert('Error', 'Please enter your client key');
return;
}
try {
await PngmeSDK.go({
clientKey,
externalId: `user_${Date.now()}`,
companyName: 'Test Company',
firstName: 'Test',
lastName: 'User',
dialogStyle: {
primaryColor: '#007AFF',
buttonBackgroundColor: '#007AFF',
}
});
} catch (error: any) {
Alert.alert('Error', error.message);
}
};
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>Pngme SDK Demo</Text>
<View style={styles.card}>
<Text style={styles.label}>Status</Text>
<Text>SMS: {status?.smsPermission || 'Unknown'}</Text>
<Text>Terms: {status?.termsAccepted ? 'Yes' : 'No'}</Text>
{userId && <Text>User ID: {userId}</Text>}
</View>
<View style={styles.card}>
<Text style={styles.label}>Client Key</Text>
<TextInput
style={styles.input}
value={clientKey}
onChangeText={setClientKey}
placeholder="Enter your Pngme client key"
/>
<Button title="Initialize SDK" onPress={initialize} />
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginVertical: 20,
},
card: {
backgroundColor: 'white',
padding: 15,
borderRadius: 10,
marginBottom: 15,
},
label: {
fontSize: 16,
fontWeight: '600',
marginBottom: 10,
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 5,
padding: 10,
marginBottom: 10,
},
});
```
## 🔧 Troubleshooting
### Common Issues and Solutions
#### "Module not found" Error
```bash
# Clear cache and rebuild
expo start -c
eas build --clear-cache --platform android
```
#### Permission Dialog Not Showing
- Ensure you're testing on a real Android device or emulator
- Check that `clientKey` and `externalId` are provided
- Verify the app is in the foreground
#### SDK Not Initializing
- Confirm you've created a development build (not using Expo Go)
- Check that the Pngme SDK dependency is correctly configured
- Verify your client key is valid
#### iOS Platform Error
This SDK is **Android-only**. It will not work on iOS devices and will throw appropriate errors.
## 🏗️ Build Configuration
### EAS Build
In your `eas.json`:
```json
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"preview": {
"distribution": "internal"
},
"production": {}
}
}
```
### Local Build
```bash
# Install dependencies
npm install
# Run on Android
expo run:android
# Build APK locally
expo build:android -t apk
```
## 📊 Data Collection
The SDK collects and processes:
- SMS messages (filtered for financial transactions)
- Basic device information
- User-provided information
All data is:
- Encrypted in transit
- Processed according to privacy regulations
- Used solely for credit scoring purposes
## 🔒 Security Best Practices
1. **Store Client Key Securely**
```typescript
// Use environment variables
import Constants from 'expo-constants';
const CLIENT_KEY = Constants.expoConfig?.extra?.pngmeClientKey;
```
2. **Validate User Data**
```typescript
// Validate before passing to SDK
const isValidEmail = (email: string) => /\S+@\S+\.\S+/.test(email);
const isValidPhone = (phone: string) => /^\+\d{10,15}$/.test(phone);
```
3. **Handle Errors Gracefully**
```typescript
try {
await PngmeSDK.go(config);
} catch (error) {
// Log to analytics, not console
analytics.track('pngme_init_failed', { error });
}
```
**Note:** This SDK requires a valid Pngme client key. Contact [Pngme](https://pngme.com) to get started.