基于Django项目的Python版微信支付-H5支付方式

本文详细讲解Python语言进行公众号开发时,参考开发者文档进行H5支付,并给出具体的代码:

一.开发流程

技术图片

业务流程说明:

1、用户在商户侧完成下单,使用微信支付进行支付

2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB

3、统一下单接口返回支付相关参数给商户后台,如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页

4、中间页进行H5权限的校验,安全性检查(此处常见错误请见下文)

5、如支付成功,商户后台会接收到微信侧的异步通知

6、用户在微信支付收银台完成支付或取消支付,返回商户页面(默认为返回支付发起页面)

7、商户在展示页面,引导用户主动发起支付结果的查询

8,9、商户后台判断是否接到收微信侧的支付结果通知,如没有,后台调用我们的订单查询接口确认订单状态

10、展示最终的订单支付结果给用户

二.具体代码

1.需准备的参数

import jsonimport hashlibfrom urllib.parse import urlencodefrom random import Randomimport requestsfrom django.http import HttpResponse, HttpResponseRedirectnotify_url = "....../wx_result_H/" # 回调函数,完整路由,服务器要带上域名,对应的视图是下面3中的回调函数trade_type = MWEB # 交易方式APP_ID = "wx......" # 公众账号的appidMCH_ID = "......" # 商户号API_KEY = "......" # 微信商户平台(pay.weixin.qq.com) -->账户设置 -->API安全 -->密钥设置,设置完成后把密钥复制到这里UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder" # 该url是微信下单apiCREATE_IP = ...... # 服务器IP

2.调用支付接口

def wx_pay_H(request): # data = json.loads(request.body) # print(request.body) total_price = 0.01 # 订单总价 order_name = 商品费用 # 订单名字 order_detail = 商品费用 # 订单描述 order_id = 20200411234567 # 自定义的订单号 data_dict = wxpay(order_id, order_name, order_detail, total_price) # 如果请求成功 if data_dict.get(return_code) == SUCCESS: # 请求成功跳转微信中间页面进行支付 mweb_url = data_dict.get(mweb_url, "") if mweb_url: # print(mweb_url, type(mweb_url)) return HttpResponseRedirect(urlencode({"mweb_url": mweb_url})) s = { "code": 1001, "msg": "获取失败", "data": "" } s = json.dumps(s, ensure_ascii=False) return HttpResponse(s)

3.支付后回调接口

def wx_result_n(request): data_dict = trans_xml_to_dict(request.body) # 回调数据转字典 print(支付回调结果, data_dict) sign = data_dict.pop(sign) # 取出签名 back_sign = get_sign(data_dict, API_KEY) # 计算签名 # 验证签名是否与回调签名相同 if sign == back_sign and data_dict[return_code] == SUCCESS: order_no = data_dict[out_trade_no] # 处理支付成功逻辑,根据订单号修改后台数据库状态 # 返回接收结果给微信,否则微信会每隔8分钟发送post请求 return HttpResponse(trans_dict_to_xml({return_code: SUCCESS, return_msg: OK})) return HttpResponse(trans_dict_to_xml({return_code: FAIL, return_msg: SIGNERROR}))

4.工具函数

def random_str(randomlength=8): """ 生成随机字符串 :param randomlength: 字符串长度 :return: """ strs = ‘‘ chars = AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789 length = len(chars) - 1 random = Random() for i in range(randomlength): strs += chars[random.randint(0, length)] print(strs) return strsdef wxpay(order_id, order_name, order_price_detail, order_total_price): nonce_str = random_str() # 拼接出随机的字符串即可,我这里是用 时间+随机数字+5个随机字母 total_fee = int(float(order_total_price) * 100) # 付款金额,单位是分,必须是整数 print(total_fee) params = { appid: APP_ID, # APPID mch_id: MCH_ID, # 商户号 nonce_str: nonce_str, # 随机字符串 out_trade_no: order_id, # 订单编号,可自定义 total_fee: total_fee, # 订单总金额 spbill_create_ip: CREATE_IP, # 自己服务器的IP地址 notify_url: notify_url, # 回调地址,微信支付成功后会回调这个url,告知商户支付结果 body: order_name, # 商品描述 detail: order_price_detail, # 商品描述 trade_type: trade_type, # 扫码支付类型 } sign = get_sign(params, API_KEY) # 获取签名 params[sign] = sign # 添加签名到参数字典 xml = trans_dict_to_xml(params) # 转换字典为XML response = requests.request(post, UFDODER_URL, data=xml.encode()) # 以POST方式向微信公众平台服务器发起请求 data_dict = trans_xml_to_dict(response.content) # 将请求返回的数据转为字典 print(data_dict) return data_dictdef get_sign(data_dict, key): """ 签名函数 :param data_dict: 需要签名的参数,格式为字典 :param key: 密钥 ,即上面的API_KEY :return: 字符串 """ params_list = sorted(data_dict.items(), key=lambda e: e[0], reverse=False) # 参数字典倒排序为列表 params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + &key= + key # 组织参数字符串并在末尾添加商户交易密钥 md5 = hashlib.md5() # 使用MD5加密模式 md5.update(params_str.encode(utf-8)) # 将参数字符串传入 sign = md5.hexdigest().upper() # 完成加密并转为大写 print(sign) return signdef trans_dict_to_xml(data_dict): """ 定义字典转XML的函数 :param data_dict: :return: """ data_xml = [] for k in sorted(data_dict.keys()): # 遍历字典排序后的key v = data_dict.get(k) # 取出字典中key对应的value if k == detail and not v.startswith(<![CDATA[): # 添加XML标记 v = <![CDATA[{}]]>.format(v) data_xml.append(<{key}>{value}</{key}>.format(key=k, value=v)) return <xml>{}</xml>.format(‘‘.join(data_xml)) # 返回XMLdef trans_xml_to_dict(data_xml): """ 定义XML转字典的函数 :param data_xml: :return: """ data_dict = {} try: import xml.etree.cElementTree as ET except ImportError: import xml.etree.ElementTree as ET root = ET.fromstring(data_xml) for child in root: data_dict[child.tag] = child.text return data_dict

mamicode_m_adload('8004');

评论(
0

mamicode_m_adload('8005');

相关文章