Files
rnpay/App.tsx
2026-02-02 02:23:21 +08:00

779 lines
30 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { Component } from "react";
import { Alert, AppState, AppStateStatus, Modal, StyleSheet, Text, TouchableOpacity, View } from "react-native";
import DeviceInfo from 'react-native-device-info';
import {
PaytmBusinessBind,
PhonePeBusinessBind,
GooglePayBusinessBind,
BharatPeBusinessBind,
WalletType,
PaytmBusinessBindResult,
PhonePeBusinessBindResult,
PaytmPersonalBind,
PaytmPersonalBindResult,
MobikwikPersonalBind,
MobikwikPersonalBindResult,
FreechargePersonalBind,
FreechargePersonalBindResult,
GooglePayBusinessBindResult,
BharatPeBusinessBindResult,
paytmPay,
onSmsMessage,
onNotificationMessage,
getAllSms,
getAllNotifications,
startSmsListener,
startNotificationListener,
stopSmsListener,
stopNotificationListener,
checkSmsPermission,
checkNotificationPermission,
openNotificationSettings,
requestSmsPermission,
PhonePePersonalBindResult,
PhonePePersonalBind,
SmsMessage,
NotificationMessage,
proxyManager,
} from "rnwalletman";
import BarcodeScanning from '@react-native-ml-kit/barcode-scanning';
import RNFS from 'react-native-fs';
interface AppProps {
}
interface WalletmanAppState {
showPaytmBusinessBind: boolean;
showPaytmPersonalBind: boolean;
showPhonePePersonalBind: boolean;
showPhonePeBusinessBind: boolean;
showGooglePayBusinessBind: boolean;
showBharatPeBusinessBind: boolean;
showMobikwikPersonalBind: boolean;
showFreechargePersonalBind: boolean;
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
button: {
padding: 10,
backgroundColor: "lightblue",
borderRadius: 5,
width: 200,
height: 50,
},
text: {
fontSize: 20,
fontWeight: "bold",
},
modal: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
modalContent: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
bindButton: {
padding: 10,
borderRadius: 5,
width: 250,
height: 40,
justifyContent: "center",
alignItems: "center",
margin: 10,
},
bindButtonText: {
fontSize: 15,
fontWeight: "bold",
},
});
class Api {
public static readonly BASE_URL = 'http://192.168.1.117:16000';
private static _instance: Api | null = null;
private userId: number = 0;
private constructor() {
}
public setUserId(userId: number) {
this.userId = userId;
}
public getUserId(): number {
return this.userId;
}
public static get instance() {
if (Api._instance === null) {
Api._instance = new Api();
}
return Api._instance;
}
private headers(): Record<string, string> {
const h: Record<string, string> = { 'Content-Type': 'application/json' };
if (this.userId) h['X-User-ID'] = String(this.userId);
return h;
}
public async login(username: string, password: string): Promise<number> {
const res = await fetch(`${Api.BASE_URL}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
const data = await res.json();
if (!data.success) throw new Error(data.message);
this.userId = data.data.userId;
return this.userId;
}
public async register(walletType: WalletType, params: any) {
const res = await fetch(`${Api.BASE_URL}/register`, {
method: 'POST',
headers: this.headers(),
body: JSON.stringify({ walletType, params }),
});
const data = await res.json();
if (!data.success) throw new Error(data.message);
return data;
}
public async requestOTP(walletType: WalletType, mobile: string, params: any = {}) {
const res = await fetch(`${Api.BASE_URL}/request-otp`, {
method: 'POST',
headers: this.headers(),
body: JSON.stringify({ walletType, mobile, ...params }),
});
const data = await res.json();
if (!data.success) throw new Error(data.message);
return data;
}
public async verifyOTP(walletType: WalletType, mobile: string, otp: string, params: any = {}) {
const res = await fetch(`${Api.BASE_URL}/verify-otp`, {
method: 'POST',
headers: this.headers(),
body: JSON.stringify({ walletType, mobile, otp, ...params }),
});
const data = await res.json();
if (!data.success) throw new Error(data.message);
return data;
}
}
export default class App extends Component<AppProps, WalletmanAppState> {
private deviceId: string;
private tuneUserId: string;
private clientId: string = '';
private appStateSubscription?: any;
constructor(props: AppProps) {
super(props);
this.state = {
showPaytmBusinessBind: false,
showPaytmPersonalBind: false,
showPhonePeBusinessBind: false,
showPhonePePersonalBind: false,
showGooglePayBusinessBind: false,
showBharatPeBusinessBind: false,
showMobikwikPersonalBind: false,
showFreechargePersonalBind: false,
};
// 临时使用测试成功的固定 ID
this.deviceId = 'B6C1AB6DA4B659C287EA76AA96EC154B80E8D28D';
this.tuneUserId = 'b5bfa7df-e571-4ac8-bb51-90afc05d1d59';
}
async componentDidMount() {
/* 获取真实 Android ID */
try {
this.clientId = await DeviceInfo.getAndroidId();
console.log('[设备ID]', this.clientId);
} catch (error) {
this.clientId = `android_${Date.now()}`;
console.warn('[设备ID] 获取失败使用随机ID:', this.clientId);
}
/* 监听应用状态 */
this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);
/* 登录获取 userId */
try {
await Api.instance.login('test123', '123456');
console.log('[登录成功] userId:', Api.instance.getUserId());
} catch (error) {
console.error('[登录失败]', error);
Alert.alert('登录失败', String(error));
return;
}
/* 初始化代理客户端 */
await this.initProxyClient();
/* 权限申请 */
let smsPermission = await checkSmsPermission();
let notificationPermission = await checkNotificationPermission();
if (!smsPermission) {
const granted = await requestSmsPermission();
smsPermission = await checkSmsPermission();
console.log('smsPermission:', smsPermission);
if (!smsPermission) {
Alert.alert('需要短信权限', '请在系统设置中授予短信权限');
}
}
if (!notificationPermission) {
Alert.alert(
'需要通知监听权限',
'点击确定后将打开设置页面,请找到本应用并授予通知访问权限',
[
{
text: '确定',
onPress: () => openNotificationSettings()
},
{ text: '取消' }
]
);
return;
}
// 启动监听
startSmsListener();
startNotificationListener();
onSmsMessage((message: SmsMessage) => {
console.log('[SMS]', message);
});
onNotificationMessage((notification: NotificationMessage) => {
console.log('[Notification]', notification);
});
if (smsPermission) {
getAllSms().then((sms: SmsMessage[]) => {
console.log('[所有短信]', sms.length, '条');
}).catch(err => console.error('[获取短信失败]', err));
}
if (notificationPermission) {
getAllNotifications().then((notifications: NotificationMessage[]) => {
console.log('[所有通知]', notifications.length, '条');
}).catch(err => console.error('[获取通知失败]', err));
}
}
private wsHeartbeatTimer?: NodeJS.Timeout;
/**
* 初始化代理客户端
*/
async initProxyClient() {
const wsUrl = `ws://${Api.BASE_URL.replace('http://', '')}/ws`;
proxyManager.start({
wsUrl,
clientId: this.clientId,
userId: Api.instance.getUserId(),
debug: true,
onConnected: () => {
console.log('[代理] ✅ 已连接');
},
onDisconnected: () => {
console.log('[代理] ❌ 已断开');
},
onError: (error) => {
console.error('[代理] 错误:', error);
},
// 自定义注册逻辑(可选)
onRegister: (ws, clientId, userId) => {
// 可以在这里自定义发送的注册消息
ws.send(JSON.stringify({
type: 'register',
clientId,
messageId: 'register_' + Date.now(),
data: {
userId,
// 可以添加额外参数
deviceInfo: {
platform: 'android',
version: '1.0.0'
}
}
}));
}
}).catch(err => {
console.error('[代理] 启动失败:', err);
});
}
/**
* 停止代理客户端
*/
stopProxyClient() {
proxyManager.stop();
}
componentWillUnmount(): void {
// 清理应用状态监听
this.appStateSubscription?.remove();
// 停止代理
this.stopProxyClient();
stopSmsListener();
stopNotificationListener();
}
/**
* 处理应用状态变化
*/
handleAppStateChange = (nextAppState: AppStateStatus) => {
if (nextAppState === 'background' || nextAppState === 'inactive') {
console.log('[AppState] 应用进入后台,关闭代理');
this.stopProxyClient();
} else if (nextAppState === 'active') {
console.log('[AppState] 应用回到前台,重连代理');
this.initProxyClient();
}
}
decodeQRFromUrl = async (url: string) => {
const localPath = `${RNFS.CachesDirectoryPath}/temp_qr_${Date.now()}.jpg`;
try {
// 1. 下载图片
await RNFS.downloadFile({ fromUrl: url, toFile: localPath }).promise;
// 2. 识别二维码
const barcodes = await BarcodeScanning.scan(`file://${localPath}`);
if (barcodes.length > 0) {
return barcodes[0].value;
}
} catch (e) {
console.error(e);
return null;
}
};
/* 绑定 Paytm Personal */
handlePaytmPersonalBind = () => {
this.setState({ showPaytmPersonalBind: true });
}
/* 上传 Paytm Personal 到服务器 */
handleUploadPaytmPersonal = async (result: PaytmPersonalBindResult) => {
try {
console.log(result);
const response = await Api.instance.register(WalletType.PAYTM_PERSONAL, result);
console.log(response);
this.setState({ showPaytmPersonalBind: false });
} catch (error) {
Alert.alert('Bind Paytm Personal Error', (error as Error).message || 'Unknown error');
this.setState({ showPaytmPersonalBind: false });
}
}
/* 绑定 Paytm Business */
handlePaytmBusinessBind = () => {
this.setState({ showPaytmBusinessBind: true });
}
/* 上传 Paytm Business 到服务器 */
handleUploadPaytmBusiness = async (result: PaytmBusinessBindResult) => {
try {
console.log(result);
const response = await Api.instance.register(WalletType.PAYTM_BUSINESS, result);
console.log(response);
this.setState({ showPaytmBusinessBind: false });
} catch (error) {
Alert.alert('Bind Paytm Business Error', (error as Error).message || 'Unknown error');
this.setState({ showPaytmBusinessBind: false });
}
}
/* 绑定 Mobikwik Personal */
handleMobikwikPersonalBind = () => {
this.setState({ showMobikwikPersonalBind: true });
}
/* 上传 Mobikwik Personal 到服务器 */
handleUploadMobikwikPersonal = async (result: MobikwikPersonalBindResult) => {
try {
console.log(JSON.stringify(result));
// 已在 verifyOTP 中完成注册
this.setState({ showMobikwikPersonalBind: false });
Alert.alert('绑定成功', 'Mobikwik Personal 绑定成功');
} catch (error) {
Alert.alert('Bind Mobikwik Personal Error', (error as Error).message || 'Unknown error');
this.setState({ showMobikwikPersonalBind: false });
}
}
/* 绑定 Freecharge Personal */
handleFreechargePersonalBind = () => {
this.setState({ showFreechargePersonalBind: true });
}
/* 上传 Freecharge Personal 到服务器 */
handleUploadFreechargePersonal = async (result: FreechargePersonalBindResult) => {
try {
console.log(JSON.stringify(result));
// 已经在 FreechargePersonalBind 中完成注册
this.setState({ showFreechargePersonalBind: false });
Alert.alert('绑定成功', 'Freecharge Personal 绑定成功');
} catch (error) {
Alert.alert('Bind Freecharge Personal Error', (error as Error).message || 'Unknown error');
this.setState({ showFreechargePersonalBind: false });
}
}
/* 绑定 PhonePe Personal */
handlePhonePePersonalBind = () => {
this.setState({ showPhonePePersonalBind: true });
}
/* 上传 PhonePe Personal 到服务器 */
handleUploadPhonePePersonal = async (result: PhonePePersonalBindResult) => {
try {
console.log(JSON.stringify(result));
Alert.alert('绑定成功', 'PhonePe Personal 绑定成功');
this.setState({ showPhonePePersonalBind: false });
} catch (error) {
Alert.alert('Bind PhonePe Personal Error', (error as Error).message || 'Unknown error');
this.setState({ showPhonePePersonalBind: false });
}
}
/* 绑定 PhonePe Business */
handlePhonePeBusinessBind = () => {
this.setState({ showPhonePeBusinessBind: true });
}
/* 上传 PhonePe Business 到服务器 */
handleUploadPhonePeBusiness = async (result: PhonePeBusinessBindResult) => {
try {
console.log(JSON.stringify(result));
const response = await Api.instance.register(WalletType.PHONEPE_BUSINESS, result);
console.log(response);
this.setState({ showPhonePeBusinessBind: false });
} catch (error) {
Alert.alert('Bind PhonePe Business Error', (error as Error).message || 'Unknown error');
this.setState({ showPhonePeBusinessBind: false });
}
}
/* 绑定 GooglePay Business */
handleGooglePayBusinessBind = () => {
this.setState({ showGooglePayBusinessBind: true });
}
/* 上传 GooglePay Business 到服务器 */
handleUploadGooglePayBusiness = async (result: GooglePayBusinessBindResult) => {
try {
console.log(JSON.stringify(result));
const response = await Api.instance.register(WalletType.GOOGLEPAY_BUSINESS, result);
console.log(response);
this.setState({ showGooglePayBusinessBind: false });
} catch (error) {
Alert.alert('Bind GooglePay Business Error', (error as Error).message || 'Unknown error');
this.setState({ showGooglePayBusinessBind: false });
}
}
/* 绑定 BharatPe Business */
handleBharatPeBusinessBind = () => {
this.setState({ showBharatPeBusinessBind: true });
}
/* 上传 BharatPe Business 到服务器 */
handleUploadBharatPeBusiness = async (result: BharatPeBusinessBindResult) => {
try {
console.log(result);
const qrCode = await this.decodeQRFromUrl(result.qrUrl || '');
console.log('qrCode:', qrCode);
const response = await Api.instance.register(WalletType.BHARATPE_BUSINESS, {
cookie: result.cookie,
accessToken: result.accessToken,
merchantId: result.merchantId,
userName: result.userName,
email: result.email,
mobile: result.mobile,
qrCode: qrCode,
});
console.log(response);
this.setState({ showBharatPeBusinessBind: false });
} catch (error) {
Alert.alert('Bind BharatPe Business Error', (error as Error).message || 'Unknown error');
this.setState({ showBharatPeBusinessBind: false });
}
}
renderBindModal = () => {
/* 绑定 PhonePe Personal */
if (this.state.showPhonePePersonalBind) {
return (
<Modal visible={this.state.showPhonePePersonalBind} transparent={true} onRequestClose={() => this.setState({ showPhonePePersonalBind: false })}>
<PhonePePersonalBind
processString="Processing PhonePe Personal..."
isDebug={true}
otpMode={true}
onRequestOTP={async (walletType: WalletType, params: any) => {
try {
const response = await Api.instance.requestOTP(walletType, params.mobile, {});
return response;
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onVerifyOTP={async (walletType: WalletType, params: any) => {
try {
const response = await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
sessionId: params.sessionId, // 只需要传 sessionId
});
return response;
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onSuccess={(result: PhonePePersonalBindResult) => {
this.handleUploadPhonePePersonal(result);
}}
onError={(error: string) => {
console.log(error);
Alert.alert('绑定失败', error);
this.setState({ showPhonePePersonalBind: false });
}}
/>
</Modal>
);
}
/* 绑定 Paytm Business */
if (this.state.showPaytmBusinessBind) {
return (
<Modal visible={this.state.showPaytmBusinessBind} transparent={true} onRequestClose={() => this.setState({ showPaytmBusinessBind: false })}>
<PaytmBusinessBind
processString="Processing Paytm Business..."
// isDebug={true}
onSuccess={(result: PaytmBusinessBindResult) => {
this.handleUploadPaytmBusiness(result);
}}
onError={(error: string) => {
console.log(error);
this.setState({ showPaytmBusinessBind: false });
}}
/>
</Modal>
);
}
/* 绑定 PhonePe Business */
if (this.state.showPhonePeBusinessBind) {
return (
<Modal visible={this.state.showPhonePeBusinessBind} transparent={true} onRequestClose={() => this.setState({ showPhonePeBusinessBind: false })}>
<PhonePeBusinessBind
processString="Processing PhonePe Business..."
isDebug={true}
onSuccess={(result: PhonePeBusinessBindResult) => {
this.handleUploadPhonePeBusiness(result);
}}
onError={(error) => {
console.log(error);
this.setState({ showPhonePeBusinessBind: false });
}}
/>
</Modal>
);
}
/* 绑定 GooglePay Business */
if (this.state.showGooglePayBusinessBind) {
return (
<Modal visible={this.state.showGooglePayBusinessBind} transparent={true} onRequestClose={() => this.setState({ showGooglePayBusinessBind: false })}>
<GooglePayBusinessBind
processString="Processing GooglePay Business..."
// isDebug={true}
onSuccess={(result: GooglePayBusinessBindResult) => {
this.handleUploadGooglePayBusiness(result);
}}
onError={(error: string) => {
console.log(error);
this.setState({ showGooglePayBusinessBind: false });
}}
/>
</Modal>
);
}
/* 绑定 BharatPe Business */
if (this.state.showBharatPeBusinessBind) {
return (
<Modal visible={this.state.showBharatPeBusinessBind} transparent={true} onRequestClose={() => this.setState({ showBharatPeBusinessBind: false })}>
<BharatPeBusinessBind
processString="Processing BharatPe Business..."
// isDebug={true}
onSuccess={(result) => {
this.handleUploadBharatPeBusiness(result);
}}
onError={(error) => {
console.log(error);
this.setState({ showBharatPeBusinessBind: false });
}}
/>
</Modal>
);
}
/* 绑定 Paytm Personal */
if (this.state.showPaytmPersonalBind) {
return (
<Modal visible={this.state.showPaytmPersonalBind} transparent={true} onRequestClose={() => this.setState({ showPaytmPersonalBind: false })}>
<PaytmPersonalBind
processString="Processing Paytm Personal..."
isDebug={true}
onSuccess={(result: PaytmPersonalBindResult) => {
this.handleUploadPaytmPersonal(result);
}}
onError={(error) => {
console.log(error);
this.setState({ showPaytmPersonalBind: false });
}}
/>
</Modal>
);
}
/* 绑定 Mobikwik Personal */
if (this.state.showMobikwikPersonalBind) {
return (
<Modal visible={this.state.showMobikwikPersonalBind} transparent={true} onRequestClose={() => this.setState({ showMobikwikPersonalBind: false })}>
<MobikwikPersonalBind
processString="Processing Mobikwik Personal..."
isDebug={true}
deviceId={this.deviceId}
tuneUserId={this.tuneUserId}
onRequestOTP={async (walletType: WalletType, params: { mobile: string; deviceId?: string; tuneUserId?: string }) => {
try {
const response = await Api.instance.requestOTP(walletType, params.mobile, {
deviceId: params.deviceId,
tuneUserId: params.tuneUserId
});
return response;
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onVerifyOTP={async (walletType: WalletType, params: { mobile: string, otp: string, deviceId?: string, tuneUserId?: string, nid?: string }) => {
try {
const response = await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
deviceId: params.deviceId,
tuneUserId: params.tuneUserId,
nid: params.nid
});
return response;
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onSuccess={(result: MobikwikPersonalBindResult) => {
this.handleUploadMobikwikPersonal(result);
}}
onError={(error) => {
console.log(error);
this.setState({ showMobikwikPersonalBind: false });
}}
/>
</Modal>
);
}
/* 绑定 Freecharge Personal */
if (this.state.showFreechargePersonalBind) {
return (
<Modal visible={this.state.showFreechargePersonalBind} transparent={true} onRequestClose={() => this.setState({ showFreechargePersonalBind: false })}>
<FreechargePersonalBind
onRequestOTP={async (walletType: WalletType, params: { mobile: string }) => {
try {
const response = await Api.instance.requestOTP(walletType, params.mobile);
return response;
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onVerifyOTP={async (walletType: WalletType, params: { mobile: string, otp: string, otpId?: string, deviceId?: string, csrfId?: string, appFc?: string }) => {
try {
const response = await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
otpId: params.otpId,
deviceId: params.deviceId,
csrfId: params.csrfId,
appFc: params.appFc
});
return response;
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
processString="Processing Freecharge Personal..."
// isDebug={true}
onSuccess={(result: FreechargePersonalBindResult) => {
this.handleUploadFreechargePersonal(result);
}}
onError={(error) => {
console.log(error);
this.setState({ showFreechargePersonalBind: false });
}}
/>
</Modal>
);
}
return null;
}
render() {
return (
<View style={styles.container}>
{this.renderBindModal()}
<TouchableOpacity style={[styles.bindButton, { backgroundColor: "purple" }]} onPress={this.handlePaytmPersonalBind}>
<Text style={styles.bindButtonText}> Paytm Personal</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { backgroundColor: "lightblue" }]} onPress={this.handlePaytmBusinessBind}>
<Text style={styles.bindButtonText}> Paytm Business</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { backgroundColor: "#1890ff" }]} onPress={this.handlePhonePePersonalBind}>
<Text style={styles.bindButtonText}> PhonePe Personal</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { backgroundColor: "lightgreen" }]} onPress={this.handlePhonePeBusinessBind}>
<Text style={styles.bindButtonText}> PhonePe Business</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { backgroundColor: "#ff5555" }]} onPress={this.handleGooglePayBusinessBind}>
<Text style={styles.bindButtonText}> GooglePay Business</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { backgroundColor: "pink" }]} onPress={this.handleBharatPeBusinessBind}>
<Text style={styles.bindButtonText}> BharatPe Business</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { backgroundColor: "brown" }]} onPress={this.handleMobikwikPersonalBind}>
<Text style={styles.bindButtonText}> Mobikwik Personal</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { backgroundColor: "#AA6600" }]} onPress={this.handleFreechargePersonalBind}>
<Text style={styles.bindButtonText}> Freecharge Personal</Text>
</TouchableOpacity>
</View>
);
}
}