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
|
// Derive wallet on-demand if not yet available
|
||||||
if (!this.walletAddress) {
|
if (!this.walletAddress) {
|
||||||
|
// Attempt 1: Client-side PRF-based derivation (desktop browsers with PRF support)
|
||||||
try {
|
try {
|
||||||
const { getKeyManager } = await import('../../../src/encryptid/key-derivation');
|
const { getKeyManager } = await import('../../../src/encryptid/key-derivation');
|
||||||
const km = getKeyManager();
|
const km = getKeyManager();
|
||||||
|
|
@ -190,10 +191,62 @@ class FolkPaymentRequest extends HTMLElement {
|
||||||
const keys = await km.getKeys();
|
const keys = await km.getKeys();
|
||||||
if (keys.eoaAddress) this.walletAddress = keys.eoaAddress;
|
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) {
|
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.generating = false;
|
||||||
this.render();
|
this.render();
|
||||||
return;
|
return;
|
||||||
|
|
@ -643,19 +696,8 @@ class FolkPaymentRequest extends HTMLElement {
|
||||||
.action-row { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; }
|
.action-row { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; }
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@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; }
|
.field-row { flex-direction: column; }
|
||||||
.action-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