yield-siftscience
Version:
Promise-wrapped Sift Science lib for yielding. Also supports regular callback functions.
676 lines (547 loc) • 19.4 kB
Markdown
# Yield Sift Science - NodeJS
A promise-wrapped helper lib for yielding Sift Science API calls in nodejs.
Also supports regular [callbacks](#callbacks).
#### Table of contents:
- [Installation](#installation)
- [Usage](#usage)
- [Events API](#events-api)
- [Labels API](#labels-api)
- [Decision API](#decision-api)
- [Workflow API](#workflow-api)
- [Score API](#score-api)
- [Device Fingerprinting API](#device-fingerprinting-api)
- [Partner API](#partner-api)
- [Callbacks](#callbacks)
- [Constants](#constants)
- [Webhooks](#webhooks)
- [Sift Science Documentation](https://siftscience.com/docs)
- [Testing](#testing)
- [Change Log](#change-log)
## INSTALLATION
#### Using [npm](https://npmjs.org):
```bash
$ npm install yield-siftscience
```
## USAGE
#### Require with [API Key](https://siftscience.com/console/developer/api-keys):
```js
var siftscience = require('yield-siftscience')({
api_key: 'YOUR_SIFT_SCIENCE_REST_API_KEY'
});
```
#### Available Options:
- {string} **api_key** ([get your api key](https://siftscience.com/console/developer/api-keys))
- {string} **[account_id]** (required for [device fingerprinting api](#device-fingerprinting-api), [get your account id](https://siftscience.com/console/account/profile))
- {string} **[partner_id]** (required for [partner api](#partner-api), [get your partner id](https://siftscience.com/console/account/profile))
- {string[]} **[custom_events]** (ex: `['referral_code_redeemed', 'contacted_customer_support', ...]`)
- {function} **[global_callback]** (ex: `function(err, response) { ... }` - can be used to override promise and make regular callback on all requests)
- {string[]} **[abuse_types]** (default: `[]` - specify an array of sift science products. This parameter restricts the list of score or workflow decision retrieved to the specific products requested. [more info](https://siftscience.com/developers/docs/curl/decisions-api/decision-status). Possible values: Array with one or more of - `['payment_abuse','promo_abuse','content_abuse','account_abuse','legacy']`)
- {boolean} **[return_score]** (default: `false` - can be used to return score from sift science synchronously [more info](https://siftscience.com/developers/docs/curl/score-api/synchronous-scores))
- {boolean} **[return_workflow_status]** (default: `false` - can be used to return workflow status from sift science synchronously [more info](https://siftscience.com/developers/docs/curl/workflows-api/workflow-decisions))
- {object} **[webhooks]** (default: `{}` - see [webhooks](#webhooks) for usage)
- {boolean} **[return_action]** *DEPRECATED* (default: `false` - can be used to get extra params from sift science responses [more info](https://siftscience.com/resources/tutorials/formulas#add-actions))
**Note:** In v204 of the sift science API, return_action is deprecated in favor of the more granular combined use of abuse_types, return_score and return_workflow_status flags. [more info](https://siftscience.com/resources/tutorials/formulas)
## EVENTS API
[https://siftscience.com/developers/docs/curl/events-api](https://siftscience.com/developers/docs/curl/events-api)
#### Send Event:
```js
var result = yield siftscience.event.create_account({
'$session_id': session.id,
'$user_id': user.id,
'$user_email': user.email
});
```
```js
var result = yield siftscience.event.login({
'$session_id': session.id,
'$user_id': user.id,
'$login_status': siftscience.CONSTANTS.STATUS.SUCCESS
});
```
#### Send Generic Custom Event:
```js
var result = yield siftscience.event.custom_event('referral_code_redeemed', {
'$session_id': session.id,
'$user_id': user.id,
'code': 'abc123'
});
```
```js
var result = yield siftscience.event.custom_event('contacted_customer_support', {
'$session_id': session.id,
'$user_id': user.id
});
```
#### Inject Custom Events:
Optionally, you can pass in an array of custom event names to add to the lib
```js
var siftscience = require('yield-siftscience')({
api_key: 'YOUR_SIFT_SCIENCE_REST_API_KEY',
custom_events: ['referral_code_redeemed', 'contacted_customer_support', ...]
});
```
Then you could use
```js
var result = yield siftscience.event.referral_code_redeemed({
'$session_id': session.id,
'$user_id': user.id,
'code': 'abc123'
});
```
```js
var result = yield siftscience.event.contacted_customer_support({
'$session_id': session.id,
'$user_id': user.id,
});
```
## LABELS API
[https://siftscience.com/developers/docs/curl/labels-api](https://siftscience.com/developers/docs/curl/labels-api)
#### Send Label:
```js
var result = yield siftscience.label(user.id, {
'$is_bad': true,
'$abuse_type': siftscience.CONSTANTS.ABUSE_TYPE.PAYMENT_ABUSE,
'$description': 'Because they are spamming and abusing our system',
'$source': 'manual review',
'$analyst': 'admin@example.com'
});
```
#### Remove Label:
```js
var result = yield siftscience.unlabel(user.id);
```
## DECISION API
[https://siftscience.com/developers/docs/curl/decisions-api](https://siftscience.com/developers/docs/curl/decisions-api)
#### Get decision status:
```js
var result = yield siftscience.decision.status(siftscience.CONSTANTS.ENTITY_TYPE.USERS, entity.id)
```
#### List decisions:
```js
var result = yield siftscience.decision.list(siftscience.CONSTANTS.ENTITY_TYPE.USER)
```
#### Apply decision to user:
```js
var result = yield siftscience.decision.apply(user.id, null, {
'decision_id': 'user_looks_ok_payment_abuse',
'source': siftscience.CONSTANTS.DECISION_SOURCE.MANUAL_REVIEW,
'analyst': 'analyst@email.com',
'description': 'applied via the high priority queue, queued user because their risk score exceeded 85'
})
```
#### Apply decision to order:
```js
var result = yield siftscience.decision.apply(user.id, order.id, {
'decision_id': 'user_looks_ok_payment_abuse',
'source': siftscience.CONSTANTS.DECISION_SOURCE.AUTOMATED_RULE,
'description': 'Auto block pending order as score exceeded risk threshold of 90'
})
```
## WORKFLOW API
[https://siftscience.com/developers/docs/curl/workflows-api](https://siftscience.com/developers/docs/curl/workflows-api)
#### Get workflow status:
```js
var result = yield siftscience.workflow.status(workflow.id)
```
## SCORE API
[https://siftscience.com/developers/docs/curl/score-api](https://siftscience.com/developers/docs/curl/score-apii)
#### Get Score:
```js
var result = yield siftscience.score(user.id);
```
## DEVICE FINGERPRINTING API
[https://siftscience.com/developers/docs/curl/device-fingerprinting-api](https://siftscience.com/developers/docs/curl/device-fingerprinting-api)
#### JavaScript Snippet:
[https://siftscience.com/developers/docs/javascript/javascript-api](https://siftscience.com/developers/docs/javascript/javascript-api)
Install the following JavaScript snippet on every public-facing page on your site. Do not include this snippet on internal tools or administration systems.
Replace `'UNIQUE_SESSION_ID'`, `'UNIQUE_USER_ID'`, and `'INSERT_JS_SNIPPET_KEY_HERE'` with proper values
```html
<script type="text/javascript">
var _user_id = 'UNIQUE_USER_ID';
var _session_id = 'UNIQUE_SESSION_ID';
var _sift = window._sift = window._sift || [];
_sift.push(['_setAccount', 'INSERT_JS_SNIPPET_KEY_HERE']);
_sift.push(['_setUserId', _user_id]);
_sift.push(['_setSessionId', _session_id]);
_sift.push(['_trackPageview']);
(function() {
function ls() {
var e = document.createElement('script');
e.src = 'https://cdn.siftscience.com/s.js';
document.body.appendChild(e);
}
if (window.attachEvent) {
window.attachEvent('onload', ls);
} else {
window.addEventListener('load', ls, false);
}
})();
</script>
```
#### Init with Account ID:
An Account ID is required to use the fingerprint api. [Get your Account ID](https://siftscience.com/console/account/profile)
```js
var siftscience = require('yield-siftscience')({
api_key: 'YOUR_SIFT_SCIENCE_REST_API_KEY',
account_id: 'YOUR_SIFT_SCIENCE_ACCOUNT_ID'
});
```
#### Get Session:
```js
var result = yield siftscience.fingerprint.get_session(session.id);
```
#### Get Device:
```js
var result = yield siftscience.fingerprint.get_device(device_fingerprint);
```
#### Label Device:
```js
var result = yield siftscience.fingerprint.label_device(device_fingerprint, siftscience.CONSTANTS.DEVICE_LABEL.BAD);
```
#### Get Devices:
```js
var result = yield siftscience.fingerprint.get_devices(user.id);
```
## PARTNER API
[https://siftscience.com/developers/docs/curl/partner-api](https://siftscience.com/developers/docs/curl/partner-api)
**NOTE:** I have not tested these as I do not have a partner account with sift science. Please report any bugs.
#### Init with Account ID and Partner ID (they may be the same thing, I'm not sure):
An Account & Partner ID are required to use the partner api. [Get your Account & Partner ID](https://siftscience.com/console/account/profile)
```js
var siftscience = require('yield-siftscience')({
api_key: 'YOUR_SIFT_SCIENCE_REST_API_KEY',
account_id: 'YOUR_SIFT_SCIENCE_ACCOUNT_ID',
partner_id: 'YOUR_SIFT_SCIENCE_PARTNER_ID'
});
```
#### Create Account:
```js
var result = yield siftscience.partner.create_account({
site_url: 'merchant123.com',
site_email: 'owner@merchant123.com',
analyst_email: 'john.doe@merchant123.com',
password: 's0mepA55word'
});
```
#### List Accounts:
```js
var result = yield siftscience.partner.list_accounts();
```
#### Configure Notifications:
```js
var result = yield siftscience.partner.configure_notifications({
email_notification_threshold: 0.5,
http_notification_threshold: 0.5,
http_notification_url: 'https://api.partner.com/notify?account=%s'
});
```
## CALLBACKS
#### Don't know what yielding or promising is? All calls support regular callbacks:
```js
siftscience.score(user.id, function(_err, _response) {
if (_err) {
console.log(_err);
}
else {
var score = _response.body;
console.log(score);
}
});
```
#### You can also inject a global callback for all requests:
```js
var siftscience = require('yield-siftscience')({
api_key: 'YOUR_SIFT_SCIENCE_REST_API_KEY',
global_callback: function(_err, _response) {
if (_err) {
console.log(_err);
}
else {
var result = _response.body;
console.log(result);
}
}
});
```
## CONSTANTS
```js
siftscience.CONSTANTS = {
SHIPPING_METHOD: {
ELECTRONIC: '$electronic',
PHYSICAL: '$physical'
},
TRANSACTION_TYPE: {
SALE: '$sale',
AUTHORIZE: '$authorize',
CAPTURE: '$capture',
VOID: '$void',
REFUND: '$refund',
DEPOSIT: '$deposit',
WITHDRAWAL: '$withdrawal',
TRANSFER: '$transfer'
},
STATUS: {
SUCCESS: '$success',
FAILURE: '$failure',
PENDING: '$pending'
},
FAILURE_REASON: {
ALREADY_USED: '$already_used',
INVALID_CODE: '$invalid_code',
NOT_APPLICABLE: '$not_applicable',
EXPIRED: '$expired'
},
SOCIAL_SIGN_ON_TYPE: {
FACEBOOK: '$facebook',
GOOGLE: '$google',
YAHOO: '$yahoo',
TWITTER: '$twitter',
OTHER: '$other',
LINKEDIN: '$linkedin'
},
CONTENT_STATUS: {
DRAFT: '$draft',
PENDING: '$pending',
ACTIVE: '$active',
PAUSED: '$paused',
DELETED_BY_USER: '$deleted_by_user',
DELETED_BY_COMPANY: '$deleted_by_company'
},
CHARGEBACK_STATE: {
RECEIVED: '$received',
ACCEPTED: '$accepted',
DISPUTED: '$disputed',
WON: '$won',
LOST: '$lost'
},
CHARGEBACK_REASON: {
FRAUD: '$fraud',
DUPLICATE: '$duplicate',
PRODUCT_NOT_RECEIVED: '$product_not_received',
PRODUCT_UNACCEPTABLE: '$product_unacceptable',
OTHER: '$other'
},
ORDER_STATUS: {
APPROVED: '$approved',
CANCELED: '$canceled',
HELD: '$held',
FULFILLED: '$fulfilled',
RETURNED: '$returned'
},
ORDER_CANCEL_REASON: {
PAYMENT_RISK: '$payment_risk',
ABUSE: '$abuse',
POLICY: '$policy',
OTHER: '$other'
},
ORDER_STATUS_SOURCE: {
AUTOMATED: '$automated',
MANUAL_REVIEW: '$manual_review'
},
VERIFICATION_TYPE: {
SMS: '$sms',
PHONE_CALL: '$phone_call',
EMAIL: '$email',
APP_TFA: '$app_tfa',
CAPTCHA: '$captcha'
},
PAYMENT_TYPE: {
CASH: '$cash',
CHECK: '$check',
CREDIT_CARD: '$credit_card',
CRYPTO_CURRENCY: '$crypto_currency',
DIGITAL_WALLET: '$digital_wallet',
ELECTRONIC_FUND_TRANSFER: '$electronic_fund_transfer',
FINANCING: '$financing',
GIFT_CARD: '$gift_card',
INTERAC: '$interac', // Deprecated?
INVOICE: '$invoice',
MONEY_ORDER: '$money_order',
MASTERPASS: '$masterpass', // Deprecated?
POINTS: '$points',
STORE_CREDIT: '$store_credit',
THIRD_PARTY_PROCESSOR: '$third_party_processor',
VOUCHER: '$voucher'
},
RESPONSE_STATUS_MESSAGE: {
'-4': 'Service currently unavailable. Please try again later.',
'-3': 'Server-side timeout processing request. Please try again later.',
'-2': 'Unexpected server-side error',
'-1': 'Unexpected server-side error',
'0': 'Success',
'51': 'Invalid API key',
'52': 'Invalid characters in field name',
'53': 'Invalid characters in field value',
'54': 'Specified user_id has no scoreable events',
'55': 'Missing required field',
'56': 'Invalid JSON in request',
'57': 'Invalid HTTP body',
'60': 'Rate limited',
'104': 'Invalid API version',
'105': 'Not a valid reserved field',
'111': 'This feature is not enabled in your feature plan.'
},
REASON: {
CHARGEBACK: '$chargeback',
SPAM: '$spam',
FUNNELING: '$funneling',
FAKE: '$fake',
REFERRAL: '$referral',
DUPLICATE_ACCOUNT: '$duplicate_account'
},
DEVICE_LABEL: {
BAD: 'bad',
NOT_BAD: 'not_bad'
},
DEVICE_PERVASIVENESS: {
LOW: 'low',
MEDIUM: 'medium',
HIGH: 'high'
},
ABUSE_TYPE: {
PAYMENT_ABUSE: 'payment_abuse',
CONTENT_ABUSE: 'content_abuse',
PROMOTION_ABUSE: 'promotion_abuse',
PROMO_ABUSE: 'promo_abuse',
ACCOUNT_ABUSE: 'account_abuse',
LEGACY: 'legacy'
},
DECISION: {
PAYMENT_ABUSE: 'payment_abuse',
PROMOTION_ABUSE: 'promotion_abuse',
PROMO_ABUSE: 'promo_abuse',
CONTENT_ABUSE: 'content_abuse',
ACCOUNT_ABUSE: 'account_abuse',
LEGACY: 'legacy'
},
DECISION_CATEGORY: {
BLOCK: 'BLOCK',
WATCH: 'WATCH',
ACCEPT: 'ACCEPT'
},
DECISION_SOURCE: {
MANUAL_REVIEW: 'MANUAL_REVIEW',
AUTOMATED_RULE: 'AUTOMATED_RULE',
CHARGEBACK: 'CHARGEBACK'
},
STATE: {
RUNNING: 'running',
FINISHED: 'finished',
FAILED: 'failed'
},
ENTITY_TYPE: {
USERS: 'users',
ORDERS: 'orders',
USER: 'user',
ORDER: 'order'
},
APP: {
DECISION: 'decision',
REVIEW_QUEUE: 'review_queue',
USER_SCORER: 'user_scorer',
ORDER_SCORER: 'order_scorer',
EVENT_PROCESSOR: 'event_processor'
},
ACCOUNT_STATE: {
ACTIVE: 'ACTIVE',
DISABLED: 'DISABLED',
DELETED: 'DELETED'
}
};
```
## WEBHOOKS
**NOTE:** Currently only supports express/body-parser
#### Install Express/Body-Parser:
```bash
$ npm install --save express
$ npm install --save body-parser
$ npm install --save yield-siftscience
```
#### Example App:
Let's say you have [created an action](https://siftscience.com/console/actions/users) called "Test" with Action ID `test`
```js
var express = require('express');
var bodyParser = require('body-parser');
// Require yield-siftscience with a webhooks mapping option
var siftscience = require('yield-siftscience')({
api_key: 'YOUR_SIFT_SCIENCE_REST_API_KEY',
webhooks: {
// This will receive all webhooks, regardless of Action ID
all: function(req, res, done) {
console.log('all: ', req.body);
done();
},
// This will receive webhooks with Action ID 'test'
test: function(req, res, done) {
console.log('test: ', req.body);
done();
}
}
});
// Set up the webhook listener
var app = express();
app.post('/siftscience', bodyParser.json(), siftscience.webhook.express());
app.listen(config.port);
```
## SIFT SCIENCE DOCUMENTATION
[https://siftscience.com/developers/docs/curl/apis-overview](https://siftscience.com/developers/docs/curl/apis-overview)
## TESTING
#### Copy Example Config:
```bash
$ cp test/config-example.js test/config.js
$ nano test/config.js
```
#### Set Sandbox [API Key](https://siftscience.com/console/developer/api-keys), [JS Key](https://siftscience.com/console/developer/api-keys), [Account ID](https://siftscience.com/console/account/profile), [Workflow Run ID](https://siftscience.com/console/developer/workflow-logs)
```js
module.exports = {
api_key: 'xxxxxxxxxxxxxxxx',
js_key: 'xxxxxxxxxx',
account_id: 'xxxxxxxxxxxxxxxxxxxxxxxx',
workflow_run_id: 'xxxxxxxxxxxxx',
host: 'localhost',
port: 3000
};
```
#### Install Dependencies:
```bash
$ npm install
```
#### Run Test:
```bash
$ npm test
```
#### Visit Page:
Visiting the test web page will trigger a page view for user_id = '1' and session_id = '1'
```
http://localhost:3000
```
**NOTE:** You will have to run the test a second time if this is your first time visiting the test web page
## CHANGE LOG
#### 0.2.1:
- Add support for decision list api
- Add support for apply decision api
- Add many missing `CONSTANTS`
- Add `$verification` event
#### 0.2.0:
- Update to API v204
- **BREAKING CHANGE:** `siftscience.unlabel()` function signature has changed to support abuse type
- **BREAKING CHANGE:** `siftscience.score()` function signature has changed to support abuse types
- Add support for decision status api
- Add support for workflow status api
#### 0.1.2:
- Add validations for all arguments
- Update readme with more links to source reference
#### 0.1.1:
- Found the `return_action` documentation - [MORE INFO](https://siftscience.com/resources/tutorials/formulas#add-actions)
- Remove support for api version option - will only be supporting the most current version `v203`
- Add validations for all init options
#### 0.1.0:
- **BREAKING CHANGE:** Consolidate init args into one `options` arg - see [USAGE](#usage)
- Add support for `return_action` in init options - [MORE INFO](https://siftscience.com/resources/tutorials/formulas#add-actions)
- Add support for device fingerprinting api
- Add support for partner api - **NOTE:** I do not have a partner account with sift science, this is untested. Please report any bugs.
- Add `CONSTANTS` object to `siftscience` object for things like `$reasons` and `$shipping_method` - see [LABELS API](#labels-api)
- Add express/body-parser webhook support
- Add a minimal test package
#### 0.0.9:
- Add `unlabel` method to `siftscience` object