python-payway 0.0.4__tar.gz → 0.0.5__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.
- {python_payway-0.0.4 → python_payway-0.0.5}/PKG-INFO +1 -1
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/client.py +30 -21
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/customers.py +3 -1
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/exceptions.py +6 -6
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/model.py +78 -78
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/test_utils.py +2 -1
- {python_payway-0.0.4 → python_payway-0.0.5}/pyproject.toml +2 -2
- {python_payway-0.0.4 → python_payway-0.0.5}/python_payway.egg-info/PKG-INFO +1 -1
- {python_payway-0.0.4 → python_payway-0.0.5}/tests/test_client.py +15 -16
- {python_payway-0.0.4 → python_payway-0.0.5}/tests/test_customers.py +8 -9
- {python_payway-0.0.4 → python_payway-0.0.5}/tests/test_transactions.py +2 -3
- {python_payway-0.0.4 → python_payway-0.0.5}/LICENSE +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/README.md +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/__init__.py +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/conf.py +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/constants.py +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/transactions.py +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/payway/utils.py +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/python_payway.egg-info/SOURCES.txt +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/python_payway.egg-info/dependency_links.txt +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/python_payway.egg-info/requires.txt +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/python_payway.egg-info/top_level.txt +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/setup.cfg +0 -0
- {python_payway-0.0.4 → python_payway-0.0.5}/tests/__init__.py +0 -0
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from logging import getLogger
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
7
|
import requests
|
|
8
8
|
|
|
@@ -46,7 +46,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
46
46
|
bank_account_id: str,
|
|
47
47
|
secret_api_key: str,
|
|
48
48
|
publishable_api_key: str,
|
|
49
|
-
) ->
|
|
49
|
+
) -> None:
|
|
50
50
|
"""
|
|
51
51
|
:param merchant_id : str = PayWay Merchant ID
|
|
52
52
|
:param bank_account_id : str = PayWay Bank Account ID
|
|
@@ -66,8 +66,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
66
66
|
|
|
67
67
|
session = requests.Session()
|
|
68
68
|
session.auth = (self.secret_api_key, "")
|
|
69
|
-
headers = {"content-type": "application/x-www-form-urlencoded"}
|
|
70
|
-
session.headers = headers
|
|
69
|
+
session.headers = {"content-type": "application/x-www-form-urlencoded"}
|
|
71
70
|
self.session = session
|
|
72
71
|
session_no_headers = requests.Session()
|
|
73
72
|
session_no_headers.auth = session.auth
|
|
@@ -79,7 +78,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
79
78
|
bank_account_id: str,
|
|
80
79
|
secret_api_key: str,
|
|
81
80
|
publishable_api_key: str,
|
|
82
|
-
) ->
|
|
81
|
+
) -> None:
|
|
83
82
|
if not merchant_id or not bank_account_id or not secret_api_key or not publishable_api_key:
|
|
84
83
|
if not secret_api_key or not publishable_api_key:
|
|
85
84
|
logger.error("PayWay API keys not found")
|
|
@@ -99,7 +98,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
99
98
|
return requests.get(url=endpoint, auth=(self.secret_api_key, ""), timeout=30)
|
|
100
99
|
|
|
101
100
|
def post_request(
|
|
102
|
-
self, endpoint: str, data: dict, auth: tuple | None = None, idempotency_key: str | None = None
|
|
101
|
+
self, endpoint: str, data: dict[str, Any], auth: tuple[str, str] | None = None, idempotency_key: str | None = None
|
|
103
102
|
) -> requests.Response:
|
|
104
103
|
"""
|
|
105
104
|
Supply an idempotency_key to avoid duplicate POSTs
|
|
@@ -112,7 +111,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
112
111
|
headers["Idempotency-Key"] = idempotency_key
|
|
113
112
|
return requests.post(url=endpoint, auth=auth, data=data, headers=headers, timeout=30)
|
|
114
113
|
|
|
115
|
-
def put_request(self, endpoint: str, data: dict) -> requests.Response:
|
|
114
|
+
def put_request(self, endpoint: str, data: dict[str, Any]) -> requests.Response:
|
|
116
115
|
return requests.put(
|
|
117
116
|
url=endpoint,
|
|
118
117
|
auth=(self.secret_api_key, ""),
|
|
@@ -123,7 +122,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
123
122
|
|
|
124
123
|
def create_token(
|
|
125
124
|
self, payway_obj: BankAccount | PayWayCard, payment_method: str, idempotency_key: str | None = None
|
|
126
|
-
) -> tuple[TokenResponse, list]:
|
|
125
|
+
) -> tuple[TokenResponse | None, list[PaymentError] | None]:
|
|
127
126
|
"""
|
|
128
127
|
Creates a single use token for a Customer's payment setup (credit card or bank account)
|
|
129
128
|
:param payway_obj: object: one of model.PayWayCard or model.BankAccount object
|
|
@@ -160,14 +159,18 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
160
159
|
token_response = TokenResponse().from_dict(response.json())
|
|
161
160
|
return token_response, errors
|
|
162
161
|
|
|
163
|
-
def create_card_token(
|
|
162
|
+
def create_card_token(
|
|
163
|
+
self, card: PayWayCard, idempotency_key: str | None = None
|
|
164
|
+
) -> tuple[TokenResponse | None, list[PaymentError] | None]:
|
|
164
165
|
"""
|
|
165
166
|
:param card: PayWayCard object represents a customer's credit card details
|
|
166
167
|
:param idempotency_key: str: unique value to avoid duplicate POSTs
|
|
167
168
|
"""
|
|
168
169
|
return self.create_token(card, "card", idempotency_key=idempotency_key)
|
|
169
170
|
|
|
170
|
-
def create_bank_account_token(
|
|
171
|
+
def create_bank_account_token(
|
|
172
|
+
self, bank_account: BankAccount, idempotency_key: str | None = None
|
|
173
|
+
) -> tuple[TokenResponse | None, list[PaymentError] | None]:
|
|
171
174
|
"""
|
|
172
175
|
:param bank_account: BankAccount object represents a customer's bank account
|
|
173
176
|
:param idempotency_key: str: unique value to avoid duplicate POSTs
|
|
@@ -179,7 +182,9 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
179
182
|
idempotency_key=idempotency_key,
|
|
180
183
|
)
|
|
181
184
|
|
|
182
|
-
def create_customer(
|
|
185
|
+
def create_customer(
|
|
186
|
+
self, customer: PayWayCustomer, idempotency_key: str | None = None
|
|
187
|
+
) -> tuple[PayWayCustomer | None, list[PaymentError] | None]:
|
|
183
188
|
"""
|
|
184
189
|
Create a customer in PayWay system
|
|
185
190
|
|
|
@@ -217,7 +222,9 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
217
222
|
customer = PayWayCustomer().from_dict(response.json())
|
|
218
223
|
return customer, errors
|
|
219
224
|
|
|
220
|
-
def process_payment(
|
|
225
|
+
def process_payment(
|
|
226
|
+
self, payment: PayWayPayment, idempotency_key: str | None = None
|
|
227
|
+
) -> tuple[PayWayTransaction | None, list[PaymentError] | None]:
|
|
221
228
|
"""
|
|
222
229
|
Process an individual payment against a Customer with active Recurring Billing setup.
|
|
223
230
|
:param payment: PayWayPayment object (see model.PayWayPayment)
|
|
@@ -234,7 +241,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
234
241
|
transaction = PayWayTransaction.from_dict(response.json())
|
|
235
242
|
return transaction, errors
|
|
236
243
|
|
|
237
|
-
def _validate_response(self, response: requests.Response) -> list | None:
|
|
244
|
+
def _validate_response(self, response: requests.Response) -> list[PaymentError] | None:
|
|
238
245
|
"""
|
|
239
246
|
Validates all responses from PayWay to catch documented PayWay errors.
|
|
240
247
|
:param response: requests response object
|
|
@@ -254,7 +261,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
254
261
|
503,
|
|
255
262
|
]:
|
|
256
263
|
http_error_msg = f"{response.status_code} Client Error: {response.reason} for url: {response.url}"
|
|
257
|
-
raise PaywayError(code=response.status_code, message=http_error_msg)
|
|
264
|
+
raise PaywayError(code=str(response.status_code), message=http_error_msg)
|
|
258
265
|
|
|
259
266
|
if response.status_code in [404, 422]: # Documented PayWay errors in JSON
|
|
260
267
|
# parse error message
|
|
@@ -265,17 +272,17 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
265
272
|
errors = response.json()
|
|
266
273
|
except json.JSONDecodeError:
|
|
267
274
|
raise PaywayError(
|
|
268
|
-
code=response.status_code,
|
|
275
|
+
code=str(response.status_code),
|
|
269
276
|
message="Internal server error",
|
|
270
277
|
)
|
|
271
278
|
# Documented PayWay server errors in JSON
|
|
272
279
|
payway_error = ServerError().from_dict(errors)
|
|
273
280
|
message = payway_error.to_message()
|
|
274
|
-
raise PaywayError(code=response.status_code, message=message)
|
|
281
|
+
raise PaywayError(code=str(response.status_code), message=message)
|
|
275
282
|
|
|
276
283
|
return None
|
|
277
284
|
|
|
278
|
-
def get_transaction(self, transaction_id: int) -> tuple[PayWayTransaction, list]:
|
|
285
|
+
def get_transaction(self, transaction_id: int) -> tuple[PayWayTransaction | None, list[PaymentError] | None]:
|
|
279
286
|
"""
|
|
280
287
|
Lookup and return a transaction if found in PayWay
|
|
281
288
|
:param transaction_id: str A PayWay transaction ID
|
|
@@ -288,7 +295,9 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
288
295
|
transaction = PayWayTransaction.from_dict(response.json())
|
|
289
296
|
return transaction, errors
|
|
290
297
|
|
|
291
|
-
def void_transaction(
|
|
298
|
+
def void_transaction(
|
|
299
|
+
self, transaction_id: int, idempotency_key: str | None = None
|
|
300
|
+
) -> tuple[PayWayTransaction | None, list[PaymentError] | None]:
|
|
292
301
|
"""
|
|
293
302
|
Void a transaction in PayWay
|
|
294
303
|
:param transaction_id: str A PayWay transaction ID
|
|
@@ -309,7 +318,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
309
318
|
order_id: str | None = None,
|
|
310
319
|
ip_address: str | None = None,
|
|
311
320
|
idempotency_key: str | None = None,
|
|
312
|
-
) -> tuple[PayWayTransaction, list]:
|
|
321
|
+
) -> tuple[PayWayTransaction | None, list[PaymentError] | None]:
|
|
313
322
|
"""
|
|
314
323
|
Refund a transaction in PayWay
|
|
315
324
|
:param transaction_id: str A PayWay transaction ID
|
|
@@ -334,7 +343,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
334
343
|
transaction = PayWayTransaction.from_dict(response.json())
|
|
335
344
|
return transaction, errors
|
|
336
345
|
|
|
337
|
-
def get_customer(self, customer_id: str) -> tuple[PayWayCustomer, list]:
|
|
346
|
+
def get_customer(self, customer_id: str) -> tuple[PayWayCustomer | None, list[PaymentError] | None]:
|
|
338
347
|
"""
|
|
339
348
|
Returns a PayWay Customer's Payment Setup, [Payment] Schedule, Contact Details, Custom Fields and Notes
|
|
340
349
|
:param customer_id str PayWay customer ID in PayWay system
|
|
@@ -347,7 +356,7 @@ class Client(CustomerRequest, TransactionRequest):
|
|
|
347
356
|
customer = PayWayCustomer.from_dict(response.json())
|
|
348
357
|
return customer, errors
|
|
349
358
|
|
|
350
|
-
def update_payment_setup(self, token: str, customer_id: str) -> tuple[PaymentSetup,
|
|
359
|
+
def update_payment_setup(self, token: str, customer_id: str) -> tuple[PaymentSetup | None, list[PaymentError] | None]:
|
|
351
360
|
"""
|
|
352
361
|
Updates the Customer's Payment Setup with a new Credit Card or Bank Account.
|
|
353
362
|
:param token: PayWay credit card or bank account token
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
3
5
|
import requests
|
|
4
6
|
|
|
5
7
|
from payway.conf import CUSTOMER_URL
|
|
@@ -79,7 +81,7 @@ class CustomerRequest:
|
|
|
79
81
|
|
|
80
82
|
@json_list("update_contact_details")
|
|
81
83
|
def update_contact_details(
|
|
82
|
-
self, customer_number: int, customer: PayWayCustomer | None = None, **options: dict
|
|
84
|
+
self, customer_number: int, customer: PayWayCustomer | None = None, **options: dict[str, Any]
|
|
83
85
|
) -> requests.Response:
|
|
84
86
|
"""
|
|
85
87
|
param: customer_number: PayWay customer number
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class PaywayError(Exception):
|
|
7
|
-
_code: str =
|
|
8
|
-
_message: str =
|
|
7
|
+
_code: str = ""
|
|
8
|
+
_message: str = ""
|
|
9
9
|
|
|
10
|
-
def __init__(self, code: str, message: str, *args: dict, **kwargs: dict) ->
|
|
10
|
+
def __init__(self, code: str, message: str, *args: dict[str, Any], **kwargs: dict[str, Any]) -> None:
|
|
11
11
|
"""
|
|
12
12
|
code : str = PayWay API response/error code
|
|
13
13
|
message : str = appropriate message
|
|
@@ -16,7 +16,7 @@ class PaywayError(Exception):
|
|
|
16
16
|
super().__init__(*args, **kwargs)
|
|
17
17
|
|
|
18
18
|
self._code = code
|
|
19
|
-
self._message = f"{code}: {message}"
|
|
19
|
+
self._message = f"{code}: {message}"
|
|
20
20
|
|
|
21
21
|
def __str__(self) -> str:
|
|
22
|
-
return self.
|
|
22
|
+
return self._message
|
|
@@ -48,7 +48,7 @@ class PayWayCard:
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
@staticmethod
|
|
51
|
-
def from_dict(payway_card: dict) -> PayWayCard:
|
|
51
|
+
def from_dict(payway_card: dict[str, Any]) -> PayWayCard:
|
|
52
52
|
card = PayWayCard()
|
|
53
53
|
if payway_card.get("maskedCardNumber"):
|
|
54
54
|
card.card_number = payway_card.get("maskedCardNumber")
|
|
@@ -124,14 +124,14 @@ class PayWayCustomer:
|
|
|
124
124
|
return customer
|
|
125
125
|
|
|
126
126
|
@staticmethod
|
|
127
|
-
def from_dict(response: dict) -> PayWayCustomer:
|
|
127
|
+
def from_dict(response: dict[str, Any]) -> PayWayCustomer:
|
|
128
128
|
"""
|
|
129
129
|
Parse PayWay Customer response data
|
|
130
130
|
:param response: dict PayWay response dictionary
|
|
131
131
|
:return:
|
|
132
132
|
"""
|
|
133
133
|
customer = PayWayCustomer()
|
|
134
|
-
contact = response.get("contact")
|
|
134
|
+
contact = response.get("contact", {})
|
|
135
135
|
customer.customer_name = contact.get("customerName")
|
|
136
136
|
customer.email_address = contact.get("emailAddress")
|
|
137
137
|
customer.send_email_receipts = contact.get("sendEmailReceipts")
|
|
@@ -146,11 +146,11 @@ class PayWayCustomer:
|
|
|
146
146
|
|
|
147
147
|
if response.get("paymentSetup") is not None:
|
|
148
148
|
customer.payment_setup = PaymentSetup().from_dict(
|
|
149
|
-
response.get("paymentSetup"),
|
|
149
|
+
response.get("paymentSetup", {}),
|
|
150
150
|
)
|
|
151
151
|
|
|
152
152
|
if response.get("customFields") is not None:
|
|
153
|
-
custom_fields = response.get("customFields")
|
|
153
|
+
custom_fields = response.get("customFields", {})
|
|
154
154
|
for k, v in custom_fields.items():
|
|
155
155
|
setattr(customer, k, v)
|
|
156
156
|
|
|
@@ -161,17 +161,17 @@ class PayWayCustomer:
|
|
|
161
161
|
|
|
162
162
|
|
|
163
163
|
class PaymentError:
|
|
164
|
-
field_name: str = None
|
|
165
|
-
message: str = None
|
|
166
|
-
field_value: str = None
|
|
164
|
+
field_name: str | None = None
|
|
165
|
+
message: str | None = None
|
|
166
|
+
field_value: str | None = None
|
|
167
167
|
|
|
168
168
|
@staticmethod
|
|
169
|
-
def from_dict(payway_response: dict) -> list:
|
|
169
|
+
def from_dict(payway_response: dict[str, Any]) -> list[PaymentError]:
|
|
170
170
|
"""
|
|
171
171
|
Returns a list of errors from PayWay
|
|
172
172
|
:param: payway_response: dict PayWay response dictionary
|
|
173
173
|
"""
|
|
174
|
-
errors = payway_response.get("data")
|
|
174
|
+
errors = payway_response.get("data", [])
|
|
175
175
|
payment_errors = []
|
|
176
176
|
for error in errors:
|
|
177
177
|
payway_error = PaymentError()
|
|
@@ -200,17 +200,17 @@ class PaymentError:
|
|
|
200
200
|
|
|
201
201
|
|
|
202
202
|
class ServerError:
|
|
203
|
-
error_number: int = None
|
|
204
|
-
trace_code: str = None
|
|
203
|
+
error_number: int | None = None
|
|
204
|
+
trace_code: str | None = None
|
|
205
205
|
|
|
206
206
|
@staticmethod
|
|
207
|
-
def from_dict(response: dict) -> ServerError:
|
|
207
|
+
def from_dict(response: dict[str, Any]) -> ServerError:
|
|
208
208
|
"""
|
|
209
209
|
:param: response: dict PayWay response dictionary
|
|
210
210
|
"""
|
|
211
211
|
payway_error = ServerError()
|
|
212
|
-
payway_error.error_number = response.get("errorNumber")
|
|
213
|
-
payway_error.trace_code = response.get("traceCode")
|
|
212
|
+
payway_error.error_number = response.get("errorNumber", {})
|
|
213
|
+
payway_error.trace_code = response.get("traceCode", {})
|
|
214
214
|
return payway_error
|
|
215
215
|
|
|
216
216
|
def to_message(self) -> str:
|
|
@@ -233,8 +233,8 @@ class PayWayPayment:
|
|
|
233
233
|
def __init__(
|
|
234
234
|
self,
|
|
235
235
|
transaction_type: str,
|
|
236
|
-
customer_number:
|
|
237
|
-
amount:
|
|
236
|
+
customer_number: str | None = None,
|
|
237
|
+
amount: float | None = None,
|
|
238
238
|
currency: str | None = None,
|
|
239
239
|
order_number: str | None = None,
|
|
240
240
|
ip_address: str | None = None,
|
|
@@ -272,42 +272,42 @@ class PayWayPayment:
|
|
|
272
272
|
|
|
273
273
|
|
|
274
274
|
class PayWayTransaction:
|
|
275
|
-
transaction_id: int = None
|
|
276
|
-
receipt_number: str = None
|
|
277
|
-
status: str = None
|
|
278
|
-
response_code: str = None
|
|
279
|
-
response_text: str = None
|
|
280
|
-
transaction_type: str = None
|
|
281
|
-
customer_number: str = None
|
|
282
|
-
customer_name: str = None
|
|
283
|
-
customer_email: str = None
|
|
284
|
-
bpay_ref: str = None
|
|
285
|
-
order_number: str = None
|
|
286
|
-
currency: str = None
|
|
287
|
-
principal_amount: float = None
|
|
288
|
-
surcharge_amount: float = None
|
|
289
|
-
payment_amount: float = None
|
|
290
|
-
payment_method: str = None
|
|
291
|
-
declined_date: str = None
|
|
292
|
-
card: PayWayCard = None
|
|
293
|
-
merchant: Merchant = None
|
|
294
|
-
virtual_account: dict = None
|
|
295
|
-
australia_post: dict = None
|
|
296
|
-
bpay: dict = None
|
|
297
|
-
your_bank_account: dict = None
|
|
298
|
-
customer_paypal_account: dict = None
|
|
299
|
-
your_paypal_account: dict = None
|
|
300
|
-
transaction_date_time: str = None
|
|
301
|
-
user: dict = None
|
|
302
|
-
settlement_date: str = None
|
|
303
|
-
parent_transaction: dict = None
|
|
304
|
-
ip_address: str = None
|
|
305
|
-
fraud_result: str = None
|
|
306
|
-
ip_country: str = None
|
|
307
|
-
card_country: str = None
|
|
308
|
-
custom_fields: dict = None
|
|
309
|
-
is_voidable: bool = None
|
|
310
|
-
is_refundable: bool = None
|
|
275
|
+
transaction_id: int | None = None
|
|
276
|
+
receipt_number: str | None = None
|
|
277
|
+
status: str | None = None
|
|
278
|
+
response_code: str | None = None
|
|
279
|
+
response_text: str | None = None
|
|
280
|
+
transaction_type: str | None = None
|
|
281
|
+
customer_number: str | None = None
|
|
282
|
+
customer_name: str | None = None
|
|
283
|
+
customer_email: str | None = None
|
|
284
|
+
bpay_ref: str | None = None
|
|
285
|
+
order_number: str | None = None
|
|
286
|
+
currency: str | None = None
|
|
287
|
+
principal_amount: float | None = None
|
|
288
|
+
surcharge_amount: float | None = None
|
|
289
|
+
payment_amount: float | None = None
|
|
290
|
+
payment_method: str | None = None
|
|
291
|
+
declined_date: str | None = None
|
|
292
|
+
card: PayWayCard | None = None
|
|
293
|
+
merchant: Merchant | None = None
|
|
294
|
+
virtual_account: dict[str, Any] | None = None
|
|
295
|
+
australia_post: dict[str, Any] | None = None
|
|
296
|
+
bpay: dict[str, Any] | None = None
|
|
297
|
+
your_bank_account: dict[str, Any] | None = None
|
|
298
|
+
customer_paypal_account: dict[str, Any] | None = None
|
|
299
|
+
your_paypal_account: dict[str, Any] | None = None
|
|
300
|
+
transaction_date_time: str | None = None
|
|
301
|
+
user: dict[str, Any] | None = None
|
|
302
|
+
settlement_date: str | None = None
|
|
303
|
+
parent_transaction: dict[str, Any] | None = None
|
|
304
|
+
ip_address: str | None = None
|
|
305
|
+
fraud_result: str | None = None
|
|
306
|
+
ip_country: str | None = None
|
|
307
|
+
card_country: str | None = None
|
|
308
|
+
custom_fields: dict[str, Any] | None = None
|
|
309
|
+
is_voidable: bool | None = None
|
|
310
|
+
is_refundable: bool | None = None
|
|
311
311
|
|
|
312
312
|
def to_dict(self) -> dict[str, Any]:
|
|
313
313
|
return {
|
|
@@ -325,8 +325,8 @@ class PayWayTransaction:
|
|
|
325
325
|
"surchargeAmount": self.surcharge_amount,
|
|
326
326
|
"paymentAmount": self.payment_amount,
|
|
327
327
|
"paymentMethod": self.payment_method,
|
|
328
|
-
"creditCard": self.card.to_dict(),
|
|
329
|
-
"merchant": self.merchant.to_dict(),
|
|
328
|
+
"creditCard": self.card.to_dict() if self.card is not None else {},
|
|
329
|
+
"merchant": self.merchant.to_dict() if self.merchant is not None else {},
|
|
330
330
|
"virtualAccount": self.virtual_account,
|
|
331
331
|
"bpaustraliaPostay": self.australia_post,
|
|
332
332
|
"bpay": self.bpay,
|
|
@@ -348,7 +348,7 @@ class PayWayTransaction:
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
@staticmethod
|
|
351
|
-
def from_dict(response: dict) -> PayWayTransaction:
|
|
351
|
+
def from_dict(response: dict[str, Any]) -> PayWayTransaction:
|
|
352
352
|
"""
|
|
353
353
|
:param: response: dict PayWay response dictionary
|
|
354
354
|
"""
|
|
@@ -369,10 +369,10 @@ class PayWayTransaction:
|
|
|
369
369
|
transaction.payment_method = response.get("paymentMethod")
|
|
370
370
|
|
|
371
371
|
if response.get("creditCard") is not None:
|
|
372
|
-
transaction.card = PayWayCard.from_dict(response.get("creditCard"))
|
|
372
|
+
transaction.card = PayWayCard.from_dict(response.get("creditCard", {}))
|
|
373
373
|
|
|
374
374
|
if response.get("merchant") is not None:
|
|
375
|
-
transaction.merchant = Merchant.from_dict(response.get("merchant"))
|
|
375
|
+
transaction.merchant = Merchant.from_dict(response.get("merchant", {}))
|
|
376
376
|
|
|
377
377
|
transaction.virtual_account = response.get("virtualAccount")
|
|
378
378
|
transaction.australia_post = response.get("bpaustraliaPostay")
|
|
@@ -406,14 +406,14 @@ class Merchant:
|
|
|
406
406
|
account
|
|
407
407
|
"""
|
|
408
408
|
|
|
409
|
-
merchant_id: str = None
|
|
410
|
-
merchant_name: str = None
|
|
411
|
-
settlement_bsb: str = None
|
|
412
|
-
settlement_account_number: str = None
|
|
413
|
-
surcharge_bsb: str = None
|
|
414
|
-
surcharge_account_number: str = None
|
|
409
|
+
merchant_id: str | None = None
|
|
410
|
+
merchant_name: str | None = None
|
|
411
|
+
settlement_bsb: str | None = None
|
|
412
|
+
settlement_account_number: str | None = None
|
|
413
|
+
surcharge_bsb: str | None = None
|
|
414
|
+
surcharge_account_number: str | None = None
|
|
415
415
|
|
|
416
|
-
def to_dict(self) -> dict[str,
|
|
416
|
+
def to_dict(self) -> dict[str, Any]:
|
|
417
417
|
return {
|
|
418
418
|
"merchantId": self.merchant_id,
|
|
419
419
|
"merchantName": self.merchant_name,
|
|
@@ -424,7 +424,7 @@ class Merchant:
|
|
|
424
424
|
}
|
|
425
425
|
|
|
426
426
|
@staticmethod
|
|
427
|
-
def from_dict(payway_obj: dict) -> Merchant:
|
|
427
|
+
def from_dict(payway_obj: dict[str, Any]) -> Merchant:
|
|
428
428
|
"""
|
|
429
429
|
:param: payway_obj: dict PayWay response dictionary
|
|
430
430
|
"""
|
|
@@ -439,13 +439,13 @@ class Merchant:
|
|
|
439
439
|
|
|
440
440
|
|
|
441
441
|
class PaymentSetup:
|
|
442
|
-
payment_method: str = None
|
|
443
|
-
stopped: bool = None
|
|
444
|
-
credit_card: PayWayCard = None
|
|
445
|
-
merchant: Merchant = None
|
|
442
|
+
payment_method: str | None = None
|
|
443
|
+
stopped: bool | None = None
|
|
444
|
+
credit_card: PayWayCard | None = None
|
|
445
|
+
merchant: Merchant | None = None
|
|
446
446
|
|
|
447
447
|
@staticmethod
|
|
448
|
-
def from_dict(response: dict) -> PaymentSetup:
|
|
448
|
+
def from_dict(response: dict[str, Any]) -> PaymentSetup:
|
|
449
449
|
"""
|
|
450
450
|
:param: response: dict PayWay response dictionary
|
|
451
451
|
"""
|
|
@@ -453,20 +453,20 @@ class PaymentSetup:
|
|
|
453
453
|
ps.payment_method = response.get("paymentMethod")
|
|
454
454
|
ps.stopped = response.get("stopped")
|
|
455
455
|
if response.get("creditCard") is not None:
|
|
456
|
-
ps.credit_card = PayWayCard().from_dict(response.get("creditCard"))
|
|
456
|
+
ps.credit_card = PayWayCard().from_dict(response.get("creditCard", {}))
|
|
457
457
|
if response.get("merchant") is not None:
|
|
458
|
-
ps.merchant = Merchant().from_dict(response.get("merchant"))
|
|
458
|
+
ps.merchant = Merchant().from_dict(response.get("merchant", {}))
|
|
459
459
|
return ps
|
|
460
460
|
|
|
461
461
|
|
|
462
462
|
class TokenResponse:
|
|
463
|
-
token: str = None
|
|
464
|
-
payment_method: str = None
|
|
465
|
-
card: PayWayCard = None
|
|
466
|
-
bank_account: dict = None
|
|
463
|
+
token: str | None = None
|
|
464
|
+
payment_method: str | None = None
|
|
465
|
+
card: PayWayCard | None = None
|
|
466
|
+
bank_account: dict[str, Any] | None = None
|
|
467
467
|
|
|
468
468
|
@staticmethod
|
|
469
|
-
def from_dict(response: dict) -> TokenResponse:
|
|
469
|
+
def from_dict(response: dict[str, Any]) -> TokenResponse:
|
|
470
470
|
"""
|
|
471
471
|
:param: response: dict PayWay response dictionary
|
|
472
472
|
"""
|
|
@@ -2,9 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import pathlib
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
def load_json_file(file_path: str) -> dict:
|
|
8
|
+
def load_json_file(file_path: str) -> dict[str, Any]:
|
|
8
9
|
file = pathlib.Path(file_path)
|
|
9
10
|
with open(file) as f:
|
|
10
11
|
return json.load(f)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-payway"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.5"
|
|
8
8
|
description = "Python client for working with Westpac's PayWay REST API"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Ben Napper", email = "reppan197@gmail.com" }
|
|
@@ -114,7 +114,7 @@ exclude = ["__init__.py"]
|
|
|
114
114
|
required-imports = ["from __future__ import annotations"]
|
|
115
115
|
|
|
116
116
|
[tool.pyright]
|
|
117
|
-
typeCheckingMode = "
|
|
117
|
+
typeCheckingMode = "strict"
|
|
118
118
|
|
|
119
119
|
[tool.pytest.ini_options]
|
|
120
120
|
log_cli = true
|
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
4
|
import unittest
|
|
5
|
-
from typing import NoReturn
|
|
6
5
|
from unittest.mock import patch
|
|
7
6
|
|
|
8
7
|
from payway.client import Client
|
|
@@ -18,7 +17,7 @@ from payway.test_utils import load_json_file
|
|
|
18
17
|
|
|
19
18
|
class TestClient(unittest.TestCase):
|
|
20
19
|
@classmethod
|
|
21
|
-
def setUpClass(cls) ->
|
|
20
|
+
def setUpClass(cls) -> None:
|
|
22
21
|
merchant_id = "TEST"
|
|
23
22
|
bank_account_id = "0000000A"
|
|
24
23
|
publishable_api_key = "TPUBLISHABLE-API-KEY"
|
|
@@ -72,7 +71,7 @@ class TestClient(unittest.TestCase):
|
|
|
72
71
|
cls.payment = PayWayPayment(
|
|
73
72
|
customer_number="",
|
|
74
73
|
transaction_type="payment",
|
|
75
|
-
amount=
|
|
74
|
+
amount=10,
|
|
76
75
|
currency="aud",
|
|
77
76
|
order_number="5100",
|
|
78
77
|
ip_address="127.0.0.1",
|
|
@@ -80,7 +79,7 @@ class TestClient(unittest.TestCase):
|
|
|
80
79
|
cls.pre_auth_payment = PayWayPayment(
|
|
81
80
|
customer_number="",
|
|
82
81
|
transaction_type="preAuth",
|
|
83
|
-
amount=
|
|
82
|
+
amount=2.15,
|
|
84
83
|
currency="aud",
|
|
85
84
|
order_number="5110",
|
|
86
85
|
ip_address="127.0.0.1",
|
|
@@ -88,7 +87,7 @@ class TestClient(unittest.TestCase):
|
|
|
88
87
|
cls.pre_auth_capture_payment = PayWayPayment(
|
|
89
88
|
transaction_type="capture",
|
|
90
89
|
parent_transaction_id="",
|
|
91
|
-
amount=
|
|
90
|
+
amount=2.15,
|
|
92
91
|
order_number="5111",
|
|
93
92
|
ip_address="127.0.0.1",
|
|
94
93
|
)
|
|
@@ -104,7 +103,7 @@ class TestClient(unittest.TestCase):
|
|
|
104
103
|
)
|
|
105
104
|
|
|
106
105
|
@patch("requests.post")
|
|
107
|
-
def test_create_token(self, mock_post) ->
|
|
106
|
+
def test_create_token(self, mock_post) -> None:
|
|
108
107
|
mock_post.return_value.status_code = 200
|
|
109
108
|
mock_post.return_value.json.return_value = {
|
|
110
109
|
"singleUseTokenId": "2bcec36f-7b02-43db-b3ec-bfb65acfe272",
|
|
@@ -118,7 +117,7 @@ class TestClient(unittest.TestCase):
|
|
|
118
117
|
self.assertEqual(token_response.token, "2bcec36f-7b02-43db-b3ec-bfb65acfe272")
|
|
119
118
|
|
|
120
119
|
@patch("requests.post")
|
|
121
|
-
def test_create_bank_account_token(self, mock_post) ->
|
|
120
|
+
def test_create_bank_account_token(self, mock_post) -> None:
|
|
122
121
|
mock_post.return_value.status_code = 200
|
|
123
122
|
mock_post.return_value.json.return_value = {
|
|
124
123
|
"singleUseTokenId": "3bcec36f-7b02-43db-b3ec-bfb65acfe272",
|
|
@@ -131,7 +130,7 @@ class TestClient(unittest.TestCase):
|
|
|
131
130
|
self.assertEqual(token, "3bcec36f-7b02-43db-b3ec-bfb65acfe272")
|
|
132
131
|
|
|
133
132
|
@patch("requests.post")
|
|
134
|
-
def test_create_customer(self, mock_post) ->
|
|
133
|
+
def test_create_customer(self, mock_post) -> None:
|
|
135
134
|
mock_post.return_value.status_code = 200
|
|
136
135
|
mock_post.return_value.json.return_value = load_json_file("tests/data/customer.json")
|
|
137
136
|
card = self.card
|
|
@@ -146,7 +145,7 @@ class TestClient(unittest.TestCase):
|
|
|
146
145
|
self.assertEqual(payway_customer.email_address, "bect@example.net")
|
|
147
146
|
|
|
148
147
|
@patch("requests.put")
|
|
149
|
-
def test_create_customer_with_custom_id(self, mock_post) ->
|
|
148
|
+
def test_create_customer_with_custom_id(self, mock_post) -> None:
|
|
150
149
|
mock_post.return_value.status_code = 200
|
|
151
150
|
mock_post.return_value.json.return_value = load_json_file("tests/data/customer.json")
|
|
152
151
|
customer = copy.deepcopy(self.customer)
|
|
@@ -158,7 +157,7 @@ class TestClient(unittest.TestCase):
|
|
|
158
157
|
self.assertEqual(payway_customer_number, "98")
|
|
159
158
|
|
|
160
159
|
@patch("requests.post")
|
|
161
|
-
def test_process_payment(self, mock_post) ->
|
|
160
|
+
def test_process_payment(self, mock_post) -> None:
|
|
162
161
|
# Take payment (using a credit card token)
|
|
163
162
|
mock_post.return_value.status_code = 200
|
|
164
163
|
mock_post.return_value.json.return_value = load_json_file("tests/data/transaction.json")
|
|
@@ -177,7 +176,7 @@ class TestClient(unittest.TestCase):
|
|
|
177
176
|
self.assertEqual(transaction.response_code, "11")
|
|
178
177
|
|
|
179
178
|
@patch("requests.post")
|
|
180
|
-
def test_process_payment_with_idempotency_key(self, mock_post) ->
|
|
179
|
+
def test_process_payment_with_idempotency_key(self, mock_post) -> None:
|
|
181
180
|
"""
|
|
182
181
|
Send a payment using a unique idempotency key to try and avoid duplicate POSTs
|
|
183
182
|
https://www.payway.com.au/docs/rest.html#avoiding-duplicate-posts
|
|
@@ -204,7 +203,7 @@ class TestClient(unittest.TestCase):
|
|
|
204
203
|
self.assertEqual(transaction.response_code, "11")
|
|
205
204
|
|
|
206
205
|
@patch("requests.get")
|
|
207
|
-
def test_get_transaction_card(self, mock_get) ->
|
|
206
|
+
def test_get_transaction_card(self, mock_get) -> None:
|
|
208
207
|
mock_get.return_value.status_code = 200
|
|
209
208
|
mock_get.return_value.json.return_value = load_json_file("tests/data/card_transaction.json")
|
|
210
209
|
transaction, errors = self.client.get_transaction(
|
|
@@ -215,7 +214,7 @@ class TestClient(unittest.TestCase):
|
|
|
215
214
|
self.assertEqual(transaction.transaction_id, 1179985404)
|
|
216
215
|
|
|
217
216
|
@patch("requests.post")
|
|
218
|
-
def test_void(self, mock_post) ->
|
|
217
|
+
def test_void(self, mock_post) -> None:
|
|
219
218
|
mock_post.return_value.status_code = 200
|
|
220
219
|
mock_post.return_value.json.return_value = load_json_file("tests/data/void_transaction.json")
|
|
221
220
|
void_transaction, void_errors = self.client.void_transaction(1179985404)
|
|
@@ -224,7 +223,7 @@ class TestClient(unittest.TestCase):
|
|
|
224
223
|
self.assertEqual(void_transaction.status, "voided")
|
|
225
224
|
|
|
226
225
|
@patch("requests.post")
|
|
227
|
-
def test_refund(self, mock_post) ->
|
|
226
|
+
def test_refund(self, mock_post) -> None:
|
|
228
227
|
mock_post.return_value.status_code = 200
|
|
229
228
|
mock_post.return_value.json.return_value = load_json_file("tests/data/refund_transaction.json")
|
|
230
229
|
transaction, errors = self.client.refund_transaction(
|
|
@@ -235,7 +234,7 @@ class TestClient(unittest.TestCase):
|
|
|
235
234
|
self.assertEqual(transaction.status, "refunded")
|
|
236
235
|
|
|
237
236
|
@patch("requests.get")
|
|
238
|
-
def test_get_customer(self, mock_get) ->
|
|
237
|
+
def test_get_customer(self, mock_get) -> None:
|
|
239
238
|
mock_get.return_value.status_code = 200
|
|
240
239
|
mock_get.return_value.json.return_value = load_json_file("tests/data/customer.json")
|
|
241
240
|
customer, customer_errors = self.client.get_customer("98")
|
|
@@ -245,7 +244,7 @@ class TestClient(unittest.TestCase):
|
|
|
245
244
|
self.assertEqual(customer.customer_name, "Rebecca Turing")
|
|
246
245
|
|
|
247
246
|
@patch("requests.put")
|
|
248
|
-
def test_update_payment_setup_card(self, mock_post) ->
|
|
247
|
+
def test_update_payment_setup_card(self, mock_post) -> None:
|
|
249
248
|
# update card or bank account in PayWay from token
|
|
250
249
|
mock_post.return_value.status_code = 200
|
|
251
250
|
mock_post.return_value.json.return_value = {
|
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
4
|
import unittest
|
|
5
|
-
from typing import NoReturn
|
|
6
5
|
from unittest.mock import patch
|
|
7
6
|
|
|
8
7
|
from payway.client import Client
|
|
@@ -12,7 +11,7 @@ from payway.test_utils import load_json_file
|
|
|
12
11
|
|
|
13
12
|
class TestCustomerRequest(unittest.TestCase):
|
|
14
13
|
@classmethod
|
|
15
|
-
def setUpClass(cls) ->
|
|
14
|
+
def setUpClass(cls) -> None:
|
|
16
15
|
"""
|
|
17
16
|
You will need to create a sandbox PayWay account and add your sandbox API keys into your environment
|
|
18
17
|
"""
|
|
@@ -102,7 +101,7 @@ class TestCustomerRequest(unittest.TestCase):
|
|
|
102
101
|
)
|
|
103
102
|
|
|
104
103
|
@patch("requests.Session.patch")
|
|
105
|
-
def test_stop_all_payments(self, mock_post) ->
|
|
104
|
+
def test_stop_all_payments(self, mock_post) -> None:
|
|
106
105
|
# stop payments for customer
|
|
107
106
|
mock_post.return_value.status_code = 200
|
|
108
107
|
mock_post.return_value.json.return_value = {
|
|
@@ -113,7 +112,7 @@ class TestCustomerRequest(unittest.TestCase):
|
|
|
113
112
|
self.assertEqual(stopped, True)
|
|
114
113
|
|
|
115
114
|
@patch("requests.Session.patch")
|
|
116
|
-
def test_start_all_payments(self, mock_post) ->
|
|
115
|
+
def test_start_all_payments(self, mock_post) -> None:
|
|
117
116
|
# start payments for customer
|
|
118
117
|
mock_post.return_value.status_code = 200
|
|
119
118
|
mock_post.return_value.json.return_value = {
|
|
@@ -124,14 +123,14 @@ class TestCustomerRequest(unittest.TestCase):
|
|
|
124
123
|
self.assertEqual(stopped, False)
|
|
125
124
|
|
|
126
125
|
@patch("requests.Session.delete")
|
|
127
|
-
def test_delete_customer(self, mock_post) ->
|
|
126
|
+
def test_delete_customer(self, mock_post) -> None:
|
|
128
127
|
mock_post.return_value.status_code = 204
|
|
129
128
|
# delete customer record in PayWay
|
|
130
129
|
response = self.client.delete_customer("1")
|
|
131
130
|
self.assertEqual(response.status_code, 204)
|
|
132
131
|
|
|
133
132
|
@patch("requests.Session.put")
|
|
134
|
-
def test_schedule_payments(self, mock_post) ->
|
|
133
|
+
def test_schedule_payments(self, mock_post) -> None:
|
|
135
134
|
next_week = datetime.datetime.now() + datetime.timedelta(weeks=1)
|
|
136
135
|
next_payment_date = next_week.strftime("%d %b %Y")
|
|
137
136
|
mock_post.return_value.status_code = 200
|
|
@@ -156,14 +155,14 @@ class TestCustomerRequest(unittest.TestCase):
|
|
|
156
155
|
self.assertEqual(response["regularPaymentAmount"], 10.50)
|
|
157
156
|
|
|
158
157
|
@patch("requests.Session.delete")
|
|
159
|
-
def test_stop_schedule(self, mock_post) ->
|
|
158
|
+
def test_stop_schedule(self, mock_post) -> None:
|
|
160
159
|
# stop schedule
|
|
161
160
|
mock_post.return_value.status_code = 204
|
|
162
161
|
response = self.client.stop_schedule("1")
|
|
163
162
|
self.assertEqual(response.status_code, 204)
|
|
164
163
|
|
|
165
164
|
@patch("requests.Session.put")
|
|
166
|
-
def test_update_contact_details(self, mock_post) ->
|
|
165
|
+
def test_update_contact_details(self, mock_post) -> None:
|
|
167
166
|
# update contact details
|
|
168
167
|
mock_post.return_value.status_code = 200
|
|
169
168
|
mock_post.return_value.json.return_value = {
|
|
@@ -208,7 +207,7 @@ class TestCustomerRequest(unittest.TestCase):
|
|
|
208
207
|
self.assertEqual(address["postalCode"], new_postcode)
|
|
209
208
|
|
|
210
209
|
@patch("requests.Session.get")
|
|
211
|
-
def test_list_customers(self, mock_get) ->
|
|
210
|
+
def test_list_customers(self, mock_get) -> None:
|
|
212
211
|
mock_get.return_value.status_code = 200
|
|
213
212
|
mock_get.return_value.json.return_value = load_json_file("tests/data/customers.json")
|
|
214
213
|
response = self.client.list_customers()
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import unittest
|
|
4
|
-
from typing import NoReturn
|
|
5
4
|
from unittest.mock import patch
|
|
6
5
|
|
|
7
6
|
from payway.client import Client
|
|
@@ -10,7 +9,7 @@ from payway.test_utils import load_json_file
|
|
|
10
9
|
|
|
11
10
|
class TestTransactionRequest(unittest.TestCase):
|
|
12
11
|
@classmethod
|
|
13
|
-
def setUpClass(cls) ->
|
|
12
|
+
def setUpClass(cls) -> None:
|
|
14
13
|
merchant_id = "TEST"
|
|
15
14
|
bank_account_id = "0000000A"
|
|
16
15
|
publishable_api_key = "TPUBLISHABLE-API-KEY"
|
|
@@ -24,7 +23,7 @@ class TestTransactionRequest(unittest.TestCase):
|
|
|
24
23
|
)
|
|
25
24
|
|
|
26
25
|
@patch("requests.Session.get")
|
|
27
|
-
def test_search_transactions(self, mock_get) ->
|
|
26
|
+
def test_search_transactions(self, mock_get) -> None:
|
|
28
27
|
mock_get.return_value.status_code = 200
|
|
29
28
|
mock_get.return_value.json.return_value = load_json_file("tests/data/transactions.json")
|
|
30
29
|
query = "/search-customer?customerNumber=1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|