Otp / Token 模式 OK
This commit is contained in:
@@ -13,6 +13,7 @@ import { useOTPBind } from '../hooks/useOTPBind';
|
|||||||
interface OTPBindUIProps {
|
interface OTPBindUIProps {
|
||||||
walletType: WalletType;
|
walletType: WalletType;
|
||||||
title: string;
|
title: string;
|
||||||
|
subtitle?: string;
|
||||||
otpLength?: number;
|
otpLength?: number;
|
||||||
mobileLength?: number;
|
mobileLength?: number;
|
||||||
passwordBeforeOtp?: boolean;
|
passwordBeforeOtp?: boolean;
|
||||||
@@ -32,6 +33,7 @@ interface OTPBindUIProps {
|
|||||||
export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
||||||
walletType,
|
walletType,
|
||||||
title,
|
title,
|
||||||
|
subtitle,
|
||||||
otpLength = 6,
|
otpLength = 6,
|
||||||
mobileLength = 10,
|
mobileLength = 10,
|
||||||
passwordBeforeOtp = false,
|
passwordBeforeOtp = false,
|
||||||
@@ -78,6 +80,7 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
|||||||
<View style={styles.overlay}>
|
<View style={styles.overlay}>
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<Text style={styles.title}>{title}</Text>
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
{!!subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
|
||||||
{!!state.errorMessage && (
|
{!!state.errorMessage && (
|
||||||
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
||||||
)}
|
)}
|
||||||
@@ -182,6 +185,7 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
|||||||
<View style={styles.overlay}>
|
<View style={styles.overlay}>
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<Text style={styles.title}>{title}</Text>
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
{!!subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
|
||||||
|
|
||||||
{!showOtp && (
|
{!showOtp && (
|
||||||
<>
|
<>
|
||||||
@@ -287,6 +291,14 @@ const styles = StyleSheet.create({
|
|||||||
color: '#111',
|
color: '#111',
|
||||||
marginBottom: 18,
|
marginBottom: 18,
|
||||||
},
|
},
|
||||||
|
subtitle: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#888',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: -10,
|
||||||
|
marginBottom: 14,
|
||||||
|
lineHeight: 17,
|
||||||
|
},
|
||||||
label: {
|
label: {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: '#666',
|
color: '#666',
|
||||||
|
|||||||
@@ -1,8 +1,45 @@
|
|||||||
import React, { Component, useState, useEffect } from 'react';
|
import React, { Component, useState, useEffect } from 'react';
|
||||||
import { View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
|
import { Alert, View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
|
||||||
import { WalletType, FreechargePersonalBindResult, MobikwikPersonalBindResult, PaytmPersonalBindResult, PhonePePersonalBindResult, PhonePeBusinessBindResult, BharatPeBusinessBindResult, PaytmBusinessBindResult } from 'rnwalletman';
|
import {
|
||||||
|
WalletType,
|
||||||
|
BindErrorCode,
|
||||||
|
MobikwikPersonalBind,
|
||||||
|
FreechargePersonalBindResult,
|
||||||
|
MobikwikPersonalBindResult,
|
||||||
|
PaytmPersonalBindResult,
|
||||||
|
PhonePePersonalBindResult,
|
||||||
|
PhonePeBusinessBindResult,
|
||||||
|
BharatPeBusinessBindResult,
|
||||||
|
PaytmBusinessBindResult,
|
||||||
|
} from 'rnwalletman';
|
||||||
import { OTPBindUI } from './OTPBindUI';
|
import { OTPBindUI } from './OTPBindUI';
|
||||||
|
|
||||||
|
export function alertMobikwikAidlBindError(code: string, message: string, onClose?: () => void) {
|
||||||
|
let msg = message || 'Bind failed';
|
||||||
|
switch (code) {
|
||||||
|
case BindErrorCode.NOT_INSTALLED:
|
||||||
|
msg = 'Patched Mobikwik app not installed. Install mobikwik_ipay_2365.apk';
|
||||||
|
break;
|
||||||
|
case BindErrorCode.NOT_LOGGED_IN:
|
||||||
|
msg = 'Please log in to the Mobikwik app first';
|
||||||
|
break;
|
||||||
|
case BindErrorCode.SERVICE_DISCONNECTED:
|
||||||
|
msg = 'Mobikwik service unavailable. Open the app and try again';
|
||||||
|
break;
|
||||||
|
case BindErrorCode.NO_DATA:
|
||||||
|
msg = 'No login data received';
|
||||||
|
break;
|
||||||
|
case BindErrorCode.BIND_ERROR:
|
||||||
|
msg = 'Bind failed. Open Mobikwik manually and try again';
|
||||||
|
break;
|
||||||
|
case BindErrorCode.NATIVE_MODULE_UNAVAILABLE:
|
||||||
|
msg = 'Native module not available';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Alert.alert('Bind Failed', msg);
|
||||||
|
onClose?.();
|
||||||
|
}
|
||||||
|
|
||||||
export class FreeChargeBind extends Component<{
|
export class FreeChargeBind extends Component<{
|
||||||
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
@@ -28,10 +65,38 @@ export class FreeChargeBind extends Component<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MobikwikOTPBind extends Component<{
|
/** Mobikwik 2365 AIDL bind */
|
||||||
|
export class MobikwikPersonalTokenBind extends Component<{
|
||||||
|
userToken: string;
|
||||||
|
onSuccess: (result: MobikwikPersonalBindResult) => void;
|
||||||
|
onClose?: () => void;
|
||||||
|
isDebug?: boolean;
|
||||||
|
}> {
|
||||||
|
render() {
|
||||||
|
const { userToken, onSuccess, onClose, isDebug = false } = this.props;
|
||||||
|
return (
|
||||||
|
<MobikwikPersonalBind
|
||||||
|
processString="Binding Mobikwik..."
|
||||||
|
userToken={userToken}
|
||||||
|
isDebug={isDebug}
|
||||||
|
onSuccess={(result: MobikwikPersonalBindResult) => {
|
||||||
|
if (!result.hashId) {
|
||||||
|
Alert.alert('Bind Failed', 'Please log in to the Mobikwik app first');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSuccess(result);
|
||||||
|
}}
|
||||||
|
onError={(code: string, message: string) => alertMobikwikAidlBindError(code, message, onClose)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Mobikwik web OTP bind */
|
||||||
|
export class MobikwikPersonalOTPBind extends Component<{
|
||||||
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
onSuccess: (result: MobikwikPersonalBindResult) => void;
|
onSuccess: (result: any) => void;
|
||||||
onError: (error: string) => void;
|
onError: (error: string) => void;
|
||||||
isDebug: boolean;
|
isDebug: boolean;
|
||||||
initialMobile?: string;
|
initialMobile?: string;
|
||||||
@@ -40,7 +105,8 @@ export class MobikwikOTPBind extends Component<{
|
|||||||
return (
|
return (
|
||||||
<OTPBindUI
|
<OTPBindUI
|
||||||
walletType={WalletType.MOBIKWIK_PERSONAL}
|
walletType={WalletType.MOBIKWIK_PERSONAL}
|
||||||
title="Bind Mobikwik"
|
title="Bind Mobikwik (OTP)"
|
||||||
|
subtitle="Web OTP login. If you have the 2365 patched app, use Token Mode instead"
|
||||||
otpLength={6}
|
otpLength={6}
|
||||||
onRequestOTP={this.props.onRequestOTP}
|
onRequestOTP={this.props.onRequestOTP}
|
||||||
onVerifyOTP={this.props.onVerifyOTP}
|
onVerifyOTP={this.props.onVerifyOTP}
|
||||||
@@ -53,6 +119,9 @@ export class MobikwikOTPBind extends Component<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @deprecated Use MobikwikPersonalOTPBind */
|
||||||
|
export const MobikwikOTPBind = MobikwikPersonalOTPBind;
|
||||||
|
|
||||||
export class PayTmPersonalOTPBind extends Component<{
|
export class PayTmPersonalOTPBind extends Component<{
|
||||||
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
|||||||
255
components/WalletSelectModal.tsx
Normal file
255
components/WalletSelectModal.tsx
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Image,
|
||||||
|
Modal,
|
||||||
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from 'react-native';
|
||||||
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
|
||||||
|
export type WalletBindAction = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WalletSelectRow = {
|
||||||
|
walletType: string;
|
||||||
|
label: string;
|
||||||
|
actions: WalletBindAction[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WALLET_ICONS: Record<string, number> = {
|
||||||
|
paytm: require('../res/paytm.png'),
|
||||||
|
'paytm business': require('../res/paytm-business.png'),
|
||||||
|
phonepe: require('../res/phonepe.webp'),
|
||||||
|
'phonepe business': require('../res/phonepe-business.webp'),
|
||||||
|
'googlepay business': require('../res/googlepay-business.webp'),
|
||||||
|
'bharatpe business': require('../res/bharatpe-business.webp'),
|
||||||
|
mobikwik: require('../res/mobikwik.png'),
|
||||||
|
freecharge: require('../res/freecharge.png'),
|
||||||
|
amazonpay: require('../res/amazon.png'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WALLET_TYPE_COLORS: Record<string, string> = {
|
||||||
|
paytm: '#002970',
|
||||||
|
'paytm business': '#002970',
|
||||||
|
phonepe: '#5a2d9c',
|
||||||
|
'phonepe business': '#5a2d9c',
|
||||||
|
'googlepay business': '#4285f4',
|
||||||
|
'bharatpe business': '#e91e63',
|
||||||
|
mobikwik: '#00bcd4',
|
||||||
|
freecharge: '#ff5722',
|
||||||
|
amazonpay: '#ff9900',
|
||||||
|
};
|
||||||
|
|
||||||
|
const TOKEN_MODE = 'Token Mode';
|
||||||
|
const OTP_MODE = 'Otp Mode';
|
||||||
|
|
||||||
|
export const WALLET_SELECT_ROWS: WalletSelectRow[] = [
|
||||||
|
{
|
||||||
|
walletType: 'paytm',
|
||||||
|
label: 'Paytm Personal',
|
||||||
|
actions: [
|
||||||
|
{ key: 'paytm_personal_token', label: TOKEN_MODE },
|
||||||
|
{ key: 'paytm_personal_otp', label: OTP_MODE },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walletType: 'paytm business',
|
||||||
|
label: 'Paytm Business',
|
||||||
|
actions: [{ key: 'paytm_business', label: OTP_MODE }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walletType: 'phonepe',
|
||||||
|
label: 'PhonePe Personal',
|
||||||
|
actions: [
|
||||||
|
{ key: 'phonepe_personal_token', label: TOKEN_MODE },
|
||||||
|
{ key: 'phonepe_personal_otp', label: OTP_MODE },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walletType: 'phonepe business',
|
||||||
|
label: 'PhonePe Business',
|
||||||
|
actions: [
|
||||||
|
{ key: 'phonepe_business_web', label: TOKEN_MODE },
|
||||||
|
{ key: 'phonepe_business_otp', label: OTP_MODE },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walletType: 'googlepay business',
|
||||||
|
label: 'GooglePay Business',
|
||||||
|
actions: [{ key: 'googlepay_business', label: TOKEN_MODE }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walletType: 'bharatpe business',
|
||||||
|
label: 'BharatPe Business',
|
||||||
|
actions: [{ key: 'bharatpe_business', label: OTP_MODE }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walletType: 'mobikwik',
|
||||||
|
label: 'Mobikwik Personal',
|
||||||
|
actions: [
|
||||||
|
{ key: 'mobikwik_personal_token', label: TOKEN_MODE },
|
||||||
|
{ key: 'mobikwik_personal', label: OTP_MODE },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walletType: 'freecharge',
|
||||||
|
label: 'Freecharge Personal',
|
||||||
|
actions: [
|
||||||
|
{ key: 'freecharge_personal_token', label: TOKEN_MODE },
|
||||||
|
{ key: 'freecharge_personal', label: OTP_MODE },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walletType: 'amazonpay',
|
||||||
|
label: 'Amazon Pay',
|
||||||
|
actions: [{ key: 'amazonpay_personal', label: OTP_MODE }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onSelectBind: (bindKey: string) => void;
|
||||||
|
rows?: WalletSelectRow[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WalletSelectModal: React.FC<Props> = ({
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
onSelectBind,
|
||||||
|
rows = WALLET_SELECT_ROWS,
|
||||||
|
}) => (
|
||||||
|
<Modal visible={visible} transparent animationType="none" onRequestClose={onClose}>
|
||||||
|
<View style={styles.overlay}>
|
||||||
|
<Animatable.View
|
||||||
|
animation="zoomIn"
|
||||||
|
duration={220}
|
||||||
|
easing="ease-out-back"
|
||||||
|
useNativeDriver
|
||||||
|
style={styles.box}
|
||||||
|
>
|
||||||
|
<View style={styles.header}>
|
||||||
|
<Text style={styles.title}>Select Wallet Type</Text>
|
||||||
|
<TouchableOpacity onPress={onClose} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
|
||||||
|
<Text style={styles.close}>✕</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<ScrollView bounces={false}>
|
||||||
|
{rows.map((row) => (
|
||||||
|
<View key={row.label} style={styles.row}>
|
||||||
|
{WALLET_ICONS[row.walletType] ? (
|
||||||
|
<Image
|
||||||
|
source={WALLET_ICONS[row.walletType]}
|
||||||
|
style={styles.icon}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.dot,
|
||||||
|
{ backgroundColor: WALLET_TYPE_COLORS[row.walletType] ?? '#888' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Text style={styles.label} numberOfLines={1}>
|
||||||
|
{row.label}
|
||||||
|
</Text>
|
||||||
|
<View style={styles.actions}>
|
||||||
|
{row.actions.map((action) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={action.key}
|
||||||
|
style={styles.actionBtn}
|
||||||
|
onPress={() => onSelectBind(action.key)}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
>
|
||||||
|
<Text style={styles.actionBtnText}>{action.label}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
</Animatable.View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
overlay: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
box: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 16,
|
||||||
|
width: '88%',
|
||||||
|
maxHeight: '80%',
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 17,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#222',
|
||||||
|
},
|
||||||
|
close: {
|
||||||
|
fontSize: 20,
|
||||||
|
color: '#999',
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#f0f0f0',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
borderRadius: 6,
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
dot: {
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
borderRadius: 5,
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#333',
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 6,
|
||||||
|
},
|
||||||
|
actionBtn: {
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 6,
|
||||||
|
borderRadius: 6,
|
||||||
|
backgroundColor: '#f0f4ff',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#d0daf0',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
actionBtnText: {
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#3355aa',
|
||||||
|
},
|
||||||
|
});
|
||||||
42
docs/成功率分析.log
Normal file
42
docs/成功率分析.log
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
日期 成功率 修改
|
||||||
|
2026-04-19 0
|
||||||
|
2026-04-20 9.57
|
||||||
|
2026-04-21 8.1
|
||||||
|
2026-04-22 0
|
||||||
|
2026-04-23 8.08
|
||||||
|
2026-04-24 6.87
|
||||||
|
2026-04-25 14.44
|
||||||
|
2026-04-26 0
|
||||||
|
2026-04-27 16.8
|
||||||
|
2026-04-28 14.65
|
||||||
|
2026-04-29 5.98
|
||||||
|
2026-04-30 0
|
||||||
|
2026-05-01 25
|
||||||
|
2026-05-02 21.96
|
||||||
|
2026-05-03 20.29
|
||||||
|
2026-05-04 20.44
|
||||||
|
2026-05-05 34.15
|
||||||
|
2026-05-06 26.89
|
||||||
|
2026-05-07 31.13
|
||||||
|
2026-05-08 30.75
|
||||||
|
2026-05-09 33.63
|
||||||
|
2026-05-10 28.56
|
||||||
|
2026-05-11 17.14
|
||||||
|
2026-05-12 27.34
|
||||||
|
2026-05-13 29.52
|
||||||
|
2026-05-14 31.12
|
||||||
|
2026-05-15 29.83
|
||||||
|
2026-05-16 35.86 收银台+拉起优化
|
||||||
|
2026-05-17 26.96
|
||||||
|
2026-05-18 35.82
|
||||||
|
2026-05-19 36.79
|
||||||
|
2026-05-20 39.74
|
||||||
|
2026-05-21 35.04 服务器更新(风控/限制/收银台/超时)
|
||||||
|
2026-05-22 42.95
|
||||||
|
2026-05-23 39.76
|
||||||
|
2026-05-24 37.51
|
||||||
|
2026-05-25 46.22 mobikwik新端点修改上线
|
||||||
|
2026-05-26 41.54
|
||||||
|
2026-05-27 41.25 上线phone魔改包,下线paytm魔改
|
||||||
|
2026-05-28 43.56
|
||||||
|
2026-05-29 46.05
|
||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
WalletType,
|
WalletType,
|
||||||
PaytmBusinessBindResult,
|
PaytmBusinessBindResult,
|
||||||
PaytmPersonalBind,
|
PaytmPersonalBind,
|
||||||
MobikwikPersonalBindResult,
|
|
||||||
FreechargePersonalBindResult,
|
FreechargePersonalBindResult,
|
||||||
GooglePayBusinessBindResult,
|
GooglePayBusinessBindResult,
|
||||||
BharatPeBusinessBindResult,
|
BharatPeBusinessBindResult,
|
||||||
@@ -42,7 +41,8 @@ import {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
FreeChargeBind,
|
FreeChargeBind,
|
||||||
MobikwikOTPBind,
|
MobikwikPersonalTokenBind,
|
||||||
|
MobikwikPersonalOTPBind,
|
||||||
PayTmPersonalOTPBind,
|
PayTmPersonalOTPBind,
|
||||||
PhonePePersonalOTPBind,
|
PhonePePersonalOTPBind,
|
||||||
BharatPeBusinessOTPBind,
|
BharatPeBusinessOTPBind,
|
||||||
@@ -50,6 +50,7 @@ import {
|
|||||||
PhonePeBusinessOTPBind,
|
PhonePeBusinessOTPBind,
|
||||||
AmazonPayOTPBind,
|
AmazonPayOTPBind,
|
||||||
} from '../components/WalletBindComponents';
|
} from '../components/WalletBindComponents';
|
||||||
|
import { WalletSelectModal, WALLET_ICONS, WALLET_TYPE_COLORS } from '../components/WalletSelectModal';
|
||||||
|
|
||||||
import Api, {
|
import Api, {
|
||||||
WalletItem,
|
WalletItem,
|
||||||
@@ -58,113 +59,6 @@ import Api, {
|
|||||||
getServerDomain,
|
getServerDomain,
|
||||||
} from '../services/api';
|
} from '../services/api';
|
||||||
|
|
||||||
// key matches server WalletType string (see types.go)
|
|
||||||
const WALLET_ICONS: Record<string, any> = {
|
|
||||||
paytm: require('../res/paytm.png'),
|
|
||||||
'paytm business': require('../res/paytm-business.png'),
|
|
||||||
phonepe: require('../res/phonepe.webp'),
|
|
||||||
'phonepe business': require('../res/phonepe-business.webp'),
|
|
||||||
'googlepay business': require('../res/googlepay-business.webp'),
|
|
||||||
'bharatpe business': require('../res/bharatpe-business.webp'),
|
|
||||||
mobikwik: require('../res/mobikwik.png'),
|
|
||||||
freecharge: require('../res/freecharge.png'),
|
|
||||||
amazonpay: require('../res/amazon.png'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const WALLET_TYPE_COLORS: Record<string, string> = {
|
|
||||||
paytm: '#002970',
|
|
||||||
'paytm business': '#002970',
|
|
||||||
phonepe: '#5a2d9c',
|
|
||||||
'phonepe business': '#5a2d9c',
|
|
||||||
'googlepay business': '#4285f4',
|
|
||||||
'bharatpe business': '#e91e63',
|
|
||||||
mobikwik: '#00bcd4',
|
|
||||||
freecharge: '#ff5722',
|
|
||||||
amazonpay: '#ff9900',
|
|
||||||
};
|
|
||||||
|
|
||||||
// wallet type display info (walletType matches server)
|
|
||||||
const WALLET_TYPE_OPTIONS = [
|
|
||||||
{
|
|
||||||
key: 'paytm_personal_otp',
|
|
||||||
walletType: 'paytm',
|
|
||||||
label: 'Paytm Personal (OTP)',
|
|
||||||
mode: 'otp',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'paytm_personal_token',
|
|
||||||
walletType: 'paytm',
|
|
||||||
label: 'Paytm Personal (Token)',
|
|
||||||
mode: 'token',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'paytm_business',
|
|
||||||
walletType: 'paytm business',
|
|
||||||
label: 'Paytm Business (OTP)',
|
|
||||||
mode: 'otp',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'phonepe_personal_otp',
|
|
||||||
walletType: 'phonepe',
|
|
||||||
label: 'PhonePe Personal (OTP)',
|
|
||||||
mode: 'otp',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'phonepe_personal_token',
|
|
||||||
walletType: 'phonepe',
|
|
||||||
label: 'PhonePe Personal (Token)',
|
|
||||||
mode: 'token',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'phonepe_business_otp',
|
|
||||||
walletType: 'phonepe business',
|
|
||||||
label: 'PhonePe Business (OTP)',
|
|
||||||
mode: 'otp',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'phonepe_business_web',
|
|
||||||
walletType: 'phonepe business',
|
|
||||||
label: 'PhonePe Business (Web)',
|
|
||||||
mode: 'token',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'googlepay_business',
|
|
||||||
walletType: 'googlepay business',
|
|
||||||
label: 'GooglePay Business',
|
|
||||||
mode: 'token',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'bharatpe_business',
|
|
||||||
walletType: 'bharatpe business',
|
|
||||||
label: 'BharatPe Business (OTP)',
|
|
||||||
mode: 'otp',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'mobikwik_personal',
|
|
||||||
walletType: 'mobikwik',
|
|
||||||
label: 'Mobikwik Personal (OTP)',
|
|
||||||
mode: 'otp',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'freecharge_personal',
|
|
||||||
walletType: 'freecharge',
|
|
||||||
label: 'Freecharge Personal (OTP)',
|
|
||||||
mode: 'otp',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'freecharge_personal_token',
|
|
||||||
walletType: 'freecharge',
|
|
||||||
label: 'Freecharge Personal (Token)',
|
|
||||||
mode: 'token',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'amazonpay_personal',
|
|
||||||
walletType: 'amazonpay',
|
|
||||||
label: 'Amazon Pay (OTP)',
|
|
||||||
mode: 'otp',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function formatWalletTypeLabel(walletType: string) {
|
function formatWalletTypeLabel(walletType: string) {
|
||||||
return walletType.replace(/\b\w/g, (c) => c.toUpperCase());
|
return walletType.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||||
}
|
}
|
||||||
@@ -201,7 +95,7 @@ function getBindKeyForWallet(item: WalletItem): string | null {
|
|||||||
case 'bharatpe business':
|
case 'bharatpe business':
|
||||||
return 'bharatpe_business';
|
return 'bharatpe_business';
|
||||||
case 'mobikwik':
|
case 'mobikwik':
|
||||||
return 'mobikwik_personal';
|
return otp ? 'mobikwik_personal' : 'mobikwik_personal_token';
|
||||||
case 'freecharge':
|
case 'freecharge':
|
||||||
return otp ? 'freecharge_personal' : 'freecharge_personal_token';
|
return otp ? 'freecharge_personal' : 'freecharge_personal_token';
|
||||||
case 'amazonpay':
|
case 'amazonpay':
|
||||||
@@ -224,6 +118,7 @@ interface HomeScreenState {
|
|||||||
showGooglePayBusinessBind: boolean;
|
showGooglePayBusinessBind: boolean;
|
||||||
showBharatPeBusinessBind: boolean;
|
showBharatPeBusinessBind: boolean;
|
||||||
showMobikwikPersonalBind: boolean;
|
showMobikwikPersonalBind: boolean;
|
||||||
|
mobikwikPersonalBindType: 'otpMode' | 'tokenMode';
|
||||||
showFreechargePersonalBind: boolean;
|
showFreechargePersonalBind: boolean;
|
||||||
showAmazonPayPersonalBind: boolean;
|
showAmazonPayPersonalBind: boolean;
|
||||||
// proxy
|
// proxy
|
||||||
@@ -270,6 +165,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
showGooglePayBusinessBind: false,
|
showGooglePayBusinessBind: false,
|
||||||
showBharatPeBusinessBind: false,
|
showBharatPeBusinessBind: false,
|
||||||
showMobikwikPersonalBind: false,
|
showMobikwikPersonalBind: false,
|
||||||
|
mobikwikPersonalBindType: 'otpMode',
|
||||||
showFreechargePersonalBind: false,
|
showFreechargePersonalBind: false,
|
||||||
freechargePersonalBindType: 'otpMode',
|
freechargePersonalBindType: 'otpMode',
|
||||||
showAmazonPayPersonalBind: false,
|
showAmazonPayPersonalBind: false,
|
||||||
@@ -427,7 +323,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
|
|
||||||
// ---- bind handlers ----
|
// ---- bind handlers ----
|
||||||
|
|
||||||
/** Token 等需客户端 register 的流程(与 OTP 同 modal key 时勿用推断 map) */
|
/** Token bind: client calls register (do not infer modal key from wallet type alone) */
|
||||||
handleBindSuccess = (key: keyof HomeScreenState, walletType: WalletType, msg: string) =>
|
handleBindSuccess = (key: keyof HomeScreenState, walletType: WalletType, msg: string) =>
|
||||||
async (result: any) => {
|
async (result: any) => {
|
||||||
try {
|
try {
|
||||||
@@ -441,7 +337,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** OTP:服务端 verify 已注册,只提示并关弹窗 */
|
/** OTP bind: server registers on verify; only show success and close modal */
|
||||||
onOtpBindSuccess = (key: keyof HomeScreenState, msg: string) => () => {
|
onOtpBindSuccess = (key: keyof HomeScreenState, msg: string) => () => {
|
||||||
Alert.alert('Bind Success', msg);
|
Alert.alert('Bind Success', msg);
|
||||||
this.setState({ [key]: false } as any);
|
this.setState({ [key]: false } as any);
|
||||||
@@ -454,7 +350,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
const {
|
const {
|
||||||
showPaytmPersonalBind, paytmPersonalBindType, showPhonePePersonalBind, phonePePersonalBindType,
|
showPaytmPersonalBind, paytmPersonalBindType, showPhonePePersonalBind, phonePePersonalBindType,
|
||||||
showPaytmBusinessBind, showPhonePeBusinessBind, phonePeBusinessBindType, showGooglePayBusinessBind, showBharatPeBusinessBind,
|
showPaytmBusinessBind, showPhonePeBusinessBind, phonePeBusinessBindType, showGooglePayBusinessBind, showBharatPeBusinessBind,
|
||||||
showMobikwikPersonalBind, showFreechargePersonalBind, freechargePersonalBindType,
|
showMobikwikPersonalBind, mobikwikPersonalBindType, showFreechargePersonalBind, freechargePersonalBindType,
|
||||||
showAmazonPayPersonalBind,
|
showAmazonPayPersonalBind,
|
||||||
bindPrefillMobile,
|
bindPrefillMobile,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
@@ -463,7 +359,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
this.setState({ [key]: false, bindPrefillMobile: '' } as any);
|
this.setState({ [key]: false, bindPrefillMobile: '' } as any);
|
||||||
|
|
||||||
if (showPaytmPersonalBind && paytmPersonalBindType === 'tokenMode') {
|
if (showPaytmPersonalBind && paytmPersonalBindType === 'tokenMode') {
|
||||||
const remoteVersion = '1'; // 目前版本是 1
|
const remoteVersion = '1';
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
visible
|
visible
|
||||||
@@ -475,41 +371,37 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
isDebug
|
isDebug
|
||||||
onSuccess={(result: PaytmPersonalBindResult) => {
|
onSuccess={(result: PaytmPersonalBindResult) => {
|
||||||
if (result.chType != 'ipay') {
|
if (result.chType != 'ipay') {
|
||||||
// 有 AIDL 通信,能拿回数据,但是不是我们的魔改 type
|
Alert.alert('Bind Failed', 'Patched Paytm app not installed. Reinstall required');
|
||||||
// 视作未安装
|
|
||||||
Alert.alert('Bind Failed', '未安装Paytm 魔改包,需要重新安装');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result.chVersion != remoteVersion) {
|
if (result.chVersion != remoteVersion) {
|
||||||
// 版本有更新
|
Alert.alert('Bind Failed', 'App version outdated. Reinstall required');
|
||||||
Alert.alert('Bind Failed', '版本有更新,需要重新安装');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实际请求注册钱包
|
|
||||||
this.handleBindSuccess('showPaytmPersonalBind', WalletType.PAYTM_PERSONAL, 'Paytm Personal bound successfully')(result);
|
this.handleBindSuccess('showPaytmPersonalBind', WalletType.PAYTM_PERSONAL, 'Paytm Personal bound successfully')(result);
|
||||||
}}
|
}}
|
||||||
onError={(code: string, message: string) => {
|
onError={(code: string, message: string) => {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case BindErrorCode.NATIVE_MODULE_UNAVAILABLE: // 没有模块
|
case BindErrorCode.NATIVE_MODULE_UNAVAILABLE:
|
||||||
Alert.alert('Bind Failed', 'Native module not available');
|
Alert.alert('Bind Failed', 'Native module not available');
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.NOT_LOGGED_IN: // 未登录,提示用户登录
|
case BindErrorCode.NOT_LOGGED_IN:
|
||||||
Alert.alert('Bind Failed', 'Please login in paytm app first');
|
Alert.alert('Bind Failed', 'Please login in paytm app first');
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.ERROR: // catch 错误,提示错误即可
|
case BindErrorCode.ERROR:
|
||||||
Alert.alert('Bind Failed', message);
|
Alert.alert('Bind Failed', message);
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.NO_DATA: // 拿到了错误的信息,提示错误就可以 (aidl 给了错误的 resp,几率低),提示错误就可以
|
case BindErrorCode.NO_DATA:
|
||||||
Alert.alert('Bind Failed', 'No data received from Paytm');
|
Alert.alert('Bind Failed', 'No data received from Paytm');
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.SERVICE_DISCONNECTED: // 服务不可用 (可能aidl 通信失败,几率低)提示错误就可以
|
case BindErrorCode.SERVICE_DISCONNECTED:
|
||||||
Alert.alert('Bind Failed', 'Paytm service disconnected');
|
Alert.alert('Bind Failed', 'Paytm service disconnected');
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.NOT_INSTALLED: // 未安装,提示用户安装
|
case BindErrorCode.NOT_INSTALLED:
|
||||||
Alert.alert('Bind Failed', '未安装Paytm 魔改包,需要重新安装');
|
Alert.alert('Bind Failed', 'Patched Paytm app not installed. Reinstall required');
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.BIND_ERROR: // 绑定错误,aidl 通信失败,可能存在 paytm 未打开的情况,出现这个,手动拉起一下
|
case BindErrorCode.BIND_ERROR:
|
||||||
Alert.alert('Bind Failed', 'Paytm bind error');
|
Alert.alert('Bind Failed', 'Paytm bind error');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -545,7 +437,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (showPhonePePersonalBind && phonePePersonalBindType === 'tokenMode') {
|
if (showPhonePePersonalBind && phonePePersonalBindType === 'tokenMode') {
|
||||||
const remoteVersion = '1'; // 目前版本是 1
|
const remoteVersion = '1';
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
visible
|
visible
|
||||||
@@ -558,45 +450,36 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
isDebug
|
isDebug
|
||||||
onSuccess={(result: PhonePePersonalBindResult) => {
|
onSuccess={(result: PhonePePersonalBindResult) => {
|
||||||
if (result.chType != 'ipay') {
|
if (result.chType != 'ipay') {
|
||||||
// 有 AIDL 通信,能拿回数据,但是不是我们的魔改 type
|
Alert.alert('Bind Failed', 'Patched PhonePe app not installed. Reinstall required');
|
||||||
// 视作未安装
|
|
||||||
Alert.alert('Bind Failed', '未安装Phonepe 魔改包,需要重新安装');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result.chVersion != remoteVersion) {
|
if (result.chVersion != remoteVersion) {
|
||||||
// 版本有更新
|
Alert.alert('Bind Failed', 'App version outdated. Reinstall required');
|
||||||
Alert.alert('Bind Failed', '版本有更新,需要重新安装');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实际请求注册钱包
|
|
||||||
this.handleBindSuccess('showPhonePePersonalBind', WalletType.PHONEPE_PERSONAL, 'PhonePe Personal bound successfully')(result);
|
this.handleBindSuccess('showPhonePePersonalBind', WalletType.PHONEPE_PERSONAL, 'PhonePe Personal bound successfully')(result);
|
||||||
}}
|
}}
|
||||||
onError={(code: string, message: string) => {
|
onError={(code: string, message: string) => {
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case BindErrorCode.NOT_INSTALLED:
|
case BindErrorCode.NOT_INSTALLED:
|
||||||
// 未安装
|
Alert.alert('Bind Failed', 'Patched PhonePe app not installed. Reinstall required');
|
||||||
Alert.alert('Bind Failed', '未安装Phonepe 魔改包,需要重新安装');
|
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.SERVICE_DISCONNECTED:
|
case BindErrorCode.SERVICE_DISCONNECTED:
|
||||||
// 服务不可用 (可能aidl 通信失败,几率低)提示错误就可以
|
Alert.alert('Bind Failed', 'Service unavailable');
|
||||||
Alert.alert('Bind Failed', '服务不可用');
|
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.NO_DATA:
|
case BindErrorCode.NO_DATA:
|
||||||
// 拿到了错误的信息,提示错误就可以 (aidl 给了错误的 resp,几率低),提示错误就可以
|
Alert.alert('Bind Failed', 'Invalid response from PhonePe');
|
||||||
Alert.alert('Bind Failed', '未知错误信息');
|
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.BIND_ERROR:
|
case BindErrorCode.BIND_ERROR:
|
||||||
// 绑定错误,aidl 通信失败,可能存在 paytm / phonepe 未打开的情况,出现这个,手动拉起一下
|
Alert.alert('Bind Failed', 'Bind failed. Open PhonePe manually and try again');
|
||||||
Alert.alert('Bind Failed', '绑定失败,请手动打开PhonePe后重试');
|
|
||||||
break;
|
break;
|
||||||
case BindErrorCode.ERROR:
|
case BindErrorCode.ERROR:
|
||||||
// catch 错误,通用错误,提示错误即可
|
|
||||||
Alert.alert('Bind Failed', message);
|
Alert.alert('Bind Failed', message);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Alert.alert('Bind Failed', '未知错误');
|
Alert.alert('Bind Failed', 'Unknown error');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,6 +610,26 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (showMobikwikPersonalBind && mobikwikPersonalBindType === 'tokenMode') {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible
|
||||||
|
transparent
|
||||||
|
onRequestClose={close('showMobikwikPersonalBind')}
|
||||||
|
>
|
||||||
|
<MobikwikPersonalTokenBind
|
||||||
|
userToken={Api.instance.getUserToken()}
|
||||||
|
isDebug
|
||||||
|
onClose={close('showMobikwikPersonalBind')}
|
||||||
|
onSuccess={this.handleBindSuccess(
|
||||||
|
'showMobikwikPersonalBind',
|
||||||
|
WalletType.MOBIKWIK_PERSONAL,
|
||||||
|
'Mobikwik bound successfully',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
if (showMobikwikPersonalBind) {
|
if (showMobikwikPersonalBind) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -734,15 +637,15 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
transparent
|
transparent
|
||||||
onRequestClose={close('showMobikwikPersonalBind')}
|
onRequestClose={close('showMobikwikPersonalBind')}
|
||||||
>
|
>
|
||||||
<MobikwikOTPBind
|
<MobikwikPersonalOTPBind
|
||||||
isDebug={true}
|
isDebug
|
||||||
initialMobile={bindPrefillMobile}
|
initialMobile={bindPrefillMobile}
|
||||||
onRequestOTP={async (wt, p) => {
|
onRequestOTP={async (wt, p) =>
|
||||||
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile))
|
this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile))
|
||||||
}}
|
}
|
||||||
onVerifyOTP={async (wt, p) => {
|
onVerifyOTP={async (wt, p) =>
|
||||||
return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p));
|
this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p))
|
||||||
}}
|
}
|
||||||
onSuccess={this.onOtpBindSuccess('showMobikwikPersonalBind', 'Mobikwik bound successfully')}
|
onSuccess={this.onOtpBindSuccess('showMobikwikPersonalBind', 'Mobikwik bound successfully')}
|
||||||
onError={() => {}}
|
onError={() => {}}
|
||||||
/>
|
/>
|
||||||
@@ -850,7 +753,10 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
this.setState({ showBharatPeBusinessBind: true });
|
this.setState({ showBharatPeBusinessBind: true });
|
||||||
break;
|
break;
|
||||||
case 'mobikwik_personal':
|
case 'mobikwik_personal':
|
||||||
this.setState({ showMobikwikPersonalBind: true });
|
this.setState({ showMobikwikPersonalBind: true, mobikwikPersonalBindType: 'otpMode' });
|
||||||
|
break;
|
||||||
|
case 'mobikwik_personal_token':
|
||||||
|
this.setState({ showMobikwikPersonalBind: true, mobikwikPersonalBindType: 'tokenMode' });
|
||||||
break;
|
break;
|
||||||
case 'freecharge_personal':
|
case 'freecharge_personal':
|
||||||
this.setState({ showFreechargePersonalBind: true, freechargePersonalBindType: 'otpMode' });
|
this.setState({ showFreechargePersonalBind: true, freechargePersonalBindType: 'otpMode' });
|
||||||
@@ -874,51 +780,6 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAddWalletModal() {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
visible={this.state.showAddWallet}
|
|
||||||
transparent
|
|
||||||
animationType="none"
|
|
||||||
onRequestClose={() => this.setState({ showAddWallet: false })}
|
|
||||||
>
|
|
||||||
<View style={s.modalOverlay}>
|
|
||||||
<Animatable.View
|
|
||||||
animation="zoomIn"
|
|
||||||
duration={220}
|
|
||||||
easing="ease-out-back"
|
|
||||||
useNativeDriver
|
|
||||||
style={s.addModalBox}
|
|
||||||
>
|
|
||||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
|
||||||
<Text style={s.addModalTitle}>Select Wallet Type</Text>
|
|
||||||
<TouchableOpacity onPress={() => this.setState({ showAddWallet: false })}>
|
|
||||||
<Text style={{ fontSize: 20, color: '#999' }}>✕</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
<ScrollView bounces={false}>
|
|
||||||
{WALLET_TYPE_OPTIONS.map((opt) => (
|
|
||||||
<TouchableOpacity
|
|
||||||
key={opt.key}
|
|
||||||
style={s.walletTypeRow}
|
|
||||||
onPress={() => this.openWalletBind(opt.key)}
|
|
||||||
activeOpacity={0.7}
|
|
||||||
>
|
|
||||||
{WALLET_ICONS[opt.walletType] ? (
|
|
||||||
<Image source={WALLET_ICONS[opt.walletType]} style={s.walletTypeIcon} resizeMode="contain" />
|
|
||||||
) : (
|
|
||||||
<View style={[s.walletTypeDot, { backgroundColor: WALLET_TYPE_COLORS[opt.walletType] ?? '#888' }]} />
|
|
||||||
)}
|
|
||||||
<Text style={s.walletTypeLabel}>{opt.label}</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
</ScrollView>
|
|
||||||
</Animatable.View>
|
|
||||||
</View>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderServerSettingsModal() {
|
renderServerSettingsModal() {
|
||||||
const { showServerSettings, settingsHost, settingsPort } = this.state;
|
const { showServerSettings, settingsHost, settingsPort } = this.state;
|
||||||
const presets = [
|
const presets = [
|
||||||
@@ -1214,7 +1075,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
|||||||
|
|
||||||
{this.renderBindModal()}
|
{this.renderBindModal()}
|
||||||
{this.renderServerSettingsModal()}
|
{this.renderServerSettingsModal()}
|
||||||
{this.renderAddWalletModal()}
|
<WalletSelectModal
|
||||||
|
visible={this.state.showAddWallet}
|
||||||
|
onClose={() => this.setState({ showAddWallet: false })}
|
||||||
|
onSelectBind={this.openWalletBind}
|
||||||
|
/>
|
||||||
{this.renderVpaModal()}
|
{this.renderVpaModal()}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@@ -1420,29 +1285,6 @@ const s = StyleSheet.create({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
addModalBox: {
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
borderRadius: 12,
|
|
||||||
padding: 20,
|
|
||||||
width: '88%',
|
|
||||||
maxHeight: '70%',
|
|
||||||
},
|
|
||||||
addModalTitle: {
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: '700',
|
|
||||||
color: '#222',
|
|
||||||
},
|
|
||||||
walletTypeRow: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingVertical: 12,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: '#f0f0f0',
|
|
||||||
},
|
|
||||||
walletTypeLabel: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#333',
|
|
||||||
},
|
|
||||||
boundWalletGroup: {
|
boundWalletGroup: {
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
},
|
},
|
||||||
@@ -1476,18 +1318,6 @@ const s = StyleSheet.create({
|
|||||||
width: 16,
|
width: 16,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
},
|
},
|
||||||
walletTypeIcon: {
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
borderRadius: 6,
|
|
||||||
marginRight: 12,
|
|
||||||
},
|
|
||||||
walletTypeDot: {
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
borderRadius: 5,
|
|
||||||
marginRight: 12,
|
|
||||||
},
|
|
||||||
settingsBox: {
|
settingsBox: {
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
|
|||||||
Reference in New Issue
Block a user