diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c9c7787c..ad0b5245 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -110,7 +110,7 @@ repos: args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: https://github.com/OCA/pylint-odoo - rev: v8.0.19 + rev: v9.0.4 hooks: - id: pylint_odoo name: pylint with optional checks diff --git a/payment_payphone/README.rst b/payment_payphone/README.rst new file mode 100644 index 00000000..ea5559d7 --- /dev/null +++ b/payment_payphone/README.rst @@ -0,0 +1,89 @@ +========================= +Payphone Payment Provider +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:61dadd3e2996246649ce8044e6192e97e205e0f7a474685a1e61ef2e21eeeef3 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--ecuador-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-ecuador/tree/17.0/payment_payphone + :alt: OCA/l10n-ecuador +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-ecuador-17-0/l10n-ecuador-17-0-payment_payphone + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-ecuador&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Payment Payphone provider by redirection + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +See https://docs.payphone.app/doc/redireccion/ + +Usage +===== + +To configure this module, you need to: + +- Go to Payment providers Menu +- Configure your Access Token from your Payphone account +- Enable the payment method + +|image| + +.. |image| image:: https://raw.githubusercontent.com/OCA/l10n-ecuador/17.0/payment_payphone/static/description/configure.png + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Carlos Lopez + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/l10n-ecuador `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/payment_payphone/__init__.py b/payment_payphone/__init__.py new file mode 100644 index 00000000..eead65ff --- /dev/null +++ b/payment_payphone/__init__.py @@ -0,0 +1,11 @@ +from . import models +from . import controllers +from odoo.addons.payment import setup_provider, reset_payment_provider + + +def post_init_hook(env): + setup_provider(env, "payphone") + + +def uninstall_hook(env): + reset_payment_provider(env, "payphone") diff --git a/payment_payphone/__manifest__.py b/payment_payphone/__manifest__.py new file mode 100644 index 00000000..9068667e --- /dev/null +++ b/payment_payphone/__manifest__.py @@ -0,0 +1,23 @@ +{ + "name": "Payphone Payment Provider", + "category": "Accounting/Payment Acquirers", + "summary": "Payphone Payment Provider", + "version": "17.0.1.0.0", + "author": "Odoo Community Association (OCA), Carlos Lopez", + "website": "https://github.com/OCA/l10n-ecuador", + "license": "AGPL-3", + "depends": [ + "payment", + ], + "external_dependencies": { + "python": [], + }, + "data": [ + "views/payment_provider_templates.xml", + "data/payment_provider_data.xml", + "views/payment_provider_views.xml", + ], + "installable": True, + "post_init_hook": "post_init_hook", + "uninstall_hook": "uninstall_hook", +} diff --git a/payment_payphone/controllers/__init__.py b/payment_payphone/controllers/__init__.py new file mode 100644 index 00000000..12a7e529 --- /dev/null +++ b/payment_payphone/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/payment_payphone/controllers/main.py b/payment_payphone/controllers/main.py new file mode 100644 index 00000000..95c11d34 --- /dev/null +++ b/payment_payphone/controllers/main.py @@ -0,0 +1,33 @@ +import logging +import pprint + +from odoo import http +from odoo.http import request + +_logger = logging.getLogger(__name__) + + +class PayphoneController(http.Controller): + _return_url = "/payment/payphone/return" + + @http.route( + _return_url, + type="http", + methods=["GET"], + auth="public", + csrf=False, + save_session=False, + ) + def payphone_return_from_checkout(self, **data): + """Process the notification data sent by Payphone after redirection from checkout. + + :param dict data: The notification data. + """ + # Handle the notification data. + _logger.debug( + "Handling redirection from Payphone with data:\n%s", pprint.pformat(data) + ) + request.env["payment.transaction"].sudo()._handle_notification_data( + "payphone", data + ) + return request.redirect("/payment/status") diff --git a/payment_payphone/data/payment_provider_data.xml b/payment_payphone/data/payment_provider_data.xml new file mode 100644 index 00000000..423f669f --- /dev/null +++ b/payment_payphone/data/payment_provider_data.xml @@ -0,0 +1,31 @@ + + + + Payphone + payphone + 10 + + False + False + + + + + Payphone + + payphone + + + + diff --git a/payment_payphone/models/__init__.py b/payment_payphone/models/__init__.py new file mode 100644 index 00000000..9afbccd3 --- /dev/null +++ b/payment_payphone/models/__init__.py @@ -0,0 +1,2 @@ +from . import payment_provider +from . import payment_transaction diff --git a/payment_payphone/models/payment_provider.py b/payment_payphone/models/payment_provider.py new file mode 100644 index 00000000..d596d7ed --- /dev/null +++ b/payment_payphone/models/payment_provider.py @@ -0,0 +1,70 @@ +import logging +import pprint + +import requests +from werkzeug import urls + +from odoo import _, fields, models +from odoo.exceptions import ValidationError + +_logger = logging.getLogger(__name__) + +PAYPHONE_URL = "https://pay.payphonetodoesposible.com/" + + +class PaymentProvider(models.Model): + _inherit = "payment.provider" + + code = fields.Selection( + selection_add=[("payphone", "Payphone")], ondelete={"payphone": "set default"} + ) + payphone_access_token = fields.Char( + required_if_provider="payphone", + groups="base.group_system", + ) + + def _payphone_make_request(self, endpoint, payload): + """Make a request to Payphone API at the specified endpoint. + + Note: self.ensure_one() + + :param str endpoint: The endpoint to be reached by the request. + :param dict payload: The payload of the request. + :return The JSON-formatted content of the response. + :rtype: dict + :raise ValidationError: If an HTTP error occurs. + """ + self.ensure_one() + url = urls.url_join(PAYPHONE_URL, f"api/{endpoint}") + headers = {"Authorization": f"Bearer {self.payphone_access_token}"} + response_content = {} + try: + response = requests.post(url, json=payload, headers=headers, timeout=10) + response_content = response.json() + response.raise_for_status() + except Exception: + _logger.exception( + "Invalid API request at %s with data:\n%s", + url, + pprint.pformat(payload), + ) + message_list = [] + if response_content.get("message"): + message_list.append( + f"Error Code: {response_content.get('errorCode')}. " + f"Descripcion: {response_content.get('message')}" + ) + for messaje in response_content.get("errors", []): + msj = messaje.get("message") + msj_description = "".join(messaje.get("errorDescriptions")) + message_list.append( + f"Error Code: {msj}. Descripcion: {msj_description}" + ) + raise ValidationError( + _( + "Payphone: The communication with the API failed. " + "Payphone gave us the following information: '%s'", + "\n".join(message_list), + ) + ) from None + return response_content diff --git a/payment_payphone/models/payment_transaction.py b/payment_payphone/models/payment_transaction.py new file mode 100644 index 00000000..934a2124 --- /dev/null +++ b/payment_payphone/models/payment_transaction.py @@ -0,0 +1,130 @@ +import logging + +from werkzeug import urls + +from odoo import _, models +from odoo.exceptions import ValidationError + +from ..controllers.main import PayphoneController + +_logger = logging.getLogger(__name__) + + +class PaymentTransaction(models.Model): + _inherit = "payment.transaction" + + def _get_specific_rendering_values(self, processing_values): + """Override of `payment` to return Payphone-specific rendering values. + :param dict processing_values: + The generic and specific processing values of the transaction + :return: The dict of provider-specific processing values. + :rtype: dict + """ + res = super()._get_specific_rendering_values(processing_values) + if self.provider_code != "payphone": + return res + + # Initiate the payment and retrieve the payment link data. + payload = self._payphone_prepare_preference_request_payload() + payphone_response = self.provider_id._payphone_make_request( + "button/Prepare", payload=payload + ) + + # Extract the payment link URL and params and embed them in the redirect form. + self.write({"provider_reference": payphone_response.get("paymentId")}) + api_url = payphone_response.get("payWithPayPhone") + parsed_url = urls.url_parse(api_url) + url_params = urls.url_decode(parsed_url.query) + rendering_values = { + "api_url": api_url, + "url_params": url_params, # Encore the params as inputs to preserve them. + } + return rendering_values + + def _payphone_prepare_preference_request_payload(self): + """Create the payload for the preference request based on the transaction values. + + :return: The request payload. + :rtype: dict + """ + base_url = self.provider_id.get_base_url() + return_url = urls.url_join(base_url, PayphoneController._return_url) + return { + "amount": int(self.amount * 100), + "AmountWithoutTax": int(self.amount * 100), + "currency": "USD", + "clientTransactionId": self.reference, + "reference": self.reference, + "ResponseUrl": return_url, + "cancellationUrl": return_url, + } + + def _get_tx_from_notification_data(self, provider_code, notification_data): + """Override of `payment` to find the transaction based on Payphone data. + + :param str provider_code: The code of the provider that handled the transaction. + :param dict notification_data: The notification data sent by the provider. + :return: The transaction if found. + :rtype: recordset of `payment.transaction` + :raise ValidationError: If inconsistent data were received. + :raise ValidationError: If the data match no transaction. + """ + tx = super()._get_tx_from_notification_data(provider_code, notification_data) + if provider_code != "payphone" or len(tx) == 1: + return tx + + reference = notification_data.get("clientTransactionId") + if not reference: + raise ValidationError(_("Payphone: Received data with missing reference.")) + + tx = self.search( + [("reference", "=", reference), ("provider_code", "=", "payphone")] + ) + if not tx: + raise ValidationError( + _("Payphone: No transaction found matching reference %s.", reference) + ) + return tx + + def _process_notification_data(self, notification_data): + """Override of `payment` to process the transaction based on Payphone data. + + Note: self.ensure_one() from `_process_notification_data` + + :param dict notification_data: The notification data sent by the provider. + :return: None + :raise ValidationError: If inconsistent data were received. + """ + super()._process_notification_data(notification_data) + if self.provider_code != "payphone": + return + + if notification_data.get("id") == "0": + self._set_canceled() + return + payload = { + "id": notification_data.get("id"), + "clientTxId": notification_data.get("clientTransactionId"), + } + # Verify the notification data. + verified_payment_data = self.provider_id._payphone_make_request( + "button/V2/Confirm", payload=payload + ) + + # Update the payment method. + payment_status = verified_payment_data.get("statusCode") + if payment_status == 3: + self._set_done() + elif payment_status == 2: + self._set_canceled() + else: # Classify unsupported payment status as the `error` tx state. + _logger.warning( + "Received data for transaction " + "with reference %s with invalid payment status: %s", + self.reference, + payment_status, + ) + self._set_error( + "Payphone: " + + _("Received data with invalid status: %s", payment_status) + ) diff --git a/payment_payphone/pyproject.toml b/payment_payphone/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/payment_payphone/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/payment_payphone/readme/CONFIGURE.md b/payment_payphone/readme/CONFIGURE.md new file mode 100644 index 00000000..bdc0dc94 --- /dev/null +++ b/payment_payphone/readme/CONFIGURE.md @@ -0,0 +1 @@ +See diff --git a/payment_payphone/readme/DESCRIPTION.md b/payment_payphone/readme/DESCRIPTION.md new file mode 100644 index 00000000..a129c778 --- /dev/null +++ b/payment_payphone/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Payment Payphone provider by redirection diff --git a/payment_payphone/readme/USAGE.md b/payment_payphone/readme/USAGE.md new file mode 100644 index 00000000..6ef1bd6b --- /dev/null +++ b/payment_payphone/readme/USAGE.md @@ -0,0 +1,7 @@ +To configure this module, you need to: + +- Go to Payment providers Menu +- Configure your Access Token from your Payphone account +- Enable the payment method + +![image](../static/description/configure.png) diff --git a/payment_payphone/static/description/configure.png b/payment_payphone/static/description/configure.png new file mode 100644 index 00000000..2a5c3747 Binary files /dev/null and b/payment_payphone/static/description/configure.png differ diff --git a/payment_payphone/static/description/icon.jpeg b/payment_payphone/static/description/icon.jpeg new file mode 100644 index 00000000..8eb36724 Binary files /dev/null and b/payment_payphone/static/description/icon.jpeg differ diff --git a/payment_payphone/static/description/index.html b/payment_payphone/static/description/index.html new file mode 100644 index 00000000..ba1eb577 --- /dev/null +++ b/payment_payphone/static/description/index.html @@ -0,0 +1,430 @@ + + + + + + +Payphone Payment Provider + + + +
+

Payphone Payment Provider

+ + +

Beta License: AGPL-3 OCA/l10n-ecuador Translate me on Weblate Try me on Runboat

+

Payment Payphone provider by redirection

+

Table of contents

+ + +
+

Usage

+

To configure this module, you need to:

+
    +
  • Go to Payment providers Menu
  • +
  • Configure your Access Token from your Payphone account
  • +
  • Enable the payment method
  • +
+

image

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Carlos Lopez
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/l10n-ecuador project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/payment_payphone/tests/__init__.py b/payment_payphone/tests/__init__.py new file mode 100644 index 00000000..96b5f13e --- /dev/null +++ b/payment_payphone/tests/__init__.py @@ -0,0 +1,2 @@ +from . import common +from . import test_payment_transaction diff --git a/payment_payphone/tests/common.py b/payment_payphone/tests/common.py new file mode 100644 index 00000000..ff741d11 --- /dev/null +++ b/payment_payphone/tests/common.py @@ -0,0 +1,78 @@ +import json + +from requests import PreparedRequest, Response, Session +from werkzeug import urls + +from odoo.addons.payment.tests.common import PaymentCommon +from odoo.addons.payment_payphone.models.payment_provider import PAYPHONE_URL + +PAYPHONE_DUMMY_ID = "DUMMY_ID" + + +class PayphoneCommon(PaymentCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.amount = 10 + cls.currency = cls.currency_usd + cls.provider = cls._prepare_provider( + "payphone", + update_values={ + "payphone_access_token": "DUMMY_ACCESS_TOKEN", + }, + ) + cls.init_transaction_vals = { + "paymentId": PAYPHONE_DUMMY_ID, + "payWithPayPhone": urls.url_join( + PAYPHONE_URL, f"PayPhone/Index?paymentId={PAYPHONE_DUMMY_ID}" + ), + } + cls.payphone_wrong_account = { + "message": "Su aplicaciĆ³n no esta autorizada para acceder a este recurso", + } + cls.payphone_result = { + "id": PAYPHONE_DUMMY_ID, + "clientTransactionId": cls.reference, + } + cls.payphone_result_cancel = dict(cls.payphone_result, id="0") + cls.payphone_transaction = { + "email": "test@example.com", + "cardType": "Test", + "bin": "777300", + "lastDigits": "2632", + "deferredCode": "00000000", + "deferred": False, + "cardBrand": "PayPhone PayPhone", + "amount": 10, + "clientTransactionId": PAYPHONE_DUMMY_ID, + "phoneNumber": "593123456789", + "statusCode": 3, + "transactionStatus": "Approved", + "authorizationCode": "W23801042", + "messageCode": 0, + } + # statusCode: 3 = OK, 2 = Cancel, else: Error + cls.payphone_transaction_ok = dict(cls.payphone_transaction) + cls.payphone_transaction_rejected = dict(cls.payphone_transaction, statusCode=2) + cls.payphone_transaction_error = dict(cls.payphone_transaction, statusCode=4) + + @classmethod + def _request_handler(cls, s: Session, res: PreparedRequest, /, **kw): + # Payphone transaction init, set default values + if res.url.startswith(PAYPHONE_URL) and "button/Prepare" in res.url: + return cls._make_payphone_requests(res.url, cls.init_transaction_vals) + return super()._request_handler(s, res, **kw) + + @classmethod + def _make_payphone_requests(cls, url, json_data=None, status_code=200): + """ + :param json_data: dict to response + :param status_code: + :returns response: requests response""" + json_content = json_data or {} + response = Response() + response.status_code = status_code + response._content = json.dumps(json_content).encode() + response.url = url + response.headers["Content-Type"] = "application/json" + return response diff --git a/payment_payphone/tests/test_payment_transaction.py b/payment_payphone/tests/test_payment_transaction.py new file mode 100644 index 00000000..2bf8ace3 --- /dev/null +++ b/payment_payphone/tests/test_payment_transaction.py @@ -0,0 +1,169 @@ +from unittest.mock import patch + +import requests + +from odoo import _ +from odoo.exceptions import ValidationError +from odoo.tests import tagged +from odoo.tools import mute_logger + +from odoo.addons.payment.tests.http_common import PaymentHttpCommon +from odoo.addons.payment_payphone.controllers.main import PayphoneController +from odoo.addons.payment_payphone.tests.common import PayphoneCommon + + +@tagged("post_install", "-at_install") +class TestPaymentTransaction(PayphoneCommon, PaymentHttpCommon): + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_01_redirect_form_values(self): + tx = self._create_transaction(flow="redirect") + processing_values = tx._get_processing_values() + form_info = self._extract_values_from_html_form( + processing_values["redirect_form_html"] + ) + self.assertEqual( + form_info["action"], self.init_transaction_vals["payWithPayPhone"] + ) + self.assertEqual(form_info["method"], "get") + self.assertEqual( + form_info["inputs"].get("paymentId"), + self.init_transaction_vals["paymentId"], + ) + + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_01_transaction_error_account(self): + _origin_post = requests.post + + def _patch_get_payphone(url, *args, **kwargs): + if "button/Prepare" in url: + return PayphoneCommon._make_payphone_requests( + url, self.payphone_wrong_account, status_code=401 + ) + return _origin_post(url, *args, **kwargs) + + tx = self._create_transaction(flow="redirect") + with patch.object(requests, "post", _patch_get_payphone): + with self.assertRaises(ValidationError), self.assertLogs( + level="ERROR" + ) as log_catcher: + tx._get_processing_values() + self.assertEqual( + len(log_catcher.output), 1, "Exactly one warning should be logged" + ) + self.assertIn("Invalid API request", log_catcher.output[0]) + + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_02_feedback_done(self): + _origin_post = requests.post + + def _patch_get_payphone(url, *args, **kwargs): + if "button/V2/Confirm" in url: + return PayphoneCommon._make_payphone_requests( + url, self.payphone_transaction_ok + ) + return _origin_post(url, *args, **kwargs) + + tx = self._create_transaction(flow="redirect") + tx._get_processing_values() + with patch.object(requests, "post", _patch_get_payphone): + tx._handle_notification_data(tx.provider_code, self.payphone_result) + self.assertEqual(tx.state, "done") + + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_03_feedback_rejected(self): + _origin_post = requests.post + + def _patch_get_payphone(url, *args, **kwargs): + if "button/V2/Confirm" in url: + return PayphoneCommon._make_payphone_requests( + url, self.payphone_transaction_rejected + ) + return _origin_post(url, *args, **kwargs) + + tx = self._create_transaction(flow="redirect") + tx._get_processing_values() + with patch.object(requests, "post", _patch_get_payphone): + tx._handle_notification_data( + tx.provider_code, self.payphone_transaction_rejected + ) + self.assertEqual(tx.state, "cancel") + + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_04_feedback_cancel(self): + tx = self._create_transaction(flow="redirect") + tx._get_processing_values() + tx._handle_notification_data(tx.provider_code, self.payphone_result_cancel) + self.assertEqual(tx.state, "cancel") + + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_05_feedback_error(self): + _origin_post = requests.post + + def _patch_get_payphone(url, *args, **kwargs): + if "button/V2/Confirm" in url: + return PayphoneCommon._make_payphone_requests( + url, self.payphone_transaction_error + ) + return _origin_post(url, *args, **kwargs) + + tx = self._create_transaction(flow="redirect") + tx._get_processing_values() + with patch.object(requests, "post", _patch_get_payphone): + tx._handle_notification_data( + tx.provider_code, self.payphone_transaction_error + ) + self.assertEqual(tx.state, "error") + + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_06_provider_none(self): + self.provider = self.dummy_provider + tx = self._create_transaction(flow="redirect") + tx._get_processing_values() + tx._handle_notification_data(tx.provider_code, {}) + # en provider none no se ejecutara codigo, asi que deberia seguir en draft + self.assertEqual(tx.state, "draft") + + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_07_transaction_from_controller_ok(self): + _origin_post = requests.post + + def _patch_get_payphone(url, *args, **kwargs): + if "button/V2/Confirm" in url: + return PayphoneCommon._make_payphone_requests( + url, self.payphone_transaction_ok + ) + return _origin_post(url, *args, **kwargs) + + return_url = self._build_url(PayphoneController._return_url) + tx = self._create_transaction(flow="redirect") + tx._get_processing_values() + with patch.object(requests, "post", _patch_get_payphone): + controller_response = self._make_http_get_request( + return_url, params=self.payphone_result + ) + self.assertEqual(controller_response.status_code, 200) + self.assertEqual(tx.state, "done") + self.assertTrue("/payment/status" in controller_response.url) + + @mute_logger("odoo.addons.payment.models.payment_transaction") + def test_08_controller_wrong_client_transaction_id(self): + return_url = self._build_url(PayphoneController._return_url) + tx = self._create_transaction(flow="redirect") + tx._get_processing_values() + controller_response = self._make_http_get_request( + return_url, + params={}, # no clientTransactionId + ) + self.assertEqual(controller_response.status_code, 400) + text_expected = _("Payphone: Received data with missing reference.") + self.assertIn(text_expected, controller_response.text) + clientTransactionId = "WRONG" + controller_response = self._make_http_get_request( + return_url, + params={"clientTransactionId": clientTransactionId}, + ) + self.assertEqual(controller_response.status_code, 400) + text_expected = _( + "Payphone: No transaction found matching reference %s.", clientTransactionId + ) + self.assertIn(text_expected, controller_response.text) diff --git a/payment_payphone/views/payment_provider_templates.xml b/payment_payphone/views/payment_provider_templates.xml new file mode 100644 index 00000000..ccedbc6c --- /dev/null +++ b/payment_payphone/views/payment_provider_templates.xml @@ -0,0 +1,15 @@ + + + + diff --git a/payment_payphone/views/payment_provider_views.xml b/payment_payphone/views/payment_provider_views.xml new file mode 100644 index 00000000..476b8afd --- /dev/null +++ b/payment_payphone/views/payment_provider_views.xml @@ -0,0 +1,22 @@ + + + + + Payphone Provider Form + payment.provider + + + + + + + + + + +