# Part of Odoo. See LICENSE file for full copyright and licensing details. import logging import pprint from odoo import _, api, fields, models from odoo.fields import Command from odoo.exceptions import UserError, ValidationError from .authorize_request import AuthorizeAPI _logger = logging.getLogger(__name__) class PaymentProvider(models.Model): _inherit = 'payment.provider' code = fields.Selection( selection_add=[('authorize', 'Authorize.Net')], ondelete={'authorize': 'set default'}) authorize_login = fields.Char( string="API Login ID", help="The ID solely used to identify the account with Authorize.Net", required_if_provider='authorize') authorize_transaction_key = fields.Char( string="API Transaction Key", required_if_provider='authorize', groups='base.group_system') authorize_signature_key = fields.Char( string="API Signature Key", required_if_provider='authorize', groups='base.group_system') authorize_client_key = fields.Char( string="API Client Key", help="The public client key. To generate directly from Odoo or from Authorize.Net backend.") # Authorize.Net supports only one currency: "One gateway account is required for each currency" # See https://community.developer.authorize.net/t5/The-Authorize-Net-Developer-Blog/Authorize-Net-UK-Europe-Update/ba-p/35957 authorize_currency_id = fields.Many2one( string="Authorize Currency", comodel_name='res.currency') authorize_payment_method_type = fields.Selection( string="Allow Payments From", help="Determines with what payment method the customer can pay.", selection=[('credit_card', "Credit Card"), ('bank_account', "Bank Account (USA Only)")], default='credit_card', required_if_provider='authorize', ) # === CONSTRAINT METHODS ===# @api.constrains('authorize_payment_method_type') def _check_payment_method_type(self): for provider in self.filtered(lambda p: p.code == "authorize"): if self.env['payment.token'].search([('provider_id', '=', provider.id)], limit=1): raise ValidationError(_( "There are active tokens linked to this provider. To change the payment method " "type, please disable the provider and duplicate it. Then, change the payment " "method type on the duplicated provider." )) #=== COMPUTE METHODS ===# def _compute_feature_support_fields(self): """ Override of `payment` to enable additional features. """ super()._compute_feature_support_fields() self.filtered(lambda p: p.code == 'authorize').update({ 'support_manual_capture': True, 'support_refund': 'full_only', 'support_tokenization': True, }) # === ONCHANGE METHODS ===# @api.onchange('authorize_payment_method_type') def _onchange_authorize_payment_method_type(self): if self.authorize_payment_method_type == 'bank_account': self.display_as = _("Bank (powered by Authorize)") self.payment_icon_ids = [Command.clear()] else: self.display_as = _("Credit Card (powered by Authorize)") self.payment_icon_ids = [Command.set([self.env.ref(icon_xml_id).id for icon_xml_id in ( 'payment.payment_icon_cc_maestro', 'payment.payment_icon_cc_mastercard', 'payment.payment_icon_cc_discover', 'payment.payment_icon_cc_diners_club_intl', 'payment.payment_icon_cc_jcb', 'payment.payment_icon_cc_visa', )])] # === ACTION METHODS ===# def action_update_merchant_details(self): """ Fetch the merchant details to update the client key and the account currency. """ self.ensure_one() if self.state == 'disabled': raise UserError(_("This action cannot be performed while the provider is disabled.")) authorize_API = AuthorizeAPI(self) # Validate the API Login ID and Transaction Key res_content = authorize_API.test_authenticate() _logger.info("test_authenticate request response:\n%s", pprint.pformat(res_content)) if res_content.get('err_msg'): raise UserError(_("Failed to authenticate.\n%s", res_content['err_msg'])) # Update the merchant details res_content = authorize_API.merchant_details() _logger.info("merchant_details request response:\n%s", pprint.pformat(res_content)) if res_content.get('err_msg'): raise UserError(_("Could not fetch merchant details:\n%s", res_content['err_msg'])) currency = self.env['res.currency'].search([('name', 'in', res_content.get('currencies'))]) self.authorize_currency_id = currency self.authorize_client_key = res_content.get('publicClientKey') # === BUSINESS METHODS ===# @api.model def _get_compatible_providers(self, *args, currency_id=None, **kwargs): """ Override of payment to unlist Authorize providers for unsupported currencies. """ providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) currency = self.env['res.currency'].browse(currency_id).exists() if currency: providers = providers.filtered( lambda p: p.code != 'authorize' or currency == p.authorize_currency_id ) return providers def _get_validation_amount(self): """ Override of payment to return the amount for Authorize.Net validation operations. :return: The validation amount :rtype: float """ res = super()._get_validation_amount() if self.code != 'authorize': return res return 0.01 def _get_validation_currency(self): """ Override of payment to return the currency for Authorize.Net validation operations. :return: The validation currency :rtype: recordset of `res.currency` """ res = super()._get_validation_currency() if self.code != 'authorize': return res return self.authorize_currency_id