diff --git a/modules/rcart/components/folk-payment-request.ts b/modules/rcart/components/folk-payment-request.ts index 88bc2f6..1c61e8e 100644 --- a/modules/rcart/components/folk-payment-request.ts +++ b/modules/rcart/components/folk-payment-request.ts @@ -30,6 +30,7 @@ class FolkPaymentRequest extends HTMLElement { private enabledMethods = { card: true, wallet: true, encryptid: true }; // Result state + private loading = false; private generating = false; private generatedPayment: any = null; private qrDataUrl = ''; @@ -50,7 +51,14 @@ class FolkPaymentRequest extends HTMLElement { connectedCallback() { this.space = this.getAttribute('space') || 'default'; this.checkExistingSession(); - this.render(); + + // Restore payment from URL if present (e.g. ?id=abc-123) + const urlId = new URLSearchParams(window.location.search).get('id'); + if (urlId) { + this.loadExistingPayment(urlId); + } else { + this.render(); + } } private getApiBase(): string { @@ -134,6 +142,48 @@ class FolkPaymentRequest extends HTMLElement { this.render(); } + // ── Load existing payment from URL ── + + private async loadExistingPayment(paymentId: string) { + this.loading = true; + this.render(); + + try { + const res = await fetch(`${this.getApiBase()}/api/payments/${paymentId}`); + if (!res.ok) throw new Error('Payment not found'); + const data = await res.json(); + + this.generatedPayment = data; + this.description = data.description || ''; + this.amount = data.amount || ''; + this.amountEditable = data.amountEditable || false; + this.token = data.token || 'USDC'; + this.chainId = data.chainId || 8453; + this.paymentType = data.paymentType || 'single'; + this.maxPayments = data.maxPayments || 0; + this.enabledMethods = data.enabledMethods || { card: true, wallet: true, encryptid: true }; + this.authenticated = true; // skip auth since we're viewing + + // Build URLs + const host = window.location.origin; + this.payUrl = `${host}/${this.space}/rcart/pay/${paymentId}`; + this.qrSvgUrl = `${host}/${this.space}/rcart/api/payments/${paymentId}/qr`; + + // Generate client-side QR + try { + const QRCode = await import('qrcode'); + this.qrDataUrl = await QRCode.toDataURL(this.payUrl, { + margin: 2, width: 280, + color: { dark: '#1e1b4b', light: '#ffffff' }, + }); + } catch { /* QR generation optional */ } + } catch (e) { + this.authError = e instanceof Error ? e.message : String(e); + } + this.loading = false; + this.render(); + } + // ── Generate payment request ── private async generatePayment() { @@ -188,6 +238,11 @@ class FolkPaymentRequest extends HTMLElement { color: { dark: '#1e1b4b', light: '#ffffff' }, }); } catch { /* QR generation optional */ } + + // Update URL so page can be reloaded + const newUrl = new URL(window.location.href); + newUrl.searchParams.set('id', this.generatedPayment.id); + history.pushState(null, '', newUrl.toString()); } catch (e) { this.authError = e instanceof Error ? e.message : String(e); } @@ -206,6 +261,12 @@ class FolkPaymentRequest extends HTMLElement { this.paymentType = 'single'; this.maxPayments = 0; this.enabledMethods = { card: true, wallet: true, encryptid: true }; + + // Clear URL param + const newUrl = new URL(window.location.href); + newUrl.searchParams.delete('id'); + history.pushState(null, '', newUrl.toString()); + this.render(); } @@ -218,7 +279,8 @@ class FolkPaymentRequest extends HTMLElement {

Request Payment

Generate a QR code anyone can scan to pay you

- ${this.generatedPayment ? this.renderResult() : + ${this.loading ? '

Loading payment...

' : + this.generatedPayment ? this.renderResult() : !this.authenticated ? this.renderAuthStep() : this.renderForm()} `;