Like other platforms, React requires the same two or three simple steps: Add a web view, handle events/messages, and, in some cases, authenticate the user.
Component used: WebView
Required Web View Configuration
- Enable JavaScript
- Enable Web Storage (DOM Storage)
elements-in-react.tsx
// Step 1: Setup a "web view"
<View style={styles.container}>
<Text style={styles.text}>DailyPay Elements</Text>
<View style={styles.webview}>
<WebView
cacheEnabled={false} // Disable caching for development
ref={dpElementWebView}
source={{ uri: dpElementUrl.toString() }}
style={{ flex: 1 }}
onMessage={handleMessageFromElement}
domStorageEnabled={true}
javaScriptEnabled={true}
/>
</View>
<View style={{ height: 100, margin: 10, flex: 1 }}>
<Text style={styles.text}>Log:</Text>
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
<Text style={styles.text}>{logMessages}</Text>
</ScrollView>
</View>
<View>
<Text style={styles.text}>Type:</Text>
<TextInput
style={styles.input}
value={inputMessageType}
onChangeText={setInputMessageType}
/>
<Text style={styles.text}>Payload (JSON):</Text>
<TextInput
style={styles.input}
value={inputMessagePayload}
onChangeText={setInputMessagePayload}
/>
<Button
title="Send Message"
onPress={() =>
sendMessageToElement(inputMessageType, JSON.parse(inputMessagePayload))
}
/>
</View>
</View>
Method used: postMessage
Please Log In to view this content.
elements-in-react.tsx
// Step 2: Setup Communications: Event/Message Handling
// Step 2a: Send Event to Elements
const sendMessageToElement = (type: string, payload: object) => {
const message = { type: type, payload: payload };
if (dpElementWebView.current) {
dpElementWebView.current.postMessage(JSON.stringify(message), dpElementOrigin);
setLogMessages(
(prevMessages) =>
"Sent: " + JSON.stringify(message) + "\n" + prevMessages,
);
}
};
Property used: onMessage
Please Log In to view this content.
elements-in-react.tsx
// Step 2b: Listen for events from Elements
const handleMessageFromElement = (event: WebViewMessageEvent) => {
const receivedEvent = event.nativeEvent;
// Log the raw event for debugging and illustration
console.log("Raw event received:", receivedEvent);
// It is crucial to verify the origin of the message for security reasons.
if (receivedEvent.url.startsWith(dpElementOrigin) === false) {
console.warn("Received message from unknown origin:", receivedEvent.url);
return;
}
// Parse the received data - it should be a JSON formatted string
let receivedMessage;
try {
receivedMessage = JSON.parse(receivedEvent.data);
} catch (e) {
console.error("Invalid JSON in payload input:", e);
return;
}
console.log("Message from WebView:", receivedMessage);
if (receivedMessage && receivedMessage.type) {
setLogMessages(
(prevMessages) =>
"Received: " + JSON.stringify(receivedMessage) + "\n" + prevMessages,
);
switch (receivedMessage.type) {
case "DAILYPAY_CARD_TOKENIZE_RESPONSE":
console.log("DAILYPAY_CARD_TOKENIZE_RESPONSE received: ", receivedMessage.success);
console.log("DAILYPAY_CARD_TOKENIZE_RESPONSE received: ", receivedMessage.response);
// Call API create account function
break;
case "EXT_LOG":
// Log if needed
console.log("EXT_LOG received: ", receivedMessage.payload);
break;
case "EXT_NEED_AUTH":
console.log("EXT_NEED_AUTH received: ", receivedMessage.payload);
handleExtNeedAuth();
break;
default:
console.log("Unhandled event type: ", receivedMessage.type);
}
}
};
Authentication Message Sample Code:
elements-in-react.tsx
// Step 3: Setup Authentication
var dpAuthTokens: AuthorizeResult | RefreshResult | null = null;
const dpAuthConfig: AuthConfiguration = {
issuer: 'https://auth.uat.dailypay.com',
clientId: 'REPLACE_WITH_YOUR_OAUTH_CLIENT_ID',
redirectUrl: 'REPLACE_WITH_YOUR_APP_REDIRECT_URI',
scopes: ['user:read'],
serviceConfiguration: {
authorizationEndpoint: 'https://auth.uat.dailypay.com/oauth2/auth',
tokenEndpoint: 'https://auth.uat.dailypay.com/oauth2/token',
},
};
const dpAuthenticateUser = async () => {
try {
const dpAuthTokens: AuthorizeResult = await authorize(dpAuthConfig);
console.log('Authorization successful:', dpAuthTokens);
// Save token in secure storage
await SecureStore.setItemAsync('dpAuthTokens', JSON.stringify(dpAuthTokens));
// Send token to WebView
sendMessageToElement('GIVE_TOKEN', { token: dpAuthTokens.accessToken });
} catch (error) {
console.error('Authorization failed:', error);
dpAuthTokens = null;
// Handle authentication errors
}
};
const handleExtNeedAuth = async () => {
// Check secure storage for existing tokens
let dpAuthTokensString = await SecureStore.getItemAsync('dpAuthTokens');
if (dpAuthTokensString) {
dpAuthTokens = JSON.parse(dpAuthTokensString);
console.log('Found existing tokens:', dpAuthTokens);
// Check if token is expired
if (dpAuthTokens && dpAuthTokens.refreshToken && Date.parse(dpAuthTokens.accessTokenExpirationDate) < Date.now()) {
try{
dpAuthTokens = await refresh(dpAuthConfig, {
refreshToken: dpAuthTokens.refreshToken,
});
console.log('Token refresh successful:', dpAuthTokens);
await SecureStore.setItemAsync('dpAuthTokens', JSON.stringify(dpAuthTokens));
} catch (error) {
console.warn('Token refresh failed:', error);
dpAuthTokens = null;
dpAuthenticateUser();
};
};
} else {
console.log('No tokens found, initiating login.');
dpAuthenticateUser();
};
if (dpAuthTokens) {
console.log(dpAuthTokens)
sendMessageToElement('GIVE_TOKEN', { token: dpAuthTokens.accessToken });
} else {
console.warn('No valid tokens available, user needs to log in.');
};
};
elements-in-react.tsx
import * as SecureStore from 'expo-secure-store';
import React, { useRef, useState } from "react";
import {
Button,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from "react-native";
import { AuthConfiguration, authorize, AuthorizeResult, refresh, RefreshResult } from 'react-native-app-auth';
import { WebView, WebViewMessageEvent } from "react-native-webview";
export default function Index() {
const dpElementOrigin = 'https://elements/uat.dailypay.com';
const dpElementUrl = new URL(dpElementOrigin + '/v1/available-earnings');
dpElementUrl.searchParams.append('client_id', 'REPLACE_WITH_YOUR_CLIENT_ID');
dpElementUrl.searchParams.append('implementation_id', 'REPLACE_WITH_YOUR_IMPLEMENTATION_ID');
// Not required but useful during development to avoid caching issues
dpElementUrl.searchParams.append('cache_bust', Date.now().toString());
const dpElementWebView = useRef<Window | null>(null);
const [inputMessageType, setInputMessageType] = useState("GIVE_TOKEN");
const [inputMessagePayload, setInputMessagePayload] =
useState('{ "token": "" }');
const [logMessages, setLogMessages] = useState("");
// Step 3: Setup Authentication
var dpAuthTokens: AuthorizeResult | RefreshResult | null = null;
const dpAuthConfig: AuthConfiguration = {
issuer: 'https://auth.uat.dailypay.com',
clientId: 'REPLACE_WITH_YOUR_OAUTH_CLIENT_ID',
redirectUrl: 'REPLACE_WITH_YOUR_APP_REDIRECT_URI',
scopes: ['user:read'],
serviceConfiguration: {
authorizationEndpoint: 'https://auth.uat.dailypay.com/oauth2/auth',
tokenEndpoint: 'https://auth.uat.dailypay.com/oauth2/token',
},
};
const dpAuthenticateUser = async () => {
try {
const dpAuthTokens: AuthorizeResult = await authorize(dpAuthConfig);
console.log('Authorization successful:', dpAuthTokens);
// Save token in secure storage
await SecureStore.setItemAsync('dpAuthTokens', JSON.stringify(dpAuthTokens));
// Send token to WebView
sendMessageToElement('GIVE_TOKEN', { token: dpAuthTokens.accessToken });
} catch (error) {
console.error('Authorization failed:', error);
dpAuthTokens = null;
// Handle authentication errors
}
};
const handleExtNeedAuth = async () => {
// Check secure storage for existing tokens
let dpAuthTokensString = await SecureStore.getItemAsync('dpAuthTokens');
if (dpAuthTokensString) {
dpAuthTokens = JSON.parse(dpAuthTokensString);
console.log('Found existing tokens:', dpAuthTokens);
// Check if token is expired
if (dpAuthTokens && dpAuthTokens.refreshToken && Date.parse(dpAuthTokens.accessTokenExpirationDate) < Date.now()) {
try{
dpAuthTokens = await refresh(dpAuthConfig, {
refreshToken: dpAuthTokens.refreshToken,
});
console.log('Token refresh successful:', dpAuthTokens);
await SecureStore.setItemAsync('dpAuthTokens', JSON.stringify(dpAuthTokens));
} catch (error) {
console.warn('Token refresh failed:', error);
dpAuthTokens = null;
dpAuthenticateUser();
};
};
} else {
console.log('No tokens found, initiating login.');
dpAuthenticateUser();
};
if (dpAuthTokens) {
console.log(dpAuthTokens)
sendMessageToElement('GIVE_TOKEN', { token: dpAuthTokens.accessToken });
} else {
console.warn('No valid tokens available, user needs to log in.');
};
};
// Step 3 End
// Step 2: Setup Communications: Event/Message Handling
// Step 2a: Send Event to Elements
const sendMessageToElement = (type: string, payload: object) => {
const message = { type: type, payload: payload };
if (dpElementWebView.current) {
dpElementWebView.current.postMessage(JSON.stringify(message), dpElementOrigin);
setLogMessages(
(prevMessages) =>
"Sent: " + JSON.stringify(message) + "\n" + prevMessages,
);
}
};
// Step 2b: Listen for events from Elements
const handleMessageFromElement = (event: WebViewMessageEvent) => {
const receivedEvent = event.nativeEvent;
// Log the raw event for debugging and illustration
console.log("Raw event received:", receivedEvent);
// It is crucial to verify the origin of the message for security reasons.
if (receivedEvent.url.startsWith(dpElementOrigin) === false) {
console.warn("Received message from unknown origin:", receivedEvent.url);
return;
}
// Parse the received data - it should be a JSON formatted string
let receivedMessage;
try {
receivedMessage = JSON.parse(receivedEvent.data);
} catch (e) {
console.error("Invalid JSON in payload input:", e);
return;
}
console.log("Message from WebView:", receivedMessage);
if (receivedMessage && receivedMessage.type) {
setLogMessages(
(prevMessages) =>
"Received: " + JSON.stringify(receivedMessage) + "\n" + prevMessages,
);
switch (receivedMessage.type) {
case "DAILYPAY_CARD_TOKENIZE_RESPONSE":
console.log("DAILYPAY_CARD_TOKENIZE_RESPONSE received: ", receivedMessage.success);
console.log("DAILYPAY_CARD_TOKENIZE_RESPONSE received: ", receivedMessage.response);
// Call API create account function
break;
case "EXT_LOG":
// Log if needed
console.log("EXT_LOG received: ", receivedMessage.payload);
break;
case "EXT_NEED_AUTH":
console.log("EXT_NEED_AUTH received: ", receivedMessage.payload);
handleExtNeedAuth();
break;
default:
console.log("Unhandled event type: ", receivedMessage.type);
}
}
};
// Step 2 End
return (
// Step 1: Setup a "web view"
<View style={styles.container}>
<Text style={styles.text}>DailyPay Elements</Text>
<View style={styles.webview}>
<WebView
cacheEnabled={false} // Disable caching for development
ref={dpElementWebView}
source={{ uri: dpElementUrl.toString() }}
style={{ flex: 1 }}
onMessage={handleMessageFromElement}
domStorageEnabled={true}
javaScriptEnabled={true}
/>
</View>
<View style={{ height: 100, margin: 10, flex: 1 }}>
<Text style={styles.text}>Log:</Text>
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
<Text style={styles.text}>{logMessages}</Text>
</ScrollView>
</View>
<View>
<Text style={styles.text}>Type:</Text>
<TextInput
style={styles.input}
value={inputMessageType}
onChangeText={setInputMessageType}
/>
<Text style={styles.text}>Payload (JSON):</Text>
<TextInput
style={styles.input}
value={inputMessagePayload}
onChangeText={setInputMessagePayload}
/>
<Button
title="Send Message"
onPress={() =>
sendMessageToElement(inputMessageType, JSON.parse(inputMessagePayload))
}
/>
</View>
</View>
// Step 1 End
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#25292e",
justifyContent: "center",
alignItems: "center",
},
input: {
height: 40,
borderWidth: 1,
padding: 10,
width: 200,
backgroundColor: "#fff",
},
text: {
color: "#fff",
},
webview: {
width: 380,
height: 180,
borderRadius: 10,
overflow: 'hidden',
},
});