Files
suite/website_sale_signifyd/models/signifyd_connector.py
2021-12-02 12:19:08 -08:00

169 lines
6.6 KiB
Python

# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import requests
from datetime import datetime as dt
from base64 import b64encode
import json
from odoo import api, fields, models
class SignifydConnector(models.Model):
_name = 'signifyd.connector'
_description = 'Interact with Signifyd API'
name = fields.Char(string='Connector Name', required=True)
test_mode = fields.Boolean(string='Test Mode')
secret_key = fields.Char(string='API Key', required=True)
secret_key_test = fields.Char(string='TEST API Key')
webhooks_registered = fields.Boolean(string='Successfully Registered Webhooks')
notify_user_ids = fields.Many2many('res.users', string='Receive decline notifications')
website_ids = fields.One2many('website', 'signifyd_connector_id', string='Used on Websites')
signifyd_case_type = fields.Selection([
('', 'No Case'),
('SCORE', 'Score'),
('DECISION', 'Decision'),
('GUARANTEE', 'Guarantee'),
], string='Default Case Creation', help='Used for internal/admin orders, overridden by payment acquirer.',
required=True, default='')
# TODO ideally this would be a regular constant
# however other entities currently use this by reference
API_URL = 'https://api.signifyd.com/v2'
def get_headers(self):
self.ensure_one()
# Check for prod or test mode
if self.test_mode:
api_key = self.secret_key_test
else:
api_key = self.secret_key
b64_auth_key = b64encode(api_key.encode()).decode().replace('=', '')
headers = {
'Authorization': 'Basic ' + b64_auth_key,
'Content-Type': 'application/json',
}
return headers
def register_webhooks(self):
self.ensure_one()
headers = self.get_headers()
# This should come from the website...
# we may need a better way to link the connector to the website.
base_url = None
website = self.env['website'].search([('signifyd_connector_id', '=', self.id)], limit=1)
if website and website.domain:
base_url = website.domain
if base_url.find('://') <= 0: # documentation says if no protocol use http
base_url = 'http://' + base_url
if not base_url:
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
values = {
"webhooks": [
# Given we are creating the cases, we do not need to know about it
# {
# "event": "CASE_CREATION",
# "url": base_url + "/signifyd/cases/update"
# },
{
"event": "CASE_RESCORE",
"url": base_url + "/signifyd/cases/update"
},
{
"event": "CASE_REVIEW",
"url": base_url + "/signifyd/cases/update"
},
{
"event": "GUARANTEE_COMPLETION",
"url": base_url + "/signifyd/cases/update"
},
{
"event": "DECISION_MADE",
"url": base_url + "/signifyd/cases/update"
},
]
}
data = json.dumps(values, indent=4)
r = requests.post(
self.API_URL + '/teams/webhooks',
headers=headers,
data=data,
)
# r.raise_for_status()
return r
def action_register_webhooks(self):
notification = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': ('Signifyd Connector'),
'sticky': True,
},
}
res = self.register_webhooks()
if 200 <= res.status_code < 300:
notification['params']['type'] = 'success'
notification['params']['message'] = 'Successfully registered webhooks with Signifyd.'
self.webhooks_registered = True
return notification
else:
notification['params']['type'] = 'danger'
notification['params']['message'] = res.content.decode('utf-8')
try:
# trying to make a better error, not be exhaustive with error handling.
object = json.loads(notification['params']['message'])
notification['params']['message'] = '\n'.join([e[0] for e in (object.get('errors') or {}).values()])
except:
pass
self.webhooks_registered = False
return notification
def process_post_values(self, post):
# Construct dict from request data for endpoints
uuid = post.get('uuid')
case_id = post.get('caseId')
team_name = post.get('teamName')
team_id = post.get('teamId')
review_disposition = post.get('reviewDisposition')
guarantee_disposition = post.get('guaranteeDisposition')
order_outcome = post.get('orderOutcome')
status = post.get('status')
score = post.get('score')
disposition_reason = post.get('dispositionReason')
disposition = post.get('disposition')
checkpoint_action = post.get('checkpointAction', '')
if not checkpoint_action and guarantee_disposition:
if guarantee_disposition == 'APPROVED':
checkpoint_action = 'ACCEPT'
elif guarantee_disposition == 'DECLINED':
checkpoint_action = 'REJECT'
else:
checkpoint_action = 'HOLD'
last_update = str(dt.now())
values = {}
# Validate that the order and case match the request
values.update({'uuid': uuid}) if uuid else ''
values.update({'team_name': team_name}) if team_name else ''
values.update({'team_id': team_id}) if team_id else ''
values.update({'review_disposition': review_disposition}) if review_disposition else ''
values.update({'guarantee_disposition': guarantee_disposition}) if guarantee_disposition else ''
values.update({'order_outcome': order_outcome}) if order_outcome else ''
values.update({'status': status}) if status else ''
values.update({'score': score}) if score else ''
values.update({'case_id': case_id}) if case_id else ''
values.update({'disposition_reason': disposition_reason}) if disposition_reason else ''
values.update({'disposition': disposition}) if disposition else ''
values.update({'last_update': last_update})
values.update({'checkpoint_action': checkpoint_action})
return values