From 3e810b6913528e87548819e54bb0bb366f2a9129 Mon Sep 17 00:00:00 2001 From: Adi1231234 Date: Tue, 24 Mar 2026 15:01:31 +0200 Subject: [PATCH] feat: add cancelPairingCode() and requestPairingCode() on QR clients (#22449) * feat: add cancelPairingCode() and allow requestPairingCode() - requestPairingCode() now exposes onCodeReceivedEvent if needed, so it works on clients initialized in QR mode - Add cancelPairingCode() method to stop pairing and return to QR - Update TypeScript definitions * fix(auth): dynamic ADV key in getQR + structural cancelPairingCode the getQR closure captured advSecretKey once during initialize(), but requestpairingcode/cancelPairingCode changes the key in IndexedDB. the stale closure key caused QR scans to fail with HMAC validation error. fix: read key dynamically via WAWebUserPrefsMultiDevice.getADVSecretKey() (synchronous in-memory cache) on every getQR call, matching how WhatsApp web's internal QR component works. cancelpairingcode now mirrors WhatsApp Web's resetLinkDeviceState: refreshqr() restarts WebSocket for fresh refs, then initializeqrlinking() generates a new key. QR emission happens automatically via the existing change:ref listener. --------- Co-authored-by: BenyFilho <168232825+BenyFilho@users.noreply.github.com> --- index.d.ts | 3 +++ src/Client.js | 31 +++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index 268d8c3..631d8bc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -197,6 +197,9 @@ declare namespace WAWebJS { intervalMs?: number, ): Promise; + /** Cancels an active pairing code session and returns to QR code mode */ + cancelPairingCode(): Promise + /** Force reset of connection state for the client */ resetState(): Promise; diff --git a/src/Client.js b/src/Client.js index deb4f7e..ed2392d 100644 --- a/src/Client.js +++ b/src/Client.js @@ -255,8 +255,6 @@ class Client extends EventEmitter { window.AuthStore.Base64Tools.encodeB64( registrationInfo.identityKeyPair.pubKey, ); - const advSecretKey = - await window.AuthStore.RegistrationUtils.getADVSecretKey(); const platform = window.AuthStore.RegistrationUtils.DEVICE_PLATFORM; const getQR = (ref) => @@ -266,10 +264,11 @@ class Client extends EventEmitter { ',' + identityKeyB64 + ',' + - advSecretKey + + window + .require('WAWebUserPrefsMultiDevice') + .getADVSecretKey() + ',' + platform; - window.onQRChangedEvent(getQR(window.AuthStore.Conn.ref)); // initial qr window.AuthStore.Conn.on('change:ref', (_, ref) => { window.onQRChangedEvent(getQR(ref)); @@ -516,6 +515,14 @@ class Client extends EventEmitter { showNotification = true, intervalMs = 180000, ) { + await exposeFunctionIfAbsent( + this.pupPage, + 'onCodeReceivedEvent', + async (code) => { + this.emit(Events.CODE_RECEIVED, code); + return code; + }, + ); return await this.pupPage.evaluate( async (phoneNumber, showNotification, intervalMs) => { const getCode = async () => { @@ -553,6 +560,22 @@ class Client extends EventEmitter { ); } + /** + * Cancels an active pairing code session and returns to QR code mode + */ + async cancelPairingCode() { + await this.pupPage.evaluate(async () => { + if (window.codeInterval) { + clearInterval(window.codeInterval); + window.codeInterval = undefined; + } + window.require('WAWebLaunchSocketUtils').refreshQR(); + await window + .require('WAWebAltDeviceLinkingApi') + .initializeQRLinking(); + }); + } + /** * Attach event listeners to WA Web * Private function