|
@@ -1,342 +0,0 @@
|
|
|
-#coding:utf-8
|
|
|
|
|
-"""
|
|
|
|
|
-Created on 2014-11-24
|
|
|
|
|
-
|
|
|
|
|
-@author: http://blog.csdn.net/yueguanghaidao,yihaibo@longtugame.com
|
|
|
|
|
-
|
|
|
|
|
- * 微信支付帮助库
|
|
|
|
|
- * ====================================================
|
|
|
|
|
- * 接口分三种类型:
|
|
|
|
|
- * 【请求型接口】--Wxpay_client_
|
|
|
|
|
- * 统一支付接口类--UnifiedOrder
|
|
|
|
|
- * 订单查询接口--OrderQuery
|
|
|
|
|
- * 退款申请接口--Refund
|
|
|
|
|
- * 退款查询接口--RefundQuery
|
|
|
|
|
- * 对账单接口--DownloadBill
|
|
|
|
|
- * 短链接转换接口--ShortUrl
|
|
|
|
|
- * 【响应型接口】--Wxpay_server_
|
|
|
|
|
- * 通用通知接口--Notify
|
|
|
|
|
- * Native支付——请求商家获取商品信息接口--NativeCall
|
|
|
|
|
- * 【其他】
|
|
|
|
|
- * 静态链接二维码--NativeLink
|
|
|
|
|
- * JSAPI支付--JsApi
|
|
|
|
|
- * =====================================================
|
|
|
|
|
- * 【CommonUtil】常用工具:
|
|
|
|
|
- * trimString(),设置参数时需要用到的字符处理函数
|
|
|
|
|
- * createNoncestr(),产生随机字符串,不长于32位
|
|
|
|
|
- * formatBizQueryParaMap(),格式化参数,签名过程需要用到
|
|
|
|
|
- * getSign(),生成签名
|
|
|
|
|
- * arrayToXml(),array转xml
|
|
|
|
|
- * xmlToArray(),xml转 array
|
|
|
|
|
- * postXmlCurl(),以post方式提交xml到对应的接口url
|
|
|
|
|
- * postXmlSSLCurl(),使用证书,以post方式提交xml到对应的接口url
|
|
|
|
|
-
|
|
|
|
|
-"""
|
|
|
|
|
-
|
|
|
|
|
-import json
|
|
|
|
|
-import time
|
|
|
|
|
-import random
|
|
|
|
|
-import urllib2
|
|
|
|
|
-import hashlib
|
|
|
|
|
-import threading
|
|
|
|
|
-import urllib
|
|
|
|
|
-from urllib import quote
|
|
|
|
|
-import xml.etree.ElementTree as ET
|
|
|
|
|
-
|
|
|
|
|
-try:
|
|
|
|
|
- import pycurl
|
|
|
|
|
- from cStringIO import StringIO
|
|
|
|
|
-except ImportError:
|
|
|
|
|
- pycurl = None
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class WxPayConf_pub(object):
|
|
|
|
|
- """配置账号信息"""
|
|
|
|
|
-
|
|
|
|
|
- ##=======【基本信息设置】=====================================
|
|
|
|
|
- #微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看
|
|
|
|
|
- APPID = "wx2938132b773c7b5a"
|
|
|
|
|
- #JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看
|
|
|
|
|
- APPSECRET = "b32b08a8757cc34b1ee5490af897b065"
|
|
|
|
|
- #受理商ID,身份标识
|
|
|
|
|
- MCHID = "1590893081"
|
|
|
|
|
- #商户支付密钥Key。审核通过后,在微信发送的邮件中查看
|
|
|
|
|
- KEY = "kAHuCc2g4MINcLRk3o0lxT6J1Z04WuZq"
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- #=======【异步通知url设置】===================================
|
|
|
|
|
- #异步通知url,商户根据实际开发过程设定
|
|
|
|
|
- NOTIFY_URL = "http://baoming.siyusai.com/wxjspay/weixin_qrcode_notify/"
|
|
|
|
|
-
|
|
|
|
|
- #=======【JSAPI路径设置】===================================
|
|
|
|
|
- #获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面
|
|
|
|
|
- #JS_API_CALL_URL = "http://www.huodongjia.com/pay/?showwxpaytitle=1"
|
|
|
|
|
- JS_API_CALL_URL = "http://baoming.siyusai.com/wxjspay/forwxjspay/"
|
|
|
|
|
-
|
|
|
|
|
- #=======【证书路径设置】=====================================
|
|
|
|
|
- #证书路径,注意应该填写绝对路径
|
|
|
|
|
- SSLCERT_PATH = "/data/web/m_website_dev/m_web/weixinpay/cert/apiclient_cert.pem"
|
|
|
|
|
- SSLKEY_PATH = "/data/web/m_website_dev/m_web/weixinpay/cert/apiclient_key.pem"
|
|
|
|
|
-
|
|
|
|
|
- #=======【curl超时设置】===================================
|
|
|
|
|
- CURL_TIMEOUT = 30
|
|
|
|
|
-
|
|
|
|
|
- #=======【HTTP客户端设置】===================================
|
|
|
|
|
- HTTP_CLIENT = "URLLIB" # ("URLLIB", "CURL")
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class Singleton(object):
|
|
|
|
|
- """单例模式"""
|
|
|
|
|
-
|
|
|
|
|
- _instance_lock = threading.Lock()
|
|
|
|
|
-
|
|
|
|
|
- def __new__(cls, *args, **kwargs):
|
|
|
|
|
- if not hasattr(cls, "_instance"):
|
|
|
|
|
- with cls._instance_lock:
|
|
|
|
|
- if not hasattr(cls, "_instance"):
|
|
|
|
|
- impl = cls.configure() if hasattr(cls, "configure") else cls
|
|
|
|
|
- instance = super(Singleton, cls).__new__(impl, *args, **kwargs)
|
|
|
|
|
- if not isinstance(instance, cls):
|
|
|
|
|
- instance.__init__(*args, **kwargs)
|
|
|
|
|
- cls._instance = instance
|
|
|
|
|
- return cls._instance
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class UrllibClient(object):
|
|
|
|
|
- """使用urlib2发送请求"""
|
|
|
|
|
-
|
|
|
|
|
- def get(self, url, second=30):
|
|
|
|
|
- return self.postXml(None, url, second)
|
|
|
|
|
-
|
|
|
|
|
- def post(self,url,data):
|
|
|
|
|
- req = urllib2.Request(url)
|
|
|
|
|
- data = urllib.urlencode(data)
|
|
|
|
|
- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
|
|
|
|
|
- response = opener.open(req, data)
|
|
|
|
|
- return response.read()
|
|
|
|
|
-
|
|
|
|
|
- def postXml(self, xml, url, second=30):
|
|
|
|
|
- """不使用证书"""
|
|
|
|
|
- data = urllib2.urlopen(url, xml, timeout=second).read()
|
|
|
|
|
- return data
|
|
|
|
|
-
|
|
|
|
|
- def postXmlSSL(self, xml, url, second=30):
|
|
|
|
|
- """使用证书"""
|
|
|
|
|
- raise TypeError("please use CurlClient")
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class CurlClient(object):
|
|
|
|
|
- """使用Curl发送请求"""
|
|
|
|
|
- def __init__(self):
|
|
|
|
|
- self.curl = pycurl.Curl()
|
|
|
|
|
- self.curl.setopt(pycurl.SSL_VERIFYHOST, False)
|
|
|
|
|
- self.curl.setopt(pycurl.SSL_VERIFYPEER, False)
|
|
|
|
|
- #设置不输出header
|
|
|
|
|
- self.curl.setopt(pycurl.HEADER, False)
|
|
|
|
|
-
|
|
|
|
|
- def get(self, url, second=30):
|
|
|
|
|
- return self.postXmlSSL(None, url, second=second, cert=False, post=False)
|
|
|
|
|
-
|
|
|
|
|
- def postXml(self, xml, url, second=30):
|
|
|
|
|
- """不使用证书"""
|
|
|
|
|
- return self.postXmlSSL(xml, url, second=second, cert=False, post=True)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- def postXmlSSL(self, xml, url, second=30, cert=True, post=True):
|
|
|
|
|
- """使用证书"""
|
|
|
|
|
- self.curl.setopt(pycurl.URL, url)
|
|
|
|
|
- self.curl.setopt(pycurl.TIMEOUT, second)
|
|
|
|
|
- #设置证书
|
|
|
|
|
- #使用证书:cert 与 key 分别属于两个.pem文件
|
|
|
|
|
- #默认格式为PEM,可以注释
|
|
|
|
|
- if cert:
|
|
|
|
|
- self.curl.setopt(pycurl.SSLKEYTYPE, "PEM")
|
|
|
|
|
- self.curl.setopt(pycurl.SSLKEY, WxPayConf_pub.SSLKEY_PATH)
|
|
|
|
|
- self.curl.setopt(pycurl.SSLCERTTYPE, "PEM")
|
|
|
|
|
- self.curl.setopt(pycurl.SSLCERT, WxPayConf_pub.SSLCERT_PATH)
|
|
|
|
|
- #post提交方式
|
|
|
|
|
- if post:
|
|
|
|
|
- self.curl.setopt(pycurl.POST, True)
|
|
|
|
|
- self.curl.setopt(pycurl.POSTFIELDS, xml)
|
|
|
|
|
- buff = StringIO()
|
|
|
|
|
- self.curl.setopt(pycurl.WRITEFUNCTION, buff.write)
|
|
|
|
|
-
|
|
|
|
|
- self.curl.perform()
|
|
|
|
|
- return buff.getvalue()
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class HttpClient(Singleton):
|
|
|
|
|
- @classmethod
|
|
|
|
|
- def configure(cls):
|
|
|
|
|
- if pycurl is not None and WxPayConf_pub.HTTP_CLIENT != "URLLIB":
|
|
|
|
|
- return CurlClient
|
|
|
|
|
- else:
|
|
|
|
|
- return UrllibClient
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class Common_util_pub(object):
|
|
|
|
|
- """所有接口的基类"""
|
|
|
|
|
-
|
|
|
|
|
- def trimString(self, value):
|
|
|
|
|
- if value is not None and len(value) == 0:
|
|
|
|
|
- value = None
|
|
|
|
|
- return value
|
|
|
|
|
-
|
|
|
|
|
- def createNoncestr(self, length = 32):
|
|
|
|
|
- """产生随机字符串,不长于32位"""
|
|
|
|
|
- chars = "abcdefghijklmnopqrstuvwxyz0123456789"
|
|
|
|
|
- strs = []
|
|
|
|
|
- for x in range(length):
|
|
|
|
|
- strs.append(chars[random.randrange(0, len(chars))])
|
|
|
|
|
- return "".join(strs)
|
|
|
|
|
-
|
|
|
|
|
- def formatBizQueryParaMap(self, paraMap, urlencode):
|
|
|
|
|
- """格式化参数,签名过程需要使用"""
|
|
|
|
|
- slist = sorted(paraMap)
|
|
|
|
|
- buff = []
|
|
|
|
|
- for k in slist:
|
|
|
|
|
- v = quote(paraMap[k]) if urlencode else paraMap[k]
|
|
|
|
|
- buff.append("{0}={1}".format(k, v))
|
|
|
|
|
-
|
|
|
|
|
- return "&".join(buff)
|
|
|
|
|
-
|
|
|
|
|
- def getSign(self, obj):
|
|
|
|
|
- """生成签名"""
|
|
|
|
|
- #签名步骤一:按字典序排序参数,formatBizQueryParaMap已做
|
|
|
|
|
- String = self.formatBizQueryParaMap(obj, False)
|
|
|
|
|
- #签名步骤二:在string后加入KEY
|
|
|
|
|
- String = "{0}&key={1}".format(String,WxPayConf_pub.KEY)
|
|
|
|
|
- #签名步骤三:MD5加密
|
|
|
|
|
- String = hashlib.md5(String).hexdigest()
|
|
|
|
|
- #签名步骤四:所有字符转为大写
|
|
|
|
|
- result_ = String.upper()
|
|
|
|
|
- return result_
|
|
|
|
|
-
|
|
|
|
|
- def arrayToXml(self, arr):
|
|
|
|
|
- """array转xml"""
|
|
|
|
|
- xml = ["<xml>"]
|
|
|
|
|
- for k, v in arr.iteritems():
|
|
|
|
|
- if v.isdigit():
|
|
|
|
|
- xml.append("<{0}>{1}</{0}>".format(k, v))
|
|
|
|
|
- else:
|
|
|
|
|
- xml.append("<{0}>{1}</{0}>".format(k, v))
|
|
|
|
|
- xml.append("</xml>")
|
|
|
|
|
- return "".join(xml)
|
|
|
|
|
-
|
|
|
|
|
- def xmlToArray(self, xml):
|
|
|
|
|
- """将xml转为array"""
|
|
|
|
|
- array_data = {}
|
|
|
|
|
- root = ET.fromstring(xml)
|
|
|
|
|
- for child in root:
|
|
|
|
|
- value = child.text
|
|
|
|
|
- array_data[child.tag] = value
|
|
|
|
|
- return array_data
|
|
|
|
|
-
|
|
|
|
|
- def postXmlCurl(self, xml, url, second=30):
|
|
|
|
|
- """以post方式提交xml到对应的接口url"""
|
|
|
|
|
- return HttpClient().postXml(xml, url, second=second)
|
|
|
|
|
-
|
|
|
|
|
- def postXmlSSLCurl(self, xml, url, second=30):
|
|
|
|
|
- """使用证书,以post方式提交xml到对应的接口url"""
|
|
|
|
|
- return HttpClient().postXmlSSL(xml, url, second=second)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class Wxpay_client_pub(Common_util_pub):
|
|
|
|
|
- """请求型接口的基类"""
|
|
|
|
|
- response = None #微信返回的响应
|
|
|
|
|
- url = None #接口链接
|
|
|
|
|
- curl_timeout = None #curl超时时间
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self):
|
|
|
|
|
- self.parameters = {} #请求参数,类型为关联数组
|
|
|
|
|
- self.result = {} #返回参数,类型为关联数组
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- def setParameter(self, parameter, parameterValue):
|
|
|
|
|
- """设置请求参数"""
|
|
|
|
|
- self.parameters[self.trimString(parameter)] = self.trimString(parameterValue)
|
|
|
|
|
-
|
|
|
|
|
- def createXml(self):
|
|
|
|
|
- """设置标配的请求参数,生成签名,生成接口参数xml"""
|
|
|
|
|
- return self.arrayToXml(self.parameters)
|
|
|
|
|
-
|
|
|
|
|
- def postXml(self):
|
|
|
|
|
- """post请求xml"""
|
|
|
|
|
- xml = self.createXml()
|
|
|
|
|
- self.response = self.postXmlCurl(xml, self.url, self.curl_timeout)
|
|
|
|
|
- return self.response
|
|
|
|
|
-
|
|
|
|
|
- def postXmlSSL(self):
|
|
|
|
|
- """使用证书post请求xml"""
|
|
|
|
|
- xml = self.createXml()
|
|
|
|
|
- self.response = self.postXmlSSLCurl(xml, self.url, self.curl_timeout)
|
|
|
|
|
- return self.response
|
|
|
|
|
-
|
|
|
|
|
- def getResult(self):
|
|
|
|
|
- """获取结果,默认不使用证书"""
|
|
|
|
|
- self.postXml()
|
|
|
|
|
- self.result = self.xmlToArray(self.response)
|
|
|
|
|
- return self.result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class UnifiedOrder_pub(Wxpay_client_pub):
|
|
|
|
|
- """统一支付接口类"""
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, timeout=WxPayConf_pub.CURL_TIMEOUT):
|
|
|
|
|
- #设置接口链接
|
|
|
|
|
- self.url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
|
|
|
|
|
- #设置curl超时时间
|
|
|
|
|
- self.curl_timeout = timeout
|
|
|
|
|
- super(UnifiedOrder_pub, self).__init__()
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- def createXml(self):
|
|
|
|
|
- """生成接口参数xml"""
|
|
|
|
|
- #检测必填参数
|
|
|
|
|
- if any(self.parameters[key] is None for key in ("out_trade_no", "total_fee")):
|
|
|
|
|
- raise ValueError("missing parameter")
|
|
|
|
|
- if self.parameters["trade_type"] == "JSAPI" and self.parameters["openid"] is None:
|
|
|
|
|
- raise ValueError("JSAPI need openid parameters")
|
|
|
|
|
-
|
|
|
|
|
- self.parameters["appid"] = WxPayConf_pub.APPID #公众账号ID
|
|
|
|
|
- self.parameters["mch_id"] = WxPayConf_pub.MCHID #商户号
|
|
|
|
|
- self.parameters["spbill_create_ip"] = "127.0.0.1" #终端ip
|
|
|
|
|
- self.parameters["nonce_str"] = self.createNoncestr() #随机字符串
|
|
|
|
|
- self.parameters["notify_url"] = WxPayConf_pub.NOTIFY_URL #随机字符串
|
|
|
|
|
- self.parameters["body"] = "培训报名费" #随机字符串
|
|
|
|
|
- if self.parameters.has_key("sign"):
|
|
|
|
|
- self.parameters.pop("sign")
|
|
|
|
|
- sign = self.getSign(self.parameters)
|
|
|
|
|
- print self.parameters
|
|
|
|
|
- self.parameters["sign"] = sign #签名
|
|
|
|
|
- return self.arrayToXml(self.parameters)
|
|
|
|
|
-
|
|
|
|
|
- def getPrepayId(self):
|
|
|
|
|
- """获取prepay_id"""
|
|
|
|
|
- self.postXml()
|
|
|
|
|
- self.result = self.xmlToArray(self.response)
|
|
|
|
|
- print self.result
|
|
|
|
|
- return self.result
|
|
|
|
|
-
|
|
|
|
|
- def geth5url(self):
|
|
|
|
|
- """获取prepay_id"""
|
|
|
|
|
- self.postXml()
|
|
|
|
|
- self.result = self.xmlToArray(self.response)
|
|
|
|
|
- prepay_id = self.result["mweb_url"]
|
|
|
|
|
- return prepay_id
|
|
|
|
|
-
|
|
|
|
|
-def get_wx_unifiedorder(out_trade_no,total_fee,openid,trade_type="JSAPI"):
|
|
|
|
|
- par_obj = UnifiedOrder_pub()
|
|
|
|
|
- par_obj.setParameter('out_trade_no',out_trade_no)
|
|
|
|
|
- par_obj.setParameter('total_fee',total_fee)
|
|
|
|
|
- par_obj.setParameter('openid',openid)
|
|
|
|
|
- par_obj.setParameter('trade_type',trade_type)
|
|
|
|
|
- par_obj.createXml()
|
|
|
|
|
- return par_obj.getPrepayId()
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-if __name__ == "__main__":
|
|
|
|
|
- pass
|
|
|