rejoiner 1.2.0 → 2.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -43
- package/lib/config.js +6 -2
- package/lib/endpoints/customer.js +22 -0
- package/lib/endpoints/journeys.js +39 -0
- package/lib/endpoints/lists.js +21 -3
- package/lib/endpoints/ping.js +11 -0
- package/lib/endpoints/segments.js +16 -0
- package/lib/helpers.js +31 -0
- package/lib/rejoiner.js +38 -28
- package/package.json +5 -2
- package/lib/endpoints/contact.js +0 -11
- package/lib/endpoints/lead.js +0 -26
package/README.md
CHANGED
|
@@ -17,89 +17,138 @@ npm install rejoiner --save
|
|
|
17
17
|
````js
|
|
18
18
|
var Rejoiner = require('rejoiner')
|
|
19
19
|
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
var client = new Rejoiner({
|
|
21
|
+
// Your Site ID
|
|
22
|
+
siteId: 'eXaMpLe',
|
|
23
|
+
// Your API key
|
|
24
|
+
apiKey: 'tHiSaPiKeYiSjUsTaNeXaMpLeAnDyOuCaNtUsEiT',
|
|
25
25
|
})
|
|
26
26
|
````
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## Ping
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
The `ping` endpoint can be used to verify your credentials are working.
|
|
31
31
|
|
|
32
32
|
````js
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
client.verify.ping()
|
|
34
|
+
.then(...)
|
|
35
|
+
.catch(...)
|
|
36
|
+
````
|
|
37
|
+
|
|
38
|
+
## Customer Endpoints
|
|
39
|
+
|
|
40
|
+
### Convert Customer
|
|
41
|
+
|
|
42
|
+
````js
|
|
43
|
+
client.customer.convert({
|
|
44
|
+
email: 'test@example.com',
|
|
35
45
|
cart_data: {
|
|
36
|
-
cart_value:
|
|
46
|
+
cart_value: 20000,
|
|
37
47
|
cart_item_count: 2,
|
|
48
|
+
promo: 'COUPON_CODE',
|
|
49
|
+
return_url: 'https://www.example.com/return_url',
|
|
50
|
+
...
|
|
38
51
|
},
|
|
39
|
-
cart_items: [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
cart_items: [
|
|
53
|
+
{
|
|
54
|
+
product_id: 'example',
|
|
55
|
+
name: 'Example Product',
|
|
56
|
+
price: 10000,
|
|
57
|
+
description: 'Information about Example Product.',
|
|
58
|
+
category: [
|
|
59
|
+
'Example Category 1',
|
|
60
|
+
'Example Category 2',
|
|
61
|
+
],
|
|
62
|
+
item_qty: 1,
|
|
63
|
+
qty_price: 10000,
|
|
64
|
+
product_url: 'https://www.example.com/products/example',
|
|
65
|
+
image_url: 'https://www.example.com/products/example/images/example.jpg',
|
|
66
|
+
...
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
product_id: 'example2',
|
|
70
|
+
name: 'Example Product 2',
|
|
71
|
+
price: 10000,
|
|
72
|
+
description: 'Information about Example Product 2.',
|
|
73
|
+
category: [
|
|
74
|
+
'Example Category 2',
|
|
75
|
+
'Example Category 3',
|
|
76
|
+
],
|
|
77
|
+
item_qty: 1,
|
|
78
|
+
qty_price: 10000,
|
|
79
|
+
product_url: 'https://www.example.com/products/example2',
|
|
80
|
+
image_url: 'https://www.example.com/products/example2/images/example.jpg',
|
|
81
|
+
...
|
|
82
|
+
},
|
|
83
|
+
...
|
|
84
|
+
],
|
|
58
85
|
})
|
|
59
86
|
.then(...)
|
|
60
87
|
.catch(...)
|
|
61
88
|
````
|
|
62
89
|
|
|
63
|
-
### Cancellation
|
|
90
|
+
### Journey Cancellation
|
|
64
91
|
|
|
65
92
|
````js
|
|
66
|
-
|
|
93
|
+
client.customer.cancel('test@example.com')
|
|
67
94
|
.then(...)
|
|
68
95
|
.catch(...)
|
|
69
96
|
````
|
|
70
97
|
|
|
71
|
-
###
|
|
98
|
+
### Customer Unsubscribe
|
|
72
99
|
|
|
73
100
|
````js
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
101
|
+
client.customer.unsubscribe('test@example.com')
|
|
102
|
+
.then(...)
|
|
103
|
+
.catch(...)
|
|
104
|
+
````
|
|
105
|
+
|
|
106
|
+
### Record Explicit Customer Consent
|
|
107
|
+
|
|
108
|
+
````js
|
|
109
|
+
client.customer.optIn('test@example.com')
|
|
110
|
+
.then(...)
|
|
111
|
+
.catch(...)
|
|
112
|
+
````
|
|
113
|
+
|
|
114
|
+
## Email List Endpoints
|
|
115
|
+
|
|
116
|
+
### Email Lists
|
|
117
|
+
|
|
118
|
+
````js
|
|
119
|
+
client.lists.get()
|
|
120
|
+
.then(...)
|
|
121
|
+
.catch(...)
|
|
122
|
+
````
|
|
123
|
+
|
|
124
|
+
### Retrieving Listing of Contacts
|
|
125
|
+
|
|
126
|
+
````js
|
|
127
|
+
client.lists.contacts('eXaMpLeLiStId').get()
|
|
79
128
|
.then(...)
|
|
80
129
|
.catch(...)
|
|
81
130
|
````
|
|
82
131
|
|
|
83
|
-
|
|
132
|
+
#### With optional page number for pagination
|
|
84
133
|
|
|
85
134
|
````js
|
|
86
|
-
|
|
135
|
+
client.lists.contacts('eXaMpLeLiStId').get(2)
|
|
87
136
|
.then(...)
|
|
88
137
|
.catch(...)
|
|
89
138
|
````
|
|
90
139
|
|
|
91
|
-
###
|
|
140
|
+
### Add Customer to List
|
|
92
141
|
|
|
93
142
|
````js
|
|
94
|
-
|
|
143
|
+
client.lists.contacts('eXaMpLeLiStId').add('test@example.com')
|
|
95
144
|
.then(...)
|
|
96
145
|
.catch(...)
|
|
97
146
|
````
|
|
98
147
|
|
|
99
|
-
###
|
|
148
|
+
### Remove Customer From List
|
|
100
149
|
|
|
101
150
|
````js
|
|
102
|
-
|
|
151
|
+
client.lists.contacts('eXaMpLeLiStId').remove('test@example.com')
|
|
103
152
|
.then(...)
|
|
104
153
|
.catch(...)
|
|
105
154
|
````
|
package/lib/config.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
const VERSION = require('../package.json').version
|
|
2
2
|
|
|
3
|
-
const { REJOINER_API_KEY,
|
|
3
|
+
const { REJOINER_API_KEY, REJOINER_SITE_ID, REJOINER_WEBHOOK_SECRET } = process.env
|
|
4
|
+
|
|
5
|
+
const DEFAULT_BASE_URL = 'https://rj2.rejoiner.com/api/v1'
|
|
6
|
+
const REJOINER_BASE_URL = process.env.REJOINER_BASE_URL || DEFAULT_BASE_URL
|
|
4
7
|
|
|
5
8
|
module.exports = {
|
|
6
9
|
VERSION,
|
|
7
10
|
REJOINER_API_KEY,
|
|
8
|
-
REJOINER_API_SECRET,
|
|
9
11
|
REJOINER_SITE_ID,
|
|
12
|
+
REJOINER_WEBHOOK_SECRET,
|
|
13
|
+
REJOINER_BASE_URL,
|
|
10
14
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { withClient } = require('../helpers')
|
|
2
|
+
|
|
3
|
+
const customerEndpoint = (client) => {
|
|
4
|
+
const endpoint = 'customer'
|
|
5
|
+
|
|
6
|
+
const { dispatchReturnData, postEmail, putEmail } = withClient(client)
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
path: endpoint,
|
|
10
|
+
convert: (data, always) => {
|
|
11
|
+
if (always) return dispatchReturnData('post')(`${endpoint}/always_convert/`, data)
|
|
12
|
+
return dispatchReturnData('post')(`${endpoint}/convert/`, data)
|
|
13
|
+
},
|
|
14
|
+
cancel: email => postEmail(`${endpoint}/cancel/`, email),
|
|
15
|
+
unsubscribe: email => postEmail(`${endpoint}/unsubscribe/`, email),
|
|
16
|
+
optIn: email => postEmail(`${endpoint}/opt_in/`, email),
|
|
17
|
+
get: email => dispatchReturnData('get')(`customers/${email}/`),
|
|
18
|
+
update: data => putEmail(`customers/${data.email}/`, data),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = customerEndpoint
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { withClient } = require('../helpers')
|
|
2
|
+
|
|
3
|
+
const path = 'journeys'
|
|
4
|
+
|
|
5
|
+
const journeysEndpoint = (client) => {
|
|
6
|
+
const { dispatchReturnData } = withClient(client)
|
|
7
|
+
|
|
8
|
+
const endpoint = journeyId => ({
|
|
9
|
+
nodes: nodeId => ({
|
|
10
|
+
webhook: (email) => {
|
|
11
|
+
const requestPath = `${path}/${journeyId}/nodes/${nodeId}/webhook_event_wait/`
|
|
12
|
+
|
|
13
|
+
if (typeof email === 'string') {
|
|
14
|
+
return dispatchReturnData('post')(requestPath, {
|
|
15
|
+
email: email.toLowerCase(),
|
|
16
|
+
customer_data: {},
|
|
17
|
+
session_data: {},
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (typeof email === 'object' && typeof email.email === 'string') {
|
|
22
|
+
return dispatchReturnData('post')(requestPath, Object.assign(
|
|
23
|
+
{ customer_data: {}, session_data: {} },
|
|
24
|
+
email,
|
|
25
|
+
{ email: email.email.toLowerCase() },
|
|
26
|
+
))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Promise.reject(new Error('Request is missing required email parameter.'))
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
endpoint.path = path
|
|
35
|
+
|
|
36
|
+
return endpoint
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = journeysEndpoint
|
package/lib/endpoints/lists.js
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
|
+
const { withClient } = require('../helpers')
|
|
2
|
+
|
|
1
3
|
const listsEndpoint = (client) => {
|
|
2
4
|
const endpoint = 'lists'
|
|
3
5
|
|
|
6
|
+
const { dispatchReturnData, postEmail } = withClient(client)
|
|
7
|
+
|
|
4
8
|
return {
|
|
5
|
-
|
|
6
|
-
get: () =>
|
|
7
|
-
|
|
9
|
+
path: endpoint,
|
|
10
|
+
get: () => dispatchReturnData('get')(`${endpoint}/`),
|
|
11
|
+
add: (name) => {
|
|
12
|
+
if (typeof name === 'string') {
|
|
13
|
+
return dispatchReturnData('post')(`${endpoint}/`, { name })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return dispatchReturnData('post')(`${endpoint}/`, name)
|
|
17
|
+
},
|
|
18
|
+
contacts: listId => ({
|
|
19
|
+
get: (page) => {
|
|
20
|
+
const pagination = page ? `?page=${page}` : ''
|
|
21
|
+
return dispatchReturnData('get')(`${endpoint}/${listId}/contacts/${pagination}`)
|
|
22
|
+
},
|
|
23
|
+
add: email => postEmail(`${endpoint}/${listId}/contacts/`, email),
|
|
24
|
+
remove: email => postEmail(`${endpoint}/${listId}/contacts/remove/`, email),
|
|
25
|
+
}),
|
|
8
26
|
}
|
|
9
27
|
}
|
|
10
28
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const { withClient } = require('../helpers')
|
|
2
|
+
|
|
3
|
+
const segmentsEndpoint = (client) => {
|
|
4
|
+
const endpoint = 'segments'
|
|
5
|
+
|
|
6
|
+
const { dispatchReturnData } = withClient(client)
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
path: endpoint,
|
|
10
|
+
customers: segmentId => ({
|
|
11
|
+
get: () => dispatchReturnData('get')(`${endpoint}/${segmentId}/customers/`),
|
|
12
|
+
}),
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = segmentsEndpoint
|
package/lib/helpers.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const withClient = (client) => {
|
|
2
|
+
const dispatchReturnData = method => (...args) => client.dispatch[method](...args)
|
|
3
|
+
.then(res => res.data)
|
|
4
|
+
|
|
5
|
+
const withEmail = (method, path, email) => {
|
|
6
|
+
if (typeof email === 'string') {
|
|
7
|
+
return dispatchReturnData(method)(path, { email: email.toLowerCase() })
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (typeof email === 'object' && typeof email.email === 'string') {
|
|
11
|
+
return dispatchReturnData(method)(path, Object.assign(
|
|
12
|
+
{},
|
|
13
|
+
email,
|
|
14
|
+
{ email: email.email.toLowerCase() },
|
|
15
|
+
))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return Promise.reject(new Error('Request is missing required email parameter.'))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const postEmail = (path, email) => withEmail('post', path, email)
|
|
22
|
+
const putEmail = (path, email) => withEmail('put', path, email)
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
dispatchReturnData,
|
|
26
|
+
postEmail,
|
|
27
|
+
putEmail,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { withClient }
|
package/lib/rejoiner.js
CHANGED
|
@@ -2,57 +2,67 @@ const axios = require('axios')
|
|
|
2
2
|
const crypto = require('crypto')
|
|
3
3
|
const fs = require('graceful-fs')
|
|
4
4
|
const path = require('path')
|
|
5
|
-
const
|
|
5
|
+
const merge = require('lodash.merge')
|
|
6
6
|
|
|
7
7
|
const {
|
|
8
8
|
VERSION,
|
|
9
9
|
REJOINER_API_KEY,
|
|
10
|
-
REJOINER_API_SECRET,
|
|
11
10
|
REJOINER_SITE_ID,
|
|
11
|
+
REJOINER_WEBHOOK_SECRET,
|
|
12
|
+
REJOINER_BASE_URL,
|
|
12
13
|
} = require('./config')
|
|
13
14
|
|
|
14
|
-
function
|
|
15
|
+
function Rejoiner2(options) {
|
|
15
16
|
const opts = merge({}, options)
|
|
16
17
|
|
|
17
18
|
this.siteId = opts.siteId || REJOINER_SITE_ID
|
|
18
19
|
this.apiKey = opts.apiKey || REJOINER_API_KEY
|
|
19
|
-
this.
|
|
20
|
+
this.webhookSecret = opts.webhookSecret || REJOINER_WEBHOOK_SECRET
|
|
21
|
+
this.baseURL = opts.baseURL || REJOINER_BASE_URL
|
|
20
22
|
|
|
21
|
-
this.
|
|
22
|
-
|
|
23
|
-
return crypto.createHmac('sha1', this.apiSecret).update(req).digest('base64')
|
|
24
|
-
}
|
|
23
|
+
if (!this.siteId) throw new Error('Site ID must be configured')
|
|
24
|
+
if (!this.apiKey) throw new Error('API Key must be configured')
|
|
25
25
|
|
|
26
26
|
this.dispatch = axios.create({
|
|
27
|
-
baseURL:
|
|
27
|
+
baseURL: `${this.baseURL}/${this.siteId}`,
|
|
28
28
|
headers: {
|
|
29
|
-
|
|
29
|
+
Authorization: `Rejoiner ${this.apiKey}`,
|
|
30
|
+
'User-Agent': `rejoiner2-node/v${VERSION}`,
|
|
30
31
|
},
|
|
31
32
|
})
|
|
32
33
|
|
|
33
|
-
this.dispatch.interceptors.request.use((conf) => {
|
|
34
|
-
const signedReq = this.sign({
|
|
35
|
-
httpVerb: conf.method.toUpperCase(),
|
|
36
|
-
requestPath: conf.url.replace('https://app.rejoiner.com', ''),
|
|
37
|
-
requestBody: JSON.stringify(conf.data),
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
const withSignedReqHeader = merge({}, conf, {
|
|
41
|
-
headers: {
|
|
42
|
-
Authorization: `Rejoiner ${this.apiKey}:${signedReq}`,
|
|
43
|
-
},
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
return withSignedReqHeader
|
|
47
|
-
})
|
|
48
|
-
|
|
49
34
|
fs.readdirSync(path.join(__dirname, 'endpoints'))
|
|
50
35
|
.filter(file => file.indexOf('.') !== 0)
|
|
51
36
|
.forEach((file) => {
|
|
52
37
|
// eslint-disable-next-line global-require, import/no-dynamic-require
|
|
53
38
|
const endpoint = require(path.join(__dirname, 'endpoints', file))(this)
|
|
54
|
-
this[endpoint.
|
|
39
|
+
this[endpoint.path] = endpoint
|
|
55
40
|
})
|
|
41
|
+
|
|
42
|
+
this.verifyWebhook = (signatureHeader, payload) => {
|
|
43
|
+
if (!this.webhookSecret) throw new Error('No webhook secret configured')
|
|
44
|
+
|
|
45
|
+
const { timestamp, hmac } = signatureHeader.split(',')
|
|
46
|
+
.reduce((signature, element) => {
|
|
47
|
+
const [key, value] = element.trim().split('=')
|
|
48
|
+
switch (key) {
|
|
49
|
+
case 't':
|
|
50
|
+
return { ...signature, timestamp: value }
|
|
51
|
+
case 'sha1':
|
|
52
|
+
return { ...signature, hmac: value }
|
|
53
|
+
default:
|
|
54
|
+
return signature
|
|
55
|
+
}
|
|
56
|
+
}, {})
|
|
57
|
+
|
|
58
|
+
const signedPayload = `${timestamp}.${payload}`
|
|
59
|
+
|
|
60
|
+
const digest = crypto.createHmac('sha1', this.webhookSecret)
|
|
61
|
+
.update(signedPayload)
|
|
62
|
+
.digest('hex')
|
|
63
|
+
|
|
64
|
+
return digest === hmac
|
|
65
|
+
}
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
module.exports =
|
|
68
|
+
module.exports = Rejoiner2
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rejoiner",
|
|
3
3
|
"description": "Rejoiner REST API client wrapper for Node.js",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.10.2",
|
|
5
5
|
"main": "lib/rejoiner.js",
|
|
6
6
|
"author": "Sascha Bratton <sascha@brattonbratton.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -26,12 +26,15 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"axios": ">=0.18.1",
|
|
28
28
|
"graceful-fs": "^4.1.11",
|
|
29
|
-
"lodash": "^4.
|
|
29
|
+
"lodash.merge": "^4.6.2"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"eslint": "^5.3.0",
|
|
33
33
|
"eslint-config-airbnb-base": "^13.2.0",
|
|
34
34
|
"eslint-plugin-import": "^2.17.2",
|
|
35
35
|
"husky": "^0.14.3"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=8.3.0"
|
|
36
39
|
}
|
|
37
40
|
}
|
package/lib/endpoints/contact.js
DELETED
package/lib/endpoints/lead.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const leadEndpoint = (client) => {
|
|
2
|
-
const endpoint = 'lead'
|
|
3
|
-
|
|
4
|
-
const postEmail = (path, email) => {
|
|
5
|
-
let data
|
|
6
|
-
|
|
7
|
-
if (typeof email === 'string') {
|
|
8
|
-
data = { email }
|
|
9
|
-
} else {
|
|
10
|
-
data = email
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return client.dispatch.post(path, data).then(res => res.data)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
name: endpoint,
|
|
18
|
-
convert: data => client.dispatch.post(`${endpoint}/convert`, data)
|
|
19
|
-
.then(res => res.data),
|
|
20
|
-
cancel: email => postEmail(`${endpoint}/cancel`, email),
|
|
21
|
-
optIn: email => postEmail(`${endpoint}/opt_in`, email),
|
|
22
|
-
unsubscribe: email => postEmail(`${endpoint}/unsubscribe`, email),
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = leadEndpoint
|