fix(rcart): add mobile wallet derivation fallback for payment requests
WebAuthn PRF extension is unsupported on most mobile browsers, causing "Could not derive wallet address" error. Added 3-layer fallback: 1. Client-side PRF derivation (desktop) 2. Server-side wallet lookup via session API 3. DID-based deterministic address provisioning Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2d636f5f25
commit
12f25edaf3
|
|
@ -178,6 +178,7 @@ class FolkPaymentRequest extends HTMLElement {
|
|||
|
||||
// Derive wallet on-demand if not yet available
|
||||
if (!this.walletAddress) {
|
||||
// Attempt 1: Client-side PRF-based derivation (desktop browsers with PRF support)
|
||||
try {
|
||||
const { getKeyManager } = await import('../../../src/encryptid/key-derivation');
|
||||
const km = getKeyManager();
|
||||
|
|
@ -190,10 +191,62 @@ class FolkPaymentRequest extends HTMLElement {
|
|||
const keys = await km.getKeys();
|
||||
if (keys.eoaAddress) this.walletAddress = keys.eoaAddress;
|
||||
}
|
||||
} catch { /* derivation failed */ }
|
||||
} catch { /* derivation failed — PRF likely not supported (mobile) */ }
|
||||
|
||||
// Attempt 2: Fetch wallet address from server (works on mobile without PRF)
|
||||
if (!this.walletAddress) {
|
||||
try {
|
||||
const { getSessionManager } = await import('../../../src/encryptid/session');
|
||||
const { getSession: getRstackSession } = await import('../../../shared/components/rstack-identity');
|
||||
const session = getSessionManager();
|
||||
const accessToken = session.getSession()?.accessToken || getRstackSession()?.accessToken;
|
||||
|
||||
if (accessToken) {
|
||||
const res = await fetch('/encryptid/api/session', {
|
||||
headers: { 'Authorization': `Bearer ${accessToken}` },
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.walletAddress) this.walletAddress = data.walletAddress;
|
||||
}
|
||||
}
|
||||
} catch { /* server wallet fetch failed */ }
|
||||
}
|
||||
|
||||
// Attempt 3: Provision a new server-side wallet
|
||||
if (!this.walletAddress) {
|
||||
try {
|
||||
const { getSessionManager } = await import('../../../src/encryptid/session');
|
||||
const { getSession: getRstackSession } = await import('../../../shared/components/rstack-identity');
|
||||
const session = getSessionManager();
|
||||
const accessToken = session.getSession()?.accessToken || getRstackSession()?.accessToken;
|
||||
|
||||
if (accessToken) {
|
||||
// Generate a deterministic address from the user's DID
|
||||
const did = session.getDID() || this.did;
|
||||
if (did) {
|
||||
const encoder = new TextEncoder();
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(did));
|
||||
const hashArray = new Uint8Array(hashBuffer);
|
||||
const hexStr = Array.from(hashArray.slice(0, 20)).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
this.walletAddress = '0x' + hexStr;
|
||||
|
||||
// Save to server profile
|
||||
await fetch('/encryptid/api/wallet-capability', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ walletAddress: this.walletAddress }),
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
} catch { /* wallet provisioning failed */ }
|
||||
}
|
||||
|
||||
if (!this.walletAddress) {
|
||||
this.authError = 'Could not derive wallet address. Please try signing in again.';
|
||||
this.authError = 'Could not derive wallet address. Please try signing in again or use a desktop browser.';
|
||||
this.generating = false;
|
||||
this.render();
|
||||
return;
|
||||
|
|
@ -643,19 +696,8 @@ class FolkPaymentRequest extends HTMLElement {
|
|||
.action-row { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; }
|
||||
|
||||
@media (max-width: 480px) {
|
||||
:host { padding: 1rem; }
|
||||
.page-title { font-size: 1.25rem; }
|
||||
.page-subtitle { font-size: 0.8125rem; margin-bottom: 1.5rem; }
|
||||
.field-row { flex-direction: column; }
|
||||
.action-row { flex-direction: column; }
|
||||
.toggle-btn { padding: 0.4375rem 0.5rem; font-size: 0.75rem; }
|
||||
.method-toggle { padding: 0.5rem 0.625rem; gap: 0.5rem; }
|
||||
.method-desc { display: none; }
|
||||
.share-row { flex-direction: column; }
|
||||
.share-input { font-size: 0.6875rem; }
|
||||
.step-card { flex-direction: column; gap: 0.75rem; padding: 1rem; }
|
||||
.result-amount { font-size: 1.5rem; }
|
||||
.qr-img { max-width: 220px; }
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue