This commit is contained in:
2026-05-28 03:06:19 +08:00
parent d7e16ee9c4
commit 6eaa171ba4
8 changed files with 76 additions and 9 deletions

View File

@@ -87,10 +87,33 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
{showOtp && ( {showOtp && (
<> <>
<Text style={styles.hint}> {state.mobile}</Text> <Text style={styles.hint}>
{state.needPassword
? `验证码已发送至 ${state.mobile},该账号需输入密码`
: `验证码已发送至 ${state.mobile}`}
</Text>
{!!state.errorMessage && ( {!!state.errorMessage && (
<Text style={styles.errorText}>{state.errorMessage}</Text> <Text style={styles.errorText}>{state.errorMessage}</Text>
)} )}
{state.needPassword && (
<>
<Text style={styles.label}>Amazon </Text>
<TextInput
style={[styles.input, !!state.errorMessage && styles.inputError]}
placeholder="请输入账号密码"
placeholderTextColor="#aaa"
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
value={state.password}
onChangeText={t => {
actions.setPassword(t);
if (state.errorMessage) actions.clearError();
}}
editable={!isLoading}
/>
</>
)}
<Text style={styles.label}></Text> <Text style={styles.label}></Text>
<TextInput <TextInput
style={[styles.input, !!state.errorMessage && styles.inputError]} style={[styles.input, !!state.errorMessage && styles.inputError]}

20
docs/流程图.mmd Normal file
View File

@@ -0,0 +1,20 @@
graph TD
A[开始: 订单状态未支付] --> B{最后一次查单结果<br/>是否成功?}
B -- 失败 --> C[标记: 接口异常/待重试]
B -- 成功 --> D{系统内是否有<br/>匹配记录?}
D -- 有记录 --> E[按正常流程处理]
D -- 无记录 --> F[查询当天整天流水]
F --> G{当天是否有<br/>其它成功流水?}
G -- 有流水 --> H[结论: 没付<br/>原因: 流水正常/该单未付]
G -- 无流水 --> I[查询前1-2天流水]
I --> J{前1-2天是否有<br/>成功流水?}
J -- 有流水 --> H
J -- 无流水 --> K[结论: 判定为异常或死卡<br/>原因: 持续无数据,需检查通道]
%% 样式定义
style H fill:#f96,stroke:#333,stroke-width:2px
style K fill:#ff9,stroke:#333,stroke-width:2px
style C fill:#ccc,stroke:#333

BIN
docs/流程图.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -12,6 +12,8 @@ export interface OTPBindCallbacks {
export interface OTPBindState { export interface OTPBindState {
mobile: string; mobile: string;
otp: string; otp: string;
password: string;
needPassword: boolean;
step: 'mobile' | 'otp' | 'processing'; step: 'mobile' | 'otp' | 'processing';
loading: boolean; loading: boolean;
otpData: any; otpData: any;
@@ -21,6 +23,7 @@ export interface OTPBindState {
export interface OTPBindActions { export interface OTPBindActions {
setMobile: (mobile: string) => void; setMobile: (mobile: string) => void;
setOtp: (otp: string) => void; setOtp: (otp: string) => void;
setPassword: (password: string) => void;
requestOTP: () => Promise<void>; requestOTP: () => Promise<void>;
verifyOTP: () => Promise<void>; verifyOTP: () => Promise<void>;
resetToMobile: () => void; resetToMobile: () => void;
@@ -39,6 +42,8 @@ export function useOTPBind(
): [OTPBindState, OTPBindActions] { ): [OTPBindState, OTPBindActions] {
const [mobile, setMobile] = useState(''); const [mobile, setMobile] = useState('');
const [otp, setOtp] = useState(''); const [otp, setOtp] = useState('');
const [password, setPassword] = useState('');
const [needPassword, setNeedPassword] = useState(false);
const [step, setStep] = useState<'mobile' | 'otp' | 'processing'>('mobile'); const [step, setStep] = useState<'mobile' | 'otp' | 'processing'>('mobile');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [otpData, setOtpData] = useState<any>(null); const [otpData, setOtpData] = useState<any>(null);
@@ -82,6 +87,7 @@ export function useOTPBind(
if (response.success) { if (response.success) {
setOtpData(response.data); setOtpData(response.data);
setNeedPassword(!!response.data?.needPassword);
setStep('otp'); setStep('otp');
setErrorMessage(''); setErrorMessage('');
} else { } else {
@@ -107,6 +113,12 @@ export function useOTPBind(
onError(msg); onError(msg);
return; return;
} }
if (needPassword && !password.trim()) {
const msg = '请输入 Amazon 账号密码';
setErrorMessage(msg);
onError(msg);
return;
}
setLoading(true); setLoading(true);
setErrorMessage(''); setErrorMessage('');
@@ -117,6 +129,7 @@ export function useOTPBind(
const response = await onVerifyOTP(walletType, { const response = await onVerifyOTP(walletType, {
mobile, mobile,
otp, otp,
password: needPassword ? password : undefined,
...additionalParams, ...additionalParams,
...(otpData || {}), ...(otpData || {}),
}); });
@@ -144,11 +157,13 @@ export function useOTPBind(
const resetToMobile = () => { const resetToMobile = () => {
setStep('mobile'); setStep('mobile');
setOtp(''); setOtp('');
setPassword('');
setNeedPassword(false);
setErrorMessage(''); setErrorMessage('');
}; };
return [ return [
{ mobile, otp, step, loading, otpData, errorMessage }, { mobile, otp, password, needPassword, step, loading, otpData, errorMessage },
{ setMobile, setOtp, requestOTP, verifyOTP, resetToMobile, clearError }, { setMobile, setOtp, setPassword, requestOTP, verifyOTP, resetToMobile, clearError },
]; ];
} }

View File

@@ -801,7 +801,10 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); return 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, { sessionId: p.sessionId })); return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, {
sessionId: p.sessionId,
...(p.password ? { password: p.password } : {}),
}));
}} }}
onSuccess={this.onOtpBindSuccess('showAmazonPayPersonalBind', 'Amazon Pay bound successfully')} onSuccess={this.onOtpBindSuccess('showAmazonPayPersonalBind', 'Amazon Pay bound successfully')}
onError={() => {}} onError={() => {}}
@@ -1054,10 +1057,6 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
bounces={false} bounces={false}
> >
{groups.map(({ walletType, items }) => { {groups.map(({ walletType, items }) => {
if (items.length === 1) {
return this.renderWalletItem(items[0]);
}
const expanded = !!expandedBoundWalletGroups[walletType]; const expanded = !!expandedBoundWalletGroups[walletType];
const color = WALLET_TYPE_COLORS[walletType] ?? '#888'; const color = WALLET_TYPE_COLORS[walletType] ?? '#888';
@@ -1079,7 +1078,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
</View> </View>
<View style={{ flex: 1, marginLeft: 12 }}> <View style={{ flex: 1, marginLeft: 12 }}>
<Text style={s.boundWalletGroupTitle}>{formatWalletTypeLabel(walletType)}</Text> <Text style={s.boundWalletGroupTitle}>{formatWalletTypeLabel(walletType)}</Text>
<Text style={s.boundWalletGroupSub}>{items.length} accounts</Text> <Text style={s.boundWalletGroupSub}>{items.length} account{items.length > 1 ? 's' : ''}</Text>
</View> </View>
<Text style={s.walletGroupChevron}>{expanded ? '▼' : '▶'}</Text> <Text style={s.walletGroupChevron}>{expanded ? '▼' : '▶'}</Text>
</TouchableOpacity> </TouchableOpacity>

View File

@@ -7,6 +7,7 @@ import {
openMobikwikPayToBank, openMobikwikPayToBank,
openPhonePePayToBank, openPhonePePayToBank,
openFreechargePayToBank, openFreechargePayToBank,
openAmazonPayPayToBank,
} from 'rnwalletman'; } from 'rnwalletman';
export default function TestScreen() { export default function TestScreen() {
@@ -51,6 +52,12 @@ export default function TestScreen() {
.catch((err: unknown) => Alert.alert('Error', String(err))); .catch((err: unknown) => Alert.alert('Error', String(err)));
}; };
const handleAmazonPayPayToBank = () => {
openAmazonPayPayToBank()
.then((result: boolean) => console.log('Amazon Pay To Bank', result ? 'Success' : 'Failed'))
.catch((err: unknown) => Alert.alert('Error', String(err)));
};
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.sectionTitle}>Test Tools</Text> <Text style={styles.sectionTitle}>Test Tools</Text>
@@ -67,6 +74,9 @@ export default function TestScreen() {
<TouchableOpacity style={[styles.btn, { backgroundColor: '#5468db' }]} onPress={handleFreechargePayToBank}> <TouchableOpacity style={[styles.btn, { backgroundColor: '#5468db' }]} onPress={handleFreechargePayToBank}>
<Text style={styles.btnText}>Freecharge Pay To Bank Test</Text> <Text style={styles.btnText}>Freecharge Pay To Bank Test</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#ff9900' }]} onPress={handleAmazonPayPayToBank}>
<Text style={styles.btnText}>Amazon Pay To Bank Test</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#3498db' }]} onPress={handleEcho}> <TouchableOpacity style={[styles.btn, { backgroundColor: '#3498db' }]} onPress={handleEcho}>
<Text style={styles.btnText}>Echo Test</Text> <Text style={styles.btnText}>Echo Test</Text>
</TouchableOpacity> </TouchableOpacity>