feat: add pending manual compliance notifications

This commit is contained in:
siiky 2024-04-10 11:34:36 +01:00
parent 068f68e838
commit 3dbf10183e
4 changed files with 175 additions and 58 deletions

View file

@ -173,7 +173,8 @@ function complianceNotify (settings, customer, deviceId, action, period) {
const msgCore = { const msgCore = {
BLOCKED: `was blocked`, BLOCKED: `was blocked`,
SUSPENDED: `was suspended for ${!!period && period} days` SUSPENDED: `was suspended for ${!!period && period} days`,
PENDING_COMPLIANCE: `is waiting for your manual approval`,
} }
const rec = { const rec = {

View file

@ -38,7 +38,9 @@ const customerComplianceNotify = (customer, deviceId, code, days = null) => {
if (days) { if (days) {
date.setDate(date.getDate() + days) date.setDate(date.getDate() + days)
} }
const message = code === 'SUSPENDED' ? `Customer suspended until ${date.toLocaleString()}` : `Customer blocked` const message = code === 'SUSPENDED' ? `Customer ${customer.phone} suspended until ${date.toLocaleString()}` :
code === 'BLOCKED' ? `Customer ${customer.phone} blocked` :
`Customer ${customer.phone} has pending compliance`
return clearOldCustomerSuspendedNotifications(customer.id, deviceId) return clearOldCustomerSuspendedNotifications(customer.id, deviceId)
.then(() => queries.getValidNotifications(COMPLIANCE, detailB)) .then(() => queries.getValidNotifications(COMPLIANCE, detailB))

View file

@ -4,6 +4,7 @@ const semver = require('semver')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const { zonedTimeToUtc, utcToZonedTime } = require('date-fns-tz/fp') const { zonedTimeToUtc, utcToZonedTime } = require('date-fns-tz/fp')
const { add, intervalToDuration } = require('date-fns/fp') const { add, intervalToDuration } = require('date-fns/fp')
const uuid = require('uuid')
const sms = require('../sms') const sms = require('../sms')
const BN = require('../bn') const BN = require('../bn')
@ -25,15 +26,54 @@ const Tx = require('../tx')
const loyalty = require('../loyalty') const loyalty = require('../loyalty')
const logger = require('../logger') const logger = require('../logger')
function updateCustomerCustomInfoRequest (customerId, patch, req, res) { function updateCustomerCustomInfoRequest (customerId, patch) {
if (_.isNil(patch.data)) { const promise = _.isNil(patch.data) ?
return customers.getById(customerId) Promise.resolve(null) :
.then(customer => respond(req, res, { customer })) customInfoRequestQueries.setCustomerDataViaMachine(customerId, patch.infoRequestId, patch)
return promise.then(() => customers.getById(customerId))
}
const createPendingManualComplianceNotifs = (settings, customer, deviceId) => {
const customInfoRequests = _.reduce(
(reqs, req) => _.set(req.info_request_id, req, reqs),
{},
_.get(['customInfoRequestData'], customer)
)
const isPending = field =>
uuid.validate(field) ?
_.get([field, 'override'], customInfoRequests) === 'automatic' :
customer[`${field}At`]
&& (!customer[`${field}OverrideAt`]
|| customer[`${field}OverrideAt`].getTime() < customer[`${field}At`].getTime())
const unnestCustomTriggers = triggersAutomation => {
const customTriggers = _.fromPairs(_.map(({ id, type }) => [id, type], triggersAutomation.custom))
return _.flow(
_.unset('custom'),
_.mapKeys(k => k === 'facephoto' ? 'frontCamera' : k),
_.assign(customTriggers),
)(triggersAutomation)
} }
return customInfoRequestQueries.setCustomerDataViaMachine(customerId, patch.infoRequestId, patch) const isManual = v => v === 'Manual'
.then(() => customers.getById(customerId))
.then(customer => respond(req, res, { customer })) const hasManualAutomation = triggersAutomation =>
_.any(isManual, _.values(triggersAutomation))
configManager.getTriggersAutomation(customInfoRequestQueries.getCustomInfoRequests(true), settings.config)
.then(triggersAutomation => {
triggersAutomation = unnestCustomTriggers(triggersAutomation)
if (!hasManualAutomation(triggersAutomation)) return
const pendingFields = _.filter(
field => isManual(triggersAutomation[field]) && isPending(field),
_.keys(triggersAutomation)
)
if (!_.isEmpty(pendingFields))
notifier.complianceNotify(settings, customer, deviceId, 'PENDING_COMPLIANCE')
})
} }
function updateCustomer (req, res, next) { function updateCustomer (req, res, next) {
@ -44,32 +84,34 @@ function updateCustomer (req, res, next) {
const triggers = configManager.getTriggers(req.settings.config) const triggers = configManager.getTriggers(req.settings.config)
const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers) const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers)
const deviceId = req.deviceId const deviceId = req.deviceId
const settings = req.settings
if (patch.customRequestPatch) { if (patch.customRequestPatch) {
return updateCustomerCustomInfoRequest(id, patch.customRequestPatch, req, res).catch(next) return updateCustomerCustomInfoRequest(id, patch.customRequestPatch)
}
customers.getById(id)
.then(customer => { .then(customer => {
if (!customer) { throw httpError('Not Found', 404) } createPendingManualComplianceNotifs(settings, customer, deviceId)
respond(req, res, { customer })
const mergedCustomer = _.merge(customer, patch) })
.catch(next)
}
// BACKWARDS_COMPATIBILITY 7.5 // BACKWARDS_COMPATIBILITY 7.5
// machines before 7.5 expect customer with sanctions result // machines before 7.5 expect customer with sanctions result
const isOlderMachineVersion = !machineVersion || semver.lt(machineVersion, '7.5.0-beta.0') const isOlderMachineVersion = !machineVersion || semver.lt(machineVersion, '7.5.0-beta.0')
customers.getById(id)
return Promise.resolve({}) .then(customer =>
.then(emptyObj => { !customer ? Promise.reject(httpError('Not Found', 404)) :
if (!isOlderMachineVersion) return Promise.resolve(emptyObj) !isOlderMachineVersion ? {} :
return compliance.validationPatch(deviceId, !!compatTriggers.sanctions, mergedCustomer) compliance.validationPatch(deviceId, !!compatTriggers.sanctions, _.merge(customer, patch))
}) )
.then(_.merge(patch)) .then(_.merge(patch))
.then(newPatch => customers.updatePhotoCard(id, newPatch)) .then(newPatch => customers.updatePhotoCard(id, newPatch))
.then(newPatch => customers.updateFrontCamera(id, newPatch)) .then(newPatch => customers.updateFrontCamera(id, newPatch))
.then(newPatch => customers.update(id, newPatch, null, txId)) .then(newPatch => customers.update(id, newPatch, null, txId))
.then(customer => {
createPendingManualComplianceNotifs(settings, customer, deviceId)
respond(req, res, { customer })
}) })
.then(customer => respond(req, res, { customer }))
.catch(next) .catch(next)
} }

132
package-lock.json generated
View file

@ -770,6 +770,36 @@
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
}, },
"bitcoinjs-message": {
"version": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.2",
"resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-message/-/bitcoinjs-message-1.0.0-master.2.tgz",
"integrity": "sha512-XSDGM3rA75vcDxeKqHPexika/TgWUFWdfKTv1lV8TZTb5XFHHD6ARckLdMOBiCf29eZSzbJQvF/OIWqNqMl/2A==",
"requires": {
"bech32": "^1.1.3",
"bs58check": "^2.1.2",
"buffer-equals": "^1.0.3",
"create-hash": "^1.1.2",
"secp256k1": "5.0.0",
"varuint-bitcoin": "^1.0.1"
},
"dependencies": {
"bech32": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
},
"secp256k1": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz",
"integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==",
"requires": {
"elliptic": "^6.5.4",
"node-addon-api": "^5.0.0",
"node-gyp-build": "^4.2.0"
}
}
}
},
"ethereumjs-util": { "ethereumjs-util": {
"version": "7.1.5", "version": "7.1.5",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
@ -960,6 +990,38 @@
"secp256k1": "^4.0.2", "secp256k1": "^4.0.2",
"secrets.js-grempe": "^1.1.0", "secrets.js-grempe": "^1.1.0",
"superagent": "3.8.3" "superagent": "3.8.3"
},
"dependencies": {
"bech32": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
},
"bitcoinjs-message": {
"version": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.2",
"resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-message/-/bitcoinjs-message-1.0.0-master.2.tgz",
"integrity": "sha512-XSDGM3rA75vcDxeKqHPexika/TgWUFWdfKTv1lV8TZTb5XFHHD6ARckLdMOBiCf29eZSzbJQvF/OIWqNqMl/2A==",
"requires": {
"bech32": "^1.1.3",
"bs58check": "^2.1.2",
"buffer-equals": "^1.0.3",
"create-hash": "^1.1.2",
"secp256k1": "5.0.0",
"varuint-bitcoin": "^1.0.1"
},
"dependencies": {
"secp256k1": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz",
"integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==",
"requires": {
"elliptic": "^6.5.4",
"node-addon-api": "^5.0.0",
"node-gyp-build": "^4.2.0"
}
}
}
}
} }
}, },
"@bitgo/sdk-coin-bch": { "@bitgo/sdk-coin-bch": {
@ -1058,6 +1120,26 @@
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
}, },
"bitcoinjs-message": {
"version": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.2",
"resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-message/-/bitcoinjs-message-1.0.0-master.2.tgz",
"integrity": "sha512-XSDGM3rA75vcDxeKqHPexika/TgWUFWdfKTv1lV8TZTb5XFHHD6ARckLdMOBiCf29eZSzbJQvF/OIWqNqMl/2A==",
"requires": {
"bech32": "^1.1.3",
"bs58check": "^2.1.2",
"buffer-equals": "^1.0.3",
"create-hash": "^1.1.2",
"secp256k1": "5.0.0",
"varuint-bitcoin": "^1.0.1"
},
"dependencies": {
"bech32": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
}
}
},
"bs58": { "bs58": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
@ -1278,6 +1360,26 @@
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
}, },
"bitcoinjs-message": {
"version": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.2",
"resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-message/-/bitcoinjs-message-1.0.0-master.2.tgz",
"integrity": "sha512-XSDGM3rA75vcDxeKqHPexika/TgWUFWdfKTv1lV8TZTb5XFHHD6ARckLdMOBiCf29eZSzbJQvF/OIWqNqMl/2A==",
"requires": {
"bech32": "^1.1.3",
"bs58check": "^2.1.2",
"buffer-equals": "^1.0.3",
"create-hash": "^1.1.2",
"secp256k1": "5.0.0",
"varuint-bitcoin": "^1.0.1"
},
"dependencies": {
"bech32": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
}
}
},
"bs58": { "bs58": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
@ -4629,36 +4731,6 @@
"resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz", "resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz",
"integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==" "integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow=="
}, },
"bitcoinjs-message": {
"version": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.2",
"resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-message/-/bitcoinjs-message-1.0.0-master.2.tgz",
"integrity": "sha512-XSDGM3rA75vcDxeKqHPexika/TgWUFWdfKTv1lV8TZTb5XFHHD6ARckLdMOBiCf29eZSzbJQvF/OIWqNqMl/2A==",
"requires": {
"bech32": "^1.1.3",
"bs58check": "^2.1.2",
"buffer-equals": "^1.0.3",
"create-hash": "^1.1.2",
"secp256k1": "5.0.0",
"varuint-bitcoin": "^1.0.1"
},
"dependencies": {
"bech32": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
},
"secp256k1": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz",
"integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==",
"requires": {
"elliptic": "^6.5.4",
"node-addon-api": "^5.0.0",
"node-gyp-build": "^4.2.0"
}
}
}
},
"bitcore-lib": { "bitcore-lib": {
"version": "8.25.47", "version": "8.25.47",
"resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.25.47.tgz", "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.25.47.tgz",