Skip to content

Commit 5bbccab

Browse files
committed
Add alternative payment methods support (like Google Pay)
1 parent 64d31aa commit 5bbccab

File tree

10 files changed

+212
-10
lines changed

10 files changed

+212
-10
lines changed

.github/workflows/pythonpackage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
runs-on: ubuntu-latest
99
strategy:
1010
matrix:
11-
python-version: ['3.9', '3.10', '3.11', '3.12']
11+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
1212

1313
steps:
1414
- uses: actions/checkout@v1

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,29 @@ new_payment.command_data_set().add_payment_method_data_source(gateway.DATA_SOURC
145145
new_payment.command_data_set().add_payment_method_data_token('initial gateway-transaction-id')
146146
```
147147

148+
### Using alternative payment methods
149+
150+
To use an alternative payment method (like Google Pay), send a received token AS-IS or data from a decrypted token.
151+
152+
```python
153+
# set a corresponding flag that indicates a token provider
154+
payment.command_data_set().add_payment_method_type(gateway.PAYMENT_METHOD_TYPE_GOOGLE_PAY)
155+
156+
# option 1: send received token AS-IS
157+
payment.payment_method_set().add_token('<token>')
158+
159+
# option 2: send data from decrypted token
160+
payment.payment_method_set().add_pan_number('4111111111111111')
161+
payment.payment_method_set().add_pan_expiry_date('12/30')
162+
payment.payment_method_set().add_pan_cardholder_name('John Doe') # if available
163+
payment.payment_method_set().add_external_token_cryptogram('<cryptogram from token>') # if available
164+
payment.payment_method_set().add_external_token_eci('<ECI from token>') # if available
165+
payment.payment_method_set().add_external_token_trans_status('<transStatus>') # available for Click to Pay
166+
payment.payment_method_set().add_external_token_ds_trans_id('<dsTransId>') # available for Click to Pay
167+
payment.payment_method_set().add_external_token_acs_trans_id('<acsTransId>') # available for Click to Pay
168+
payment.payment_method_set().add_external_token_cardholder_authenticated(decryptedToken.paymentMethodDetails.assuranceDetails.cardHolderAuthenticated) # for Google Pay
169+
```
170+
148171
### Callback validation
149172

150173
```python
@@ -221,4 +244,4 @@ Please review code style guideline and try to keep in accordance with it
221244
[CodeStyle](https://github.com/TransactPRO/gw3-python-client/blob/master/CODESTYLE.md)
222245

223246
### License
224-
This library is licensed under the MIT License - see the `LICENSE` file for details.
247+
This library is licensed under the MIT License - see the `LICENSE` file for details.

gateway/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,8 @@
5252
DATA_SOURCE_USE_MERCHANT_SAVED_CARDHOLDER_INITIATED = 4
5353
DATA_SOURCE_USE_GATEWAY_SAVED_MERCHANT_INITIATED = 5
5454
DATA_SOURCE_USE_MERCHANT_SAVED_MERCHANT_INITIATED = 6
55+
56+
PAYMENT_METHOD_TYPE_CARD = 'cc'
57+
PAYMENT_METHOD_TYPE_GOOGLE_PAY = 'google_pay'
58+
PAYMENT_METHOD_TYPE_APPLE_PAY = 'apple_pay'
59+
PAYMENT_METHOD_TYPE_CLICK2PAY = 'click2pay'

gateway/builders/command_data_builder.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,22 @@ def add_payment_method_data_token(self, token=None):
135135
new_key=self.__COMMAND_DATA_KEY,
136136
new_dict={self.__data_sets.COMMAND_DATA_PAYMENT_METHOD_DATA_TOKEN: token}
137137
)
138+
139+
def add_payment_method_type(self, type=None):
140+
"""
141+
Add payment method type
142+
143+
Args:
144+
type (str): payment method type
145+
cc: card data
146+
google_pay: Google Pay token
147+
apple_pay: Apple Pay token
148+
click2pay: Click to Pay token
149+
"""
150+
151+
self.__data_structure_util.add_to_dict(
152+
source_dict=self.__command_data_set,
153+
working_dict=self.__command_data_nested_structure,
154+
new_key=self.__COMMAND_DATA_KEY,
155+
new_dict={self.__data_sets.COMMAND_DATA_PAYMENT_METHOD_TYPE: type}
156+
)

gateway/builders/payment_data_builder.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class PaymentDataBuilder(object):
3434
__PAYMENT_METHOD_DATA_KEY = 'payment-method-data'
3535
# Nested layer of external 3-D Secure data set
3636
__EXTERNAL_MPI_DATA_KEY = 'external-mpi-data'
37+
# Nested layer of decrypted token's data set
38+
__EXTERNAL_TOKEN_DATA_KEY = 'external-token-data'
3739

3840
def __init__(self, __client_transaction_data_set, __client_mandatory_fields):
3941
self.__payment_data_structure = {
@@ -44,6 +46,10 @@ def __init__(self, __client_transaction_data_set, __client_mandatory_fields):
4446
self.__EXTERNAL_MPI_DATA_KEY: None
4547
}
4648

49+
self.__external_token_data_structure = {
50+
self.__EXTERNAL_TOKEN_DATA_KEY: None
51+
}
52+
4753
self.__data_structure_util = DataStructuresUtils
4854
self.__data_sets = RequestParameters
4955
self.__data_types = RequestParametersTypes
@@ -54,6 +60,10 @@ def __setup_external_mpi_data(self):
5460
if self.__EXTERNAL_MPI_DATA_KEY not in self.__payment_data_structure:
5561
self.__payment_data_structure[self.__EXTERNAL_MPI_DATA_KEY] = self.__external_mpi_data_structure
5662

63+
def __setup_external_token_data(self):
64+
if self.__EXTERNAL_TOKEN_DATA_KEY not in self.__payment_data_structure:
65+
self.__payment_data_structure[self.__EXTERNAL_TOKEN_DATA_KEY] = self.__external_token_data_structure
66+
5767
def add_pan_number(self, pan_number=None):
5868
"""
5969
Add credit card number
@@ -115,6 +125,21 @@ def add_pan_cardholder_name(self, first_last_name=None):
115125
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_CARDHOLDER_NAME: first_last_name}
116126
)
117127

128+
def add_token(self, token=None):
129+
"""
130+
Add token
131+
132+
Args:
133+
token (str): Token AS-IS
134+
"""
135+
136+
self.__data_structure_util.add_to_dict(
137+
source_dict=self.__payment_data_set,
138+
working_dict=self.__payment_data_structure,
139+
new_key=self.__PAYMENT_METHOD_DATA_KEY,
140+
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_TOKEN: token}
141+
)
142+
118143
def add_external_mpi_protocol_version(self, protocol_version=None):
119144
"""
120145
Add 3-D Secure protocolVersion
@@ -194,3 +219,99 @@ def add_external_mpi_trans_status(self, trans_status=None):
194219
new_key=self.__EXTERNAL_MPI_DATA_KEY,
195220
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS: trans_status}
196221
)
222+
223+
def add_external_token_cryptogram(self, cryptogram=None):
224+
"""
225+
Add cryptogram from decrypted token's data
226+
227+
Args:
228+
cryptogram (str): token cryptogram (TAVV etc.)
229+
"""
230+
231+
self.__setup_external_token_data()
232+
self.__data_structure_util.add_to_dict(
233+
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
234+
working_dict=self.__external_token_data_structure,
235+
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
236+
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM: cryptogram}
237+
)
238+
239+
def add_external_token_eci(self, eci=None):
240+
"""
241+
Add ECI from decrypted token's data
242+
243+
Args:
244+
eci (str): token ECI
245+
"""
246+
247+
self.__setup_external_token_data()
248+
self.__data_structure_util.add_to_dict(
249+
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
250+
working_dict=self.__external_token_data_structure,
251+
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
252+
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI: eci}
253+
)
254+
255+
def add_external_token_trans_status(self, trans_status=None):
256+
"""
257+
Add transStatus from decrypted token's data
258+
259+
Args:
260+
trans_status (str): token transStatus
261+
"""
262+
263+
self.__setup_external_token_data()
264+
self.__data_structure_util.add_to_dict(
265+
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
266+
working_dict=self.__external_token_data_structure,
267+
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
268+
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS: trans_status}
269+
)
270+
271+
def add_external_token_ds_trans_id(self, ds_trans_id=None):
272+
"""
273+
Add dsTransId from decrypted token's data
274+
275+
Args:
276+
ds_trans_id (str): token dsTransId
277+
"""
278+
279+
self.__setup_external_token_data()
280+
self.__data_structure_util.add_to_dict(
281+
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
282+
working_dict=self.__external_token_data_structure,
283+
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
284+
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID: ds_trans_id}
285+
)
286+
287+
def add_external_token_acs_trans_id(self, acs_trans_id=None):
288+
"""
289+
Add acsTransId from decrypted token's data
290+
291+
Args:
292+
acs_trans_id (str): token acsTransId
293+
"""
294+
295+
self.__setup_external_token_data()
296+
self.__data_structure_util.add_to_dict(
297+
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
298+
working_dict=self.__external_token_data_structure,
299+
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
300+
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID: acs_trans_id}
301+
)
302+
303+
def add_external_token_cardholder_authenticated(self, cardholder_authenticated=None):
304+
"""
305+
Add cardHolderAuthenticated from decrypted Google Pay token's data
306+
307+
Args:
308+
cardholder_authenticated (bool): value of paymentMethodDetails.assuranceDetails.cardHolderAuthenticated from Google Pay token
309+
"""
310+
311+
self.__setup_external_token_data()
312+
self.__data_structure_util.add_to_dict(
313+
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
314+
working_dict=self.__external_token_data_structure,
315+
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
316+
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED: cardholder_authenticated}
317+
)

gateway/data_sets/request_parameters.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class RequestParameters:
4040
COMMAND_DATA_CARDS_VERIFICATION = 'card-verification'
4141
COMMAND_DATA_PAYMENT_METHOD_DATA_SOURCE = 'payment-method-data-source'
4242
COMMAND_DATA_PAYMENT_METHOD_DATA_TOKEN = 'payment-method-data-token'
43+
COMMAND_DATA_PAYMENT_METHOD_TYPE = 'payment-method-type'
4344

4445
# Customer data sets
4546
GENERAL_DATA_CUSTOMER_DATA_EMAIL = 'email'
@@ -81,11 +82,18 @@ class RequestParameters:
8182
PAYMENT_METHOD_DATA_EXPIRE = 'exp-mm-yy'
8283
PAYMENT_METHOD_DATA_CVV = 'cvv'
8384
PAYMENT_METHOD_DATA_CARDHOLDER_NAME = 'cardholder-name'
85+
PAYMENT_METHOD_DATA_TOKEN = 'token'
8486
PAYMENT_METHOD_DATA_EXTERNAL_MPI_PROTOCOL = 'protocolVersion'
8587
PAYMENT_METHOD_DATA_EXTERNAL_MPI_DS_TRANS_ID = 'dsTransID'
8688
PAYMENT_METHOD_DATA_EXTERNAL_MPI_XID = 'xid'
8789
PAYMENT_METHOD_DATA_EXTERNAL_MPI_CAVV = 'cavv'
8890
PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS = 'transStatus'
91+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM = 'cryptogram'
92+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI = 'eci'
93+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS = 'transStatus'
94+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID = 'dsTransID'
95+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID = 'acsTransID'
96+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED = 'cardHolderAuthenticated'
8997

9098
# Money data sets
9199
MONEY_DATA_AMOUNT = 'amount'
@@ -133,6 +141,7 @@ class RequestParametersTypes(RequestParameters):
133141
COMMAND_DATA_CARDS_VERIFICATION = int
134142
COMMAND_DATA_PAYMENT_METHOD_DATA_SOURCE = int
135143
COMMAND_DATA_PAYMENT_METHOD_DATA_TOKEN = str
144+
COMMAND_DATA_PAYMENT_METHOD_TYPE = str
136145

137146
# Customer data sets
138147
GENERAL_DATA_CUSTOMER_DATA_EMAIL = str
@@ -174,11 +183,18 @@ class RequestParametersTypes(RequestParameters):
174183
PAYMENT_METHOD_DATA_EXPIRE = str
175184
PAYMENT_METHOD_DATA_CVV = str
176185
PAYMENT_METHOD_DATA_CARDHOLDER_NAME = str
186+
PAYMENT_METHOD_DATA_TOKEN = str
177187
PAYMENT_METHOD_DATA_EXTERNAL_MPI_PROTOCOL = str
178188
PAYMENT_METHOD_DATA_EXTERNAL_MPI_DS_TRANS_ID = str
179189
PAYMENT_METHOD_DATA_EXTERNAL_MPI_XID = str
180190
PAYMENT_METHOD_DATA_EXTERNAL_MPI_CAVV = str
181191
PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS = str
192+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM = str
193+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI = str
194+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS = str
195+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID = str
196+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID = str
197+
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED = bool
182198

183199
# Money data sets
184200
MONEY_DATA_AMOUNT = int

gateway/transport/transport.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def new_http_client(cli_name='requests', *args, **kwargs):
7575

7676
class HttpTransport(object):
7777
def __init__(self, verify_ssl=True, proxy=None, timeout=60):
78-
if verify_ssl is bool:
78+
if isinstance(verify_ssl, bool):
7979
self.verify_ssl = verify_ssl
8080
else:
8181
self.verify_ssl = True

setup.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
try:
1010
import pypandoc
1111

12-
LONG_DESCRIPTION = pypandoc.convert('README.md', 'rst')
12+
LONG_DESCRIPTION = pypandoc.convert_file('README.md', 'rst')
1313
except (IOError, ImportError, OSError, RuntimeError):
1414
LONG_DESCRIPTION = ''
1515

@@ -21,12 +21,11 @@
2121
'Operating System :: OS Independent',
2222
'Programming Language :: Python',
2323
'Programming Language :: Python :: 3',
24-
'Programming Language :: Python :: 3.6',
25-
'Programming Language :: Python :: 3.7',
26-
'Programming Language :: Python :: 3.8',
2724
'Programming Language :: Python :: 3.9',
2825
'Programming Language :: Python :: 3.10',
2926
'Programming Language :: Python :: 3.11',
27+
'Programming Language :: Python :: 3.12',
28+
'Programming Language :: Python :: 3.13',
3029
'Topic :: Software Development :: Libraries :: Python Modules'
3130
]
3231

@@ -36,7 +35,7 @@
3635

3736
setuptools.setup(
3837
name='transactpro-gw3-client',
39-
version='1.7.8',
38+
version='1.7.9',
4039
description='Transact PRO Gateway3 implementation in Python.',
4140
long_description=LONG_DESCRIPTION,
4241
long_description_content_type="text/markdown",
@@ -48,5 +47,5 @@
4847
license='MIT',
4948
classifiers=CLASSIFIERS,
5049
keywords='GW3 gateway3 integration gateway TransactPRO python python3',
51-
python_requires='>=3.6',
50+
python_requires='>=3.9',
5251
)

tests/builders/test_command_data_builder.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def test_mandatory_and_data_fields(self):
4747
new.add_card_verification_mode(321)
4848
new.add_payment_method_data_source(456)
4949
new.add_payment_method_data_token('mega-token')
50+
new.add_payment_method_type('google_pay')
5051
from gateway.data_sets.request_parameters import (RequestParameters, RequestParametersTypes)
5152
valid_fields_types = {
5253
RequestParameters.COMMAND_DATA_GATEWAY_TRANSACTION_ID:
@@ -60,7 +61,8 @@ def test_mandatory_and_data_fields(self):
6061
'form-id': '#Bravo345',
6162
'card-verification': 321,
6263
'payment-method-data-source': 456,
63-
'payment-method-data-token': 'mega-token'
64+
'payment-method-data-token': 'mega-token',
65+
'payment-method-type': 'google_pay'
6466
}
6567
}
6668
self.assertDictEqual(valid_data_structure, self.DATA)

tests/builders/test_payment_data_builder.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,21 @@ def test_additional_data_fields(self):
6767
new.add_pan_cvv_code(cvv_number='442')
6868
new.add_pan_expiry_date(mm_yy='12/30')
6969
new.add_pan_number(pan_number='4222222222222')
70+
new.add_token(token='qwerty')
7071

7172
new.add_external_mpi_protocol_version('2.2.0')
7273
new.add_external_mpi_ds_trans_id('26221368-1c3d-4f3c-ba34-2efb76644c320')
7374
new.add_external_mpi_xid('b+f8duAy8jNTQ0DB4U3mSmPyp8s=')
7475
new.add_external_mpi_cavv('kBMI/uGZvlKCygBkcQIlLJeBTPLG')
7576
new.add_external_mpi_trans_status('Y')
7677

78+
new.add_external_token_cryptogram('AAMI/uGZvlKCygBkcQIlLJeBTPLG')
79+
new.add_external_token_eci('07')
80+
new.add_external_token_trans_status('N')
81+
new.add_external_token_ds_trans_id('33321368-1c3d-4f3c-ba34-2efb76644c320')
82+
new.add_external_token_acs_trans_id('99921368-1c3d-4f3c-ba34-2efb76644c320')
83+
new.add_external_token_cardholder_authenticated(True)
84+
7785
from gateway.data_sets.request_parameters import (RequestParameters, RequestParametersTypes)
7886
valid_fields_types = {
7987
RequestParameters.PAYMENT_METHOD_DATA_PAN:
@@ -86,12 +94,21 @@ def test_additional_data_fields(self):
8694
'pan': '4222222222222',
8795
'cardholder-name': 'Jane Doe',
8896
'exp-mm-yy': '12/30',
97+
'token': 'qwerty',
8998
'external-mpi-data': {
9099
'protocolVersion': '2.2.0',
91100
'dsTransID': '26221368-1c3d-4f3c-ba34-2efb76644c320',
92101
'xid': 'b+f8duAy8jNTQ0DB4U3mSmPyp8s=',
93102
'cavv': 'kBMI/uGZvlKCygBkcQIlLJeBTPLG',
94103
'transStatus': 'Y'
104+
},
105+
'external-token-data': {
106+
'cryptogram': 'AAMI/uGZvlKCygBkcQIlLJeBTPLG',
107+
'eci': '07',
108+
'cardHolderAuthenticated': True,
109+
'transStatus': 'N',
110+
'dsTransID': '33321368-1c3d-4f3c-ba34-2efb76644c320',
111+
'acsTransID': '99921368-1c3d-4f3c-ba34-2efb76644c320'
95112
}
96113
}
97114
}

0 commit comments

Comments
 (0)