python-terminusgps 45.6.1__tar.gz → 45.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/.github/workflows/sphinx.yml +19 -2
  2. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/PKG-INFO +1 -1
  3. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/requirements.txt +2 -1
  4. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/authorizenet/constants.rst +4 -1
  5. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/authorizenet/index.rst +4 -6
  6. python_terminusgps-45.8.0/docs/source/authorizenet/service.rst +9 -0
  7. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/conf.py +1 -2
  8. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/pyproject.toml +1 -1
  9. python_terminusgps-45.8.0/terminusgps/authorizenet/api/transactions.py +201 -0
  10. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/authorizenet/constants.py +15 -0
  11. python_terminusgps-45.8.0/terminusgps/authorizenet/service.py +112 -0
  12. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/uv.lock +7 -7
  13. python_terminusgps-45.6.1/docs/source/authorizenet/auth.rst +0 -22
  14. python_terminusgps-45.6.1/docs/source/authorizenet/exceptions.rst +0 -4
  15. python_terminusgps-45.6.1/docs/source/authorizenet/usage.rst +0 -20
  16. python_terminusgps-45.6.1/terminusgps/authorizenet/api/transactions.py +0 -2
  17. python_terminusgps-45.6.1/terminusgps/authorizenet/auth.py +0 -38
  18. python_terminusgps-45.6.1/terminusgps/authorizenet/controllers.py +0 -57
  19. python_terminusgps-45.6.1/terminusgps/authorizenet/services.py +0 -66
  20. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/.gitignore +0 -0
  21. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/.python-version +0 -0
  22. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/COPYING +0 -0
  23. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/README.md +0 -0
  24. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/Makefile +0 -0
  25. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/make.bat +0 -0
  26. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/authorizenet/api.rst +0 -0
  27. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/index.rst +0 -0
  28. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/mixins.rst +0 -0
  29. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/validators.rst +0 -0
  30. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/wialon/constants.rst +0 -0
  31. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/wialon/exceptions.rst +0 -0
  32. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/wialon/index.rst +0 -0
  33. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/wialon/items.rst +0 -0
  34. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/wialon/session.rst +0 -0
  35. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/docs/source/wialon/usage.rst +0 -0
  36. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/__init__.py +0 -0
  37. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/authorizenet/__init__.py +0 -0
  38. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/authorizenet/api/__init__.py +0 -0
  39. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/authorizenet/api/address_profiles.py +0 -0
  40. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/authorizenet/api/customer_profiles.py +0 -0
  41. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/authorizenet/api/payment_profiles.py +0 -0
  42. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/authorizenet/api/subscriptions.py +0 -0
  43. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/default_settings.py +0 -0
  44. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/mixins.py +0 -0
  45. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/validators.py +0 -0
  46. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/__init__.py +0 -0
  47. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/constants.py +0 -0
  48. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/flags.py +0 -0
  49. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/__init__.py +0 -0
  50. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/account.py +0 -0
  51. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/base.py +0 -0
  52. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/factory.py +0 -0
  53. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/resource.py +0 -0
  54. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/retranslator.py +0 -0
  55. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/route.py +0 -0
  56. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/unit.py +0 -0
  57. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/unit_group.py +0 -0
  58. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/items/user.py +0 -0
  59. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/session.py +0 -0
  60. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/utils.py +0 -0
  61. {python_terminusgps-45.6.1 → python_terminusgps-45.8.0}/terminusgps/wialon/validators.py +0 -0
@@ -12,8 +12,25 @@ jobs:
12
12
  with:
13
13
  persist-credentials: false
14
14
 
15
- - name: Build HTML
16
- uses: ammaraskar/sphinx-action@8.2.3
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v6
17
+ with:
18
+ version: "0.8.17"
19
+
20
+ - name: Set up python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version-file: ".python-version"
24
+
25
+ - name: Install project
26
+ run: uv sync --locked --group docs
27
+
28
+ - name: Build docs
29
+ run: |
30
+ source .venv/bin/activate
31
+ cd docs
32
+ make html
33
+ deactivate
17
34
 
18
35
  - name: Upload artifacts
19
36
  uses: actions/upload-artifact@v4
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 45.6.1
3
+ Version: 45.8.0
4
4
  Summary: Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more.
5
5
  Project-URL: Documentation, https://terminusgps.github.io/python-terminusgps
6
6
  Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
@@ -1,5 +1,6 @@
1
+ authorizenet>=1.1.5
1
2
  autoclasstoc>=1.7.0
2
3
  django>=5.2.1
3
- furo==2024.04.27
4
4
  python-wialon>=1.2.4
5
+ sphinx>=8.2.3
5
6
  sphinxawesome-theme>=5.3.2
@@ -1,12 +1,15 @@
1
1
  Constants
2
2
  =========
3
3
 
4
- These constants inherit from :py:obj:`~django.db.models.TextChoices`, not the built-in Python :py:obj:`~enum.StrEnum` type.
4
+ These constants inherit from :py:obj:`django.db.models.TextChoices`, not the built-in Python :py:obj:`~enum.StrEnum` type.
5
5
 
6
6
  Django :py:obj:`~django.db.models.TextChoices` provides attributes such as :py:attr:`~django.db.models.TextChoices.choices`, :py:attr:`~django.db.models.TextChoices.values`, :py:attr:`~django.db.models.TextChoices.labels` and more.
7
7
 
8
8
  .. py:currentmodule:: terminusgps.authorizenet.constants
9
9
 
10
+ .. autoclass:: CurrencyCode
11
+ :members:
12
+
10
13
  .. autoclass:: Environment
11
14
  :members:
12
15
 
@@ -1,13 +1,13 @@
1
1
  Authorizenet
2
2
  ============
3
3
 
4
- The :py:mod:`terminusgps.authorizenet` package provides convenient functions for interacting with the Authorizenet API in a Pythonic interface.
4
+ The :py:mod:`terminusgps.authorizenet` package provides a Pythonic interface for interacting with the Authorizenet API.
5
5
 
6
6
  Most `Authorizenet API endpoints <https://developer.authorize.net/api/reference/index.html>`_ are represented as plain Python functions.
7
7
 
8
- .. attention:: These functions require the following settings to be present in your Django project's ``settings.py`` module.
8
+ .. attention:: :py:mod:`terminusgps.authorizenet` requires the following settings to be present in your Django project's ``settings.py`` module.
9
9
 
10
- Using this package without setting these settings will raise :py:exc:`~django.core.exceptions.ImproperlyConfigured`.
10
+ Using the package without setting these settings will raise :py:exc:`~django.core.exceptions.ImproperlyConfigured`.
11
11
 
12
12
  +-----------------------------------+---------------+
13
13
  | setting | type |
@@ -25,8 +25,6 @@ Most `Authorizenet API endpoints <https://developer.authorize.net/api/reference/
25
25
  :maxdepth: 2
26
26
  :caption: Contents:
27
27
 
28
- auth.rst
29
28
  constants.rst
30
- exceptions.rst
31
29
  api.rst
32
- usage.rst
30
+ service.rst
@@ -0,0 +1,9 @@
1
+ Service
2
+ =======
3
+
4
+ .. autoexception:: terminusgps.authorizenet.service.AuthorizenetControllerExecutionError
5
+ :members:
6
+
7
+ .. autoclass:: terminusgps.authorizenet.service.AuthorizenetService
8
+ :autoclasstoc:
9
+ :members:
@@ -12,7 +12,7 @@ sys.path.insert(0, os.path.abspath("../../"))
12
12
  project = "python-terminusgps"
13
13
  copyright = "2025, Terminus GPS, LLC"
14
14
  author = "Terminus GPS, LLC"
15
- release = "45.6.1"
15
+ release = "45.8.0"
16
16
 
17
17
  # -- General configuration ---------------------------------------------------
18
18
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -33,7 +33,6 @@ intersphinx_mapping = {
33
33
  ),
34
34
  }
35
35
 
36
- templates_path = ["_templates"]
37
36
  exclude_patterns = []
38
37
 
39
38
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-terminusgps"
3
- version = "45.6.1"
3
+ version = "45.8.0"
4
4
  description = "Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more."
5
5
  readme = "README.md"
6
6
  authors = [ {name = "Blake Nall", email = "blake@terminusgps.com" } ]
@@ -0,0 +1,201 @@
1
+ from decimal import Decimal
2
+
3
+ from authorizenet import apicontractsv1, apicontrollers
4
+ from authorizenet.apicontrollersbase import APIOperationBase
5
+ from lxml.objectify import ObjectifiedElement
6
+
7
+ __all__ = [
8
+ "charge_credit_card",
9
+ "authorize_credit_card",
10
+ "capture_authorized_amount",
11
+ "refund_credit_card",
12
+ ]
13
+
14
+
15
+ def build_transaction(
16
+ amount: Decimal, **kwargs
17
+ ) -> apicontractsv1.transactionRequestType:
18
+ """Returns a transaction element for a transaction request."""
19
+ transaction_request = apicontractsv1.transactionRequestType()
20
+ transaction_request.amount = amount
21
+
22
+ if payment := kwargs.get("payment"):
23
+ transaction_request.payment = payment
24
+ if address := kwargs.get("address"):
25
+ transaction_request.billTo = address
26
+ if order := kwargs.get("order"):
27
+ transaction_request.order = order
28
+ if customer_data := kwargs.get("customer_data"):
29
+ transaction_request.customerData = customer_data
30
+ if settings := kwargs.get("settings"):
31
+ transaction_request.transactionSettings = settings
32
+ if line_items := kwargs.get("line_items"):
33
+ transaction_request.lineItems = line_items
34
+
35
+ return transaction_request
36
+
37
+
38
+ def charge_credit_card(
39
+ amount: Decimal,
40
+ credit_card: apicontractsv1.creditCardType,
41
+ address: apicontractsv1.customerAddressType,
42
+ order: apicontractsv1.orderType | None = None,
43
+ customer_data: apicontractsv1.customerDataType | None = None,
44
+ settings: apicontractsv1.ArrayOfSetting | None = None,
45
+ line_items: apicontractsv1.ArrayOfLineItem | None = None,
46
+ ) -> tuple[ObjectifiedElement, type[APIOperationBase]]:
47
+ """
48
+ `Charges a credit card <https://developer.authorize.net/api/reference/index.html#payment-transactions-charge-a-credit-card>`_.
49
+
50
+ :param amount: Dollar amount to charge.
51
+ :type amount: ~decimal.Decimal
52
+ :param credit_card: A credit card.
53
+ :type credit_card: ~authorizenet.apicontractsv1.creditCardType
54
+ :param address: A customer address.
55
+ :type address: ~authorizenet.apicontractsv1.customerAddressType
56
+ :param order: Order information. Default is :py:obj:`None`.
57
+ :type order: ~authorizenet.apicontractsv1.orderType | None
58
+ :param customer_data: Customer data. Default is :py:obj:`None`.
59
+ :type customer_data: ~authorizenet.apicontractsv1.customerDataType | None
60
+ :param settings: Transaction settings. Default is :py:obj:`None`.
61
+ :type settings: ~authorizenet.apicontractsv1.ArrayOfSetting | None
62
+ :param line_items: An array of line items. Default is :py:obj:`None`.
63
+ :type line_items: ~authorizenet.apicontractsv1.ArrayOfLineItem | None
64
+ :returns: A tuple containing an Authorizenet API request element and controller class.
65
+ :rtype: tuple[~lxml.objectify.ObjectifiedElement, type[~authorizenet.apicontrollersbase.APIOperationBase]]
66
+
67
+ """
68
+ request = apicontractsv1.createTransactionRequest()
69
+ payment = apicontractsv1.paymentType()
70
+ payment.creditCard = credit_card
71
+ request.transactionRequest = build_transaction(
72
+ amount=amount,
73
+ payment=payment,
74
+ address=address,
75
+ order=order,
76
+ customer_data=customer_data,
77
+ settings=settings,
78
+ line_items=line_items,
79
+ )
80
+
81
+ request.transactionRequest.transactionType = (
82
+ apicontractsv1.transactionTypeEnum.authCaptureTransaction
83
+ )
84
+ return request, apicontrollers.createTransactionController
85
+
86
+
87
+ def authorize_credit_card(
88
+ amount: Decimal,
89
+ credit_card: apicontractsv1.creditCardType,
90
+ address: apicontractsv1.customerAddressType,
91
+ order: apicontractsv1.orderType | None = None,
92
+ customer_data: apicontractsv1.customerDataType | None = None,
93
+ settings: apicontractsv1.ArrayOfSetting | None = None,
94
+ line_items: apicontractsv1.ArrayOfLineItem | None = None,
95
+ ) -> tuple[ObjectifiedElement, type[APIOperationBase]]:
96
+ """
97
+ `Authorizes a credit card <https://developer.authorize.net/api/reference/index.html#payment-transactions-authorize-a-credit-card>`_.
98
+
99
+ :param amount: Dollar amount to authorize.
100
+ :type amount: ~decimal.Decimal
101
+ :param credit_card: A credit card.
102
+ :type credit_card: ~authorizenet.apicontractsv1.creditCardType
103
+ :param address: A customer address.
104
+ :type address: ~authorizenet.apicontractsv1.customerAddressType
105
+ :param order: Additional order information. Default is :py:obj:`None`.
106
+ :type order: ~authorizenet.apicontractsv1.orderType | None
107
+ :param customer_data: Additional customer data. Default is :py:obj:`None`.
108
+ :type customer_data: ~authorizenet.apicontractsv1.customerDataType | None
109
+ :param settings: Transaction settings. Default is :py:obj:`None`.
110
+ :type settings: ~authorizenet.apicontractsv1.ArrayOfSetting | None
111
+ :param line_items: An array of line items. Default is :py:obj:`None`.
112
+ :type line_items: ~authorizenet.apicontractsv1.ArrayOfLineItem | None
113
+ :returns: A tuple containing an Authorizenet API request element and controller class.
114
+ :rtype: tuple[~lxml.objectify.ObjectifiedElement, type[~authorizenet.apicontrollersbase.APIOperationBase]]
115
+
116
+ """
117
+ request = apicontractsv1.createTransactionRequest()
118
+ payment = apicontractsv1.paymentType()
119
+ payment.creditCard = credit_card
120
+ request.transactionRequest = build_transaction(
121
+ amount=amount,
122
+ payment=payment,
123
+ address=address,
124
+ order=order,
125
+ customer_data=customer_data,
126
+ settings=settings,
127
+ line_items=line_items,
128
+ )
129
+
130
+ request.transactionRequest.transactionType = (
131
+ apicontractsv1.transactionTypeEnum.authOnlyTransaction
132
+ )
133
+ return request, apicontrollers.createTransactionController
134
+
135
+
136
+ def capture_authorized_amount(
137
+ amount: Decimal,
138
+ ) -> tuple[ObjectifiedElement, type[APIOperationBase]]:
139
+ """
140
+ `Captures a previously authorized amount <https://developer.authorize.net/api/reference/index.html#payment-transactions-capture-a-previously-authorized-amount>`_.
141
+
142
+ :param amount: Dollar amount to capture.
143
+ :type amount: ~decimal.Decimal
144
+ :returns: A tuple containing an Authorizenet API request element and controller class.
145
+ :rtype: tuple[~lxml.objectify.ObjectifiedElement, type[~authorizenet.apicontrollersbase.APIOperationBase]]
146
+
147
+ """
148
+ request = apicontractsv1.createTransactionRequest()
149
+ request.transactionRequest = build_transaction(amount)
150
+ request.transactionRequest.transactionType = (
151
+ apicontractsv1.transactionTypeEnum.priorAuthCaptureTransaction
152
+ )
153
+ return request, apicontrollers.createTransactionController
154
+
155
+
156
+ def refund_credit_card(
157
+ amount: Decimal, credit_card: apicontractsv1.creditCardType
158
+ ) -> tuple[ObjectifiedElement, type[APIOperationBase]]:
159
+ """
160
+ `Refunds a credit card <https://developer.authorize.net/api/reference/index.html#payment-transactions-refund-a-transaction>`_.
161
+
162
+ :param amount: Dollar amount to refund.
163
+ :type amount: ~decimal.Decimal
164
+ :param credit_card: Destination credit card.
165
+ :type credit_card: ~authorizenet.apicontractsv1.creditCardType
166
+ :returns: A tuple containing an Authorizenet API request element and controller class.
167
+ :rtype: tuple[~lxml.objectify.ObjectifiedElement, type[~authorizenet.apicontrollersbase.APIOperationBase]]
168
+
169
+ """
170
+ request = apicontractsv1.createTransactionRequest()
171
+ payment = apicontractsv1.paymentType()
172
+ payment.creditCard = credit_card
173
+ request.transactionRequest = build_transaction(
174
+ amount=amount, payment=payment
175
+ )
176
+
177
+ request.transactionRequest.transactionType = (
178
+ apicontractsv1.transactionTypeEnum.refundTransaction
179
+ )
180
+ return request, apicontrollers.createTransactionController
181
+
182
+
183
+ def void_transaction(
184
+ reference_id: str,
185
+ ) -> tuple[ObjectifiedElement, type[APIOperationBase]]:
186
+ """
187
+ `Voids a transaction <https://developer.authorize.net/api/reference/index.html#payment-transactions-void-a-transaction>`_.
188
+
189
+ :param reference_id: Transaction reference id.
190
+ :type reference_id: str
191
+ :returns: A tuple containing an Authorizenet API request element and controller class.
192
+ :rtype: tuple[~lxml.objectify.ObjectifiedElement, type[~authorizenet.apicontrollersbase.APIOperationBase]]
193
+
194
+ """
195
+ request = apicontractsv1.createTransactionRequest()
196
+ request.transactionRequest = apicontractsv1.transactionRequestType()
197
+ request.transactionRequest.refTransId = reference_id
198
+ request.transactionRequest.transactionType = (
199
+ apicontractsv1.transactionTypeEnum.voidTransaction
200
+ )
201
+ return request, apicontrollers.createTransactionController
@@ -4,6 +4,21 @@ from django.db import models
4
4
  from django.utils.translation import gettext_lazy as _
5
5
 
6
6
 
7
+ class CreditCardType(models.TextChoices):
8
+ AMEX = apicontractsv1.cardTypeEnum.AmericanExpress, _("American Express")
9
+ """American express card."""
10
+ DINERS = apicontractsv1.cardTypeEnum.DinersClub, _("Diners Club")
11
+ """Diners club card."""
12
+ DISCOVER = apicontractsv1.cardTypeEnum.Discover, _("Discover")
13
+ """Disover card."""
14
+ JCB = apicontractsv1.cardTypeEnum.JCB, _("JCB")
15
+ """JCB card."""
16
+ MASTERCARD = apicontractsv1.cardTypeEnum.MasterCard, _("Mastercard")
17
+ """Mastercard card."""
18
+ VISA = apicontractsv1.cardTypeEnum.Visa, _("Visa")
19
+ """Visa card."""
20
+
21
+
7
22
  class CurrencyCode(models.TextChoices):
8
23
  USD = "USD", _("United States Dollar")
9
24
  """US dollar."""
@@ -0,0 +1,112 @@
1
+ from functools import cached_property
2
+
3
+ from authorizenet.apicontractsv1 import merchantAuthenticationType
4
+ from authorizenet.apicontrollersbase import APIOperationBase
5
+ from django.conf import settings
6
+ from django.core.exceptions import ImproperlyConfigured
7
+ from lxml.objectify import ObjectifiedElement
8
+
9
+ if not settings.configured:
10
+ from terminusgps import default_settings
11
+
12
+ settings.configure(default_settings)
13
+
14
+
15
+ class AuthorizenetControllerExecutionError(Exception):
16
+ """Raised when an Authorizenet API controller fails to execute."""
17
+
18
+ def __init__(self, message: str, code: str, *args, **kwargs) -> None:
19
+ super().__init__(message, *args, **kwargs)
20
+ self._message: str = message
21
+ self._code: str = code
22
+
23
+ def __str__(self) -> str:
24
+ return f"{self.code}: {self.message}"
25
+
26
+ @property
27
+ def message(self) -> str:
28
+ """An Authorizenet API error message."""
29
+ return self._message
30
+
31
+ @property
32
+ def code(self) -> str:
33
+ """An Authorizenet API error code."""
34
+ return self._code
35
+
36
+
37
+ class AuthorizenetService:
38
+ """A service for safely interacting with the Authorizenet API."""
39
+
40
+ REQUIRED_SETTINGS = (
41
+ "MERCHANT_AUTH_ENVIRONMENT",
42
+ "MERCHANT_AUTH_LOGIN_ID",
43
+ "MERCHANT_AUTH_TRANSACTION_KEY",
44
+ "MERCHANT_AUTH_VALIDATION_MODE",
45
+ )
46
+
47
+ def __init__(self) -> None:
48
+ """Raises :py:exc:`~django.core.exceptions.ImproperlyConfigured` if required settings weren't set."""
49
+ for setting in self.REQUIRED_SETTINGS:
50
+ if not hasattr(settings, setting):
51
+ raise ImproperlyConfigured(f"'{setting}' setting is required.")
52
+
53
+ def call_api(
54
+ self,
55
+ request: ObjectifiedElement,
56
+ controller_cls: type[APIOperationBase],
57
+ reference_id: str | None = None,
58
+ ) -> ObjectifiedElement:
59
+ """
60
+ Adds required authentication data to the request before executing it and returning its response.
61
+
62
+ If ``reference_id`` was provided, it is added to the request before execution.
63
+
64
+ :param request: An Authorizenet API request element.
65
+ :type request: ~lxml.objectify.ObjectifiedElement
66
+ :param controller_cls: An Authorizenet controller class.
67
+ :type controller_cls: type[~authorizenet.apicontrollersbase.APIOperationBase]
68
+ :param reference_id: An optional reference id string for the API call. Default is :py:obj:`None`.
69
+ :type reference_id: str | None
70
+ :raises AuthorizenetControllerExecutionError: If the API call failed.
71
+ :returns: An Authorizenet API response.
72
+ :rtype: ~lxml.objectify.ObjectifiedElement
73
+
74
+ """
75
+ request.merchantAuthentication = self.merchantAuthentication
76
+ if reference_id is not None:
77
+ request.refId = reference_id
78
+
79
+ controller = controller_cls(request)
80
+ controller.setenvironment(self.environment)
81
+ controller.execute()
82
+
83
+ response: ObjectifiedElement | None = controller.getresponse()
84
+ if response is None:
85
+ raise AuthorizenetControllerExecutionError(
86
+ message="No response from the Authorizenet API controller.",
87
+ code="1",
88
+ )
89
+ elif response is not None and response.messages.resultCode != "Ok":
90
+ raise AuthorizenetControllerExecutionError(
91
+ message=response.messages.message[0]["text"].text,
92
+ code=response.messages.message[0]["code"].text,
93
+ )
94
+ return response
95
+
96
+ @cached_property
97
+ def merchantAuthentication(self) -> merchantAuthenticationType:
98
+ """Merchant authentication element for Authorizenet API requests."""
99
+ return merchantAuthenticationType(
100
+ name=str(settings.MERCHANT_AUTH_LOGIN_ID),
101
+ transactionKey=str(settings.MERCHANT_AUTH_TRANSACTION_KEY),
102
+ )
103
+
104
+ @cached_property
105
+ def environment(self) -> str:
106
+ """Environment for Authorizenet API requests."""
107
+ return str(settings.MERCHANT_AUTH_ENVIRONMENT)
108
+
109
+ @cached_property
110
+ def validationMode(self) -> str:
111
+ """Validation mode for Authorizenet API requests."""
112
+ return str(settings.MERCHANT_AUTH_VALIDATION_MODE)
@@ -142,7 +142,7 @@ wheels = [
142
142
 
143
143
  [[package]]
144
144
  name = "django-stubs"
145
- version = "5.2.2"
145
+ version = "5.2.4"
146
146
  source = { registry = "https://pypi.org/simple" }
147
147
  dependencies = [
148
148
  { name = "django" },
@@ -150,22 +150,22 @@ dependencies = [
150
150
  { name = "types-pyyaml" },
151
151
  { name = "typing-extensions" },
152
152
  ]
153
- sdist = { url = "https://files.pythonhosted.org/packages/bc/27/ab9813da817a29ae69ec92af31ad8fc58ce3c904f23ea604bd3bdd9adc37/django_stubs-5.2.2.tar.gz", hash = "sha256:2a04b510c7a812f88223fd7e6d87fb4ea98717f19c8e5c8b59691d83ad40a8a6", size = 243049 }
153
+ sdist = { url = "https://files.pythonhosted.org/packages/fd/71/430901a1a799e1e66bb169a28e48a5f7b6e4ad5d6cadf60d9319d44b4181/django_stubs-5.2.4.tar.gz", hash = "sha256:89e1493d57ed091fdc990bf625bde77088e8660fcf6a3cdf0faf68e33c11c79b", size = 247751 }
154
154
  wheels = [
155
- { url = "https://files.pythonhosted.org/packages/55/cb/bb387a1d40691ad54fec2be9e5093becebd63cca0ccb9348cbb27602e1d1/django_stubs-5.2.2-py3-none-any.whl", hash = "sha256:79bd0fdbc78958a8f63e0b062bd9d03f1de539664476c0be62ade5f063c9e41e", size = 485188 },
155
+ { url = "https://files.pythonhosted.org/packages/e0/0a/6e1326888d2a8b5f390a28b543a9766105bff573641bbfd7edc7394ab5fe/django_stubs-5.2.4-py3-none-any.whl", hash = "sha256:000d59e826a15b032070d4b1b7f95b281005d690493173b95837c4c07b68dcf6", size = 490115 },
156
156
  ]
157
157
 
158
158
  [[package]]
159
159
  name = "django-stubs-ext"
160
- version = "5.2.2"
160
+ version = "5.2.4"
161
161
  source = { registry = "https://pypi.org/simple" }
162
162
  dependencies = [
163
163
  { name = "django" },
164
164
  { name = "typing-extensions" },
165
165
  ]
166
- sdist = { url = "https://files.pythonhosted.org/packages/fc/06/5e94715d103e6cc72380cb0d0b6682a7d5ad2c366cee478c94d77aad777d/django_stubs_ext-5.2.2.tar.gz", hash = "sha256:d9d151b919fe2438760f5bd938f03e1cb08c84d0651f9e5917f1313907e42683", size = 6244 }
166
+ sdist = { url = "https://files.pythonhosted.org/packages/63/9a/d3554928fca79bc8e28dccb7f9c410c132667a47a216b53850c268a94114/django_stubs_ext-5.2.4.tar.gz", hash = "sha256:920bb81c1fbf95b8882db7dcd3fff2d851a3c01d49aef9c7b5d79da40ff39fe6", size = 6488 }
167
167
  wheels = [
168
- { url = "https://files.pythonhosted.org/packages/4e/38/2903676f97f7902ee31984a06756b0e8836e897f4b617e1a03be4a43eb4f/django_stubs_ext-5.2.2-py3-none-any.whl", hash = "sha256:8833bbe32405a2a0ce168d3f75a87168f61bd16939caf0e8bf173bccbd8a44c5", size = 8816 },
168
+ { url = "https://files.pythonhosted.org/packages/59/20/4595270c02c5763ff751dab83df148f606de3738a236a4c961c71df7c005/django_stubs_ext-5.2.4-py3-none-any.whl", hash = "sha256:e539cb40c5281f291c285ba7ecd704e70343d8f610f13629c2b1521454364d1b", size = 9307 },
169
169
  ]
170
170
 
171
171
  [[package]]
@@ -298,7 +298,7 @@ wheels = [
298
298
 
299
299
  [[package]]
300
300
  name = "python-terminusgps"
301
- version = "45.6.1"
301
+ version = "45.8.0"
302
302
  source = { editable = "." }
303
303
  dependencies = [
304
304
  { name = "authorizenet" },
@@ -1,22 +0,0 @@
1
- Authentication
2
- ==============
3
-
4
- Authentication for each Authorizenet API call is handled by values defined in a Django ``settings.py`` module.
5
-
6
- Required settings:
7
-
8
- +-----------------------------------+---------------+
9
- | setting | type |
10
- +===================================+===============+
11
- | ``MERCHANT_AUTH_ENVIRONMENT`` | :py:obj:`str` |
12
- +-----------------------------------+---------------+
13
- | ``MERCHANT_AUTH_LOGIN_ID`` | :py:obj:`str` |
14
- +-----------------------------------+---------------+
15
- | ``MERCHANT_AUTH_TRANSACTION_KEY`` | :py:obj:`str` |
16
- +-----------------------------------+---------------+
17
- | ``MERCHANT_AUTH_VALIDATION_MODE`` | :py:obj:`str` |
18
- +-----------------------------------+---------------+
19
-
20
- .. automodule:: terminusgps.authorizenet.auth
21
- :synopsis: Provides functions for authenticating Authorizenet API calls.
22
- :members:
@@ -1,4 +0,0 @@
1
- Exceptions
2
- ==========
3
-
4
- .. autoexception:: terminusgps.authorizenet.controllers.AuthorizenetControllerExecutionError
@@ -1,20 +0,0 @@
1
- Usage
2
- =====
3
-
4
- Check the Authorizenet API documentation for expected attributes in each response.
5
-
6
- .. code:: python
7
-
8
- from terminusgps.authorizenet import api as anet
9
-
10
- # An Authorizenet 'createCustomerProfileRequest'
11
- response = anet.create_customer_profile(
12
- merchant_id="1",
13
- email="blake@terminusgps.com",
14
- description="Blake Nall"
15
- )
16
-
17
- # Authorizenet API calls may return None
18
- # Check first before trying to access attributes on it
19
- if response is not None and hasattr(response, "customerProfileId"):
20
- response.customerProfileId
@@ -1,2 +0,0 @@
1
- # TODO
2
- __all__ = []
@@ -1,38 +0,0 @@
1
- from authorizenet.apicontractsv1 import merchantAuthenticationType
2
- from django.conf import settings
3
-
4
-
5
- def get_merchant_auth() -> merchantAuthenticationType:
6
- """
7
- Returns the merchant authentication information for Authorizenet API controller execution.
8
-
9
- :returns: A merchant authentication object.
10
- :rtype: ~authorizenet.apicontractsv1.merchantAuthenticationType
11
-
12
- """
13
- return merchantAuthenticationType(
14
- name=str(settings.MERCHANT_AUTH_LOGIN_ID),
15
- transactionKey=str(settings.MERCHANT_AUTH_TRANSACTION_KEY),
16
- )
17
-
18
-
19
- def get_environment() -> str:
20
- """
21
- Returns the environment for Authorizenet API controller execution.
22
-
23
- :returns: An Authorizenet API environment string.
24
- :rtype: str
25
-
26
- """
27
- return settings.MERCHANT_AUTH_ENVIRONMENT
28
-
29
-
30
- def get_validation_mode() -> str:
31
- """
32
- Returns the validation mode for Authorizenet API controller execution.
33
-
34
- :returns: An Authorizenet API validation string.
35
- :rtype: str
36
-
37
- """
38
- return settings.MERCHANT_AUTH_VALIDATION_MODE
@@ -1,57 +0,0 @@
1
- from authorizenet.apicontrollersbase import APIOperationBase
2
- from lxml.objectify import ObjectifiedElement
3
-
4
-
5
- def execute_controller(
6
- controller: APIOperationBase, environment: str
7
- ) -> ObjectifiedElement:
8
- """
9
- Executes an Authorizenet API controller and returns its response.
10
-
11
- :param controller: An Authorizenet API controller.
12
- :type controller: ~authorizenet.apicontrollersbase.APIOperationBase
13
- :param environment: Authorizenet environment to execute the controller in.
14
- :type environment: :py:obj:`str`
15
- :param merchant_auth: Authorizenet merchant authentication element.
16
- :type merchant_auth: ~authorizenet.apicontractsv1.merchantAuthenticationType
17
- :raises AuthorizenetControllerExecutionError: If the API call fails.
18
- :returns: An Authorizenet API response.
19
- :rtype: ~lxml.objectify.ObjectifiedElement
20
-
21
- """
22
- controller.setenvironment(environment)
23
- controller.execute()
24
- response: ObjectifiedElement | None = controller.getresponse()
25
-
26
- if response is None:
27
- raise AuthorizenetControllerExecutionError(
28
- message="Authorizenet controller response didn't exist.", code="1"
29
- )
30
- elif response is not None and response.messages.resultCode != "Ok":
31
- raise AuthorizenetControllerExecutionError(
32
- message=response.messages.message[0]["text"].text,
33
- code=response.messages.message[0]["code"].text,
34
- )
35
- return response
36
-
37
-
38
- class AuthorizenetControllerExecutionError(Exception):
39
- """Raised when an Authorizenet API controller fails to execute."""
40
-
41
- def __init__(self, message: str, code: str, *args, **kwargs) -> None:
42
- super().__init__(message, *args, **kwargs)
43
- self._message: str = message
44
- self._code: str = code
45
-
46
- def __str__(self) -> str:
47
- return f"{self.code}: {self.message}"
48
-
49
- @property
50
- def message(self) -> str:
51
- """An Authorizenet API error message."""
52
- return self._message
53
-
54
- @property
55
- def code(self) -> str:
56
- """An Authorizenet API error code."""
57
- return self._code
@@ -1,66 +0,0 @@
1
- from functools import cached_property
2
- from typing import Callable
3
-
4
- from authorizenet.apicontractsv1 import merchantAuthenticationType
5
- from django.conf import settings
6
- from django.core.exceptions import ImproperlyConfigured
7
- from lxml.objectify import ObjectifiedElement
8
-
9
- from .auth import get_environment, get_merchant_auth, get_validation_mode
10
- from .controllers import (
11
- AuthorizenetControllerExecutionError,
12
- execute_controller,
13
- )
14
-
15
-
16
- class AuthorizenetService:
17
- """A service for safely interacting with the Authorizenet API."""
18
-
19
- REQUIRED_SETTINGS = (
20
- "MERCHANT_AUTH_ENVIRONMENT",
21
- "MERCHANT_AUTH_LOGIN_ID",
22
- "MERCHANT_AUTH_TRANSACTION_KEY",
23
- "MERCHANT_AUTH_VALIDATION_MODE",
24
- )
25
-
26
- def __init__(self) -> None:
27
- """Raises :py:exc:`~django.core.exceptions.ImproperlyConfigured` if required settings weren't set."""
28
- for setting in self.REQUIRED_SETTINGS:
29
- if not hasattr(settings, setting):
30
- raise ImproperlyConfigured(f"'{setting}' setting is required.")
31
-
32
- def request(self, func: Callable, *args, **kwargs) -> ObjectifiedElement:
33
- """
34
- Calls the Authorizenet API function with arguments and returns the result.
35
-
36
- :param func: An Authorizenet API function.
37
- :type func: ~typing.Callable
38
- :param args: Positional arguments for the API call.
39
- :param kwargs: Keyword arguments for the API call.
40
- :raises ~terminusgps.authorizenet.controllers.AuthorizenetControllerExecutionError: If the API call failed.
41
- :returns: The Authorizenet API call response.
42
- :rtype: ~lxml.objectify.ObjectifiedElement
43
-
44
- """
45
- try:
46
- request, controller_cls = func(*args, **kwargs)
47
- request.merchantAuthentication = self.merchantAuthentication
48
- controller = controller_cls(request)
49
- return execute_controller(controller, self.environment)
50
- except AuthorizenetControllerExecutionError:
51
- raise
52
-
53
- @cached_property
54
- def merchantAuthentication(self) -> merchantAuthenticationType:
55
- """Merchant authentication element for Authorizenet API requests."""
56
- return get_merchant_auth()
57
-
58
- @cached_property
59
- def environment(self) -> str:
60
- """Environment for Authorizenet API requests."""
61
- return get_environment()
62
-
63
- @cached_property
64
- def validationMode(self) -> str:
65
- """Validation mode for Authorizenet API requests."""
66
- return get_validation_mode()