diff --git a/addons/payment_stripe/controllers/main.py b/addons/payment_stripe/controllers/main.py index 0253b973fae..8cabfb8917c 100644 --- a/addons/payment_stripe/controllers/main.py +++ b/addons/payment_stripe/controllers/main.py @@ -13,8 +13,10 @@ from odoo import http from odoo.exceptions import ValidationError from odoo.http import request +from odoo.addons.payment_stripe import utils as stripe_utils from odoo.addons.payment_stripe.const import HANDLED_WEBHOOK_EVENTS + _logger = logging.getLogger(__name__) @@ -142,7 +144,7 @@ class StripeController(http.Controller): :raise: :class:`werkzeug.exceptions.Forbidden` if the timestamp is too old or if the signatures don't match """ - webhook_secret = tx_sudo.acquirer_id._get_stripe_webhook_secret() + webhook_secret = stripe_utils.get_webhook_secret(tx_sudo.acquirer_id) if not webhook_secret: _logger.warning("ignored webhook event due to undefined webhook secret") return diff --git a/addons/payment_stripe/models/payment_acquirer.py b/addons/payment_stripe/models/payment_acquirer.py index db49fc01ec0..47a9c33ca88 100644 --- a/addons/payment_stripe/models/payment_acquirer.py +++ b/addons/payment_stripe/models/payment_acquirer.py @@ -4,15 +4,17 @@ import logging import uuid import requests -from werkzeug.urls import url_join, url_encode +from werkzeug.urls import url_encode, url_join from odoo import _, api, fields, models from odoo.exceptions import ValidationError +from odoo.addons.payment_stripe import utils as stripe_utils from odoo.addons.payment_stripe.const import API_VERSION, PROXY_URL, HANDLED_WEBHOOK_EVENTS from odoo.addons.payment_stripe.controllers.onboarding import OnboardingController from odoo.addons.payment_stripe.controllers.main import StripeController + _logger = logging.getLogger(__name__) @@ -39,12 +41,12 @@ class PaymentAcquirer(models.Model): """ Check that the acquirer of a connected account can never been set to 'test'. This constraint is defined in the present module to allow the export of the translation - string of the `ValidationError` that can be raised by the internal module responsible for - Stripe Connect, although this constraint depends on a field that is defined in that external - module. - Additionally, having the field defined in the external module prevents from using it - as a trigger for this constraint. This is however not a problem because the field `state` is - used as trigger, and we always write on `state` when we write on the internal field. + string of the `ValidationError` should it be raised by modules that would fully implement + Stripe Connect. + + Additionally, the field `state` is used as a trigger for this constraint to allow those + modules to indirectly trigger it when writing on custom fields. Indeed, by always writing on + `state` together with writing on those custom fields, the constraint would be triggered. :return: None """ @@ -58,7 +60,7 @@ class PaymentAcquirer(models.Model): def _stripe_has_connected_account(self): """ Return whether the acquirer is linked to a connected Stripe account. - Note: This method is overridden by the internal module responsible for Stripe Connect. + Note: This method serves as a hook for modules that would fully implement Stripe Connect. Note: self.ensure_one() :return: Whether the acquirer is linked to a connected Stripe account @@ -77,7 +79,7 @@ class PaymentAcquirer(models.Model): the URL the user is redirected to when coming back on Odoo after the onboarding. If the link generation failed, redirect the user to the acquirer form. - Note: This method is overridden by the internal module responsible for Stripe Connect. + Note: This method serves as a hook for modules that would fully implement Stripe Connect. Note: self.ensure_one() :param int menu_id: The menu from which the user started the onboarding step, as an @@ -173,7 +175,11 @@ class PaymentAcquirer(models.Model): self.ensure_one() url = url_join('https://api.stripe.com/v1/', endpoint) - headers = self._get_stripe_request_headers(endpoint) + headers = { + 'AUTHORIZATION': f'Bearer {stripe_utils.get_secret_key(self)}', + 'Stripe-Version': API_VERSION, # SetupIntent requires a specific version. + **self._get_stripe_extra_request_headers(), + } try: response = requests.request(method, url, data=payload, headers=headers, timeout=60) # Stripe can send 4XX errors for payment failures (not only for badly-formed requests). @@ -200,19 +206,15 @@ class PaymentAcquirer(models.Model): raise ValidationError("Stripe: " + _("Could not establish the connection to the API.")) return response.json() - def _get_stripe_request_headers(self, endpoint): - """ Return the headers for the Stripe API request. + def _get_stripe_extra_request_headers(self): + """ Return the extra headers for the Stripe API request. - Note: This method is overridden by the internal module responsible for Stripe Connect. + Note: This method serves as a hook for modules that would fully implement Stripe Connect. - :param str endpoint: The Stripe endpoint that will be called - :returns: The request headers + :return: The extra request headers. :rtype: dict """ - return { - 'AUTHORIZATION': f'Bearer {self._get_stripe_secret_key()}', - 'Stripe-Version': API_VERSION, # SetupIntent needs a specific version - } + return {} def _get_default_payment_method_id(self): self.ensure_one() @@ -228,44 +230,12 @@ class PaymentAcquirer(models.Model): 'stripe_webhook_secret', ]) - # === BUSINESS METHODS - STRIPE CONNECT CREDENTIALS === # - - def _get_stripe_publishable_key(self): - """ Return the publishable key for Stripe. - - Note: This method is overridden by the internal module responsible for Stripe Connect. - - :return: The publishable key - :rtype: str - """ - return self.stripe_publishable_key - - def _get_stripe_secret_key(self): - """ Return the secret key for Stripe. - - Note: This method is overridden by the internal module responsible for Stripe Connect. - - :return: The secret key - :rtype: str - """ - return self.stripe_secret_key - - def _get_stripe_webhook_secret(self): - """ Return the webhook secret for Stripe. - - Note: This method is overridden by the internal module responsible for Stripe Connect. - - :returns: The webhook secret - :rtype: str - """ - return self.stripe_webhook_secret - # === BUSINESS METHODS - STRIPE CONNECT ONBOARDING === # def _stripe_fetch_or_create_connected_account(self): """ Fetch the connected Stripe account and create one if not already done. - Note: This method is overridden by the internal module responsible for Stripe Connect. + Note: This method serves as a hook for modules that would fully implement Stripe Connect. :return: The connected account :rtype: dict @@ -277,7 +247,7 @@ class PaymentAcquirer(models.Model): def _stripe_prepare_connect_account_payload(self): """ Prepare the payload for the creation of a connected account in Stripe format. - Note: This method is overridden by the internal module responsible for Stripe Connect. + Note: This method serves as a hook for modules that would fully implement Stripe Connect. Note: self.ensure_one() :return: The Stripe-formatted payload for the creation request diff --git a/addons/payment_stripe/models/payment_transaction.py b/addons/payment_stripe/models/payment_transaction.py index 9f42d91d07f..684aaa89185 100644 --- a/addons/payment_stripe/models/payment_transaction.py +++ b/addons/payment_stripe/models/payment_transaction.py @@ -9,9 +9,11 @@ from odoo import _, fields, models from odoo.exceptions import UserError, ValidationError from odoo.addons.payment import utils as payment_utils +from odoo.addons.payment_stripe import utils as stripe_utils from odoo.addons.payment_stripe.const import INTENT_STATUS_MAPPING, PAYMENT_METHOD_TYPES from odoo.addons.payment_stripe.controllers.main import StripeController + _logger = logging.getLogger(__name__) @@ -35,7 +37,7 @@ class PaymentTransaction(models.Model): checkout_session = self._stripe_create_checkout_session() return { - 'publishable_key': self.acquirer_id._get_stripe_publishable_key(), + 'publishable_key': stripe_utils.get_publishable_key(self.acquirer_id), 'session_id': checkout_session['id'], } @@ -147,7 +149,7 @@ class PaymentTransaction(models.Model): def _get_common_stripe_session_values(self, pmt_values, customer): """ Return the Stripe Session values that are common to redirection and validation. - Note: This method is overridden by the internal module responsible for Stripe Connect. + Note: This method serves as a hook for modules that would fully implement Stripe Connect. :param dict pmt_values: The payment method types values :param dict customer: The Stripe customer to assign to the session @@ -278,7 +280,7 @@ class PaymentTransaction(models.Model): def _stripe_prepare_payment_intent_payload(self): """ Prepare the payload for the creation of a payment intent in Stripe format. - Note: This method is overridden by the internal module responsible for Stripe Connect. + Note: This method serves as a hook for modules that would fully implement Stripe Connect. Note: self.ensure_one() :return: The Stripe-formatted payload for the payment intent request diff --git a/addons/payment_stripe/utils.py b/addons/payment_stripe/utils.py new file mode 100644 index 00000000000..f997af5dd1a --- /dev/null +++ b/addons/payment_stripe/utils.py @@ -0,0 +1,39 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +def get_publishable_key(acquirer_sudo): + """ Return the publishable key for Stripe. + + Note: This method serves as a hook for modules that would fully implement Stripe Connect. + + :param recordset acquirer_sudo: The acquirer on which the key should be read, as a sudoed + `payment.acquire` record. + :return: The publishable key + :rtype: str + """ + return acquirer_sudo.stripe_publishable_key + + +def get_secret_key(acquirer_sudo): + """ Return the secret key for Stripe. + + Note: This method serves as a hook for modules that would fully implement Stripe Connect. + + :param recordset acquirer_sudo: The acquirer on which the key should be read, as a sudoed + `payment.acquire` record. + :return: The secret key + :rtype: str + """ + return acquirer_sudo.stripe_secret_key + + +def get_webhook_secret(acquirer_sudo): + """ Return the webhook secret for Stripe. + + Note: This method serves as a hook for modules that would fully implement Stripe Connect. + + :param recordset acquirer_sudo: The acquirer on which the key should be read, as a sudoed + `payment.acquire` record. + :returns: The webhook secret + :rtype: str + """ + return acquirer_sudo.stripe_webhook_secret