1- import { getCarbonServiceRole } from "@carbon/auth" ;
1+ import { getCarbonServiceRole , QUICKBOOKS_WEBHOOK_SECRET } from "@carbon/auth" ;
22
33import type { ActionFunctionArgs } from "@vercel/remix" ;
44import { json } from "@vercel/remix" ;
@@ -9,17 +9,34 @@ export const config = {
99 runtime : "nodejs" ,
1010} ;
1111
12- const integrationValidator = z . object ( {
13- webhookToken : z . string ( ) ,
12+ const quickbooksEventValidator = z . object ( {
13+ eventNotifications : z . array (
14+ z . object ( {
15+ realmId : z . string ( ) ,
16+ dataChangeEvent : z . object ( {
17+ entities : z . array (
18+ z . object ( {
19+ id : z . string ( ) ,
20+ name : z . string ( ) ,
21+ operation : z . enum ( [ "Create" , "Update" , "Delete" ] ) ,
22+ } )
23+ ) ,
24+ } ) ,
25+ } )
26+ ) ,
1427} ) ;
1528
1629function verifyQuickBooksSignature (
1730 payload : string ,
18- signature : string ,
19- webhookToken : string
31+ signature : string
2032) : boolean {
33+ if ( ! QUICKBOOKS_WEBHOOK_SECRET ) {
34+ console . warn ( "QUICKBOOKS_WEBHOOK_SECRET is not set" ) ;
35+ return true ;
36+ }
37+
2138 const expectedSignature = crypto
22- . createHmac ( "sha256" , webhookToken )
39+ . createHmac ( "sha256" , QUICKBOOKS_WEBHOOK_SECRET )
2340 . update ( payload )
2441 . digest ( "base64" ) ;
2542
@@ -32,8 +49,48 @@ function verifyQuickBooksSignature(
3249export async function action ( { request, params } : ActionFunctionArgs ) {
3350 const serviceRole = await getCarbonServiceRole ( ) ;
3451
35- const payload = await request . json ( ) ;
36- console . log ( { payload } ) ;
52+ const payload = await request . clone ( ) . json ( ) ;
53+
54+ const parsedPayload = quickbooksEventValidator . safeParse ( payload ) ;
55+ if ( ! parsedPayload . success ) {
56+ return json ( { success : false } , { status : 400 } ) ;
57+ }
58+
59+ const payloadText = await request . text ( ) ;
60+ const signatureHeader = request . headers . get ( "intuit-signature" ) ;
61+
62+ if ( ! signatureHeader ) {
63+ return json ( { success : false } , { status : 401 } ) ;
64+ }
65+
66+ const requestIsValid = verifyQuickBooksSignature (
67+ payloadText ,
68+ signatureHeader
69+ ) ;
70+
71+ if ( ! requestIsValid ) {
72+ return json ( { success : false } , { status : 401 } ) ;
73+ }
74+
75+ const events = parsedPayload . data . eventNotifications ;
76+ for await ( const event of events ) {
77+ const { realmId, dataChangeEvent } = event ;
78+
79+ const companyIntegration = await serviceRole
80+ . from ( "companyIntegration" )
81+ . select ( "*" )
82+ . eq ( "metadata->>tenantId" , realmId )
83+ . eq ( "id" , "quickbooks" )
84+ . single ( ) ;
85+
86+ console . log ( { companyIntegration } ) ;
87+
88+ const { entities } = dataChangeEvent ;
89+ for await ( const entity of entities ) {
90+ const { id, name, operation } = entity ;
91+ console . log ( { realmId, id, name, operation } ) ;
92+ }
93+ }
3794
3895 // const quickbooksIntegration = await serviceRole
3996 // .from("companyIntegration")
@@ -48,17 +105,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
48105 // );
49106
50107 // const payloadText = await request.text();
51- // const signatureHeader = request.headers.get("intuit-signature");
52-
53- // if (!signatureHeader) {
54- // return json({ success: false }, { status: 401 });
55- // }
56-
57- // if (
58- // !verifyQuickBooksSignature(payloadText, signatureHeader, webhookToken)
59- // ) {
60- // return json({ success: false }, { status: 401 });
61- // }
108+ //
62109
63110 // const payload = JSON.parse(payloadText);
64111 // console.log("QuickBooks webhook payload", payload);
0 commit comments