pos_viva_wallet/models/pos_payment_method.py

208 lines
9.7 KiB
Python
Raw Permalink Normal View History

# coding: utf-8
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import requests
from odoo import fields, models, api, tools, _
from odoo.exceptions import UserError, AccessError
_logger = logging.getLogger(__name__)
TIMEOUT = 10
class PosPaymentMethod(models.Model):
_inherit = 'pos.payment.method'
# Viva Wallet
viva_wallet_merchant_id = fields.Char(string="Merchant ID", help='Used when connecting to Viva Wallet: https://developer.vivawallet.com/getting-started/find-your-account-credentials/merchant-id-and-api-key/')
viva_wallet_api_key = fields.Char(string="API Key", help='Used when connecting to Viva Wallet: https://developer.vivawallet.com/getting-started/find-your-account-credentials/merchant-id-and-api-key/')
viva_wallet_client_id = fields.Char(string="Client ID", help='Used when connecting to Viva Wallet: https://developer.vivawallet.com/getting-started/find-your-account-credentials/pos-apis-credentials/#find-your-pos-apis-credentials')
viva_wallet_client_secret = fields.Char(string="Client secret")
viva_wallet_terminal_id = fields.Char(string="Terminal ID", help='[Terminal ID of the Viva Wallet terminal], for example: 16002169')
viva_wallet_bearer_token = fields.Char(default='Bearer Token')
viva_wallet_webhook_verification_key = fields.Char()
viva_wallet_latest_response = fields.Json() # used to buffer the latest asynchronous notification from Adyen.
viva_wallet_test_mode = fields.Boolean(string="Test mode", help="Run transactions in the test environment.")
viva_wallet_webhook_endpoint = fields.Char(compute='_compute_viva_wallet_webhook_endpoint', readonly=True)
def _viva_wallet_account_get_endpoint(self):
if self.viva_wallet_test_mode:
return 'https://demo-accounts.vivapayments.com'
return 'https://accounts.vivapayments.com'
def _viva_wallet_api_get_endpoint(self):
if self.viva_wallet_test_mode:
return 'https://demo-api.vivapayments.com'
return 'https://api.vivapayments.com'
def _viva_wallet_webhook_get_endpoint(self):
if self.viva_wallet_test_mode:
return 'https://demo.vivapayments.com'
return 'https://www.vivapayments.com'
def _compute_viva_wallet_webhook_endpoint(self):
web_base_url = self.get_base_url()
self.viva_wallet_webhook_endpoint = f"{web_base_url}/pos_viva_wallet/notification?company_id={self.company_id.id}&token={self.viva_wallet_webhook_verification_key}"
def _is_write_forbidden(self, fields):
# Allow the modification of these fields even if a pos_session is open
whitelisted_fields = {'viva_wallet_bearer_token', 'viva_wallet_webhook_verification_key', 'viva_wallet_latest_response'}
return bool(fields - whitelisted_fields and self.open_session_ids)
def _get_payment_terminal_selection(self):
return super()._get_payment_terminal_selection() + [('viva_wallet', 'Viva Wallet')]
def _bearer_token(self, session):
self.ensure_one()
if not self.env.user.has_group('point_of_sale.group_pos_user'):
raise AccessError(_("Do not have access to fetch token from Viva Wallet"))
data = {'grant_type': 'client_credentials'}
auth = requests.auth.HTTPBasicAuth(self.viva_wallet_client_id, self.viva_wallet_client_secret)
try:
resp = session.post(f"{self._viva_wallet_account_get_endpoint()}/connect/token", auth=auth, data=data, timeout=TIMEOUT)
except requests.exceptions.RequestException:
_logger.exception("Failed to call viva_wallet_bearer_token endpoint")
access_token = resp.json().get('access_token')
if access_token:
self.viva_wallet_bearer_token = access_token
return {'Authorization': f"Bearer {access_token}"}
else:
raise UserError(_('Not receive Bearer Token'))
def _get_verification_key(self, endpoint, viva_wallet_merchant_id, viva_wallet_api_key):
# Get a key to configure the webhook.
# this key need to be the response when we receive a notifiaction
# do not execute this query in test mode
if tools.config['test_enable']:
return 'viva_wallet_test'
auth = requests.auth.HTTPBasicAuth(viva_wallet_merchant_id, viva_wallet_api_key)
try:
resp = requests.get(f"{endpoint}/api/messages/config/token", auth=auth, timeout=TIMEOUT)
except requests.exceptions.RequestException:
_logger.exception('Failed to call https://%s/api/messages/config/token endpoint', endpoint)
return resp.json().get('Key')
def _call_viva_wallet(self, endpoint, action, data=None):
session = get_viva_wallet_session()
session.headers.update({'Authorization': f"Bearer {self.viva_wallet_bearer_token}"})
endpoint = f"{self._viva_wallet_api_get_endpoint()}/ecr/v1/{endpoint}"
try:
resp = session.request(action, endpoint, json=data, timeout=TIMEOUT)
except requests.exceptions.RequestException as e:
return {'error': _("There are some issues between us and Viva Wallet, try again later.%s)", e)}
if resp.text and resp.json().get('detail') == 'Could not validate credentials':
session.headers.update(self._bearer_token(session))
resp = session.request(action, endpoint, json=data, timeout=TIMEOUT)
if resp.status_code == 200:
if resp.text:
return resp.json()
return {'success': resp.status_code}
else:
return {'error': _("There are some issues between us and Viva Wallet, try again later. %s", resp.json().get('detail'))}
def _retrieve_session_id(self, data_webhook):
# Send a request to confirm the status of the sesions_id
# Need wait to the status of sesions_id is updated setted in session headers; code 202
session_id, pos_session_id = data_webhook.get('MerchantTrns', '').split("/") # Split to retrieve pos_sessions_id
endpoint = f"sessions/{session_id}"
data = self._call_viva_wallet(endpoint, 'get')
if data.get('success'):
data.update({'pos_session_id': pos_session_id, 'data_webhook': data_webhook})
self.viva_wallet_latest_response = data
self._send_notification(data)
else:
self._send_notification(
{'error': _(
"There are some issues between us and Viva Wallet, try again later. %s",
data.get('detail')
)}
)
def _send_notification(self, data):
# Send a notification to the point of sale channel to indicate that the transaction are finish
pos_session_sudo = self.env["pos.session"].browse(int(data.get('pos_session_id', False)))
if pos_session_sudo:
self.env['bus.bus']._sendone(pos_session_sudo._get_bus_channel_name(), 'VIVA_WALLET_LATEST_RESPONSE', pos_session_sudo.config_id.id)
def viva_wallet_send_payment_request(self, data):
if not self.env.user.has_group('point_of_sale.group_pos_user'):
raise AccessError(_("Only 'group_pos_user' are allowed to fetch token from Viva Wallet"))
endpoint = "transactions:sale"
return self._call_viva_wallet(endpoint, 'post', data)
def viva_wallet_send_payment_cancel(self, data):
if not self.env.user.has_group('point_of_sale.group_pos_user'):
raise AccessError(_("Only 'group_pos_user' are allowed to fetch token from Viva Wallet"))
session_id = data.get('sessionId')
cash_register_id = data.get('cashRegisterId')
endpoint = f"sessions/{session_id}?cashRegisterId={cash_register_id}"
return self._call_viva_wallet(endpoint, 'delete')
def write(self, vals):
record = super().write(vals)
if vals.get('viva_wallet_merchant_id') and vals.get('viva_wallet_api_key'):
self.viva_wallet_webhook_verification_key = self._get_verification_key(
self._viva_wallet_webhook_get_endpoint(),
self.viva_wallet_merchant_id,
self.viva_wallet_api_key
)
return record
def create(self, vals):
records = super().create(vals)
for record in records:
if record.viva_wallet_merchant_id and record.viva_wallet_api_key:
record.viva_wallet_webhook_verification_key = record._get_verification_key(
record._viva_wallet_webhook_get_endpoint(),
record.viva_wallet_merchant_id,
record.viva_wallet_api_key,
)
return records
def get_latest_viva_wallet_status(self):
if not self.env.user.has_group('point_of_sale.group_pos_user'):
raise AccessError(_("Only 'group_pos_user' are allowed to get latest transaction status"))
self.ensure_one()
latest_response = self.sudo().viva_wallet_latest_response
return latest_response
@api.constrains('use_payment_terminal')
def _check_viva_wallet_credentials(self):
for record in self:
if (record.use_payment_terminal == 'viva_wallet'
and not all(record[f] for f in [
'viva_wallet_merchant_id',
'viva_wallet_api_key',
'viva_wallet_client_id',
'viva_wallet_client_secret',
'viva_wallet_terminal_id']
)
):
raise UserError(_('It is essential to provide API key for the use of viva wallet'))
def get_viva_wallet_session():
session = requests.Session()
session.mount('https://', requests.adapters.HTTPAdapter(max_retries=requests.adapters.Retry(
total=6,
backoff_factor=2,
status_forcelist=[202, 500, 502, 503, 504],
)))
return session