Files
rnpay/components/WalletBindComponents.tsx
2026-03-11 22:48:39 +08:00

251 lines
11 KiB
TypeScript

import React, { Component, useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
import { WalletType, FreechargePersonalBindResult, MobikwikPersonalBindResult, PaytmPersonalBindResult, PhonePePersonalBindResult, BharatPeBusinessBindResult, PaytmBusinessBindResult } from 'rnwalletman';
import { OTPBindUI } from './OTPBindUI';
export class FreeChargeBind extends Component<{
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
onSuccess: (result: FreechargePersonalBindResult) => void;
onError: (error: string) => void;
isDebug: boolean;
}> {
render() {
return (
<OTPBindUI
walletType={WalletType.FREECHARGE_PERSONAL}
title="Freecharge 绑定"
otpLength={4}
onRequestOTP={this.props.onRequestOTP}
onVerifyOTP={this.props.onVerifyOTP}
onSuccess={this.props.onSuccess}
onError={this.props.onError}
isDebug={this.props.isDebug}
/>
);
}
}
export class MobikwikOTPBind extends Component<{
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
onSuccess: (result: MobikwikPersonalBindResult) => void;
onError: (error: string) => void;
isDebug: boolean;
deviceId: string;
tuneUserId: string;
}> {
render() {
return (
<OTPBindUI
walletType={WalletType.MOBIKWIK_PERSONAL}
title="Mobikwik 绑定"
otpLength={6}
onRequestOTP={this.props.onRequestOTP}
onVerifyOTP={this.props.onVerifyOTP}
onSuccess={this.props.onSuccess}
onError={this.props.onError}
isDebug={this.props.isDebug}
additionalParams={{
deviceId: this.props.deviceId,
tuneUserId: this.props.tuneUserId,
}}
/>
);
}
}
export class PayTmPersonalOTPBind extends Component<{
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
onSuccess: (result: PaytmPersonalBindResult) => void;
onError: (error: string) => void;
isDebug: boolean;
}> {
render() {
return (
<OTPBindUI
walletType={WalletType.PAYTM_PERSONAL}
title="Paytm 绑定"
otpLength={6}
onRequestOTP={this.props.onRequestOTP}
onVerifyOTP={this.props.onVerifyOTP}
onSuccess={this.props.onSuccess}
onError={this.props.onError}
isDebug={this.props.isDebug}
/>
);
}
}
export class BharatPeBusinessOTPBind extends Component<{
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
onSuccess: (result: BharatPeBusinessBindResult) => void;
onError: (error: string) => void;
isDebug: boolean;
}> {
render() {
return (
<OTPBindUI
walletType={WalletType.BHARATPE_BUSINESS}
title="BharatPe 绑定"
otpLength={4}
onRequestOTP={this.props.onRequestOTP}
onVerifyOTP={this.props.onVerifyOTP}
onSuccess={this.props.onSuccess}
onError={this.props.onError}
isDebug={this.props.isDebug}
/>
);
}
}
export class PaytmBusinessOTPBind extends Component<{
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
onSuccess: (result: PaytmBusinessBindResult) => void;
onError: (error: string) => void;
isDebug: boolean;
}> {
render() {
return (
<PaytmBusinessForm
onRequestOTP={this.props.onRequestOTP}
onVerifyOTP={this.props.onVerifyOTP}
onSuccess={this.props.onSuccess}
onError={this.props.onError}
isDebug={this.props.isDebug}
/>
);
}
}
function PaytmBusinessForm({ onRequestOTP, onVerifyOTP, onSuccess, onError, isDebug }: {
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
onSuccess: (result: PaytmBusinessBindResult) => void;
onError: (error: string) => void;
isDebug: boolean;
}) {
const [step, setStep] = useState<'credentials' | 'otp' | 'processing'>('credentials');
const [mobile, setMobile] = useState('');
const [password, setPassword] = useState('');
const [otp, setOtp] = useState('');
const [sessionId, setSessionId] = useState('');
const [loading, setLoading] = useState(false);
const [errorMsg, setErrorMsg] = useState('');
const log = (...args: any[]) => { if (isDebug) console.log('[PaytmBusiness]', ...args); };
const handleRequestOTP = async () => {
if (!mobile || mobile.length !== 10) { setErrorMsg('请输入10位手机号'); return; }
if (!password) { setErrorMsg('请输入密码'); return; }
setLoading(true); setErrorMsg('');
try {
const res = await onRequestOTP(WalletType.PAYTM_BUSINESS, { mobile, password });
log('RequestOTP:', res);
if (res.success) { setSessionId(res.data?.sessionId || ''); setStep('otp'); }
else { const msg = res.message || 'Failed to send OTP'; setErrorMsg(msg); onError(msg); }
} catch (e) {
const msg = e instanceof Error ? e.message : 'Failed to send OTP';
setErrorMsg(msg); onError(msg);
} finally { setLoading(false); }
};
const handleVerifyOTP = async () => {
if (!otp || otp.length !== 6) { setErrorMsg('请输入6位验证码'); return; }
setLoading(true); setErrorMsg(''); setStep('processing');
try {
const res = await onVerifyOTP(WalletType.PAYTM_BUSINESS, { mobile, otp, sessionId });
log('VerifyOTP:', res);
if (res.success) {
onSuccess({ type: WalletType.PAYTM_BUSINESS, success: true, cookie: res.data?.cookie || '', xCsrfToken: res.data?.xCsrfToken || '', qrData: res.data?.qrData || [] });
} else {
const msg = res.message || 'Failed to verify OTP';
setErrorMsg(msg); setStep('otp'); onError(msg);
}
} catch (e) {
const msg = e instanceof Error ? e.message : 'Failed to verify OTP';
setErrorMsg(msg); setStep('otp'); onError(msg);
} finally { setLoading(false); }
};
if (step === 'processing') {
return (
<View style={ptStyles.container}>
<ActivityIndicator size="large" color="#fff" />
<Text style={ptStyles.processingText}>...</Text>
</View>
);
}
return (
<View style={ptStyles.container}>
<View style={ptStyles.form}>
<Text style={ptStyles.title}>Paytm Business </Text>
{errorMsg ? <Text style={ptStyles.errorText}>{errorMsg}</Text> : null}
{step === 'credentials' && (
<>
<TextInput style={ptStyles.input} placeholder="手机号" placeholderTextColor="#999" keyboardType="phone-pad" maxLength={10} value={mobile} onChangeText={t => { setMobile(t); setErrorMsg(''); }} editable={!loading} />
<TextInput style={ptStyles.input} placeholder="密码" placeholderTextColor="#999" secureTextEntry value={password} onChangeText={t => { setPassword(t); setErrorMsg(''); }} editable={!loading} />
<TouchableOpacity style={[ptStyles.button, loading && ptStyles.buttonDisabled]} onPress={handleRequestOTP} disabled={loading}>
{loading ? <ActivityIndicator color="#fff" /> : <Text style={ptStyles.buttonText}></Text>}
</TouchableOpacity>
</>
)}
{step === 'otp' && (
<>
<Text style={ptStyles.hint}> {mobile}</Text>
<TextInput style={ptStyles.input} placeholder="6位验证码" placeholderTextColor="#999" keyboardType="number-pad" maxLength={6} value={otp} onChangeText={t => { setOtp(t); setErrorMsg(''); }} editable={!loading} />
<TouchableOpacity style={[ptStyles.button, loading && ptStyles.buttonDisabled]} onPress={handleVerifyOTP} disabled={loading}>
{loading ? <ActivityIndicator color="#fff" /> : <Text style={ptStyles.buttonText}></Text>}
</TouchableOpacity>
<TouchableOpacity style={ptStyles.linkButton} onPress={() => setStep('credentials')} disabled={loading}>
<Text style={ptStyles.linkText}></Text>
</TouchableOpacity>
</>
)}
</View>
</View>
);
}
const ptStyles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'rgba(0,0,0,0.8)', justifyContent: 'center', alignItems: 'center' },
form: { width: '80%', backgroundColor: '#fff', borderRadius: 10, padding: 20 },
title: { fontSize: 20, fontWeight: 'bold', textAlign: 'center', marginBottom: 20 },
input: { borderWidth: 1, borderColor: '#ddd', borderRadius: 5, padding: 12, fontSize: 16, marginBottom: 15 },
errorText: { color: '#ff3b30', fontSize: 14, marginBottom: 10, textAlign: 'center' },
button: { backgroundColor: '#007AFF', borderRadius: 5, padding: 15, alignItems: 'center' },
buttonDisabled: { backgroundColor: '#ccc' },
buttonText: { color: '#fff', fontSize: 16, fontWeight: 'bold' },
linkButton: { marginTop: 10, alignItems: 'center' },
linkText: { color: '#007AFF', fontSize: 14 },
hint: { fontSize: 14, color: '#666', marginBottom: 10, textAlign: 'center' },
processingText: { color: '#fff', fontSize: 16, marginTop: 10 },
});
export class PhonePePersonalOTPBind extends Component<{
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
onSuccess: (result: PhonePePersonalBindResult) => void;
onError: (error: string) => void;
isDebug: boolean;
}> {
render() {
return (
<OTPBindUI
walletType={WalletType.PHONEPE_PERSONAL}
title="PhonePe 绑定"
otpLength={5}
onRequestOTP={this.props.onRequestOTP}
onVerifyOTP={this.props.onVerifyOTP}
onSuccess={this.props.onSuccess}
onError={this.props.onError}
isDebug={this.props.isDebug}
/>
);
}
}