title: 微信支付接口的开发(Python)踩坑总结
date: 2017-08-22 10:02:09
tags: Python
引言
首先,强推这个轮子,用Python语言来进行开发,包括最基本的工具类,还提供Django的demo供参考,看懂了直接拿来用就好了
https://github.com/Skycrab/weixin-knife
http://www.cnblogs.com/myloveblogs/p/6623708.html这篇文章总结的很好,通俗易懂
下面谈谈我自己的理解:
首先,根据官方文档显示:
1、登录,获取openid
这个不用说了,做小程序的人基本上都知道怎么获取吧,这里我就不展开了。
2、生成用户订单
这个由商户自己的服务端自行处理,这里给出我写的代码来参考(没有优化,只是简单地实现功能,求Python大佬轻喷)
@csrf_exempt
def saveOrder(request): # 保存订单中的商品
dict = {}
info = 'Data log save success'
try:
if request.method == 'POST':
req = simplejson.loads(request.body)
'''
从JSON数组中获取一系列的参数
'''
openid = req['openid']
area = req['area']
detail = req['detail']
name = req['name']
phone = req['phone']
queryset = models.Address.objects.filter(detail=detail, name=name, phone=phone, user_id=openid)
'''
新地址的情况
'''
if not queryset.exists():
aid = uuid1_hex()
address = models.Address(aid=aid, area=area, detail=detail, name=name, phone=phone, first=0,
user_id=openid)
address.save()
else:
# 查询与所填信息相关的模型
a = models.Address.objects.get(detail=detail, name=name, phone=phone, user_id=openid)
# 获取他的aid
aid = a.aid
count = req['count']
sum = req['sum']
vid = req['vid']
except_time = req['time']
commoditys = req['fields']
oid = uuid1_hex()
order = models.Order(user_id=openid, total=sum, village_id=vid, state=1, address_id=aid, oid=oid,except_time=except_time)
order.save()
'''
遍历载入商品信息
'''
for i in range(0, count):
comid = commoditys[i]['comid']
com_count = commoditys[i]['com_count']
c = models.Commodity.objects.get(cid=comid)
c.inventory = c.inventory - int(com_count)
c.sale = c.sale + int(com_count)
c.save()
o = models.Order.objects.get(oid=oid)
# o.commodity.add(c)
member = models.Membership(order=o, Commodity=c, com_count=com_count)
member.save()
except Exception:
import sys
info = "%s || %s" % (sys.exc_info()[0], sys.exc_info()[1])
dict['message'] = info
dict['create_at'] = str(ctime())
dict['order_id'] = str(oid)
json = simplejson.dumps(dict)
return HttpResponse(json)
3、调用统一下单api
官方解释:商户在小程序中先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易后调起支付。
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
通过XML的方式向微信后台发送请求信息
字段名 变量名 必填 类型 示例值 描述
小程序ID appid 是 String(32) wxd678efh567hg6787 微信分配的小程序ID
商户号 mch_id 是 String(32) 1230000109 微信支付分配的商户号
设备号 device_info 否 String(32) 013467007045764 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
签名类型 sign_type 否 String(32) HMAC-SHA256 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
商品描述 body 是 String(128) 腾讯充值中心-QQ会员充值 商品简单描述,该字段须严格按照规范传递,具体请见参数规定
商品详情 detail 否 String(6000) 单品优惠字段(暂未上线)
附加数据 attach 否 String(127) 深圳分店 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
商户订单号 out_trade_no 是 String(32) 20150806125346 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
货币类型 fee_type 否 String(16) CNY 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
总金额 total_fee 是 Int 888 订单总金额,单位为分,详见支付金额
终端IP spbill_create_ip 是 String(16) 123.12.12.123 APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
交易起始时间 time_start 否 String(14) 20091225091010 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
交易结束时间 time_expire 否 String(14) 20091227091010 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则注意:最短失效时间间隔必须大于5分钟
商品标记 goods_tag 否 String(32) WXG 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
通知地址 notify_url 是 String(256) http://www.weixin.qq.com/wxpay/pay.php 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
交易类型 trade_type 是 String(16) JSAPI 小程序取值如下:JSAPI,详细说明见参数规定
指定支付方式 limit_pay 否 String(32) no_credit no_credit--指定不能使用信用卡支付
用户标识 openid 否 String(128) oUpF8uMuAJO_M2pxb1Q9zNjWeS6o trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。
参考实例:
<mch_id>10000100</mch_id>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>JSAPI</trade_type>
那么我们就应该提供一个输出xml的方法:
def createXml(self):
"""生成接口参数xml"""
#检测必填参数
if any(self.parameters[key] is None for key in ("out_trade_no", "body", "total_fee", "notify_url", "trade_type")):
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["sign"] = self.getSign(self.parameters) #签名
return self.arrayToXml(self.parameters)
而当以下字段在return_code 和result_code都为SUCCESS的时候有返回
字段名 变量名 必填 类型 示例值 描述
交易类型 trade_type 是 String(16) JSAPI 调用接口提交的交易类型,取值如下:JSAPI,详细说明见参数规定
预支付交易会话标识 prepay_id 是 String(64) wx201410272009395522657a690389285100 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
可以看出,prepay_id是我们所需要返回到前端小程序的一个重要信息,因此我们需要这样一个方法:
def getPrepayId(self):
"""获取prepay_id"""
self.postXml()
self.result = self.xmlToArray(self.response)
prepay_id = self.result["prepay_id"]
return prepay_id
小程序调起支付API
小程序调起支付数据签名字段列表:
字段名 变量名 必填 类型 示例值 描述
小程序ID appId 是 String wxd678efh567hg6787 微信分配的小程序ID
时间戳 timeStamp 是 String 1490840662 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
随机串 nonceStr 是 String 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
数据包 package 是 String prepay_id=wx2017033010242291fcfe0db70013231072 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=wx2017033010242291fcfe0db70013231072
签名方式 signType 是 String MD5 签名算法,暂支持 MD5
举例说明:
paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D9B4E54AB1950F51E0649E8810ACD6
调用wx.requestPayment(OBJECT)发起微信支付:
wx.requestPayment(
{
'timeStamp': '',
'nonceStr': '',
'package': '',
'signType': 'MD5',
'paySign': '',
'success':function(res){},
'fail':function(res){},
'complete':function(res){}
})
至此,支付接口调用成功,大家快去尝试吧