mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[WIP] signifyd v3: working webhooks
This commit is contained in:
@@ -13,16 +13,20 @@ class SignifydWebhooks(Controller):
|
|||||||
|
|
||||||
def _case_update(self):
|
def _case_update(self):
|
||||||
data = json.loads(request.httprequest.data)
|
data = json.loads(request.httprequest.data)
|
||||||
vals = request.env['signifyd.connector'].process_post_values(data)
|
|
||||||
case_id = vals.get('case_id')
|
case_id = data.get('signifydId')
|
||||||
|
if not case_id:
|
||||||
|
# Testing webhook
|
||||||
|
return {'response': 'success'}
|
||||||
|
|
||||||
case = self._get_case(case_id)
|
case = self._get_case(case_id)
|
||||||
if case:
|
if not case:
|
||||||
case.update_case_info(vals)
|
raise NotFound('CaseId: %s Cannot be found.' % (case_id,))
|
||||||
return {'response': 'success'}
|
|
||||||
if case_id == 1:
|
case.connector_id._check_webhook_signature(request)
|
||||||
# Special case when verifying webhook.
|
|
||||||
return {'response': 'success'}
|
case.update_case_info(data)
|
||||||
raise NotFound('CaseId: %s Cannot be found.' % (case_id,))
|
return {'response': 'success'}
|
||||||
|
|
||||||
def _get_case(self, case_id):
|
def _get_case(self, case_id):
|
||||||
return request.env['signifyd.case'].sudo().search([('case_id', '=', case_id)], limit=1)
|
return request.env['signifyd.case'].sudo().search([('case_id', '=', case_id)], limit=1)
|
||||||
|
|||||||
@@ -73,32 +73,14 @@ class SignifydCase(models.Model):
|
|||||||
subtype_xmlid='website_sale_signifyd.disposition_change')
|
subtype_xmlid='website_sale_signifyd.disposition_change')
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# @api.model
|
def get_decision_vals(self, data=None):
|
||||||
# def post_case(self, connector, values):
|
|
||||||
# headers = connector.get_headers()
|
|
||||||
# # data = json.dumps(values, indent=4, sort_keys=True, default=str)
|
|
||||||
# url = connector.API_URL + '/orders/events/sales'
|
|
||||||
# # TODO this should be in `signifyd.connector`
|
|
||||||
# r = requests.post(url=url, headers=headers, json=values)
|
|
||||||
# return r.json()
|
|
||||||
|
|
||||||
# def get_case(self):
|
|
||||||
# self.ensure_one()
|
|
||||||
# if not self.case_id:
|
|
||||||
# raise UserError(_('Case not represented in Signifyd.'))
|
|
||||||
# connector = self._get_connector()
|
|
||||||
# headers = connector.get_headers()
|
|
||||||
# r = requests.get(
|
|
||||||
# connector.API_URL + '/cases/' + str(self.case_id),
|
|
||||||
# headers=headers
|
|
||||||
# )
|
|
||||||
# return r.json()
|
|
||||||
def get_decision_vals(self):
|
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
api = self.connector_id.get_connection()
|
|
||||||
response = api.get_decision(self.ref)
|
if not data:
|
||||||
response.raise_for_status()
|
api = self.connector_id.get_connection()
|
||||||
data = response.json()
|
response = api.get_decision(self.ref)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
decision = data['decision']
|
decision = data['decision']
|
||||||
case_vals = {
|
case_vals = {
|
||||||
@@ -116,21 +98,10 @@ class SignifydCase(models.Model):
|
|||||||
'snadChargebacks': self.env.ref('website_sale_signifyd.signifyd_coverage_snad').id,
|
'snadChargebacks': self.env.ref('website_sale_signifyd.signifyd_coverage_snad').id,
|
||||||
'allChargebacks': self.env.ref('website_sale_signifyd.signifyd_coverage_all').id
|
'allChargebacks': self.env.ref('website_sale_signifyd.signifyd_coverage_all').id
|
||||||
}
|
}
|
||||||
# coverage_ids = []
|
|
||||||
# if coverage.get('inrChargebacks'):
|
|
||||||
# coverage_ids += self.env.ref('website_sale_signifyd.signifyd_coverage_inr').ids
|
|
||||||
# if coverage.get('fraudChargebacks'):
|
|
||||||
# coverage_ids += self.env.ref('website_sale_signifyd.signifyd_coverage_fraud').ids
|
|
||||||
# if coverage.get('snadChargebacks'):
|
|
||||||
# coverage_ids += self.env.ref('website_sale_signifyd.signifyd_coverage_snad').ids
|
|
||||||
# if coverage.get('allChargebacks'):
|
|
||||||
# coverage_ids = self.env.ref('website_sale_signifyd.signifyd_coverage_all').ids
|
|
||||||
# vals['coverage_ids'] = coverage_ids
|
|
||||||
for name, vals in coverage.items():
|
for name, vals in coverage.items():
|
||||||
if not vals:
|
if not vals:
|
||||||
continue
|
continue
|
||||||
case_vals['coverage_line_ids'] += [(0, 0, {
|
case_vals['coverage_line_ids'] += [(0, 0, {
|
||||||
# 'case_id': self.id,
|
|
||||||
'coverage_type_id': coverage_map[name],
|
'coverage_type_id': coverage_map[name],
|
||||||
'amount': vals.get('amount'),
|
'amount': vals.get('amount'),
|
||||||
'currency_id': self.env['res.currency'].search(
|
'currency_id': self.env['res.currency'].search(
|
||||||
@@ -143,47 +114,12 @@ class SignifydCase(models.Model):
|
|||||||
for record in self:
|
for record in self:
|
||||||
record.update_case_info()
|
record.update_case_info()
|
||||||
|
|
||||||
def update_case_info(self, vals=None):
|
def update_case_info(self, data=None):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
if not self.case_id:
|
if not self.case_id:
|
||||||
raise UserError(_('Case not represented in Signifyd.'))
|
raise UserError(_('Case not represented in Signifyd.'))
|
||||||
if not vals:
|
|
||||||
vals = self.get_decision_vals()
|
|
||||||
# case_id = case.get('caseId')
|
|
||||||
# if not case_id:
|
|
||||||
# raise ValueError(_('Signifyd Case has no ID?'))
|
|
||||||
# team_id = case.get('teamId', self.team_id)
|
|
||||||
# team_name = case.get('teamName', self.team_name)
|
|
||||||
# ref = case.get('ref', self.ref)
|
|
||||||
# status = case.get('status', self.status)
|
|
||||||
# review_disposition = case.get('reviewDisposition', self.review_disposition)
|
|
||||||
# order_outcome = case.get('orderOutcome', self.order_outcome)
|
|
||||||
# guarantee_disposition = case.get('guaranteeDisposition', self.guarantee_disposition)
|
|
||||||
# adjusted_score = case.get('adjustedScore', self.adjusted_score)
|
|
||||||
# score = case.get('score', self.score)
|
|
||||||
# checkpoint_action = case.get('checkpointAction', self.checkpoint_action)
|
|
||||||
# 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'
|
|
||||||
|
|
||||||
# vals = {
|
vals = self.get_decision_vals(data)
|
||||||
# 'case_id': case_id,
|
|
||||||
# 'team_id': team_id,
|
|
||||||
# 'team_name': team_name,
|
|
||||||
# 'ref': ref,
|
|
||||||
# 'status': status,
|
|
||||||
# 'review_disposition': review_disposition,
|
|
||||||
# 'order_outcome': order_outcome,
|
|
||||||
# 'adjusted_score': adjusted_score,
|
|
||||||
# 'guarantee_disposition': guarantee_disposition,
|
|
||||||
# 'score': score,
|
|
||||||
# 'last_update': dt.now(), # why not just use
|
|
||||||
# 'checkpoint_action': checkpoint_action,
|
|
||||||
# }
|
|
||||||
|
|
||||||
outcome = vals.get('guarantee_disposition')
|
outcome = vals.get('guarantee_disposition')
|
||||||
checkpoint_action = vals.get('checkpoint_action')
|
checkpoint_action = vals.get('checkpoint_action')
|
||||||
|
|||||||
@@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
from datetime import datetime as dt
|
from datetime import datetime as dt
|
||||||
from base64 import b64encode
|
from base64 import b64encode, b64decode
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
from .signifyd_api import SignifydAPI
|
from .signifyd_api import SignifydAPI
|
||||||
|
|
||||||
@@ -14,9 +17,7 @@ class SignifydConnector(models.Model):
|
|||||||
_description = 'Interact with Signifyd API'
|
_description = 'Interact with Signifyd API'
|
||||||
|
|
||||||
name = fields.Char(string='Connector Name', required=True)
|
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 = fields.Char(string='API Key', required=True)
|
||||||
secret_key_test = fields.Char(string='TEST API Key')
|
|
||||||
webhooks_registered = fields.Boolean(string='Successfully Registered Webhooks')
|
webhooks_registered = fields.Boolean(string='Successfully Registered Webhooks')
|
||||||
notify_user_ids = fields.Many2many('res.users', string='Receive decline notifications')
|
notify_user_ids = fields.Many2many('res.users', string='Receive decline notifications')
|
||||||
website_ids = fields.One2many('website', 'signifyd_connector_id', string='Used on Websites')
|
website_ids = fields.One2many('website', 'signifyd_connector_id', string='Used on Websites')
|
||||||
@@ -88,6 +89,16 @@ class SignifydConnector(models.Model):
|
|||||||
self.webhooks_registered = False
|
self.webhooks_registered = False
|
||||||
return notification
|
return notification
|
||||||
|
|
||||||
|
def _check_webhook_signature(self, request):
|
||||||
|
signature = b64decode(request.httprequest.headers.get('SIGNIFYD-SEC-HMAC-SHA256'))
|
||||||
|
if not signature:
|
||||||
|
raise Exception('Signifyd webhook missing signature')
|
||||||
|
|
||||||
|
key = self.secret_key.encode()
|
||||||
|
digest = hmac.new(key, request.httprequest.data, hashlib.sha256).digest()
|
||||||
|
if signature != digest:
|
||||||
|
raise Exception('Signifyd webhook signature does not match')
|
||||||
|
|
||||||
def process_post_values(self, post):
|
def process_post_values(self, post):
|
||||||
# Construct dict from request data for endpoints
|
# Construct dict from request data for endpoints
|
||||||
uuid = post.get('uuid')
|
uuid = post.get('uuid')
|
||||||
|
|||||||
Reference in New Issue
Block a user