python-payway 0.0.5__tar.gz → 0.0.6__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 (24) hide show
  1. {python_payway-0.0.5 → python_payway-0.0.6}/PKG-INFO +3 -3
  2. {python_payway-0.0.5 → python_payway-0.0.6}/README.md +2 -2
  3. {python_payway-0.0.5 → python_payway-0.0.6}/payway/client.py +22 -49
  4. {python_payway-0.0.5 → python_payway-0.0.6}/payway/constants.py +22 -1
  5. {python_payway-0.0.5 → python_payway-0.0.6}/payway/customers.py +1 -1
  6. {python_payway-0.0.5 → python_payway-0.0.6}/payway/model.py +166 -192
  7. {python_payway-0.0.5 → python_payway-0.0.6}/payway/transactions.py +1 -1
  8. {python_payway-0.0.5 → python_payway-0.0.6}/pyproject.toml +66 -54
  9. {python_payway-0.0.5 → python_payway-0.0.6}/python_payway.egg-info/PKG-INFO +3 -3
  10. {python_payway-0.0.5 → python_payway-0.0.6}/python_payway.egg-info/SOURCES.txt +0 -1
  11. python_payway-0.0.5/payway/conf.py +0 -8
  12. {python_payway-0.0.5 → python_payway-0.0.6}/LICENSE +0 -0
  13. {python_payway-0.0.5 → python_payway-0.0.6}/payway/__init__.py +0 -0
  14. {python_payway-0.0.5 → python_payway-0.0.6}/payway/exceptions.py +0 -0
  15. {python_payway-0.0.5 → python_payway-0.0.6}/payway/test_utils.py +0 -0
  16. {python_payway-0.0.5 → python_payway-0.0.6}/payway/utils.py +0 -0
  17. {python_payway-0.0.5 → python_payway-0.0.6}/python_payway.egg-info/dependency_links.txt +0 -0
  18. {python_payway-0.0.5 → python_payway-0.0.6}/python_payway.egg-info/requires.txt +0 -0
  19. {python_payway-0.0.5 → python_payway-0.0.6}/python_payway.egg-info/top_level.txt +0 -0
  20. {python_payway-0.0.5 → python_payway-0.0.6}/setup.cfg +0 -0
  21. {python_payway-0.0.5 → python_payway-0.0.6}/tests/__init__.py +0 -0
  22. {python_payway-0.0.5 → python_payway-0.0.6}/tests/test_client.py +0 -0
  23. {python_payway-0.0.5 → python_payway-0.0.6}/tests/test_customers.py +0 -0
  24. {python_payway-0.0.5 → python_payway-0.0.6}/tests/test_transactions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-payway
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Python client for working with Westpac's PayWay REST API
5
5
  Author-email: Ben Napper <reppan197@gmail.com>
6
6
  License: MIT
@@ -232,7 +232,7 @@ Please follow PayWay's advice about reducing your risk of fraudulent transaction
232
232
 
233
233
  ```bash
234
234
  uv python install 3.8.19
235
- uv venv
235
+ uv venv --python 3.8.19
236
236
  source .venv/bin/activate
237
237
  uv sync --extra dev
238
238
  ```
@@ -240,5 +240,5 @@ uv sync --extra dev
240
240
  ## Testing
241
241
 
242
242
  ```bash
243
- python -m unittest discover tests
243
+ uv run pytest tests/ -v
244
244
  ```
@@ -213,7 +213,7 @@ Please follow PayWay's advice about reducing your risk of fraudulent transaction
213
213
 
214
214
  ```bash
215
215
  uv python install 3.8.19
216
- uv venv
216
+ uv venv --python 3.8.19
217
217
  source .venv/bin/activate
218
218
  uv sync --extra dev
219
219
  ```
@@ -221,5 +221,5 @@ uv sync --extra dev
221
221
  ## Testing
222
222
 
223
223
  ```bash
224
- python -m unittest discover tests
224
+ uv run pytest tests/ -v
225
225
  ```
@@ -6,10 +6,13 @@ from typing import Any
6
6
 
7
7
  import requests
8
8
 
9
- from payway.conf import CUSTOMER_URL, TOKEN_NO_REDIRECT, TRANSACTION_URL
10
9
  from payway.constants import (
11
10
  BANK_ACCOUNT_PAYMENT_CHOICE,
12
11
  CREDIT_CARD_PAYMENT_CHOICE,
12
+ CUSTOMER_URL,
13
+ PAYWAY_ERROR_RESPONSE_CODES,
14
+ TOKEN_NO_REDIRECT,
15
+ TRANSACTION_URL,
13
16
  VALID_PAYMENT_METHOD_CHOICES,
14
17
  )
15
18
  from payway.customers import CustomerRequest
@@ -48,10 +51,10 @@ class Client(CustomerRequest, TransactionRequest):
48
51
  publishable_api_key: str,
49
52
  ) -> None:
50
53
  """
51
- :param merchant_id : str = PayWay Merchant ID
52
- :param bank_account_id : str = PayWay Bank Account ID
53
- :param secret_api_key : str = PayWay Secret APi Key
54
- :param publishable_api_key : str = PayWay Publishable API Key
54
+ :param merchant_id: PayWay Merchant ID
55
+ :param bank_account_id: PayWay Bank Account ID
56
+ :param secret_api_key: PayWay Secret APi Key
57
+ :param publishable_api_key: PayWay Publishable API Key
55
58
  """
56
59
  self._validate_credentials(
57
60
  merchant_id,
@@ -63,7 +66,6 @@ class Client(CustomerRequest, TransactionRequest):
63
66
  self.bank_account_id = bank_account_id
64
67
  self.secret_api_key = secret_api_key
65
68
  self.publishable_api_key = publishable_api_key
66
-
67
69
  session = requests.Session()
68
70
  session.auth = (self.secret_api_key, "")
69
71
  session.headers = {"content-type": "application/x-www-form-urlencoded"}
@@ -79,16 +81,14 @@ class Client(CustomerRequest, TransactionRequest):
79
81
  secret_api_key: str,
80
82
  publishable_api_key: str,
81
83
  ) -> None:
82
- if not merchant_id or not bank_account_id or not secret_api_key or not publishable_api_key:
83
- if not secret_api_key or not publishable_api_key:
84
- logger.error("PayWay API keys not found")
85
- raise PaywayError(
86
- message="PayWay API keys not found",
87
- code="INVALID_API_KEYS",
88
- )
89
- logger.error(
90
- "Merchant ID, bank account ID, secret API key, publishable API key are " "invalid",
84
+ if any(not key for key in (secret_api_key, publishable_api_key)):
85
+ logger.error("PayWay API keys not found")
86
+ raise PaywayError(
87
+ message="PayWay API keys not found",
88
+ code="INVALID_API_KEYS",
91
89
  )
90
+ if any(not val for val in (merchant_id, bank_account_id)):
91
+ logger.error("Merchant ID or bank account ID invalid")
92
92
  raise PaywayError(
93
93
  message="Invalid credentials",
94
94
  code="INVALID_API_CREDENTIALS",
@@ -145,10 +145,9 @@ class Client(CustomerRequest, TransactionRequest):
145
145
  "paymentMethod": payway_payment_method,
146
146
  },
147
147
  )
148
- endpoint = TOKEN_NO_REDIRECT
149
148
  logger.info("Sending Create Token request to PayWay.")
150
149
  response = self.post_request(
151
- endpoint,
150
+ TOKEN_NO_REDIRECT,
152
151
  data,
153
152
  auth=(self.publishable_api_key, ""),
154
153
  idempotency_key=idempotency_key,
@@ -156,8 +155,7 @@ class Client(CustomerRequest, TransactionRequest):
156
155
  errors = self._validate_response(response)
157
156
  if errors:
158
157
  return None, errors
159
- token_response = TokenResponse().from_dict(response.json())
160
- return token_response, errors
158
+ return TokenResponse().from_dict(response.json()), errors
161
159
 
162
160
  def create_card_token(
163
161
  self, card: PayWayCard, idempotency_key: str | None = None
@@ -187,23 +185,18 @@ class Client(CustomerRequest, TransactionRequest):
187
185
  ) -> tuple[PayWayCustomer | None, list[PaymentError] | None]:
188
186
  """
189
187
  Create a customer in PayWay system
190
-
191
188
  POST /customers to have PayWay generate the customer number
192
189
  PUT /customers/{customerNumber} to use your own customer number
193
-
194
190
  :param customer: PayWayCustomer object represents a customer in PayWay
195
191
  :param idempotency_key: str: unique value to avoid duplicate POSTs
196
192
  See model.PayWayCustomer
197
- :return:
198
193
  """
199
194
 
200
195
  data = customer.to_dict()
201
196
  data.update(
202
197
  {"merchantId": self.merchant_id, "bankAccountId": self.bank_account_id},
203
198
  )
204
-
205
199
  logger.info("Sending Create Customer request to PayWay.")
206
-
207
200
  if customer.custom_id:
208
201
  endpoint = f"{CUSTOMER_URL}/{customer.custom_id}"
209
202
  response = self.put_request(endpoint, data)
@@ -214,11 +207,9 @@ class Client(CustomerRequest, TransactionRequest):
214
207
  data,
215
208
  idempotency_key=idempotency_key,
216
209
  )
217
-
218
210
  errors = self._validate_response(response)
219
211
  if errors:
220
212
  return None, errors
221
-
222
213
  customer = PayWayCustomer().from_dict(response.json())
223
214
  return customer, errors
224
215
 
@@ -246,25 +237,11 @@ class Client(CustomerRequest, TransactionRequest):
246
237
  Validates all responses from PayWay to catch documented PayWay errors.
247
238
  :param response: requests response object
248
239
  """
249
- if response.status_code in [
250
- 400,
251
- 401,
252
- 403,
253
- 405,
254
- 406,
255
- 407,
256
- 409,
257
- 410,
258
- 415,
259
- 429,
260
- 501,
261
- 503,
262
- ]:
240
+ if response.status_code in PAYWAY_ERROR_RESPONSE_CODES:
263
241
  http_error_msg = f"{response.status_code} Client Error: {response.reason} for url: {response.url}"
264
242
  raise PaywayError(code=str(response.status_code), message=http_error_msg)
265
243
 
266
244
  if response.status_code in [404, 422]: # Documented PayWay errors in JSON
267
- # parse error message
268
245
  return PaymentError().from_dict(response.json())
269
246
 
270
247
  if response.status_code == 500:
@@ -308,8 +285,7 @@ class Client(CustomerRequest, TransactionRequest):
308
285
  errors = self._validate_response(response)
309
286
  if errors:
310
287
  return None, errors
311
- transaction = PayWayTransaction.from_dict(response.json())
312
- return transaction, errors
288
+ return PayWayTransaction.from_dict(response.json()), errors
313
289
 
314
290
  def refund_transaction(
315
291
  self,
@@ -340,8 +316,7 @@ class Client(CustomerRequest, TransactionRequest):
340
316
  errors = self._validate_response(response)
341
317
  if errors:
342
318
  return None, errors
343
- transaction = PayWayTransaction.from_dict(response.json())
344
- return transaction, errors
319
+ return PayWayTransaction.from_dict(response.json()), errors
345
320
 
346
321
  def get_customer(self, customer_id: str) -> tuple[PayWayCustomer | None, list[PaymentError] | None]:
347
322
  """
@@ -353,8 +328,7 @@ class Client(CustomerRequest, TransactionRequest):
353
328
  errors = self._validate_response(response)
354
329
  if errors:
355
330
  return None, errors
356
- customer = PayWayCustomer.from_dict(response.json())
357
- return customer, errors
331
+ return PayWayCustomer.from_dict(response.json()), errors
358
332
 
359
333
  def update_payment_setup(self, token: str, customer_id: str) -> tuple[PaymentSetup | None, list[PaymentError] | None]:
360
334
  """
@@ -372,5 +346,4 @@ class Client(CustomerRequest, TransactionRequest):
372
346
  errors = self._validate_response(response)
373
347
  if errors:
374
348
  return None, errors
375
- ps = PaymentSetup.from_dict(response.json())
376
- return ps, errors
349
+ return PaymentSetup.from_dict(response.json()), errors
@@ -1,5 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ PAYWAY_API_URL = "https://api.payway.com.au/rest/v1"
4
+ TOKEN_URL = PAYWAY_API_URL + "/single-use-tokens-redirect"
5
+ TRANSACTION_URL = PAYWAY_API_URL + "/transactions"
6
+ CUSTOMER_URL = PAYWAY_API_URL + "/customers"
7
+ OWN_BANK_ACCOUNTS_URL = PAYWAY_API_URL + "/your-bank-accounts"
8
+ TOKEN_NO_REDIRECT = PAYWAY_API_URL + "/single-use-tokens"
3
9
  TRANSACTION_APPROVED = "0"
4
10
 
5
11
  SUMMARY_CODES = {
@@ -69,7 +75,8 @@ EFT_RESPONSE_CODES = {
69
75
  CVN_RESPONSE_CODES = {
70
76
  "M": "Matched", # i.e. the CVN is correct
71
77
  "N": "Not Matched", # i.e. the CVN is incorrect
72
- "P": "Not Processed", # i.e. the CVN was not processed for some reason; do not assume that the CVN is necessarily correct
78
+ "P": "Not Processed",
79
+ # i.e. the CVN was not processed for some reason; do not assume that the CVN is necessarily correct
73
80
  "S": "Suspicious",
74
81
  "U": "Unknown", # i.e. the CVN was not processed for some reason; do not assume that the CVN is necessarily correct
75
82
  }
@@ -132,3 +139,17 @@ DIRECT_DEBIT_CHOICES = (
132
139
  )
133
140
  PAYMENT_METHOD_CHOICES = OTHER_PAYMENT_CHOICES + DIRECT_DEBIT_CHOICES
134
141
  VALID_PAYMENT_METHOD_CHOICES = ["card", "direct_debit"]
142
+ PAYWAY_ERROR_RESPONSE_CODES = [
143
+ 400,
144
+ 401,
145
+ 403,
146
+ 405,
147
+ 406,
148
+ 407,
149
+ 409,
150
+ 410,
151
+ 415,
152
+ 429,
153
+ 501,
154
+ 503,
155
+ ]
@@ -4,7 +4,7 @@ from typing import Any
4
4
 
5
5
  import requests
6
6
 
7
- from payway.conf import CUSTOMER_URL
7
+ from payway.constants import CUSTOMER_URL
8
8
  from payway.model import PayWayCustomer
9
9
  from payway.utils import json_list
10
10
 
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from dataclasses import dataclass
3
4
  from typing import Any
4
5
 
5
6
 
7
+ @dataclass
6
8
  class BankAccount:
7
9
  """
8
10
  account_name: str: Name used to open bank account.
@@ -10,10 +12,9 @@ class BankAccount:
10
12
  account_number: str: bank account number
11
13
  """
12
14
 
13
- def __init__(self, account_name: str, bsb: str, account_number: str) -> None:
14
- self.account_name = account_name
15
- self.bsb = bsb
16
- self.account_number = account_number
15
+ account_name: str
16
+ bsb: str
17
+ account_number: str
17
18
 
18
19
  def to_dict(self) -> dict[str, Any]:
19
20
  return {
@@ -23,20 +24,13 @@ class BankAccount:
23
24
  }
24
25
 
25
26
 
27
+ @dataclass
26
28
  class PayWayCard:
27
- def __init__(
28
- self,
29
- card_number: str | None = None,
30
- cvn: str | None = None,
31
- card_holder_name: str | None = None,
32
- expiry_date_month: str | None = None,
33
- expiry_date_year: str | None = None,
34
- ) -> None:
35
- self.card_number = card_number
36
- self.cvn = cvn
37
- self.card_holder_name = card_holder_name
38
- self.expiry_date_month = expiry_date_month
39
- self.expiry_date_year = expiry_date_year
29
+ card_number: str | None = None
30
+ cvn: str | None = None
31
+ card_holder_name: str | None = None
32
+ expiry_date_month: str | None = None
33
+ expiry_date_year: str | None = None
40
34
 
41
35
  def to_dict(self) -> dict[str, Any]:
42
36
  return {
@@ -49,58 +43,36 @@ class PayWayCard:
49
43
 
50
44
  @staticmethod
51
45
  def from_dict(payway_card: dict[str, Any]) -> PayWayCard:
52
- card = PayWayCard()
53
- if payway_card.get("maskedCardNumber"):
54
- card.card_number = payway_card.get("maskedCardNumber")
55
- else:
56
- card.card_number = payway_card.get("cardNumber")
57
- card.cvn = payway_card.get("cvn")
58
- card.card_holder_name = payway_card.get("cardholderName")
59
- card.expiry_date_month = payway_card.get("expiryDateMonth")
60
- card.expiry_date_year = payway_card.get("expiryDateYear")
61
- return card
46
+ card_number = payway_card.get("maskedCardNumber") or payway_card.get("cardNumber")
47
+ return PayWayCard(
48
+ card_number=card_number,
49
+ cvn=payway_card.get("cvn"),
50
+ card_holder_name=payway_card.get("cardholderName"),
51
+ expiry_date_month=payway_card.get("expiryDateMonth"),
52
+ expiry_date_year=payway_card.get("expiryDateYear"),
53
+ )
62
54
 
63
55
 
56
+ @dataclass
64
57
  class PayWayCustomer:
65
- def __init__(
66
- self,
67
- custom_id: str | None = None,
68
- customer_name: str | None = None,
69
- email_address: str | None = None,
70
- send_email_receipts: bool | None = None,
71
- phone_number: str | None = None,
72
- street: str | None = None,
73
- street2: str | None = None,
74
- city_name: str | None = None,
75
- state: str | None = None,
76
- postal_code: str | None = None,
77
- token: str | None = None,
78
- customer_number: str | None = None,
79
- payment_setup: PaymentSetup | None = None,
80
- notes: str | None = None,
81
- custom_field_1: str | None = None,
82
- custom_field_2: str | None = None,
83
- custom_field_3: str | None = None,
84
- custom_field_4: str | None = None,
85
- ) -> None:
86
- self.custom_id = custom_id
87
- self.customer_name = customer_name
88
- self.email_address = email_address
89
- self.send_email_receipts = send_email_receipts
90
- self.phone_number = phone_number
91
- self.street = street
92
- self.street2 = street2
93
- self.city_name = city_name
94
- self.state = state
95
- self.postal_code = postal_code
96
- self.token = token
97
- self.customer_number = customer_number
98
- self.payment_setup = payment_setup
99
- self.notes = notes
100
- self.customField1 = custom_field_1
101
- self.customField2 = custom_field_2
102
- self.customField3 = custom_field_3
103
- self.customField4 = custom_field_4
58
+ custom_id: str | None = None
59
+ customer_name: str | None = None
60
+ email_address: str | None = None
61
+ send_email_receipts: bool | None = None
62
+ phone_number: str | None = None
63
+ street: str | None = None
64
+ street2: str | None = None
65
+ city_name: str | None = None
66
+ state: str | None = None
67
+ postal_code: str | None = None
68
+ token: str | None = None
69
+ customer_number: str | None = None
70
+ payment_setup: PaymentSetup | None = None
71
+ notes: str | None = None
72
+ custom_field_1: str | None = None
73
+ custom_field_2: str | None = None
74
+ custom_field_3: str | None = None
75
+ custom_field_4: str | None = None
104
76
 
105
77
  def to_dict(self) -> dict[str, Any]:
106
78
  customer = {
@@ -114,10 +86,10 @@ class PayWayCustomer:
114
86
  "state": self.state,
115
87
  "postalCode": self.postal_code,
116
88
  "notes": self.notes,
117
- "customField1": self.customField1,
118
- "customField2": self.customField2,
119
- "customField3": self.customField3,
120
- "customField4": self.customField4,
89
+ "customField1": self.custom_field_1,
90
+ "customField2": self.custom_field_2,
91
+ "customField3": self.custom_field_3,
92
+ "customField4": self.custom_field_4,
121
93
  }
122
94
  if self.token:
123
95
  customer.update({"singleUseTokenId": self.token})
@@ -130,36 +102,33 @@ class PayWayCustomer:
130
102
  :param response: dict PayWay response dictionary
131
103
  :return:
132
104
  """
133
- customer = PayWayCustomer()
134
105
  contact = response.get("contact", {})
135
- customer.customer_name = contact.get("customerName")
136
- customer.email_address = contact.get("emailAddress")
137
- customer.send_email_receipts = contact.get("sendEmailReceipts")
138
- customer.phone_number = contact.get("phoneNumber")
139
- address = contact.get("address")
140
- customer.street = address.get("street1")
141
- customer.street2 = address.get("street2")
142
- customer.city_name = address.get("cityName")
143
- customer.state = address.get("state")
144
- customer.postal_code = address.get("postalCode")
145
- customer.customer_number = response.get("customerNumber")
146
-
106
+ address = contact.get("address", {})
107
+ payment_setup = None
147
108
  if response.get("paymentSetup") is not None:
148
- customer.payment_setup = PaymentSetup().from_dict(
149
- response.get("paymentSetup", {}),
150
- )
151
-
152
- if response.get("customFields") is not None:
153
- custom_fields = response.get("customFields", {})
154
- for k, v in custom_fields.items():
155
- setattr(customer, k, v)
156
-
157
- if response.get("notes") is not None:
158
- customer.notes = response["notes"]
159
-
160
- return customer
161
-
162
-
109
+ payment_setup = PaymentSetup.from_dict(response.get("paymentSetup", {}))
110
+ custom_fields = response.get("customFields", {})
111
+ return PayWayCustomer(
112
+ customer_name=contact.get("customerName"),
113
+ email_address=contact.get("emailAddress"),
114
+ send_email_receipts=contact.get("sendEmailReceipts"),
115
+ phone_number=contact.get("phoneNumber"),
116
+ street=address.get("street1"),
117
+ street2=address.get("street2"),
118
+ city_name=address.get("cityName"),
119
+ state=address.get("state"),
120
+ postal_code=address.get("postalCode"),
121
+ customer_number=response.get("customerNumber"),
122
+ payment_setup=payment_setup,
123
+ notes=response.get("notes"),
124
+ custom_field_1=custom_fields.get("customField1"),
125
+ custom_field_2=custom_fields.get("customField2"),
126
+ custom_field_3=custom_fields.get("customField3"),
127
+ custom_field_4=custom_fields.get("customField4"),
128
+ )
129
+
130
+
131
+ @dataclass
163
132
  class PaymentError:
164
133
  field_name: str | None = None
165
134
  message: str | None = None
@@ -172,14 +141,14 @@ class PaymentError:
172
141
  :param: payway_response: dict PayWay response dictionary
173
142
  """
174
143
  errors = payway_response.get("data", [])
175
- payment_errors = []
176
- for error in errors:
177
- payway_error = PaymentError()
178
- payway_error.field_name = error.get("fieldName")
179
- payway_error.message = error.get("message")
180
- payway_error.field_value = error.get("fieldValue")
181
- payment_errors.append(payway_error)
182
- return payment_errors
144
+ return [
145
+ PaymentError(
146
+ field_name=error.get("fieldName"),
147
+ message=error.get("message"),
148
+ field_value=error.get("fieldValue"),
149
+ )
150
+ for error in errors
151
+ ]
183
152
 
184
153
  def to_message(self) -> str:
185
154
  return f"Field: {self.field_name} Message: {self.message} Field Value: {self.field_value}"
@@ -199,6 +168,7 @@ class PaymentError:
199
168
  return message
200
169
 
201
170
 
171
+ @dataclass
202
172
  class ServerError:
203
173
  error_number: int | None = None
204
174
  trace_code: str | None = None
@@ -208,15 +178,16 @@ class ServerError:
208
178
  """
209
179
  :param: response: dict PayWay response dictionary
210
180
  """
211
- payway_error = ServerError()
212
- payway_error.error_number = response.get("errorNumber", {})
213
- payway_error.trace_code = response.get("traceCode", {})
214
- return payway_error
181
+ return ServerError(
182
+ error_number=response.get("errorNumber"),
183
+ trace_code=response.get("traceCode"),
184
+ )
215
185
 
216
186
  def to_message(self) -> str:
217
187
  return f"Error number: {self.error_number} Trace code: {self.trace_code}"
218
188
 
219
189
 
190
+ @dataclass
220
191
  class PayWayPayment:
221
192
  """
222
193
  customer_number: Customer to which this payment belongs.
@@ -230,27 +201,15 @@ class PayWayPayment:
230
201
  merchant_id: This merchant will be used for processing.
231
202
  """
232
203
 
233
- def __init__(
234
- self,
235
- transaction_type: str,
236
- customer_number: str | None = None,
237
- amount: float | None = None,
238
- currency: str | None = None,
239
- order_number: str | None = None,
240
- ip_address: str | None = None,
241
- parent_transaction_id: str | None = None,
242
- token: str | None = None,
243
- merchant_id: str | None = None,
244
- ) -> None:
245
- self.transaction_type = transaction_type
246
- self.customer_number = customer_number
247
- self.amount = amount
248
- self.currency = currency
249
- self.order_number = order_number
250
- self.ip_address = ip_address
251
- self.parent_transaction_id = parent_transaction_id
252
- self.token = token
253
- self.merchant_id = merchant_id
204
+ transaction_type: str
205
+ customer_number: str | None = None
206
+ amount: float | None = None
207
+ currency: str | None = None
208
+ order_number: str | None = None
209
+ ip_address: str | None = None
210
+ parent_transaction_id: str | None = None
211
+ token: str | None = None
212
+ merchant_id: str | None = None
254
213
 
255
214
  def to_dict(self) -> dict[str, Any]:
256
215
  payment = {
@@ -271,6 +230,7 @@ class PayWayPayment:
271
230
  return payment
272
231
 
273
232
 
233
+ @dataclass
274
234
  class PayWayTransaction:
275
235
  transaction_id: int | None = None
276
236
  receipt_number: str | None = None
@@ -328,7 +288,7 @@ class PayWayTransaction:
328
288
  "creditCard": self.card.to_dict() if self.card is not None else {},
329
289
  "merchant": self.merchant.to_dict() if self.merchant is not None else {},
330
290
  "virtualAccount": self.virtual_account,
331
- "bpaustraliaPostay": self.australia_post,
291
+ "australiaPost": self.australia_post,
332
292
  "bpay": self.bpay,
333
293
  "yourBankAccount": self.your_bank_account,
334
294
  "customerPayPalAccount": self.customer_paypal_account,
@@ -352,49 +312,53 @@ class PayWayTransaction:
352
312
  """
353
313
  :param: response: dict PayWay response dictionary
354
314
  """
355
- transaction = PayWayTransaction()
356
- transaction.transaction_id = response.get("transactionId")
357
- transaction.receipt_number = response.get("receiptNumber")
358
- transaction.status = response.get("status")
359
- transaction.response_code = response.get("responseCode")
360
- transaction.response_text = response.get("responseText")
361
- transaction.transaction_type = response.get("transactionType")
362
- transaction.customer_number = response.get("customerNumber")
363
- transaction.customer_name = response.get("customerName")
364
- transaction.customer_email = response.get("customerEmail")
365
- transaction.currency = response.get("currency")
366
- transaction.principal_amount = response.get("principalAmount")
367
- transaction.surcharge_amount = response.get("surchargeAmount")
368
- transaction.payment_amount = response.get("paymentAmount")
369
- transaction.payment_method = response.get("paymentMethod")
370
-
315
+ card = None
371
316
  if response.get("creditCard") is not None:
372
- transaction.card = PayWayCard.from_dict(response.get("creditCard", {}))
317
+ card = PayWayCard.from_dict(response.get("creditCard", {}))
373
318
 
319
+ merchant = None
374
320
  if response.get("merchant") is not None:
375
- transaction.merchant = Merchant.from_dict(response.get("merchant", {}))
376
-
377
- transaction.virtual_account = response.get("virtualAccount")
378
- transaction.australia_post = response.get("bpaustraliaPostay")
379
- transaction.bpay = response.get("bpay")
380
- transaction.your_bank_account = response.get("yourBankAccount")
381
- transaction.customer_paypal_account = response.get("customerPayPalAccount")
382
- transaction.your_paypal_account = response.get("yourPayPalAccount")
383
- transaction.transaction_date_time = response.get("transactionDateTime")
384
- transaction.user = response.get("user")
385
- transaction.settlement_date = response.get("settlementDate")
386
- transaction.declined_date = response.get("declinedDate")
387
- transaction.parent_transaction = response.get("parentTransaction")
388
- transaction.ip_address = response.get("customerIpAddress")
389
- transaction.fraud_result = response.get("fraudResult")
390
- transaction.ip_country = response.get("customerIpCountry")
391
- transaction.card_country = response.get("cardCountry")
392
- transaction.custom_fields = response.get("customFields")
393
- transaction.is_voidable = response.get("isVoidable")
394
- transaction.is_refundable = response.get("isRefundable")
395
- return transaction
396
-
397
-
321
+ merchant = Merchant.from_dict(response.get("merchant", {}))
322
+
323
+ return PayWayTransaction(
324
+ transaction_id=response.get("transactionId"),
325
+ receipt_number=response.get("receiptNumber"),
326
+ status=response.get("status"),
327
+ response_code=response.get("responseCode"),
328
+ response_text=response.get("responseText"),
329
+ transaction_type=response.get("transactionType"),
330
+ customer_number=response.get("customerNumber"),
331
+ customer_name=response.get("customerName"),
332
+ customer_email=response.get("customerEmail"),
333
+ currency=response.get("currency"),
334
+ principal_amount=response.get("principalAmount"),
335
+ surcharge_amount=response.get("surchargeAmount"),
336
+ payment_amount=response.get("paymentAmount"),
337
+ payment_method=response.get("paymentMethod"),
338
+ card=card,
339
+ merchant=merchant,
340
+ virtual_account=response.get("virtualAccount"),
341
+ australia_post=response.get("australiaPost"),
342
+ bpay=response.get("bpay"),
343
+ your_bank_account=response.get("yourBankAccount"),
344
+ customer_paypal_account=response.get("customerPayPalAccount"),
345
+ your_paypal_account=response.get("yourPayPalAccount"),
346
+ transaction_date_time=response.get("transactionDateTime"),
347
+ user=response.get("user"),
348
+ settlement_date=response.get("settlementDate"),
349
+ declined_date=response.get("declinedDate"),
350
+ parent_transaction=response.get("parentTransaction"),
351
+ ip_address=response.get("customerIpAddress"),
352
+ fraud_result=response.get("fraudResult"),
353
+ ip_country=response.get("customerIpCountry"),
354
+ card_country=response.get("cardCountry"),
355
+ custom_fields=response.get("customFields"),
356
+ is_voidable=response.get("isVoidable"),
357
+ is_refundable=response.get("isRefundable"),
358
+ )
359
+
360
+
361
+ @dataclass
398
362
  class Merchant:
399
363
  """
400
364
  merchantId Issued by us to uniquely identify a merchant facility
@@ -428,16 +392,17 @@ class Merchant:
428
392
  """
429
393
  :param: payway_obj: dict PayWay response dictionary
430
394
  """
431
- merchant = Merchant()
432
- merchant.merchant_id = payway_obj.get("merchantId")
433
- merchant.merchant_name = payway_obj.get("merchantName")
434
- merchant.settlement_bsb = payway_obj.get("settlementBsb")
435
- merchant.settlement_account_number = payway_obj.get("settlementAccountNumber")
436
- merchant.surcharge_bsb = payway_obj.get("surchargeBsb")
437
- merchant.surcharge_account_number = payway_obj.get("surchargeAccountNumber")
438
- return merchant
395
+ return Merchant(
396
+ merchant_id=payway_obj.get("merchantId"),
397
+ merchant_name=payway_obj.get("merchantName"),
398
+ settlement_bsb=payway_obj.get("settlementBsb"),
399
+ settlement_account_number=payway_obj.get("settlementAccountNumber"),
400
+ surcharge_bsb=payway_obj.get("surchargeBsb"),
401
+ surcharge_account_number=payway_obj.get("surchargeAccountNumber"),
402
+ )
439
403
 
440
404
 
405
+ @dataclass
441
406
  class PaymentSetup:
442
407
  payment_method: str | None = None
443
408
  stopped: bool | None = None
@@ -449,16 +414,23 @@ class PaymentSetup:
449
414
  """
450
415
  :param: response: dict PayWay response dictionary
451
416
  """
452
- ps = PaymentSetup()
453
- ps.payment_method = response.get("paymentMethod")
454
- ps.stopped = response.get("stopped")
417
+ credit_card = None
455
418
  if response.get("creditCard") is not None:
456
- ps.credit_card = PayWayCard().from_dict(response.get("creditCard", {}))
419
+ credit_card = PayWayCard.from_dict(response.get("creditCard", {}))
420
+
421
+ merchant = None
457
422
  if response.get("merchant") is not None:
458
- ps.merchant = Merchant().from_dict(response.get("merchant", {}))
459
- return ps
423
+ merchant = Merchant.from_dict(response.get("merchant", {}))
424
+
425
+ return PaymentSetup(
426
+ payment_method=response.get("paymentMethod"),
427
+ stopped=response.get("stopped"),
428
+ credit_card=credit_card,
429
+ merchant=merchant,
430
+ )
460
431
 
461
432
 
433
+ @dataclass
462
434
  class TokenResponse:
463
435
  token: str | None = None
464
436
  payment_method: str | None = None
@@ -470,10 +442,12 @@ class TokenResponse:
470
442
  """
471
443
  :param: response: dict PayWay response dictionary
472
444
  """
473
- tr = TokenResponse()
474
- tr.token = response.get("singleUseTokenId")
475
- tr.payment_method = response.get("paymentMethod")
445
+ card = None
476
446
  if response.get("creditCard") is not None:
477
- card = PayWayCard().from_dict(response["creditCard"])
478
- tr.card = card
479
- return tr
447
+ card = PayWayCard.from_dict(response["creditCard"])
448
+
449
+ return TokenResponse(
450
+ token=response.get("singleUseTokenId"),
451
+ payment_method=response.get("paymentMethod"),
452
+ card=card,
453
+ )
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import requests
4
4
 
5
- from payway.conf import TRANSACTION_URL
5
+ from payway.constants import TRANSACTION_URL
6
6
  from payway.utils import json_list
7
7
 
8
8
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-payway"
7
- version = "0.0.5"
7
+ version = "0.0.6"
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" }
@@ -24,9 +24,9 @@ dependencies = [
24
24
 
25
25
  [project.optional-dependencies]
26
26
  dev = [
27
- "pre-commit>=3.5.0",
28
- "ruff==0.6.6",
29
- "mypy>=0.971",
27
+ "pre-commit>=3.5.0",
28
+ "ruff==0.6.6",
29
+ "mypy>=0.971",
30
30
  ]
31
31
 
32
32
  [tool.mypy]
@@ -48,53 +48,53 @@ target-version = "py38"
48
48
 
49
49
  [tool.ruff.lint]
50
50
  select = [
51
- "F",
52
- "E",
53
- "W",
54
- "C90",
55
- "I",
56
- "N",
57
- "UP",
58
- "YTT",
59
- "ANN",
60
- "ASYNC",
61
- "S",
62
- "BLE",
63
- "FBT",
64
- "B",
65
- "A",
66
- "COM",
67
- "C4",
68
- "DTZ",
69
- "T10",
70
- "DJ",
71
- "EM",
72
- "EXE",
73
- "FA",
74
- 'ISC',
75
- "ICN",
76
- "G",
77
- 'INP',
78
- 'PIE',
79
- "T20",
80
- 'PYI',
81
- 'PT',
82
- "Q",
83
- "RSE",
84
- "RET",
85
- "SLF",
86
- "SLOT",
87
- "SIM",
88
- "TID",
89
- "INT",
90
- "ERA",
91
- "PD",
92
- "PGH",
93
- "PL",
94
- "TRY",
95
- "FLY",
96
- "PERF",
97
- "RUF",
51
+ "F",
52
+ "E",
53
+ "W",
54
+ "C90",
55
+ "I",
56
+ "N",
57
+ "UP",
58
+ "YTT",
59
+ "ANN",
60
+ "ASYNC",
61
+ "S",
62
+ "BLE",
63
+ "FBT",
64
+ "B",
65
+ "A",
66
+ "COM",
67
+ "C4",
68
+ "DTZ",
69
+ "T10",
70
+ "DJ",
71
+ "EM",
72
+ "EXE",
73
+ "FA",
74
+ 'ISC',
75
+ "ICN",
76
+ "G",
77
+ 'INP',
78
+ 'PIE',
79
+ "T20",
80
+ 'PYI',
81
+ 'PT',
82
+ "Q",
83
+ "RSE",
84
+ "RET",
85
+ "SLF",
86
+ "SLOT",
87
+ "SIM",
88
+ "TID",
89
+ "INT",
90
+ "ERA",
91
+ "PD",
92
+ "PGH",
93
+ "PL",
94
+ "TRY",
95
+ "FLY",
96
+ "PERF",
97
+ "RUF",
98
98
  ]
99
99
  ignore = ["ANN101", "COM812", "ISC001"]
100
100
 
@@ -116,6 +116,10 @@ required-imports = ["from __future__ import annotations"]
116
116
  [tool.pyright]
117
117
  typeCheckingMode = "strict"
118
118
 
119
+ [tool.ty.environment]
120
+ extra-paths = []
121
+ python-version = "3.8"
122
+
119
123
  [tool.pytest.ini_options]
120
124
  log_cli = true
121
125
  log_cli_level = "INFO"
@@ -123,11 +127,19 @@ log_format = "%(asctime)s %(levelname)s %(message)s"
123
127
  log_date_format = "%Y-%m-%d %H:%M:%S"
124
128
  addopts = "-v -ra -q"
125
129
  filterwarnings = [
126
- "ignore:.*defines default_app_config.*",
127
- "ignore:The providing_args argument is deprecated..*",
128
- "ignore::DeprecationWarning",
130
+ "ignore:.*defines default_app_config.*",
131
+ "ignore:The providing_args argument is deprecated..*",
132
+ "ignore::DeprecationWarning",
129
133
  ]
130
134
 
131
135
  [tool.bandit]
132
136
  exclude_dirs = ["tests", "migrations"]
133
137
  tests = ["B201", "B301"]
138
+
139
+ [dependency-groups]
140
+ dev = [
141
+ "mypy>=0.971",
142
+ "pytest>=8.3.5",
143
+ "ruff>=0.6.6",
144
+ "ty>=0.0.1a33",
145
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-payway
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Python client for working with Westpac's PayWay REST API
5
5
  Author-email: Ben Napper <reppan197@gmail.com>
6
6
  License: MIT
@@ -232,7 +232,7 @@ Please follow PayWay's advice about reducing your risk of fraudulent transaction
232
232
 
233
233
  ```bash
234
234
  uv python install 3.8.19
235
- uv venv
235
+ uv venv --python 3.8.19
236
236
  source .venv/bin/activate
237
237
  uv sync --extra dev
238
238
  ```
@@ -240,5 +240,5 @@ uv sync --extra dev
240
240
  ## Testing
241
241
 
242
242
  ```bash
243
- python -m unittest discover tests
243
+ uv run pytest tests/ -v
244
244
  ```
@@ -3,7 +3,6 @@ README.md
3
3
  pyproject.toml
4
4
  payway/__init__.py
5
5
  payway/client.py
6
- payway/conf.py
7
6
  payway/constants.py
8
7
  payway/customers.py
9
8
  payway/exceptions.py
@@ -1,8 +0,0 @@
1
- from __future__ import annotations
2
-
3
- PAYWAY_API_URL = "https://api.payway.com.au/rest/v1"
4
- TOKEN_URL = PAYWAY_API_URL + "/single-use-tokens-redirect"
5
- TRANSACTION_URL = PAYWAY_API_URL + "/transactions"
6
- CUSTOMER_URL = PAYWAY_API_URL + "/customers"
7
- OWN_BANK_ACCOUNTS_URL = PAYWAY_API_URL + "/your-bank-accounts"
8
- TOKEN_NO_REDIRECT = PAYWAY_API_URL + "/single-use-tokens"
File without changes
File without changes