diff --git a/Adyen/__init__.py b/Adyen/__init__.py index a4afcd2d..03a00f75 100644 --- a/Adyen/__init__.py +++ b/Adyen/__init__.py @@ -18,7 +18,8 @@ AdyenRecurring, AdyenPayment, AdyenThirdPartyPayout, - AdyenHPP) + AdyenHPP, + AdyenCheckoutApi) from .httpclient import HTTPClient @@ -30,6 +31,7 @@ def __init__(self, **kwargs): self.payout = AdyenThirdPartyPayout(client=self.client) self.hpp = AdyenHPP(client=self.client) self.recurring = AdyenRecurring(client=self.client) + self.checkout = AdyenCheckoutApi(client=self.client) _base_adyen_obj = Adyen() @@ -37,3 +39,4 @@ def __init__(self, **kwargs): hpp = _base_adyen_obj.hpp payment = _base_adyen_obj.payment payout = _base_adyen_obj.payout +checkout = _base_adyen_obj.checkout diff --git a/Adyen/client.py b/Adyen/client.py index 6b563e79..2146bc80 100644 --- a/Adyen/client.py +++ b/Adyen/client.py @@ -15,7 +15,7 @@ AdyenInvalidRequestError, AdyenAPIInvalidFormat, AdyenAPIInvalidAmount, -) + AdyenEndpointInvalidFormat) from . import settings @@ -26,7 +26,7 @@ class AdyenResult(object): status_code (int, optional): Default 200. HTTP response code, ie 200, 404, 500, etc. psp (str, optional): Psp reference returned by Adyen for a payment. - raw_request (str, optionl): Raw request placed to Adyen. + raw_request (str, optional): Raw request placed to Adyen. raw_response (str, optional): Raw response returned by Adyen. """ @@ -66,15 +66,16 @@ class AdyenClient(object): hmac (str, optional): Hmac key that is used for signature calculation. """ - def __init__(self, username=None, password=None, + def __init__(self, username=None, password=None, xapikey=None, review_payout_username=None, review_payout_password=None, store_payout_username=None, store_payout_password=None, platform="test", merchant_account=None, merchant_specific_url=None, skin_code=None, hmac=None, app_name=None, - http_force=None): + http_force=None, live_endpoint_prefix=None): self.username = username self.password = password + self.xapikey = xapikey self.review_payout_username = review_payout_username self.review_payout_password = review_payout_password self.store_payout_username = store_payout_username @@ -86,10 +87,11 @@ def __init__(self, username=None, password=None, self.skin_code = skin_code self.psp_list = [] self.app_name = app_name - self.LIB_VERSION = "1.3.0" + self.LIB_VERSION = "1.4.0" self.USER_AGENT_SUFFIX = "adyen-python-api-library/" self.http_init = False self.http_force = http_force + self.live_endpoint_prefix = live_endpoint_prefix def _determine_api_url(self, platform, service, action): """This returns the Adyen API endpoint based on the provided platform, @@ -103,8 +105,10 @@ def _determine_api_url(self, platform, service, action): base_uri = settings.BASE_PAL_URL.format(platform) if service == "Recurring": api_version = settings.API_RECURRING_VERSION + elif service == "Payout": + api_version = settings.API_PAYOUT_VERSION else: - api_version = settings.API_VERSION + api_version = settings.API_PAYMENT_VERSION return '/'.join([base_uri, service, api_version, action]) def _determine_hpp_url(self, platform, action): @@ -121,6 +125,34 @@ def _determine_hpp_url(self, platform, action): result = '/'.join([base_uri, service]) return result + def _determine_checkout_url(self, platform, action): + """This returns the Adyen API endpoint based on the provided platform, + service and action. + + Args: + platform (str): Adyen platform, ie 'live' or 'test'. + action (str): the API action to perform. + """ + api_version = settings.API_CHECKOUT_VERSION + if platform == "test": + base_uri = settings.ENDPOINT_CHECKOUT_TEST + elif self.live_endpoint_prefix is not None and platform == "live": + base_uri = settings.ENDPOINT_CHECKOUT_LIVE_SUFFIX.format( + self.live_endpoint_prefix) + elif self.live_endpoint_prefix is None and platform == "live": + errorstring = """Please set your live suffix. You can set it + by running 'settings. + ENDPOINT_CHECKOUT_LIVE_SUFFIX = 'Your live suffix'""" + raise AdyenEndpointInvalidFormat(errorstring) + if action == "paymentsDetails": + action = "payments/details" + if action == "paymentsResult": + action = "payments/result" + if action == "originKeys": + api_version = settings.API_CHECKOUT_UTILITY_VERSION + + return '/'.join([base_uri, api_version, action]) + def _review_payout_username(self, **kwargs): if 'username' in kwargs: return kwargs['username'] @@ -190,35 +222,47 @@ def call_api(self, request_data, service, action, idempotency=False, # username at self object has highest priority. fallback to root module # and ensure that it is set. + if self.xapikey: + xapikey = self.xapikey + elif 'xapikey' in kwargs: + xapikey = kwargs.pop("xapikey") + if self.username: username = self.username elif 'username' in kwargs: username = kwargs.pop("username") elif service == "Payout": - if any(substring in action for substring in ["store", "submit"]): + if any(substring in action for substring in + ["store", "submit"]): username = self._store_payout_username(**kwargs) else: username = self._review_payout_username(**kwargs) if not username: errorstring = """Please set your webservice username. - You can do this by running 'Adyen.username = 'Your username'""" + You can do this by running + 'Adyen.username = 'Your username'""" raise AdyenInvalidRequestError(errorstring) - - # password at self object has highest priority. fallback to root module - # and ensure that it is set. + # password at self object has highest priority. + # fallback to root module + # and ensure that it is set. if self.password: password = self.password elif 'password' in kwargs: password = kwargs.pop("password") elif service == "Payout": - if any(substring in action for substring in ["store", "submit"]): + if any(substring in action for substring in + ["store", "submit"]): password = self._store_payout_pass(**kwargs) else: password = self._review_payout_pass(**kwargs) if not password: errorstring = """Please set your webservice password. - You can do this by running 'Adyen.password = 'Your password'""" + You can do this by running + 'Adyen.password = 'Your password'""" raise AdyenInvalidRequestError(errorstring) + # xapikey at self object has highest priority. + # fallback to root module + # and ensure that it is set. # platform at self object has highest priority. fallback to root module # and ensure that it is set to either 'live' or 'test'. @@ -331,6 +375,73 @@ class instance. status_code, headers, message) return adyen_result + def call_checkout_api(self, request_data, action, **kwargs): + """This will call the checkout adyen api. xapi key merchant_account, + and platform are pulled from root module level and or self object. + AdyenResult will be returned on 200 response. Otherwise, an exception + is raised. + + Args: + request_data (dict): The dictionary of the request to place. This + should be in the structure of the Adyen API. + https://docs.adyen.com/developers/checkout/api-integration + service (str): This is the API service to be called. + action (str): The specific action of the API service to be called + """ + if not self.http_init: + self.http_client = HTTPClient(self.app_name, + self.USER_AGENT_SUFFIX, + self.LIB_VERSION, + self.http_force) + self.http_init = True + + # xapi at self object has highest priority. fallback to root module + # and ensure that it is set. + if self.xapikey: + xapikey = self.xapikey + elif 'xapikey' in kwargs: + xapikey = kwargs.pop("xapikey") + + if not xapikey: + errorstring = """Please set your webservice xapikey. + You can do this by running 'Adyen.xapikey = 'Your xapikey'""" + raise AdyenInvalidRequestError(errorstring) + + # platform at self object has highest priority. fallback to root module + # and ensure that it is set to either 'live' or 'test'. + if self.platform: + platform = self.platform + elif 'platform' in kwargs: + platform = kwargs.pop('platform') + + if not isinstance(platform, str): + errorstring = "'platform' value must be type of string" + raise TypeError(errorstring) + elif platform.lower() not in ['live', 'test']: + errorstring = "'platform' must be the value of 'live' or 'test'" + raise ValueError(errorstring) + + if not request_data.get('merchantAccount'): + request_data['merchantAccount'] = self.merchant_account + + # Adyen requires this header to be set and uses the combination of + # merchant account and merchant reference to determine uniqueness. + headers = {} + + url = self._determine_checkout_url(platform, action) + + raw_response, raw_request, status_code, headers = \ + self.http_client.request(url, json=request_data, + xapikey=xapikey, headers=headers, + **kwargs) + + # Creates AdyenResponse if request was successful, raises error if not. + adyen_result = self._handle_response(url, raw_response, raw_request, + status_code, headers, + request_data) + + return adyen_result + def hpp_payment(self, request_data, action, hmac_key="", **kwargs): if not self.http_init: @@ -386,7 +497,6 @@ def _handle_response(self, url, raw_response, raw_request, Returns: AdyenResult: Result object if successful. """ - if status_code != 200: response = {} # If the result can't be parsed into json, most likely is raw html. @@ -401,7 +511,21 @@ def _handle_response(self, url, raw_response, raw_request, try: if response['errorCode']: - return raw_response + raise AdyenAPICommunicationError( + "Unexpected error while communicating with Adyen." + " Received the response data:'{}', HTTP Code:'{}'. " + "Please reach out to support@adyen.com if the " + "problem persists with the psp:{}".format( + raw_response, + status_code, + headers.get('pspReference')), + status_code=status_code, + raw_request=raw_request, + raw_response=raw_response, + url=url, + psp=headers.get('pspReference'), + headers=headers, + error_code=response['errorCode']) except KeyError: erstr = 'KeyError: errorCode' raise AdyenAPICommunicationError(erstr) @@ -454,7 +578,9 @@ def _handle_http_error(self, url, response_obj, status_code, psp_ref, erstr = "Received a 404 for url:'{}'. Please ensure that" \ " the custom merchant specific url is correct" \ .format(url) - raise AdyenAPICommunicationError(erstr) + raise AdyenAPICommunicationError(erstr, + error_code=response_obj.get( + "errorCode")) else: erstr = "Unexpected error while communicating with Adyen." \ " Please reach out to support@adyen.com" \ @@ -464,8 +590,10 @@ def _handle_http_error(self, url, response_obj, status_code, psp_ref, raw_response=raw_response, url=url, psp=psp_ref, - headers=headers) - elif status_code in [400, 422]: + headers=headers, + error_code=response_obj.get( + "errorCode")) + elif status_code == 400: erstr = "Received validation error with errorCode: %s," \ " message: %s, HTTP Code: %s. Please verify" \ " the values provided. Please reach out" \ @@ -474,13 +602,16 @@ def _handle_http_error(self, url, response_obj, status_code, psp_ref, response_obj["errorCode"], response_obj["message"], status_code, psp_ref) - raise AdyenAPIValidationError(erstr) + raise AdyenAPIValidationError(erstr, error_code=response_obj.get( + "errorCode")) elif status_code == 401: erstr = "Unable to authenticate with Adyen's Servers." \ " Please verify the credentials set with the Adyen base" \ " class. Please reach out to your Adyen Admin" \ " if the problem persists" - raise AdyenAPIAuthenticationError(erstr) + raise AdyenAPIAuthenticationError(erstr, + error_code=response_obj.get( + "errorCode")) elif status_code == 403: if response_obj.get("message") == "Invalid Merchant Account": @@ -490,7 +621,9 @@ def _handle_http_error(self, url, response_obj, status_code, psp_ref, "Reach out to support@adyen.com" " if the issue persists") \ % raw_request['merchantAccount'] - raise AdyenAPIInvalidPermission(erstr) + raise AdyenAPIInvalidPermission(erstr, + error_code=response_obj.get( + "errorCode")) erstr = "Unable to perform the requested action. message: %s." \ " If you think your webservice user: %s might not have" \ @@ -498,18 +631,15 @@ def _handle_http_error(self, url, response_obj, status_code, psp_ref, " Please reach out to support@adyen.com, providing" \ " the PSP reference: %s" % ( response_obj["message"], self.username, psp_ref) - - raise AdyenAPIInvalidPermission(erstr, self.username, psp_ref, - raw_request=raw_request, - raw_response=raw_response, url=url, - psp=psp_ref, headers=headers) + raise AdyenAPIInvalidPermission(erstr, error_code=response_obj.get( + "errorCode")) elif status_code == 422: if response_obj.get("message") == "Invalid amount specified": raise AdyenAPIInvalidAmount( "Invalid amount specified" "Amount may be improperly formatted, too small or too big." - "If the issue persists, contact support@adyen.com" - ) + "If the issue persists, contact support@adyen.com", + error_code=response_obj.get("errorCode")) elif status_code == 500: if response_obj.get("errorType") == "validation": @@ -519,14 +649,17 @@ def _handle_http_error(self, url, response_obj, status_code, psp_ref, erstr = "Received validation error with errorCode: %s," \ " message: %s, HTTP Code: %s. Please verify" \ " the values provided." % err_args - raise AdyenAPIValidationError(erstr) + raise AdyenAPIValidationError(erstr, + error_code=response_obj.get( + "errorCode")) if response_obj.get("message") == "Failed to serialize node " \ "Failed to parse [123.34]" \ " as a Long": raise AdyenAPIInvalidFormat( - "The paymount amount must be set in cents," - " and can not contain commas or points." + "The payment amount must be set in cents," + " and can not contain commas or points.", + error_code=response_obj.get("errorCode") ) else: raise AdyenAPICommunicationError( @@ -539,7 +672,7 @@ def _handle_http_error(self, url, response_obj, status_code, psp_ref, raw_response=raw_response, url=url, psp=psp_ref, - headers=headers) + headers=headers, error_code=response_obj.get("errorCode")) def _error_from_hpp(self, html): # Must be updated when Adyen response is changed: diff --git a/Adyen/exceptions.py b/Adyen/exceptions.py index 1b99efe7..331fd764 100644 --- a/Adyen/exceptions.py +++ b/Adyen/exceptions.py @@ -9,7 +9,8 @@ def __init__(self, url="", psp="", headers="", - status_code=""): + status_code="", + error_code=""): self.message = message self.raw_request = raw_request self.raw_response = raw_response @@ -17,22 +18,17 @@ def __init__(self, self.psp = psp self.headers = headers self.status_code = status_code + self.error_code = error_code def __str__(self): return repr("{}:{}".format(self.__class__.__name__, self.message)) def debug(self): return ("class: {}\nmessage: {}\nHTTP status_code:{}\nurl: {}" - "request: {}\nresponse: {}\nheaders: {}".format( - self.__class__.__name__, - self.message, - self.status_code, - self.url, - self.raw_request, - self.raw_response, - self.headers - ) - ) + "request: {}\nresponse: {}\nheaders: {}" + .format(self.__class__.__name__, self.message, + self.status_code, self.url, self.raw_request, + self.raw_response, self.headers)) class AdyenInvalidRequestError(AdyenError): @@ -42,13 +38,9 @@ class AdyenInvalidRequestError(AdyenError): class AdyenAPIResponseError(AdyenError): def __init__(self, message, - result="", - error_code="", *args, **kwargs): super(AdyenAPIResponseError, self).__init__(message, *args, **kwargs) - self.error_code = error_code - self.result = result class AdyenAPIAuthenticationError(AdyenAPIResponseError): @@ -73,3 +65,7 @@ class AdyenAPIInvalidAmount(AdyenAPIResponseError): class AdyenAPIInvalidFormat(AdyenAPIResponseError): pass + + +class AdyenEndpointInvalidFormat(AdyenError): + pass diff --git a/Adyen/httpclient.py b/Adyen/httpclient.py index ceaa39c5..2379e9c4 100644 --- a/Adyen/httpclient.py +++ b/Adyen/httpclient.py @@ -142,6 +142,7 @@ def _requests_post(self, url, data=None, username="", password="", + xapikey="", headers=None, timeout=30): """This function will POST to the url endpoint using requests. @@ -175,6 +176,8 @@ def _requests_post(self, url, auth = None if username and password: auth = requests.auth.HTTPBasicAuth(username, password) + elif xapikey: + headers['x-api-key'] = xapikey # Add User-Agent header to request so that the request # can be identified as coming from the Adyen Python library. @@ -246,12 +249,12 @@ def _urllib_post(self, url, if username and password: if sys.version_info[0] >= 3: basic_authstring = base64.encodebytes(('%s:%s' % - (username, password)) - .encode()).decode().\ + (username, password)) + .encode()).decode(). \ replace('\n', '') else: basic_authstring = base64.encodestring('%s:%s' % (username, - password)).\ + password)). \ replace('\n', '') url_request.add_header("Authorization", "Basic %s" % basic_authstring) diff --git a/Adyen/services.py b/Adyen/services.py index ac8e79c3..077f1eb4 100644 --- a/Adyen/services.py +++ b/Adyen/services.py @@ -117,7 +117,7 @@ def hpp_payment(self, request="", skip_details=None, **kwargs): if all(k in request for k in ("shopperEmail", "shopperReference", "recurringContract")): recc = request['recurringContract'] - if recc != 'ONECLICK' and recc != 'RECURRING'\ + if recc != 'ONECLICK' and recc != 'RECURRING' \ and recc != 'ONECLICK,RECURRING': raise ValueError( "HPP: recurringContract must be on of the following" @@ -190,8 +190,8 @@ def capture(self, request="", **kwargs): action = "capture" if validation.check_in(request, action): - if request['modificationAmount']["value"] == "" or\ - request['modificationAmount']['value'] == "0": + if request['modificationAmount']["value"] == "" or \ + request['modificationAmount']['value'] == "0": raise ValueError( "Set the 'modificationAmount' to the original transaction" " amount, or less for a partial capture. " @@ -210,8 +210,8 @@ def refund(self, request="", **kwargs): action = "refund" if validation.check_in(request, action): - if request['modificationAmount']['value'] == "" or\ - request['modificationAmount']['value'] == "0": + if request['modificationAmount']['value'] == "" or \ + request['modificationAmount']['value'] == "0": raise ValueError( "To refund this payment, provide the original value. " "Set the value to less than the original amount, " @@ -279,3 +279,62 @@ def store_detail_and_submit(self, request=None, **kwargs): return self.client.call_api( request, self.service, action, **kwargs ) + + +class AdyenCheckoutApi(AdyenServiceBase): + """This represents the Adyen Checkout API . + + API calls currently implemented: + paymentMethods + payments + payments/details + originKeys + Please refer to the checkout documentation for specifics around the API. + https://docs.adyen.com/developers/checkout + + The AdyenPayment class, is accessible as adyen.payment.method(args) + + Args: + client (AdyenAPIClient, optional): An API client for the service to + use. If not provided, a new API client will be created. + """ + + def __init__(self, client=""): + super(AdyenCheckoutApi, self).__init__(client=client) + self.service = "Checkout" + + def payment_methods(self, request="", **kwargs): + action = "paymentMethods" + if validation.check_in(request, action): + if 'merchantAccount' in request: + if request['merchantAccount'] == '': + raise ValueError( + 'merchantAccount must contain the merchant account' + ' when retrieving payment methods.') + + return self.client.call_checkout_api(request, action, **kwargs) + + def payments(self, request="", **kwargs): + action = "payments" + if validation.check_in(request, action): + return self.client.call_checkout_api(request, action, **kwargs) + + def payments_details(self, request="", **kwargs): + action = "paymentsDetails" + if validation.check_in(request, action): + return self.client.call_checkout_api(request, action, **kwargs) + + def payment_session(self, request="", **kwargs): + action = "paymentSession" + if validation.check_in(request, action): + return self.client.call_checkout_api(request, action, **kwargs) + + def payment_result(self, request="", **kwargs): + action = "paymentsResult" + if validation.check_in(request, action): + return self.client.call_checkout_api(request, action, **kwargs) + + def origin_keys(self, request="", **kwargs): + action = "originKeys" + if validation.check_in(request, action): + return self.client.call_checkout_api(request, action, **kwargs) diff --git a/Adyen/settings.py b/Adyen/settings.py index 2364ed4b..d11759ee 100644 --- a/Adyen/settings.py +++ b/Adyen/settings.py @@ -1,6 +1,11 @@ -#!/bin/python - +# Those constants are used from the library only BASE_PAL_URL = "https://pal-{}.adyen.com/pal/servlet" BASE_HPP_URL = "https://{}.adyen.com/hpp" -API_VERSION = "v30" +ENDPOINT_CHECKOUT_TEST = "https://checkout-test.adyen.com" +ENDPOINT_CHECKOUT_LIVE_SUFFIX = "https://{}-checkout-live" \ + ".adyenpayments.com/checkout" +API_CHECKOUT_VERSION = "v40" +API_CHECKOUT_UTILITY_VERSION = "v1" API_RECURRING_VERSION = "v25" +API_PAYMENT_VERSION = "v40" +API_PAYOUT_VERSION = "v30" diff --git a/Adyen/validation.py b/Adyen/validation.py index 58e8c768..171fef9c 100644 --- a/Adyen/validation.py +++ b/Adyen/validation.py @@ -17,6 +17,17 @@ actions['refund'] = ["modificationAmount", "originalReference"] actions['cancelOrRefund'] = ["originalReference"] +actions['paymentMethods'] = ["merchantAccount"] +actions['payments'] = ["amount", "reference", "paymentMethod", + "merchantAccount", "returnUrl"] +actions['paymentsDetails'] = ["paymentData", "details"] +actions['paymentSession'] = ["amount", "reference", "shopperReference", + "channel", "returnUrl", "countryCode", + "shopperLocale", "sessionValidity", + "merchantAccount"] +actions['paymentsResult'] = ["payload"] +actions['originKeys'] = ["originDomains"] + payout_required_fields = { 'confirmThirdParty': ( 'merchantAccount', diff --git a/setup.py b/setup.py index bfb3082e..53adcc86 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='Adyen', packages=['Adyen'], - version='1.3.0', + version='1.4.0', maintainer='Adyen', maintainer_email='support@adyen.com', description='Adyen Python Api', diff --git a/test/CheckoutTest.py b/test/CheckoutTest.py new file mode 100644 index 00000000..f93ac940 --- /dev/null +++ b/test/CheckoutTest.py @@ -0,0 +1,196 @@ +import Adyen +import unittest +from BaseTest import BaseTest + + +class TestCheckout(unittest.TestCase): + adyen = Adyen.Adyen() + + client = adyen.client + test = BaseTest(adyen) + client.xapikey = "YourXapikey" + client.platform = "test" + client.app_name = "appname" + + def test_payment_methods_success_mocked(self): + request = {'merchantAccount': "YourMerchantAccount"} + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentmethods" + "-success.json") + result = self.adyen.checkout.payment_methods(request) + self.assertEqual("AliPay", result.message['paymentMethods'][0]['name']) + self.assertEqual("Credit Card", + result.message['paymentMethods'][2]['name']) + self.assertEqual("Credit Card via AsiaPay", + result.message['paymentMethods'][3]['name']) + + def test_payment_methods_error_mocked(self): + request = {'merchantAccount': "YourMerchantAccount"} + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentmethods-" + "error-forbidden" + "-403.json") + result = self.adyen.checkout.payment_methods(request) + self.assertEqual(403, result.message['status']) + self.assertEqual("901", result.message['errorCode']) + self.assertEqual("Invalid Merchant Account", result.message['message']) + self.assertEqual("security", result.message['errorType']) + + def test_payments_success_mocked(self): + request = {'amount': {"value": "100000", "currency": "EUR"}, + 'reference': "123456", 'paymentMethod': { + "type": "scheme", + "number": "4111111111111111", + "expiryMonth": "08", + "expiryYear": "2018", + "holderName": "John Smith", + "cvc": "737" + }, 'merchantAccount': "YourMerchantAccount", + 'returnUrl': "https://your-company.com/..."} + + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "payments" + "-success" + ".json") + result = self.adyen.checkout.payments(request) + self.assertEqual("8535296650153317", result.message['pspReference']) + self.assertEqual("Authorised", result.message['resultCode']) + self.assertEqual("8/2018", + result.message["additionalData"]['expiryDate']) + self.assertEqual("GREEN", + result.message["additionalData"]['fraudResultType']) + + def test_payments_error_mocked(self): + request = {'amount': {"value": "100000", "currency": "EUR"}, + 'reference': "54431", 'paymentMethod': { + "type": "scheme", + "number": "4111111111111111", + "expiryMonth": "08", + "expiryYear": "2018", + "holderName": "John Smith", + "cvc": "737" + }, 'merchantAccount': "YourMerchantAccount", + 'returnUrl': "https://your-company.com/..."} + + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "payments-error" + "-invalid" + "-data-422" + ".json") + result = self.adyen.checkout.payments(request) + self.assertEqual(422, result.message['status']) + self.assertEqual("130", result.message['errorCode']) + self.assertEqual("Reference Missing", result.message['message']) + self.assertEqual("validation", result.message['errorType']) + + def test_payments_details_success_mocked(self): + request = {'paymentData': "Hee57361f99....", 'details': { + "MD": "sdfsdfsdf...", + "PaRes": "sdkfhskdjfsdf..." + }} + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentsdetails" + "-success.json") + result = self.adyen.checkout.payments_details(request) + self.assertEqual("8515232733321252", result.message['pspReference']) + self.assertEqual("Authorised", result.message['resultCode']) + self.assertEqual("true", + result.message['additionalData']['liabilityShift']) + self.assertEqual("AUTHORISED", + result.message['additionalData']['refusalReasonRaw']) + + def test_payments_details_error_mocked(self): + request = {'paymentData': "Hee57361f99....", 'details': { + "MD": "sdfsdfsdf...", + "PaRes": "sdkfhskdjfsdf..." + }} + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentsdetails" + "-error-invalid-" + "data-422.json") + result = self.adyen.checkout.payments_details(request) + self.assertEqual(422, result.message['status']) + self.assertEqual("101", result.message['errorCode']) + self.assertEqual("Invalid card number", result.message['message']) + self.assertEqual("validation", result.message['errorType']) + + def test_payments_session_success_mocked(self): + request = {"reference": "Your order number", + "shopperReference": "yourShopperId_IOfW3k9G2PvXFu2j", + "channel": "iOS", + "token": "TOKEN_YOU_GET_FROM_CHECKOUT_SDK", + "returnUrl": "app://", "countryCode": "NL", + "shopperLocale": "nl_NL", + "sessionValidity": "2017-04-06T13:09:13Z", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + 'amount': {"value": "17408", "currency": "EUR"}} + + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentsession" + "-success.json") + result = self.adyen.checkout.payment_session(request) + self.assertIsNotNone(result.message['paymentSession']) + + def test_payments_session_error_mocked(self): + request = {"reference": "Your wro order number", + "shopperReference": "yourShopperId_IOfW3k9G2PvXFu2j", + "channel": "iOS", + "token": "WRONG_TOKEN", + "returnUrl": "app://", "countryCode": "NL", + "shopperLocale": "nl_NL", + "sessionValidity": "2017-04-06T13:09:13Z", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + 'amount': {"value": "17408", "currency": "EUR"}} + + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentsession" + "-error-invalid-" + "data-422.json") + result = self.adyen.checkout.payment_session(request) + self.assertEqual(422, result.message['status']) + self.assertEqual("14_012", result.message['errorCode']) + self.assertEqual("The provided SDK token could not be parsed.", + result.message['message']) + self.assertEqual("validation", result.message['errorType']) + + def test_payments_result_success_mocked(self): + request = {"payload": "VALUE_YOU_GET_FROM_CHECKOUT_SDK"} + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentsresult" + "-success.json") + result = self.adyen.checkout.payment_result(request) + self.assertEqual("8535253563623704", result.message['pspReference']) + self.assertEqual("Authorised", result.message['resultCode']) + + def test_payments_result_error_mocked(self): + request = {"payload": "VALUE_YOU_GET_FROM_CHECKOUT_SDK"} + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentsresult" + "-error-invalid-" + "data-payload-" + "422.json") + result = self.adyen.checkout.payment_result(request) + self.assertEqual(422, result.message['status']) + self.assertEqual("14_018", result.message['errorCode']) + self.assertEqual("Invalid payload provided", result.message['message']) + self.assertEqual("validation", result.message['errorType']) diff --git a/test/CheckoutUtilityTest.py b/test/CheckoutUtilityTest.py new file mode 100644 index 00000000..121dc1bb --- /dev/null +++ b/test/CheckoutUtilityTest.py @@ -0,0 +1,54 @@ +import Adyen +import unittest +from BaseTest import BaseTest + +from Adyen import settings + + +class TestCheckoutUtility(unittest.TestCase): + ady = Adyen.Adyen() + + client = ady.client + test = BaseTest(ady) + client.xapikey = "YourXapikey" + client.platform = "test" + client.app_name = "appname" + + def test_origin_keys_success_mocked(self): + request = { + "originDomains": { + "https://www.your-domain1.com", + "https://www.your-domain2.com", + "https://www.your-domain3.com" + } + } + + self.ady.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkoututility/" + "originkeys" + "-success.json") + result = self.ady.checkout.origin_keys(request) + + self.assertEqual("pub.v2.7814286629520534.aHR0cHM6Ly93d3cu" + "eW91ci1kb21haW4xLmNvbQ.UEwIBmW9-c_uXo5wS" + "Er2w8Hz8hVIpujXPHjpcEse3xI", + result.message['originKeys'] + ['https://www.your-domain1.com']) + + self.assertEqual("pub.v2.7814286629520534.aHR0cHM6Ly93d3cu" + "eW91ci1kb21haW4zLmNvbQ.fUvflu-YIdZSsLEH8" + "Qqmr7ksE4ag_NYiiMXK0s6aq_4", + result.message['originKeys'] + ['https://www.your-domain3.com']) + + self.assertEqual("pub.v2.7814286629520534.aHR0cHM6Ly93d3cue" + "W91ci1kb21haW4yLmNvbQ.EP6eXBJKk0t7-QIUl6e_" + "b1qMuMHGepxG_SlUqxAYrfY", + result.message['originKeys'] + ['https://www.your-domain2.com']) + + def test_checkout_utility_api_url_custom(self): + url = self.ady.client._determine_checkout_url("test", "originKeys") + + self.assertEqual(url, "https://checkout-test.adyen.com/v1/originKeys") diff --git a/test/DetermineEndpointTest.py b/test/DetermineEndpointTest.py new file mode 100644 index 00000000..9652b7b5 --- /dev/null +++ b/test/DetermineEndpointTest.py @@ -0,0 +1,48 @@ +import Adyen +import unittest +from BaseTest import BaseTest +from Adyen.exceptions import AdyenEndpointInvalidFormat + + +class TestDetermineUrl(unittest.TestCase): + adyen = Adyen.Adyen() + + client = adyen.client + test = BaseTest(adyen) + client.xapikey = "YourXapikey" + client.app_name = "appname" + + def test_checkout_api_url_custom(self): + self.client.live_endpoint_prefix = "1797a841fbb37ca7-AdyenDemo" + url = self.adyen.client._determine_checkout_url("live", "payments") + self.client.live_endpoint_prefix = "1797a841fbb37ca7-AdyenDemo" + self.assertEqual(url, "https://1797a841fbb37ca7-AdyenDemo-checkout-" + "live.adyenpayments.com/checkout/v40/payments") + + def test_checkout_api_url(self): + self.client.live_endpoint_prefix = None + url = self.adyen.client._determine_checkout_url("test", + "paymentsDetails") + self.assertEqual(url, "https://checkout-test.adyen.com" + "/v40/payments/details") + + def test_payments_invalid_platform(self): + + request = {'amount': {"value": "100000", "currency": "EUR"}, + "reference": "Your order number", + 'paymentMethod': { + "type": "scheme", + "number": "4111111111111111", + "expiryMonth": "08", + "expiryYear": "2018", + "holderName": "John Smith", + "cvc": "737" + }, 'merchantAccount': "YourMerchantAccount", + 'returnUrl': "https://your-company.com/..."} + + self.client.platform = "live" + self.client.live_endpoint_prefix = None + try: + result = self.adyen.checkout.payments(request) + except AdyenEndpointInvalidFormat as error: + self.assertIsNotNone(error) diff --git a/test/ModificationTest.py b/test/ModificationTest.py index 6b56a88f..77eb0c33 100644 --- a/test/ModificationTest.py +++ b/test/ModificationTest.py @@ -36,14 +36,12 @@ def test_capture_error_167(self): 'test/mocks/' 'capture-error-167' '.json') - self.assertRaisesRegexp(Adyen.AdyenAPIValidationError, - "Received validation error with errorCode:" - " 167, message: Original pspReference required" - " for this operation, HTTP Code: 422." + - " Please verify the values provided. Please " - "reach out to support@adyen.com if the problem" - " persists, providing the PSP reference.*", - self.ady.payment.capture, request) + self.assertRaisesRegexp( + Adyen.AdyenAPICommunicationError, + "Unexpected error", + self.ady.payment.capture, + request + ) def test_cancel_or_refund_received(self): request = {} diff --git a/test/PaymentTest.py b/test/PaymentTest.py index d67ad045..fcfb800c 100644 --- a/test/PaymentTest.py +++ b/test/PaymentTest.py @@ -4,10 +4,10 @@ class TestPayments(unittest.TestCase): - ady = Adyen.Adyen() + adyen = Adyen.Adyen() - client = ady.client - test = BaseTest(ady) + client = adyen.client + test = BaseTest(adyen) client.username = "YourWSUser" client.password = "YourWSPassword" client.platform = "test" @@ -25,11 +25,12 @@ def test_authorise_success_mocked(self): "cvc": "737", "holderName": "John Doe" } - self.ady.client = self.test.create_client_from_file(200, request, - 'test/mocks/' - 'authorise-success' - '.json') - result = self.ady.payment.authorise(request) + self.adyen.client = self.test.create_client_from_file(200, request, + 'test/mocks/' + 'authorise' + '-success' + '.json') + result = self.adyen.payment.authorise(request) self.assertEqual("Authorised", result.message['resultCode']) self.assertEqual("8/2018", result.message['additionalData']['expiryDate']) @@ -64,13 +65,13 @@ def test_authorise_error010_mocked(self): "cvc": "737", "holderName": "John Doe" } - self.ady.client = self.test.create_client_from_file(403, request, - 'test/mocks/' - 'authorise-error' - '-010' - '.json') + self.adyen.client = self.test.create_client_from_file(403, request, + 'test/mocks/' + 'authorise-error' + '-010' + '.json') self.assertRaises(Adyen.AdyenAPIInvalidPermission, - self.ady.payment.authorise, request) + self.adyen.payment.authorise, request) def test_authorise_error_cvc_declined_mocked(self): request = {} @@ -83,12 +84,13 @@ def test_authorise_error_cvc_declined_mocked(self): "cvc": "787", "holderName": "John Doe" } - self.ady.client = self.test.create_client_from_file(200, request, - 'test/mocks/' - 'authorise-error-' - 'cvc-declined' - '.json') - result = self.ady.payment.authorise(request) + self.adyen.client = self.test.create_client_from_file(200, request, + 'test/mocks/' + 'authorise' + '-error-' + 'cvc-declined' + '.json') + result = self.adyen.payment.authorise(request) self.assertEqual("Refused", result.message['resultCode']) def test_authorise_success_3d_mocked(self): @@ -107,11 +109,12 @@ def test_authorise_success_3d_mocked(self): "userAgent": "YourUserAgent", "acceptHeader": "YourAcceptHeader" } - self.ady.client = self.test.create_client_from_file(200, request, - 'test/mocks/' - 'authorise-success' - '-3d.json') - result = self.ady.payment.authorise(request) + self.adyen.client = self.test.create_client_from_file(200, request, + 'test/mocks/' + 'authorise' + '-success' + '-3d.json') + result = self.adyen.payment.authorise(request) self.assertEqual("RedirectShopper", result.message['resultCode']) self.assertIsNotNone(result.message['md']) self.assertIsNotNone(result.message['issuerUrl']) @@ -126,11 +129,11 @@ def test_authorise_3d_success_mocked(self): "userAgent": "YourUserAgent", "acceptHeader": "YourAcceptHeader" } - self.ady.client = self.test.create_client_from_file(200, request, - 'test/mocks/' - 'authorise3d-' - 'success.json') - result = self.ady.payment.authorise3d(request) + self.adyen.client = self.test.create_client_from_file(200, request, + 'test/mocks/' + 'authorise3d-' + 'success.json') + result = self.adyen.payment.authorise3d(request) self.assertEqual("Authorised", result.message['resultCode']) self.assertIsNotNone(result.message['pspReference']) @@ -142,11 +145,12 @@ def test_authorise_cse_success_mocked(self): request['additionalData'] = { "card.encrypted.json": "YourCSEToken" } - self.ady.client = self.test.create_client_from_file(200, request, - 'test/mocks/' - 'authorise-success' - '-cse.json') - result = self.ady.payment.authorise(request) + self.adyen.client = self.test.create_client_from_file(200, request, + 'test/mocks/' + 'authorise' + '-success' + '-cse.json') + result = self.adyen.payment.authorise(request) self.assertEqual("Authorised", result.message['resultCode']) def test_authorise_cse_error_expired_mocked(self): @@ -158,11 +162,12 @@ def test_authorise_cse_error_expired_mocked(self): "card.encrypted.json": "YourCSEToken" } - self.ady.client = self.test.create_client_from_file(200, request, - 'test/mocks/' - 'authorise-error-' - 'expired.json') - result = self.ady.payment.authorise(request) + self.adyen.client = self.test.create_client_from_file(200, request, + 'test/mocks/' + 'authorise' + '-error-' + 'expired.json') + result = self.adyen.payment.authorise(request) self.assertEqual("Refused", result.message['resultCode']) self.assertEqual("DECLINED Expiry Incorrect", result.message['additionalData']['refusalReasonRaw']) @@ -179,16 +184,17 @@ def test_error_401_mocked(self): "cvc": "787", "holderName": "John Doe" } - self.ady.client = self.test.create_client_from_file(401, request, - 'test/mocks/' - 'authorise-error-' - '010.json') + self.adyen.client = self.test.create_client_from_file(401, request, + 'test/mocks/' + 'authorise' + '-error-' + '010.json') self.assertRaisesRegexp(Adyen.AdyenAPIAuthenticationError, "Unable to authenticate with Adyen's Servers." " Please verify the credentials set with the" " Adyen base class. Please reach out to your" " Adyen Admin if the problem persists", - self.ady.payment.authorise, request) + self.adyen.payment.authorise, request) TestPayments.client.http_force = "requests" diff --git a/test/RecurringTest.py b/test/RecurringTest.py index 4bb1072e..7cdfbd44 100644 --- a/test/RecurringTest.py +++ b/test/RecurringTest.py @@ -60,11 +60,12 @@ def test_disable_803(self): 'recurring/' 'disable-error-803' '.json') - self.assertRaisesRegexp(Adyen.AdyenAPIValidationError, - "Received validation error with errorCode: " - "803, message: PaymentDetail not found, " - "HTTP Code: 422.*", - self.ady.recurring.disable, request) + self.assertRaisesRegexp( + Adyen.AdyenAPICommunicationError, + "Unexpected error", + self.ady.recurring.disable, + request + ) TestRecurring.client.http_force = "requests" diff --git a/test/ThirdPartyPayoutTest.py b/test/ThirdPartyPayoutTest.py index 0d63acbf..517d48a0 100644 --- a/test/ThirdPartyPayoutTest.py +++ b/test/ThirdPartyPayoutTest.py @@ -132,12 +132,8 @@ def test_submit_invalid_recurring_reference(self): resp = 'test/mocks/payout/submit-invalid-reference.json' self.ady.client = self.test.create_client_from_file(422, request, resp) self.assertRaisesRegexp( - Adyen.AdyenAPIValidationError, - "Received validation error with errorCode: 800," - " message: Contract not found, HTTP Code: 422." - " Please verify the values provided." - " Please reach out to support@adyen.com" - " if the problem persists, providing the PSP reference.*", + Adyen.AdyenAPICommunicationError, + "Unexpected error", self.ady.payout.submit, request ) @@ -183,12 +179,8 @@ def test_store_detail_and_submit_missing_payment(self): resp = 'test/mocks/payout/storeDetailAndSubmit-missing-payment.json' self.ady.client = self.test.create_client_from_file(422, request, resp) self.assertRaisesRegexp( - Adyen.AdyenAPIValidationError, - "Received validation error with errorCode: 000," - " message: Please supply paymentDetails, HTTP Code: 422." - " Please verify the values provided." - " Please reach out to support@adyen.com" - " if the problem persists, providing the PSP reference:.*", + Adyen.AdyenAPICommunicationError, + "Unexpected error", self.ady.payout.store_detail_and_submit, request ) @@ -215,12 +207,8 @@ def test_store_detail_and_submit_invalid_iban(self): resp = 'test/mocks/payout/storeDetailAndSubmit-invalid-iban.json' self.ady.client = self.test.create_client_from_file(422, request, resp) self.assertRaisesRegexp( - Adyen.AdyenAPIValidationError, - "Received validation error with errorCode: 161," - " message: Invalid iban, HTTP Code: 422." - " Please verify the values provided." - " Please reach out to support@adyen.com" - " if the problem persists, providing the PSP reference:.*", + Adyen.AdyenAPICommunicationError, + "Unexpected error", self.ady.payout.store_detail_and_submit, request ) diff --git a/test/mocks/checkout/paymentmethods-error-forbidden-403.json b/test/mocks/checkout/paymentmethods-error-forbidden-403.json new file mode 100644 index 00000000..28afc988 --- /dev/null +++ b/test/mocks/checkout/paymentmethods-error-forbidden-403.json @@ -0,0 +1,6 @@ +{ + "status": 403, + "errorCode": "901", + "message": "Invalid Merchant Account", + "errorType": "security" +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentmethods-success.json b/test/mocks/checkout/paymentmethods-success.json new file mode 100644 index 00000000..d79ce47b --- /dev/null +++ b/test/mocks/checkout/paymentmethods-success.json @@ -0,0 +1,985 @@ +{ + "paymentMethods": [ + { + "name": "AliPay", + "type": "alipay" + }, + { + "name": "AliPay", + "type": "alipay_wap" + }, + { + "details": [ + { + "key": "additionalData.card.encrypted.json", + "type": "cardToken" + } + ], + "name": "Credit Card", + "type": "scheme" + }, + { + "name": "Credit Card via AsiaPay", + "type": "asiapay" + }, + { + "name": "BancNet", + "type": "bancnet" + }, + { + "name": "Bank Transfer (BE)", + "type": "bankTransfer_BE" + }, + { + "name": "Bank Transfer (DE)", + "type": "bankTransfer_DE" + }, + { + "name": "Bank Transfer (DK)", + "type": "bankTransfer_DK" + }, + { + "name": "Bank Transfer (GB)", + "type": "bankTransfer_GB" + }, + { + "name": "SEPA Bank Transfer", + "type": "bankTransfer_IBAN" + }, + { + "name": "Bank Transfer (NL)", + "type": "bankTransfer_NL" + }, + { + "name": "Bank Transfer (NO)", + "type": "bankTransfer_NO" + }, + { + "name": "Bank Transfer (PL)", + "type": "bankTransfer_PL" + }, + { + "name": "Bank Transfer (SE)", + "type": "bankTransfer_SE" + }, + { + "name": "Russian Bank Transfer", + "type": "bank_ru" + }, + { + "details": [ + { + "key": "additionalData.card.encrypted.json", + "type": "cardToken" + } + ], + "name": "Bancontact card", + "type": "bcmc" + }, + { + "name": "Boleto Bancario via HSBC", + "type": "boletobancario_hsbc" + }, + { + "name": "Boleto Bancario via Itau", + "type": "boletobancario_itau" + }, + { + "name": "Boleto Bancario via Santander", + "type": "boletobancario_santander" + }, + { + "name": "c_cash", + "type": "c_cash" + }, + { + "name": "CashU", + "type": "cashu" + }, + { + "name": "Paiement en 3 fois par Cartes Bancaires", + "type": "cofinoga_3xcb" + }, + { + "name": "DineroMail", + "type": "dineromail" + }, + { + "name": "Online bank transfer.", + "type": "directEbanking" + }, + { + "name": "Direct Debit Brazil - Banco do Brazil", + "type": "directdebit_BR_bancodobrasil" + }, + { + "name": "Direct Debit Brazil - Bradesco", + "type": "directdebit_BR_bradesco" + }, + { + "name": "Direct Debit Brazil - Caixa Economica Federal", + "type": "directdebit_BR_caixa" + }, + { + "name": "Direct Debit Brazil - HSBC", + "type": "directdebit_BR_hsbc" + }, + { + "name": "Direct Debit Brazil - Itau", + "type": "directdebit_BR_itau" + }, + { + "name": "Direct Debit Brazil - Santander", + "type": "directdebit_BR_santander" + }, + { + "name": "Eenmalige machtiging", + "type": "directdebit_NL" + }, + { + "details": [ + { + "items": [ + { + "id": "11", + "name": "Bank transfer / postal" + }, + { + "id": "74", + "name": "Banki Spółdzielcze" + }, + { + "id": "73", + "name": "BLIK" + }, + { + "id": "32", + "name": "BNP Paribas" + }, + { + "id": "16", + "name": "Credit Agricole" + }, + { + "id": "83", + "name": "EnveloBank" + }, + { + "id": "55", + "name": "erata - dotpay installment" + }, + { + "id": "93", + "name": "eSKOK" + }, + { + "id": "56", + "name": "eurobank płatności online" + }, + { + "id": "76", + "name": "Getin Bank PBL" + }, + { + "id": "81", + "name": "Idea Cloud" + }, + { + "id": "7", + "name": "ING Corporate customers" + }, + { + "id": "35", + "name": "Kantor Polski" + }, + { + "id": "44", + "name": "Millennium - Płatności Internetowe" + }, + { + "id": "10", + "name": "Millennium Corporate customers" + }, + { + "id": "68", + "name": "mRaty" + }, + { + "id": "1", + "name": "mTransfer" + }, + { + "id": "80", + "name": "Noble Pay" + }, + { + "id": "50", + "name": "Pay Way Toyota Bank" + }, + { + "id": "45", + "name": "Pay with Alior Bank" + }, + { + "id": "65", + "name": "Paylink Idea Bank" + }, + { + "id": "36", + "name": "Pekao24Przelew" + }, + { + "id": "70", + "name": "Pocztowy24" + }, + { + "id": "6", + "name": "Przelew24" + }, + { + "id": "46", + "name": "Płacę z Citi Handlowy" + }, + { + "id": "38", + "name": "Płacę z ING" + }, + { + "id": "2", + "name": "Płacę z Inteligo" + }, + { + "id": "4", + "name": "Płacę z iPKO" + }, + { + "id": "72", + "name": "Płacę z Orange" + }, + { + "id": "66", + "name": "Płacę z PBS" + }, + { + "id": "75", + "name": "Płacę z Plus Bank" + }, + { + "id": "51", + "name": "Płać z BOŚ" + }, + { + "id": "48", + "name": "R-Przelew" + }, + { + "id": "88", + "name": "Raiffeisen" + }, + { + "id": "52", + "name": "SkyCash" + }, + { + "id": "58", + "name": "Szybkie Platnosci Internetowe z Deutsche Bank PBC" + }, + { + "id": "60", + "name": "T-Mobile usługi bankowe" + }, + { + "id": "21", + "name": "VIA - Moje Rachunki" + }, + { + "id": "84", + "name": "Volkswagen Bank direct" + }, + { + "id": "31", + "name": "Zaplac w Zabce i we Freshmarket" + }, + { + "id": "24", + "name": "mPay" + } + ], + "key": "issuer", + "type": "select" + } + ], + "name": "Local Polish Payment Methods", + "type": "dotpay" + }, + { + "name": "Finnish E-Banking", + "type": "ebanking_FI" + }, + { + "name": "Lastschrift (ELV)", + "type": "elv" + }, + { + "details": [ + { + "items": [ + { + "id": "550", + "name": "?eská spo?itelna" + }, + { + "id": "231", + "name": "POP Pankki" + }, + { + "id": "551", + "name": "Kb" + }, + { + "id": "232", + "name": "Aktia" + }, + { + "id": "552", + "name": "Raiffeisen" + }, + { + "id": "750", + "name": "Swedbank" + }, + { + "id": "211", + "name": "Nordea" + }, + { + "id": "233", + "name": "Säästöpankki" + }, + { + "id": "553", + "name": "Csob" + }, + { + "id": "751", + "name": "SEB" + }, + { + "id": "234", + "name": "S-Pankki" + }, + { + "id": "554", + "name": "Moneta" + }, + { + "id": "752", + "name": "Nordea" + }, + { + "id": "235", + "name": "OmaSP" + }, + { + "id": "213", + "name": "Op-Pohjola" + }, + { + "id": "555", + "name": "UniCredit" + }, + { + "id": "753", + "name": "LHV" + }, + { + "id": "556", + "name": "Fio" + }, + { + "id": "557", + "name": "mBank" + }, + { + "id": "216", + "name": "Handelsbanken" + }, + { + "id": "260", + "name": "Länsförsäkringar" + }, + { + "id": "240", + "name": "BankDeposit" + }, + { + "id": "265", + "name": "Sparbanken" + }, + { + "id": "640", + "name": "BankDeposit" + }, + { + "id": "200", + "name": "Ålandsbanken" + }, + { + "id": "720", + "name": "Swedbank" + }, + { + "id": "940", + "name": "Swedbank" + }, + { + "id": "204", + "name": "Danske Bank" + }, + { + "id": "721", + "name": "SEB" + }, + { + "id": "941", + "name": "SEB" + }, + { + "id": "722", + "name": "DNB" + }, + { + "id": "942", + "name": "Citadele" + }, + { + "id": "205", + "name": "Handelsbanken" + }, + { + "id": "723", + "name": "Šiaulių Bankas" + }, + { + "id": "943", + "name": "DNB" + }, + { + "id": "206", + "name": "Nordea" + }, + { + "id": "724", + "name": "Nordea" + }, + { + "id": "207", + "name": "SEB" + }, + { + "id": "208", + "name": "Skandiabanken" + }, + { + "id": "209", + "name": "Swedbank" + } + ], + "key": "issuer", + "type": "select" + } + ], + "name": "Bank Payment", + "type": "entercash" + }, + { + "name": "Nationale Entertainment Card", + "type": "entertainmentcard" + }, + { + "name": "Gall & Gall", + "type": "gallgall" + }, + { + "name": "Generic GiftCard", + "type": "genericgiftcard" + }, + { + "details": [ + { + "key": "bic", + "type": "text" + } + ], + "name": "GiroPay", + "type": "giropay" + }, + { + "name": "Globe GCash", + "type": "globegcash" + }, + { + "name": "Hunkemoller Lingerie Card", + "type": "hmlingerie" + }, + { + "details": [ + { + "items": [ + { + "id": "1121", + "name": "Test Issuer" + }, + { + "id": "1154", + "name": "Test Issuer 5" + }, + { + "id": "1153", + "name": "Test Issuer 4" + }, + { + "id": "1152", + "name": "Test Issuer 3" + }, + { + "id": "1151", + "name": "Test Issuer 2" + }, + { + "id": "1162", + "name": "Test Issuer Cancelled" + }, + { + "id": "1161", + "name": "Test Issuer Pending" + }, + { + "id": "1160", + "name": "Test Issuer Refused" + }, + { + "id": "1159", + "name": "Test Issuer 10" + }, + { + "id": "1158", + "name": "Test Issuer 9" + }, + { + "id": "1157", + "name": "Test Issuer 8" + }, + { + "id": "1156", + "name": "Test Issuer 7" + }, + { + "id": "1155", + "name": "Test Issuer 6" + } + ], + "key": "idealIssuer", + "type": "select" + } + ], + "name": "iDEAL", + "type": "ideal" + }, + { + "name": "Phone Payment", + "type": "ivr" + }, + { + "name": "Landline phone", + "type": "ivrLandline" + }, + { + "name": "Mobile phone", + "type": "ivrMobile" + }, + { + "details": [ + { + "details": [ + { + "key": "firstName", + "type": "text" + }, + { + "key": "infix", + "optional": "true", + "type": "text" + }, + { + "key": "lastName", + "type": "text" + }, + { + "items": [ + { + "id": "M", + "name": "MALE" + }, + { + "id": "F", + "name": "FEMALE" + } + ], + "key": "gender", + "type": "radio" + }, + { + "key": "dateOfBirth", + "type": "date" + }, + { + "key": "telephoneNumber", + "type": "tel" + }, + { + "key": "socialSecurityNumber", + "optional": "true", + "type": "text" + }, + { + "key": "shopperEmail", + "type": "emailAddress" + } + ], + "key": "personalDetails", + "type": "fieldSet" + }, + { + "details": [ + { + "key": "street", + "type": "text" + }, + { + "key": "houseNumberOrName", + "type": "text" + }, + { + "key": "city", + "type": "text" + }, + { + "key": "postalCode", + "type": "text" + }, + { + "key": "stateOrProvince", + "optional": "true", + "type": "text" + }, + { + "items": [ + { + "id": "SE", + "name": "SWEDEN" + }, + { + "id": "NO", + "name": "NORWAY" + }, + { + "id": "FI", + "name": "FINLAND" + }, + { + "id": "DK", + "name": "DENMARK" + }, + { + "id": "AT", + "name": "AUSTRIA" + }, + { + "id": "DE", + "name": "GERMANY" + }, + { + "id": "NL", + "name": "NETHERLANDS" + } + ], + "key": "country", + "type": "select" + } + ], + "key": "billingAddress", + "type": "address" + }, + { + "key": "separateDeliveryAddress", + "optional": "true", + "type": "boolean", + "value": "false" + }, + { + "details": [ + { + "key": "street", + "type": "text" + }, + { + "key": "houseNumberOrName", + "type": "text" + }, + { + "key": "city", + "type": "text" + }, + { + "key": "postalCode", + "type": "text" + }, + { + "key": "stateOrProvince", + "optional": "true", + "type": "text" + }, + { + "items": [ + { + "id": "SE", + "name": "SWEDEN" + }, + { + "id": "NO", + "name": "NORWAY" + }, + { + "id": "FI", + "name": "FINLAND" + }, + { + "id": "DK", + "name": "DENMARK" + }, + { + "id": "AT", + "name": "AUSTRIA" + }, + { + "id": "DE", + "name": "GERMANY" + }, + { + "id": "NL", + "name": "NETHERLANDS" + } + ], + "key": "country", + "type": "select" + } + ], + "key": "deliveryAddress", + "optional": "true", + "type": "address" + } + ], + "name": "Pay later with Klarna.", + "type": "klarna" + }, + { + "name": "Multibanco", + "type": "multibanco" + }, + { + "name": "Russian Online Payments", + "type": "online_RU" + }, + { + "name": "Invoice", + "type": "openinvoice" + }, + { + "name": "PayPal", + "type": "paypal" + }, + { + "name": "Paysafecard", + "type": "paysafecard" + }, + { + "name": "POLi", + "type": "poli" + }, + { + "details": [ + { + "items": [ + { + "id": "+7", + "name": "RU" + }, + { + "id": "+9955", + "name": "GE" + }, + { + "id": "+507", + "name": "PA" + }, + { + "id": "+44", + "name": "GB" + }, + { + "id": "+992", + "name": "TJ" + }, + { + "id": "+370", + "name": "LT" + }, + { + "id": "+972", + "name": "IL" + }, + { + "id": "+996", + "name": "KG" + }, + { + "id": "+380", + "name": "UA" + }, + { + "id": "+84", + "name": "VN" + }, + { + "id": "+90", + "name": "TR" + }, + { + "id": "+994", + "name": "AZ" + }, + { + "id": "+374", + "name": "AM" + }, + { + "id": "+371", + "name": "LV" + }, + { + "id": "+91", + "name": "IN" + }, + { + "id": "+66", + "name": "TH" + }, + { + "id": "+373", + "name": "MD" + }, + { + "id": "+1", + "name": "US" + }, + { + "id": "+81", + "name": "JP" + }, + { + "id": "+998", + "name": "UZ" + }, + { + "id": "+77", + "name": "KZ" + }, + { + "id": "+375", + "name": "BY" + }, + { + "id": "+372", + "name": "EE" + }, + { + "id": "+40", + "name": "RO" + }, + { + "id": "+82", + "name": "KR" + } + ], + "key": "qiwiwallet.telephoneNumberPrefix", + "type": "select" + }, + { + "key": "qiwiwallet.telephoneNumber", + "type": "text" + } + ], + "name": "Qiwi Wallet", + "type": "qiwiwallet" + }, + { + "name": "RatePay Invoice", + "type": "ratepay" + }, + { + "name": "SafetyPay", + "type": "safetypay" + }, + { + "details": [ + { + "key": "sepa.ownerName", + "type": "text" + }, + { + "key": "sepa.ibanNumber", + "type": "text" + } + ], + "name": "SEPA Direct Debit", + "type": "sepadirectdebit" + }, + { + "name": "Premium SMS", + "type": "sms" + }, + { + "name": "TenPay", + "type": "tenpay" + }, + { + "name": "Russian Cash Terminal Payments", + "type": "terminal_RU" + }, + { + "name": "Trustly Direct bank e-Payments", + "type": "trustly" + }, + { + "name": "Online Banking by Trustpay", + "type": "trustpay" + }, + { + "name": "UnionPay", + "type": "unionpay" + }, + { + "name": "Russian Wallet Payments", + "type": "wallet_RU" + }, + { + "name": "Webshop Giftcard", + "type": "webshopgiftcard" + }, + { + "name": "Your Gift", + "type": "yourgift" + } + ] +} \ No newline at end of file diff --git a/test/mocks/checkout/payments-error-invalid-data-422.json b/test/mocks/checkout/payments-error-invalid-data-422.json new file mode 100644 index 00000000..540ea247 --- /dev/null +++ b/test/mocks/checkout/payments-error-invalid-data-422.json @@ -0,0 +1,6 @@ +{ + "status": 422, + "errorCode": "130", + "message": "Reference Missing", + "errorType": "validation" +} \ No newline at end of file diff --git a/test/mocks/checkout/payments-success.json b/test/mocks/checkout/payments-success.json new file mode 100644 index 00000000..c4ed10fb --- /dev/null +++ b/test/mocks/checkout/payments-success.json @@ -0,0 +1,97 @@ +{ + "additionalData": { + "expiryDate": "8/2018", + "fraudResultType": "GREEN", + "cardBin": "411111", + "cardSummary": "1111", + "fraudManualReview": "false", + "aliasType": "Default", + "alias": "H167852639363479", + "cardPaymentMethod": "visa", + "cardIssuingCountry": "NL" + }, + "fraudResult": { + "accountScore": 0, + "results": [ + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 2, + "name": "CardChunkUsage" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 3, + "name": "PaymentDetailUsage" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 4, + "name": "HolderNameUsage" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 1, + "name": "PaymentDetailRefCheck" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 13, + "name": "IssuerRefCheck" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 15, + "name": "IssuingCountryReferral" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 27, + "name": "PmOwnerRefCheck" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 10, + "name": "HolderNameContainsNumber" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 11, + "name": "HolderNameIsOneWord" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 82, + "name": "CustomFieldCheck" + } + }, + { + "FraudCheckResult": { + "accountScore": 0, + "checkId": 25, + "name": "CVCAuthResultCheck" + } + } + ] + }, + "pspReference": "8535296650153317", + "resultCode": "Authorised" +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentsdetails-error-invalid-data-422.json b/test/mocks/checkout/paymentsdetails-error-invalid-data-422.json new file mode 100644 index 00000000..ce814f1e --- /dev/null +++ b/test/mocks/checkout/paymentsdetails-error-invalid-data-422.json @@ -0,0 +1,6 @@ +{ + "status": 422, + "errorCode": "101", + "message": "Invalid card number", + "errorType": "validation" +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentsdetails-success.json b/test/mocks/checkout/paymentsdetails-success.json new file mode 100644 index 00000000..e87446a4 --- /dev/null +++ b/test/mocks/checkout/paymentsdetails-success.json @@ -0,0 +1,8 @@ +{ + "pspReference": "8515232733321252", + "resultCode": "Authorised", + "additionalData": { + "liabilityShift": "true", + "refusalReasonRaw": "AUTHORISED" + } +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentsession-error-invalid-data-422.json b/test/mocks/checkout/paymentsession-error-invalid-data-422.json new file mode 100644 index 00000000..8e7c76da --- /dev/null +++ b/test/mocks/checkout/paymentsession-error-invalid-data-422.json @@ -0,0 +1,6 @@ +{ + "status": 422, + "errorCode": "14_012", + "message": "The provided SDK token could not be parsed.", + "errorType": "validation" +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentsession-success.json b/test/mocks/checkout/paymentsession-success.json new file mode 100644 index 00000000..42446ad4 --- /dev/null +++ b/test/mocks/checkout/paymentsession-success.json @@ -0,0 +1,3 @@ +{ + "paymentSession": "eyJjaGVja291dHNob3BwZXJCYXNlVXJsIjoiaHR0cHM6XC9cL2NoZWNrb3V0c2asdHBlci10ZXN0LmFkeWVuLmNvbVwvY2hlY2tvdXRzaG9wcGVyXC8iLCJkaXNhYmxlUmVjdXJyaW5nRGV0YWlsVXJsIjoiaHR0cHM6XC9cL2NoZWNrb3V0c2hvcHBlci10ZXN0LmFkeWVuLmNvbVwvY2hlY2tvdXRzaG9wcGVyXC9zZXJ2aWNlc1wvUGF5bWVudEluaXRpYXRpb25cL3YxXC9kaXNhYmxlUmVjdXJyaW5nRGV0YWlsIiwiZ2VuZXJhdGlvbnRpbWUiOiIyMDE4LTA2LTIyVDE0OjMxOjI1WiIsImluaXRpYXRpb25VcmwiOiJodHRwczpcL1wvY2hlY2tvdXRzaG9wcGVyLXRlc3QuYWR5ZW4uY29tXC9jaGVja291dHNob3BwZXJcL3NlcnZpY2VzXC9QYXltZW50SW5pdGlhdGlvblwvdjFcL2luaXRpYXRlIiwib3JpZ2luIjoiIiwicGF5bWVudCI6eyJhbW91bnQiOnsiY3VycmVuY3kiOiJFVVIiLCJ2YWx1ZSI6MTAwMH0sImNvdW50cnlDb2RlIjoiTkwiLCJyZWZlcmVuY2UiOiJZb3VyIG9yZGVyIG51bWJlciIsInJldHVyblVybCI6Imh0dHBzOlwvXC95b3VyLWNvbXBhbnkuY29tXC8iLCJzZXNzaW9uVmFsaWRpdHkiOiIyMDE4LTA2LTIyVDE1OjMxOjI1WiJ9LCJwYXltZW50RGF0YSI6IkFiMDJiNGMwIUJRQUJBZ0FJemlidUZ2Z3hsMGVTOTFQZWtjbDB3VHJhRG1xTU1BWFdZUGdxOXI1NXJjKzJ5N2l5bWV2ZWswR0VWdU9sZDhPSkwyeTBzWnl3UVlYWFhHUDkwa0pZU21SVzIzS1dHYXp3VEhyMDV0cGpGMGZOMHJrZ2lPNTFBdkxrVnhQSWI4RE1iQUtmeVlyRVErdjlpZWIyUjI0emdiTXhYWGJJMWtjZGJWeEdKbENMRWVaXC9kNDUzclVGZ0NnRktWczNXUk1JRHVTTlBoQ1hCclwvMlhqMXJ4dDRFTkFFZEN3czVwb1VnTEdWdnBQK0RSSU9FNlU1bHB1djVJV1k0N256azBIeHdGdWZnNVZmUWhWOGZHd2RCQzZrdW4wTWI2dlZcL1JqWDc5Tm9FVHBKcUlXcDNseEFpQW5HZjl5Mlp0Q3UzNEROUlNOZUR4eUQ1UFFhaTlwWHRNTFo2YnBlMkZEMG1BRkpHNXAyUk9kNUc2RkM0UXRDMzA3YTRcL2d3ZVh5TklpeFN2MHBXdVZzV2RsUzlUM1RDN2dXZEZVSEVLb1ozU041MzZqeVdTZGRMTVNoMlRJZWwxN25ISnZsMnBxTmltQTlBRWpmUG05NG53U0M1SGJkd1NuSDlpQmdmY3NqaSsxVXYrVmlzVnVHbUk5V2lDYjRIR2pPZlFoQUxXbmxWZWZXVlhPWGs0NmkwOExRemZPN1krUXNRMjVRNmVkdEduM1ZCeHlUdDEwajlWMTZnMlwvaTl1Y3pcL2owZ0piODRoUGw3NFZ3bTlWYTNPZEl2bk1MdjdXNE14bG9MWlhcL2paakF3NGZ5V0ZtNHhQclp5ZUpWK2ZLbkV4UHdsY2FzbjVLQkp4SHpcLzJXNk9MOWQ1cE02T003WnprOE9RNVIwOWhcL1pKOHNSMTBGT29kOU1nQkJTNW1YeUMrWXU2Q1JSaGR6N2I4ZlRBRXA3SW10bGVTSTZJa0ZHTUVGQlFURXdNME5CTlRNM1JVRkZSRGczUXpJMFJFUTFNemt3T1VJNE1FRTNPRUU1TWpORk16Z3lNMFEyT0VSQlEwTTVORUk1UmtZNE16QTFSRU1pZlFkd01idGdFczlUZGRXeU96NGQrdlYyQVQxVUEwV3h3XC9NR05Pa0owdWxoaGhPYXVCQVpLK0RIdU5xXC9vQnQzRVQ0S3N2MDd2VnZRWFBZNkc2MzQ4Q2pzWWYyQURHQjV3NzlpeDc3ZERtZzZhWGRhSG95T3RxdHdDcyt2VUUxbXJhVytFOVhpZlh6Qm1UK2Roc2t4WlQ0NHllTzh1VGpjMTRKQnNpVWpmXC9ESEl5YUtzOHV3UFk2UnNxK3JUNjFtR3BseTBMQ25VNWlObjNDM2lPMHRcL3Y1WDY3eGRqWXhnYW5QcHEzTFVpZ29FNDZaOUpNdnp4NEFIdTZJNWJaUDJ6S3UwQzNWcXMyWjUwSENpQjh2MlhKY3dlY0lOcWFUWEZaQmwyclhNRmdBbDlDSjY2dkMwRnJJSWp1d3B4M1ZGelQxTHJmc3psOWdkYUY1aUp4VDlRemgraGNqNk9KaEhmcTNnQ0RGUFZsUnZIWkRcL1VHa3ltUmlvTGNPeDFBVEw3azhRclwvREpZQ3NjY3g5aXlEcnhhQ2hPa1FrV054VjZqcjBYSHRnaHdGSE5GeTFJc3kyeWxPcFJYMUVyY2Y0XC92bHJVU0N3WHQ2R0Z0dHhmR0xHbExlN0dBKzVaU3R3UGR4aGxUb2haeTdwMWg2a0U5ZnJxbU1tZk1YV2N1Rkt4NnZMWjVtMEtsbzZydllROWZ1Q0pUYkdKMDRENkI2RnR5WXYrOHlEWTJaeVBvRzhjbHJJZ3Z6dEtLaVlibG10RnRHakd3OVNaWkVxXC80ODBDOEZOdlVuZmtnXC9IT2FScnlKaXhvSjFicFBEK1YwRVlrYTVIbjBnWlwvRXVQTDFcL1B5eUp5Q0d5SVh2SlBFMHhZUWdDWllqcDJiNUhGZjZpMDdtUDFNdHNkYVBZd280VkQwRUNrckpDeE5DcnBIVmJqMWt3dmtcL0JxNE5QRXlFVFd2N25FamswUG1WalFySHpxZ2lpb2RIM1YrYXZodGxpSjcwNUx1RWFIdmNRTHU1aE5OSlVVRFNlTnMwR2hldVkxcGRuK3l6R1Y5ZG1OaklMSFI0N1B3UlQzRlwvMUJMNGU4elZLdFFOSWpYVnN1ZHd2UHN2RUNzamhmTGpaOEhmY3JGM1JTTW9pNnR0UlwvNkJlTVlSb1BHTlZ5bG9ybnBSQ3lqTXVsZmJlSzkzNWdaOVwvWkd6alFtZzRTRzlTWkUxSGdpaGhldFZ4bnh1YmRmMjhEcUxVNHBTZ2ZHWlFuMk83ZnYwNCt6RjQ0eHQ2VnJ6NFwvNHNzbTdaZUpKYW5HQStSVlFUZTlNTVNEYXU5Z1YyXC93Y0N4bGN6YmdkaUllYWQzb3RVK296V1FpS2VoODY2RVBrWjUxQmJraHRMNUxlT1lUYjAwYU5GRGk2QnVWZ2s2SG5sTnpJcDZIRDFcLytSaXVvc3llYTNmcVg0b1hwYVR0RE5WMEF1TlVLVnFvZUJkaktoTVVpb005UTVqbW94Q2hpcStWRndGbUlNUGFLMWpSRkpxTzRIUElsYWYwXC9MWk5cL3hzQU96WHQrVW5BQTFFZzZUU2FLYWN5amwxUXFmT21CNFV6TFwvcmx2WXRqRW5IdlwvdzZ4eUROMnp1IiwicGF5bWVudE1ldGhvZHMiOlt7ImNvbmZpZ3VyYXRpb24iOnsiY2FuSWdub3JlQ29va2llcyI6InRydWUifSwiZGV0YWlscyI6W3siaXRlbXMiOlt7ImlkIjoiMTEyMSIsIm5hbWUiOiJUZXN0IElzc3VlciJ9LHsiaWQiOiIxMTU0IiwibmFtZSI6IlRlc3QgSXNzdWVyIDUifSx7ImlkIjoiMTE1MyIsIm5hbWUiOiJUZXN0IElzc3VlciA0In0seyJpZCI6IjExNTIiLCJuYW1lIjoiVGVzdCBJc3N1ZXIgMyJ9LHsiaWQiOiIxMTUxIiwibmFtZSI6IlRlc3QgSXNzdWVyIDIifSx7ImlkIjoiMTE2MiIsIm5hbWUiOiJUZXN0IElzc3VlciBDYW5jZWxsZWQifSx7ImlkIjoiMTE2MSIsIm5hbWUiOiJUZXN0IElzc3VlciBQZW5kaW5nIn0seyJpZCI6IjExNjAiLCJuYW1lIjoiVGVzdCBJc3N1ZXIgUmVmdXNlZCJ9LHsiaWQiOiIxMTU5IiwibmFtZSI6IlRlc3QgSXNzdWVyIDEwIn0seyJpZCI6IjExNTgiLCJuYW1lIjoiVGVzdCBJc3N1ZXIgOSJ9LHsiaWQiOiIxMTU3IiwibmFtZSI6IlRlc3QgSXNzdWVyIDgifSx7ImlkIjoiMTE1NiIsIm5hbWUiOiJUZXN0IElzc3VlciA3In0seyJpZCI6IjExNTUiLCJuYW1lIjoiVGVzdCBJc3N1ZXIgNiJ9XSwia2V5IjoiaWRlYWxJc3N1ZXIiLCJ0eXBlIjoic2VsZWN0In1dLCJuYW1lIjoiaURFQUwiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBXbGtaV0ZzIiwidHlwZSI6ImlkZWFsIn0seyJkZXRhaWxzIjpbeyJrZXkiOiJlbmNyeXB0ZWRDYXJkTnVtYmVyIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkU2VjdXJpdHlDb2RlIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkRXhwaXJ5TW9udGgiLCJ0eXBlIjoiY2FyZFRva2VuIn0seyJrZXkiOiJlbmNyeXB0ZWRFeHBpcnlZZWFyIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiaG9sZGVyTmFtZSIsIm9wdGlvbmFsIjoidHJ1ZSIsInR5cGUiOiJ0ZXh0In1dLCJncm91cCI6eyJuYW1lIjoiQ3JlZGl0IENhcmQiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBYTmphR1Z0WlE9PSIsInR5cGUiOiJjYXJkIn0sIm5hbWUiOiJNYXN0ZXJDYXJkIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQVzFqIiwidHlwZSI6Im1jIn0seyJuYW1lIjoiUGF5UGFsIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQWEJoZVhCaGJBPT0iLCJ0eXBlIjoicGF5cGFsIn0seyJkZXRhaWxzIjpbeyJrZXkiOiJlbmNyeXB0ZWRDYXJkTnVtYmVyIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkU2VjdXJpdHlDb2RlIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkRXhwaXJ5TW9udGgiLCJ0eXBlIjoiY2FyZFRva2VuIn0seyJrZXkiOiJlbmNyeXB0ZWRFeHBpcnlZZWFyIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiaG9sZGVyTmFtZSIsIm9wdGlvbmFsIjoidHJ1ZSIsInR5cGUiOiJ0ZXh0In1dLCJncm91cCI6eyJuYW1lIjoiQ3JlZGl0IENhcmQiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBYTmphR1Z0WlE9PSIsInR5cGUiOiJjYXJkIn0sIm5hbWUiOiJWSVNBIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQWFpwYzJFPSIsInR5cGUiOiJ2aXNhIn0seyJkZXRhaWxzIjpbeyJrZXkiOiJlbmNyeXB0ZWRDYXJkTnVtYmVyIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkU2VjdXJpdHlDb2RlIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkRXhwaXJ5TW9udGgiLCJ0eXBlIjoiY2FyZFRva2VuIn0seyJrZXkiOiJlbmNyeXB0ZWRFeHBpcnlZZWFyIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiaG9sZGVyTmFtZSIsIm9wdGlvbmFsIjoidHJ1ZSIsInR5cGUiOiJ0ZXh0In1dLCJncm91cCI6eyJuYW1lIjoiQ3JlZGl0IENhcmQiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBYTmphR1Z0WlE9PSIsInR5cGUiOiJjYXJkIn0sIm5hbWUiOiJBbWVyaWNhbiBFeHByZXNzIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQV0Z0WlhnPSIsInR5cGUiOiJhbWV4In0seyJuYW1lIjoiU0VQQSBEaXJlY3QgRGViaXQiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBYTmxjR0ZrYVhKbFkzUmtaV0pwZEE9PSIsInR5cGUiOiJzZXBhZGlyZWN0ZGViaXQifSx7Im5hbWUiOiJQYXlzYWZlY2FyZCIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFhCaGVYTmhabVZqWVhKayIsInR5cGUiOiJwYXlzYWZlY2FyZCJ9LHsibmFtZSI6IkJhbmsgVHJhbnNmZXIgKE5MKSIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFdKaGJtdFVjbUZ1YzJabGNsOU9UQT09IiwidHlwZSI6ImJhbmtUcmFuc2Zlcl9OTCJ9LHsiZGV0YWlscyI6W3sia2V5IjoiZW5jcnlwdGVkQ2FyZE51bWJlciIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImVuY3J5cHRlZFNlY3VyaXR5Q29kZSIsIm9wdGlvbmFsIjoidHJ1ZSIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImVuY3J5cHRlZEV4cGlyeU1vbnRoIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkRXhwaXJ5WWVhciIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImhvbGRlck5hbWUiLCJvcHRpb25hbCI6InRydWUiLCJ0eXBlIjoidGV4dCJ9XSwiZ3JvdXAiOnsibmFtZSI6IkNyZWRpdCBDYXJkIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQWE5qYUdWdFpRPT0iLCJ0eXBlIjoiY2FyZCJ9LCJuYW1lIjoiTWFlc3RybyIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFcxaFpYTjBjbTg9IiwidHlwZSI6Im1hZXN0cm8ifSx7Im5hbWUiOiJIdW5rZW1vbGxlciBMaW5nZXJpZSBDYXJkIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQV2h0YkdsdVoyVnlhV1U9IiwidHlwZSI6ImhtbGluZ2VyaWUifSx7Im5hbWUiOiJFZW5tYWxpZ2UgbWFjaHRpZ2luZyIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFdScGNtVmpkR1JsWW1sMFgwNU0iLCJ0eXBlIjoiZGlyZWN0ZGViaXRfTkwifSx7Im5hbWUiOiJTRVBBIEJhbmsgVHJhbnNmZXIiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBXSmhibXRVY21GdWMyWmxjbDlKUWtGTyIsInR5cGUiOiJiYW5rVHJhbnNmZXJfSUJBTiJ9LHsibmFtZSI6ImNfY2FzaCIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFdOZlkyRnphQT09IiwidHlwZSI6ImNfY2FzaCJ9LHsiZGV0YWlscyI6W3sia2V5IjoiZW5jcnlwdGVkQ2FyZE51bWJlciIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImVuY3J5cHRlZFNlY3VyaXR5Q29kZSIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImVuY3J5cHRlZEV4cGlyeU1vbnRoIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkRXhwaXJ5WWVhciIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImhvbGRlck5hbWUiLCJvcHRpb25hbCI6InRydWUiLCJ0eXBlIjoidGV4dCJ9XSwiZ3JvdXAiOnsibmFtZSI6IkNyZWRpdCBDYXJkIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQWE5qYUdWdFpRPT0iLCJ0eXBlIjoiY2FyZCJ9LCJuYW1lIjoiRXhwcmVzc1BheSIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFdOMWNBPT0iLCJ0eXBlIjoiY3VwIn0seyJkZXRhaWxzIjpbeyJrZXkiOiJlbmNyeXB0ZWRDYXJkTnVtYmVyIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkU2VjdXJpdHlDb2RlIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkRXhwaXJ5TW9udGgiLCJ0eXBlIjoiY2FyZFRva2VuIn0seyJrZXkiOiJlbmNyeXB0ZWRFeHBpcnlZZWFyIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiaG9sZGVyTmFtZSIsIm9wdGlvbmFsIjoidHJ1ZSIsInR5cGUiOiJ0ZXh0In1dLCJncm91cCI6eyJuYW1lIjoiQ3JlZGl0IENhcmQiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBYTmphR1Z0WlE9PSIsInR5cGUiOiJjYXJkIn0sIm5hbWUiOiJEaW5lcnMgQ2x1YiIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFdScGJtVnljdz09IiwidHlwZSI6ImRpbmVycyJ9LHsiZGV0YWlscyI6W3sia2V5IjoiZW5jcnlwdGVkQ2FyZE51bWJlciIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImVuY3J5cHRlZFNlY3VyaXR5Q29kZSIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImVuY3J5cHRlZEV4cGlyeU1vbnRoIiwidHlwZSI6ImNhcmRUb2tlbiJ9LHsia2V5IjoiZW5jcnlwdGVkRXhwaXJ5WWVhciIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImhvbGRlck5hbWUiLCJvcHRpb25hbCI6InRydWUiLCJ0eXBlIjoidGV4dCJ9XSwiZ3JvdXAiOnsibmFtZSI6IkNyZWRpdCBDYXJkIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQWE5qYUdWdFpRPT0iLCJ0eXBlIjoiY2FyZCJ9LCJuYW1lIjoiRGlzY292ZXIiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBXUnBjMk52ZG1WeSIsInR5cGUiOiJkaXNjb3ZlciJ9LHsibmFtZSI6Ik5hdGlvbmFsZSBFbnRlcnRhaW5tZW50IENhcmQiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBXVnVkR1Z5ZEdGcGJtMWxiblJqWVhKayIsInR5cGUiOiJlbnRlcnRhaW5tZW50Y2FyZCJ9LHsibmFtZSI6IkdhbGwgJiBHYWxsIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQV2RoYkd4bllXeHMiLCJ0eXBlIjoiZ2FsbGdhbGwifSx7Im5hbWUiOiJQaG9uZSBQYXltZW50IiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQV2wyY2c9PSIsInR5cGUiOiJpdnIifSx7Im5hbWUiOiJMYW5kbGluZSBwaG9uZSIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFdsMmNreGhibVJzYVc1bCIsInR5cGUiOiJpdnJMYW5kbGluZSJ9LHsibmFtZSI6Ik1vYmlsZSBwaG9uZSIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFdsMmNrMXZZbWxzWlE9PSIsInR5cGUiOiJpdnJNb2JpbGUifSx7ImRldGFpbHMiOlt7ImtleSI6ImVuY3J5cHRlZENhcmROdW1iZXIiLCJ0eXBlIjoiY2FyZFRva2VuIn0seyJrZXkiOiJlbmNyeXB0ZWRTZWN1cml0eUNvZGUiLCJ0eXBlIjoiY2FyZFRva2VuIn0seyJrZXkiOiJlbmNyeXB0ZWRFeHBpcnlNb250aCIsInR5cGUiOiJjYXJkVG9rZW4ifSx7ImtleSI6ImVuY3J5cHRlZEV4cGlyeVllYXIiLCJ0eXBlIjoiY2FyZFRva2VuIn0seyJrZXkiOiJob2xkZXJOYW1lIiwib3B0aW9uYWwiOiJ0cnVlIiwidHlwZSI6InRleHQifV0sImdyb3VwIjp7Im5hbWUiOiJDcmVkaXQgQ2FyZCIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFhOamFHVnRaUT09IiwidHlwZSI6ImNhcmQifSwibmFtZSI6IkpDQiIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFdwallnPT0iLCJ0eXBlIjoiamNiIn0seyJuYW1lIjoiTW9uZXlib29rZXJzIiwicGF5bWVudE1ldGhvZERhdGEiOiJDZjYyZjFlMyFZbkpoYm1SRGIyUmxQVzF2Ym1WNVltOXZhMlZ5Y3c9PSIsInR5cGUiOiJtb25leWJvb2tlcnMifSx7Im5hbWUiOiJPbmViaXAiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBXOXVaV0pwY0E9PSIsInR5cGUiOiJvbmViaXAifSx7Im5hbWUiOiJQcmVtaXVtIFNNUyIsInBheW1lbnRNZXRob2REYXRhIjoiQ2Y2MmYxZTMhWW5KaGJtUkRiMlJsUFhOdGN3PT0iLCJ0eXBlIjoic21zIn0seyJuYW1lIjoiVW5pb25QYXkiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBYVnVhVzl1Y0dGNSIsInR5cGUiOiJ1bmlvbnBheSJ9LHsibmFtZSI6IldlYnNob3AgR2lmdGNhcmQiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBYZGxZbk5vYjNCbmFXWjBZMkZ5WkE9PSIsInR5cGUiOiJ3ZWJzaG9wZ2lmdGNhcmQifSx7Im5hbWUiOiJZb3VyIEdpZnQiLCJwYXltZW50TWV0aG9kRGF0YSI6IkNmNjJmMWUzIVluSmhibVJEYjJSbFBYbHZkWEpuYVdaMCIsInR5cGUiOiJ5b3VyZ2lmdCJ9XSwicHVibGljS2V5IjoiMTAwMDF8QkNBREY4MjU3RTE4QTFBODlBQ0M0MTQ5RDBGQzMyNEU5ODMxNUMyNDA1RDc1NUU1MDRENjY0QjJDMUM3MUE2MzhCOUQxMkZEMjkwRTEyQTA0QkIxRTZCMkRBM0YzN0M1NTJEMDExQ0ZCQUJCQ0M4NDgwNkFCQjc4RUQxNjU3OERFRDk2NDQ4Q0I4QjU0MTM5RkQ3QzcyRjkwQzA4NkMzNkFFNzdFNjlFOTE3MUEzQTBENTIwRDAyMTM2MzcyNjNFMEM1REY5NjREQUQ4RDc5N0VCMURENkU1NEFENjY5RDYwQUFDMjU1NUUwQzhCRTIyNzNGODk4NDQ3M0U3NkVFNzM4N0ZFQzBFNzFCODM2ODQ0Qjg0MDZBQzkwNTk0OUZCODhGQzY4RThGMDE4NjYzMkYxRURCNEM5QjVCODg4RUY1QzU3RERFMTEzN0JCRjM2RjY1NEU0N0U1NzFGQkM4ODgyOENCRTk0MzhENzQyRjNDMDkyQUQ1RkYzRTYyQUNBRDI1MjQ2RUE0M0QyMkNGQzhBRTE0NDE5NzY3ODY3RDJDNjBEQ0JBNkFDOTIwMDhEMEQ4ODM0QkVBMTExN0FFMzQ3RjMxMkQ2QzAxQzU3Q0I0MkFERDNENEQ2MkUzNzI3QTNDRThBQTFGMDlCRjZCNzk2QTBBMzc0MUJDNDgxMTEifQ==" +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentsresult-error-invalid-data-payload-422.json b/test/mocks/checkout/paymentsresult-error-invalid-data-payload-422.json new file mode 100644 index 00000000..aec41bc0 --- /dev/null +++ b/test/mocks/checkout/paymentsresult-error-invalid-data-payload-422.json @@ -0,0 +1,6 @@ +{ + "status": 422, + "errorCode": "14_018", + "message": "Invalid payload provided", + "errorType": "validation" +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentsresult-success.json b/test/mocks/checkout/paymentsresult-success.json new file mode 100644 index 00000000..9ee3775c --- /dev/null +++ b/test/mocks/checkout/paymentsresult-success.json @@ -0,0 +1,4 @@ +{ + "pspReference": "8535253563623704", + "resultCode": "Authorised" +} \ No newline at end of file diff --git a/test/mocks/checkoututility/originkeys-success.json b/test/mocks/checkoututility/originkeys-success.json new file mode 100644 index 00000000..12908762 --- /dev/null +++ b/test/mocks/checkoututility/originkeys-success.json @@ -0,0 +1,7 @@ +{ + "originKeys": { + "https://www.your-domain1.com": "pub.v2.7814286629520534.aHR0cHM6Ly93d3cueW91ci1kb21haW4xLmNvbQ.UEwIBmW9-c_uXo5wSEr2w8Hz8hVIpujXPHjpcEse3xI", + "https://www.your-domain3.com": "pub.v2.7814286629520534.aHR0cHM6Ly93d3cueW91ci1kb21haW4zLmNvbQ.fUvflu-YIdZSsLEH8Qqmr7ksE4ag_NYiiMXK0s6aq_4", + "https://www.your-domain2.com": "pub.v2.7814286629520534.aHR0cHM6Ly93d3cueW91ci1kb21haW4yLmNvbQ.EP6eXBJKk0t7-QIUl6e_b1qMuMHGepxG_SlUqxAYrfY" + } +} \ No newline at end of file