python-terminusgps 22.0.2__py3-none-any.whl → 23.0.0__py3-none-any.whl
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.
Potentially problematic release.
This version of python-terminusgps might be problematic. Click here for more details.
- {python_terminusgps-22.0.2.dist-info → python_terminusgps-23.0.0.dist-info}/METADATA +1 -4
- {python_terminusgps-22.0.2.dist-info → python_terminusgps-23.0.0.dist-info}/RECORD +15 -16
- terminusgps/authorizenet/auth.py +0 -1
- terminusgps/authorizenet/profiles/addresses.py +70 -28
- terminusgps/authorizenet/profiles/base.py +17 -73
- terminusgps/authorizenet/profiles/customers.py +142 -42
- terminusgps/authorizenet/profiles/payments.py +117 -32
- terminusgps/authorizenet/subscriptions.py +12 -38
- terminusgps/authorizenet/utils.py +61 -0
- terminusgps/aws/secrets.py +2 -1
- terminusgps/settings.py +2 -0
- terminusgps/wialon/session.py +2 -23
- terminusgps/wialon/utils.py +35 -26
- terminusgps/opencv/__init__.py +0 -0
- terminusgps/opencv/barcodes.py +0 -31
- {python_terminusgps-22.0.2.dist-info → python_terminusgps-23.0.0.dist-info}/WHEEL +0 -0
- {python_terminusgps-22.0.2.dist-info → python_terminusgps-23.0.0.dist-info}/licenses/COPYING +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-terminusgps
|
|
3
|
-
Version:
|
|
3
|
+
Version: 23.0.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://app.terminusgps.com/docs/apps/python-terminusgps/index.html
|
|
6
6
|
Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
|
|
@@ -17,8 +17,5 @@ Requires-Dist: authorizenet>=1.1.5
|
|
|
17
17
|
Requires-Dist: boto3>=1.34.144
|
|
18
18
|
Requires-Dist: django>=5.1.5
|
|
19
19
|
Requires-Dist: matplotlib>=3.10.1
|
|
20
|
-
Requires-Dist: opencv-python>=4.11.0.86
|
|
21
|
-
Requires-Dist: pyqt6>=6.8.1
|
|
22
20
|
Requires-Dist: python-wialon>=1.2.4
|
|
23
|
-
Requires-Dist: pyzbar>=0.1.9
|
|
24
21
|
Requires-Dist: twilio>=9.4.5
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
terminusgps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
terminusgps/settings.py,sha256=
|
|
2
|
+
terminusgps/settings.py,sha256=UBj2tvuBzQDEz05PeFqHG4_ise3Bl6Iu7pxcrtjNaYE,568
|
|
3
3
|
terminusgps/authorizenet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
terminusgps/authorizenet/auth.py,sha256=
|
|
5
|
-
terminusgps/authorizenet/subscriptions.py,sha256=
|
|
4
|
+
terminusgps/authorizenet/auth.py,sha256=l3sPpLycqVO-gppU2sH7YAScuHmm3xXylxW6Gdu6a7Q,563
|
|
5
|
+
terminusgps/authorizenet/subscriptions.py,sha256=W2oIb1oWXEhtET4bHpUWzuLIl3XbAqMkghAXy0AyWAQ,1366
|
|
6
|
+
terminusgps/authorizenet/utils.py,sha256=q8ReDcYIk2eG6d36IM1vZorfeAxKhyQRLdtrIpKTNwA,2212
|
|
6
7
|
terminusgps/authorizenet/profiles/__init__.py,sha256=QKjMKcXpCSCEhv6LBCz0yGP8t0VxAyKyYRS4_HLYnxQ,114
|
|
7
|
-
terminusgps/authorizenet/profiles/addresses.py,sha256=
|
|
8
|
-
terminusgps/authorizenet/profiles/base.py,sha256=
|
|
9
|
-
terminusgps/authorizenet/profiles/customers.py,sha256=
|
|
10
|
-
terminusgps/authorizenet/profiles/payments.py,sha256=
|
|
8
|
+
terminusgps/authorizenet/profiles/addresses.py,sha256=xAiqETexGXicYOd_M3a4SBuHweeZPDlfWhtpqdvls-g,5466
|
|
9
|
+
terminusgps/authorizenet/profiles/base.py,sha256=UTudVCju5-pLJQwoK0IfuT8OvxZVJCX3vApveUY3XE4,2286
|
|
10
|
+
terminusgps/authorizenet/profiles/customers.py,sha256=ehtsWhPt0zF8_3dFlgFBWHA4NbQb-6AvOhtjTy3uQDg,8639
|
|
11
|
+
terminusgps/authorizenet/profiles/payments.py,sha256=jcF_uJcaeU2JR5Sqza0LccsQGe9F-SUCjvg3o3QskIk,8808
|
|
11
12
|
terminusgps/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
terminusgps/aws/secrets.py,sha256=
|
|
13
|
+
terminusgps/aws/secrets.py,sha256=MxQEmmBLpMUQ2tYAsHdCYf-7RZ84LiogdKcTsACX5dw,361
|
|
13
14
|
terminusgps/aws/ses.py,sha256=sDca2GjMaZvUt3kUPtGCpcqHiOkij9lMV1ogZofgYUU,2372
|
|
14
|
-
terminusgps/opencv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
terminusgps/opencv/barcodes.py,sha256=NmdpUnixlGJI-6shbKGcb6-M_IZvgSZJRDVSsI7pcFg,669
|
|
16
15
|
terminusgps/twilio/__init__.py,sha256=dYo41F2jft_eHDWSUtSpKGSRG1bewq_qClqilUJZkYM,33
|
|
17
16
|
terminusgps/twilio/caller.py,sha256=nHCIwUulo-C_v2orsp6OY0nuJA6YetSkxpMTwrRQ-uA,1831
|
|
18
17
|
terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
18
|
terminusgps/wialon/constants.py,sha256=n1ux68oWYWnzeZdN811Iw_Lk78KBM0YMJMoonn67uZY,1723
|
|
20
19
|
terminusgps/wialon/flags.py,sha256=_NIL3mrEGhvI5YISVjimjYtUatdgOqhZPJX368MtKSU,13959
|
|
21
20
|
terminusgps/wialon/logger.py,sha256=nUBsaLMaffSaAHGHAM6mFXDzix9hbNecVtA3INVtii8,968
|
|
22
|
-
terminusgps/wialon/session.py,sha256=
|
|
23
|
-
terminusgps/wialon/utils.py,sha256=
|
|
21
|
+
terminusgps/wialon/session.py,sha256=t3d7trBQRxnXFnex73alSSD5n2LDTZQ9VAtzUKIERto,9670
|
|
22
|
+
terminusgps/wialon/utils.py,sha256=Y7fo9a81w84b3N39h1AmVLS-AnC3aRT5R137hIsqCHU,6723
|
|
24
23
|
terminusgps/wialon/items/__init__.py,sha256=3BVthghekMvhMQSzQgBMlD_phxmKSmp3Zvmo-z0O8Bs,211
|
|
25
24
|
terminusgps/wialon/items/base.py,sha256=EAtzlnCvB7WTu3iX1bV1-S9TvwXc-Eez7e0X4TMiK9U,6943
|
|
26
25
|
terminusgps/wialon/items/resource.py,sha256=qmQKEakJa9LH5YkI9-iBDA0hsotc5ctcwdb7kZuXdfQ,14549
|
|
@@ -29,7 +28,7 @@ terminusgps/wialon/items/route.py,sha256=PTJx1gmT_PGz7nlSSUS2VzjYD-aAuvkEOIdIFne
|
|
|
29
28
|
terminusgps/wialon/items/unit.py,sha256=-jQL5Alvz77FZWteYNM9VNX15jtCufI-DWRRj3hRsgA,8539
|
|
30
29
|
terminusgps/wialon/items/unit_group.py,sha256=vGVJMBY1BrKK0tP-C1DCIz4Bh6P-HKRnKY1g8g95UIs,5031
|
|
31
30
|
terminusgps/wialon/items/user.py,sha256=INwAibQVmjNNelepQXMfDevgJqMvIjHHgEjAMLRvhB0,7082
|
|
32
|
-
python_terminusgps-
|
|
33
|
-
python_terminusgps-
|
|
34
|
-
python_terminusgps-
|
|
35
|
-
python_terminusgps-
|
|
31
|
+
python_terminusgps-23.0.0.dist-info/METADATA,sha256=mWo-bctKKFZdbhH0ROVcDsqvN9kC58jMl1o49TQkFCI,946
|
|
32
|
+
python_terminusgps-23.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
33
|
+
python_terminusgps-23.0.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
34
|
+
python_terminusgps-23.0.0.dist-info/RECORD,,
|
terminusgps/authorizenet/auth.py
CHANGED
|
@@ -1,47 +1,59 @@
|
|
|
1
1
|
from authorizenet import apicontractsv1, apicontrollers
|
|
2
|
-
from authorizenet.apicontractsv1 import customerAddressType
|
|
3
2
|
|
|
4
3
|
from terminusgps.authorizenet.profiles.base import AuthorizenetSubProfileBase
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
class AddressProfile(AuthorizenetSubProfileBase):
|
|
8
|
-
|
|
7
|
+
"""An Authorizenet customer address profile."""
|
|
8
|
+
|
|
9
|
+
def create(self, shipping_addr: apicontractsv1.customerAddressType) -> int:
|
|
9
10
|
"""
|
|
10
|
-
Creates an
|
|
11
|
+
Creates an Authorizenet address profile.
|
|
11
12
|
|
|
12
|
-
:param shipping_addr: An
|
|
13
|
+
:param shipping_addr: An Authorizenet customer address.
|
|
13
14
|
:type shipping_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
|
|
14
|
-
:raises
|
|
15
|
-
:
|
|
16
|
-
:returns: The new address profile's id.
|
|
15
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
16
|
+
:returns: A new address profile id.
|
|
17
17
|
:rtype: :py:obj:`int`
|
|
18
18
|
|
|
19
19
|
"""
|
|
20
|
-
shipping_addr = kwargs.get("shipping_addr")
|
|
21
|
-
|
|
22
|
-
if not shipping_addr:
|
|
23
|
-
raise ValueError("'shipping_addr' is required on creation.")
|
|
24
|
-
|
|
25
|
-
if not isinstance(shipping_addr, customerAddressType):
|
|
26
|
-
raise TypeError(
|
|
27
|
-
f"'shipping_addr' must be customerAddressType, got '{type(shipping_addr)}'."
|
|
28
|
-
)
|
|
29
|
-
|
|
30
20
|
response = self._authorizenet_create_shipping_address(shipping_addr)
|
|
31
21
|
return int(response.customerAddressId)
|
|
32
22
|
|
|
33
|
-
def update(self, shipping_addr: customerAddressType) -> dict:
|
|
34
|
-
"""
|
|
23
|
+
def update(self, shipping_addr: apicontractsv1.customerAddressType) -> dict:
|
|
24
|
+
"""
|
|
25
|
+
Updates the Authorizenet address profile.
|
|
26
|
+
|
|
27
|
+
:param shipping_addr: A customer shipping address.
|
|
28
|
+
:type shipping_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
|
|
29
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
30
|
+
:returns: An Authorizenet API response.
|
|
31
|
+
:rtype: :py:obj:`dict`
|
|
32
|
+
|
|
33
|
+
"""
|
|
35
34
|
return self._authorizenet_update_shipping_address(shipping_addr)
|
|
36
35
|
|
|
37
36
|
def delete(self) -> dict:
|
|
38
|
-
"""
|
|
39
|
-
|
|
37
|
+
"""
|
|
38
|
+
Deletes the Authorizenet address profile.
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
41
|
+
:returns: An Authorizenet API response, if any.
|
|
42
|
+
:rtype: :py:obj:`dict`
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
return self._authorizenet_delete_shipping_address()
|
|
43
46
|
|
|
44
47
|
def _authorizenet_get_shipping_address(self) -> dict:
|
|
48
|
+
"""
|
|
49
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerShippingAddressRequest` using the Authorizenet API.
|
|
50
|
+
|
|
51
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
52
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
53
|
+
:returns: An Authorizenet API response.
|
|
54
|
+
:rtype: :py:obj:`dict`
|
|
55
|
+
|
|
56
|
+
"""
|
|
45
57
|
assert self.id, "'id' was not set."
|
|
46
58
|
|
|
47
59
|
request = apicontractsv1.getCustomerShippingAddressRequest(
|
|
@@ -53,7 +65,18 @@ class AddressProfile(AuthorizenetSubProfileBase):
|
|
|
53
65
|
response = self.execute_controller(controller)
|
|
54
66
|
return response
|
|
55
67
|
|
|
56
|
-
def _authorizenet_create_shipping_address(
|
|
68
|
+
def _authorizenet_create_shipping_address(
|
|
69
|
+
self, addr: apicontractsv1.customerAddressType
|
|
70
|
+
) -> dict:
|
|
71
|
+
"""
|
|
72
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerShippingAddressRequest` using the Authorizenet API.
|
|
73
|
+
|
|
74
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
75
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
76
|
+
:returns: An Authorizenet API response.
|
|
77
|
+
:rtype: :py:obj:`dict`
|
|
78
|
+
|
|
79
|
+
"""
|
|
57
80
|
request = apicontractsv1.createCustomerShippingAddressRequest(
|
|
58
81
|
merchantAuthentication=self.merchantAuthentication,
|
|
59
82
|
customerProfileId=self.customerProfileId,
|
|
@@ -64,10 +87,21 @@ class AddressProfile(AuthorizenetSubProfileBase):
|
|
|
64
87
|
response = self.execute_controller(controller)
|
|
65
88
|
return response
|
|
66
89
|
|
|
67
|
-
def _authorizenet_update_shipping_address(
|
|
90
|
+
def _authorizenet_update_shipping_address(
|
|
91
|
+
self, addr: apicontractsv1.customerAddressType
|
|
92
|
+
) -> dict:
|
|
93
|
+
"""
|
|
94
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.updateCustomerShippingAddressRequest` using the Authorizenet API.
|
|
95
|
+
|
|
96
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
97
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
98
|
+
:returns: An Authorizenet API response.
|
|
99
|
+
:rtype: :py:obj:`dict`
|
|
100
|
+
|
|
101
|
+
"""
|
|
68
102
|
assert self.id, "'id' was not set."
|
|
69
|
-
addr.customerAddressId = self.id
|
|
70
103
|
|
|
104
|
+
addr.customerAddressId = self.id
|
|
71
105
|
request = apicontractsv1.updateCustomerShippingAddressRequest(
|
|
72
106
|
merchantAuthentication=self.merchantAuthentication,
|
|
73
107
|
customerProfileId=self.customerProfileId,
|
|
@@ -75,10 +109,18 @@ class AddressProfile(AuthorizenetSubProfileBase):
|
|
|
75
109
|
default=self.default,
|
|
76
110
|
)
|
|
77
111
|
controller = apicontrollers.updateCustomerShippingAddressController(request)
|
|
78
|
-
|
|
79
|
-
return response
|
|
112
|
+
return self.execute_controller(controller)
|
|
80
113
|
|
|
81
114
|
def _authorizenet_delete_shipping_address(self) -> dict:
|
|
115
|
+
"""
|
|
116
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerShippingAddressRequest` using the Authorizenet API.
|
|
117
|
+
|
|
118
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
119
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
120
|
+
:returns: An Authorizenet API response.
|
|
121
|
+
:rtype: :py:obj:`dict`
|
|
122
|
+
|
|
123
|
+
"""
|
|
82
124
|
assert self.id, "'id' was not set."
|
|
83
125
|
|
|
84
126
|
request = apicontractsv1.deleteCustomerShippingAddressRequest(
|
|
@@ -1,81 +1,40 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
+
from typing import override
|
|
2
3
|
|
|
3
|
-
from authorizenet
|
|
4
|
-
from authorizenet.apicontrollersbase import APIOperationBase
|
|
4
|
+
from authorizenet import apicontractsv1
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from terminusgps.authorizenet.auth import get_merchant_auth, get_validation_mode
|
|
7
|
+
from terminusgps.authorizenet.utils import ControllerExecutionMixin
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class AuthorizenetProfileBase:
|
|
10
|
+
class AuthorizenetProfileBase(ControllerExecutionMixin):
|
|
10
11
|
def __init__(
|
|
11
|
-
self, merchant_id: int | str, id: int | str | None = None, **kwargs
|
|
12
|
+
self, merchant_id: int | str, id: int | str | None = None, *args, **kwargs
|
|
12
13
|
) -> None:
|
|
13
14
|
self._merchantCustomerId = merchant_id
|
|
14
|
-
self._id =
|
|
15
|
+
self._id = int(id) if id else self.create(*args, **kwargs)
|
|
15
16
|
|
|
17
|
+
@override
|
|
16
18
|
def __str__(self) -> str:
|
|
17
19
|
return f"#{self.id}"
|
|
18
20
|
|
|
19
|
-
def execute_controller(self, controller: APIOperationBase) -> dict:
|
|
20
|
-
"""
|
|
21
|
-
Executes an Authorize.NET controller and returns its response.
|
|
22
|
-
|
|
23
|
-
:param controller: An Authorize.NET API controller.
|
|
24
|
-
:raises ValueError: If the API call fails.
|
|
25
|
-
:returns: The Authorize.NET API response.
|
|
26
|
-
:rtype: :py:obj:`dict`
|
|
27
|
-
|
|
28
|
-
"""
|
|
29
|
-
controller.setenvironment(self.environment)
|
|
30
|
-
controller.execute()
|
|
31
|
-
response = controller.getresponse()
|
|
32
|
-
|
|
33
|
-
if response.messages.resultCode != "Ok":
|
|
34
|
-
raise ValueError(response.messages.message[0]["text"].text)
|
|
35
|
-
return response
|
|
36
|
-
|
|
37
21
|
@property
|
|
38
22
|
def merchantCustomerId(self) -> str:
|
|
39
|
-
"""
|
|
40
|
-
An internally designated customer id.
|
|
41
|
-
|
|
42
|
-
:type: :py:obj:`str`
|
|
43
|
-
|
|
44
|
-
"""
|
|
23
|
+
"""An internally designated customer id."""
|
|
45
24
|
return str(self._merchantCustomerId)
|
|
46
25
|
|
|
47
26
|
@property
|
|
48
27
|
def id(self) -> str:
|
|
49
|
-
"""
|
|
50
|
-
An Authorizenet generated id.
|
|
51
|
-
|
|
52
|
-
:type: :py:obj:`str`
|
|
53
|
-
|
|
54
|
-
"""
|
|
28
|
+
"""An Authorizenet generated id."""
|
|
55
29
|
return str(self._id)
|
|
56
30
|
|
|
57
31
|
@property
|
|
58
|
-
def merchantAuthentication(self) -> merchantAuthenticationType:
|
|
59
|
-
"""
|
|
60
|
-
Merchant authentication for Authorizenet API calls.
|
|
61
|
-
|
|
62
|
-
:type: :py:obj:`~authorizenet.apicontractsv1.merchantAuthenticationType`
|
|
63
|
-
|
|
64
|
-
"""
|
|
32
|
+
def merchantAuthentication(self) -> apicontractsv1.merchantAuthenticationType:
|
|
33
|
+
"""Merchant authentication for Authorizenet API calls."""
|
|
65
34
|
return get_merchant_auth()
|
|
66
35
|
|
|
67
|
-
@property
|
|
68
|
-
def environment(self) -> str:
|
|
69
|
-
"""
|
|
70
|
-
Environment for Authorizenet API calls to execute within.
|
|
71
|
-
|
|
72
|
-
:type: :py:obj:`str`
|
|
73
|
-
|
|
74
|
-
"""
|
|
75
|
-
return get_environment()
|
|
76
|
-
|
|
77
36
|
@abstractmethod
|
|
78
|
-
def create(self, **kwargs) -> int:
|
|
37
|
+
def create(self, *args, **kwargs) -> int:
|
|
79
38
|
raise NotImplementedError("Subclasses must implement this method.")
|
|
80
39
|
|
|
81
40
|
@abstractmethod
|
|
@@ -93,34 +52,19 @@ class AuthorizenetSubProfileBase(AuthorizenetProfileBase):
|
|
|
93
52
|
) -> None:
|
|
94
53
|
self._customerProfileId = customer_profile_id
|
|
95
54
|
self._default = default
|
|
96
|
-
|
|
55
|
+
super().__init__(*args, **kwargs)
|
|
97
56
|
|
|
98
57
|
@property
|
|
99
58
|
def validationMode(self) -> str:
|
|
100
|
-
"""
|
|
101
|
-
The validation mode for Authorizenet API calls.
|
|
102
|
-
|
|
103
|
-
:type: :py:obj:`str`
|
|
104
|
-
|
|
105
|
-
"""
|
|
59
|
+
"""The validation mode for Authorizenet API calls."""
|
|
106
60
|
return get_validation_mode()
|
|
107
61
|
|
|
108
62
|
@property
|
|
109
63
|
def default(self) -> str:
|
|
110
|
-
"""
|
|
111
|
-
Whether or not the sub profile is set as default in Authorizenet.
|
|
112
|
-
|
|
113
|
-
:type: :py:obj:`str`
|
|
114
|
-
|
|
115
|
-
"""
|
|
64
|
+
"""Whether or not the sub profile is set as default in Authorizenet."""
|
|
116
65
|
return str(self._default).lower()
|
|
117
66
|
|
|
118
67
|
@property
|
|
119
68
|
def customerProfileId(self) -> str:
|
|
120
|
-
"""
|
|
121
|
-
An Authorizenet generated customer profile id.
|
|
122
|
-
|
|
123
|
-
:py:obj:`str`
|
|
124
|
-
|
|
125
|
-
"""
|
|
69
|
+
"""An Authorizenet generated customer profile id."""
|
|
126
70
|
return str(self._customerProfileId)
|
|
@@ -2,55 +2,119 @@ from authorizenet import apicontractsv1, apicontrollers
|
|
|
2
2
|
from authorizenet.apicontractsv1 import customerProfileType
|
|
3
3
|
|
|
4
4
|
from terminusgps.authorizenet.profiles.base import AuthorizenetProfileBase
|
|
5
|
+
from terminusgps.authorizenet.utils import get_customer_profile_ids
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class CustomerProfile(AuthorizenetProfileBase):
|
|
8
|
-
|
|
9
|
+
"""An Authorizenet customer profile."""
|
|
10
|
+
|
|
11
|
+
def create(self, email: str, desc: str | None = None) -> int:
|
|
9
12
|
"""
|
|
10
|
-
Creates
|
|
13
|
+
Creates a customer profile using the Authorizenet API and returns its id.
|
|
11
14
|
|
|
12
|
-
:param email: An email address
|
|
15
|
+
:param email: An email address.
|
|
13
16
|
:type email: :py:obj:`str`
|
|
14
|
-
:param desc:
|
|
17
|
+
:param desc: An optional description.
|
|
15
18
|
:type desc: :py:obj:`str` | :py:obj:`None`
|
|
19
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
16
20
|
:returns: The new customer profile id.
|
|
17
21
|
:rtype: :py:obj:`int`
|
|
18
22
|
|
|
19
23
|
"""
|
|
20
|
-
|
|
21
|
-
raise ValueError("'email' is required for creation.")
|
|
22
|
-
|
|
23
|
-
response = self._authorizenet_create_customer_profile(
|
|
24
|
-
email=kwargs["email"], desc=kwargs.get("desc", "")
|
|
25
|
-
)
|
|
24
|
+
response = self._authorizenet_create_customer_profile(email=email, desc=desc)
|
|
26
25
|
return int(response.customerProfileId)
|
|
27
26
|
|
|
28
|
-
def update(self, email: str, desc: str = "") ->
|
|
29
|
-
"""
|
|
30
|
-
|
|
27
|
+
def update(self, email: str, desc: str = "") -> None:
|
|
28
|
+
"""
|
|
29
|
+
Updates the customer profile.
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
:param email: An email address.
|
|
32
|
+
:type email: :py:obj:`str`
|
|
33
|
+
:param desc: An optional description. Default is ``""``
|
|
34
|
+
:type desc: :py:obj:`str`
|
|
35
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
36
|
+
:returns: Nothing.
|
|
37
|
+
:rtype: :py:obj:`None`
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
self._authorizenet_update_customer_profile(email, desc)
|
|
41
|
+
|
|
42
|
+
def delete(self) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Deletes the customer profile.
|
|
45
|
+
|
|
46
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
47
|
+
:returns: Nothing.
|
|
48
|
+
:rtype: :py:obj:`None`
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
self._authorizenet_delete_customer_profile()
|
|
35
52
|
|
|
36
53
|
def get_payment_profiles(self) -> list[dict] | None:
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
"""
|
|
55
|
+
Returns a list of the customer's payment profiles.
|
|
56
|
+
|
|
57
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
58
|
+
:returns: A list of customer payment profiles, if they exist.
|
|
59
|
+
:rtype: :py:obj:`list` | :py:obj:`None`
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
response = self._authorizenet_get_customer_profile(issuer_info=False)
|
|
63
|
+
return response.get("paymentProfiles") if response else None
|
|
64
|
+
|
|
65
|
+
def get_addresses(self) -> list[dict] | None:
|
|
66
|
+
"""
|
|
67
|
+
Returns a list of the customer's address profiles.
|
|
68
|
+
|
|
69
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
70
|
+
:returns: A list of customer shipping addresses, if they exist.
|
|
71
|
+
:rtype: :py:obj:`list` | :py:obj:`None`
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
response = self._authorizenet_get_customer_profile(issuer_info=False)
|
|
75
|
+
return response.get("shipToList") if response else None
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def exists(self) -> bool:
|
|
79
|
+
"""Whether or not the customer profile exists in Authorizenet."""
|
|
80
|
+
profile_ids = get_customer_profile_ids()
|
|
81
|
+
return int(self.id) in profile_ids if self.id is not None else False
|
|
82
|
+
|
|
83
|
+
def _authorizenet_get_customer_profile_ids(self) -> dict | None:
|
|
84
|
+
"""
|
|
85
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerProfileIdsRequest` using the Authorizenet API.
|
|
86
|
+
|
|
87
|
+
`getCustomerProfileIdsRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-get-customer-profile-ids>`_
|
|
42
88
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
89
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
90
|
+
:returns: An Authorizenet API response, if any.
|
|
91
|
+
:rtype: :py:obj:`dict` | :py:obj:`None`
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
request = apicontractsv1.getCustomerProfileIdsRequest(
|
|
95
|
+
merchantAuthentication=self.merchantAuthentication
|
|
47
96
|
)
|
|
48
|
-
|
|
97
|
+
controller = apicontrollers.getCustomerProfileIdsController(request)
|
|
98
|
+
return self.execute_controller(controller)
|
|
49
99
|
|
|
50
100
|
def _authorizenet_create_customer_profile(
|
|
51
101
|
self, email: str, desc: str = ""
|
|
52
|
-
) -> dict
|
|
53
|
-
"""
|
|
102
|
+
) -> dict | None:
|
|
103
|
+
"""
|
|
104
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerProfileRequest` using the Authorizenet API.
|
|
105
|
+
|
|
106
|
+
`createCustomerProfileRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-create-customer-profile>`_
|
|
107
|
+
|
|
108
|
+
:param email: An email address.
|
|
109
|
+
:type email: :py:obj:`str`
|
|
110
|
+
:param desc: An optional description.
|
|
111
|
+
:type desc: :py:obj:`str`
|
|
112
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
113
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
114
|
+
:returns: An Authorizenet API response, if any.
|
|
115
|
+
:rtype: :py:obj:`dict` | :py:obj:`None`
|
|
116
|
+
|
|
117
|
+
"""
|
|
54
118
|
request = apicontractsv1.createCustomerProfileRequest(
|
|
55
119
|
merchantAuthentication=self.merchantAuthentication,
|
|
56
120
|
profile=customerProfileType(
|
|
@@ -60,11 +124,24 @@ class CustomerProfile(AuthorizenetProfileBase):
|
|
|
60
124
|
),
|
|
61
125
|
)
|
|
62
126
|
controller = apicontrollers.createCustomerProfileController(request)
|
|
63
|
-
|
|
64
|
-
|
|
127
|
+
return self.execute_controller(controller)
|
|
128
|
+
|
|
129
|
+
def _authorizenet_get_customer_profile(
|
|
130
|
+
self, issuer_info: bool = True
|
|
131
|
+
) -> dict | None:
|
|
132
|
+
"""
|
|
133
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerProfileRequest` using the Authorizenet API.
|
|
65
134
|
|
|
66
|
-
|
|
67
|
-
|
|
135
|
+
`getCustomerProfileRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-get-customer-profile>`_
|
|
136
|
+
|
|
137
|
+
:param issuer_info: Whether or not to include issuer info in the response.
|
|
138
|
+
:type issuer_info: :py:obj:`bool`
|
|
139
|
+
:raises AssertionError: If :py:attr:`id` was not set.
|
|
140
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
141
|
+
:returns: An Authorizenet API response, if any.
|
|
142
|
+
:rtype: :py:obj:`dict` | :py:obj:`None`
|
|
143
|
+
|
|
144
|
+
"""
|
|
68
145
|
assert self.id, "'id' was not set."
|
|
69
146
|
|
|
70
147
|
request = apicontractsv1.getCustomerProfileRequest(
|
|
@@ -73,11 +150,26 @@ class CustomerProfile(AuthorizenetProfileBase):
|
|
|
73
150
|
includeIssuerInfo=str(issuer_info).lower(),
|
|
74
151
|
)
|
|
75
152
|
controller = apicontrollers.getCustomerProfileController(request)
|
|
76
|
-
|
|
77
|
-
return response
|
|
153
|
+
return self.execute_controller(controller)
|
|
78
154
|
|
|
79
|
-
def _authorizenet_update_customer_profile(
|
|
80
|
-
|
|
155
|
+
def _authorizenet_update_customer_profile(
|
|
156
|
+
self, email: str, desc: str = ""
|
|
157
|
+
) -> dict | None:
|
|
158
|
+
"""
|
|
159
|
+
Executes an :py:obj:`~authorizenet.apicontractsv1.updateCustomerProfileRequest` using the Authorizenet API.
|
|
160
|
+
|
|
161
|
+
`updateCustomerProfileRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-update-customer-profile>`_
|
|
162
|
+
|
|
163
|
+
:param email: An email address.
|
|
164
|
+
:type email: :py:obj:`str`
|
|
165
|
+
:param desc: An optional description.
|
|
166
|
+
:type desc: :py:obj:`str`
|
|
167
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
168
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
169
|
+
:returns: An Authorizenet API response, if any.
|
|
170
|
+
:rtype: :py:obj:`dict` | :py:obj:`None`
|
|
171
|
+
|
|
172
|
+
"""
|
|
81
173
|
assert self.id, "'id' was not set."
|
|
82
174
|
|
|
83
175
|
request = apicontractsv1.updateCustomerProfileRequest(
|
|
@@ -90,11 +182,20 @@ class CustomerProfile(AuthorizenetProfileBase):
|
|
|
90
182
|
),
|
|
91
183
|
)
|
|
92
184
|
controller = apicontrollers.updateCustomerProfileController(request)
|
|
93
|
-
|
|
94
|
-
|
|
185
|
+
return self.execute_controller(controller)
|
|
186
|
+
|
|
187
|
+
def _authorizenet_delete_customer_profile(self) -> dict | None:
|
|
188
|
+
"""
|
|
189
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerProfileRequest` using the Authorizenet API.
|
|
95
190
|
|
|
96
|
-
|
|
97
|
-
|
|
191
|
+
`deleteCustomerProfileRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-delete-customer-profile>`_
|
|
192
|
+
|
|
193
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
194
|
+
:raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
|
|
195
|
+
:returns: An Authorizenet API response, if any.
|
|
196
|
+
:rtype: :py:obj:`dict` | :py:obj:`None`
|
|
197
|
+
|
|
198
|
+
"""
|
|
98
199
|
assert self.id, "'id' was not set."
|
|
99
200
|
|
|
100
201
|
request = apicontractsv1.deleteCustomerProfileRequest(
|
|
@@ -102,5 +203,4 @@ class CustomerProfile(AuthorizenetProfileBase):
|
|
|
102
203
|
customerProfileId=self.id,
|
|
103
204
|
)
|
|
104
205
|
controller = apicontrollers.deleteCustomerProfileController(request)
|
|
105
|
-
|
|
106
|
-
return response
|
|
206
|
+
return self.execute_controller(controller)
|
|
@@ -1,63 +1,108 @@
|
|
|
1
1
|
from authorizenet import apicontractsv1, apicontrollers
|
|
2
|
-
from authorizenet.apicontractsv1 import (
|
|
3
|
-
customerAddressType,
|
|
4
|
-
customerPaymentProfileType,
|
|
5
|
-
paymentType,
|
|
6
|
-
)
|
|
7
2
|
|
|
8
3
|
from terminusgps.authorizenet.profiles.base import AuthorizenetSubProfileBase
|
|
9
4
|
|
|
10
5
|
|
|
11
6
|
class PaymentProfile(AuthorizenetSubProfileBase):
|
|
12
|
-
|
|
7
|
+
"""An Authorizenet customer payment profile."""
|
|
8
|
+
|
|
9
|
+
def create(
|
|
10
|
+
self,
|
|
11
|
+
billing_addr: apicontractsv1.customerAddressType,
|
|
12
|
+
payment: apicontractsv1.paymentType,
|
|
13
|
+
) -> int:
|
|
13
14
|
"""
|
|
14
|
-
Creates an
|
|
15
|
+
Creates an Authorizenet payment profile and returns its id.
|
|
15
16
|
|
|
16
|
-
:param billing_addr:
|
|
17
|
+
:param billing_addr: A billing address.
|
|
17
18
|
:type billing_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
|
|
18
|
-
:param payment:
|
|
19
|
+
:param payment: A payment object.
|
|
19
20
|
:type payment: :py:obj:`~authorizenet.apicontractsv1.paymentType`
|
|
20
21
|
:returns: The new payment profile's id.
|
|
21
22
|
:rtype: :py:obj:`int`
|
|
22
23
|
|
|
23
24
|
"""
|
|
24
|
-
billing_addr = kwargs.get("billing_addr")
|
|
25
|
-
payment = kwargs.get("payment")
|
|
26
|
-
if not billing_addr:
|
|
27
|
-
raise ValueError("'billing_addr' is required on creation.")
|
|
28
|
-
if not payment:
|
|
29
|
-
raise ValueError("'payment' is required on creation.")
|
|
30
|
-
|
|
31
|
-
if not isinstance(billing_addr, customerAddressType):
|
|
32
|
-
raise TypeError(
|
|
33
|
-
f"'billing_addr' must be customerAddressType, got '{type(billing_addr)}'"
|
|
34
|
-
)
|
|
35
|
-
if not isinstance(payment, paymentType):
|
|
36
|
-
raise TypeError(
|
|
37
|
-
f"'payment' must be customerAddressType, got '{type(payment)}'"
|
|
38
|
-
)
|
|
39
|
-
|
|
40
25
|
response = self._authorizenet_create_payment_profile(billing_addr, payment)
|
|
41
26
|
return int(response.customerPaymentProfileId)
|
|
42
27
|
|
|
43
|
-
def update(
|
|
44
|
-
|
|
28
|
+
def update(
|
|
29
|
+
self,
|
|
30
|
+
billing_addr: apicontractsv1.customerAddressType,
|
|
31
|
+
payment: apicontractsv1.paymentType,
|
|
32
|
+
) -> dict:
|
|
33
|
+
"""Updates the Authorizenet payment profile."""
|
|
45
34
|
return self._authorizenet_update_payment_profile(billing_addr, payment)
|
|
46
35
|
|
|
47
36
|
def delete(self) -> dict:
|
|
48
|
-
"""Deletes the
|
|
37
|
+
"""Deletes the Authorizenet payment profile."""
|
|
49
38
|
return self._authorizenet_delete_payment_profile()
|
|
50
39
|
|
|
51
40
|
def get_details(self, issuer_info: bool = False) -> dict:
|
|
52
41
|
return self._authorizenet_get_payment_profile(issuer_info)
|
|
53
42
|
|
|
43
|
+
def _authorizenet_get_transaction_list_for_customer(
|
|
44
|
+
self,
|
|
45
|
+
limit: int = 100,
|
|
46
|
+
ordering: str = "submitTimeUTC",
|
|
47
|
+
offset: int = 1,
|
|
48
|
+
descending: bool = False,
|
|
49
|
+
) -> dict:
|
|
50
|
+
"""
|
|
51
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.getTransactionListForCustomerRequest` using the Authorizenet API.
|
|
52
|
+
|
|
53
|
+
:param limit: Total number of transactions to retrieve. Default is ``100``.
|
|
54
|
+
:type limit: :py:obj:`int`
|
|
55
|
+
:param ordering: A field to order the transactions by. Default is ``"submitTimeUTC"``.
|
|
56
|
+
:type ordering: :py:obj:`str`
|
|
57
|
+
:param offset: Page of the response to retrieve.
|
|
58
|
+
:type offset: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
|
|
59
|
+
:param descending: Sort the results in descending order.
|
|
60
|
+
:type descending: :py:obj:`bool`
|
|
61
|
+
:raises ValueError: If ``ordering`` is invalid.
|
|
62
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
63
|
+
:returns: An Authorizenet API response.
|
|
64
|
+
:rtype: :py:obj:`dict`
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
ordering_options: tuple[str, str] = ("id", "submitTimeUTC")
|
|
68
|
+
if ordering not in ordering_options:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"'ordering' must be one of {ordering_options}, got '{ordering}'."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
request = apicontractsv1.getTransactionListForCustomerRequest(
|
|
74
|
+
merchantAuthentication=self.merchantAuthentication,
|
|
75
|
+
customerProfileId=self.customerProfileId,
|
|
76
|
+
customerPaymentProfileId=self.id,
|
|
77
|
+
sorting={"orderBy": ordering, "orderDescending": str(descending).lower()},
|
|
78
|
+
paging={"limit": str(limit), "offset": str(offset)},
|
|
79
|
+
)
|
|
80
|
+
controller = apicontrollers.getTransactionListForCustomerController(request)
|
|
81
|
+
response = self.execute_controller(controller)
|
|
82
|
+
return response
|
|
83
|
+
|
|
54
84
|
def _authorizenet_create_payment_profile(
|
|
55
|
-
self,
|
|
85
|
+
self,
|
|
86
|
+
billing_addr: apicontractsv1.customerAddressType,
|
|
87
|
+
payment: apicontractsv1.paymentType,
|
|
56
88
|
) -> dict:
|
|
89
|
+
"""
|
|
90
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerPaymentProfileRequest` using the Authorizenet API.
|
|
91
|
+
|
|
92
|
+
:param billing_addr: A customer address.
|
|
93
|
+
:type billing_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
|
|
94
|
+
:param payment: A payment method.
|
|
95
|
+
:type payment: :py:obj:`~authorizenet.apicontractsv1.paymentType`
|
|
96
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
97
|
+
:raises : If :py:attr:`id` wasn't set.
|
|
98
|
+
:returns: An Authorizenet API response.
|
|
99
|
+
:rtype: :py:obj:`dict`
|
|
100
|
+
|
|
101
|
+
"""
|
|
57
102
|
request = apicontractsv1.createCustomerPaymentProfileRequest(
|
|
58
103
|
customerProfileId=self.customerProfileId,
|
|
59
104
|
merchantAuthentication=self.merchantAuthentication,
|
|
60
|
-
paymentProfile=customerPaymentProfileType(
|
|
105
|
+
paymentProfile=apicontractsv1.customerPaymentProfileType(
|
|
61
106
|
billTo=billing_addr, payment=payment, defaultPaymentProfile=self.default
|
|
62
107
|
),
|
|
63
108
|
validationMode=self.validationMode,
|
|
@@ -67,6 +112,16 @@ class PaymentProfile(AuthorizenetSubProfileBase):
|
|
|
67
112
|
return response
|
|
68
113
|
|
|
69
114
|
def _authorizenet_get_payment_profile(self, issuer_info: bool = False) -> dict:
|
|
115
|
+
"""
|
|
116
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerPaymentProfileRequest` using the Authorizenet API.
|
|
117
|
+
|
|
118
|
+
:param issuer_info: Whether or not to include issuer information in the response.
|
|
119
|
+
:type issuer_info: :py:obj:`bool`
|
|
120
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
121
|
+
:returns: An Authorizenet API response.
|
|
122
|
+
:rtype: :py:obj:`dict`
|
|
123
|
+
|
|
124
|
+
"""
|
|
70
125
|
assert self.id, "'id' was not set."
|
|
71
126
|
|
|
72
127
|
request = apicontractsv1.getCustomerPaymentProfileRequest(
|
|
@@ -80,14 +135,28 @@ class PaymentProfile(AuthorizenetSubProfileBase):
|
|
|
80
135
|
return response
|
|
81
136
|
|
|
82
137
|
def _authorizenet_update_payment_profile(
|
|
83
|
-
self,
|
|
138
|
+
self,
|
|
139
|
+
billing_addr: apicontractsv1.customerAddressType,
|
|
140
|
+
payment: apicontractsv1.paymentType,
|
|
84
141
|
) -> dict:
|
|
142
|
+
"""
|
|
143
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.validateCustomerPaymentProfileRequest` using the Authorizenet API.
|
|
144
|
+
|
|
145
|
+
:param billing_addr: A customer address.
|
|
146
|
+
:type billing_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
|
|
147
|
+
:param payment: A payment method.
|
|
148
|
+
:type payment: :py:obj:`~authorizenet.apicontractsv1.paymentType`
|
|
149
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
150
|
+
:returns: An Authorizenet API response.
|
|
151
|
+
:rtype: :py:obj:`dict`
|
|
152
|
+
|
|
153
|
+
"""
|
|
85
154
|
assert self.id, "'id' was not set."
|
|
86
155
|
|
|
87
156
|
request = apicontractsv1.updateCustomerPaymentProfileRequest(
|
|
88
157
|
merchantAuthentication=self.merchantAuthentication,
|
|
89
158
|
customerProfileId=self.customerProfileId,
|
|
90
|
-
paymentProfile=customerPaymentProfileType(
|
|
159
|
+
paymentProfile=apicontractsv1.customerPaymentProfileType(
|
|
91
160
|
billTo=billing_addr,
|
|
92
161
|
payment=payment,
|
|
93
162
|
defaultPaymentProfile=self.default,
|
|
@@ -100,6 +169,14 @@ class PaymentProfile(AuthorizenetSubProfileBase):
|
|
|
100
169
|
return response
|
|
101
170
|
|
|
102
171
|
def _authorizenet_validate_payment_profile(self) -> dict:
|
|
172
|
+
"""
|
|
173
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.validateCustomerPaymentProfileRequest` using the Authorizenet API.
|
|
174
|
+
|
|
175
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
176
|
+
:returns: An Authorizenet API response.
|
|
177
|
+
:rtype: :py:obj:`dict`
|
|
178
|
+
|
|
179
|
+
"""
|
|
103
180
|
assert self.id, "'id' was not set."
|
|
104
181
|
|
|
105
182
|
request = apicontractsv1.validateCustomerPaymentProfileRequest(
|
|
@@ -113,6 +190,14 @@ class PaymentProfile(AuthorizenetSubProfileBase):
|
|
|
113
190
|
return response
|
|
114
191
|
|
|
115
192
|
def _authorizenet_delete_payment_profile(self) -> dict:
|
|
193
|
+
"""
|
|
194
|
+
Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerPaymentProfileRequest` using the Authorizenet API.
|
|
195
|
+
|
|
196
|
+
:raises AssertionError: If :py:attr:`id` wasn't set.
|
|
197
|
+
:returns: An Authorizenet API response.
|
|
198
|
+
:rtype: :py:obj:`dict`
|
|
199
|
+
|
|
200
|
+
"""
|
|
116
201
|
assert self.id, "'id' was not set."
|
|
117
202
|
|
|
118
203
|
request = apicontractsv1.deleteCustomerPaymentProfileRequest(
|
|
@@ -1,54 +1,28 @@
|
|
|
1
|
-
from authorizenet import apicontractsv1, apicontrollers
|
|
2
|
-
from terminusgps.authorizenet.auth import get_merchant_auth, get_environment
|
|
1
|
+
from authorizenet import apicontractsv1, apicontrollers
|
|
3
2
|
|
|
3
|
+
from terminusgps.authorizenet.auth import get_merchant_auth
|
|
4
|
+
from terminusgps.authorizenet.utils import ControllerExecutionMixin
|
|
4
5
|
|
|
5
|
-
class Subscription:
|
|
6
|
-
def __init__(self, name: str, id: str | int | None = None, **kwargs) -> None:
|
|
7
|
-
if id is not None and isinstance(id, str) and not id.isdigit():
|
|
8
|
-
raise ValueError(f"'id' must be a digit, got '{id}'.")
|
|
9
|
-
self.name = name
|
|
10
|
-
self.id = str(id if id else self.create(**kwargs))
|
|
11
|
-
|
|
12
|
-
@property
|
|
13
|
-
def merchantAuthentication(self) -> apicontractsv1.merchantAuthenticationType:
|
|
14
|
-
return get_merchant_auth()
|
|
15
|
-
|
|
16
|
-
@property
|
|
17
|
-
def environment(self) -> str:
|
|
18
|
-
return get_environment()
|
|
19
|
-
|
|
20
|
-
def execute_controller(
|
|
21
|
-
self, controller: apicontrollersbase.APIOperationBase
|
|
22
|
-
) -> dict:
|
|
23
|
-
"""
|
|
24
|
-
Executes an Authorize.NET controller and returns its response.
|
|
25
|
-
|
|
26
|
-
:param controller: An Authorize.NET API controller.
|
|
27
|
-
:raises ValueError: If the API call fails.
|
|
28
|
-
:returns: The Authorize.NET API response.
|
|
29
|
-
:rtype: :py:obj:`dict`
|
|
30
6
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if response.messages.resultCode != "Ok":
|
|
37
|
-
raise ValueError(response.messages.message[0]["text"].text)
|
|
38
|
-
return response
|
|
7
|
+
class Subscription(ControllerExecutionMixin):
|
|
8
|
+
def __init__(self, id: str | int | None = None, *args, **kwargs) -> None:
|
|
9
|
+
if id and isinstance(id, str) and not id.isdigit():
|
|
10
|
+
raise ValueError(f"'id' must be a digit, got '{id}'.")
|
|
11
|
+
self.id = int(id) if id else self.create(*args, **kwargs)
|
|
39
12
|
|
|
40
13
|
def create(
|
|
41
14
|
self,
|
|
42
|
-
|
|
15
|
+
name: str,
|
|
43
16
|
amount: str,
|
|
17
|
+
schedule: apicontractsv1.paymentScheduleType,
|
|
44
18
|
payment: apicontractsv1.paymentType,
|
|
45
19
|
address: apicontractsv1.customerAddressType,
|
|
46
20
|
trial_amount: str = "0.00",
|
|
47
21
|
) -> int:
|
|
48
22
|
request = apicontractsv1.ARBCreateSubscriptionRequest(
|
|
49
|
-
merchantAuthentication=
|
|
23
|
+
merchantAuthentication=get_merchant_auth(),
|
|
50
24
|
subscription=apicontractsv1.ARBSubscriptionType(
|
|
51
|
-
name=
|
|
25
|
+
name=name,
|
|
52
26
|
paymentSchedule=schedule,
|
|
53
27
|
amount=amount,
|
|
54
28
|
trialAmount=trial_amount,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from authorizenet import apicontractsv1, apicontrollers
|
|
2
|
+
from authorizenet.apicontrollersbase import APIOperationBase
|
|
3
|
+
|
|
4
|
+
from .auth import get_environment, get_merchant_auth
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ControllerExecutionError(Exception):
|
|
8
|
+
"""Raised when an Authorizenet API controller fails to execute."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, message: str, code: str, *args, **kwargs) -> None:
|
|
11
|
+
self.code = code
|
|
12
|
+
super().__init__(message, *args, **kwargs)
|
|
13
|
+
|
|
14
|
+
def __str__(self) -> str:
|
|
15
|
+
return f"{self.code}: {self.message}"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ControllerExecutionMixin:
|
|
19
|
+
@staticmethod
|
|
20
|
+
def execute_controller(controller: APIOperationBase) -> dict | None:
|
|
21
|
+
"""
|
|
22
|
+
Executes an Authorizenet controller and returns its response.
|
|
23
|
+
|
|
24
|
+
:param controller: An Authorizenet API controller.
|
|
25
|
+
:type controller: :py:obj:`~authorizenet.apicontrollersbase.APIOperationBase`
|
|
26
|
+
:raises ControllerExecutionError: If the API call fails.
|
|
27
|
+
:returns: An Authorizenet API response, if any.
|
|
28
|
+
:rtype: :py:obj:`dict` | :py:obj:`None`
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
controller.setenvironment(get_environment())
|
|
32
|
+
controller.execute()
|
|
33
|
+
response = controller.getresponse()
|
|
34
|
+
|
|
35
|
+
if response is not None and response.messages.resultCode != "Ok":
|
|
36
|
+
raise ControllerExecutionError(
|
|
37
|
+
message=response.messages.message[0]["text"].text,
|
|
38
|
+
code=response.messages.message[0]["code"].text,
|
|
39
|
+
)
|
|
40
|
+
return response
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_customer_profile_ids() -> list[int]:
|
|
44
|
+
"""
|
|
45
|
+
Returns a list of all customer profile ids in Authorizenet.
|
|
46
|
+
|
|
47
|
+
:raises ValueError: If something goes wrong calling the Authorizenet API.
|
|
48
|
+
:returns: A list of all customer profile ids in Authorizenet.
|
|
49
|
+
:rtype: :py:obj:`list`
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
request = apicontractsv1.getCustomerProfileIdsRequest(
|
|
53
|
+
merchantAuthentication=get_merchant_auth()
|
|
54
|
+
)
|
|
55
|
+
controller = apicontrollers.getCustomerProfileIdsController(request)
|
|
56
|
+
controller.execute()
|
|
57
|
+
response = controller.getresponse()
|
|
58
|
+
|
|
59
|
+
if response is not None and response.messages.resultCode != "Ok":
|
|
60
|
+
raise ValueError(response.messages.message["text"].text)
|
|
61
|
+
return [int(id) for id in response.ids.getchildren()]
|
terminusgps/aws/secrets.py
CHANGED
terminusgps/settings.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import pathlib
|
|
2
3
|
|
|
3
4
|
DEBUG = True
|
|
5
|
+
BASE_DIR = pathlib.Path(__file__).resolve().parent
|
|
4
6
|
MERCHANT_AUTH_LOGIN_ID = os.getenv("MERCHANT_AUTH_LOGIN_ID")
|
|
5
7
|
MERCHANT_AUTH_TRANSACTION_KEY = os.getenv("MERCHANT_AUTH_TRANSACTION_KEY")
|
|
6
8
|
SECRET_KEY = "xem*6no%8d9%^qzt2f3x3ar-uq4_+7h9myc$t0!+4%bj5us6f)"
|
terminusgps/wialon/session.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import datetime
|
|
3
3
|
import logging
|
|
4
|
-
import threading
|
|
5
4
|
import typing
|
|
6
5
|
|
|
7
6
|
from django.conf import settings
|
|
@@ -80,7 +79,7 @@ class WialonSession:
|
|
|
80
79
|
:type host: :py:obj:`str`
|
|
81
80
|
:param port: Wialon API host port. Default is ``443``.
|
|
82
81
|
:type port: :py:obj:`int`
|
|
83
|
-
:param log_level:
|
|
82
|
+
:param log_level: Logging severity level. Default is ``20`` (logging.INFO).
|
|
84
83
|
:type log_level: :py:obj:`int`
|
|
85
84
|
:returns: Nothing.
|
|
86
85
|
:rtype: :py:obj:`None`
|
|
@@ -106,7 +105,7 @@ class WialonSession:
|
|
|
106
105
|
self.login(self.token)
|
|
107
106
|
return self
|
|
108
107
|
|
|
109
|
-
def __exit__(self,
|
|
108
|
+
def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
|
|
110
109
|
"""Logs out of the session by calling :py:meth:`logout`."""
|
|
111
110
|
self.logout()
|
|
112
111
|
|
|
@@ -324,23 +323,3 @@ class WialonSession:
|
|
|
324
323
|
self._username = login_response.get("au")
|
|
325
324
|
self._video_service_url = login_response.get("video_service_url")
|
|
326
325
|
self._wsdk_version = login_response.get("wsdk_version")
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
class WialonSessionManager:
|
|
330
|
-
_instance = None
|
|
331
|
-
_lock = threading.Lock()
|
|
332
|
-
_session = None
|
|
333
|
-
|
|
334
|
-
def __new__(cls) -> "WialonSessionManager":
|
|
335
|
-
with cls._lock:
|
|
336
|
-
if not cls._instance:
|
|
337
|
-
cls._instance = super().__new__(cls)
|
|
338
|
-
return cls._instance
|
|
339
|
-
|
|
340
|
-
def get_session(
|
|
341
|
-
self, sid: str | None = None, log_level: int = logging.INFO
|
|
342
|
-
) -> WialonSession:
|
|
343
|
-
with self._lock:
|
|
344
|
-
if not self._session:
|
|
345
|
-
self._session = WialonSession(sid=sid, log_level=log_level)
|
|
346
|
-
return self._session
|
terminusgps/wialon/utils.py
CHANGED
|
@@ -2,7 +2,7 @@ import secrets
|
|
|
2
2
|
import string
|
|
3
3
|
import typing
|
|
4
4
|
|
|
5
|
-
from terminusgps.wialon import
|
|
5
|
+
from terminusgps.wialon import flags, items
|
|
6
6
|
from terminusgps.wialon.items.base import WialonBase
|
|
7
7
|
from terminusgps.wialon.session import WialonSession
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ def get_hw_type_id(name: str, session: WialonSession) -> int | None:
|
|
|
15
15
|
:type name: :py:obj:`str`
|
|
16
16
|
:param session: A valid Wialon API session.
|
|
17
17
|
:type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
|
|
18
|
-
:raises WialonError: If something goes wrong
|
|
18
|
+
:raises WialonError: If something goes wrong during a Wialon API call.
|
|
19
19
|
:returns: A Wialon hardware type id, if it was found.
|
|
20
20
|
:rtype: :py:obj:`int` | :py:obj:`None`
|
|
21
21
|
|
|
@@ -34,18 +34,22 @@ def get_hw_type_id(name: str, session: WialonSession) -> int | None:
|
|
|
34
34
|
|
|
35
35
|
def get_id_from_imei(imei: str, session: WialonSession) -> str | None:
|
|
36
36
|
"""
|
|
37
|
-
Takes a Wialon unit's IMEI # and returns its unit id
|
|
37
|
+
Takes a Wialon unit's IMEI # and returns its unit id.
|
|
38
38
|
|
|
39
|
-
:param
|
|
40
|
-
:type
|
|
39
|
+
:param imei: A Wialon unit's IMEI #.
|
|
40
|
+
:type imei: :py:obj:`str`
|
|
41
41
|
:param session: A valid Wialon API session.
|
|
42
42
|
:type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
|
|
43
|
-
:raises
|
|
43
|
+
:raises ValueError: If ``imei`` contains non-digit characters.
|
|
44
|
+
:raises WialonError: If something goes wrong during a Wialon API call.
|
|
44
45
|
:returns: A Wialon object id, if it was found.
|
|
45
46
|
:rtype: :py:obj:`str` | :py:obj:`None`
|
|
46
47
|
|
|
47
48
|
"""
|
|
48
|
-
|
|
49
|
+
if not imei.isdigit():
|
|
50
|
+
raise ValueError(f"'imei' must be a digit, got '{imei}'.")
|
|
51
|
+
|
|
52
|
+
results = session.wialon_api.core_search_items(
|
|
49
53
|
**{
|
|
50
54
|
"spec": {
|
|
51
55
|
"itemsType": "avl_unit",
|
|
@@ -62,8 +66,8 @@ def get_id_from_imei(imei: str, session: WialonSession) -> str | None:
|
|
|
62
66
|
}
|
|
63
67
|
)
|
|
64
68
|
|
|
65
|
-
if
|
|
66
|
-
return
|
|
69
|
+
if results.get("totalItemsCount", 0) == 1:
|
|
70
|
+
return results["items"][0].get("id")
|
|
67
71
|
|
|
68
72
|
|
|
69
73
|
def get_id_from_iccid(iccid: str, session: WialonSession) -> str | None:
|
|
@@ -76,7 +80,7 @@ def get_id_from_iccid(iccid: str, session: WialonSession) -> str | None:
|
|
|
76
80
|
:type iccid: :py:obj:`str`
|
|
77
81
|
:param session: A valid Wialon API session.
|
|
78
82
|
:type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
|
|
79
|
-
:raises WialonError: If something goes wrong
|
|
83
|
+
:raises WialonError: If something goes wrong during a Wialon API call.
|
|
80
84
|
:returns: A Wialon object id, if it was found.
|
|
81
85
|
:rtype: :py:obj:`str` | :py:obj:`None`
|
|
82
86
|
|
|
@@ -96,16 +100,15 @@ def get_wialon_cls(items_type: str) -> typing.Type[WialonBase]:
|
|
|
96
100
|
"""
|
|
97
101
|
match items_type:
|
|
98
102
|
case "user":
|
|
99
|
-
|
|
103
|
+
return items.WialonUser
|
|
100
104
|
case "avl_unit":
|
|
101
|
-
|
|
105
|
+
return items.WialonUnit
|
|
102
106
|
case "avl_unit_group":
|
|
103
|
-
|
|
107
|
+
return items.WialonUnitGroup
|
|
104
108
|
case "avl_resource":
|
|
105
|
-
|
|
109
|
+
return items.WialonResource
|
|
106
110
|
case _:
|
|
107
|
-
raise ValueError(f"Invalid items_type '{items_type}'")
|
|
108
|
-
return wialon_cls
|
|
111
|
+
raise ValueError(f"Invalid items_type, got '{items_type}'.")
|
|
109
112
|
|
|
110
113
|
|
|
111
114
|
def get_vin_info(vin_number: str, session: WialonSession) -> dict:
|
|
@@ -123,7 +126,6 @@ def get_vin_info(vin_number: str, session: WialonSession) -> dict:
|
|
|
123
126
|
response = session.wialon_api.unit_get_vin_info(**{"vin": vin_number}).get(
|
|
124
127
|
"vin_lookup_result"
|
|
125
128
|
)
|
|
126
|
-
|
|
127
129
|
return (
|
|
128
130
|
{field.get("n"): field.get("v") for field in response.get("pflds")}
|
|
129
131
|
if "error" not in response.keys()
|
|
@@ -135,31 +137,38 @@ def is_unique(value: str, session: WialonSession, items_type: str = "avl_unit")
|
|
|
135
137
|
"""
|
|
136
138
|
Determines if the value is unique among Wialon objects of type 'items_type'.
|
|
137
139
|
|
|
138
|
-
:param value: A
|
|
140
|
+
:param value: A Wialon object name.
|
|
139
141
|
:type value: :py:obj:`str`
|
|
140
142
|
:param session: A valid Wialon API session.
|
|
141
143
|
:type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
|
|
142
|
-
:param items_type:
|
|
144
|
+
:param items_type: Type of Wialon objects to validate the value against. Default is ``"avl_unit"``.
|
|
143
145
|
:type items_type: :py:obj:`str`
|
|
146
|
+
:raises WialonError: If something goes wrong during a Wialon API call.
|
|
144
147
|
:returns: Whether or not the value is unique among 'items_type'.
|
|
145
148
|
:rtype: :py:obj:`bool`
|
|
146
149
|
|
|
147
150
|
"""
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
return not bool(
|
|
152
|
+
session.wialon_api.core_check_unique(
|
|
153
|
+
**{"type": items_type, "value": value.strip()}
|
|
154
|
+
).get("result")
|
|
155
|
+
)
|
|
152
156
|
|
|
153
157
|
|
|
154
158
|
def generate_wialon_password(length: int = 32) -> str:
|
|
155
159
|
"""
|
|
156
|
-
Generates a Wialon compliant password
|
|
160
|
+
Generates a Wialon compliant password between ``8`` and ``64`` characters.
|
|
161
|
+
|
|
162
|
+
The generated password will contain:
|
|
157
163
|
|
|
158
|
-
|
|
164
|
+
- At least one uppercase letter.
|
|
165
|
+
- At least one lowercase letter.
|
|
166
|
+
- At least one special symbol.
|
|
167
|
+
- At least three digits.
|
|
159
168
|
|
|
160
169
|
:param length: Length of the generated password. Default is ``32``.
|
|
161
170
|
:type length: :py:obj:`int`
|
|
162
|
-
:raises ValueError: If
|
|
171
|
+
:raises ValueError: If :py:length is less than ``8`` or greater than ``64``.
|
|
163
172
|
:returns: A Wialon compliant password.
|
|
164
173
|
:rtype: :py:obj:`str`
|
|
165
174
|
|
terminusgps/opencv/__init__.py
DELETED
|
File without changes
|
terminusgps/opencv/barcodes.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import cv2 as cv
|
|
2
|
-
from pyzbar.pyzbar import decode
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def read_barcode(filepath: str) -> None:
|
|
6
|
-
img = cv.imread(filepath)
|
|
7
|
-
barcodes = decode(img)
|
|
8
|
-
|
|
9
|
-
if not barcodes:
|
|
10
|
-
raise ValueError("No barcodes detected.")
|
|
11
|
-
|
|
12
|
-
for barcode in barcodes:
|
|
13
|
-
(x, y, w, h) = barcode.rect
|
|
14
|
-
cv.rectangle(img, (x - 10, y - 10), (x + w + 10, y + h + 10), (255, 0, 0), 2)
|
|
15
|
-
|
|
16
|
-
print(f"{barcode.data = }")
|
|
17
|
-
print(f"{barcode.type = }")
|
|
18
|
-
|
|
19
|
-
cv.imshow("Image", img)
|
|
20
|
-
cv.waitKey(0)
|
|
21
|
-
cv.destroyAllWindows()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def main() -> None:
|
|
25
|
-
filepath = "/home/blake/sift_keypoints.jpg"
|
|
26
|
-
read_barcode(filepath)
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if __name__ == "__main__":
|
|
31
|
-
main()
|
|
File without changes
|
{python_terminusgps-22.0.2.dist-info → python_terminusgps-23.0.0.dist-info}/licenses/COPYING
RENAMED
|
File without changes
|