support sanctions compliance
This commit is contained in:
parent
80e851fb59
commit
d904c8391e
5 changed files with 108 additions and 25 deletions
18
dev/ofac-match.js
Normal file
18
dev/ofac-match.js
Normal file
|
|
@ -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))
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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,18 +165,34 @@ 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)
|
||||
return addOrUpdateCustomer(req)
|
||||
.then(customer => respond(req, res, {code, customer}))
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.name === 'BadNumberError') throw httpError('Bad number', 401)
|
||||
throw err
|
||||
|
|
@ -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'
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue