@@ -21,6 +21,7 @@ export interface QuickBooksWebhookIngressPayload {
2121}
2222
2323export interface QuickBooksWebhookIngressResult {
24+ failed : number
2425 ignored : number
2526 nextCursor ?: string
2627 processed : number
@@ -33,7 +34,7 @@ export async function executeQuickBooksWebhookIngress(
3334) : Promise < QuickBooksWebhookIngressResult > {
3435 const eventIndex = payload . eventIndex ?? 0
3536 const event = payload . events [ eventIndex ]
36- if ( ! event ) return { ignored : 0 , processed : 0 , targetCount : 0 }
37+ if ( ! event ) return { failed : 0 , ignored : 0 , processed : 0 , targetCount : 0 }
3738
3839 const request = new NextRequest ( 'http://internal/api/webhooks/quickbooks' , {
3940 method : 'POST' ,
@@ -65,10 +66,6 @@ export async function executeQuickBooksWebhookIngress(
6566 else failed += 1
6667 }
6768
68- if ( failed > 0 ) {
69- throw new Error ( `Failed to dispatch ${ failed } of ${ page . targets . length } QuickBooks targets` )
70- }
71-
7269 logger . info ( `[${ payload . requestId } ] QuickBooks webhook page completed` , {
7370 eventId : event . id ,
7471 eventIndex,
@@ -77,23 +74,22 @@ export async function executeQuickBooksWebhookIngress(
7774 targetCount : page . targets . length ,
7875 } )
7976 return {
77+ failed,
8078 ignored,
8179 processed,
8280 targetCount : page . targets . length ,
8381 ...( nextCursor ? { nextCursor } : { } ) ,
8482 }
8583}
8684
87- async function runQuickBooksWebhookIngressJob (
88- payload : QuickBooksWebhookIngressPayload
85+ async function enqueueQuickBooksWebhookContinuation (
86+ payload : QuickBooksWebhookIngressPayload ,
87+ result : QuickBooksWebhookIngressResult
8988) : Promise < void > {
9089 const eventIndex = payload . eventIndex ?? 0
91- const result = await executeQuickBooksWebhookIngress ( payload )
9290 if ( result . nextCursor ) {
9391 await enqueueQuickBooksWebhookIngress ( { ...payload , afterWebhookId : result . nextCursor } )
94- return
95- }
96- if ( eventIndex + 1 < payload . events . length ) {
92+ } else if ( eventIndex + 1 < payload . events . length ) {
9793 await enqueueQuickBooksWebhookIngress ( {
9894 ...payload ,
9995 eventIndex : eventIndex + 1 ,
@@ -102,6 +98,20 @@ async function runQuickBooksWebhookIngressJob(
10298 }
10399}
104100
101+ async function runQuickBooksWebhookIngressJob (
102+ payload : QuickBooksWebhookIngressPayload
103+ ) : Promise < void > {
104+ const result = await executeQuickBooksWebhookIngress ( payload )
105+ // The continuation has a deterministic job id, so retries cannot duplicate it. Enqueue it
106+ // before retrying this page to avoid stranding later events in an already-acknowledged batch.
107+ await enqueueQuickBooksWebhookContinuation ( payload , result )
108+ if ( result . failed > 0 ) {
109+ throw new Error (
110+ `Failed to dispatch ${ result . failed } of ${ result . targetCount } QuickBooks targets`
111+ )
112+ }
113+ }
114+
105115export async function enqueueQuickBooksWebhookIngress (
106116 payload : QuickBooksWebhookIngressPayload
107117) : Promise < string > {
0 commit comments