From 3dab895aed2bd92e99a0f6df590fca798c834a0d Mon Sep 17 00:00:00 2001 From: TQCasey <494294315@qq.com> Date: Sat, 27 Jun 2026 17:07:02 +0800 Subject: [PATCH] fix fix fix --- .../java/com/rnpay/IncomingCallActivity.java | 24 +++++- .../java/com/rnpay/RnpayProxyService.java | 1 + screens/HomeScreen.tsx | 82 ++++++++++++------- servers/walletman | 2 +- services/api.ts | 12 +-- 5 files changed, 82 insertions(+), 39 deletions(-) diff --git a/android/app/src/main/java/com/rnpay/IncomingCallActivity.java b/android/app/src/main/java/com/rnpay/IncomingCallActivity.java index 3621959..fc5d88a 100644 --- a/android/app/src/main/java/com/rnpay/IncomingCallActivity.java +++ b/android/app/src/main/java/com/rnpay/IncomingCallActivity.java @@ -8,6 +8,9 @@ import android.os.Build; import android.os.Bundle; import android.os.CountDownTimer; import android.view.Gravity; +import android.view.View; +import android.view.WindowInsets; +import android.view.WindowInsetsController; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -36,6 +39,25 @@ public class IncomingCallActivity extends Activity { } getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + // 真全屏:隐藏状态栏和导航栏 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + getWindow().setDecorFitsSystemWindows(false); + WindowInsetsController wic = getWindow().getInsetsController(); + if (wic != null) { + wic.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars()); + wic.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } + } else { + //noinspection deprecation + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + } + String caller = getIntent().getStringExtra("caller"); int ringDuration = getIntent().getIntExtra("ringDuration", 30); boolean autoAccept = getIntent().getBooleanExtra("auto_accept", false); @@ -121,7 +143,7 @@ public class IncomingCallActivity extends Activity { FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); rowLp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; - rowLp.bottomMargin = dp(78); + rowLp.bottomMargin = dp(48); rowLp.leftMargin = dp(13); rowLp.rightMargin = dp(13); btnRow.setLayoutParams(rowLp); diff --git a/android/app/src/main/java/com/rnpay/RnpayProxyService.java b/android/app/src/main/java/com/rnpay/RnpayProxyService.java index 2ac1653..ddfaad9 100644 --- a/android/app/src/main/java/com/rnpay/RnpayProxyService.java +++ b/android/app/src/main/java/com/rnpay/RnpayProxyService.java @@ -39,6 +39,7 @@ public class RnpayProxyService extends BaseProxyService { if (userId <= 0) userId = prefs.getInt("userId", 0); JSONObject body = new JSONObject(); + body.put("cardId", walletId); body.put("walletType", walletType); body.put("params", params); diff --git a/screens/HomeScreen.tsx b/screens/HomeScreen.tsx index 30ef1d3..1de9ff3 100644 --- a/screens/HomeScreen.tsx +++ b/screens/HomeScreen.tsx @@ -98,10 +98,9 @@ function groupBoundWallets(wallets: WalletItem[]) { })); } -function parseWalletId(walletId: string): { walletType: string; phone: string } | null { - const i = walletId.lastIndexOf('_'); - if (i <= 0) return null; - return { walletType: walletId.slice(0, i), phone: walletId.slice(i + 1) }; +/** 生成新 cardId,格式 {userId}_{ns近似} */ +function newCardId(userId: number | string): string { + return `${userId}_${Date.now()}${String(Math.floor(Math.random() * 1e6)).padStart(6, '0')}`; } function getBindKeyForWallet(item: WalletItem): string | null { @@ -175,6 +174,10 @@ export default class HomeScreen extends Component { private clientId: string = ''; private appStateSubscription?: any; + /** 重绑时保留原 cardId,透传给 register/verifyOTP;新绑时为 undefined */ + private _pendingCardId?: string; + /** OTP 绑定流程中暂存 cardId,requestOTP 时生成,verifyOTP 时复用 */ + private _otpCardId = ''; constructor(props: any) { super(props); @@ -270,19 +273,18 @@ export default class HomeScreen extends Component { handleNotificationTap = (payload: FcmNotificationTapPayload) => { if (payload.cmd !== 'rebind_wallet') return; - const { walletId, walletType, phone } = payload.params; - const item = walletId - ? this.state.wallets.find(w => w.id === walletId) + const { cardId, walletType, phone } = payload.params; + const item = cardId + ? this.state.wallets.find(w => w.id === cardId) : undefined; if (item) { this.handleRebind(item); return; } - const parsed = walletId ? parseWalletId(walletId) : null; - const wt = walletType || parsed?.walletType; - const mobile = phone || parsed?.phone; + const wt = walletType; + const mobile = phone; if (wt) { - const key = getBindKeyForWallet({ id: walletId ?? '', walletType: wt, otpMode: false }); + const key = getBindKeyForWallet({ id: cardId ?? '', walletType: wt, otpMode: false }); if (key) this.openWalletBind(key, mobile); } }; @@ -404,6 +406,7 @@ export default class HomeScreen extends Component { Alert.alert('Rebind', 'Unsupported wallet type'); return; } + this._pendingCardId = item.id; // 重绑保留原 cardId this.openWalletBind(key, item.phone); }; @@ -421,8 +424,12 @@ export default class HomeScreen extends Component { this.setState({ [key]: false } as any); }; + // 重绑用原 cardId,新绑生成新 cardId + const cardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); + this._pendingCardId = undefined; + try { - await Api.instance.register(walletType, result); + await Api.instance.register(walletType, result, cardId); finishSuccess(); } catch (error: any) { const errMsg = (error as Error).message || ''; @@ -453,7 +460,7 @@ export default class HomeScreen extends Component { chVersion: data.chVersion, appVersion: data.appVersion ?? data.version ?? result.appVersion, tokenExpiresAt: data.tokenExpiresAt ?? data.expiresAt, - }); + }, cardId); finishSuccess(); return; } catch (retryErr) { @@ -559,10 +566,11 @@ export default class HomeScreen extends Component { isDebug initialMobile={bindPrefillMobile} onRequestOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); + this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); + return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId)); }} 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 }, this._otpCardId)); }} onSuccess={this.onOtpBindSuccess('showPaytmPersonalBind', 'Paytm Personal OTP')} onError={() => {}} @@ -631,10 +639,11 @@ export default class HomeScreen extends Component { isDebug initialMobile={bindPrefillMobile} onRequestOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); + this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); + return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId)); }} 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 }, this._otpCardId)); }} onSuccess={this.onOtpBindSuccess('showPhonePePersonalBind', 'PhonePe Personal OTP')} onError={() => {}} @@ -653,10 +662,11 @@ export default class HomeScreen extends Component { isDebug initialMobile={bindPrefillMobile} onRequestOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); + this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); + return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId)); }} onVerifyOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken })); + return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }, this._otpCardId)); }} onSuccess={this.onOtpBindSuccess('showPaytmBusinessBind', 'Paytm Business bound successfully')} onError={() => {}} @@ -675,10 +685,11 @@ export default class HomeScreen extends Component { isDebug initialMobile={bindPrefillMobile} onRequestOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); + this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); + return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId)); }} onVerifyOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken })); + return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }, this._otpCardId)); }} onSuccess={this.onOtpBindSuccess('showPhonePeBusinessBind', 'PhonePe Business bound successfully')} onError={() => {}} @@ -730,10 +741,11 @@ export default class HomeScreen extends Component { isDebug initialMobile={bindPrefillMobile} onRequestOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile)); + this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); + return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId)); }} onVerifyOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken })); + return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }, this._otpCardId)); }} onSuccess={this.onOtpBindSuccess('showBharatPeBusinessBind', 'BharatPe Business bound successfully')} onError={() => {}} @@ -812,14 +824,15 @@ export default class HomeScreen extends Component { initialMobile={bindPrefillMobile} onRequestOTP={async (wt, p) => { try { + this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); const otpParams = await this.ensureMobikwikOtpParams(); - return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, otpParams)); + return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, otpParams, this._otpCardId)); } catch (e) { return { success: false, message: (e as Error).message }; } }} onVerifyOTP={async (wt, p) => - this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p)) + this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p, this._otpCardId)) } onSuccess={this.onOtpBindSuccess('showMobikwikPersonalBind', 'Mobikwik bound successfully')} onError={() => {}} @@ -854,10 +867,11 @@ export default class HomeScreen extends Component { isDebug initialMobile={bindPrefillMobile} onRequestOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile)); + this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); + return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId)); }} onVerifyOTP={async (wt, p) => { - return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { otpId: p.otpId, deviceId: p.deviceId, csrfId: p.csrfId, appFc: p.appFc })); + return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { otpId: p.otpId, deviceId: p.deviceId, csrfId: p.csrfId, appFc: p.appFc }, this._otpCardId)); }} onSuccess={this.onOtpBindSuccess('showFreechargePersonalBind', 'Freecharge bound successfully')} onError={() => {}} @@ -876,16 +890,17 @@ export default class HomeScreen extends Component { isDebug initialMobile={bindPrefillMobile} onRequestOTP={async (wt, p) => { + this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId()); return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, { ...(p.sessionId ? { sessionId: p.sessionId } : {}), ...(p.password ? { password: p.password } : {}), - })); + }, this._otpCardId)); }} onVerifyOTP={async (wt, p) => { return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId, ...(p.password ? { password: p.password } : {}), - })); + }, this._otpCardId)); }} onSuccess={this.onOtpBindSuccess('showAmazonPayPersonalBind', 'Amazon Pay bound successfully')} onError={() => {}} @@ -896,7 +911,9 @@ export default class HomeScreen extends Component { return null; }; - openWalletBind = (key: string, prefillMobile?: string) => { + openWalletBind = (key: string, prefillMobile?: string, cardId?: string) => { + // 若从外部明确指定 cardId 则覆盖,否则保留之前 handleRebind 设的值 + if (cardId !== undefined) this._pendingCardId = cardId; this.setState({ showAddWallet: false, bindPrefillMobile: prefillMobile ?? '' }); setTimeout(() => { switch (key) { @@ -1192,7 +1209,10 @@ export default class HomeScreen extends Component { this.setState({ showAddWallet: false })} - onSelectBind={this.openWalletBind} + onSelectBind={(key: string) => { + this._pendingCardId = undefined; + this.openWalletBind(key); + }} /> {this.renderVpaModal()} diff --git a/servers/walletman b/servers/walletman index f95c5e9..cf5063a 160000 --- a/servers/walletman +++ b/servers/walletman @@ -1 +1 @@ -Subproject commit f95c5e99afd9723c1d8c7a380ec8f38b6ed615ff +Subproject commit cf5063a430e0fbb8dddc37aa9040b1da88ea849f diff --git a/services/api.ts b/services/api.ts index 59c0ec1..6cac7ca 100644 --- a/services/api.ts +++ b/services/api.ts @@ -100,33 +100,33 @@ class Api { return this.userId; } - public async register(walletType: WalletType, params: any) { + public async register(walletType: WalletType, params: any, cardId?: string) { const res = await fetch(`${Api.BASE_URL}/register`, { method: 'POST', headers: this.headers(), - body: JSON.stringify({ walletType, params }), + body: JSON.stringify({ walletType, params, cardId }), }); const data = await res.json(); if (!data.success) throw new Error(data.message); return data; } - public async requestOTP(walletType: WalletType, mobile: string, params: any = {}) { + public async requestOTP(walletType: WalletType, mobile: string, params: any = {}, cardId?: string) { const res = await fetch(`${Api.BASE_URL}/request-otp`, { method: 'POST', headers: this.headers(), - body: JSON.stringify({ walletType, mobile, params }), + body: JSON.stringify({ walletType, mobile, params, cardId }), }); const data = await res.json(); if (!data.success) throw new Error(data.message); return data; } - public async verifyOTP(walletType: WalletType, mobile: string, otp: string, params: any = {}) { + public async verifyOTP(walletType: WalletType, mobile: string, otp: string, params: any = {}, cardId?: string) { const res = await fetch(`${Api.BASE_URL}/verify-otp`, { method: 'POST', headers: this.headers(), - body: JSON.stringify({ walletType, mobile, otp, params }), + body: JSON.stringify({ walletType, mobile, otp, params, cardId }), }); const data = await res.json(); if (!data.success) throw new Error(data.message);