利用Python开发微信支付的注意事项


Posted in Python onAugust 19, 2016

前言

微信支付是由微信及财付通联合推出的移动支付创新产品。如今,随着微信支付的全面开放,相关需求也越来越多,很多开发人员进行微信支付开发及商家申请微信支付时,面临着诸多疑惑。

要想开发顺利进行,首先要对业务流程有个清晰的认识。这里以微信公众号支付为例,因此也借用微信支付官方文档中的业务流程图:

利用Python开发微信支付的注意事项

接下来来关注几个开发过程中的关键点,包括:

      1、生成商户订单与调用统一下单 API

      2、微信服务器交互的数据格式

      3、公众号支付下网页内通过 JS-API 调起支付

      4、异步通知商户支付结果(回调) 

一、生成商户订单与调用统一下单 API

这对应业务流程中的第 4 和 第 5 步,商户后台首先为用户生成订单,然后调用微信的【统一下单】接口向微信支付系统提交订单。这里有一个关键点就是签名的生成

简单来讲分为以下几个步骤:

      1、将所有有效参数以“k=v”的形式进行拼接,有效参数是指非空参数,也就是说如果参数为空,则不参与签名;

      2、将所有的“k=v”对用“&”连接,得到“k1=v1&k2=v2&k3=v3”这样的字符串;

      3、将微信支付 API 密钥 拼接在最后,如“k1=v1&k2=v2&k3=v3&key=secret”;

      4、对整体进行 MD5 运算,即得到签名。

这种签名方法有一个高大上的名字叫做 HMAC(Hash-based Message Authentication Code,基于哈希的消息码)。基于此思路,可以实现如下签名方法:

def gen_sign(params, key):
  """
  签名生成函数
 
  :param params: 参数,dict 对象
  :param key: API 密钥
  :return: sign string
  """
 
  param_list = []
  for k in sorted(params.keys()):
    v = params.get(k)
    if not v:
      # 参数的值为空不参与签名
      continue
    param_list.append('{0}={1}'.format(k, v))
  # 在最后拼接 key
  param_list.append('key={}'.format(key))
  # 用 & 连接各 k-v 对,然后对字符串进行 MD5 运算
  return md5('&'.join(param_list).encode('utf8')).hexdigest()

参与签名的参数中有一个随机字符串,在 Python 中有很多方法,当然也可以利用 uuid 库来生成:

def gen_nonce_str():
  """
  生成随机字符串,有效字符a-zA-Z0-9
 
  :return: 随机字符串
  """
 
  return ''.join(str(uuid.uuid4()).split('-'))

 

二、微信服务器交互的数据格式

微信服务器与商户服务器之间采用 XML 格式进行交互,这就涉及到与语言原生数据类型进行转换以方便处理。交互的数据参数都是 key-value 的形式,因此在 Python 中使用字典会更加方便。而要解析 XML,也有一大把第三方库供使用,比如 BeautifulSoup

以下是具体实现:

def trans_xml_to_dict(xml):
  """
  将微信支付交互返回的 XML 格式数据转化为 Python Dict 对象
 
  :param xml: 原始 XML 格式数据
  :return: dict 对象
  """
 
  soup = BeautifulSoup(xml, features='xml')
  xml = soup.find('xml')
  if not xml:
    return {}
 
  # 将 XML 数据转化为 Dict
  data = dict([(item.name, item.text) for item in xml.find_all()])
  return data
 
 
def trans_dict_to_xml(data):
  """
  将 dict 对象转换成微信支付交互所需的 XML 格式数据
 
  :param data: dict 对象
  :return: xml 格式数据
  """
 
  xml = []
  for k in sorted(data.keys()):
    v = data.get(k)
    if k == 'detail' and not v.startswith('<![CDATA['):
      v = '<![CDATA[{}]]>'.format(v)
    xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
  return '<xml>{}</xml>'.format(''.join(xml))

注意 detail 参数,即商品详情,其值为 JSON 格式,在转换为 XML 数据时应前注意使用 CDATA 标签将其保护起来。

如:

<detail><![CDATA[{"goods_detail": [{"wxpay_goods_id": "10010001", "price": 1, "goods_num": 1, "goods_name": "\\u82f9\\u679c", "goods_id": "10010001"}, {"wxpay_goods_id": "10010002", "price": 1, "goods_num": 1, "goods_name": "\\u9999\\u8549", "goods_id": "10010002"}]}]]></detail>

三、公众号支付下网页内通过 JS-API 调起支付

这一点对应业务流程中的第 7 步。之所以提及它是因为微信官方文档在此给开发者挖了一个坑(至少截至我在写这篇文章时是的),就是在“网页端调起支付API”中关于 JS 的示例代码是采用的 WeixinJSBridge,这在很早以前就是 Deprecated 的“玩意儿”,如今更是已经不可用了。正确的做法是使用 JS-SDK,可以参考微信公众号的 wiki。

使用 JS-SDK 前需要先调用 config,这里也包含一个签名,但注意这个签名与之前微信支付的签名并不相干。其首先需要用微信公众号的 APPID 和 APPKEY 来换取 access_token,然后用该 access_token 调用 JS-SDK 换取 ticket 的接口得到 ticket,最后再使用该 ticket 和用户当前页面的 URI 通过 sha1 运算生成签名。

在此之后,即可调用 wx.chooseWXPay 来调起支付,这里也有一个坑:timestamp。wx.chooseWXPay 中的参数要求 timestamp 是全小写。而微信支付中签名时要求 timestamp 中的“s”是大写。真的是要傻傻分不清了。 

四、异步通知商户支付结果(回调)

最后是关于异步回调,对应业务流程中的第 10 步。在用户支付操作完成后,微信服务器会通过回调的形式告知商户服务器支付结果。回调的地址与【统一下单】中定义的 notify_url 一致。当接收到回调时,首先应验证签名的有效性以保证“来源可靠”,然后可以通过回调中所带的 openid、out_trade_no 等来定位唯一订单。

总结

微信支付还有很多种形式,在业务流程上也不尽相同。不过只要能玩转其中一种,其他的也基本来说能很快实现。另外,支付功能的实现涉及业务流程中的安全性,因此一定要注意理清业务流程,并卡好各个关键结点。以上就是本文的全部内容,希望对大家使用Python开发微信支付能有所帮助。

Python 相关文章推荐
python zip文件 压缩
Dec 24 Python
Python简单日志处理类分享
Feb 14 Python
python访问系统环境变量的方法
Apr 29 Python
python检查序列seq是否含有aset中项的方法
Jun 30 Python
一步步解析Python斗牛游戏的概率
Feb 12 Python
python学习之matplotlib绘制散点图实例
Dec 09 Python
Python实现多进程的四种方式
Feb 22 Python
python文档字符串(函数使用说明)使用详解
Jul 30 Python
tensorflow mnist 数据加载实现并画图效果
Feb 05 Python
python argparse模块通过后台传递参数实例
Apr 20 Python
Python循环之while无限迭代
Apr 30 Python
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
May 04 Python
Python用模块pytz来转换时区
Aug 19 #Python
教你用python3根据关键词爬取百度百科的内容
Aug 18 #Python
利用Python爬取可用的代理IP
Aug 18 #Python
总结用Pdb库调试Python的方式及常用的命令
Aug 18 #Python
Python实现命令行通讯录实例教程
Aug 18 #Python
Python采用Django开发自己的博客系统
Sep 29 #Python
浅析Python中元祖、列表和字典的区别
Aug 17 #Python
You might like
phplock(php进程锁) v1.0 beta1
2009/11/24 PHP
php empty函数判断mysql表单是否为空
2010/04/12 PHP
PHP Curl出现403错误的解决办法
2014/05/29 PHP
PHP register_shutdown_function()函数的使用示例
2015/06/23 PHP
浅谈php中的循环while、do...while、for、foreach四种循环
2016/11/05 PHP
php加速缓存器opcache,apc,xcache,eAccelerator原理与配置方法实例分析
2020/03/02 PHP
Javascript this 关键字 详解
2014/10/22 Javascript
jQuery异步获取json数据方法汇总
2014/12/22 Javascript
js+CSS实现弹出居中背景半透明div层的方法
2015/02/26 Javascript
jQuery插件slick实现响应式移动端幻灯片图片切换特效
2015/04/12 Javascript
JS使用正则表达式实现关键字替换加粗功能示例
2016/08/03 Javascript
js字符串引用的两种方式(必看)
2016/09/18 Javascript
js实现简易聊天对话框
2017/08/17 Javascript
微信小程序实现分享朋友圈的图片功能示例
2019/01/18 Javascript
Vue 动态组件与 v-once 指令的实现
2019/02/12 Javascript
微信小程序textarea层级过高的解决方法
2019/03/04 Javascript
浅谈Vue CLI 3结合Lerna进行UI框架设计
2019/04/14 Javascript
记录一次开发微信网页分享的步骤
2019/05/07 Javascript
jQuery事件绑定和解绑、事件冒泡与阻止事件冒泡及弹出应用示例
2019/05/13 jQuery
javascript关于“时间”的一次探索
2019/07/24 Javascript
微信小程序实现页面跳转传递参数(实体,对象)
2019/08/12 Javascript
关于vue.js中实现方法内某些代码延时执行
2019/11/14 Javascript
Vue 实现分页与输入框关键字筛选功能
2020/01/02 Javascript
JS实现手写 forEach算法示例
2020/04/29 Javascript
解决Vue的文本编辑器 vue-quill-editor 小图标样式排布错乱问题
2020/08/03 Javascript
在Django的模型中添加自定义方法的示例
2015/07/21 Python
python中从str中提取元素到list以及将list转换为str的方法
2018/06/26 Python
Django认证系统实现的web页面实现代码
2019/08/12 Python
python rsa-oaep加密的示例代码
2020/09/23 Python
世界上最大的各式箱包网络零售店:eBag
2016/07/21 全球购物
ONLY瑞典官网:世界知名服装品牌
2018/06/19 全球购物
澳大利亚最受欢迎的美发和美容在线商店:Catwalk
2018/12/12 全球购物
汽车销售顾问求职自荐信
2014/01/01 职场文书
党支部群众路线整改措施思想汇报
2014/10/10 职场文书
儿园租房协议书范本
2014/12/02 职场文书
带你学习MySQL执行计划
2021/05/31 MySQL