From 5c84a4bb890933eaed130ef1fbed900de13a864f Mon Sep 17 00:00:00 2001 From: TQCasey <494294315@qq.com> Date: Fri, 29 May 2026 21:32:14 +0800 Subject: [PATCH] =?UTF-8?q?Otp=20/=20Token=20=E6=A8=A1=E5=BC=8F=20OK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/OTPBindUI.tsx | 12 ++ components/WalletBindComponents.tsx | 79 +++++++- components/WalletSelectModal.tsx | 255 +++++++++++++++++++++++ docs/成功率分析.log | 42 ++++ screens/HomeScreen.tsx | 300 ++++++---------------------- 5 files changed, 448 insertions(+), 240 deletions(-) create mode 100644 components/WalletSelectModal.tsx create mode 100644 docs/成功率分析.log diff --git a/components/OTPBindUI.tsx b/components/OTPBindUI.tsx index 013c4e5..26f294f 100644 --- a/components/OTPBindUI.tsx +++ b/components/OTPBindUI.tsx @@ -13,6 +13,7 @@ import { useOTPBind } from '../hooks/useOTPBind'; interface OTPBindUIProps { walletType: WalletType; title: string; + subtitle?: string; otpLength?: number; mobileLength?: number; passwordBeforeOtp?: boolean; @@ -32,6 +33,7 @@ interface OTPBindUIProps { export const OTPBindUI: React.FC = ({ walletType, title, + subtitle, otpLength = 6, mobileLength = 10, passwordBeforeOtp = false, @@ -78,6 +80,7 @@ export const OTPBindUI: React.FC = ({ {title} + {!!subtitle && {subtitle}} {!!state.errorMessage && ( {state.errorMessage} )} @@ -182,6 +185,7 @@ export const OTPBindUI: React.FC = ({ {title} + {!!subtitle && {subtitle}} {!showOtp && ( <> @@ -287,6 +291,14 @@ const styles = StyleSheet.create({ color: '#111', marginBottom: 18, }, + subtitle: { + fontSize: 12, + color: '#888', + textAlign: 'center', + marginTop: -10, + marginBottom: 14, + lineHeight: 17, + }, label: { fontSize: 13, color: '#666', diff --git a/components/WalletBindComponents.tsx b/components/WalletBindComponents.tsx index c597043..c516dc0 100644 --- a/components/WalletBindComponents.tsx +++ b/components/WalletBindComponents.tsx @@ -1,8 +1,45 @@ import React, { Component, useState, useEffect } from 'react'; -import { View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native'; -import { WalletType, FreechargePersonalBindResult, MobikwikPersonalBindResult, PaytmPersonalBindResult, PhonePePersonalBindResult, PhonePeBusinessBindResult, BharatPeBusinessBindResult, PaytmBusinessBindResult } from 'rnwalletman'; +import { Alert, View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native'; +import { + WalletType, + BindErrorCode, + MobikwikPersonalBind, + FreechargePersonalBindResult, + MobikwikPersonalBindResult, + PaytmPersonalBindResult, + PhonePePersonalBindResult, + PhonePeBusinessBindResult, + BharatPeBusinessBindResult, + PaytmBusinessBindResult, +} from 'rnwalletman'; 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<{ onRequestOTP: (walletType: WalletType, params: any) => Promise; onVerifyOTP: (walletType: WalletType, params: any) => Promise; @@ -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 ( + { + 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; onVerifyOTP: (walletType: WalletType, params: any) => Promise; - onSuccess: (result: MobikwikPersonalBindResult) => void; + onSuccess: (result: any) => void; onError: (error: string) => void; isDebug: boolean; initialMobile?: string; @@ -40,7 +105,8 @@ export class MobikwikOTPBind extends Component<{ return ( Promise; onVerifyOTP: (walletType: WalletType, params: any) => Promise; diff --git a/components/WalletSelectModal.tsx b/components/WalletSelectModal.tsx new file mode 100644 index 0000000..95ee4f1 --- /dev/null +++ b/components/WalletSelectModal.tsx @@ -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 = { + 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 = { + 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 = ({ + visible, + onClose, + onSelectBind, + rows = WALLET_SELECT_ROWS, +}) => ( + + + + + Select Wallet Type + + + + + + {rows.map((row) => ( + + {WALLET_ICONS[row.walletType] ? ( + + ) : ( + + )} + + {row.label} + + + {row.actions.map((action) => ( + onSelectBind(action.key)} + activeOpacity={0.7} + > + {action.label} + + ))} + + + ))} + + + + +); + +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', + }, +}); diff --git a/docs/成功率分析.log b/docs/成功率分析.log new file mode 100644 index 0000000..a74f2a9 --- /dev/null +++ b/docs/成功率分析.log @@ -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 diff --git a/screens/HomeScreen.tsx b/screens/HomeScreen.tsx index 453bcda..d894ead 100644 --- a/screens/HomeScreen.tsx +++ b/screens/HomeScreen.tsx @@ -22,7 +22,6 @@ import { WalletType, PaytmBusinessBindResult, PaytmPersonalBind, - MobikwikPersonalBindResult, FreechargePersonalBindResult, GooglePayBusinessBindResult, BharatPeBusinessBindResult, @@ -42,7 +41,8 @@ import { import { FreeChargeBind, - MobikwikOTPBind, + MobikwikPersonalTokenBind, + MobikwikPersonalOTPBind, PayTmPersonalOTPBind, PhonePePersonalOTPBind, BharatPeBusinessOTPBind, @@ -50,6 +50,7 @@ import { PhonePeBusinessOTPBind, AmazonPayOTPBind, } from '../components/WalletBindComponents'; +import { WalletSelectModal, WALLET_ICONS, WALLET_TYPE_COLORS } from '../components/WalletSelectModal'; import Api, { WalletItem, @@ -58,113 +59,6 @@ import Api, { getServerDomain, } from '../services/api'; -// key matches server WalletType string (see types.go) -const WALLET_ICONS: Record = { - 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 = { - 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) { return walletType.replace(/\b\w/g, (c) => c.toUpperCase()); } @@ -201,7 +95,7 @@ function getBindKeyForWallet(item: WalletItem): string | null { case 'bharatpe business': return 'bharatpe_business'; case 'mobikwik': - return 'mobikwik_personal'; + return otp ? 'mobikwik_personal' : 'mobikwik_personal_token'; case 'freecharge': return otp ? 'freecharge_personal' : 'freecharge_personal_token'; case 'amazonpay': @@ -224,6 +118,7 @@ interface HomeScreenState { showGooglePayBusinessBind: boolean; showBharatPeBusinessBind: boolean; showMobikwikPersonalBind: boolean; + mobikwikPersonalBindType: 'otpMode' | 'tokenMode'; showFreechargePersonalBind: boolean; showAmazonPayPersonalBind: boolean; // proxy @@ -270,6 +165,7 @@ export default class HomeScreen extends Component { showGooglePayBusinessBind: false, showBharatPeBusinessBind: false, showMobikwikPersonalBind: false, + mobikwikPersonalBindType: 'otpMode', showFreechargePersonalBind: false, freechargePersonalBindType: 'otpMode', showAmazonPayPersonalBind: false, @@ -427,7 +323,7 @@ export default class HomeScreen extends Component { // ---- 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) => async (result: any) => { try { @@ -441,7 +337,7 @@ export default class HomeScreen extends Component { } }; - /** OTP:服务端 verify 已注册,只提示并关弹窗 */ + /** OTP bind: server registers on verify; only show success and close modal */ onOtpBindSuccess = (key: keyof HomeScreenState, msg: string) => () => { Alert.alert('Bind Success', msg); this.setState({ [key]: false } as any); @@ -454,7 +350,7 @@ export default class HomeScreen extends Component { const { showPaytmPersonalBind, paytmPersonalBindType, showPhonePePersonalBind, phonePePersonalBindType, showPaytmBusinessBind, showPhonePeBusinessBind, phonePeBusinessBindType, showGooglePayBusinessBind, showBharatPeBusinessBind, - showMobikwikPersonalBind, showFreechargePersonalBind, freechargePersonalBindType, + showMobikwikPersonalBind, mobikwikPersonalBindType, showFreechargePersonalBind, freechargePersonalBindType, showAmazonPayPersonalBind, bindPrefillMobile, } = this.state; @@ -463,7 +359,7 @@ export default class HomeScreen extends Component { this.setState({ [key]: false, bindPrefillMobile: '' } as any); if (showPaytmPersonalBind && paytmPersonalBindType === 'tokenMode') { - const remoteVersion = '1'; // 目前版本是 1 + const remoteVersion = '1'; return ( { isDebug onSuccess={(result: PaytmPersonalBindResult) => { if (result.chType != 'ipay') { - // 有 AIDL 通信,能拿回数据,但是不是我们的魔改 type - // 视作未安装 - Alert.alert('Bind Failed', '未安装Paytm 魔改包,需要重新安装'); + Alert.alert('Bind Failed', 'Patched Paytm app not installed. Reinstall required'); return; } if (result.chVersion != remoteVersion) { - // 版本有更新 - Alert.alert('Bind Failed', '版本有更新,需要重新安装'); + Alert.alert('Bind Failed', 'App version outdated. Reinstall required'); return; } - // 实际请求注册钱包 this.handleBindSuccess('showPaytmPersonalBind', WalletType.PAYTM_PERSONAL, 'Paytm Personal bound successfully')(result); }} onError={(code: string, message: string) => { switch (code) { - case BindErrorCode.NATIVE_MODULE_UNAVAILABLE: // 没有模块 + case BindErrorCode.NATIVE_MODULE_UNAVAILABLE: Alert.alert('Bind Failed', 'Native module not available'); break; - case BindErrorCode.NOT_LOGGED_IN: // 未登录,提示用户登录 + case BindErrorCode.NOT_LOGGED_IN: Alert.alert('Bind Failed', 'Please login in paytm app first'); break; - case BindErrorCode.ERROR: // catch 错误,提示错误即可 + case BindErrorCode.ERROR: Alert.alert('Bind Failed', message); break; - case BindErrorCode.NO_DATA: // 拿到了错误的信息,提示错误就可以 (aidl 给了错误的 resp,几率低),提示错误就可以 + case BindErrorCode.NO_DATA: Alert.alert('Bind Failed', 'No data received from Paytm'); break; - case BindErrorCode.SERVICE_DISCONNECTED: // 服务不可用 (可能aidl 通信失败,几率低)提示错误就可以 + case BindErrorCode.SERVICE_DISCONNECTED: Alert.alert('Bind Failed', 'Paytm service disconnected'); break; - case BindErrorCode.NOT_INSTALLED: // 未安装,提示用户安装 - Alert.alert('Bind Failed', '未安装Paytm 魔改包,需要重新安装'); + case BindErrorCode.NOT_INSTALLED: + Alert.alert('Bind Failed', 'Patched Paytm app not installed. Reinstall required'); break; - case BindErrorCode.BIND_ERROR: // 绑定错误,aidl 通信失败,可能存在 paytm 未打开的情况,出现这个,手动拉起一下 + case BindErrorCode.BIND_ERROR: Alert.alert('Bind Failed', 'Paytm bind error'); break; default: @@ -545,7 +437,7 @@ export default class HomeScreen extends Component { ); } if (showPhonePePersonalBind && phonePePersonalBindType === 'tokenMode') { - const remoteVersion = '1'; // 目前版本是 1 + const remoteVersion = '1'; return ( { isDebug onSuccess={(result: PhonePePersonalBindResult) => { if (result.chType != 'ipay') { - // 有 AIDL 通信,能拿回数据,但是不是我们的魔改 type - // 视作未安装 - Alert.alert('Bind Failed', '未安装Phonepe 魔改包,需要重新安装'); + Alert.alert('Bind Failed', 'Patched PhonePe app not installed. Reinstall required'); return; } if (result.chVersion != remoteVersion) { - // 版本有更新 - Alert.alert('Bind Failed', '版本有更新,需要重新安装'); + Alert.alert('Bind Failed', 'App version outdated. Reinstall required'); return; } - // 实际请求注册钱包 this.handleBindSuccess('showPhonePePersonalBind', WalletType.PHONEPE_PERSONAL, 'PhonePe Personal bound successfully')(result); }} onError={(code: string, message: string) => { switch (code) { case BindErrorCode.NOT_INSTALLED: - // 未安装 - Alert.alert('Bind Failed', '未安装Phonepe 魔改包,需要重新安装'); + Alert.alert('Bind Failed', 'Patched PhonePe app not installed. Reinstall required'); break; case BindErrorCode.SERVICE_DISCONNECTED: - // 服务不可用 (可能aidl 通信失败,几率低)提示错误就可以 - Alert.alert('Bind Failed', '服务不可用'); + Alert.alert('Bind Failed', 'Service unavailable'); break; case BindErrorCode.NO_DATA: - // 拿到了错误的信息,提示错误就可以 (aidl 给了错误的 resp,几率低),提示错误就可以 - Alert.alert('Bind Failed', '未知错误信息'); + Alert.alert('Bind Failed', 'Invalid response from PhonePe'); break; case BindErrorCode.BIND_ERROR: - // 绑定错误,aidl 通信失败,可能存在 paytm / phonepe 未打开的情况,出现这个,手动拉起一下 - Alert.alert('Bind Failed', '绑定失败,请手动打开PhonePe后重试'); + Alert.alert('Bind Failed', 'Bind failed. Open PhonePe manually and try again'); break; case BindErrorCode.ERROR: - // catch 错误,通用错误,提示错误即可 Alert.alert('Bind Failed', message); break; default: - Alert.alert('Bind Failed', '未知错误'); + Alert.alert('Bind Failed', 'Unknown error'); break; } @@ -727,6 +610,26 @@ export default class HomeScreen extends Component { ); } + if (showMobikwikPersonalBind && mobikwikPersonalBindType === 'tokenMode') { + return ( + + + + ); + } if (showMobikwikPersonalBind) { return ( { transparent onRequestClose={close('showMobikwikPersonalBind')} > - { - return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile)) - }} - onVerifyOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p)); - }} + onRequestOTP={async (wt, p) => + this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile)) + } + onVerifyOTP={async (wt, p) => + this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p)) + } onSuccess={this.onOtpBindSuccess('showMobikwikPersonalBind', 'Mobikwik bound successfully')} onError={() => {}} /> @@ -850,7 +753,10 @@ export default class HomeScreen extends Component { this.setState({ showBharatPeBusinessBind: true }); break; 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; case 'freecharge_personal': this.setState({ showFreechargePersonalBind: true, freechargePersonalBindType: 'otpMode' }); @@ -874,51 +780,6 @@ export default class HomeScreen extends Component { })); }; - renderAddWalletModal() { - return ( - this.setState({ showAddWallet: false })} - > - - - - Select Wallet Type - this.setState({ showAddWallet: false })}> - - - - - {WALLET_TYPE_OPTIONS.map((opt) => ( - this.openWalletBind(opt.key)} - activeOpacity={0.7} - > - {WALLET_ICONS[opt.walletType] ? ( - - ) : ( - - )} - {opt.label} - - ))} - - - - - ); - } - renderServerSettingsModal() { const { showServerSettings, settingsHost, settingsPort } = this.state; const presets = [ @@ -1214,7 +1075,11 @@ export default class HomeScreen extends Component { {this.renderBindModal()} {this.renderServerSettingsModal()} - {this.renderAddWalletModal()} + this.setState({ showAddWallet: false })} + onSelectBind={this.openWalletBind} + /> {this.renderVpaModal()} ); @@ -1420,29 +1285,6 @@ const s = StyleSheet.create({ justifyContent: '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: { marginBottom: 4, }, @@ -1476,18 +1318,6 @@ const s = StyleSheet.create({ width: 16, textAlign: 'right', }, - walletTypeIcon: { - width: 32, - height: 32, - borderRadius: 6, - marginRight: 12, - }, - walletTypeDot: { - width: 10, - height: 10, - borderRadius: 5, - marginRight: 12, - }, settingsBox: { backgroundColor: '#fff', borderRadius: 10,