diff --git a/dev/ofac-match.js b/dev/ofac-match.js new file mode 100644 index 00000000..6a0415bb --- /dev/null +++ b/dev/ofac-match.js @@ -0,0 +1,18 @@ +const compliance = require('../lib/compliance') +const ofac = require('../lib/ofac/index') + +const [firstName, lastName, dateOfBirth] = process.argv.slice(2) + +const customer = { + idCardData: {firstName, lastName, dateOfBirth} +} + +const config = { + sanctionsVerificationActive: true +} + +console.log('DEBUG100') +ofac.load() + .then(() => compliance.validateCustomer(config, customer)) + .then(() => console.log('SUCCESS!')) + .catch(err => console.log(err)) diff --git a/lib/app.js b/lib/app.js index dcbd6f59..b6dff7ed 100644 --- a/lib/app.js +++ b/lib/app.js @@ -33,8 +33,9 @@ function run () { } function runOnce () { + logger.info('Loading sanctions DB...') return ofac.load() - .then(settingsLoader.loadLatest()) + .then(settingsLoader.loadLatest) .then(settings => { poller.start(settings) diff --git a/lib/compliance.js b/lib/compliance.js index 4d6938d4..37867869 100644 --- a/lib/compliance.js +++ b/lib/compliance.js @@ -1,23 +1,64 @@ const _ = require('lodash/fp') +const logger = require('./logger') const ofac = require('./ofac/index') function matchOfac (customer) { - const nameParts = _.flatMap(_.split(/\s+/), [customer.firstName, customer.lastName]) - const birthDate = customer.dateOfBirth - - const result = ofac.match(nameParts, birthDate) - console.log('DEBUG200: %s', result) - - if (result > 0.8) throw new Error('Compliance error') -} - -function validateCustomer (config, customer) { - if (config.sanctionsVerificationActive) { - matchOfac(customer) + // Probably because we haven't asked for ID yet + if (!_.isPlainObject(customer.idCardData)) { + return true } - return customer + const nameParts = { + firstName: customer.idCardData.firstName, + lastName: customer.idCardData.lastName + } + + if (_.some(_.isNil, _.values(nameParts))) { + logger.error(new Error(`Insufficient idCardData while matching OFAC for: ${customer.id}`)) + return true + } + + const birthDate = customer.idCardData.dateOfBirth + + if (_.isNil(birthDate)) { + logger.error(new Error(`No birth date while matching OFAC for: ${customer.id}`)) + return true + } + + const options = { + threshold: 0.85, + fullNameThreshold: 0.95, + debug: false + } + + const results = ofac.match(nameParts, birthDate, options) + console.log('DEBUG200: %j', results) + + return !_.isEmpty(results) } -module.exports = {validateCustomer} +function validateOfac (customer) { + if (customer.sanctionsOverride === 'blocked') return false + if (customer.sanctionsOverride === 'verified') return true + + console.log('DEBUG400') + return !matchOfac(customer) +} + +function validationPatch (config, customer) { + return Promise.resolve() + .then(() => { + const ofacValidation = validateOfac(customer) + + console.log('DEBUG401: %s, %j', ofacValidation, customer) + + if (_.isNil(customer.sanctions) || customer.sanctions !== ofacValidation) { + return {sanctions: ofacValidation} + } + + return {} + }) +} + +module.exports = {validationPatch} diff --git a/lib/customers.js b/lib/customers.js index b201ff83..5ae0c013 100644 --- a/lib/customers.js +++ b/lib/customers.js @@ -23,6 +23,10 @@ const NUM_RESULTS = 20 function add (customer) { const sql = 'insert into customers (id, phone, phone_at) values ($1, $2, now()) returning *' return db.one(sql, [uuid.v4(), customer.phone]) + .then(populateOverrideUsernames) + .then(computeStatus) + .then(populateDailyVolume) + .then(camelize) } /** diff --git a/lib/routes.js b/lib/routes.js index bbe7a95b..fbb60302 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -18,6 +18,7 @@ const Tx = require('./tx') const E = require('./error') const customers = require('./customers') const logs = require('./logs') +const compliance = require('./compliance') const argv = require('minimist')(process.argv.slice(2)) @@ -164,17 +165,33 @@ function verifyTx (req, res, next) { .catch(next) } +function addOrUpdateCustomer (req) { + const customerData = req.body + const config = configManager.unscoped(req.settings.config) + + return customers.get(customerData.phone) + .then(customer => { + if (customer) return customer + + return customers.add(req.body) + }) + .then(customer => { + return compliance.validationPatch(config, customer) + .then(patch => { + if (_.isEmpty(patch)) return customer + return customers.update(customer.id, patch) + }) + }) +} + function getCustomerWithPhoneCode (req, res, next) { const pi = plugins(req.settings, req.deviceId) const phone = req.body.phone + return pi.getPhoneCode(phone) .then(code => { - return customers.get(phone) - .then(customer => { - if (customer) return respond(req, res, {code, customer}) - return customers.add(req.body) - .then(customer => respond(req, res, {code, customer})) - }) + return addOrUpdateCustomer(req) + .then(customer => respond(req, res, {code, customer})) }) .catch(err => { if (err.name === 'BadNumberError') throw httpError('Bad number', 401) @@ -191,9 +208,12 @@ function updateCustomer (req, res, next) { customers.getById(id) .then(customer => { if (!customer) { throw httpError('Not Found', 404) } - return customers.update(id, patch) + + const mergedCustomer = _.merge(customer, patch) + return compliance.validationPatch(config, mergedCustomer) + .then(_.merge(patch)) + .then(newPatch => customers.update(id, newPatch)) }) - .then(customer => customers.validate(config, customer)) .then(customer => respond(req, res, {customer})) .catch(next) } @@ -295,9 +315,8 @@ const skip = (req, res) => _.includes(req.path, ['/poll', '/state', '/logs']) && const configRequiredRoutes = [ '/poll', '/event', - '/verify_user', - '/verify_transaction', '/phone_code', + '/customer', '/tx' ]