From a460dafeff5d9f34dba63c0ba788027ab6976193 Mon Sep 17 00:00:00 2001 From: Petros Perlepes Date: Wed, 14 Sep 2016 08:32:50 +0300 Subject: [PATCH] Initial --- .gitignore | 2 + CreateOrder/Python/LICENSE.txt | 21 +++ CreateOrder/Python/README.md | 54 +++++++ CreateOrder/Python/README.rst | 76 +++++++++ CreateOrder/Python/VivaPayments/__init__.py | 0 CreateOrder/Python/VivaPayments/client.py | 144 ++++++++++++++++++ .../Python/VivaPayments/models/__init__.py | 5 + .../Python/VivaPayments/models/card.py | 50 ++++++ .../Python/VivaPayments/models/order.py | 61 ++++++++ .../Python/VivaPayments/models/source.py | 30 ++++ .../Python/VivaPayments/models/transaction.py | 109 +++++++++++++ .../Python/VivaPayments/models/wallet.py | 33 ++++ .../Python/VivaPayments/tests/__init__.py | 6 + CreateOrder/Python/VivaPayments/tests/base.py | 16 ++ .../Python/VivaPayments/tests/test_card.py | 19 +++ .../Python/VivaPayments/tests/test_order.py | 46 ++++++ .../Python/VivaPayments/tests/test_source.py | 13 ++ .../VivaPayments/tests/test_transaction.py | 137 +++++++++++++++++ .../Python/VivaPayments/tests/test_wallet.py | 13 ++ .../Python/VivaPayments/util/__init__.py | 0 CreateOrder/Python/VivaPayments/util/http.py | 80 ++++++++++ .../Python/VivaPayments/util/optional.py | 24 +++ .../Python/dist/VivaPython-0.0.1.tar.gz | Bin 0 -> 9617 bytes CreateOrder/Python/setup.cfg | 2 + CreateOrder/Python/setup.py | 23 +++ CreateOrder/Python/vivapayments.py | 131 ---------------- 26 files changed, 964 insertions(+), 131 deletions(-) create mode 100644 CreateOrder/Python/LICENSE.txt create mode 100644 CreateOrder/Python/README.md create mode 100644 CreateOrder/Python/README.rst create mode 100644 CreateOrder/Python/VivaPayments/__init__.py create mode 100644 CreateOrder/Python/VivaPayments/client.py create mode 100644 CreateOrder/Python/VivaPayments/models/__init__.py create mode 100644 CreateOrder/Python/VivaPayments/models/card.py create mode 100644 CreateOrder/Python/VivaPayments/models/order.py create mode 100644 CreateOrder/Python/VivaPayments/models/source.py create mode 100644 CreateOrder/Python/VivaPayments/models/transaction.py create mode 100644 CreateOrder/Python/VivaPayments/models/wallet.py create mode 100644 CreateOrder/Python/VivaPayments/tests/__init__.py create mode 100644 CreateOrder/Python/VivaPayments/tests/base.py create mode 100644 CreateOrder/Python/VivaPayments/tests/test_card.py create mode 100644 CreateOrder/Python/VivaPayments/tests/test_order.py create mode 100644 CreateOrder/Python/VivaPayments/tests/test_source.py create mode 100644 CreateOrder/Python/VivaPayments/tests/test_transaction.py create mode 100644 CreateOrder/Python/VivaPayments/tests/test_wallet.py create mode 100644 CreateOrder/Python/VivaPayments/util/__init__.py create mode 100644 CreateOrder/Python/VivaPayments/util/http.py create mode 100644 CreateOrder/Python/VivaPayments/util/optional.py create mode 100644 CreateOrder/Python/dist/VivaPython-0.0.1.tar.gz create mode 100644 CreateOrder/Python/setup.cfg create mode 100644 CreateOrder/Python/setup.py delete mode 100644 CreateOrder/Python/vivapayments.py diff --git a/.gitignore b/.gitignore index e43b0f98..8c76fd2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .DS_Store +**/venv +**/VivaPython.egg-info \ No newline at end of file diff --git a/CreateOrder/Python/LICENSE.txt b/CreateOrder/Python/LICENSE.txt new file mode 100644 index 00000000..176139ad --- /dev/null +++ b/CreateOrder/Python/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Peter Perlepes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/CreateOrder/Python/README.md b/CreateOrder/Python/README.md new file mode 100644 index 00000000..bcb4f944 --- /dev/null +++ b/CreateOrder/Python/README.md @@ -0,0 +1,54 @@ +# VivaPayments Python API wrapper + +![N|Solid](https://www.vivawallet.com/App_Themes/VivaWallet/Resources/img/viva-logo.png) + +### Installation +```bash +python setup.py install +``` + +PyPI will be up soon :) + +### Usage +```sh +from VivaPayments import client +viva_client = client.Client('MERCHANT_ID', 'API_KEY') +#for production do client.Client('MERCHANT_ID', 'API_KEY', 'production') +``` +With viva_client you can call any model you require and the desired action. The tests folder can serve as an initial guideline for your API calls. +```sh +#example +order = viva_client.Order.Create(100) +order_code = order['result']['OrderCode'] +``` +### Models + +The Python wrapper currently supports the listed calls. + +* Card (CreateToken, CheckInstallments) +* Order(Create, Cancel, Get, Update) +* Source(Add) +* Transaction(Get, Create, CreateRecurring, Cance, OriginalCreditTransaction) +* Wallet(BalanceTransfer) + +For some of the calls you need special permissions from Viva so consult the wiki before using. + +### Testing + +```sh +python setup.py test +``` +For the tests to pass you need to set up the followiwng enviroment variables.: +* TEST_MERCHANT (Your demo merchant ID) +* TEST_KEY (Your demo API Key) +* WALLET_ID (Your Viva wallet ID) + +License +---- +MIT + + +### Documentation +Code is clearly documented inside the module at the moment but will be officially documented after the PyPI release. + +For more information about the API usage refer to [Viva wiki](https://github.com/VivaPayments/API/wiki). diff --git a/CreateOrder/Python/README.rst b/CreateOrder/Python/README.rst new file mode 100644 index 00000000..d8dc76ca --- /dev/null +++ b/CreateOrder/Python/README.rst @@ -0,0 +1,76 @@ +VivaPayments Python API wrapper +=============================== + +.. figure:: https://www.vivawallet.com/App_Themes/VivaWallet/Resources/img/viva-logo.png + :alt: N\|Solid + + N\|Solid + +Installation +~~~~~~~~~~~~ + +.. code:: bash + + python setup.py install + +PyPI will be up soon :) + +Usage +~~~~~ + +.. code:: sh + + from VivaPayments import client + viva_client = client.Client('MERCHANT_ID', 'API_KEY') + #for production do client.Client('MERCHANT_ID', 'API_KEY', 'production') + +With viva\_client you can call any model you require and the desired +action. The tests folder can serve as an initial guideline for your API +calls. + +.. code:: sh + + #example + order = viva_client.Order.Create(100) + order_code = order['result']['OrderCode'] + +Models +~~~~~~ + +The Python wrapper currently supports the listed calls. + +- Card (CreateToken, CheckInstallments) +- Order(Create, Cancel, Get, Update) +- Source(Add) +- Transaction(Get, Create, CreateRecurring, Cance, + OriginalCreditTransaction) +- Wallet(BalanceTransfer) + +For some of the calls you need special permissions from Viva so consult +the wiki before using. + +Testing +~~~~~~~ + +.. code:: sh + + python setup.py test + +For the tests to pass you need to set up the followiwng enviroment +variables.: \* TEST\_MERCHANT (Your demo merchant ID) \* TEST\_KEY (Your +demo API Key) \* WALLET\_ID (Your Viva wallet ID) + +License +------- + +MIT + +Documentation +~~~~~~~~~~~~~ + +Code is clearly documented inside the module at the moment but will be +officially documented after the PyPI release. + +For more information about the API usage refer to `Viva wiki`_. + +.. _Viva wiki: https://github.com/VivaPayments/API/wiki \ No newline at end of file diff --git a/CreateOrder/Python/VivaPayments/__init__.py b/CreateOrder/Python/VivaPayments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/CreateOrder/Python/VivaPayments/client.py b/CreateOrder/Python/VivaPayments/client.py new file mode 100644 index 00000000..de004b1a --- /dev/null +++ b/CreateOrder/Python/VivaPayments/client.py @@ -0,0 +1,144 @@ +import json +import logging +import urllib +from base64 import b64encode +from util.http import Urllib2Client +from util.optional import allowed_parameters +from models import * + +class Client(object): + """Client responsible for constructing the API calls. + + Example: + new_client = Client(merchant_id = 'TEST_ID', api_key = 'TEST_KEY') + new_client.Order.Create(100) + + """ + http_client = Urllib2Client() + + def __init__(self, merchant_id=None, api_key=None, enviroment=None): + """__init__ function for Client + + Args: + merchant_id (str): Defaults to None. The client's Merchant ID. + api_key (str): Defaults to None. The client's API Key. + enviroment (str): Defaults to None. The enviroment that + the client calls will be directed to. The production enviroment + will only be called if 'production' is passed. + + Attributes: + url (str): URL that will be used for the API calls. Depends on the + enviroment argument. + headers (str): Authentication header that is used in every request. + Transaction (object): Transaction class instance. + Order (object): Order class instance. + Source (object): Source class instance. + Card (object): Card class instance. + Wallet (object): Wallet class instance. + + Raises: + Exception: If the Client instance was initialized without Merchant ID or API Key. + """ + if not (api_key and merchant_id): + raise Exception('Client must be instantiated with Merchant ID and API Key.') + + self.api_key = api_key + self.merchant_id = merchant_id + self.url = 'https://www.vivapayments.com/api/' if enviroment in [ + 'production'] else 'http://demo.vivapayments.com/api/' + self.enviroment = 'demo' if 'demo' in self.url else 'production' + self.headers = 'Basic {}'.format( + b64encode(self.merchant_id + ':' + self.api_key)) + + self.Transaction = Transaction(self) + self.Order = Order(self) + self.Source = Source(self) + self.Card = Card(self) + self.Wallet = Wallet(self) + + def __repr__(self): + return 'Vivapayments {} Client object with api_key: {} , merchant_id: {} '.format(self.enviroment, self.api_key, self.merchant_id) + + def call_api(self, method, path, params=None, data=None, headers=None, optional_parameters = None): + """Function that formats the API request. + + The call_api function is used for every request done to API, it formats the request parameters, + request body and optional parameters if expected and optional headers passed. Finally uses the + http_client to fullfill the request. + + Args: + method (str): The HTTP method that will be used. + path (str): The path that the request is directed to. + params (dict): Defaults to None. URL parameters to be used in the API call. + data (dict): Defaults to None. Data to be used in the request's body if the + appropriate method is used. + headers (dict): Defaults to None. Optional headers that will be used along with + the Basic Auth for the API. + optional_parameters (dict): Defaults to None. Any optional parameter that could be + passed in the request's body. + + Raises: + Exception: HTTP method not supported by the API. + + """ + headers = headers if headers else {} + headers['Authorization'] = self.headers + + request_url = self.url + path + + if params: + request_url = request_url + '?' + urllib.urlencode(self._pack(params)) + + if data and method not in ['POST', 'PUT', 'PATCH', 'DELETE']: + raise Exception('Incorrect HTTP method for arguments: ' + data) + elif data: + headers['Content-type'] = 'application/json' + if optional_parameters: + self._check_allowed(optional_parameters) + data = json.dumps(self._pack(data, optional_parameters = optional_parameters)) + + return self.http_client.make_request(method, request_url, headers, post_data = data) + + + def _pack(self, data, optional_parameters = None): + """Function to merge the standard with optional params in the request. + + The _pack function is used to add the optional parameters along with the standard, in the + body of the request. It is also used to remove None values from body and URL params. + + Args: + data (dict): The standard data to be passed in the URL body. + optional_parameters (dict): Any optional parameters that the user added. + + Returns: + merged_data (dict): Dictionary stripped of the None valued keys, along with + any optional parameters merged. + + Example: + data = {'OrderCode':'1234', 'date': None} + optional_parameters = {'TransactionId': '5678'} + merged_data = _pack(data, optional_parameters) + '{'OrderCode':'1234', 'TransactionId': '5678'}' + + """ + data = dict((key,value) for key, value in data.iteritems() if value != None) + merged_data = data.copy() + if optional_parameters: merged_data.update(optional_parameters) + return merged_data + + def _check_allowed(self, optional_parameters): + """Function that checks the validity of the optional parameters passed. + + The _check_allowed function, checks if the optional parameteres that were + passed in the request, are valid based on the online documentation and warns + the user using the logging module. + + Args: + optional_parameters (dict): The optional parameters to be passed in the request. + + """ + for key in optional_parameters.keys(): + if key not in allowed_parameters.keys(): + logging.warn('Parameter {} is most likely not supported by the API.'.format(key)) + + diff --git a/CreateOrder/Python/VivaPayments/models/__init__.py b/CreateOrder/Python/VivaPayments/models/__init__.py new file mode 100644 index 00000000..70801906 --- /dev/null +++ b/CreateOrder/Python/VivaPayments/models/__init__.py @@ -0,0 +1,5 @@ +from order import Order +from source import Source +from transaction import Transaction +from card import Card +from wallet import Wallet \ No newline at end of file diff --git a/CreateOrder/Python/VivaPayments/models/card.py b/CreateOrder/Python/VivaPayments/models/card.py new file mode 100644 index 00000000..71e64f67 --- /dev/null +++ b/CreateOrder/Python/VivaPayments/models/card.py @@ -0,0 +1,50 @@ +from datetime import datetime + + +class Card(object): + + def __init__(self, config): + self.config = config + + def CreateToken(self, public_key, cc_id, cvc, expiration_date, cardholder_name): + """Function used to generate a credit card token. + + Args: + public_key (str): The user's public API key. + cc_id (int): The credit card number. + cvc (int): The credit card CVC/CVV. + expiration_date (str) or (datetime): The credit card's + expiration date. + cardholder_name: The cardholder name of the card. + + """ + if isinstance(expiration_date, datetime): + expiration_date = date.strftime('%Y-%m-%d') + elif expiration_date: + expiration_date = expiration_date[:10] + + params = { + 'key': public_key + } + headers = { + 'NativeCheckoutVersion': '230' + } + data = { + 'Number': cc_id, + 'CVC': cvc, + 'ExpirationDate': expiration_date, + 'CardHolderName': cardholder_name + } + return self.config.call_api('POST', 'cards', params=params, data=data) + + def CheckInstallments(self, cc_id): + """Function used to check the installments limit of a credit card. + + Args: + cc_id (int): The credit card number to be checked. + + """ + headers = { + 'CardNumber': cc_id + } + return self.config.call_api('GET', 'cards/installments', headers=headers) diff --git a/CreateOrder/Python/VivaPayments/models/order.py b/CreateOrder/Python/VivaPayments/models/order.py new file mode 100644 index 00000000..ea74707a --- /dev/null +++ b/CreateOrder/Python/VivaPayments/models/order.py @@ -0,0 +1,61 @@ + + +class Order(object): + """Class to handle order API calls""" + def __init__(self, config): + self.config = config + + def Create(self, amount, **kwargs): + """Function used to create a new order. + + Args: + amount (int): Credit amount for the order. + + """ + data = { + 'Amount': amount + } + return self.config.call_api('POST', 'orders', data = data, optional_parameters = kwargs) + + def Cancel(self, order_code, **kwargs): + """Function used to create a new order. + + Args: + order_code (str): Code of the order to be cancelled. + + """ + url = 'orders/{}'.format(order_code) + return self.config.call_api('DELETE', url, optional_parameters = kwargs) + + def Get(self, order_code): + """Function used to get a specific order and info. + + Args: + order_code (str): Code of the order to aquire info. + + """ + url = 'orders/{}'.format(order_code) + return self.config.call_api('GET', url) + + def Update(self, order_code, amount = None, is_canceled = None, disable_paid_state = None, expiration_date = None): + """Function used to update specific order. + + Args: + order_code (str): Code of the order to be updated. + amount (int): Defaults to None. The new amount of the order. + is_canceled (bool): Defaults to None. Changes + the canceled state of the order. + disabled_paid_state (bool): Defaults to None. Enables + multiple payments for an order + expiration_date (str): Defaults to None. If passed + changes the expiration date of the order. + + """ + url = 'orders/{}'.format(order_code) + data = { + 'Amount': amount, + 'IsCanceled': is_canceled, + 'DisablePaidState': disable_paid_state, + 'ExpirationDate': expiration_date + } + return self.config.call_api('PATCH', url, data = data) \ No newline at end of file diff --git a/CreateOrder/Python/VivaPayments/models/source.py b/CreateOrder/Python/VivaPayments/models/source.py new file mode 100644 index 00000000..1b0fee5c --- /dev/null +++ b/CreateOrder/Python/VivaPayments/models/source.py @@ -0,0 +1,30 @@ + + +class Source(object): + """Class to handle source API calls""" + def __init__(self, config): + self.config = config + + def Add(self, name, source_code, domain, is_secure, path_fail, path_sucess): + """Function used to add a new source for sales groups. + + Args: + name (str): The name to be used for the new source. + source_code (str): A unique code to be used for the new source. + domain (str): The primary domain for the new source. + is_secure (bool): Value indicating id the protocol is http or https. + path_fail (str): The relative path the client will end up after + a failed transaction. + path_success (str): The relative path the client will end up after + a successful transaction. + + """ + data = { + 'Name': name, + 'SourceCode': source_code, + 'Domain': domain, + 'IsSecure': is_secure, + 'PathFail': path_fail, + 'PathSuccess': path_sucess + } + return self.config.call_api('POST', 'Sources', data = data) diff --git a/CreateOrder/Python/VivaPayments/models/transaction.py b/CreateOrder/Python/VivaPayments/models/transaction.py new file mode 100644 index 00000000..ab043b1e --- /dev/null +++ b/CreateOrder/Python/VivaPayments/models/transaction.py @@ -0,0 +1,109 @@ +from datetime import datetime + + +class Transaction(object): + """Class to handle transaction API calls""" + + def __init__(self, config): + self.config = config + + def Get(self, transaction_id=None, date=None, order_code=None): + """Function to get transactions based on function arguments. + + Args: + transaction_id (str): Defaults to None. Passing transaction_id + will search for the specific transaction info. + date (str) or (datetime): Defaults to None. Passing date will + result in all the transactions made this date. Date can be + either string or datetime object. + order_code (str): Defaults to None. Passing order_code will + return all the transactions based on the order code. + + """ + if transaction_id: + url = 'transactions/{}'.format(transaction_id) + else: + url = 'transactions/' + + if isinstance(date, datetime): + date = date.strftime('%Y-%m-%d') + elif date: + date = date[:10] + + params = { + 'date': date, + 'ordercode': order_code + } + + return self.config.call_api('GET', url, params=params) + + def Create(self, amount, order_code = None, card_token = None, **kwargs): + """Function used to generate a transaction based on order code and token. + + Args: + amount (int): Transaction amount. + order_code (str): The order code that the transaction will be based on. + card_token (str): The credit card token created by passing the credit + card to the API. + + """ + url = 'transactions' + data = { + 'Amount': amount, + 'OrderCode': order_code, + 'CreditCard': { + 'Token': card_token + } + } + return self.config.call_api('POST', url, data = data, optional_parameters = kwargs) + + def CreateRecurring(self, transaction_id, amount, **kwargs): + """Function used to generate a recurring transaction based on specific transaction id. + + Args: + transaction_id (str): The first transaction_id that was marked as recurring. + amount (int): Transaction amount. + + """ + url = 'transactions/{}'.format(transaction_id) + data = { + 'Amount': amount + } + return self.config.call_api('POST', url, data = data, optional_parameters = kwargs) + + def Cancel(self, transaction_id, amount = None, action_user = None): + """Function used to cancel a transaction based on specific transaction id. + + Args: + transaction_id (str): The transaction_id that will be cancelled. + amount (int): Defaults to None. Transaction amount. + action_user (str): Defaults to None. The user that initiated this action, + could be used for logging purposes. + + """ + url = 'transactions/{}'.format(transaction_id) + params = { + 'amount': amount, + 'actionuser': action_user + } + return self.config.call_api('DELETE', url, params = params) + + def OriginalCreditTransaction(self, transaction_id, amount, **kwargs): + """Function used to directly pay an amount to a card(refer to the docs). + + Args: + transaction_id (str): The transaction that the original payment was done. + amount (int): Amount to be paid. + + """ + url = 'transactions/{}'.format(transaction_id) + params = { + 'Amount': amount, + 'ServiceId': 6 + } + return self.config.call_api('DELETE', url, params = params, optional_parameters = kwargs) + + + + + diff --git a/CreateOrder/Python/VivaPayments/models/wallet.py b/CreateOrder/Python/VivaPayments/models/wallet.py new file mode 100644 index 00000000..40f56a06 --- /dev/null +++ b/CreateOrder/Python/VivaPayments/models/wallet.py @@ -0,0 +1,33 @@ + + +class Wallet(object): + """Class to handle wallet API calls""" + + def __init__(self, config): + self.config = config + + def BalanceTransfer(self, wallet_id, amount, target_wallet=None, person_id=None, description=None): + """Function that is used to transfer wallet funds to another wallet or person. + + Args: + wallet_id (str): The wallet that the money originate from. + amount (int): The amount to be transfered. + target_wallet (str): Default to None. The target wallet that the money will be transfered to. + person_id (str): The person account Id that the money will be transfered to. + description (str): The reason for transfer. + + """ + if (target_wallet and person_id) or (not target_wallet and not person_id): + raise Exception( + 'Balance transfer requires either target wallet or target person ID.') + if person_id: + url = 'wallets/{}/balancetransfer?TargetPersonId={}'.format( + wallet_id, person_id) + elif target_wallet: + url = 'wallets/{}/balancetransfer/{}'.format( + wallet_id, target_wallet) + data = { + 'Amount': amount, + 'Description': description + } + return self.config.call_api('POST', url, data=data) diff --git a/CreateOrder/Python/VivaPayments/tests/__init__.py b/CreateOrder/Python/VivaPayments/tests/__init__.py new file mode 100644 index 00000000..ad5ecd9a --- /dev/null +++ b/CreateOrder/Python/VivaPayments/tests/__init__.py @@ -0,0 +1,6 @@ +import unittest + +def viva_test_suite(): + test_loader = unittest.TestLoader() + return test_loader.discover('.', pattern = 'test_*.py') + diff --git a/CreateOrder/Python/VivaPayments/tests/base.py b/CreateOrder/Python/VivaPayments/tests/base.py new file mode 100644 index 00000000..c7d55cae --- /dev/null +++ b/CreateOrder/Python/VivaPayments/tests/base.py @@ -0,0 +1,16 @@ +import os +import unittest +from VivaPayments import client + +TEST_MERCHANT = os.environ['TEST_MERCHANT'] +TEST_KEY = os.environ['TEST_KEY'] + +class VivaTestClient(unittest.TestCase): + """unittest.TestCase subclass used for the modules tests. + + This class provides the test client already initialized and all + the unittest.TestCase methods. + + """ + test_client = client.Client( TEST_MERCHANT, TEST_KEY) + \ No newline at end of file diff --git a/CreateOrder/Python/VivaPayments/tests/test_card.py b/CreateOrder/Python/VivaPayments/tests/test_card.py new file mode 100644 index 00000000..573585ff --- /dev/null +++ b/CreateOrder/Python/VivaPayments/tests/test_card.py @@ -0,0 +1,19 @@ +import os +import unittest +from base import * + + +class CardTestCase(VivaTestClient): + + @unittest.skip("Skipping CreateToken.") + def test_create_token(self): + ret = self.test_client.Card.CreateToken(os.environ['PUBLIC_KEY'], 4111111111111111, 123, '2019-12-12', 'Igneel64') + self.assertEqual(ret['status_code'], 200) + self.assertEqual(len(ret['result']['Token']), 352) + + @unittest.skip("Skipping check_installments.") + def test_check_installments(self): + ret = self.test_client.Card.CheckInstallments(4111111111111111) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result.keys(), ['MaxInstallments']) \ No newline at end of file diff --git a/CreateOrder/Python/VivaPayments/tests/test_order.py b/CreateOrder/Python/VivaPayments/tests/test_order.py new file mode 100644 index 00000000..7e30ec1b --- /dev/null +++ b/CreateOrder/Python/VivaPayments/tests/test_order.py @@ -0,0 +1,46 @@ +import unittest +from base import * +from datetime import datetime + + +class OrderTestCase(VivaTestClient): + + @unittest.skip("Skipping create.") + def test_create(self): + ret = self.test_client.Order.Create(100) + result = ret['result'] + self.assertTrue(isinstance(result['TimeStamp'], datetime)) + self.assertTrue(result['OrderCode']) + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['ErrorCode'], 0) + self.assertIsNone(result['ErrorText']) + + @unittest.skip("Skipping get.") + def test_get(self): + order = self.test_client.Order.Create(100) + order_code = order['result']['OrderCode'] + ret = self.test_client.Order.Get(order_code) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertEqual(ret['result'].keys(), ['RequestAmount', 'MerchantTrns', 'OrderCode', + 'Tags', 'StateId', 'ExpirationDate', 'SourceCode', 'RequestLang', 'CustomerTrns']) + + @unittest.skip("Skipping cancel.") + def test_cancel(self): + order = self.test_client.Order.Create(100) + order_code = order['result']['OrderCode'] + ret = self.test_client.Order.Cancel(order_code) + result = ret['result'] + self.assertTrue(isinstance(result['TimeStamp'], datetime)) + self.assertEqual(result['OrderCode'], order_code) + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['ErrorCode'], 0) + self.assertIsNone(result['ErrorText']) + + @unittest.skip("Skipping update.") + def test_update(self): + order = self.test_client.Order.Create(100) + order_code = order['result']['OrderCode'] + ret = self.test_client.Order.Update(order_code, amount=40000) + self.assertEqual(ret['status_code'], 200) + self.assertIsNone(ret['result']) diff --git a/CreateOrder/Python/VivaPayments/tests/test_source.py b/CreateOrder/Python/VivaPayments/tests/test_source.py new file mode 100644 index 00000000..1a8cf9ba --- /dev/null +++ b/CreateOrder/Python/VivaPayments/tests/test_source.py @@ -0,0 +1,13 @@ +import unittest +from base import * + + +class SourceTestCase(VivaTestClient): + + @unittest.skip("Skipping add.") + def test_add(self): + ret = self.test_client.Source.Add( + 'Test Source', 'test1', 'mydomain.com', False, 'site/failure.py', 'site/success.py') + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertIsNone(result['result']) diff --git a/CreateOrder/Python/VivaPayments/tests/test_transaction.py b/CreateOrder/Python/VivaPayments/tests/test_transaction.py new file mode 100644 index 00000000..eaea0eca --- /dev/null +++ b/CreateOrder/Python/VivaPayments/tests/test_transaction.py @@ -0,0 +1,137 @@ +import unittest +from base import * +from datetime import datetime + + +class TransactionTestCase(VivaTestClient): + + def setUp(self): + card = self.test_client.Card.CreateToken( + os.environ['PUBLIC_KEY'], 4111111111111111, 123, '2019-12-12', 'Igneel64') + self.card_token = card['result']['Token'] + + order = self.test_client.Order.Create(100) + self.order_code = order['result']['OrderCode'] + + def tearDown(self): + pass + + @unittest.skip("Skipping create.") + def test_create(self): + ret = self.test_client.Transaction.Create( + 1000, order_code=self.order_code) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertIsNone(result['Amount']) + self.assertEqual(result['ErrorCode'], 403) + self.assertIsNone(result['TransactionId']) + self.assertIsNone(result['AuthorizationId']) + self.assertTrue(isinstance(result['TimeStamp'], datetime)) + + ret = self.test_client.Transaction.Create( + 1000, order_code=self.order_code, card_token=self.card_token) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['Amount'], 10.0) + self.assertEqual(result['ErrorCode'], 0) + self.assertIsNotNone(result['TransactionId']) + self.assertIsNotNone(result['AuthorizationId']) + self.assertTrue(isinstance(result['TimeStamp'], datetime)) + + @unittest.skip("Skipping create_recurring.") + def test_create_recurring(self): + initial_transaction = self.test_client.Transaction.Create( + 1000, order_code=self.order_code, card_token=self.card_token, AllowsRecurring=True) + initial_transaction_id = initial_transaction['result']['TransactionId'] + + ret = self.test_client.Transaction.CreateRecurring( + initial_transaction_id, 1000) + result = ret['result'] + + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['Amount'], 10.0) + self.assertEqual(result['ErrorCode'], 0) + self.assertNotEqual(result['TransactionId'], initial_transaction_id) + self.assertIsNotNone(result['AuthorizationId']) + self.assertTrue(isinstance(result['TimeStamp'], datetime)) + + ret = self.test_client.Transaction.CreateRecurring( + initial_transaction_id, 2000) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['Amount'], 20.0) + self.assertEqual(result['ErrorCode'], 0) + self.assertNotEqual(result['TransactionId'], initial_transaction_id) + self.assertIsNotNone(result['AuthorizationId']) + self.assertTrue(isinstance(result['TimeStamp'], datetime)) + + @unittest.skip("Skipping get.") + def test_get(self): + ret = self.test_client.Transaction.Get(order_code=self.order_code) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['ErrorCode'], 0) + self.assertFalse(result['Transactions']) + self.assertIsNone(result['ErrorText']) + self.assertTrue(isinstance(result['TimeStamp'], datetime)) + + transaction = self.test_client.Transaction.Create( + 100, order_code=self.order_code, card_token=self.card_token) + transaction_id = transaction['result']['TransactionId'] + + ret = self.test_client.Transaction.Get(order_code=self.order_code) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['ErrorCode'], 0) + self.assertIsNotNone(result['Transactions']) + self.assertEqual(result['Transactions'][0]['Order'][ + 'OrderCode'], self.order_code) + + ret = self.test_client.Transaction.Get(date=datetime.now()) + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['ErrorCode'], 0) + self.assertIsNotNone(result['Transactions']) + + @unittest.skip("Skipping cancel.") + def test_cancel(self): + transaction = self.test_client.Transaction.Create( + 100, order_code=self.order_code, card_token=self.card_token) + transaction_id = transaction['result']['TransactionId'] + + ret = self.test_client.Transaction.Cancel(transaction_id) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertIsNotNone(result['ErrorText']) + self.assertEqual(result['ErrorCode'], 400) + self.assertIsNone(result['TransactionId']) + + ret = self.test_client.Transaction.Cancel(transaction_id, amount=10) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertIsNotNone(result['ErrorText']) + self.assertEqual(result['ErrorCode'], 400) + self.assertIsNone(result['TransactionId']) + + ret = self.test_client.Transaction.Cancel(transaction_id, amount=100) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertIsNone(result['ErrorText']) + self.assertEqual(result['ErrorCode'], 0) + + ret = self.test_client.Transaction.Cancel(transaction_id, amount=100) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertEqual(result['ErrorText'], 'Non reversible transaction') + self.assertEqual(result['ErrorCode'], 403) + + @unittest.skip("Skipping OCT.") + def test_original_credit_transaction(self): + transaction = self.test_client.Transaction.Create( + 100, order_code=self.order_code, card_token=self.card_token) + transaction_id = transaction['result']['TransactionId'] + ret = self.test_client.Transaction.OriginalCreditTransaction( + transaction_id, 10) + result = ret['result'] + self.assertEqual(ret['status_code'], 200) + self.assertTrue(isinstance(result['TimeStamp'], datetime)) + self.assertEqual(result['ErrorCode'], 403) diff --git a/CreateOrder/Python/VivaPayments/tests/test_wallet.py b/CreateOrder/Python/VivaPayments/tests/test_wallet.py new file mode 100644 index 00000000..26a979ba --- /dev/null +++ b/CreateOrder/Python/VivaPayments/tests/test_wallet.py @@ -0,0 +1,13 @@ +import os +import unittest +import urllib2 +from base import * + + +class TransactionTestCase(VivaTestClient): + + @unittest.skip("Skipping balance_transfer.") + def test_balance_transfer(self): + with self.assertRaises(urllib2.HTTPError): + self.test_client.Wallet.BalanceTransfer( + os.environ['WALLET_ID'], 1000, target_wallet='587200706057') \ No newline at end of file diff --git a/CreateOrder/Python/VivaPayments/util/__init__.py b/CreateOrder/Python/VivaPayments/util/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/CreateOrder/Python/VivaPayments/util/http.py b/CreateOrder/Python/VivaPayments/util/http.py new file mode 100644 index 00000000..384b19fd --- /dev/null +++ b/CreateOrder/Python/VivaPayments/util/http.py @@ -0,0 +1,80 @@ +import json +import urllib2 +import urllib +from datetime import datetime, timedelta + + +def _timestamp_toUTC(timestamp): + """Function to turn a timestamp to UTC datetime object. + + Arguments: + timestamp (str): Timestamp returned from the API response. + + Attributes: + offset (str): The last six characters representing the offset. + sign (str): The offset sign (+ or -). + hours (int): The hours that need to be converted. + extra (int): The second part of the offset to be converted. + transaction_date (datetime): The datetime object from the timestamp. + + Returns: + Unaware Datetime object, in UTC time. + """ + + offset = timestamp[-6:] + sign = offset[0] + hours = int(offset.split(':')[0][1:]) + extra = int(offset.split(':')[1]) % 60.0 + hours + transaction_date = datetime.strptime(timestamp[:-7], '%Y-%m-%dT%H:%M:%S.%f') + return transaction_date - timedelta(hours=extra) if sign == '+' else transaction_date + timedelta(hours=extra) + + +class Urllib2Client(object): + + def make_request(self, method, url, headers, post_data=None): + """Function that makes the API requests using the urllib2 module. + + Arguments: + method (str): Request method to be used. + url (str): The url the request will be sent to. + headers (str): The headers to be included in the request. + post_data (dict): The data to be passed in the URL's body. + + Returns: + { + 'result': result + 'status_code': status_code + } + result (dict): Dictionary with the API response data. + status_code (int): Integer with the status code returned from the API. + + Raises: + urllib2.HTTPError: Generic urllib2 error handler. + """ + + request = urllib2.Request(url, post_data, headers) + + if method not in ('GET', 'POST'): + request.get_method = lambda: method.upper() + + try: + response = urllib2.urlopen(request) + rbody = response.read() + if hasattr(rbody, 'decode'): + rbody = rbody.decode('utf-8') + rcode = response.code + except urllib2.HTTPError, e: + raise urllib2.HTTPError(e.url, e.code, e.msg, e.hdrs, e.fp) + + try: + result = json.loads(rbody) + except ValueError: + result = rbody + + if 'TimeStamp' in (result or {}): + result['TimeStamp'] = _timestamp_toUTC(result['TimeStamp']) + + return { + 'result': result, + 'status_code': rcode + } diff --git a/CreateOrder/Python/VivaPayments/util/optional.py b/CreateOrder/Python/VivaPayments/util/optional.py new file mode 100644 index 00000000..6796b3ce --- /dev/null +++ b/CreateOrder/Python/VivaPayments/util/optional.py @@ -0,0 +1,24 @@ +allowed_parameters = { + 'IsPreAuth': bool, + 'ServiceId': int, + 'RequestLang': str, + 'FullName': str, + 'Email': str, + 'Phone': str, + 'Installments': int, + 'MaxInstallments': int, + 'MerchantTrns': str, + 'CustomerTrns': str, + 'SourceCode': str, + 'PaymentTimeOut': str, + 'ExpirationDate': str, + 'AllowRecurring': bool, + 'AllowsRecurring': bool, + 'Tags': list, + 'ActionUser': str, + 'DisableIVR': bool, + 'DisableCash': bool, + 'DisableCard': bool, + 'DisablePayAtHome': bool, + 'DisableExactAmount': bool +} \ No newline at end of file diff --git a/CreateOrder/Python/dist/VivaPython-0.0.1.tar.gz b/CreateOrder/Python/dist/VivaPython-0.0.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..037206cce95a13d6df9d9345dd11cbd1a7909985 GIT binary patch literal 9617 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 H0Pp|+m(djv literal 0 HcmV?d00001 diff --git a/CreateOrder/Python/setup.cfg b/CreateOrder/Python/setup.cfg new file mode 100644 index 00000000..11e9ec40 --- /dev/null +++ b/CreateOrder/Python/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +description-file = README.rst \ No newline at end of file diff --git a/CreateOrder/Python/setup.py b/CreateOrder/Python/setup.py new file mode 100644 index 00000000..df29d7d4 --- /dev/null +++ b/CreateOrder/Python/setup.py @@ -0,0 +1,23 @@ +import os +from setuptools import setup + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + +setup( + name = 'VivaPython', + version = '0.0.1', + description = 'Python wrapper for VivaPayments API', + author = 'igneel64', + author_email = 'p.perlepes@gmail.com', + long_description = read('README.rst'), + classifiers = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Programming Language :: Python :: 2.7' + ], + license = 'LICENSE.txt', + keywords = 'Viva VivaPayments payment', + packages = ['VivaPayments', 'VivaPayments.util', 'VivaPayments.models', 'VivaPayments.tests'], + test_suite = 'VivaPayments.tests.viva_test_suite' +) \ No newline at end of file diff --git a/CreateOrder/Python/vivapayments.py b/CreateOrder/Python/vivapayments.py deleted file mode 100644 index 777645cb..00000000 --- a/CreateOrder/Python/vivapayments.py +++ /dev/null @@ -1,131 +0,0 @@ -import datetime -import urllib2 -import urllib -import base64 -import json -import math - -class VivaPayments(object): - """VivaPayments API Wrapper""" - - # Demo constants - DEMO_URL = 'http://demo.vivapayments.com/api/' - DEMO_REDIRECT_URL = 'http://demo.vivapayments.com/web/newtransaction.aspx?ref=' - - # Production constants - PRODUCTION_URL = 'https://www.vivapayments.com/api/' - PRODUCT_REDIRECT_URL = 'https://www.vivapayments.com/web/newtransaction.aspx?ref=' - - def __init__(self,merchant_id=None,api_key=None,production=False): - self.url = VivaPayments.DEMO_URL if production == False else PRODUCT_REDIRECT_URL - self.merchant_id=merchant_id - self.api_key=api_key - - def create_order(self,amount,**kwargs): - """Create a Payment Order.""" - data = self.pack_data('amount',amount,kwargs) - return self._request('POST','orders',data) - - def cancel_order(self,order_code,**kwargs): - """Cancel an existing Payment Order.""" - data = self.pack_data('order_code',order_code,kwargs) - return self._request('DELETE','orders/'+str(order_code),data) - - def get_transaction(self,transaction_id,**kwargs): - """Get all details for a specific transaction, or for all transactions of a given date.""" - data = self.pack_data('transaction_id',transaction_id,kwargs) - return self._request('GET','transactions/'+str(transaction_id),data) - - def create_recurring_transaction(self,transaction_id,**kwargs): - """Make a recurring transaction.""" - data = self.pack_data('transaction_id',transaction_id,kwargs) - return self._request('POST','transactions/'+str(transaction_id),data) - - def cancel_transaction(self,transaction_id,amount): - """Cancel or refund a payment.""" - return self._grequest('DELETE','transactions/'+str(transaction_id)+'?amount='+str(amount)) - - def get_redirect_url(self,order_code): - """Returns the order code appended on the REDIRECT_URL_PREFIX""" - redirect_url = VivaPayments.DEMO_REDIRECT_URL if self.url == VivaPayments.DEMO_URL else VivaPayments.PRODUCT_REDIRET_URL - return redirect_url+str(order_code) - - ### UTILITY FUNCTIONS ### - def pack_data(self,arg_name,arg_val,kwargs): - return dict({arg_name:arg_val}.items() + kwargs.items()) - - def _grequest(self,request_method,url_suffix): - # Construct request object - request_url = self.url + url_suffix - request = urllib2.Request(request_url) - - # Request basic access authentication - base64string = base64.encodestring('%s:%s' % (self.merchant_id,self.api_key)).replace('\n', '') - request.add_header("Authorization", "Basic %s" % base64string) - - # Set http request method - request.get_method = lambda: request_method - response = urllib2.urlopen(request) - return self._decode(response.read()) - - def _request(self,request_method,url_suffix,data): - # Construct request object - data = urllib.urlencode(data) - request_url = self.url + url_suffix - request = urllib2.Request(request_url,data=data) - - # Request basic access authentication - base64string = base64.encodestring('%s:%s' % (self.merchant_id,self.api_key)).replace('\n', '') - request.add_header("Authorization", "Basic %s" % base64string) - - # Set http request method - request.get_method = lambda: request_method - response = urllib2.urlopen(request) - return self._decode(response.read()) - - def _decode(self,json_response): - obj = json.loads(json_response) - - timestamp=obj['TimeStamp'] - - date_tokens =timestamp[0:10].split('-') - year = int(date_tokens[0]) - month = int(date_tokens[1]) - day = int(date_tokens[2]) - - time_tokens =timestamp[11:27].split(':') - hour = int(time_tokens[0]) - minute = int(time_tokens[1]) - second = int(math.floor(float(time_tokens[2]))) - - # Doesn't add timezone info - obj['TimeStamp'] = datetime.datetime(year,month,day,hour,minute,second) - - return obj - -# Examples -if __name__ == '__main__': - # Create vivapayments API Wraper - viva_payments = VivaPayments(merchant_id='1b2573e7-2f67-4443-8a2e-84cac16ec79f',api_key='09014933') - - # Example 1 - - # Create order - result = viva_payments.create_order(100,RequestLang='en-US') - - # Get order code - order_code = result['OrderCode'] - - # Get redirect url - redirect_url = viva_payments.get_redirect_url(order_code) - - # Get the redirect url and paste it at your browser - print redirect_url - - # Example 2 - # Cancel Transaction - - result = viva_payments.cancel_transaction('959A0471-2CC8-4E75-A422-97E318E48ACD', 10) - print result - -