123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- # coding=utf-8
- import base64
- import datetime
- import hashlib
- import json
- import logging
- import random
- import time
- from binascii import hexlify
- from collections import Iterable
- from urlparse import urljoin
- import requests
- from Crypto.Cipher import DES3
- from apilib.monetary import JDMerchantPermillage
- from library.jdpsi.constants import JdPsiErrorCode, ACCESS_KEY
- from library.jdpsi.exceptions import JdPsiException
- logger = logging.getLogger(__name__)
- class MyEncoder(json.JSONEncoder):
- def default(self, obj):
- if isinstance(obj, JDMerchantPermillage):
- return obj.to_jd_params()
- return super(MyEncoder, self).default(obj)
- class JdPsiMerchantClient(object):
- BASE_URL = "https://psi.jd.com"
- def __init__(self, accessKey=None):
- self.accessKey = accessKey or ACCESS_KEY
- def pad(self):
- pass
- @staticmethod
- def sign(*args):
- h = hashlib.md5()
- h.update("".join((str(_)for _ in args)))
- return h.hexdigest()
- @staticmethod
- def pad(rawJson):
- # 转换为字节数组
- rawBytes = bytes.encode(rawJson, encoding="utf-8")
- rawBytesLen = len(rawBytes)
- # 计算补位
- x = (rawBytesLen + 4) % 8
- y = 0 if x == 0 else 8 - x
- # 将有效数据长度byte[]添加到原始byte数组的头部
- resultBytes = bytearray(rawBytesLen + 4 + y)
- resultBytes[0] = (rawBytesLen >> 24) & 0xFF
- resultBytes[1] = (rawBytesLen >> 16) & 0xFF
- resultBytes[2] = (rawBytesLen >> 8) & 0xFF
- resultBytes[3] = rawBytesLen & 0xFF
- # 填充补位数据
- for i in range(rawBytesLen):
- resultBytes[4 + i] = ord(rawBytes[i])
- for i in range(y):
- resultBytes[rawBytesLen + 4 + i] = 0x00
- return bytes(resultBytes)
- def encrypt(self, raw):
- plaintext = json.dumps(raw, sort_keys=True, separators=(',', ':'), cls=MyEncoder)
- key = base64.b64decode(self.accessKey)
- cipher = DES3.new(key, DES3.MODE_ECB)
- encrypt_bytes = cipher.encrypt(JdPsiMerchantClient.pad(plaintext))
- return hexlify(encrypt_bytes)
- def _handle_result(self, res, **kwargs):
- try:
- result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False)
- except (TypeError, ValueError, json.JSONDecodeError):
- logger.debug(u'错误的解析结构', exc_info=True)
- result = res
- if "code" not in result:
- return result
- if result["code"] == JdPsiErrorCode.SUCCESS:
- return result
- if result["code"] in []:
- return self._request(kwargs["path"], kwargs["agentNo"], kwargs["entity"], kwargs["images"], kwargs["signField"])
- raise JdPsiException(
- errCode='{}'.format(result["code"]),
- errMsg=result["message"],
- client=self,
- )
- def _request(self, path, agentNo, entity, images=None, signField=None):
- # 添加 签名标签
- if signField and isinstance(signField, Iterable):
- sign = JdPsiMerchantClient.sign(*(entity.get(_) for _ in signField))
- entity.update({"sign": sign})
- # 组织报文
- data = {
- "agentNo": agentNo,
- "entity": self.encrypt(entity)
- }
- # 图片文件
- if images and isinstance(images, dict):
- files = {_k: (_k, _v, "image/png") for _k, _v in images.items()}
- else:
- files = None
- url = urljoin(JdPsiMerchantClient.BASE_URL, path)
- try:
- res = requests.post(url=url, data=data, files=files)
- except requests.Timeout:
- raise JdPsiException(
- errCode='timeout',
- errMsg="timeout",
- client=self
- )
- else:
- try:
- res.raise_for_status()
- except requests.RequestException as rre:
- raise JdPsiException(
- errCode='HTTP{}'.format(res.status_code),
- errMsg=rre.message,
- client=self,
- request=rre.request,
- response=rre.response
- )
- return self._handle_result(
- res, path=path, agentNo=agentNo, entity=entity, images=images, signField=signField
- )
- def create_customer(
- self, agentNo, blicUrla, lepUrla, lepUrlb, lepUrlc, img, enterimg, innerimg, cardPhoto, settleManPhotoFront,
- settleManPhotoBack, settleHoldingIDCard,
- companyType, serialNo, regEmail, regPhone, blicCardType, blicCompanyName, abMerchantName, indTwoCode, blicProvince,
- blicCity, blicAddress, blicLongTerm, blicValidityStart, blicValidityEnd, lepCardType, lepName, lepCardNo, lepLongTerm,
- lepValidityStart, lepValidityEnd, contactName, contactPhone, contactEmail, contactProvince, contactCity, contactAddress,
- ifPhyStore, storeProvince, storeCity, storeAddress, settleToCard, priatePublic, bankName, subBankCode, bankAccountNo,
- bankAccountName, settleCardPhone, settlementPeriod, directoryList,
- occUrla=None, blicUscc=None, blicScope=None, merchantNo=None
- ):
- images = {
- "blicUrla": blicUrla,
- "lepUrla": lepUrla,
- "lepUrlb": lepUrlb,
- "lepUrlc": lepUrlc,
- "img": img,
- "enterimg": enterimg,
- "innerimg": innerimg,
- "cardPhoto": cardPhoto,
- "settleManPhotoFront": settleManPhotoFront,
- "settleManPhotoBack": settleManPhotoBack,
- "settleHoldingIDCard": settleHoldingIDCard
- }
- occUrla and images.update({"occUrla": occUrla})
- entity = {
- "companyType": companyType,
- "serialNo": serialNo,
- "agentNo": agentNo,
- "regEmail": regEmail,
- "regPhone": regPhone,
- "blicCardType": blicCardType,
- "blicCompanyName": blicCompanyName,
- "abMerchantName": abMerchantName,
- "indTwoCode": indTwoCode,
- "blicProvince": blicProvince,
- "blicCity": blicCity,
- "blicAddress": blicAddress,
- "blicLongTerm": blicLongTerm,
- "blicValidityStart": blicValidityStart,
- "blicValidityEnd": blicValidityEnd,
- "lepCardType": lepCardType,
- "lepName": lepName,
- "lepCardNo": lepCardNo,
- "lepLongTerm": lepLongTerm,
- "lepValidityStart": lepValidityStart,
- "lepValidityEnd": lepValidityEnd,
- "contactName": contactName,
- "contactPhone": contactPhone,
- "contactEmail": contactEmail,
- "contactProvince": contactProvince,
- "contactCity": contactCity,
- "contactAddress": contactAddress,
- "ifPhyStore": ifPhyStore,
- "storeProvince": storeProvince,
- "storeCity": storeCity,
- "storeAddress": storeAddress,
- "settleToCard": settleToCard,
- "priatePublic": priatePublic,
- "bankName": bankName,
- "subBankCode": subBankCode,
- "bankAccountNo": bankAccountNo,
- "bankAccountName": bankAccountName,
- "settleCardPhone": settleCardPhone,
- "settlementPeriod": settlementPeriod,
- "directoryList": directoryList
- }
- blicUscc and entity.update({"blicUscc": blicUscc})
- blicScope and entity.update({"blicScope": blicScope})
- merchantNo and entity.update({"merchantNo": merchantNo})
- logger.info("[create_customer] agentNo={}, entity={}".format(
- agentNo, entity
- ))
- path = "/merchant/enterSingle"
- return self._request(path=path, agentNo=agentNo, entity=entity, images=images, signField=[
- "serialNo", "lepCardNo", "bankAccountNo", "settleCardPhone"
- ])
- def create_product(self, agentNo, serialNo, merchantNo, productId, payToolId, mfeeType, mfee=None, ladderList=None):
- """
- 创建 结算 支付 产品
- """
- logger.info("[create_product] agentNo={}, serialNo={}, merchantNo={}, productId={}, payToolId={}, mfeeType={}, mfee={}, ladderList={}".format(
- agentNo, serialNo, merchantNo, productId, payToolId, mfeeType, mfee, ladderList
- ))
- entity = {
- "agentNo": agentNo,
- "serialNo": serialNo,
- "merchantNo": merchantNo,
- "productId": productId,
- "payToolId": payToolId,
- "mfeeType": mfeeType
- }
- if mfee:
- entity.update({"mfee": mfee})
- if ladderList:
- entity.update({"ladderList": ladderList})
- path = "/merchant/applySingle"
- return self._request(path=path, agentNo=agentNo, entity=entity, signField=[
- "serialNo", "agentNo", "merchantNo", "productId", "payToolId"
- ])
- def query_product(self, agentNo, serialNo, merchantNo):
- """
- 查询产品信息
- """
- logger.info("[query_product] agentNo={}, serialNo={}, merchantNo={}".format(
- agentNo, serialNo, merchantNo
- ))
- entity = {
- "agentNo": agentNo,
- "serialNo": serialNo,
- "merchantNo": merchantNo
- }
- path = "/merchant/status/queryApplySingle"
- return self._request(path=path, agentNo=agentNo, entity=entity, signField=[
- "serialNo", "merchantNo"
- ])
- def query_secret_key(self, agentNo, serialNo, merchantNo):
- """
- 查询产品秘钥
- """
- logger.info("[query_secret_key] agentNo={}, serialNo={}, merchantNo={}".format(
- agentNo, serialNo, merchantNo
- ))
- entity = {
- "serialNo": serialNo,
- "merchantNo": merchantNo,
- }
- path = "/merchant/status/queryMerchantKeys"
- return self._request(path=path, agentNo=agentNo, entity=entity, signField=[
- "serialNo", "merchantNo"
- ])
- def query_bill_file(self, agentNo, merchantNo, billDate=None):
- """
- 查询账单 的下载地址
- """
- logger.info("[query_bill_file] billDate={}, merchantNo={}, agentNo={}".format(billDate, merchantNo, agentNo))
- billDate = billDate or datetime.datetime.now().strftime("%Y%m%d")
- entity = {
- "serialNo": "{}{}".format(int(time.time() * 1000), random.randint(1000, 9999)),
- "agentNo": agentNo,
- "billDate": billDate,
- "merchantNo": merchantNo
- }
- path = "/agentmerchant/download"
- return self._request(path=path, agentNo=agentNo, entity=entity, signField=["billDate", "agentNo"])
- def query_settle(self, agentNo, startTime, endTime, pageNum=1, pageSize=10, orderStatus="", merchantNo=None):
- """
- 查询 商户的结算信息
- """
- logger.info("[query_settle] merchantNo={}, agentNo={}, startTime={}, endTime={}, orderStatus={}".format(
- merchantNo, agentNo, startTime, endTime, orderStatus
- ))
- entity = {
- "agentNo": agentNo,
- "merchantNo": merchantNo,
- "pageNum": pageNum,
- "pageSize": pageSize,
- "queryStartTime": startTime,
- "queryEndTime": endTime,
- "orderStatus": orderStatus,
- }
- path = "/merchant/status/queryMerchantsSettle"
- return self._request(path=path, agentNo=agentNo, entity=entity, signField=[
- "agentNo", "merchantNo", "queryStartTime", "queryEndTime", "orderStatus", "pageNum", "pageSize"
- ])
- def query_sub_channel(self, agentNo, merchantNo, productCode=None):
- logger.info("[query_sub_channel] merchantNo={}, agentNo={}, productCode={}".format(
- merchantNo, agentNo, productCode
- ))
- # 查询编号固定是401
- productCode = productCode or "401"
- entity = {
- "agentNo": agentNo,
- "merchantNo": merchantNo,
- "productCode": str(productCode),
- }
- path = "/merchant/status/queryMerchantWXNo"
- return self._request(path=path, agentNo=agentNo, entity=entity, signField=[
- "agentNo", "merchantNo", "productCode"
- ])
|