Last active
September 4, 2019 07:35
-
-
Save khaledosman/513c31e30566e12bb4fbae16392cfaee to your computer and use it in GitHub Desktop.
NodeJS wrapper functions around Amazon Dash Replenishment API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fetch = require('node-fetch') | |
const { URLSearchParams } = require('url') | |
const { dateFromJSON, dateToJSON } = require('../database/utils') | |
const { User } = require('../database') | |
const DRS_API_BASE_URL = 'https://dash-replenishment-service-na.amazon.com' | |
const CLIENT_SECRET = process.env.DRS_CLIENT_SECRET | |
// const REDIRECT_URL = `${process.env.BASE_URL}/api/amazonLWA` | |
async function handleErrors (response) { | |
const json = await response.json() | |
if (!response.ok) { | |
console.log('ERROR: ' + JSON.stringify(json)) | |
throw Error(json.error_description || json.error || response.statusText) | |
} | |
return json | |
} | |
async function deregisterUserFromDRSIfHesRegistered ({ _id, amazon_drs_token: amazonDRSToken, os, bundle_id: bundleId }) { | |
if (amazonDRSToken && amazonDRSToken.access_token) { | |
const { access_token: accessToken } = await ensureTokenValid({ _id, amazon_drs_token: amazonDRSToken }, { os: os, bundleId: bundleId }) | |
return Promise.all([ | |
deregisterDRS(accessToken), | |
User.findByIdAndUpdate(_id, { amazon_drs_token: {}, is_subscribed_to_drs: false }).lean() | |
]) | |
} else { | |
return Promise.resolve() | |
} | |
} | |
function getOrderInfo (eventInstanceId, token) { | |
return fetch(`${DRS_API_BASE_URL}/getOrderInfo/${eventInstanceId}`, { | |
method: 'GET', | |
headers: { | |
'x-amzn-accept-type': '[email protected]', | |
'x-amzn-type-version': '[email protected]', | |
Authorization: `Bearer ${token}` | |
} | |
}) | |
.then(handleErrors) | |
} | |
function getSubscriptionInfo (token) { | |
return fetch(`${DRS_API_BASE_URL}/subscriptionInfo`, { | |
method: 'GET', | |
headers: { | |
'x-amzn-accept-type': '[email protected]', | |
'x-amzn-type-version': '[email protected]', | |
Authorization: `Bearer ${token}` | |
} | |
}) | |
.then(handleErrors) | |
} | |
function cancelTestOrder (slotId, token) { | |
return fetch(`${DRS_API_BASE_URL}/testOrders/slots/${slotId}`, { | |
method: 'DELETE', | |
headers: { | |
'x-amzn-accept-type': '[email protected]', | |
'x-amzn-type-version': '[email protected]', | |
Authorization: `Bearer ${token}` | |
} | |
}) | |
.then(handleErrors) | |
} | |
function cancelAllTestOrders (token) { | |
return fetch(`${DRS_API_BASE_URL}/testOrders`, { | |
method: 'DELETE', | |
headers: { | |
'x-amzn-accept-type': '[email protected]', | |
'x-amzn-type-version': '[email protected]', | |
Authorization: `Bearer ${token}` | |
} | |
}) | |
.then(handleErrors) | |
} | |
function deregisterDRS (token) { | |
return fetch(`${DRS_API_BASE_URL}/registration`, { | |
method: 'DELETE', | |
headers: { | |
'x-amzn-accept-type': '[email protected]', | |
'x-amzn-type-version': '[email protected]', | |
Authorization: `Bearer ${token}` | |
} | |
}) | |
.then(handleErrors) | |
} | |
function sendSlotStatus (slotId, token, body) { | |
/* | |
{ | |
"expectedReplenishmentDate" : "2015-12-28T10:00:00Z", | |
"remainingQuantityInUnit" : 3.5, | |
"originalQuantityInUnit" : 10, | |
"totalQuantityOnHand" : 20, | |
"lastUseDate" : "2015-12-21T10:00:00Z" | |
} | |
*/ | |
return fetch(`${DRS_API_BASE_URL}/slotStatus/${slotId}`, { | |
method: 'POST', | |
headers: { | |
'x-amzn-accept-type': '[email protected]', | |
'x-amzn-type-version': '[email protected]', | |
'Content-Type': 'application/json', | |
Authorization: `Bearer ${token}` | |
}, | |
body: JSON.stringify(body) | |
}) | |
.then(async function handleErrors (response) { | |
const text = await response.text() | |
if (!response.ok) { | |
console.log('ERROR: ' + text) | |
throw Error(text || response.statusText) | |
} | |
return text | |
}) | |
} | |
function sendDeviceStatus (token, mostRecentlyActiveDate) { | |
return fetch(`${DRS_API_BASE_URL}/deviceStatus`, { | |
method: 'POST', | |
headers: { | |
'x-amzn-accept-type': '[email protected]', | |
'x-amzn-type-version': '[email protected]', | |
'Content-Type': 'application/json', | |
Authorization: `Bearer ${token}` | |
}, | |
body: JSON.stringify({ | |
mostRecentlyActiveDate | |
}) | |
}) | |
.then(handleErrors) | |
} | |
function replenish (slotId, token) { | |
return fetch(`${DRS_API_BASE_URL}/replenish/${slotId}`, { | |
method: 'POST', | |
headers: { | |
'x-amzn-accept-type': '[email protected]', | |
'x-amzn-type-version': '[email protected]', | |
'Content-Type': 'application/json', | |
Authorization: `Bearer ${token}` | |
} | |
}) | |
.then(handleErrors) | |
} | |
function createToken (code, os, bundleId) { | |
const REDIRECT_URL = `${process.env.BASE_URL}/api/amazonLWA` | |
const CLIENT_ID = os === 'iOS' ? process.env.DRS_CLIENT_ID_IOS : process.env.DRS_CLIENT_ID | |
// const REDIRECT_URL = `amzn://${bundleId}` | |
const params = new URLSearchParams() | |
params.append('grant_type', 'authorization_code') | |
params.append('client_id', CLIENT_ID) | |
params.append('client_secret', CLIENT_SECRET) | |
params.append('redirect_uri', REDIRECT_URL) | |
params.append('code', code) | |
return fetch('https://api.amazon.com/auth/o2/token', { | |
method: 'POST', | |
body: params, | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded' | |
} | |
}) | |
.then(handleErrors) | |
} | |
function createTokenFromSDK ({ redirectURL, code, codeVerifier, os, bundleId }) { | |
const CLIENT_ID = os === 'iOS' ? process.env.DRS_CLIENT_ID_IOS : process.env.DRS_CLIENT_ID | |
// const REDIRECT_URL = `amzn://${bundleId}` | |
const params = new URLSearchParams() | |
params.append('grant_type', 'authorization_code') | |
params.append('client_id', CLIENT_ID) | |
params.append('redirect_uri', redirectURL) | |
params.append('code', code) | |
params.append('code_verifier', codeVerifier) | |
console.log({ CLIENT_ID, redirectURL, code, codeVerifier }) | |
return fetch('https://api.amazon.com/auth/o2/token', { | |
method: 'POST', | |
body: params, | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded' | |
} | |
}) | |
.then(handleErrors) | |
} | |
// authorization code, client ID, and redirect UR, code_verifier | |
/* | |
The access token is valid for one hour. When the access token expires, or is about to expire, you can exchange the refresh token for a new access token. | |
*/ | |
function refreshAccessToken (refreshToken, { os }) { | |
const CLIENT_ID = os === 'iOS' ? process.env.DRS_CLIENT_ID_IOS : process.env.DRS_CLIENT_ID | |
// const REDIRECT_URL = `amzn://${bundleId}` | |
const params = new URLSearchParams() | |
params.append('grant_type', 'refresh_token') | |
params.append('client_id', CLIENT_ID) | |
params.append('client_secret', CLIENT_SECRET) | |
// params.append('redirect_uri', REDIRECT_URL) | |
params.append('refresh_token', refreshToken) | |
return fetch('https://api.amazon.com/auth/o2/token', { | |
method: 'POST', | |
body: params, | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded' | |
} | |
}) | |
.then(handleErrors) | |
} | |
async function ensureTokenValid ({ _id, amazon_drs_token: amazonDRSToken }, { os, bundleId }) { | |
const { access_token: accessToken, expires_in: expiresIn, refresh_token: refreshToken, created_at: createdAt } = amazonDRSToken | |
if (accessToken && refreshToken && expiresIn && createdAt) { | |
const creationDate = dateFromJSON(createdAt) | |
if (Date.now() >= creationDate.getTime() + (expiresIn * 1000)) { | |
// refresh token | |
const newTokenInfo = await refreshAccessToken(refreshToken, { os, bundleId }) | |
await User.findByIdAndUpdate(_id, { amazon_drs_token: { ...newTokenInfo, created_at: dateToJSON(new Date()) } }) | |
.select({ _id: 1 }) | |
.lean() | |
return newTokenInfo | |
} else { | |
// token still valid | |
return { access_token: accessToken, expires_in: expiresIn, refresh_token: refreshToken, created_at: createdAt } | |
} | |
} else { | |
// user not logged in wih DRS, or DRS not enabled | |
throw Error('no token found') | |
} | |
} | |
module.exports = { | |
refreshAccessToken, | |
createToken, | |
createTokenFromSDK, | |
replenish, | |
sendDeviceStatus, | |
sendSlotStatus, | |
deregisterDRS, | |
cancelAllTestOrders, | |
cancelTestOrder, | |
getOrderInfo, | |
getSubscriptionInfo, | |
ensureTokenValid, | |
deregisterUserFromDRSIfHesRegistered | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment