@@ -31,13 +31,23 @@ export default class CustomerInvoiceComponent extends Component {
3131 @tracked paymentReference = '' ;
3232 @tracked error = null ;
3333 @tracked successMessage = null ;
34+ @tracked pendingMessage = null ;
3435 @tracked isRedirectingToCheckout = false ;
36+ @tracked talerPaymentUri = null ;
37+ @tracked paymentQrImage = null ;
38+ @tracked paymentQrText = null ;
3539
3640 constructor ( ) {
3741 super ( ...arguments ) ;
42+ this . installTalerSupportMeta ( ) ;
3843 this . loadInvoice . perform ( ) ;
3944 }
4045
46+ willDestroy ( ) {
47+ super . willDestroy ( ...arguments ) ;
48+ this . removeTalerSupportMeta ( ) ;
49+ }
50+
4151 // ── Getters ───────────────────────────────────────────────────────────────
4252
4353 get invoiceId ( ) {
@@ -72,6 +82,22 @@ export default class CustomerInvoiceComponent extends Component {
7282 return this . selectedGateway ?. driver === 'stripe' ;
7383 }
7484
85+ get hasTalerPaymentUri ( ) {
86+ return typeof this . talerPaymentUri === 'string' && this . talerPaymentUri . startsWith ( 'taler' ) ;
87+ }
88+
89+ get paymentQrImageSrc ( ) {
90+ if ( typeof this . paymentQrImage !== 'string' || this . paymentQrImage . length === 0 ) {
91+ return null ;
92+ }
93+
94+ if ( this . paymentQrImage . startsWith ( 'data:' ) || this . paymentQrImage . startsWith ( 'http://' ) || this . paymentQrImage . startsWith ( 'https://' ) ) {
95+ return this . paymentQrImage ;
96+ }
97+
98+ return `data:image/png;base64,${ this . paymentQrImage } ` ;
99+ }
100+
75101 // ── Tasks ─────────────────────────────────────────────────────────────────
76102
77103 /**
@@ -133,9 +159,8 @@ export default class CustomerInvoiceComponent extends Component {
133159 /**
134160 * Submits a payment request.
135161 *
136- * For Stripe: backend returns { checkout_url } and the browser is redirected
137- * to Stripe's hosted checkout page. isRedirectingToCheckout is set to true
138- * to show a loading state while the redirect happens.
162+ * For redirect-capable gateways: backend returns { payment_url } or
163+ * { checkout_url } and the browser is redirected to the hosted/wallet flow.
139164 *
140165 * For other gateways: backend records the payment immediately and returns
141166 * the updated invoice.
@@ -159,10 +184,28 @@ export default class CustomerInvoiceComponent extends Component {
159184 { namespace : 'ledger/public' }
160185 ) ;
161186
162- // Stripe Checkout Session — redirect the browser to Stripe's hosted page
163- if ( data ?. checkout_url ) {
187+ const paymentUrl = data ?. payment_url ?? data ?. payment_uri ?? data ?. checkout_url ?? data ?. data ?. taler_pay_uri ;
188+ this . paymentQrImage = data ?. qr_image ?? data ?. data ?. qr_image ?? null ;
189+ this . paymentQrText = data ?. qr_text ?? data ?. data ?. qr_text ?? paymentUrl ?? null ;
190+
191+ if ( this . isTalerUri ( paymentUrl ) ) {
192+ this . talerPaymentUri = paymentUrl ;
193+ this . isRedirectingToCheckout = false ;
194+ this . pendingMessage = data ?. message ?? 'Payment started. Open your GNU Taler wallet to complete it.' ;
195+ return ;
196+ }
197+
198+ // Redirect payment sessions — Stripe Checkout, QPay app link, etc.
199+ if ( paymentUrl ) {
164200 this . isRedirectingToCheckout = true ;
165- window . location . href = data . checkout_url ;
201+ this . pendingMessage = data ?. message ?? 'Redirecting to payment provider...' ;
202+ window . location . href = paymentUrl ;
203+ return ;
204+ }
205+
206+ if ( data ?. payment_status === 'pending' ) {
207+ this . pendingMessage = data ?. message ?? 'Payment started. Complete it in your payment app, then refresh this invoice.' ;
208+ this . showPaymentForm = false ;
166209 return ;
167210 }
168211
@@ -180,6 +223,10 @@ export default class CustomerInvoiceComponent extends Component {
180223 @action togglePaymentForm ( ) {
181224 this . showPaymentForm = ! this . showPaymentForm ;
182225 this . successMessage = null ;
226+ this . pendingMessage = null ;
227+ this . talerPaymentUri = null ;
228+ this . paymentQrImage = null ;
229+ this . paymentQrText = null ;
183230 this . error = null ;
184231 }
185232
@@ -190,4 +237,34 @@ export default class CustomerInvoiceComponent extends Component {
190237 @action updateReference ( event ) {
191238 this . paymentReference = event . target . value ;
192239 }
240+
241+ isTalerUri ( value ) {
242+ return typeof value === 'string' && ( value . startsWith ( 'taler://' ) || value . startsWith ( 'taler+http://' ) || value . startsWith ( 'taler+https://' ) ) ;
243+ }
244+
245+ installTalerSupportMeta ( ) {
246+ if ( typeof document === 'undefined' ) {
247+ return ;
248+ }
249+
250+ let meta = document . querySelector ( 'meta[name="taler-support"]' ) ;
251+
252+ if ( ! meta ) {
253+ meta = document . createElement ( 'meta' ) ;
254+ meta . name = 'taler-support' ;
255+ meta . dataset . ledgerTalerSupport = 'true' ;
256+ document . head . appendChild ( meta ) ;
257+ }
258+
259+ meta . content = 'uri' ;
260+ }
261+
262+ removeTalerSupportMeta ( ) {
263+ if ( typeof document === 'undefined' ) {
264+ return ;
265+ }
266+
267+ const meta = document . querySelector ( 'meta[name="taler-support"][data-ledger-taler-support="true"]' ) ;
268+ meta ?. remove ( ) ;
269+ }
193270}
0 commit comments