微信支付有条件,必须申请公众号为服务号
settings配置
# 商户id
WEIXIN_SHANG_ID = "xxxx"
微信支付,获取二维码
from PIL import Image,ImageOps
from users.models import UserProfile
from datetime import datetime, timedelta
import qrcode
from io import BytesIO
from .PayToolUtil import *
from django import http
from django.views.decorators.csrf import csrf_exempt
def getWeChatQRCode(request):
order_id = request.GET.get("order_id")
order = OrderInfo.objects.filter(o_id=order_id).first()
if order:
# 获取充值类型
if order.o_type == "vip":
tishi = "开通/续费VIP"
else:
tishi = "积分充值"
# 获取客户端生成的订单号
goodsName = tishi # 获取商品信息
goodsPrice = int(order.o_total_price) * 100 # 获取价格,单位是分,需要是整数
# goodsPrice = 1
toolUtil = PayToolUtil()
code_url = toolUtil.getPayUrl(order_id, goodsName, goodsPrice) # 调用统一下单方法,获得支付订单的url链接
# result = toolUtil.getPayUrl(order_id, goodsName, goodsPrice)
# if result:
# code_url = result[0]
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() # 生成二维码图片
img = img.convert("RGBA")
# 添加logo
icon = Image.open('/home/pythonProjects/media/logo/logo.png')
# icon = Image.open('D://Users/Administrator/Desktop/项目文件/前后端分离Django/ucasonline/media/logo/logo.png')
w, h = img.size
# 可调节logo大小
factor = 3
size_w = int(w / factor)
size_h = int(h / factor)
icon_w, icon_h = icon.size
if icon_w > size_w:
icon_w = size_w
if icon_h > size_h:
icon_h = size_h
icon = icon.resize((icon_w, icon_h), Image.ANTIALIAS)
w = int((w - icon_w) / 2)
h = int((h - icon_h) / 2)
icon = icon.convert("RGBA")
img.paste(icon, (w, h), icon)
byte_io = BytesIO()
img.save(byte_io, 'PNG') # 存入字节流
byte_io.seek(0)
response = http.HttpResponse(byte_io, content_type="image/png")
return response
支付回调
from rest_framework import status
# 微信支付回调
@csrf_exempt
def weChatQRCodeNotify(request):
order_result_xml = request.body # 从请求流提取数据
order_result_xml = str(order_result_xml,encoding="utf-8")
doc = xmltodict.parse(order_result_xml) # 解析得到的xml字符串,转为dict
out_trade_no = doc['xml']['out_trade_no'] # 提取返回数据中的订单号
total_fee = doc['xml']['total_fee']
# todo:提取签名、支付金额等,验证签名是否正确、金额是否正确
# 思路:在前面获取二维码时,生成了一条订单记录,订单应该保存下订单号、签名、金额等信息。在这里,根据回传的订单号查询数据库,得到对应的签名、金额进行验证即可
order = OrderInfo.objects.filter(o_id=out_trade_no).first()
if order:
user = UserProfile.objects.filter(id=order.user.id).first()
if user:
# if int(order.o_total_price) == int(total_fee) * 100:
if int(total_fee) == 1:
# 判断支付状态
if order.o_pay == False:
# 写入支付完成的逻辑
order.o_pay = True
order.save()
# 最后,别忘了应答微信支付平台,防止重复发送数据
return http.HttpResponse('''
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
''',status=status.HTTP_200_OK )
# return http.HttpResponse({"return_code":"SUCCESS","return_msg":"OK"})
else:
return http.HttpResponse('''<xml><return_code><![CDATA[FAIL]]></return_code></xml>''', status=status.HTTP_400_BAD_REQUEST)
else:
return http.HttpResponse('''<xml><return_code><![CDATA[FAIL]]></return_code></xml>''',status=status.HTTP_400_BAD_REQUEST)
else:
return http.HttpResponse('''<xml><return_code><![CDATA[FAIL]]></return_code></xml>''',status=status.HTTP_400_BAD_REQUEST)
前端调接口:支付结果轮询
def weChatQRCodeHadPay(request):
order_id = request.GET.get('order_id') # 获取订单号
# todo:根据订单号查询对应的订单记录状态,返回支付结果
order = OrderInfo.objects.filter(o_id=order_id).first()
if order:
if order.o_pay == True:
return http.JsonResponse({"type": "object","msg": "success","status": "0", "data": { "error": "订单支付成功"}})
else:
return http.JsonResponse({"type": "object", "msg": "success", "status": "1", "data": {"error": "未成功"}})
else:
return http.JsonResponse({"type": "object","msg": "success","status": "1", "data": { "error": "订单不存在"}})
另需:
import socket
import random
import json
import hashlib
import xmltodict
import sys
class PayToolUtil(object):
# ========支付相关配置信息===========
_APP_ID = "XXXXX"; # 公众账号appid
_MCH_ID = "XXXX"; # 商户号
_API_KEY = "XXXXX"; # key设置路径:微信商户平台(pay.weixin.qq.com) -->账户设置 -->API安全 -->密钥设置
# 有关url
_host_name = socket.gethostname()
_ip_address = socket.gethostbyname(_host_name)
_CREATE_IP = _ip_address; # 发起支付的ip
_UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
_NOTIFY_URL = "http://www.XXXX/vuser/wei/checkpay"; # 微信支付结果回调的处理方法
def getPayUrl(self,orderid,goodsName,goodsPrice,**kwargs):
'''
向微信支付端发出请求,获取url
'''
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))+"".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(" ","") #生成随机字符串
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'] = orderid
# params['out_trade_no'] = orderid.encode('utf-8') #客户端生成并传过来,参数必须用utf8编码,否则报错
params['total_fee'] = goodsPrice #单位是分,必须是整数
params['spbill_create_ip'] = spbill_create_ip
params['notify_url'] = notify_url
params['body'] = goodsName
# params['body'] = goodsName.encode('utf-8') #中文必须用utf-8编码,否则xml格式错误
params['trade_type'] = trade_type
#生成签名
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}
params_str = hashlib.md5(params_str.encode('utf-8')).hexdigest()
sign = params_str.upper()
params['sign'] = sign
#拼接参数的xml字符串
request_xml_str = '<xml>'
for key, value in params.items():
if isinstance(value,str):
# 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
#向微信支付发出请求,并提取回传数据
res = urllib.request.Request(self._UFDODER_URL, data=request_xml_str.encode("utf-8"))
res_data = urllib.request.urlopen(res)
res_read = res_data.read()
doc = xmltodict.parse(res_read)
return_code = doc['xml']['return_code']
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)
感谢大佬资源分享,仅作记录