Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']

steps:
- uses: actions/checkout@v1
Expand Down
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,29 @@ new_payment.command_data_set().add_payment_method_data_source(gateway.DATA_SOURC
new_payment.command_data_set().add_payment_method_data_token('initial gateway-transaction-id')
```

### Using alternative payment methods

To use an alternative payment method (like Google Pay), send a received token AS-IS or data from a decrypted token.

```python
# set a corresponding flag that indicates a token provider
payment.command_data_set().add_payment_method_type(gateway.PAYMENT_METHOD_TYPE_GOOGLE_PAY)

# option 1: send received token AS-IS
payment.payment_method_set().add_token('<token>')

# option 2: send data from decrypted token
payment.payment_method_set().add_pan_number('4111111111111111')
payment.payment_method_set().add_pan_expiry_date('12/30')
payment.payment_method_set().add_pan_cardholder_name('John Doe') # if available
payment.payment_method_set().add_external_token_cryptogram('<cryptogram from token>') # if available
payment.payment_method_set().add_external_token_eci('<ECI from token>') # if available
payment.payment_method_set().add_external_token_trans_status('<transStatus from token>') # available for Click to Pay
payment.payment_method_set().add_external_token_ds_trans_id('<dsTransId from token>') # available for Click to Pay
payment.payment_method_set().add_external_token_acs_trans_id('<acsTransId from token>') # available for Click to Pay
payment.payment_method_set().add_external_token_cardholder_authenticated(decryptedToken.paymentMethodDetails.assuranceDetails.cardHolderAuthenticated) # for Google Pay
```

### Callback validation

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

### License
This library is licensed under the MIT License - see the `LICENSE` file for details.
This library is licensed under the MIT License - see the `LICENSE` file for details.
5 changes: 5 additions & 0 deletions gateway/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,8 @@
DATA_SOURCE_USE_MERCHANT_SAVED_CARDHOLDER_INITIATED = 4
DATA_SOURCE_USE_GATEWAY_SAVED_MERCHANT_INITIATED = 5
DATA_SOURCE_USE_MERCHANT_SAVED_MERCHANT_INITIATED = 6

PAYMENT_METHOD_TYPE_CARD = 'cc'
PAYMENT_METHOD_TYPE_GOOGLE_PAY = 'google_pay'
PAYMENT_METHOD_TYPE_APPLE_PAY = 'apple_pay'
PAYMENT_METHOD_TYPE_CLICK2PAY = 'click2pay'
19 changes: 19 additions & 0 deletions gateway/builders/command_data_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,22 @@ def add_payment_method_data_token(self, token=None):
new_key=self.__COMMAND_DATA_KEY,
new_dict={self.__data_sets.COMMAND_DATA_PAYMENT_METHOD_DATA_TOKEN: token}
)

def add_payment_method_type(self, type=None):
"""
Add payment method type

Args:
type (str): payment method type
cc: card data
google_pay: Google Pay token
apple_pay: Apple Pay token
click2pay: Click to Pay token
"""

self.__data_structure_util.add_to_dict(
source_dict=self.__command_data_set,
working_dict=self.__command_data_nested_structure,
new_key=self.__COMMAND_DATA_KEY,
new_dict={self.__data_sets.COMMAND_DATA_PAYMENT_METHOD_TYPE: type}
)
121 changes: 121 additions & 0 deletions gateway/builders/payment_data_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class PaymentDataBuilder(object):
__PAYMENT_METHOD_DATA_KEY = 'payment-method-data'
# Nested layer of external 3-D Secure data set
__EXTERNAL_MPI_DATA_KEY = 'external-mpi-data'
# Nested layer of decrypted token's data set
__EXTERNAL_TOKEN_DATA_KEY = 'external-token-data'

def __init__(self, __client_transaction_data_set, __client_mandatory_fields):
self.__payment_data_structure = {
Expand All @@ -44,6 +46,10 @@ def __init__(self, __client_transaction_data_set, __client_mandatory_fields):
self.__EXTERNAL_MPI_DATA_KEY: None
}

self.__external_token_data_structure = {
self.__EXTERNAL_TOKEN_DATA_KEY: None
}

self.__data_structure_util = DataStructuresUtils
self.__data_sets = RequestParameters
self.__data_types = RequestParametersTypes
Expand All @@ -54,6 +60,10 @@ def __setup_external_mpi_data(self):
if self.__EXTERNAL_MPI_DATA_KEY not in self.__payment_data_structure:
self.__payment_data_structure[self.__EXTERNAL_MPI_DATA_KEY] = self.__external_mpi_data_structure

def __setup_external_token_data(self):
if self.__EXTERNAL_TOKEN_DATA_KEY not in self.__payment_data_structure:
self.__payment_data_structure[self.__EXTERNAL_TOKEN_DATA_KEY] = self.__external_token_data_structure

def add_pan_number(self, pan_number=None):
"""
Add credit card number
Expand Down Expand Up @@ -115,6 +125,21 @@ def add_pan_cardholder_name(self, first_last_name=None):
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_CARDHOLDER_NAME: first_last_name}
)

def add_token(self, token=None):
"""
Add token

Args:
token (str): Token AS-IS
"""

self.__data_structure_util.add_to_dict(
source_dict=self.__payment_data_set,
working_dict=self.__payment_data_structure,
new_key=self.__PAYMENT_METHOD_DATA_KEY,
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_TOKEN: token}
)

def add_external_mpi_protocol_version(self, protocol_version=None):
"""
Add 3-D Secure protocolVersion
Expand Down Expand Up @@ -194,3 +219,99 @@ def add_external_mpi_trans_status(self, trans_status=None):
new_key=self.__EXTERNAL_MPI_DATA_KEY,
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS: trans_status}
)

def add_external_token_cryptogram(self, cryptogram=None):
"""
Add cryptogram from decrypted token's data

Args:
cryptogram (str): token cryptogram (TAVV etc.)
"""

self.__setup_external_token_data()
self.__data_structure_util.add_to_dict(
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
working_dict=self.__external_token_data_structure,
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM: cryptogram}
)

def add_external_token_eci(self, eci=None):
"""
Add ECI from decrypted token's data

Args:
eci (str): token ECI
"""

self.__setup_external_token_data()
self.__data_structure_util.add_to_dict(
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
working_dict=self.__external_token_data_structure,
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI: eci}
)

def add_external_token_trans_status(self, trans_status=None):
"""
Add transStatus from decrypted token's data

Args:
trans_status (str): token transStatus
"""

self.__setup_external_token_data()
self.__data_structure_util.add_to_dict(
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
working_dict=self.__external_token_data_structure,
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS: trans_status}
)

def add_external_token_ds_trans_id(self, ds_trans_id=None):
"""
Add dsTransId from decrypted token's data

Args:
ds_trans_id (str): token dsTransId
"""

self.__setup_external_token_data()
self.__data_structure_util.add_to_dict(
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
working_dict=self.__external_token_data_structure,
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID: ds_trans_id}
)

def add_external_token_acs_trans_id(self, acs_trans_id=None):
"""
Add acsTransId from decrypted token's data

Args:
acs_trans_id (str): token acsTransId
"""

self.__setup_external_token_data()
self.__data_structure_util.add_to_dict(
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
working_dict=self.__external_token_data_structure,
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID: acs_trans_id}
)

def add_external_token_cardholder_authenticated(self, cardholder_authenticated=None):
"""
Add cardHolderAuthenticated from decrypted Google Pay token's data

Args:
cardholder_authenticated (bool): value of paymentMethodDetails.assuranceDetails.cardHolderAuthenticated from Google Pay token
"""

self.__setup_external_token_data()
self.__data_structure_util.add_to_dict(
source_dict=self.__payment_data_structure[self.__PAYMENT_METHOD_DATA_KEY],
working_dict=self.__external_token_data_structure,
new_key=self.__EXTERNAL_TOKEN_DATA_KEY,
new_dict={self.__data_sets.PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED: cardholder_authenticated}
)
16 changes: 16 additions & 0 deletions gateway/data_sets/request_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class RequestParameters:
COMMAND_DATA_CARDS_VERIFICATION = 'card-verification'
COMMAND_DATA_PAYMENT_METHOD_DATA_SOURCE = 'payment-method-data-source'
COMMAND_DATA_PAYMENT_METHOD_DATA_TOKEN = 'payment-method-data-token'
COMMAND_DATA_PAYMENT_METHOD_TYPE = 'payment-method-type'

# Customer data sets
GENERAL_DATA_CUSTOMER_DATA_EMAIL = 'email'
Expand Down Expand Up @@ -81,11 +82,18 @@ class RequestParameters:
PAYMENT_METHOD_DATA_EXPIRE = 'exp-mm-yy'
PAYMENT_METHOD_DATA_CVV = 'cvv'
PAYMENT_METHOD_DATA_CARDHOLDER_NAME = 'cardholder-name'
PAYMENT_METHOD_DATA_TOKEN = 'token'
PAYMENT_METHOD_DATA_EXTERNAL_MPI_PROTOCOL = 'protocolVersion'
PAYMENT_METHOD_DATA_EXTERNAL_MPI_DS_TRANS_ID = 'dsTransID'
PAYMENT_METHOD_DATA_EXTERNAL_MPI_XID = 'xid'
PAYMENT_METHOD_DATA_EXTERNAL_MPI_CAVV = 'cavv'
PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS = 'transStatus'
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM = 'cryptogram'
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI = 'eci'
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS = 'transStatus'
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID = 'dsTransID'
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID = 'acsTransID'
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED = 'cardHolderAuthenticated'

# Money data sets
MONEY_DATA_AMOUNT = 'amount'
Expand Down Expand Up @@ -133,6 +141,7 @@ class RequestParametersTypes(RequestParameters):
COMMAND_DATA_CARDS_VERIFICATION = int
COMMAND_DATA_PAYMENT_METHOD_DATA_SOURCE = int
COMMAND_DATA_PAYMENT_METHOD_DATA_TOKEN = str
COMMAND_DATA_PAYMENT_METHOD_TYPE = str

# Customer data sets
GENERAL_DATA_CUSTOMER_DATA_EMAIL = str
Expand Down Expand Up @@ -174,11 +183,18 @@ class RequestParametersTypes(RequestParameters):
PAYMENT_METHOD_DATA_EXPIRE = str
PAYMENT_METHOD_DATA_CVV = str
PAYMENT_METHOD_DATA_CARDHOLDER_NAME = str
PAYMENT_METHOD_DATA_TOKEN = str
PAYMENT_METHOD_DATA_EXTERNAL_MPI_PROTOCOL = str
PAYMENT_METHOD_DATA_EXTERNAL_MPI_DS_TRANS_ID = str
PAYMENT_METHOD_DATA_EXTERNAL_MPI_XID = str
PAYMENT_METHOD_DATA_EXTERNAL_MPI_CAVV = str
PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS = str
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM = str
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI = str
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS = str
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID = str
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID = str
PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED = bool

# Money data sets
MONEY_DATA_AMOUNT = int
Expand Down
2 changes: 1 addition & 1 deletion gateway/transport/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def new_http_client(cli_name='requests', *args, **kwargs):

class HttpTransport(object):
def __init__(self, verify_ssl=True, proxy=None, timeout=60):
if verify_ssl is bool:
if isinstance(verify_ssl, bool):
self.verify_ssl = verify_ssl
else:
self.verify_ssl = True
Expand Down
11 changes: 5 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
try:
import pypandoc

LONG_DESCRIPTION = pypandoc.convert('README.md', 'rst')
LONG_DESCRIPTION = pypandoc.convert_file('README.md', 'rst')
except (IOError, ImportError, OSError, RuntimeError):
LONG_DESCRIPTION = ''

Expand All @@ -21,12 +21,11 @@
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Topic :: Software Development :: Libraries :: Python Modules'
]

Expand All @@ -36,7 +35,7 @@

setuptools.setup(
name='transactpro-gw3-client',
version='1.7.8',
version='1.7.9',
description='Transact PRO Gateway3 implementation in Python.',
long_description=LONG_DESCRIPTION,
long_description_content_type="text/markdown",
Expand All @@ -48,5 +47,5 @@
license='MIT',
classifiers=CLASSIFIERS,
keywords='GW3 gateway3 integration gateway TransactPRO python python3',
python_requires='>=3.6',
python_requires='>=3.9',
)
4 changes: 3 additions & 1 deletion tests/builders/test_command_data_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def test_mandatory_and_data_fields(self):
new.add_card_verification_mode(321)
new.add_payment_method_data_source(456)
new.add_payment_method_data_token('mega-token')
new.add_payment_method_type('google_pay')
from gateway.data_sets.request_parameters import (RequestParameters, RequestParametersTypes)
valid_fields_types = {
RequestParameters.COMMAND_DATA_GATEWAY_TRANSACTION_ID:
Expand All @@ -60,7 +61,8 @@ def test_mandatory_and_data_fields(self):
'form-id': '#Bravo345',
'card-verification': 321,
'payment-method-data-source': 456,
'payment-method-data-token': 'mega-token'
'payment-method-data-token': 'mega-token',
'payment-method-type': 'google_pay'
}
}
self.assertDictEqual(valid_data_structure, self.DATA)
17 changes: 17 additions & 0 deletions tests/builders/test_payment_data_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,21 @@ def test_additional_data_fields(self):
new.add_pan_cvv_code(cvv_number='442')
new.add_pan_expiry_date(mm_yy='12/30')
new.add_pan_number(pan_number='4222222222222')
new.add_token(token='qwerty')

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

new.add_external_token_cryptogram('AAMI/uGZvlKCygBkcQIlLJeBTPLG')
new.add_external_token_eci('07')
new.add_external_token_trans_status('N')
new.add_external_token_ds_trans_id('33321368-1c3d-4f3c-ba34-2efb76644c320')
new.add_external_token_acs_trans_id('99921368-1c3d-4f3c-ba34-2efb76644c320')
new.add_external_token_cardholder_authenticated(True)

from gateway.data_sets.request_parameters import (RequestParameters, RequestParametersTypes)
valid_fields_types = {
RequestParameters.PAYMENT_METHOD_DATA_PAN:
Expand All @@ -86,12 +94,21 @@ def test_additional_data_fields(self):
'pan': '4222222222222',
'cardholder-name': 'Jane Doe',
'exp-mm-yy': '12/30',
'token': 'qwerty',
'external-mpi-data': {
'protocolVersion': '2.2.0',
'dsTransID': '26221368-1c3d-4f3c-ba34-2efb76644c320',
'xid': 'b+f8duAy8jNTQ0DB4U3mSmPyp8s=',
'cavv': 'kBMI/uGZvlKCygBkcQIlLJeBTPLG',
'transStatus': 'Y'
},
'external-token-data': {
'cryptogram': 'AAMI/uGZvlKCygBkcQIlLJeBTPLG',
'eci': '07',
'cardHolderAuthenticated': True,
'transStatus': 'N',
'dsTransID': '33321368-1c3d-4f3c-ba34-2efb76644c320',
'acsTransID': '99921368-1c3d-4f3c-ba34-2efb76644c320'
}
}
}
Expand Down