Files
rnpay/App.tsx
2026-02-06 17:48:43 +08:00

639 lines
26 KiB
TypeScript

import React, { Component } from "react";
import { Alert, AppState, AppStateStatus, Modal, Text, TouchableOpacity, View } from "react-native";
import DeviceInfo from 'react-native-device-info';
import {
PaytmBusinessBind,
PhonePeBusinessBind,
GooglePayBusinessBind,
BharatPeBusinessBind,
WalletType,
PaytmBusinessBindResult,
PhonePeBusinessBindResult,
PaytmPersonalBind,
PaytmPersonalBindResult,
MobikwikPersonalBindResult,
FreechargePersonalBindResult,
GooglePayBusinessBindResult,
BharatPeBusinessBindResult,
onSmsMessage,
onNotificationMessage,
startSmsListener,
startNotificationListener,
stopSmsListener,
stopNotificationListener,
checkSmsPermission,
checkNotificationPermission,
openNotificationSettings,
requestSmsPermission,
PhonePePersonalBindResult,
PhonePePersonalBind,
SmsMessage,
NotificationMessage,
proxyBackgroundService,
} from "rnwalletman";
import BarcodeScanning from '@react-native-ml-kit/barcode-scanning';
import RNFS from 'react-native-fs';
import {
FreeChargeBind,
MobikwikOTPBind,
PayTmPersonalOTPBind,
PhonePePersonalOTPBind
} from './components/WalletBindComponents';
import Api from './services/api';
import { AppProps, WalletmanAppState } from './types';
import { styles } from './styles';
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 = {
paytmPersonalBindType: 'otpMode',
showPaytmPersonalBind: false,
showPaytmBusinessBind: false,
showPhonePePersonalBind: false,
phonePePersonalBindType: 'otpMode',
showPhonePeBusinessBind: false,
showGooglePayBusinessBind: false,
showBharatPeBusinessBind: false,
showMobikwikPersonalBind: false,
showFreechargePersonalBind: false,
};
this.deviceId = DeviceInfo.getUniqueIdSync();
this.tuneUserId = Math.random().toString(36).substring(2, 15);
}
async componentDidMount() {
await this.setupPermissions();
// Login with auto-retry every 3s until success (setTimeout, non-blocking)
const doLogin = () => {
Api.instance.login('test123', '123456')
.then(async (userId) => {
console.log('[Login] userId:', userId);
await this.startProxyClient();
})
.catch((error) => {
console.log('[Login] retry in 3s:', error);
setTimeout(doLogin, 3000);
});
};
doLogin();
this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);
}
componentWillUnmount() {
this.stopProxyClient();
stopSmsListener();
stopNotificationListener();
}
handleAppStateChange = (nextAppState: AppStateStatus) => {
if (nextAppState === 'background' || nextAppState === 'inactive') {
console.log('[AppState] 应用进入后台,代理服务继续运行');
} else if (nextAppState === 'active') {
console.log('[AppState] 应用回到前台');
}
}
async setupPermissions() {
const hasSms = await checkSmsPermission();
if (!hasSms) await requestSmsPermission();
const hasNotif = await checkNotificationPermission();
if (!hasNotif) await openNotificationSettings();
startSmsListener();
startNotificationListener();
onSmsMessage((msg: SmsMessage) => {
console.log('[SMS]', msg.address, msg.body);
});
onNotificationMessage((msg: NotificationMessage) => {
// console.log('[Notification]', msg.packageName, msg.title, msg.text);
});
}
async startProxyClient() {
try {
this.clientId = DeviceInfo.getUniqueIdSync();
const userId = Api.instance.getUserId();
const { clientId } = this;
console.log('[Proxy] 初始化后台服务:', clientId, 'userId:', userId);
await proxyBackgroundService.start({
wsUrl: Api.WS_URL,
clientId: this.clientId || '',
userId: userId,
// debug: true,
heartbeatInterval: 10000,
reconnectInterval: 5000,
reconnectMaxAttempts: Infinity,
onConnected: () => {
console.log('[Proxy] 后台服务已连接');
},
onDisconnected: () => {
console.log('[Proxy] 后台服务已断开');
},
onError: (error: string) => {
console.warn('[Proxy] 错误:', error);
},
});
} catch (error) {
console.error('[Proxy] 初始化失败:', error);
}
}
stopProxyClient() {
try {
proxyBackgroundService.stop();
console.log('[Proxy] 后台服务已停止');
} catch (error) {
console.error('[Proxy] 停止失败:', error);
}
}
decodeQRFromUrl = async (url: string) => {
const localPath = `${RNFS.CachesDirectoryPath}/temp_qr_${Date.now()}.jpg`;
try {
await RNFS.downloadFile({ fromUrl: url, toFile: localPath }).promise;
const barcodes = await BarcodeScanning.scan(`file://${localPath}`);
if (barcodes.length > 0) return barcodes[0].value;
} catch (e) {
console.error(e);
return null;
}
};
// Paytm Personal
handleUploadPaytmPersonalToken = async (result: PaytmPersonalBindResult) => {
try {
console.log(result);
await Api.instance.register(WalletType.PAYTM_PERSONAL, result);
this.setState({ showPaytmPersonalBind: false });
Alert.alert('绑定成功', 'Paytm Personal Token 绑定成功');
} catch (error) {
Alert.alert('绑定失败', (error as Error).message);
this.setState({ showPaytmPersonalBind: false });
}
}
// PhonePe Personal
handleUploadPhonePePersonalToken = async (result: PhonePePersonalBindResult) => {
try {
console.log('PhonePe Personal Token Data:', result);
await Api.instance.register(WalletType.PHONEPE_PERSONAL, result);
this.setState({ showPhonePePersonalBind: false });
Alert.alert('绑定成功', 'PhonePe Personal Token 绑定成功');
} catch (error) {
Alert.alert('绑定失败', (error as Error).message);
this.setState({ showPhonePePersonalBind: false });
}
}
// Paytm Business
handleUploadPaytmBusiness = async (result: PaytmBusinessBindResult) => {
try {
console.log(result);
await Api.instance.register(WalletType.PAYTM_BUSINESS, result);
this.setState({ showPaytmBusinessBind: false });
} catch (error) {
Alert.alert('绑定失败', (error as Error).message);
this.setState({ showPaytmBusinessBind: false });
}
}
// PhonePe Business
handleUploadPhonePeBusiness = async (result: PhonePeBusinessBindResult) => {
try {
console.log(result);
await Api.instance.register(WalletType.PHONEPE_BUSINESS, result);
this.setState({ showPhonePeBusinessBind: false });
} catch (error) {
Alert.alert('绑定失败', (error as Error).message);
this.setState({ showPhonePeBusinessBind: false });
}
}
// GooglePay Business
handleUploadGooglePayBusiness = async (result: GooglePayBusinessBindResult) => {
try {
console.log(result);
await Api.instance.register(WalletType.GOOGLEPAY_BUSINESS, result);
this.setState({ showGooglePayBusinessBind: false });
} catch (error) {
Alert.alert('绑定失败', (error as Error).message);
this.setState({ showGooglePayBusinessBind: false });
}
}
// 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('绑定失败', (error as Error).message);
this.setState({ showBharatPeBusinessBind: false });
}
}
// Mobikwik Personal
handleUploadMobikwikPersonal = async (result: MobikwikPersonalBindResult) => {
try {
console.log(JSON.stringify(result));
this.setState({ showMobikwikPersonalBind: false });
Alert.alert('绑定成功', 'Mobikwik Personal 绑定成功');
} catch (error) {
Alert.alert('绑定失败', (error as Error).message);
this.setState({ showMobikwikPersonalBind: false });
}
}
// Freecharge Personal
handleUploadFreechargePersonal = async (result: FreechargePersonalBindResult) => {
try {
console.log(JSON.stringify(result));
this.setState({ showFreechargePersonalBind: false });
Alert.alert('绑定成功', 'Freecharge Personal 绑定成功');
} catch (error) {
Alert.alert('绑定失败', (error as Error).message);
this.setState({ showFreechargePersonalBind: false });
}
}
renderPaytmPersonalTokenBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showPaytmPersonalBind: false })}>
<PaytmPersonalBind
processString="Processing..."
isDebug={true}
onSuccess={this.handleUploadPaytmPersonalToken}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showPaytmPersonalBind: false });
}}
/>
</Modal>
);
}
renderPaytmPersonalOTPBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showPaytmPersonalBind: false })}>
<PayTmPersonalOTPBind
isDebug={true}
onRequestOTP={async (walletType, params) => {
try {
return await Api.instance.requestOTP(walletType, params.mobile, {});
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onVerifyOTP={async (walletType, params) => {
try {
return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
sessionId: params.sessionId,
});
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onSuccess={(result: PaytmPersonalBindResult) => {
console.log('[PaytmPersonal] OTP 绑定成功:', result);
Alert.alert('绑定成功', 'Paytm Personal OTP 绑定成功');
this.setState({ showPaytmPersonalBind: false });
}}
onError={(error: string) => {
console.log('[PaytmPersonal] OTP 绑定失败:', error);
Alert.alert('绑定失败', error);
this.setState({ showPaytmPersonalBind: false });
}}
/>
</Modal>
);
}
renderPhonePePersonalTokenBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showPhonePePersonalBind: false })}>
<PhonePePersonalBind
processString="Processing..."
isDebug={true}
onSuccess={this.handleUploadPhonePePersonalToken}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showPhonePePersonalBind: false });
}}
/>
</Modal>
);
}
renderPhonePePersonalOTPBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showPhonePePersonalBind: false })}>
<PhonePePersonalOTPBind
isDebug={true}
onRequestOTP={async (walletType, params) => {
try {
return await Api.instance.requestOTP(walletType, params.mobile, {});
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onVerifyOTP={async (walletType, params) => {
try {
return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
sessionId: params.sessionId,
});
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onSuccess={(result: PhonePePersonalBindResult) => {
console.log('[PhonePePersonal] OTP 绑定成功:', result);
Alert.alert('绑定成功', 'PhonePe Personal OTP 绑定成功');
this.setState({ showPhonePePersonalBind: false });
}}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showPhonePePersonalBind: false });
}}
/>
</Modal>
);
}
renderPaytmBusinessBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showPaytmBusinessBind: false })}>
<PaytmBusinessBind
processString="Processing..."
isDebug={true}
onSuccess={this.handleUploadPaytmBusiness}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showPaytmBusinessBind: false });
}}
/>
</Modal>
);
}
renderPhonePeBusinessBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showPhonePeBusinessBind: false })}>
<PhonePeBusinessBind
processString="Processing..."
isDebug={true}
onSuccess={this.handleUploadPhonePeBusiness}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showPhonePeBusinessBind: false });
}}
/>
</Modal>
);
}
renderGooglePayBusinessBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showGooglePayBusinessBind: false })}>
<GooglePayBusinessBind
processString="Processing..."
isDebug={true}
onSuccess={this.handleUploadGooglePayBusiness}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showGooglePayBusinessBind: false });
}}
/>
</Modal>
);
}
renderBharatPeBusinessBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showBharatPeBusinessBind: false })}>
<BharatPeBusinessBind
processString="Processing..."
isDebug={true}
onSuccess={this.handleUploadBharatPeBusiness}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showBharatPeBusinessBind: false });
}}
/>
</Modal>
);
}
renderMobikwikPersonalOTPBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showMobikwikPersonalBind: false })}>
<MobikwikOTPBind
isDebug={true}
deviceId={this.deviceId}
tuneUserId={this.tuneUserId}
onRequestOTP={async (walletType, params) => {
try {
return await Api.instance.requestOTP(walletType, params.mobile, {
deviceId: params.deviceId,
tuneUserId: params.tuneUserId
});
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onVerifyOTP={async (walletType, params) => {
try {
return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
deviceId: params.deviceId,
tuneUserId: params.tuneUserId,
nid: params.nid
});
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onSuccess={this.handleUploadMobikwikPersonal}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showMobikwikPersonalBind: false });
}}
/>
</Modal>
);
}
renderFreechargePersonalOTPBind = () => {
return (
<Modal visible transparent onRequestClose={() => this.setState({ showFreechargePersonalBind: false })}>
<FreeChargeBind
isDebug={true}
onRequestOTP={async (walletType, params) => {
try {
return await Api.instance.requestOTP(walletType, params.mobile);
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onVerifyOTP={async (walletType, params) => {
try {
return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
otpId: params.otpId,
deviceId: params.deviceId,
csrfId: params.csrfId,
appFc: params.appFc
});
} catch (error) {
return { success: false, message: (error as Error).message };
}
}}
onSuccess={this.handleUploadFreechargePersonal}
onError={(error: string) => {
Alert.alert('绑定失败', error);
this.setState({ showFreechargePersonalBind: false });
}}
/>
</Modal>
);
}
renderBindModal = () => {
// Paytm Personal
if (this.state.showPaytmPersonalBind) {
if (this.state.paytmPersonalBindType === 'tokenMode') {
return this.renderPaytmPersonalTokenBind();
} else {
return this.renderPaytmPersonalOTPBind ();
}
}
// PhonePe Personal
if (this.state.showPhonePePersonalBind) {
if (this.state.phonePePersonalBindType === 'tokenMode') {
return this.renderPhonePePersonalTokenBind();
} else {
return this.renderPhonePePersonalOTPBind ();
}
}
// Paytm Business
if (this.state.showPaytmBusinessBind) {
return this.renderPaytmBusinessBind ();
}
// PhonePe Business
if (this.state.showPhonePeBusinessBind) {
this.renderPhonePeBusinessBind ();
}
// GooglePay Business
if (this.state.showGooglePayBusinessBind) {
return this.renderGooglePayBusinessBind ();
}
// BharatPe Business
if (this.state.showBharatPeBusinessBind) {
return this.renderBharatPeBusinessBind ();
}
// Mobikwik Personal
if (this.state.showMobikwikPersonalBind) {
return this.renderMobikwikPersonalOTPBind ();
}
// Freecharge Personal
if (this.state.showFreechargePersonalBind) {
return this.renderFreechargePersonalOTPBind ();
}
return null;
}
render() {
return (
<View style={styles.container}>
{this.renderBindModal()}
<View style={[styles.bindButton, { backgroundColor: "transparent", flexDirection: "row", justifyContent: "space-between", alignItems: "center" }]}>
<TouchableOpacity style={[styles.bindButton, { marginBottom: 0, width: '45%', backgroundColor: "#888888" }]} onPress={() => {
this.setState({ showPaytmPersonalBind: true, paytmPersonalBindType: 'otpMode' });
}}>
<Text style={styles.bindButtonText}> Paytm Personal(OTP)</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { marginBottom: 0, width: '45%', backgroundColor: "#888888" }]} onPress={() => {
this.setState({ showPaytmPersonalBind: true, paytmPersonalBindType: 'tokenMode' });
}}>
<Text style={styles.bindButtonText}> Paytm Personal(Token)</Text>
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.bindButton} onPress={() => {
this.setState({ showPaytmBusinessBind: true });
}}>
<Text style={styles.bindButtonText}> Paytm Business</Text>
</TouchableOpacity>
<View style={[styles.bindButton, { backgroundColor: "transparent", flexDirection: "row", justifyContent: "space-between", alignItems: "center" }]}>
<TouchableOpacity style={[styles.bindButton, { marginBottom: 0, width: '45%', backgroundColor: "#888888" }]} onPress={() => {
this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'otpMode' });
}}>
<Text style={styles.bindButtonText}> PhonePe Personal(OTP)</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.bindButton, { marginBottom: 0, width: '45%', backgroundColor: "#888888" }]} onPress={() => {
this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'tokenMode' });
}}>
<Text style={styles.bindButtonText}> PhonePe Personal(Token)</Text>
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.bindButton} onPress={() => {
this.setState({ showPhonePeBusinessBind: true });
}}>
<Text style={styles.bindButtonText}> PhonePe Business</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.bindButton} onPress={() => {
this.setState({ showGooglePayBusinessBind: true });
}}>
<Text style={styles.bindButtonText}> GooglePay Business</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.bindButton} onPress={() => {
this.setState({ showBharatPeBusinessBind: true });
}}>
<Text style={styles.bindButtonText}> BharatPe Business</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.bindButton} onPress={() => {
this.setState({ showMobikwikPersonalBind: true });
}}>
<Text style={styles.bindButtonText}> Mobikwik Personal</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.bindButton} onPress={() => {
this.setState({ showFreechargePersonalBind: true });
}}>
<Text style={styles.bindButtonText}> Freecharge Personal</Text>
</TouchableOpacity>
</View>
);
}
}