# -*- coding: utf-8 -*- # !/usr/bin/env python from __future__ import unicode_literals import logging from library import random_string from apilib.systypes import IterConstant from library.jdopen import JDOpenErrorCode from library.jdbase.exceptions import JDException, JDRequestException from library.jdopen.client import JdOpenBaseClient logger = logging.getLogger(__name__) __all__ = ('JDOpenPay') class SubOrderType(IterConstant): NORMAL = 'NORMAL' LEDGER = 'LEDGER' class JDOpenPay(JdOpenBaseClient): API_BASE_URL = 'https://openapi.duolabao.com' def __init__(self, agentNum, accessKey, secretKey, customerNum, shopNum, timeout = None, auto_retry = True): super(JDOpenPay, self).__init__(agentNum, accessKey, secretKey, timeout, auto_retry) self.customerNum = customerNum self.shopNum = shopNum def create_pay_url(self, out_trade_no, total_fee, notify_url, extraInfo = None, **kwargs): """ 创建主扫链接 :param out_trade_no: :param total_fee: :param notify_url: :param extraInfo: :param kwargs: 接受tableNum, machineNum参数 :return: """ url = '/v3/order/payurl/create' data = { 'agentNum': self.agentNum, 'customerNum': self.customerNum, 'shopNum': self.shopNum, 'requestNum': out_trade_no, 'amount': total_fee, 'callbackUrl': notify_url, 'source': 'API', 'payModel': 'ONCE' } if extraInfo: data.update({ 'extraInfo': extraInfo }) data.update(kwargs) result = self.post(url = url, data = data) return result['data']['url'] def unified_order(self, authId, bankType, requestNum, amount, callbackUrl, subject, ledgerRule = None, extraInfo = None, **kwargs): """ :param authId: :param bankType: :param requestNum: :param amount: :param callbackUrl: :param subject: :param ledgerRule: :param extraInfo: :param kwargs: 接受ledgerRule { "ledgerType":"FIXED", -- 分账类型 固定金额:FIXED 比例:RATE 目前只支持固定金额分账 "ledgerFeeAssume":"RECEIVER", 收款方承担:RECEIVER 分账方承担:LEDGER 默认为RECEIVER,目前仅支持收款方承担手续费 "list":[ -- 分账list必须全部包括收款方和分账方,分账金额保留两位小数,且分账金额相加需等于订单金额 { "customerNum":"10001115199597283447316", "amount":"0.01" }, { "customerNum":"10001114504252653551690", "amount":"0.01" } ] } :return: """ def processor(self, result): if 'success' not in result or not result['success'] or result['code'] != 'success': raise JDException( errCode = result.get('code'), errMsg = result.get("msg"), client = self) else: return result url = '/api/createPayWithCheck' data = { 'version': 'V4.0', 'agentNum': str(self.agentNum), 'customerNum': str(self.customerNum), # 'shopNum': str(self.shopNum), 'authCode': str(authId), 'bankType': str(bankType), 'requestNum': str(requestNum), 'orderAmount': str(amount), 'callbackUrl': str(callbackUrl), 'payModel': 'ONCE', 'orderType': 'SALES', 'payType': 'ACTIVE', 'bussinessType': 'QRCODE_TRAD', 'source': 'API', 'paySource': str(bankType) } if self.shopNum: data.update({ 'shopNum': str(self.shopNum) }) if ledgerRule: data.update({ 'subOrderType': str(SubOrderType.LEDGER), 'LedgerRequest': ledgerRule }) else: data.update({ 'subOrderType': str(SubOrderType.NORMAL) }) data.update(kwargs) if extraInfo: data.update({ 'extraInfo': extraInfo }) return self.post(url = url, data = data, processor = processor) def api_trade_query(self, out_trade_no = None): def processor(self, result): if 'success' not in result or not result['success']: raise JDException( errCode = result.get('code'), errMsg = result.get("msg"), client = self) if 'queryOrderInfoRes' not in result: raise JDException( errCode = 'QUERY_ORDER_FAILURE', errMsg = u'查询订单信息失败', client = self) if not result['queryOrderInfoRes']['success']: raise JDException( errCode = result['queryOrderInfoRes']['code'], errMsg = result['queryOrderInfoRes']['msg'], client = self) return result url = '/api/queryOrderPayDetail' data = { 'customerNum': self.customerNum, } if out_trade_no: data.update({'requestNum': out_trade_no}) else: raise JDException( errCode = JDOpenErrorCode.MY_INVALID_PARAMETER, errMsg = u'缺少requestNum参数') return self.post(url = url, data = data, processor = processor) def api_trade_refund(self, outTradeNo, outRefundNo, amount, **kwargs): """ 退款 :param outTradeNo: :param outRefundNo: :param amount: :param kwargs: :return: """ def processor(self, result): if 'result' not in result or not result['result'] or result['resultCode'] != 'success': raise JDRequestException( errCode = result.get('resultCode'), errMsg = result.get("message"), client = self) else: return result url = '/api/refundByRequestNum' data = { 'requestVersion': 'V4.0', 'agentNum': self.agentNum, 'customerNum': self.customerNum, # 'shopNum': self.shopNum, 'requestNum': outTradeNo, 'refundRequestNum': outRefundNo, 'refundPartAmount': amount } if self.shopNum: data.update({ 'shopNum': self.shopNum }) ledgerInfoList = kwargs.pop('ledgerInfoList', None) if ledgerInfoList: data.update({'list': ledgerInfoList}) data.update(kwargs) return self.post(url = url, data = data, processor = processor) def api_refund_query(self, out_refund_no): """ 查询退款订单 :param out_refund_no: 退款单号 :return: """ def processor(self, result): if 'success' not in result or not result['success'] or result['code'] != 'success': raise JDRequestException( errCode = result.get('code'), errMsg = result.get("msg"), client = self) else: return result url = '/api/queryRefundOrderByRequestNum' data = { 'agentNum': self.agentNum, 'customerNum': self.customerNum, # 'shopNum': self.shopNum } if self.shopNum: data.update({ 'shopNum': self.shopNum }) if out_refund_no: data.update({ 'requestNum': out_refund_no }) else: raise JDException( errCode = JDOpenErrorCode.MY_INVALID_PARAMETER, errMsg = u'缺少退款订单号参数') nonce_str = random_string(32) if self.shopNum: _str = 'agentnum={}&customernum={}&nonce_str={}&requestnum={}&shopnum={}&key=xrtbvjJk213W'.format( self.agentNum, self.customerNum, nonce_str, out_refund_no, self.shopNum) else: _str = 'agentnum={}&customernum={}&nonce_str={}&requestnum={}&key=xrtbvjJk213W'.format( self.agentNum, self.customerNum, nonce_str, out_refund_no) import hashlib sign = hashlib.md5(_str.encode('utf-8')).hexdigest().upper() data.update({ 'nonce_str': nonce_str, 'sign': sign }) return self.post(url = url, data = data, processor = processor) def add_wechat_auth_pay_dir(self, auth_pay_dir): """ 追加商户微信支付目录 :param auth_pay_dir: :return: """ def processor(self, result): if 'success' not in result or not result['success'] or result['code'] != 'success': raise JDException( errCode = result.get('code'), errMsg = result.get("message"), client = self) else: return result url = '/api/addAuthPayDirsDevConfig' data = { 'customerNum': self.customerNum, 'authPayDir': auth_pay_dir } return self.post(url = url, data = data, processor = processor) def query_wechat_auth_pay_dir(self, batch_num): """ 查询商户微信支付目录 :param batch_num: :return: """ def processor(self, result): if 'success' not in result or not result['success'] or result['code'] != 'success': raise JDException( errCode = result.get('code'), errMsg = result.get("message"), client = self) else: return result url = '/api/queryAddAuthPayDirsDevConfigByBatchNum' data = { 'customerNum': self.customerNum, 'batchNum': batch_num } return self.post(url = url, data = data, processor = processor) def api_close_order(self, requestNum): def processor(self, result): if 'success' not in result or not result['success'] or result['code'] != 'success': raise JDException( errCode = result.get('code'), errMsg = result.get("msg"), client = self) else: return result url = '/api/close' data = { 'agentNum': self.agentNum, 'customerNum': self.customerNum, 'requestNum': requestNum } return self.post(url = url, data = data, processor = processor) def api_cancel_order(self, requestNum): def processor(self, result): if 'success' not in result or not result['success'] or result['code'] != 'success': raise JDException( errCode = result.get('code'), errMsg = result.get("msg"), client = self) else: return result url = '/api/cancel' data = { 'agentNum': self.agentNum, 'customerNum': self.customerNum, 'requestNum': requestNum } return self.post(url = url, data = data, processor = processor) def download_bill(self, bill_date): url = '/v1/agent/checkaccountfile/download' data = { 'agentNum': self.agentNum, 'customerNum': self.customerNum, 'billType': 'QRBILL', 'date': bill_date } result = self.post(url = url, data = data) return result['data']['downloadUrl']