Skip to content

Commit d34e5e3

Browse files
committed
Moved repo
1 parent 442188f commit d34e5e3

25 files changed

+1870
-1
lines changed

.gitignore

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
*.py[cod]
2+
3+
# C extensions
4+
*.so
5+
6+
# Packages
7+
*.egg
8+
*.egg-info
9+
dist
10+
build
11+
eggs
12+
parts
13+
bin
14+
var
15+
sdist
16+
develop-eggs
17+
.installed.cfg
18+
lib
19+
lib64
20+
__pycache__
21+
22+
# Installer logs
23+
pip-log.txt
24+
25+
# Unit test / coverage reports
26+
.coverage
27+
.tox
28+
nosetests.xml
29+
30+
# Translations
31+
*.mo
32+
33+
# Mr Developer
34+
.mr.developer.cfg
35+
.project
36+
.pydevproject
37+
38+
# vim/bsd
39+
.swp
40+
.core
41+
42+
.venv
43+
.idea
44+
.ropeproject

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (C) 2014 mCASH Norge AS
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of
4+
this software and associated documentation files (the "Software"), to deal in
5+
the Software without restriction, including without limitation the rights to
6+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7+
of the Software, and to permit persons to whom the Software is furnished to do
8+
so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

README.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,44 @@
1-
settle-sdk-python
1+
=================
2+
mcash-mapi-client
3+
=================
4+
5+
A SDK used to make communication with mCASH's merchant API easier. A basic usage example is shown in `mapi_client_example`.
6+
7+
Before using this client
8+
------------------------
9+
10+
* Be aware that this client is in beta. Backwards incompatible changes should not occur, but updates might be frequent. Comments and pull requests are welcome.
11+
* Make sure you are familiar with the documentation, found at: `<http://dev.mca.sh/merchant/>`_. Consider reading the tutorials at `<http://dev.mca.sh/tutorials/>`_.
12+
* Register as a merchant at `<https://my.mca.sh/ssp/merchant/>`_
13+
14+
Usage
15+
-----
16+
MapiClient is the main class and can be found in `mapi_client`. It's constructor takes 4 required arguments:
17+
18+
* mcash_merchant: Your merchant id, received while registering.
19+
* mcash_user: Your merchant user, added in the SSP.
20+
* base_url: The base url to use. For production this is `<http://api.mca.sh/>`_. The URL's to use for testing can be found at `<http://dev.mca.sh/>`_.
21+
* auth: The authentication method to use. Accepts one of the classes defined in `auth`. See the 'Auth' section for more information.
22+
23+
After being instantiated with these arguments, the client is ready to use. All functionality is provided as member methods of the MapiClient class.
24+
25+
Auth
26+
^^^^
27+
The merchant API supports 3 authentication levels:
28+
29+
* Open (no authentication)
30+
* Secret
31+
* RSA
32+
33+
These are represented in the merchant API client as classes in the `auth` file. When passed as an argument to the MapiClient during instantiation, authentication will be automatically applied to every request.
34+
35+
36+
License
37+
-------
38+
| Copyright (C) 2014 mCASH Norge AS
39+
|
40+
| 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:
41+
|
42+
| The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
43+
|
44+
| 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.

mcash/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__import__('pkg_resources').declare_namespace(__name__)

mcash/mapi_client/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from mcash.mapi_client.mapi_client import *
2+
from mcash.mapi_client.auth import *
3+
from mcash.mapi_client.mapi_error import *
4+
from mcash.mapi_client.mapi_response import *
5+
from mcash.mapi_client.backends import *
6+
from mcash.mapi_client.backends.requestsframework import *
7+
from mcash.mapi_client.backends.urlfetch import *

mcash/mapi_client/auth.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import base64
2+
from Crypto.Hash import SHA256
3+
from Crypto.PublicKey import RSA
4+
from Crypto.Signature import PKCS1_v1_5
5+
from time import strftime
6+
7+
__all__ = ["OpenAuth", "SecretAuth", "RsaSha256Auth"]
8+
9+
10+
class OpenAuth(object):
11+
"""Attaches no authentication to the given Request object."""
12+
13+
def __call__(self, method, url, headers, body):
14+
return method, url, headers, body
15+
16+
17+
class SecretAuth(object):
18+
"""Attaches Authentication with secret token to
19+
the given Request object."""
20+
21+
def __init__(self, secret):
22+
self.secret = secret
23+
24+
def __call__(self, method, url, headers, body):
25+
headers['Authorization'] = 'SECRET ' + self.secret
26+
return method, url, headers, body
27+
28+
29+
class RsaSha256Auth(object):
30+
"""Attaches RSA authentication to the given Request object."""
31+
32+
def __init__(self, privkey):
33+
self.signer = PKCS1_v1_5.new(RSA.importKey(privkey))
34+
35+
def __call__(self, method, url, headers, body):
36+
headers['X-Mcash-Timestamp'] = self._get_timestamp()
37+
headers['X-Mcash-Content-Digest'] = self._get_sha256_digest(body)
38+
headers['Authorization'] = self._sha256_sign(method, url, headers, body)
39+
return method, url, headers, body
40+
41+
def _get_timestamp(self):
42+
"""Return the timestamp formatted to comply with
43+
Merchant API expectations.
44+
"""
45+
return str(strftime("%Y-%m-%d %H:%M:%S"))
46+
47+
def _get_sha256_digest(self, content):
48+
"""Return the sha256 digest of the content in the
49+
header format the Merchant API expects.
50+
"""
51+
content_sha256 = base64.b64encode(SHA256.new(content).digest())
52+
return 'SHA256=' + content_sha256
53+
54+
def _sha256_sign(self, method, url, headers, body):
55+
"""Sign the request with SHA256.
56+
"""
57+
d = ''
58+
sign_headers = method.upper() + '|' + url + '|'
59+
for key, value in sorted(headers.items()):
60+
if key.startswith('X-Mcash-'):
61+
sign_headers += d + key.upper() + '=' + value
62+
d = '&'
63+
64+
rsa_signature = base64.b64encode(
65+
self.signer.sign(SHA256.new(sign_headers)))
66+
67+
return 'RSA-SHA256 ' + rsa_signature

mcash/mapi_client/backends/__init__.py

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from ..mapi_response import MapiResponse
2+
3+
__all__ = ["RequestsFramework"]
4+
5+
class RequestsFramework(object):
6+
def dispatch_request(self, method, url, data, headers, auth):
7+
import requests
8+
method, url, headers, data = auth(method, url, headers, data)
9+
res = requests.request(method,
10+
url,
11+
data=data,
12+
headers=headers,
13+
auth=auth)
14+
return MapiResponse(res.status_code, res.headers, res.content)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from ..mapi_response import MapiResponse
2+
3+
__all__ = ["RequestsFramework"]
4+
5+
6+
class RequestsFramework(object):
7+
def dispatch_request(self, method, url, body, headers, auth, files=None):
8+
import requests
9+
method, url, headers, data = auth(method, url, headers, body)
10+
res = requests.request(method,
11+
url,
12+
data=data,
13+
headers=headers,
14+
auth=auth,
15+
timeout=60,
16+
files=files)
17+
return MapiResponse(res.status_code, res.headers, res.content)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from ..mapi_response import MapiResponse
2+
import json
3+
4+
from google.appengine.api import urlfetch
5+
6+
__all__ = ["UrlFetchFramework"]
7+
8+
9+
class UrlFetchFramework(object):
10+
def dispatch_request(self, method, url, body, headers, auth):
11+
method, url, headers, data = auth(method, url, headers, body)
12+
13+
payload = {}
14+
if type(data) == dict:
15+
for key, value in data.iteritems():
16+
if value is not None:
17+
payload.update({key: value})
18+
data = json.dumps(payload)
19+
20+
res = urlfetch.fetch(url=url,
21+
payload=data,
22+
method=method,
23+
deadline=60,
24+
headers=headers)
25+
return MapiResponse(res.status_code, res.headers, res.content)

0 commit comments

Comments
 (0)