@@ -6,6 +6,9 @@ import {tracked} from '@glimmer/tracking';
66const BILLING_APP_LOAD_TIMEOUT_MS = 10_000 ;
77const BILLING_APP_LOAD_RETRY_DELAYS_MS = [ 1_000 ] ;
88const BILLING_APP_LOAD_FAILURE_MESSAGE = 'Billing app failed to become ready' ;
9+ const BILLING_APP_ATTEMPT_SOURCE_PRELOAD = 'preload' ;
10+ const BILLING_APP_ATTEMPT_SOURCE_USER_OPEN = 'user_open' ;
11+ const BILLING_APP_ATTEMPT_SOURCE_RETRY = 'retry' ;
912
1013export default class BillingService extends Service {
1114 @service ghostPaths ;
@@ -29,12 +32,17 @@ export default class BillingService extends Service {
2932 @tracked billingAppLastPreReadyMessageType = null ;
3033 @tracked billingAppReadyReceivedAt = null ;
3134 @tracked billingAppReadyPayload = null ;
35+ @tracked billingAppPreloadFailure = null ;
3236
3337 billingAppLoadTimeout = null ;
3438 billingAppRetryTimeout = null ;
3539 billingAppLoadAttempts = 0 ;
3640 billingAppLoadTimeoutMs = BILLING_APP_LOAD_TIMEOUT_MS ;
3741 billingAppLoadRetryDelaysMs = BILLING_APP_LOAD_RETRY_DELAYS_MS ;
42+ billingAppLoadAttemptId = null ;
43+ billingAppLoadAttemptSource = BILLING_APP_ATTEMPT_SOURCE_PRELOAD ;
44+ billingAppIframeReloadReason = null ;
45+ billingAppLoadAttemptSequence = 0 ;
3846 billingAppIframeSrcSetAt = null ;
3947 billingAppIframeLoadFired = false ;
4048
@@ -63,8 +71,11 @@ export default class BillingService extends Service {
6371 return this . getBillingIframe ( ) !== null && this . getBillingIframe ( ) . contentWindow ;
6472 }
6573
66- startBillingAppLoadMonitor ( ) {
74+ startBillingAppLoadMonitor ( options = { } ) {
75+ const { source = this . billingAppLoadAttemptSource } = options ;
76+
6777 if ( this . billingAppLoadTimeout || this . billingAppRetryTimeout ) {
78+ this . billingAppLoadAttemptSource = source ;
6879 return ;
6980 }
7081
@@ -75,6 +86,7 @@ export default class BillingService extends Service {
7586 this . resetBillingAppLoadDiagnostics ( ) ;
7687 }
7788
89+ this . billingAppLoadAttemptSource = source ;
7890 this . billingAppLoadAttempts += 1 ;
7991 this . billingAppLoadTimeout = setTimeout ( ( ) => {
8092 this . billingAppLoadTimeout = null ;
@@ -88,45 +100,58 @@ export default class BillingService extends Service {
88100 if ( retryDelay !== undefined ) {
89101 this . billingAppRetryTimeout = setTimeout ( ( ) => {
90102 this . billingAppRetryTimeout = null ;
91- this . reloadBillingIframe ( ) ;
92- this . startBillingAppLoadMonitor ( ) ;
103+ const source = this . billingWindowOpen ? BILLING_APP_ATTEMPT_SOURCE_RETRY : this . billingAppLoadAttemptSource ;
104+
105+ this . reloadBillingIframe ( { source, reloadReason : 'timeout_retry' } ) ;
106+ this . startBillingAppLoadMonitor ( { source} ) ;
93107 } , retryDelay ) ;
94108 return ;
95109 }
96110
97111 this . reportBillingAppLoadFailure ( ) ;
98112 }
99113
100- setBillingIframeSrc ( ) {
114+ setBillingIframeSrc ( options = { } ) {
101115 const iframe = this . getBillingIframe ( ) ;
102116 if ( ! iframe ) {
103117 return ;
104118 }
119+
120+ const {
121+ source = this . billingAppLoadAttemptSource || BILLING_APP_ATTEMPT_SOURCE_PRELOAD ,
122+ reloadReason = 'set_src'
123+ } = options ;
124+
105125 if ( this . _loadListenerAttachedTo !== iframe && typeof iframe . addEventListener === 'function' ) {
106126 iframe . addEventListener ( 'load' , ( ) => {
107127 this . billingAppIframeLoadFired = true ;
108128 } ) ;
109129 this . _loadListenerAttachedTo = iframe ;
110130 }
131+ this . billingAppLoadAttemptSequence += 1 ;
132+ this . billingAppLoadAttemptId = `${ Date . now ( ) } -${ this . billingAppLoadAttemptSequence } ` ;
133+ this . billingAppLoadAttemptSource = source ;
134+ this . billingAppIframeReloadReason = reloadReason ;
111135 this . billingAppIframeLoadFired = false ;
112136 this . billingAppIframeSrcSetAt = Date . now ( ) ;
113137 this . resetBillingAppLoadDiagnostics ( ) ;
114138 iframe . src = this . getIframeURL ( ) ;
115139 }
116140
117- reloadBillingIframe ( ) {
141+ reloadBillingIframe ( options = { } ) {
118142 const iframe = this . getBillingIframe ( ) ;
119143
120144 if ( ! iframe || this . billingAppLoaded ) {
121145 return ;
122146 }
123147
124- this . setBillingIframeSrc ( ) ;
148+ this . setBillingIframeSrc ( options ) ;
125149 }
126150
127151 markBillingAppLoaded ( payload = null ) {
128152 this . billingAppLoaded = true ;
129153 this . billingAppLoadFailureReported = false ;
154+ this . billingAppPreloadFailure = null ;
130155 this . billingAppReadyReceivedAt = Date . now ( ) ;
131156 this . billingAppReadyPayload = payload ;
132157 this . clearBillingAppLoadMonitor ( ) ;
@@ -232,11 +257,51 @@ export default class BillingService extends Service {
232257 }
233258 }
234259
260+ ensureBillingAppReadyForVisibleUse ( ) {
261+ if ( this . billingAppLoaded ) {
262+ return ;
263+ }
264+
265+ const hadPreloadFailure = ! ! this . billingAppPreloadFailure ;
266+ const hadVisibleFailure = this . billingAppLoadFailureReported ;
267+
268+ this . billingAppLoadFailureReported = false ;
269+ this . clearBillingAppLoadMonitor ( ) ;
270+ this . billingAppLoadAttempts = 0 ;
271+
272+ if ( hadPreloadFailure || hadVisibleFailure ) {
273+ this . reloadBillingIframe ( {
274+ source : BILLING_APP_ATTEMPT_SOURCE_USER_OPEN ,
275+ reloadReason : hadPreloadFailure ? 'visible_open_after_preload_failure' : 'visible_open_after_load_failure'
276+ } ) ;
277+ } else {
278+ this . billingAppLoadAttemptSource = BILLING_APP_ATTEMPT_SOURCE_USER_OPEN ;
279+ }
280+
281+ this . startBillingAppLoadMonitor ( { source : BILLING_APP_ATTEMPT_SOURCE_USER_OPEN } ) ;
282+ }
283+
284+ recordBillingAppPreloadFailure ( ) {
285+ this . billingAppPreloadFailure = {
286+ attemptId : this . billingAppLoadAttemptId ,
287+ attempts : this . billingAppLoadAttempts ,
288+ elapsedMs : this . billingAppIframeSrcSetAt ? Date . now ( ) - this . billingAppIframeSrcSetAt : null ,
289+ nonReadyMessageCount : this . billingAppPreReadyMessageCount ,
290+ nonReadyMessageTypes : [ ...this . billingAppPreReadyMessageTypes ] ,
291+ lastNonReadyMessageType : this . billingAppLastPreReadyMessageType
292+ } ;
293+ }
294+
235295 reportBillingAppLoadFailure ( ) {
236296 if ( this . billingAppLoadFailureReported ) {
237297 return ;
238298 }
239299
300+ if ( ! this . billingWindowOpen ) {
301+ this . recordBillingAppPreloadFailure ( ) ;
302+ return ;
303+ }
304+
240305 this . billingAppLoadFailureReported = true ;
241306
242307 if ( ! this . config . sentry_dsn ) {
@@ -287,6 +352,10 @@ export default class BillingService extends Service {
287352 ghost : {
288353 billing_monitor : {
289354 attempts : this . billingAppLoadAttempts ,
355+ attempt_id : this . billingAppLoadAttemptId ,
356+ attempt_source : this . billingAppLoadAttemptSource ,
357+ attempt_phase : 'shell_ready' ,
358+ iframe_reload_reason : this . billingAppIframeReloadReason ,
290359 has_billing_url : ! ! this . config . hostSettings ?. billing ?. url ,
291360 is_force_upgrade : ! ! this . config . hostSettings ?. forceUpgrade ,
292361 location_hash : window . location . hash ,
@@ -304,6 +373,11 @@ export default class BillingService extends Service {
304373 non_ready_message_count : this . billingAppPreReadyMessageCount ,
305374 non_ready_message_types : this . billingAppPreReadyMessageTypes . join ( ',' ) ,
306375 last_non_ready_message_type : this . billingAppLastPreReadyMessageType ,
376+ has_preload_failure : ! ! this . billingAppPreloadFailure ,
377+ preload_failure_elapsed_ms : this . billingAppPreloadFailure ?. elapsedMs ?? null ,
378+ preload_non_ready_message_count : this . billingAppPreloadFailure ?. nonReadyMessageCount ?? null ,
379+ preload_non_ready_message_types : this . billingAppPreloadFailure ?. nonReadyMessageTypes ?. join ( ',' ) ?? null ,
380+ preload_last_non_ready_message_type : this . billingAppPreloadFailure ?. lastNonReadyMessageType ?? null ,
307381 ready_received : false ,
308382 navigator_online : navigator . onLine ,
309383 connection_effective_type : navigator . connection ?. effectiveType ?? null ,
@@ -316,6 +390,8 @@ export default class BillingService extends Service {
316390 } ,
317391 tags : {
318392 source : 'billing-app-load-monitor' ,
393+ attempt_source : this . billingAppLoadAttemptSource ,
394+ attempt_phase : 'shell_ready' ,
319395 route : this . router . currentRouteName ,
320396 path : window . location . hash
321397 }
@@ -340,7 +416,21 @@ export default class BillingService extends Service {
340416 }
341417 }
342418
343- return url ;
419+ return this . addBillingAppAttemptIdToURL ( url ) ;
420+ }
421+
422+ addBillingAppAttemptIdToURL ( url ) {
423+ if ( ! url || ! this . billingAppLoadAttemptId ) {
424+ return url ;
425+ }
426+
427+ try {
428+ const billingUrl = new URL ( url ) ;
429+ billingUrl . searchParams . set ( 'bmaAttemptId' , this . billingAppLoadAttemptId ) ;
430+ return billingUrl . toString ( ) ;
431+ } catch ( e ) {
432+ return url ;
433+ }
344434 }
345435
346436 async getOwnerUser ( ) {
@@ -398,6 +488,10 @@ export default class BillingService extends Service {
398488 this . sendRouteUpdate ( ) ;
399489
400490 this . billingWindowOpen = value ;
491+
492+ if ( value ) {
493+ this . ensureBillingAppReadyForVisibleUse ( ) ;
494+ }
401495 }
402496
403497 // Controls navigation to billing window modal which is triggered from the application UI.
0 commit comments