python的微信支付代码

python的微信支付代码

from __future__ import unicode_literals

import time
import string
import random
import hashlib
import requests

from .base import Map, WeixinError

try:
    from flask import request
except Exception:
    request = None

from lxml import etree


__all__ = ("WeixinPayError", "WeixinPay")


FAIL = "FAIL"
SUCCESS = "SUCCESS"


class WeixinPayError(WeixinError):

    def __init__(self, msg):
        super(WeixinPayError, self).__init__(msg)


class WeixinPay(object):
    PAY_HOST = "https://api.mch.weixin.qq.com"

    def __init__(self, app_id, mch_id, mch_key, notify_url, key=None, cert=None):
        self.app_id = app_id
        self.mch_id = mch_id
        self.mch_key = mch_key
        self.notify_url = notify_url
        self.key = key
        self.cert = cert
        self.sess = requests.Session()

    @property
    def timestamp(self):
        return str(int(time.time()))

    @property
    def remote_addr(self):
        if request is not None:
            return request.remote_addr
        return ""

    @property
    def nonce_str(self):
        char = string.ascii_letters + string.digits
        return "".join(random.choice(char) for _ in range(32))

    def sign(self, raw):
        raw = [(k, str(raw[k]) if isinstance(raw[k], int) else raw[k])
               for k in sorted(raw.keys())]
        s = "&".join("=".join(kv) for kv in raw if kv[1])
        s += "&key={0}".format(self.mch_key)
        return hashlib.md5(s.encode("utf-8")).hexdigest().upper()

    def check(self, data):
        sign = data.pop("sign")
        return sign == self.sign(data)

    def to_xml(self, raw):
        s = ""
        for k, v in raw.items():
            s += "<{0}>{1}</{0}>".format(k, v)
        s = "<xml>{0}</xml>".format(s)
        return s.encode("utf-8")

    def to_dict(self, content):
        raw = {}
        root = etree.fromstring(content.encode("utf-8"),
                                parser=etree.XMLParser(resolve_entities=False))
        for child in root:
            raw[child.tag] = child.text
        return raw

    def _fetch(self, url, data, use_cert=False, appid=True):
        if appid:
            data.setdefault("appid", self.app_id)
        data.setdefault("mch_id", self.mch_id)
        data.setdefault("nonce_str", self.nonce_str)
        data.setdefault("sign", self.sign(data))

        if use_cert:
            resp = self.sess.post(url, data=self.to_xml(data), cert=(self.cert, self.key))
        else:
            resp = self.sess.post(url, data=self.to_xml(data))
        content = resp.content.decode("utf-8")
        if "return_code" in content:
            data = Map(self.to_dict(content))
            if data.return_code == FAIL:
                raise WeixinPayError(data.return_msg)
            if "result_code" in content and data.result_code == FAIL:
                raise WeixinPayError(data.err_code_des)
            return data
        return content

    def reply(self, msg, ok=True):
        code = SUCCESS if ok else FAIL
        return self.to_xml(dict(return_code=code, return_msg=msg))

    def unified_order(self, **data):
        """
        统一下单
        out_trade_no、body、total_fee、trade_type必填
        app_id, mchid, nonce_str自动填写
        spbill_create_ip 在flask框架下可以自动填写, 非flask框架需要主动传入此参数
        """
        url = self.PAY_HOST + '/pay/unifiedorder'

        # 必填参数
        if "out_trade_no" not in data:
            raise WeixinPayError("缺少统一支付接口必填参数out_trade_no")
        if "body" not in data:
            raise WeixinPayError("缺少统一支付接口必填参数body")
        if "total_fee" not in data:
            raise WeixinPayError("缺少统一支付接口必填参数total_fee")
        if "trade_type" not in data:
            raise WeixinPayError("缺少统一支付接口必填参数trade_type")

        # 关联参数
        if data["trade_type"] == "JSAPI" and "openid" not in data:
            raise WeixinPayError("trade_type为JSAPI时,openid为必填参数")
        if data["trade_type"] == "NATIVE" and "product_id" not in data:
            raise WeixinPayError("trade_type为NATIVE时,product_id为必填参数")
        data.setdefault("notify_url", self.notify_url)
        if "spbill_create_ip" not in data:
            data.setdefault("spbill_create_ip", self.remote_addr)

        raw = self._fetch(url, data)
        return raw

    def jsapi(self, **kwargs):
        """
        生成给JavaScript调用的数据
        详细规则参考 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
        """
        kwargs.setdefault("trade_type", "JSAPI")
        raw = self.unified_order(**kwargs)
        package = "prepay_id={0}".format(raw["prepay_id"])
        nonce_str = self.nonce_str
        raw = dict(appId=self.app_id, timeStamp=self.timestamp,
                   nonceStr=self.nonce_str, package=package,
                   signType='MD5')
        sign = self.sign(raw)
        raw['paySign'] = sign
        return raw

    order_jsapi = jsapi

    def order_h5(self, **kwargs):
        """
        H5 支付
        详细规则参考 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1
        """
        kwargs.setdefault('trade_type', 'MWEB')
        return self.unified_order(**kwargs)

    def order_qr(self, **kwargs):
        """
        Native 支付(扫码支付)
        详细规则参考 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
        """
        kwargs.setdefault('trade_type', 'NATIVE')
        return self.unified_order(**kwargs)

    def qrcode_url(self, product_id, shorten=False):
        params = {
            'appid': self.app_id,
            'mch_id': self.mch_id,
            'product_id': product_id,
            'time_stamp': self.timestamp,
            'nonce_str': self.nonce_str,
        }
        params['sign'] = self.sign(params)
        text = ('weixin://wxpay/bizpayurl?sign=%(sign)s&appid=%(appid)s&mch_id=%(mch_id)s'
                '&product_id=%(product_id)s&time_stamp=%(time_stamp)s&nonce_str=%(nonce_str)s') % params
        if shorten:
            return self.qrcode_url_shorten(long_url=urlencode(text))
        return text

    def qrcode_url_shorten(self, **data):
        url = self.PAY_HOST + '/tools/shorturl'
        if 'long_url' not in data:
            raise WechatPayError('缺少转换短链接接口必填参数long_url')
        return self._fetch(url, data)['short_url']

    def order_query(self, **data):
        """
        订单查询
        out_trade_no, transaction_id至少填一个
        appid, mchid, nonce_str不需要填入
        """
        url = self.PAY_HOST + '/pay/orderquery'

        if "out_trade_no" not in data and "transaction_id" not in data:
            raise WeixinPayError("订单查询接口中,out_trade_no、transaction_id至少填一个")

        return self._fetch(url, data)

    def close_order(self, out_trade_no, **data):
        """
        关闭订单
        out_trade_no必填
        appid, mchid, nonce_str不需要填入
        """
        url = self.PAY_HOST + '/pay/closeorder'

        data.setdefault("out_trade_no", out_trade_no)

        return self._fetch(url, data)

    def refund(self, **data):
        """
        申请退款
        out_trade_no、transaction_id至少填一个且
        out_refund_no、total_fee、refund_fee、op_user_id为必填参数
        appid、mchid、nonce_str不需要填入
        """
        url = self.PAY_HOST + '/secapi/pay/refund'
        if not self.key or not self.cert:
            raise WeixinPayError("退款申请接口需要双向证书")
        if "out_trade_no" not in data and "transaction_id" not in data:
            raise WeixinPayError("退款申请接口中,out_trade_no、transaction_id至少填一个")
        if "out_refund_no" not in data:
            raise WeixinPayError("退款申请接口中,缺少必填参数out_refund_no");
        if "total_fee" not in data:
            raise WeixinPayError("退款申请接口中,缺少必填参数total_fee");
        if "refund_fee" not in data:
            raise WeixinPayError("退款申请接口中,缺少必填参数refund_fee");

        return self._fetch(url, data, True)

    def refund_query(self, **data):
        """
        查询退款
        提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,
        用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
        out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
        appid、mchid、nonce_str不需要填入
        """
        url = self.PAY_HOST + '/pay/refundquery'
        if "out_refund_no" not in data and "out_trade_no" not in data \
                and "transaction_id" not in data and "refund_id" not in data:
            raise WeixinPayError("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个")

        return self._fetch(url, data)

    def download_bill(self, bill_date, bill_type="ALL", **data):
        """
        下载对账单
        bill_date、bill_type为必填参数
        appid、mchid、nonce_str不需要填入
        """
        url = self.PAY_HOST + '/pay/downloadbill'
        data.setdefault("bill_date", bill_date)
        data.setdefault("bill_type", bill_type)

        if "bill_date" not in data:
            raise WeixinPayError("对账单接口中,缺少必填参数bill_date")

        return self._fetch(url, data)

    def pay_individual(self, **data):
        """
        企业付款到零钱
        """
        url = self.PAY_HOST + '/mmpaymkttransfers/promotion/transfers'
        if not self.key or not self.cert:
            raise WeixinPayError("企业接口需要双向证书")
        if "partner_trade_no" not in data:
            raise WeixinPayError("企业付款接口中, 缺少必要的参数partner_trade_no")
        if "openid" not in data:
            raise WeixinPayError("企业付款接口中,缺少必填参数openid")
        if "amount" not in data:
            raise WeixinPayError("企业付款接口中,缺少必填参数amount")
        if "desc" not in data:
            raise WeixinPayError("企业付款接口中,缺少必填参数desc")
        data.setdefault('check_name', 'NO_CHECK')
        return self._fetch_pay(url, data, True)

    def pay_individual_to_card(self, **data):
        """企业付款到银行卡"""
        url = self.PAY_HOST + '/mmpaysptrans/pay_bank'
        if not self.key or not self.cert:
            raise WeixinPayError("企业接口需要双向证书")
        if "partner_trade_no" not in data:
            raise WeixinPayError("企业付款接口中, 缺少必要的参数partner_trade_no")
        if "enc_bank_no" not in data:
            raise WeixinPayError("企业付款接口中,缺少必填参数enc_bank_no")
        if "enc_true_name" not in data:
            raise WeixinPayError("企业付款接口中,缺少必填参数enc_true_name")
        if "bank_code" not in data:
            raise WeixinPayError("企业付款接口中,缺少必填参数bank_code")
        if "amount" not in data:
            raise WeixinPayError("企业付款接口中,缺少必填参数amount")
        return self._fetch(url, data, True, False)

    def pay_individual_bank_query(self, **data):
        """企业付款到银行卡查询"""
        url = self.PAY_HOST + '/mmpaysptrans/query_bank'
        if not self.key or not self.cert:
            raise WeixinPayError("企业接口需要双向证书'")
        if "partner_trade_no" not in data:
            raise WeixinPayError("企业付款接口中, 缺少必要的参数partner_trade_no")
        return self._fetch(url, data, True, False)

    def pay_individual_query(self, **data):
        """企业付款到零钱查询"""
        url = self.PAY_HOST + '/mmpaymkttransfers/gettransferinfo'
        if not self.key or not self.cert:
            raise WeixinPayError("企业接口需要双向证书'")
        if "partner_trade_no" not in data:
            raise WeixinPayError("企业付款接口中, 缺少必要的参数partner_trade_no")
        return self._fetch(url, data, True)

    def _fetch_pay(self, url, data, use_cert=False):
        data.setdefault("mch_appid", self.app_id)
        data.setdefault("mchid", self.mch_id)
        data.setdefault("nonce_str", self.nonce_str)
        data.setdefault("sign", self.sign(data))
        if use_cert:
            resp = self.sess.post(url, data=self.to_xml(data), cert=(self.cert, self.key))
        else:
            resp = self.sess.post(url, data=self.to_xml(data))
        content = resp.content.decode("utf-8")
        if "return_code" in content:
            data = Map(self.to_dict(content))
            if data.return_code == FAIL:
                raise WeixinPayError(data.return_msg)
            if "result_code" in content and data.result_code == FAIL:
                raise WeixinPayError(data.err_code_des)
            return data
        return content

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Python 示例代码,用于微信支付: ```python import requests import time import hashlib # 微信支付的相关信息 appid = 'your_appid' mch_id = 'your_mch_id' key = 'your_key' # 生成随机字符串 def get_nonce_str(): return str(int(time.time())) # 生成签名 def get_sign(params): stringA = '&'.join(["{0}={1}".format(k, params[k]) for k in sorted(params)]) stringSignTemp = "{0}&key={1}".format(stringA, key) sign = hashlib.md5(stringSignTemp.encode('utf-8')).hexdigest().upper() return sign # 统一下单接口 def unified_order(out_trade_no, total_fee, body): url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' nonce_str = get_nonce_str() params = { 'appid': appid, 'mch_id': mch_id, 'nonce_str': nonce_str, 'body': body, 'out_trade_no': out_trade_no, 'total_fee': total_fee, 'spbill_create_ip': '127.0.0.1', 'notify_url': 'http://www.example.com/notify', 'trade_type': 'JSAPI', 'openid': 'your_openid' } sign = get_sign(params) params['sign'] = sign xml = '<xml>' for k in params.keys(): xml += '<{0}>{1}</{0}>'.format(k, params[k]) xml += '</xml>' response = requests.post(url, data=xml.encode('utf-8')) data = response.text return data # 调用统一下单接口 result = unified_order('201907010001', 100, 'test') print(result) ``` 上面的代码实现了使用 Python 发起微信支付统一下单接口的请求,并返回响应结果。其中,需要替换 `your_appid`、`your_mch_id`、`your_key` 和 `your_openid` 为自己的实际值。此外,还需要根据实际情况进行修改请求参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值