diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa93ee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/ +dist/ +smbc_gp_client.egg-info + +**/__pycache__ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..56e93f0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019 The Python Packaging Authority + +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/README.md b/README.md new file mode 100644 index 0000000..afc8691 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Python SMBC GMO Pay Client Lib +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FMonoidDev%2Fsmbc-gp-client.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FMonoidDev%2Fsmbc-gp-client?ref=badge_shield) + +An http client to call SMBC GMO Pay APIs. (smbc-gp-client) + +## Upload package +``` +python3 -m pip install --user --upgrade setuptools wheel twine +python3 setup.py sdist bdist_wheel +twine upload dist/* +``` + +## How to use +use pip install package `smbc-gp-client` +``` +pip3 install smbc-gp-client +``` +in your code: +```python +from smbc_gp_client.smbc_gp_client import SmbcGpClient + +client = SmbcGpClient() +client.create_transation(...) +client.execute_transaction(...) +``` +for more details, please refer to `SmbcGpClient` class method description. + +## License +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FMonoidDev%2Fsmbc-gp-client.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FMonoidDev%2Fsmbc-gp-client?ref=badge_large) + +## Reference +- SMBC GMO PAYMENT https://www.smbc-gp.co.jp/ diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..39a2b6e --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0b0accf --- /dev/null +++ b/setup.py @@ -0,0 +1,24 @@ +import setuptools + +with open("README.md", "r") as fh: + long_description = fh.read() + +setuptools.setup( + name="smbc-gp-client", + version="0.0.5", + author="Jierom", + author_email="jierom66@gmail.com", + description="An http client to call smbc-gp api", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/MonoidDev/smbc-gp-client", + packages=setuptools.find_packages(), + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + install_requires=[ + "requests", + ], +) \ No newline at end of file diff --git a/smbc_gp_client/__init__.py b/smbc_gp_client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/smbc_gp_client/smbc_gp_client.py b/smbc_gp_client/smbc_gp_client.py new file mode 100644 index 0000000..4e879c0 --- /dev/null +++ b/smbc_gp_client/smbc_gp_client.py @@ -0,0 +1,351 @@ +import requests +from enum import Enum + +def serialize_response(response): + data = dict() + params = response.text.split("&") + for param in params: + (key, value) = param.split("=") + data[key] = value + return data + +ENTRY_TRAN_PATH = '/EntryTran.idPass' +EXEC_TRAN_PATH = '/ExecTran.idPass' +SECURE_TRAN_PATH = '/SecureTran.idPass' +SECURE_TRAN2_PATH = '/SecureTran2.idPass' +ALTER_TRAN_PATH = '/AlterTran.idPass' +CHANGE_TRAN_PATH = '/ChangeTran.idPass' +SEARCH_TRADE_PATH = '/SearchTrade.idPass' + +class SmbcGpClient(): + + base_url = "https://pt01.smbc-gp.co.jp/payment" + + def __init__(self): + print("init smbc-gp client success") + + def __init__(self, base_url): + self.base_url = base_url + print("init smbc-gp client success") + + """ + 0. 创建交易 + + 1. 执行交易 - 不使用会员 ID (卡号/token/google token/3ds1.0/3ds2.0) + 3. 执行交易 - 使用 会员 ID (卡号/token/google token/3ds1.0/3ds2.0) + 4. 执行交易 - 3DS 1.0 认证后执行 + 5. 执行交易 - 3DS 2.0 认证后执行 + + 7. 交易变更 + 8. 金额变更 + + 9. 交易状态查询 + """ + + def create_transaction(self, shopId, shopPass, orderId, jobCd, amount, tax, + itemCode='0000990', tdFlag=0, tdTenantName='', tds2Type=1, tdRequired=0): + """ + To create a transation. Similar to login. + + Args: + - shopId: shopId is issued by smbc-gp, beginning with tshop*. + - shopPass: shopPass is issued by smbc-gp, should be 8 char. + - orderId: orderId is used to identify a transaction. + - jobCd: processing category. Available values are CHECK(validity check)/CAPTURE(immediate sales)/AUTH(temparary sales)/SAUTH(simple authorization) + - itemCode: product code, default '0000990' + - amount: transaction amount + - tax: transaction tax + - tdFlag: whether to use 3DS + - tdTenantName: show the shop name with base64 encoded + - tds2Type: determine what to do next if 3DS2.0 is not supported + - tdRequired: whether to use 3DS when executing transaction + + Returns: + - AccessId + - AccessPass + - ErrCode + - ErrInfo + """ + url = self.base_url + ENTRY_TRAN_PATH + data = { + 'ShopID': shopId, + 'ShopPass': shopPass, + 'OrderID': orderId, + 'JobCd': jobCd, + 'ItemCode': itemCode, + 'Amount': amount, + 'Tax': tax, + 'TdFlag': tdFlag, + 'TdTenantName': tdTenantName, + 'Tds2Type': tds2Type, + 'TdRequired': tdRequired + } + return serialize_response(requests.post(url, data=data)) + + def execute_transaction(self, accessId, accessPass, orderId, + method, payTimes, + cardNo, expire, holderName, securityCode, + token, pin, httpAccept, httpUserAgent, deviceCategory, + clientField1, clientField2, clientField3, clientFieldFlag, tokenType): + """ + Execute a transaction without member id. + + Args: + - accessId: returned by create transaction api. + - accessPass: returned by create transaction api. + - orderId: orderId is used to identify a transaction. + - method: 1(一括)/2(分割)/3(ボーナス一括)/4(ボーナス分割)/5(リボ). + - payTimes: set when method=2/4. + - cardNo: credit card number. + - expire: credit card expire time. + - holderName: credit card holder name. + - securityCode: credit card security code. + - token: + - pin: + - httpAccept: use when enable 3DS. + - httpUserAgent: use when enable 3DS. + - deviceCategory: 0(PC)/1(携帯装置). use when enable 3DS. + - clientField1: free field. + - clientField2: free field. + - clientField3: free field. + - clientFieldFlag: if returns back. 0(no)/1(yes). + - tokenType: 1(card service)/2(google pay). + + Returns: + + when not use 3DS: + - ACS: value 0. + - OrderID: + - Forward: + - Method: + - PayTimes: + - Approve: + - TranID: + - TranDate: + - CheckString: + - ClientField1: + - ClientField2: + - ClientField3: + - ErrCode: + - ErrInfo: + + when use 3DS 1.0: + - ACS: value 1. + - ACSUrl: + - PaReq: + - MD: + - ErrCode: + - ErrInfo: + + when use 3DS 2.0: + - ACS: value 2. + - RedirectUrl: + - ErrCode: + - ErrInfo: + """ + url = self.base_url + EXEC_TRAN_PATH + data = { + 'AccessID': accessId, + 'AccessPass': accessPass, + 'OrderID': orderId, + 'Method': method, + 'PayTimes': payTimes, + 'CardNo': cardNo, + 'Expire': expire, + 'HolderName': holderName, + 'SecurityCode': securityCode, + 'Token': token, + 'PIN': pin, + 'HttpAccept': httpAccept, + 'HttpUserAgent': httpUserAgent, + 'DeviceCategory': deviceCategory, + 'ClientField1': clientField1, + 'ClientField2': clientField2, + 'ClientField3': clientField3, + 'ClientFieldFlag': clientFieldFlag, + 'TokenType': tokenType, + } + return serialize_response(requests.post(url, data=data)) + + def execute_transaction_with_member(self, accessId, accessPass, orderId, + method, payTimes, siteId, sitePass, memberId, seqMode, + cardSeq, cardPass, securityCode, + httpAccept, httpUserAgent, deviceCategory, + clientField1, clientField2, clientField3): + """ + Execute a transaction with member id. + + Args: + - accessId: returned by create transaction api. + - accessPass: returned by create transaction api. + - orderId: orderId is used to identify a transaction. + - method: 1(一括)/2(分割)/3(ボーナス一括)/4(ボーナス分割)/5(リボ). + - payTimes: set when method=2/4. + - siteId: issued by smbc-gp. + - sitePass: issued by smbc-gp. + - memberId: + - seqMode: 0(logic)/1(physics) + - cardSeq: + - cardPass: + - securityCode: + - httpAccept: use when enable 3DS. + - httpUserAgent: use when enable 3DS. + - deviceCategory: 0(PC)/1(携帯装置). use when enable 3DS + - clientField1: free field. + - clientField2: free field. + - clientField3: free field. + - clientFieldFlag: if returns back. 0(no)/1(yes) + + Returns: + + when not use 3DS: + - ACS: value 0. + - OrderID: + - Forward: + - Method: + - PayTimes: + - Approve: + - TranID: + - TranDate: + - CheckString: + - ClientField1: + - ClientField2: + - ClientField3: + - ErrCode: + - ErrInfo: + + when use 3DS 1.0: + - ACS: value 1. + - ACSUrl: + - PaReq: + - MD: + - ErrCode: + - ErrInfo: + + when use 3DS 2.0: + - ACS: value 2. + - RedirectUrl: + - ErrCode: + - ErrInfo: + """ + url = self.base_url + EXEC_TRAN_PATH + data = { + 'AccessID': accessId, + 'AccessPass': accessPass, + 'OrderID': orderId, + 'Method': method, + 'PayTimes': payTimes, + 'SiteID': siteId, + 'SitePass': sitePass, + 'MemberID': memberId, + 'SeqMode': seqMode, + 'CardSeq': cardSeq, + 'CardPass': cardPass, + 'SecurityCode': securityCode, + 'HttpAccept': httpAccept, + 'HttpUserAgent': httpUserAgent, + 'DeviceCategory': deviceCategory, + 'ClientField1': clientField1, + 'ClientField2': clientField2, + 'ClientField3': clientField3, + } + return serialize_response(requests.post(url, data=data)) + + def execute_transaction_after_3ds1(self, paRes, md): + """ + Execute transaction after 3DS 1.0 verification + + Args: + - paRes: returned by 3DS 1.0 screen. + - md: returned by 3DS 1.0 screen. + + Returns: + - OrderID: + - Forward: + - Method: + - PayTimes: + - Approve + - TranID + - TranDate + - CheckString + - ClientField1: + - ClientField2: + - ClientField3: + - ErrCode: + - ErrInfo: + """ + url = self.base_url + SECURE_TRAN_PATH + data = { + 'PaRes': paRes, + 'MD': md, + } + return serialize_response(requests.post(url, data=data)) + + def execute_transaction_after_3ds2(self, accessId, accessPass): + """ + Execute transation after 3DS 2.0 verification. + + Args: + - accessId: + - accessPass: + + Returns: + - OrderID: + - Forward: + - Method: + - PayTimes: + - Approve + - TranID + - TranDate + - CheckString + - ClientField1: + - ClientField2: + - ClientField3: + - ErrCode: + - ErrInfo: + """ + url = self.base_url + SECURE_TRAN2_PATH + data = { + 'AccessID': accessId, + 'AccessPass': accessPass, + } + return serialize_response(requests.post(url, data=data)) + + def modify_transaction(self, shopId, shopPass, accessId, accessPass, jobCd, amount, tax, method, payTimes): + url = self.base_url + ALTER_TRAN_PATH + data = { + 'ShopID': shopId, + 'ShopPass': shopPass, + 'AccessID': accessId, + 'AccessPass': accessPass, + 'JobCd': jobCd, + 'Amount': amount, + 'Tax': tax, + 'Method': method, + 'PayTimes': payTimes, + } + return serialize_response(requests.post(url, data=data)) + + def modify_transaction_amount(self, shopId, shopPass, accessId, accessPass, jobCd, amount, tax): + url = self.base_url + CHANGE_TRAN_PATH + data = { + 'ShopID': shopId, + 'ShopPass': shopPass, + 'AccessID': accessId, + 'AccessPass': accessPass, + 'JobCd': jobCd, + 'Amount': amount, + 'Tax': tax, + } + return serialize_response(requests.post(url, data=data)) + + def search_transaction(self, shopId, shopPass, orderId, useSiteMaskLevel, useFloatinMask): + url = self.base_url + SEARCH_TRADE_PATH + data = { + 'ShopID': shopId, + 'ShopPass': shopPass, + 'OrderID': orderId, + 'UseSiteMaskLevel': useSiteMaskLevel, + 'UseFloatMask': useFloatinMask, + } + return serialize_response(requests.post(url, data=data)) diff --git a/test/test_smbc_gp_client.py b/test/test_smbc_gp_client.py new file mode 100644 index 0000000..18bcf84 --- /dev/null +++ b/test/test_smbc_gp_client.py @@ -0,0 +1,18 @@ +import os +import sys +sys.path.append("..") +from smbc_gp_client.smbc_gp_client import SmbcGpClient +import requests +# from smbc_gp_client.smbc_gp_client import SmbcGpClient + +SHOP_ID = os.environ['SMBC_SHOP_ID'] +SHOP_PASS = os.environ['SMBC_SHOP_PASS'] + +client = SmbcGpClient("https://pt01.smbc-gp.co.jp/payment") +r = client.create_transaction( + shopId=SHOP_ID, shopPass=SHOP_PASS, orderId="order003", + jobCd="AUTH", itemCode="0000990", + amount=10, tax=1, tdFlag=0, + tdTenantName="123", tds2Type=1, tdRequired=0 + ) +print(r)