转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/7649207.html

核心代码github地址:https://github.com/ygj0930/Python-WeiXinNativePay

一:项目准备

官方资料阅读:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5

微信支付需要用到微信公众平台账号、微信商户账号。

注册完成后,我们需要在公众平台、商户平台找到以下信息:

# ========支付相关配置信息===========
_APP_ID = ""; # 公众账号appid
_MCH_ID = ""; # 商户号
_API_KEY = ""; # 微信商户平台(pay.weixin.qq.com) -->账户设置 -->API安全 -->密钥设置,设置完成后把密钥复制到这里

然后,还需要配置以下信息:

    _UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; #url是微信下单api
_NOTIFY_URL = ""; # 微信支付结果回调接口,需要改为你的服务器上处理结果回调的方法路径
_CREATE_IP = 你的服务器地址; # 发起支付请求的ip

二:编写统一下单工具类

统一下单API资料阅读:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

1:全局常量配置所需信息

# ========支付相关配置信息===========
_APP_ID = ""; # 公众账号appid
_MCH_ID = ""; # 商户号
_API_KEY = ""; # 微信商户平台(pay.weixin.qq.com) -->账户设置 -->API安全 -->密钥设置,设置完成后把密钥复制到这里 _UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; #url是微信下单api
_NOTIFY_URL = ""; # 微信支付结果回调接口,需要改为你的服务器上处理结果回调的方法路径
_CREATE_IP = 你的服务器地址; # 发起支付请求的ip

然后定义统一下单方法。

2:在统一下单方法中,构造所需参数,其中,必须的参数有十个:

字段名 变量名 必填 类型 示例值 描述
公众账号ID appid String(32) wxd678efh567hg6787 微信支付分配的公众账号ID(企业号corpid即为此appId)
商户号 mch_id String(32) 1230000109 微信支付分配的商户号
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,长度要求在32位以内。推荐随机数生成算法
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 通过签名算法计算得出的签名值,详见签名生成算法
商品描述 body String(128) 腾讯充值中心-QQ会员充值

商品简单描述,该字段请按照规范传递,具体请见参数规定

商户订单号 out_trade_no String(32) 20150806125346 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。详见商户订单号
标价金额 total_fee Int 88 订单总金额,单位为分,详见支付金额
终端IP spbill_create_ip String(16) 123.12.12.123 APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
通知地址 notify_url String(256) http://www.weixin.qq.com/wxpay/pay.php 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
交易类型 trade_type String(16) JSAPI 取值如下:JSAPI,NATIVE,APP等,说明详见参数规定
        appid = self._APP_ID
mch_id = self._MCH_ID
key = self._API_KEY
nonce_str = str(int(round(time.time() * 1000)))+str(random.randint(1,999))+string.join(random.sample(['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'], 5)).replace(" ","") #拼接出随机的字符串即可,我这里是用 时间+随机数字+5个随机字母
spbill_create_ip = self._CREATE_IP
notify_url = self._NOTIFY_URL
trade_type = "NATIVE" #扫码支付类型 params = {}
params['appid'] = appid
params['mch_id'] = mch_id
params['nonce_str'] = nonce_str params['out_trade_no'] = 订单号参数.encode('utf-8') #客户端生成并传过来,参数必须用utf8编码,否则报错【订单号不是在服务器生成,而是在客户端生成的,否则客户端无法根据订单号轮询支付结果】 params['total_fee'] = 价格参数 #单位是分,必须是整数 params['spbill_create_ip'] = spbill_create_ip
params['notify_url'] = notify_url
params['body'] = 商品名参数.encode('utf-8') #中文必须用utf-8编码,否则xml格式错误
params['trade_type'] = trade_type

根据以上9个参数,生成签名:

        #生成签名
ret = []
for k in sorted(params.keys()):
if (k != 'sign') and (k != '') and (params[k] is not None):
ret.append('%s=%s' % (k, params[k]))
params_str = '&'.join(ret)
params_str = '%(params_str)s&key=%(partner_key)s'%{'params_str': params_str, 'partner_key': key}
#这里需要设置系统编码为utf-8,否则下面md5加密会报参数错误
reload(sys)
sys.setdefaultencoding('utf8')
params_str = hashlib.md5(params_str.encode('utf-8')).hexdigest()
sign = params_str.upper()
params['sign'] = sign

3:把上面10个参数拼接成XML格式字符串【微信统一下单API只接收和回传XML格式的数据

        #拼接参数的xml字符串
request_xml_str = '<xml>'
for key, value in params.items():
if isinstance(value, basestring):
request_xml_str = '%s<%s><![CDATA[%s]]></%s>' % (request_xml_str, key, value, key, )
else:
request_xml_str = '%s<%s>%s</%s>' % (request_xml_str, key, value, key, )
request_xml_str = '%s</xml>' % request_xml_str

4:向微信统一下单API发出请求,传递参数过去,获得回传结果后提取数据

 #向微信支付发出请求,接收回传数据
res = urllib2.Request(self._UFDODER_URL, data=request_xml_str)
res_data = urllib2.urlopen(res) #打开响应流
res_read = res_data.read() #读取响应流中数据
doc = xmltodict.parse(res_read) #数据是xml格式的,转为dict
return_code = doc['xml']['return_code'] #根据dict的层级,从顶层开始逐级访问提取所需内容
if return_code=="SUCCESS":
result_code = doc['xml']['result_code']
if result_code=="SUCCESS":
code_url = doc['xml']['code_url']
return code_url
else:
err_des = doc['xml']['err_code_des']
print "errdes==========="+err_des
else:
fail_des = doc['xml']['return_msg']
print "fail des============="+fail_des

返回结果

字段名 变量名 必填 类型 示例值 描述
返回状态码 return_code String(16) SUCCESS

SUCCESS/FAIL

此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断

返回信息 return_msg String(128) 签名失败

返回信息,如非空,为错误原因

签名失败

参数格式校验错误

以下字段在return_code为SUCCESS的时候有返回

字段名 变量名 必填 类型 示例值 描述
公众账号ID appid String(32) wx8888888888888888 调用接口提交的公众账号ID
商户号 mch_id String(32) 1900000109 调用接口提交的商户号
设备号 device_info String(32) 013467007045764 自定义参数,可以为请求支付的终端设备号等
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 微信返回的随机字符串
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 微信返回的签名值,详见签名算法
业务结果 result_code String(16) SUCCESS SUCCESS/FAIL
错误代码 err_code String(32) SYSTEMERROR 详细参见下文错误列表
错误代码描述 err_code_des String(128) 系统错误 错误信息描述

以下字段在return_code 和result_code都为SUCCESS的时候有返回

字段名 变量名 必填 类型 示例值 描述
交易类型 trade_type String(16) JSAPI 交易类型,取值为:JSAPI,NATIVE,APP等,说明详见参数规定
预支付交易会话标识 prepay_id String(64) wx201410272009395522657a690389285100 微信生成的预支付会话标识,用于后续接口调用中使用,该值有效期为2小时
二维码链接 code_url String(64) URl:weixin://wxpay/s/An4baqw trade_type为NATIVE时有返回,用于生成二维码,展示给用户进行扫码支付

错误码

名称 描述 原因 解决方案
NOAUTH 商户无此接口权限 商户未开通此接口权限 请商户前往申请此接口权限
NOTENOUGH 余额不足 用户帐号余额不足 用户帐号余额不足,请用户充值或更换支付卡后再支付
ORDERPAID 商户订单已支付 商户订单已支付,无需重复操作 商户订单已支付,无需更多操作
ORDERCLOSED 订单已关闭 当前订单已关闭,无法支付 当前订单已关闭,请重新下单
SYSTEMERROR 系统错误 系统超时 系统异常,请用相同参数重新调用
APPID_NOT_EXIST APPID不存在 参数中缺少APPID 请检查APPID是否正确
MCHID_NOT_EXIST MCHID不存在 参数中缺少MCHID 请检查MCHID是否正确
APPID_MCHID_NOT_MATCH appid和mch_id不匹配 appid和mch_id不匹配 请确认appid和mch_id是否匹配
LACK_PARAMS 缺少参数 缺少必要的请求参数 请检查参数是否齐全
OUT_TRADE_NO_USED 商户订单号重复 同一笔交易不能多次提交 请核实商户订单号是否重复提交
SIGNERROR 签名错误 参数签名结果不正确 请检查签名参数和方法是否都符合签名算法要求
XML_FORMAT_ERROR XML格式错误 XML格式错误 请检查XML参数格式是否正确
REQUIRE_POST_METHOD 请使用post方法 未使用post传递参数  请检查请求参数是否通过post方法提交
POST_DATA_EMPTY post数据为空 post数据不能为空 请检查post数据是否为空
NOT_UTF8 编码格式错误 未使用指定编码格式 请使用UTF-8编码格式

三:编写Controller层方法,主要有三个

1:处理客户端的二维码请求的方法

    def getWeChatQRCode(self, **kwargs):
order_id = kwargs.get('order_id') #获取客户端生成的订单号
goodsName = kwargs.get('goodsName') #获取商品信息
goodsPrice = int(float(kwargs.get('goodsPrice')) * 100) #获取价格,单位是分,需要是整数 toolUtil = PayToolUtil()
code_url=toolUtil.getPayUrl(order_id,goodsName,goodsPrice) #调用统一下单方法,获得支付订单的url链接 if code_url:
res_info = code_url
# 如果成功获得支付链接,则写入一条订单记录
#todo:自己的后台逻辑
else:
res_info = "二维码失效" #获取url失败,则二维码信息为失效 #根据res_info生成二维码,使用qrcode模块
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=1
)
qr.add_data(res_info) #二维码所含信息
img = qr.make_image() #生成二维码图片
byte_io = BytesIO()
img.save(byte_io, 'PNG') #存入字节流
byte_io.seek(0)
return http.send_file(byte_io, mimetype='image/png') #把字节流返回给客户端,解析得到二维码

2:处理微信支付平台支付结果回调

支付回调资料阅读:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7

支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答

   商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。

    def weChatQRCodeNotify(self, request, *args,**kwargs):
order_result_xml = http.request.httprequest.stream.read() #从请求流提取数据
doc = xmltodict.parse(order_result_xml) #解析得到的xml字符串,转为dict
out_trade_no = doc['xml']['out_trade_no'] #提取返回数据中的订单号
#todo:提取签名、支付金额等,验证签名是否正确、金额是否正确
#思路:在前面获取二维码时,生成了一条订单记录,订单应该保存下订单号、签名、金额等信息。在这里,根据回传的订单号查询数据库,得到对应的签名、金额进行验证即可
#最后,别忘了应答微信支付平台,防止重复发送数据
return '''
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
'''

3:处理客户端轮询请求

我们在客户端发起二维码请求后,获得二维码图片,向微信支付平台下了一张订单,那么这张订单的支付状态,客户端怎么知道呢?用轮询。

客户端生成一个随机字符串,作为订单号,把订单号作为参数,发起二维码请求,同时,另开一个线程,用这个订单号不断轮询服务器,查询该订单号对应的订单记录的支付状态,获得返回值后检查返回值:如果支付成功,则客户端执行后续操作(如:售卖机出货、页面跳转之类逻辑)并停止轮询;如果失败,也执行相应的用户提醒,如(余额不足等)并停止轮询。

    def weChatQRCodeHadPay(self, **kwargs):
order_id = kwargs.get('order_id') #获取订单号
#todo:根据订单号查询对应的订单记录状态,返回支付结果

Python实现微信扫码支付模式二(NativePay)的更多相关文章

  1. C# 微信扫码支付API (微信扫码支付模式二)

    一.SDK下载地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1,下载.NET C#版本: 二.微信相关设置:(微信扫码 ...

  2. JAVA微信扫码支付模式二功能实现完整例子

    概述 本例子实现微信扫码支付模式二的支付功能,应用场景是,web网站微信扫码支付.实现从点击付费按钮.到弹出二维码.到用户用手机微信扫码支付.到手机上用户付费成功.web网页再自动调整到支付成功后的页 ...

  3. .NET MVC结构框架下的微信扫码支付模式二 API接口开发测试

    直接上干货 ,我们的宗旨就是为人民服务.授人以鱼不如授人以渔.不吹毛求疵.不浮夸.不虚伪.不忽悠.一切都是为了社会共同进步,繁荣昌盛,小程序猿.大程序猿.老程序猿还是嫩程序猿,希望这个社会不要太急功近 ...

  4. .NET微信扫码支付模式二API接口开发测试

    主要实现微信扫码支付,官网的SDKdemo 就不要使用 一直不能调试通过的,还是自己按照API接口文档一步一步来实现,吐槽下微信一点责任感都木有,能不能demo搞个正常的吗,不要坑惨了一大群码农们有点 ...

  5. thinkphp5.0 微信扫码支付模式二

    仅供个人参考,方便大家. 一.1)https://pay.weixin.qq.com/index.php/core/home/login  复制此地址 打开微信商户平台. 2)下载安全操作证书(最好在 ...

  6. 微信公众号 扫码支付 模式二 demo

    扫码支付 本文附有代码,在下方,如果不熟悉场景的可以看看下面的场景介绍 场景介绍 官网介绍地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?ch ...

  7. 微信支付Native扫码支付模式二之CodeIgniter集成篇

    CI:3.0.5 微信支付API类库来自:https://github.com/zhangv/wechat-pay 请先看一眼官方场景及支付时序图:https://pay.weixin.qq.com/ ...

  8. Java之微信支付(扫码支付模式二)案例实战

    摘要:最近的一个项目中涉及到了支付业务,其中用到了微信支付和支付宝支付,在做的过程中也遇到些问题,所以现在总结梳理一下,分享给有需要的人,也为自己以后回顾留个思路. 一:微信支付接入准备工作: 首先, ...

  9. ThinkPHP 整合微信支付 扫码支付 模式二 图文教程

    这篇文章主要介绍扫码支付场景二. 目前有两种模式,模式一比模式二稍微复杂点,至于模式一与模式二的具体内容,流程,微信开发文档都有详细介绍,这里就不多说废话,接下来赶紧上教程! [title]下载SDK ...

随机推荐

  1. 用代码获取APP启动页图片

    用代码获取APP启动页图片 源码 - swift // // AppleSystemService.swift // Swift-Animations // // Created by YouXian ...

  2. linux find 10天内改动过的文件

    find . -name "*.h" -mtime -10 -type f -print find . -regex ".*\.\(c\|h\)" -mtime ...

  3. Orchard模块开发全接触3:分类的实现及内容呈现(Display)

    一:分类用现有技术怎么实现? 实际就是创建 Query 和 Projection,如果不知道怎么做,参考:Orchard之在前台显式一个属于自己的列表(在这篇里,还进行了稍稍拓展),当然,基础的知道, ...

  4. Java正则表达式教程及示例

    本文由 ImportNew - ImportNew读者 翻译自 journaldev.欢迎加入翻译小组.转载请见文末要求. [感谢 @CuGBabyBeaR  的热心翻译.如果其他朋友也有不错的原创或 ...

  5. Spring Scheduler定时任务 + Quartz

    原文地址: https://blog.csdn.net/revitalizing/article/details/61420556 版权声明:本文为博主原创文章,未经博主允许不得转载. https:/ ...

  6. JDBC 查询的三大参数

    本文转载至 http://blog.csdn.net/turkeyzhou/article/details/5115228 DBC1.0 .JDBC2.0 .JDBC3.0 中分别用以下方法创建Sta ...

  7. 第三章 Typescript 基本数据类型

    Typescript 基本数据类型 一.基本数据类型 Boolean Number String Array Tuple Enum Any Void Null 和 Undefined Never 二. ...

  8. SharePoint 2013 开启访问请求 链接丢失

    关于SharePoint 2013 开启访问请求的做法其实很简单,比如http://www.cnblogs.com/jianyus/archive/2014/06/21/3799386.html 这篇 ...

  9. Android adb你真的会用吗?

    引言 本文基于Android官方文档, 以及个人工作的使用经验, 总结下adb的常用用法, 备忘. 1, adb简介 adb全名Andorid Debug Bridge. 顾名思义, 这是一个Debu ...

  10. 在Android上山寨了一个Ios9的LivePhotos,放Github上了

    9月10号的凌晨上演了一场IT界的春晚,相信很多果粉(恩,如果你指坚果,那我也没办法了,是在下输了)都熬夜看了吧,看完打算去医院割肾了吧.在发布会上发布了游戏机 Apple TV,更大的砧板 Ipad ...