823 lines
36 KiB
TypeScript
823 lines
36 KiB
TypeScript
import React, { Component } from "react";
|
||
import { Alert, AppState, AppStateStatus, Modal, Text, TextInput, TouchableOpacity, View } from "react-native";
|
||
import DeviceInfo from 'react-native-device-info';
|
||
import {
|
||
PhonePeBusinessBind,
|
||
GooglePayBusinessBind,
|
||
WalletType,
|
||
PaytmBusinessBindResult,
|
||
PhonePeBusinessBindResult,
|
||
PaytmPersonalBind,
|
||
PaytmPersonalBindResult,
|
||
MobikwikPersonalBindResult,
|
||
FreechargePersonalBindResult,
|
||
GooglePayBusinessBindResult,
|
||
BharatPeBusinessBindResult,
|
||
onSmsMessage,
|
||
startSmsListener,
|
||
stopSmsListener,
|
||
checkSmsPermission,
|
||
requestSmsPermission,
|
||
PhonePePersonalBindResult,
|
||
PhonePePersonalBind,
|
||
SmsMessage,
|
||
proxyBackgroundService,
|
||
proxySendMessage,
|
||
onProxyMessage,
|
||
} from "rnwalletman";
|
||
|
||
import {
|
||
FreeChargeBind,
|
||
MobikwikOTPBind,
|
||
PayTmPersonalOTPBind,
|
||
PhonePePersonalOTPBind,
|
||
BharatPeBusinessOTPBind,
|
||
PaytmBusinessOTPBind,
|
||
} from './components/WalletBindComponents';
|
||
|
||
import Api from './services/api';
|
||
import { AppProps, WalletmanAppState } from './types';
|
||
import { styles } from './styles';
|
||
import WebView from "react-native-webview";
|
||
|
||
export default class App extends Component<AppProps, WalletmanAppState> {
|
||
private deviceId: string;
|
||
private tuneUserId: string;
|
||
private clientId: string = '';
|
||
private appStateSubscription?: any;
|
||
|
||
private webViewRef: WebView | null = null;
|
||
|
||
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,
|
||
proxyStatus: 'idle',
|
||
};
|
||
|
||
this.deviceId = DeviceInfo.getUniqueIdSync();
|
||
this.tuneUserId = Math.random().toString(36).substring(2, 15);
|
||
}
|
||
|
||
async componentDidMount() {
|
||
await this.setupPermissions();
|
||
|
||
this.onProxyMessageSub = onProxyMessage((msg) => {
|
||
switch (msg.type) {
|
||
case 'echo':
|
||
Alert.alert('Echo 回来了', JSON.stringify(msg.data));
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
});
|
||
|
||
// 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);
|
||
}
|
||
|
||
private onProxyMessageSub?: ReturnType<typeof onProxyMessage>;
|
||
|
||
componentWillUnmount() {
|
||
this.stopProxyClient();
|
||
stopSmsListener();
|
||
this.onProxyMessageSub?.remove();
|
||
}
|
||
|
||
sendEcho = () => {
|
||
const text = `hello_${Date.now()}`;
|
||
proxySendMessage({ type: 'echo', messageId: `echo_${Date.now()}`, data: { text } });
|
||
};
|
||
|
||
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();
|
||
|
||
startSmsListener();
|
||
|
||
onSmsMessage((msg: SmsMessage) => {
|
||
console.log('[SMS]', msg.address, msg.body);
|
||
});
|
||
}
|
||
|
||
async startProxyClient() {
|
||
try {
|
||
this.clientId = DeviceInfo.getUniqueIdSync();
|
||
const userId = Api.instance.getUserId();
|
||
const { clientId } = this;
|
||
console.log('[Proxy] 初始化后台服务:', clientId, 'userId:', userId);
|
||
|
||
this.setState({ proxyStatus: 'connecting' });
|
||
await proxyBackgroundService.start({
|
||
wsUrl: Api.WS_URL,
|
||
clientId: this.clientId || '',
|
||
userId: userId,
|
||
// debug: true,
|
||
heartbeatInterval: 10000,
|
||
reconnectInterval: 5000,
|
||
reconnectMaxAttempts: Infinity,
|
||
onConnected: () => {
|
||
console.log('[Proxy] 后台服务已连接');
|
||
this.setState({ proxyStatus: 'connected' });
|
||
},
|
||
onDisconnected: () => {
|
||
console.log('[Proxy] 后台服务已断开');
|
||
this.setState({ proxyStatus: 'disconnected' });
|
||
},
|
||
onError: (error: string) => {
|
||
console.log('[Proxy] 错误:', error);
|
||
this.setState({ proxyStatus: 'error', proxyError: error });
|
||
},
|
||
});
|
||
} catch (error) {
|
||
console.error('[Proxy] 初始化失败:', error);
|
||
}
|
||
}
|
||
|
||
stopProxyClient() {
|
||
try {
|
||
proxyBackgroundService.stop();
|
||
console.log('[Proxy] 后台服务已停止');
|
||
} catch (error) {
|
||
console.error('[Proxy] 停止失败:', error);
|
||
}
|
||
}
|
||
|
||
// 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);
|
||
this.setState({ showPaytmBusinessBind: false });
|
||
Alert.alert('绑定成功', 'Paytm Business 绑定成功');
|
||
} 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 });
|
||
Alert.alert('绑定成功', 'PhonePe Business 绑定成功');
|
||
} 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 });
|
||
Alert.alert('绑定成功', 'Google Pay Business 绑定成功');
|
||
} catch (error) {
|
||
Alert.alert('绑定失败', (error as Error).message);
|
||
this.setState({ showGooglePayBusinessBind: false });
|
||
}
|
||
}
|
||
|
||
// BharatPe Business
|
||
handleUploadBharatPeBusiness = async (result: BharatPeBusinessBindResult) => {
|
||
try {
|
||
console.log(JSON.stringify(result));
|
||
this.setState({ showBharatPeBusinessBind: false });
|
||
Alert.alert('绑定成功', 'BharatPe Business 绑定成功');
|
||
} 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 })}>
|
||
<PaytmBusinessOTPBind
|
||
isDebug={true}
|
||
onRequestOTP={async (walletType, params) => {
|
||
try {
|
||
return await Api.instance.requestOTP(walletType, params.mobile, { password: params.password });
|
||
} 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={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 });
|
||
}}
|
||
onRenderBottomView={({ showOtpInput, loading, formError, phone, otp, onPhoneChange, onOtpChange, onGetOtp, onSubmitOtp }) => (
|
||
<View style={{ position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: '#fff', padding: 16, borderTopWidth: 1, borderTopColor: '#e0e0e0', gap: 10 }}>
|
||
{!showOtpInput ? (
|
||
<>
|
||
<TextInput
|
||
style={{ borderWidth: 1, borderColor: '#ccc', borderRadius: 8, paddingHorizontal: 12, paddingVertical: 10, fontSize: 15, color: '#333' }}
|
||
placeholder="Mobile Number"
|
||
placeholderTextColor="#999"
|
||
keyboardType="phone-pad"
|
||
value={phone}
|
||
onChangeText={onPhoneChange}
|
||
editable={!loading}
|
||
/>
|
||
{!!formError && <Text style={{ color: '#e53935', fontSize: 13 }}>{formError}</Text>}
|
||
<TouchableOpacity style={{ backgroundColor: '#5a2d9c', borderRadius: 8, paddingVertical: 12, alignItems: 'center', opacity: loading ? 0.5 : 1 }} onPress={onGetOtp} disabled={loading}>
|
||
<Text style={{ color: '#fff', fontSize: 15, fontWeight: '600' }}>{loading ? 'Loading...' : 'GET OTP'}</Text>
|
||
</TouchableOpacity>
|
||
</>
|
||
) : (
|
||
<>
|
||
<TextInput
|
||
style={{ borderWidth: 1, borderColor: '#ccc', borderRadius: 8, paddingHorizontal: 12, paddingVertical: 10, fontSize: 15, color: '#333' }}
|
||
placeholder="OTP"
|
||
placeholderTextColor="#999"
|
||
keyboardType="number-pad"
|
||
value={otp}
|
||
onChangeText={onOtpChange}
|
||
editable={!loading}
|
||
/>
|
||
{!!formError && <Text style={{ color: '#e53935', fontSize: 13 }}>{formError}</Text>}
|
||
<TouchableOpacity style={{ backgroundColor: '#5a2d9c', borderRadius: 8, paddingVertical: 12, alignItems: 'center', opacity: loading ? 0.5 : 1 }} onPress={onSubmitOtp} disabled={loading}>
|
||
<Text style={{ color: '#fff', fontSize: 15, fontWeight: '600' }}>{loading ? 'Loading...' : 'Verify OTP'}</Text>
|
||
</TouchableOpacity>
|
||
</>
|
||
)}
|
||
</View>
|
||
)}
|
||
/>
|
||
</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 })}>
|
||
<BharatPeBusinessOTPBind
|
||
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={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) {
|
||
return 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;
|
||
}
|
||
|
||
renderWebDemo() {
|
||
const handleMessage = (event: any) => {
|
||
try {
|
||
const data = JSON.parse(event.nativeEvent.data);
|
||
if (data.type === 'iframe_message') {
|
||
console.log('[Demo] iframe message:', data.data);
|
||
}
|
||
} catch (e) {
|
||
console.error('[Demo] Parse error:', e);
|
||
}
|
||
};
|
||
|
||
const syncToIframe = (fieldId: string, value: string) => {
|
||
const js = `
|
||
window.postMessage({
|
||
type: 'set_field',
|
||
fieldId: '${fieldId}',
|
||
value: '${value.replace(/'/g, "\\'")}'
|
||
}, '*');
|
||
`;
|
||
this.webViewRef?.injectJavaScript(js);
|
||
};
|
||
|
||
const clickLogin = () => {
|
||
const js = `window.postMessage({type: 'click_login'}, '*');`;
|
||
this.webViewRef?.injectJavaScript(js);
|
||
};
|
||
|
||
const injectedJS = `
|
||
window.addEventListener('message', function(event) {
|
||
window.ReactNativeWebView.postMessage(JSON.stringify({
|
||
type: 'iframe_message',
|
||
data: event.data
|
||
}));
|
||
});
|
||
true;
|
||
`;
|
||
|
||
return (
|
||
<View style={{flex: 1}}>
|
||
<WebView
|
||
ref={(ref) => { this.webViewRef = ref; }}
|
||
source={{ uri: 'https://dashboard.paytm.com' }}
|
||
javaScriptEnabled={true}
|
||
domStorageEnabled={true}
|
||
thirdPartyCookiesEnabled={true}
|
||
sharedCookiesEnabled={true}
|
||
onMessage={handleMessage}
|
||
injectedJavaScript={injectedJS}
|
||
interceptUrlPattern="accounts.paytm.com/oauth-js-sdk/"
|
||
interceptInjectedScript={`
|
||
if (!window.__paytmMessageHandler) {
|
||
window.__paytmMessageHandler = function(event) {
|
||
if (event.data.type === 'set_field') {
|
||
const el = document.getElementById(event.data.fieldId);
|
||
if (el) {
|
||
el.click();
|
||
el.focus();
|
||
el.value = event.data.value;
|
||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||
el.dispatchEvent(new Event('change', { bubbles: true }));
|
||
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
||
}
|
||
}
|
||
if (event.data.type === 'click_login') {
|
||
const btn = document.getElementById('login_button');
|
||
if (btn) btn.click();
|
||
}
|
||
};
|
||
window.addEventListener('message', window.__paytmMessageHandler);
|
||
window.parent.postMessage({type: 'iframe_ready'}, '*');
|
||
}
|
||
`}
|
||
/>
|
||
|
||
<View style={{padding: 20, backgroundColor: '#f5f5f5', borderTopWidth: 1, borderTopColor: '#ddd'}}>
|
||
<Text style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Test Controls</Text>
|
||
|
||
<Text style={{marginTop: 5}}>Mobile:</Text>
|
||
<TextInput
|
||
style={{borderWidth: 1, borderColor: '#ccc', padding: 10, borderRadius: 5, backgroundColor: '#fff', marginTop: 5}}
|
||
onChangeText={(text) => syncToIframe('email_mobile_login', text)}
|
||
placeholder="Enter mobile number"
|
||
keyboardType="phone-pad"
|
||
/>
|
||
|
||
<Text style={{marginTop: 10}}>Password:</Text>
|
||
<TextInput
|
||
style={{borderWidth: 1, borderColor: '#ccc', padding: 10, borderRadius: 5, backgroundColor: '#fff', marginTop: 5}}
|
||
onChangeText={(text) => syncToIframe('password_login', text)}
|
||
placeholder="Enter password"
|
||
secureTextEntry
|
||
/>
|
||
|
||
<TouchableOpacity
|
||
style={{marginTop: 15, backgroundColor: '#00BAF2', padding: 15, borderRadius: 5, alignItems: 'center'}}
|
||
onPress={clickLogin}
|
||
>
|
||
<Text style={{color: '#fff', fontSize: 16, fontWeight: 'bold'}}>Login</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
render() {
|
||
// return this.renderWebDemo();
|
||
return (
|
||
<View style={styles.container}>
|
||
{(() => {
|
||
const { proxyStatus, proxyError } = this.state;
|
||
const cfg: Record<string, { label: string; color: string }> = {
|
||
idle: { label: '未连接', color: '#95a5a6' },
|
||
connecting: { label: '连接中…', color: '#f39c12' },
|
||
connected: { label: '已连接', color: '#2ecc71' },
|
||
disconnected: { label: '已断开', color: '#e74c3c' },
|
||
error: { label: '连接失败', color: '#e74c3c' },
|
||
};
|
||
const { label, color } = cfg[proxyStatus];
|
||
return (
|
||
<View style={{ flexDirection: 'row', alignItems: 'center', paddingVertical: 6, paddingHorizontal: 12 }}>
|
||
<View style={{ width: 8, height: 8, borderRadius: 4, backgroundColor: color, marginRight: 6 }} />
|
||
<Text style={{ color, fontSize: 13 }}>
|
||
Proxy {label}{proxyStatus === 'error' && proxyError ? `:${proxyError}` : ''}
|
||
</Text>
|
||
</View>
|
||
);
|
||
})()}
|
||
{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 (OTP)</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 (OTP-WEB)</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 (OTP)</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity style={styles.bindButton} onPress={() => {
|
||
this.setState({ showMobikwikPersonalBind: true });
|
||
}}>
|
||
<Text style={styles.bindButtonText}>绑定 Mobikwik Personal (OTP)</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity style={styles.bindButton} onPress={() => {
|
||
this.setState({ showFreechargePersonalBind: true });
|
||
}}>
|
||
<Text style={styles.bindButtonText}>绑定 Freecharge Personal (OTP)</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity style={[styles.bindButton, { backgroundColor: '#2ecc71' }]} onPress={this.sendEcho}>
|
||
<Text style={styles.bindButtonText}>Echo 测试</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
);
|
||
}
|
||
}
|