def weixin_callback(self, *args, **kwargs):
        """
        微信回调接口处理
        """
        _logger.info("\n\n\n")
        _logger.info("Payment Gateway: receive the weixin callback")

        xml_data = request.httprequest.data
        xml_dict = xml_to_array(xml_data)
        _logger.info(xml_dict)

        payment_id = xml_dict['out_trade_no']  # 获取返回订单号
        payment_order = get_payment_order_by_id(request.cr, payment_id)
        _logger.info(payment_order)
        if payment_order:
            ihyf_payment_id = payment_order['ihyf_payment_id']

            payment_user_info = get_payment_user_info(
                request.cr, ihyf_payment_id)  # 获取该user信息
            app_id = payment_user_info['weixinpay_app_id']  # 获得app_id
            seller_id = payment_user_info[
                'weixinpay_seller_id']  # 卖家id 和 mch_id 是一样的东西
            private_key = payment_user_info['weixinpay_private_key']  # 获取该订单私钥
            ip = get_conf_info('gateway_ip')

            if not ip:
                return {
                    "title": "Weixin Pay",
                    "error": "Gateway Ip is not configured"
                }
            # 回调url(现为hyf外网地址)
            notify_url = get_notify_url(request.cr)
            if not notify_url:
                _logger.warn("notify_url 不存在")
            else:
                notify_url += '/weixin_callback'
            weixin_pay = QRWXpay(app_id, seller_id, private_key, ip,
                                 notify_url)

            result = weixin_pay.verify_callback(xml_data)
            _logger.info(result)
            if result and len(result) >= 1:
                if result[0] and result[1][
                        'result_code'] == 'SUCCESS' and payment_order[
                            'total_amount'] * 100 == float(
                                result[1]['total_fee']
                            ):  # 如果返回true,签名验证成功,支付金额需要和订单金额一致
                    payment_type = 'weixinpay'
                    # 验证成功,更新payment_order信息
                    update_payment_order(request.cr, 'paid', payment_id,
                                         payment_type, 'done')
                    _logger.info("update the payment order")
                    return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"
                else:  # 验证失败
                    return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[FAIL]]></return_msg></xml>"
            else:  # 验证失败
                return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[FAIL]]></return_msg></xml>"
Exemple #2
0
class WXpay(object):
    """ 微信支付base class """

    URL_UINFIEDORDER = get_conf_info('URL_UINFIEDORDER')
    URL_VERIFY_ORDER = get_conf_info('URL_VERIFY_ORDER')

    # URL_UINFIEDORDER = 'https://api.mch.weixin.qq.com/pay/unifiedorder'
    # URL_VERIFY_ORDER = 'https://api.mch.weixin.qq.com/pay/orderquery'

    def __init__(self,
                 appid,
                 mch_id,
                 key,
                 ip,
                 notify_url=None,
                 appsecret=None):
        self.appid = appid
        self.mch_id = mch_id
        self.key = key
        self.appsecret = appsecret
        self.ip = ip
        self.notify_url = notify_url
        self.cert_path = "pem证书路径"

    def generate_nonce_str(self, length=32):
        """ 生成随机字符串 """
        hash_char = [
            '0',
            '1',
            '2',
            '3',
            '4',
            '5',
            '6',
            '7',
            '8',
            '9',
            'A',
            'B',
            'C',
            'D',
            'E',
            'F',
            'G',
            'H',
            'I',
            'J',
            'K',
            'L',
            'M',
            'N',
            'O',
            'P',
            'Q',
            'R',
            'S',
            'T',
            'U',
            'V',
            'W',
            'X',
            'Y',
            'Z',
            'a',
            'b',
            'c',
            'd',
            'e',
            'f',
            'g',
            'h',
            'i',
            'j',
            'k',
            'l',
            'm',
            'n',
            'o',
            'p',
            'q',
            'r',
            's',
            't',
            'u',
            'v',
            'w',
            'x',
            'y',
            'z',
        ]
        rand_list = [
            hash_char[random.randint(0, 61)] for i in range(0, length)
        ]
        nonce_str = ''.join(rand_list)
        return nonce_str

    def xml_to_array(self, xml):
        """将xml转为dict"""
        array_data = {}
        root = ElementTree.fromstring(xml)
        for child in root:
            value = child.text
            array_data[child.tag] = value
        return array_data

    def generate_sign(self, sign_dict):
        """生成签名, 目前只支持MD5签名"""

        params_dict = OrderedDict(sorted(sign_dict.items(),
                                         key=lambda t: t[0]))
        params_dict['key'] = self.key

        foo_sign = []
        for k in params_dict:
            if isinstance(params_dict[k], unicode):
                params_dict[k] = params_dict[k].encode('utf-8')
            foo_sign.append('%s=%s' % (
                k,
                params_dict[k],
            ))
        foo_sign = '&'.join(foo_sign)
        sign = hashlib.md5(foo_sign).hexdigest().upper()
        return sign

    def unifiedorder(self, product, openid=None, trade_type=None):
        """统一下单接口"""

        assert isinstance(product, dict)
        assert trade_type in ('JSAPI', 'NATIVE')

        post_dict = {
            'appid': self.appid,
            'attach': product['attach'],
            'body': product['body'],
            'mch_id': self.mch_id,
            'nonce_str': self.generate_nonce_str(),
            'notify_url': self.notify_url,
            'out_trade_no': product['payment_id'],
            'product_id': product['product_id'],
            'spbill_create_ip': self.ip,
            'total_fee': product['total_fee'],  # 微信基本单位是分
            'trade_type': trade_type,
        }
        if trade_type == 'JSAPI' and openid is None:
            raise MissingParameter(u'JSAPI必须传入openid')
        else:
            if openid:
                post_dict['openid'] = openid
        post_dict['sign'] = self.generate_sign(post_dict)
        ret_xml = dict2xml(post_dict, wrap='xml')
        # logger.info(ret_xml)
        r = requests.post(self.URL_UINFIEDORDER, data=ret_xml.encode('utf-8'))
        r.encoding = 'UTF-8'
        data = r.text.encode('UTF-8')
        logging.info("get weixinpay url")
        logging.info(data)
        ret_dict = {}
        x = ElementTree.fromstring(data)
        if x.find('return_code').text.upper() == 'FAIL':
            raise ParameterValueError(x.find('return_msg').text)
        if x.find('result_code').text.upper() == 'FAIL':
            raise ParameterValueError(x.find('err_code').text)
        if trade_type == 'NATIVE':
            ret_dict['prepay_id'] = x.find('prepay_id').text
            ret_dict['code_url'] = x.find('code_url').text
        else:
            ret_dict['prepay_id'] = x.find('prepay_id').text
        ret_dict['nonce_str'] = post_dict['nonce_str']
        ret_dict['sign'] = post_dict['sign']
        return ret_dict

    def refundorder(self,
                    out_trade_no=None,
                    transaction_id=None,
                    total_fee=None,
                    refund_fee=None):
        """退款接口"""

        post_dict = {
            'appid': self.appid,
            'mch_id': self.mch_id,
            'nonce_str': self.generate_nonce_str(),
            'out_trade_no': out_trade_no,
            "out_refund_no": out_trade_no,
            "transaction_id": transaction_id,
            "total_fee": total_fee,
            'refund_fee': refund_fee,
            "op_user_id": self.mch_id
        }

        post_dict["sign"] = self.generate_sign(post_dict)
        ret_xml = dict2xml(post_dict, wrap='xml')

        r = requests.post(self.URL_REFUND_ORDER,
                          data=ret_xml.encode('utf-8'),
                          cert=self.cert_path)
        r.encoding = 'UTF-8'
        data = r.text.encode('utf-8')
        ret_dict = {}
        x = ElementTree.fromstring(data)
        if x.find('return_code').text.upper() == 'FAIL':
            raise ParameterValueError(x.find('return_msg').text)
        if x.find('result_code').text.upper() == 'FAIL':
            raise ParameterValueError(x.find('err_code').text)

        if x.find('return_code').text.upper() == "SUCCESS" and x.find(
                'result_code').text.upper() == "SUCCESS":
            return True
        return False

    def verify_notify(self, xml_str):
        """验证通知返回值"""
        xml_dict = {}
        x = ElementTree.fromstring(xml_str)
        xml_dict['appid'] = x.find('appid').text
        xml_dict['attach'] = x.find('attach').text
        xml_dict['bank_type'] = x.find('bank_type').text
        xml_dict['cash_fee'] = x.find('cash_fee').text
        xml_dict['fee_type'] = x.find('fee_type').text
        xml_dict['is_subscribe'] = x.find('is_subscribe').text
        xml_dict['mch_id'] = x.find('mch_id').text
        xml_dict['nonce_str'] = x.find('nonce_str').text
        xml_dict['openid'] = x.find('openid').text
        xml_dict['out_trade_no'] = x.find('out_trade_no').text
        xml_dict['result_code'] = x.find('result_code').text
        xml_dict['return_code'] = x.find('return_code').text
        xml_dict['sign'] = x.find('sign').text
        xml_dict['time_end'] = x.find('time_end').text
        xml_dict['total_fee'] = x.find('total_fee').text
        xml_dict['trade_type'] = x.find('trade_type').text
        xml_dict['transaction_id'] = x.find('transaction_id').text

        sign = xml_dict.pop('sign')
        if sign == self.generate_sign(xml_dict):
            return True, xml_dict
        else:
            raise TokenAuthorizationError(u'签名验证失败')

    def generate_notify_resp(self, resp_dict):
        assert set(resp_dict.keys()) == set(['return_code', 'return_msg'])

        xml_str = dict2xml(resp_dict, wrap='xml')
        return xml_str

    def verify_order(self, out_trade_no=None, transaction_id=None):
        if out_trade_no is None and transaction_id is None:
            raise MissingParameter(u'out_trade_no, transaction_id 不能同时为空')
        params_dict = {
            'appid': self.appid,
            'mch_id': self.mch_id,
            'nonce_str': self.generate_nonce_str(),
        }
        if transaction_id is not None:
            params_dict['transaction_id'] = transaction_id
        elif out_trade_no is not None:
            params_dict['out_trade_no'] = out_trade_no
        params_dict['sign'] = self.generate_sign(params_dict)

        xml_str = dict2xml(params_dict, wrap='xml')

        r = requests.post(self.URL_VERIFY_ORDER, xml_str)
        r.encoding = 'UTF-8'
        data = r.text.encode('UTF-8')

        xml_dict = {}
        # x = ElementTree.fromstring(data)
        # xml_dict['return_code'] = x.find('return_code').text
        # xml_dict['return_msg'] = x.find('return_msg').text
        #
        # if xml_dict['return_code'] == 'FAIL':
        #     return xml_dict
        # xml_dict['appid'] = x.find('appid').text
        # xml_dict['mch_id'] = x.find('mch_id').text
        # # xml_dict['device_info'] = x.find('device_info').text
        # xml_dict['nonce_str'] = x.find('nonce_str').text
        # xml_dict['sign'] = x.find('sign').text
        # xml_dict['result_code'] = x.find('result_code').text
        # # xml_dict['err_code'] = x.find('err_code').text
        # # xml_dict['err_code_des'] = x.find('err_code_des').text
        # xml_dict['openid'] = x.find('openid').text
        # xml_dict['is_subscribe'] = x.find('is_subscribe').text
        # xml_dict['trade_type'] = x.find('trade_type').text
        # xml_dict['bank_type'] = x.find('bank_type').text
        # xml_dict['total_fee'] = x.find('total_fee').text
        # xml_dict['fee_type'] = x.find('fee_type').text
        # xml_dict['cash_fee'] = x.find('cash_fee').text
        # # xml_dict['cash_fee_type'] = x.find('cash_fee_type').text
        # # xml_dict['coupon_fee'] = x.find('coupon_fee').text
        # # xml_dict['coupon_count'] = int(x.find('coupon_count').text)
        # # for i in range(xml_dict['coupon_count']):
        # #     xml_dict['coupon_batch_id_%d' % i+1] = x.find('coupon_batch_id_%d' % i+1).text
        # #     xml_dict['coupon_id_%d' % i+1] = x.find('coupon_id_%d' % i+1).text
        # #     xml_dict['coupon_fee_%d' % i+1] = x.find('coupon_fee_%d' % i+1).text
        # xml_dict['transaction_id'] = x.find('transaction_id').text
        # xml_dict['out_trade_no'] = x.find('out_trade_no').text
        # xml_dict['attach'] = x.find('attach').text
        # xml_dict['time_end'] = x.find('time_end').text
        # xml_dict['trade_state'] = x.find('trade_state').text
        xml_dict = super(QRWXpay, self).xml_to_array(data)
        sign = xml_dict.pop('sign')
        if sign == self.generate_sign(xml_dict):
            return xml_dict
        else:
            xml_dict['result_code'] = 'FAIL'
            return xml_dict
Exemple #3
0
import rsa
import json
import base64
import urllib
import datetime
import requests
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from odoo.addons.ihyf_payment_gateway.common.backend_common import get_conf_info
import logging

_logger = logging.getLogger(__name__)

# 支付宝统一接口
ALIPAY_OPENAPI_GATEWAY = get_conf_info('ALIPAY_OPENAPI_GATEWAY')  # 获得config配置
# ALIPAY_OPENAPI_GATEWAY = 'https://openapi.alipaydev.com/gateway.do'  # 沙箱
# ALIPAY_OPENAPI_GATEWAY = 'https://openapi.alipay.com/gateway.do'  # 正式环境

# 支付宝统一收单交易查询接口
METHOD_TRADE_QUERY = "alipay.trade.query"

# 支付宝统一收单交易预创建接口
METHOD_TRADE_PRECREATE = "alipay.trade.precreate"

# 支付宝统一收单交易退款接口
METHOD_TRADE_REFUND = "alipay.trade.refund"

# 支付宝统一收单交易关闭接口
METHOD_TRADE_CLOSE = "alipay.trade.close"
    def pay_qrcode_weixinpay(self, *args, **kwargs):
        ihyf_payment_id = kwargs.get('ihyf_payment_id')
        payment_user_info = get_payment_user_info(request.cr, ihyf_payment_id)

        payment_order = kwargs.get('payment_order')
        order_id = payment_order['order_id']
        body = payment_order.get('body', u'')
        source = kwargs.get('source', 'default')

        if get_payment_order(request.cr, order_id, 'weixinpay', source):
            return {
                'title': 'failure',
                'error': 'Payment Order is alreay exist'
            }

        payment_id = get_payment_id(order_id, 'weixinpay', source)

        create_payment_order(request.cr, payment_order,
                             payment_user_info['ihyf_payment_id'],
                             payment_user_info['ihyf_secret_key'], 'weixinpay',
                             source, payment_id)  # 在网关创建未支付订单

        product = dict()
        # 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据,这里暂定售货机ID
        product['attach'] = 1
        product['body'] = body or source
        product['payment_id'] = payment_id
        product['product_id'] = 1
        product['total_fee'] = int(payment_order['total_amount'] * 100)

        app_id = payment_user_info['weixinpay_app_id']  # app_id
        mch_id = payment_user_info['weixinpay_seller_id']  # 卖家id
        key = payment_user_info['weixinpay_private_key']  # 私钥
        ip = get_conf_info('gateway_ip')

        if not ip:
            return {
                "title": "Weixin Pay",
                "error": "Gateway Ip is not configured"
            }
        # 回调url(现为hyf外网地址)
        notify_url = get_notify_url(request.cr)
        if not notify_url:
            _logger.warn("notify_url 不存在")
        else:
            notify_url += '/weixin_callback'
        try:
            weixin_pay = QRWXpay(app_id,
                                 mch_id,
                                 key,
                                 ip,
                                 notify_url=notify_url)
            weixin_result = weixin_pay.unifiedorder(product)
            result = {
                "result": "SUCCESS",
                # "returnMsg": "OK",
                # "appid": app_id,
                # "mchId": mch_id,
                # "deviceInfo": "WEB",
                # "nonceStr": weixin_result['nonce_str'],
                # "sign": weixin_result['sign'],
                # "resultCode": "SUCCESS",
                # "tradeType": "NATIVE",
                # "prepayId": weixin_result['prepay_id'],
                "code_url": weixin_result['code_url']
            }
            return result
        except Exception as e:
            return {"title": "微信订单创建", "error": e.message}
    def query_order_weixinpay(self, *args, **kwargs):
        """
        查询订单支付结果接口
        kwargs: kwargs['outTradeNo']订单ID
        return: {}
        """
        info = dict()
        info['title'] = '支付信息查询'
        order_id = kwargs.get('order_id')
        if not order_id:
            info['error'] = '订单号有误'
            return info

        source = kwargs.get('source', 'default')
        payment_order = get_payment_order(request.cr, order_id, 'weixinpay',
                                          source)
        if not payment_order:
            info['error'] = '未找到该订单'
            return info

        if payment_order and payment_order['payment_status'] == 'paid':
            info['msg'] = 'SUCCESS'
        # elif payment_order and payment_order['payment_status'] == 'not_pay':
        #     info['msg'] = 'NOT_PAY'
        else:
            ihyf_payment_id = payment_order['ihyf_payment_id']
            # 查询微信服务器
            payment_user_info = get_payment_user_info(request.cr,
                                                      ihyf_payment_id)

            app_id = payment_user_info['weixinpay_app_id']  # 获得app_id
            seller_id = payment_user_info[
                'weixinpay_seller_id']  # 卖家id 和 mch_id 是一样的东西
            private_key = payment_user_info['weixinpay_private_key']  # 获取该订单私钥
            ip = get_conf_info('gateway_ip')

            if not ip:
                return {
                    "title": "Weixin Pay",
                    "error": "Gateway Ip is not configured"
                }
            # 回调url(现为hyf外网地址)
            notify_url = get_notify_url(request.cr)
            if not notify_url:
                _logger.warn("notify_url 不存在")
            else:
                notify_url += '/weixin_callback'
            weixin_pay = QRWXpay(app_id, seller_id, private_key, ip,
                                 notify_url)

            weixin_return_result = weixin_pay.verify_order(
                out_trade_no=payment_order['payment_id'])
            logging.info("query weixinpay")
            logging.info(weixin_return_result)
            if weixin_return_result.get('trade_state') == 'SUCCESS':
                update_payment_order(request.cr, 'paid',
                                     payment_order['payment_id'], 'done')
                info['msg'] = 'SUCCESS'
            else:
                info['msg'] = 'NOT_PAY'
        return info