generated from ecomplus/application-starter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
webhook.js
176 lines (152 loc) · 5.8 KB
/
webhook.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// read configured E-Com Plus app data
const getAppData = require('./../../lib/store-api/get-app-data')
const axios = require('axios')
const Axios = axios.create({
baseURL: 'https://www.google-analytics.com',
headers: {
'Content-Type': 'application/json'
}
})
const SKIP_TRIGGER_NAME = 'SkipTrigger'
const ECHO_SUCCESS = 'SUCCESS'
const ECHO_SKIP = 'SKIP'
const ECHO_API_ERROR = 'STORE_API_ERR'
const findOrderById = (appSdk, storeId, orderId, auth) => new Promise((resolve, reject) => {
appSdk.apiRequest(storeId, `/orders/${orderId}.json`, 'GET', null, auth)
.then(({ response }) => {
resolve(response.data)
})
.catch(err => {
reject(err)
})
})
exports.post = async ({ appSdk, admin }, req, res) => {
// receiving notification from Store API
const { storeId } = req
/**
* Treat E-Com Plus trigger body here
* Ref.: https://developers.e-com.plus/docs/api/#/store/triggers/
*/
const trigger = req.body
// console.log('>>body: ', JSON.stringify(trigger), ' <<')
if (trigger.resource === 'orders') {
const orderId = trigger.inserted_id || trigger.resource_id
let order = trigger.body
try {
const auth = await appSdk.getAuth(storeId)
order = await findOrderById(appSdk, storeId, orderId, auth)
// console.log('>> Webhooh Order: ', order)
const buyer = order.buyers && order.buyers[0]
if (orderId && order.items) {
// get app configured options
const appData = await getAppData({ appSdk, storeId, auth })
if (
Array.isArray(appData.ignore_triggers) &&
appData.ignore_triggers.indexOf(trigger.resource) > -1
) {
// ignore current trigger
const err = new Error()
err.name = SKIP_TRIGGER_NAME
throw err
}
const measurementId = appData.measurement_id
const apiSecret = appData.api_secret
const enabledCustonEvent = order.financial_status?.current && appData.custom_events
const enabledRefundEvent = order.status === 'cancelled' && appData.refund_event !== false
console.log('>> Store: ', storeId, ' status: ', order.status, ' financial Status: ',
order.financial_status.current, ' enable Custon: ', enabledCustonEvent,
' Event Cancelled', enabledRefundEvent)
if (measurementId && apiSecret && (enabledCustonEvent || enabledRefundEvent)) {
const url = `/mp/collect?api_secret=${apiSecret}&measurement_id=${measurementId}`
const items = order.items.map(item => {
const eventItem = {
item_id: item.product_id || item.sku,
item_name: item.name,
price: item.final_price || item.price,
quantity: item.quantity || 1
}
if (item.variation_id) {
eventItem.item_variant = item.variation_id
}
if (order.extra_discount?.discount_coupon) {
eventItem.coupon = order.extra_discount.discount_coupon
}
return eventItem
})
const params = {
id: orderId,
currency: order.currency_id || 'BRL',
transaction_id: orderId,
value: order.amount.total,
status: order.financial_status?.current || order.status,
items
}
if (order.amount.freight) {
params.shipping = order.amount.freight
}
if (order.amount.tax || order.amount.extra) {
params.tax = (order.amount.tax || 0) + (order.amount.extra || 0)
}
if (order.utm?.campaign) {
params.coupon = order.utm.campaign
}
const events = []
const docRef = admin.firestore().doc(`events_sent/${orderId}`)
const lastSentEvents = (await docRef.get()).get('eventNames') || []
if (enabledCustonEvent && order.financial_status) {
const eventName = `purchase_${order.financial_status.current}`
if (!lastSentEvents.includes(eventName)) {
events.push({ name: eventName, params })
}
}
if (enabledRefundEvent && !lastSentEvents.includes('refund')) {
events.push({ name: 'refund', params })
}
// https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag#purchase
if (events.length) {
const body = {
client_id: buyer?._id || Date.now().toString(),
events
}
console.log('>> url: ', url, ' body: ', JSON.stringify(body))
await Axios.post(url, body)
await docRef.set({
storeId,
eventNames: events.map(({ name }) => name),
updatedAt: admin.firestore.Timestamp.now()
}, { merge: true })
.catch(console.error)
return res.send(ECHO_SUCCESS)
}
return res.send(ECHO_SKIP)
} else {
console.log('>> measurementId or apiSecret not found, or disabled event')
return res.status(400).send(ECHO_SKIP)
}
}
} catch (err) {
if (err.name === SKIP_TRIGGER_NAME) {
// trigger ignored by app configuration
res.send(ECHO_SKIP)
} else if (err.appWithoutAuth === true) {
const msg = `Webhook for ${storeId} unhandled with no authentication found`
const error = new Error(msg)
error.trigger = JSON.stringify(trigger)
console.error(error)
res.status(412).send(msg)
} else {
console.error(err)
// request to Store API with error response
// return error status code
res.status(500)
const { message } = err
res.send({
error: ECHO_API_ERROR,
message
})
}
return null
}
}
return res.sendStatus(412)
}