UNPKG

yield-siftscience

Version:

Promise-wrapped Sift Science lib for yielding. Also supports regular callback functions.

676 lines (547 loc) 19.4 kB
# 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