Python實(shí)現(xiàn)微信支付

Python實(shí)現(xiàn)微信支付

一、準(zhǔn)備環(huán)境

1、要有微信公眾號(hào),商戶平臺(tái)賬號(hào)

https://pay.weixin.qq.com/wiki/doc/api/index.html

2、支持的支付方式有

1540176727044.png

3、備案域名

選擇掃碼支付,如果使用模式二則不需要域名,只需要可訪問的ip地址就行。

一、掃碼支付

1、使用模式一生成支付二維碼

這個(gè)二維碼是沒有時(shí)間限制的。

create_qrcode.html

創(chuàng)建二維碼頁面

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>生成掃碼支付二維碼</title>
</head>
<body>
?
<form method="post" action="/wxpay/QRcode/" >
輸入手機(jī)號(hào):<input name="phone"/>
<input id="submit" type="submit"  value="生成二維碼">
 <br>
 {% if img_url %}
 <img src="{{ img_url }}" style="width: 200px;height: 200px"/>
 {% endif %}
<br>
{{ url }}
</form>
</body>
</html></pre>

pay_settings.py

#微信支付配置
# ========支付相關(guān)配置信息===========
import random
import time
import hashlib
from random import Random
?
import qrcode
from bs4 import BeautifulSoup
?
APP_ID = "xxx"  # 你公眾賬號(hào)上的appid
MCH_ID = "xxx"  # 你的商戶號(hào)
API_KEY = "xxx"  # 微信商戶平臺(tái)(pay.weixin.qq.com) -->賬戶設(shè)置 -->API安全 -->密鑰設(shè)置,設(shè)置完成后把密鑰復(fù)制到這里
APP_SECRECT = "xxx"
UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"  # 該url是微信下單api
?
NOTIFY_URL = "http://xxx/"  # 微信支付結(jié)果回調(diào)接口,需要改為你的服務(wù)器上處理結(jié)果回調(diào)的方法路徑
CREATE_IP = 'xxx'  # 你服務(wù)器的IP
?
?
def random_str(randomlength=8):
 """
 生成隨機(jī)字符串
 :param randomlength: 字符串長(zhǎng)度
 :return:
 """
 str = ''
 chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
 length = len(chars) - 1
 random = Random()
 for i in range(randomlength):
 str+=chars[random.randint(0, length)]
 return str
?
?
def order_num(phone):
 """
 生成掃碼付款訂單號(hào)
 :param phone: 手機(jī)號(hào)
 :return:
 """
 local_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
 result = phone + 'T' + local_time + random_str(5)
 return result
?
?
def get_sign(data_dict, key):
 # 簽名函數(shù),參數(shù)為簽名的數(shù)據(jù)和密鑰
 params_list = sorted(data_dict.items(), key=lambda e: e[0], reverse=False)  # 參數(shù)字典倒排序?yàn)榱斜? params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=' + key
 # 組織參數(shù)字符串并在末尾添加商戶交易密鑰
 md5 = hashlib.md5()  # 使用MD5加密模式
 md5.update(params_str.encode('utf-8'))  # 將參數(shù)字符串傳入
 sign = md5.hexdigest().upper()  # 完成加密并轉(zhuǎn)為大寫
 return sign
?
?
def trans_dict_to_xml(data_dict):  # 定義字典轉(zhuǎn)XML的函數(shù)
 data_xml = []
 for k in sorted(data_dict.keys()):  # 遍歷字典排序后的key
 v = data_dict.get(k)  # 取出字典中key對(duì)應(yīng)的value
 if k == 'detail' and not v.startswith('<![CDATA['):  # 添加XML標(biāo)記
 v = '<![CDATA[{}]]>'.format(v)
 data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
 return '<xml>{}</xml>'.format(''.join(data_xml)).encode('utf-8')  # 返回XML,并轉(zhuǎn)成utf-8,解決中文的問題
?
?
def trans_xml_to_dict(data_xml):
 soup = BeautifulSoup(data_xml, features='xml')
 xml = soup.find('xml')  # 解析XML
 if not xml:
 return {}
 data_dict = dict([(item.name, item.text) for item in xml.find_all()])
 return data_dict
?
?
def wx_pay_unifiedorde(detail):
 """
 訪問微信支付統(tǒng)一下單接口
 :param detail:
 :return:
 """
 detail['sign'] = get_sign(detail, API_KEY)
 # print(detail)
 xml = trans_dict_to_xml(detail)  # 轉(zhuǎn)換字典為XML
 response = requests.request('post', UFDODER_URL, data=xml)  # 以POST方式向微信公眾平臺(tái)服務(wù)器發(fā)起請(qǐng)求
 # data_dict = trans_xml_to_dict(response.content)  # 將請(qǐng)求返回的數(shù)據(jù)轉(zhuǎn)為字典
 return response.content
?
?
def pay_fail(err_msg):
 """
 微信支付失敗
 :param err_msg: 失敗原因
 :return:
 """
 data_dict = {'return_msg': err_msg, 'return_code': 'FAIL'}
 return trans_dict_to_xml(data_dict)
?
?
def create_qrcode(phone,url):
 """
 生成掃碼支付二維碼
 :param phone: 手機(jī)號(hào)
 :param url: 支付路由
 :return:
 """
 img = qrcode.make(url)  # 創(chuàng)建支付二維碼片
 # 你存放二維碼的地址
 img_url = r'media/QRcode' + '/' + phone + '.png'
 img.save(img_url)
 return img_url

views.py

import render
from django.http import HttpResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from pay_settings.py import *
?
class Wxpay_QRccode(View):
 """
 生成微信支付二維碼
 """
 def get(self, request, *args, **kwargs):
 return render(request, 'create_qrcode.html')
?
 def post(self, request, *args, **kwargs):
 """
 # 生成可掃碼支付的二維碼
 :param request:
 :param args:
 :param kwargs:
 :return:
 """

 phone = request.data.get('phone', None)
 if not phone:
 return HttpResponse('手機(jī)號(hào)獲取失敗')
 paydict = {
 'appid': APP_ID,
 'mch_id': MCH_ID,
 'nonce_str': random_str(phone),
 'product_id': phone,  # 商品id,可自定義
 'time_stamp': int(time.time()),
 }
 paydict['sign'] = get_sign(paydict, API_KEY)
 url = "weixin://wxpay/bizpayurl?appid=%s&mch_id=%s&nonce_str=%s&product_id=%s&time_stamp=%s&sign=%s" \
 % (paydict['appid'], paydict['mch_id'], paydict['nonce_str'], paydict['product_id'], paydict['time_stamp'], paydict['sign'])

 # 可以直接在微信中點(diǎn)擊該url,如果有錯(cuò)誤,微信會(huì)彈出提示框,如果是掃碼,如果失敗,什么提示都沒有,不利于調(diào)試
 print(url)
 # 創(chuàng)建二維碼
 img_url = create_qrcode(url)
?
 return render(request, 'create_qrcode.html', context={'img_url': img_url})
?
?
@method_decorator(csrf_exempt, name='dispatch')
class Wxpay_ModelOne_pay(View):
 """
 使用微信掃一掃掃描二維碼,微信系統(tǒng)會(huì)自動(dòng)回調(diào)此路由,Post請(qǐng)求
 """
?
 def post(self, request, *args, **kwargs):
 """
 掃描二維碼后,微信系統(tǒng)回調(diào)的地址處理
 微信傳來的參數(shù)格式經(jīng)trans_xml_to_dict()轉(zhuǎn)成字典
 {'openid': 'xxx',
 'is_subscribe': 'Y',
 'mch_id': 'xxx',
 'nonce_str': 'xxx',
 'sign': 'xxx',
 'product_id': 'xxx',
 'appid': 'xxx'}
?
 :param request:
 :param args:
 :param kwargs:
 :return:
 """
?
 data_dict = trans_xml_to_dict(request.body)  # 回調(diào)數(shù)據(jù)轉(zhuǎn)字典
 sign = data_dict.pop('sign')  # 取出簽名
 key = API_KEY  # 商戶交易密鑰
 back_sign = get_sign(data_dict, key)  # 計(jì)算簽名
?
 if sign == back_sign:  # 驗(yàn)證簽名是否與回調(diào)簽名相同
 total_fee = 1  # 付款金額,單位是分,必須是整數(shù)
?
 params = {
 'appid': APP_ID,  # APPID
 'mch_id': MCH_ID,  # 商戶號(hào)
 'nonce_str': random_str(16),  # 隨機(jī)字符串
 'out_trade_no': order_num(data_dict['product_id']),  # 訂單編號(hào)
 'total_fee': total_fee,  # 訂單總金額
 'spbill_create_ip': CREATE_IP,  # 發(fā)送請(qǐng)求服務(wù)器的IP地址
 'notify_url': NOTIFY_URL,
 'body': 'xxx公司',   # 商品描述
 'detail': 'xxx商品',  # 商品詳情
 'trade_type': 'NATIVE',  # 掃碼支付類型
 }
 # 調(diào)用微信統(tǒng)一下單支付接口url
 notify_result = wx_pay_unifiedorde(params)
 #print(trans_xml_to_dict(notify_result))
 return HttpResponse(notify_result)
?
 return HttpResponse(pay_fail('交易信息有誤,請(qǐng)重新掃碼'))
?

2、使用模式二生成支付二維碼

這個(gè)二維碼是有時(shí)間限制的。

模式二與模式一相比,流程更為簡(jiǎn)單,不依賴設(shè)置的回調(diào)支付URL。商戶后臺(tái)系統(tǒng)先調(diào)用微信支付的統(tǒng)一下單接口,微信后臺(tái)系統(tǒng)返回鏈接參數(shù)code_url,商戶后臺(tái)系統(tǒng)將code_url值生成二維碼圖片,用戶使用微信客戶端掃碼后發(fā)起支付。注意:code_url有效期為2小時(shí),過期后掃碼不能再發(fā)起支付。具體流程看微信公眾號(hào)

主體代碼:

pay_settings.py

#微信支付配置
# ========支付相關(guān)配置信息===========
import random
import time
import hashlib
from random import Random
from bs4 import BeautifulSoup
?
APP_ID = "xxx"  # 你公眾賬號(hào)上的appid
MCH_ID = "xxx"  # 你的商戶號(hào)
API_KEY = "xxx"  # 微信商戶平臺(tái)(pay.weixin.qq.com) -->賬戶設(shè)置 -->API安全 -->密鑰設(shè)置,設(shè)置完成后把密鑰復(fù)制到這里
APP_SECRECT = "xxx"
UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"  # 該url是微信下單api
?
NOTIFY_URL = "http://xxx/"  # 微信支付結(jié)果回調(diào)接口,需要改為你的服務(wù)器上處理結(jié)果回調(diào)的方法路徑
CREATE_IP = 'xxx'  # 你服務(wù)器的IP
?
?
def get_sign(data_dict, key):  
 """
 簽名函數(shù)
 :param data_dict: 需要簽名的參數(shù),格式為字典
 :param key: 密鑰 ,即上面的API_KEY
 :return: 字符串
 """
 params_list = sorted(data_dict.items(), key=lambda e: e[0], reverse=False)  # 參數(shù)字典倒排序?yàn)榱斜? params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=' + key
 # 組織參數(shù)字符串并在末尾添加商戶交易密鑰
 md5 = hashlib.md5()  # 使用MD5加密模式
 md5.update(params_str.encode('utf-8'))  # 將參數(shù)字符串傳入
 sign = md5.hexdigest().upper()  # 完成加密并轉(zhuǎn)為大寫
 return sign
?
?
def order_num(phone):
 """
 生成掃碼付款訂單號(hào)
 :param phone: 手機(jī)號(hào)
 :return:
 """
 local_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
 result = phone + 'T' + local_time + random_str(5)
 return result
?
?
def random_str(randomlength=8):
 """
 生成隨機(jī)字符串
 :param randomlength: 字符串長(zhǎng)度
 :return: 
 """
 strs = ''
 chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
 length = len(chars) - 1
 random = Random()
 for i in range(randomlength):
 strs += chars[random.randint(0, length)]
 return strs
?
def trans_dict_to_xml(data_dict):
 """
 定義字典轉(zhuǎn)XML的函數(shù)
 :param data_dict: 
 :return: 
 """
 data_xml = []
 for k in sorted(data_dict.keys()):  # 遍歷字典排序后的key
 v = data_dict.get(k)  # 取出字典中key對(duì)應(yīng)的value
 if k == 'detail' and not v.startswith('<![CDATA['):  # 添加XML標(biāo)記
 v = '<![CDATA[{}]]>'.format(v)
 data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
 return '<xml>{}</xml>'.format(''.join(data_xml))  # 返回XML
?
?
def trans_xml_to_dict(data_xml):
 """
 定義XML轉(zhuǎn)字典的函數(shù)
 :param data_xml: 
 :return: 
 """
 soup = BeautifulSoup(data_xml, features='xml')
 xml = soup.find('xml')  # 解析XML
 if not xml:
 return {}
 data_dict = dict([(item.name, item.text) for item in xml.find_all()])
 return data_dict

views.py

import qrcode
from django.shortcuts import render
from pay_settings import *
from django.http import HttpResponse
from django.views import View
?
class WXPayView(View):
 def get(self,request):
 # 在info.html有一個(gè)按鈕,點(diǎn)擊后跳轉(zhuǎn)到這個(gè)類的post()中,具體urls.py設(shè)置不詳述
 return render(request, 'info.html')
?
 def post(self,request):
 phone = request.POST.get('phone','')
 if not phone:
 return render(request, 'info.html', {'err_msg': '獲取手機(jī)號(hào)失敗'})
 data_dict = wxpay(phone)

 if data_dict.get('return_code') == 'SUCCESS':  # 如果請(qǐng)求成功
 qrcode_name = phone + '.png'  # 二維碼圖片名稱
 img = qrcode.make(data_dict.get('code_url'))  # 創(chuàng)建支付二維碼片
 img.save(r'static' + '/' + qrcode_name)
 return render(request, 'qrcode.html', {'qrcode_img': qrcode_name})
 return render(request, 'info.html', {'err_msg': '獲取微信的code_url失敗'})
?
?
def wxpay(phone):
 nonce_str = random_str() # 拼接出隨機(jī)的字符串即可,我這里是用  時(shí)間+隨機(jī)數(shù)字+5個(gè)隨機(jī)字母
?
 total_fee = 1  # 付款金額,單位是分,必須是整數(shù)
?
 params = {
 'appid': APP_ID,  # APPID
 'mch_id': MCH_ID,  # 商戶號(hào)
 'nonce_str': nonce_str,    # 隨機(jī)字符串
 'out_trade_no': order_num(phone),  # 訂單編號(hào),可自定義
 'total_fee': total_fee, # 訂單總金額
 'spbill_create_ip': CREATE_IP,  # 自己服務(wù)器的IP地址
 'notify_url': NOTIFY_URL,  # 回調(diào)地址,微信支付成功后會(huì)回調(diào)這個(gè)url,告知商戶支付結(jié)果
 'body': 'xxx公司',  # 商品描述
 'detail': 'xxx商品',  # 商品描述
 'trade_type': 'NATIVE',  # 掃碼支付類型
 }
?
 sign = get_sign(params,API_KEY)  # 獲取簽名
 params['sign'] = sign  # 添加簽名到參數(shù)字典
 # print(params)
 xml = trans_dict_to_xml(params)  # 轉(zhuǎn)換字典為XML
 response = requests.request('post', settings._UFDODER_URL, data=xml)  # 以POST方式向微信公眾平臺(tái)服務(wù)器發(fā)起請(qǐng)求
 data_dict = trans_xml_to_dict(response.content)  # 將請(qǐng)求返回的數(shù)據(jù)轉(zhuǎn)為字典
 return data_dict
?
?
class Wxpay_Result(View):
 """
 微信支付結(jié)果回調(diào)通知路由
 """
 def get(self, request, *args, **kwargs):
 machine_code = request.GET.get('machine_code', '獲取機(jī)器編號(hào)失敗')
 # 返回支付成功頁面,可自定義
 return render(request, 'zfcg.html', {'machine': {'machine_code': machine_code}})
?
 def post(self, request, *args, **kwargs):
 """
 微信支付成功后會(huì)自動(dòng)回調(diào)
 返回參數(shù)為:
 {'mch_id': '',
 'time_end': '',
 'nonce_str': '',
 'out_trade_no': '',
 'trade_type': '',
 'openid': '',
 'return_code': '',
 'sign': '',
 'bank_type': '',
 'appid': '',
 'transaction_id': '',
 'cash_fee': '',
 'total_fee': '',
 'fee_type': '', '
 is_subscribe': '',
 'result_code': 'SUCCESS'}
?
 :param request:
 :param args:
 :param kwargs:
 :return:
 """
 data_dict = trans_xml_to_dict(request.body)  # 回調(diào)數(shù)據(jù)轉(zhuǎn)字典
 # print('支付回調(diào)結(jié)果', data_dict)
 sign = data_dict.pop('sign')  # 取出簽名
 back_sign = get_sign(data_dict, API_KEY)  # 計(jì)算簽名
 # 驗(yàn)證簽名是否與回調(diào)簽名相同
 if sign == back_sign and data_dict['return_code'] == 'SUCCESS':
 '''
 檢查對(duì)應(yīng)業(yè)務(wù)數(shù)據(jù)的狀態(tài),判斷該通知是否已經(jīng)處理過,如果沒有處理過再進(jìn)行處理,如果處理過直接返回結(jié)果成功。
 '''
 print('微信支付成功會(huì)回調(diào)!')
 # 處理支付成功邏輯
 # 返回接收結(jié)果給微信,否則微信會(huì)每隔8分鐘發(fā)送post請(qǐng)求
 return HttpResponse(trans_dict_to_xml({'return_code': 'SUCCESS', 'return_msg': 'OK'}))
 return HttpResponse(trans_dict_to_xml({'return_code': 'FAIL', 'return_msg': 'SIGNERROR'}))
?

二、使用JsAPI發(fā)起微信支付

微信公眾號(hào)中左下角 設(shè)置->公眾號(hào)設(shè)置->功能設(shè)置,把業(yè)務(wù)域名,js接口安全域名,網(wǎng)頁授權(quán)域名設(shè)置好。

用戶在微信中點(diǎn)擊一個(gè)路由Url(可把這個(gè)url封裝成二維碼).在后臺(tái)中的views.py中的WxJsAPIPay類中使用get()函數(shù)處理用戶的請(qǐng)求,先獲取用戶的openid,然后調(diào)用微信統(tǒng)一下單接口,獲取prepay_id,具體看官網(wǎng)。

wx_js_pay.html

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="vp"/>
{#        <script type="text/javascript" src="/static/js/rem.js"></script>#}
{#        <link rel="stylesheet" type="text/css" href="/static/css/base.css" />#}
{#        <link rel="stylesheet" type="text/css" href="/static/css/index.css" />#}
 </head>
 <body>
 <div class="bigbox">
 <div class="top">
 <span>訂單詳情</span>
 </div>
 <div class="zhong">
?
 </div>
?
 <div class="zhifu">
 <button class="btn" type="button" onclick="javascript:callpay();return false;">立即支付</button>
 </div>
?
 </div>
?
<script src="https://cdn.bootcss.com/jquery/1.12.1/jquery.js"></script>
<script type="text/javascript">
 //調(diào)用微信JS api 支付
 function onBridgeReady() {
 WeixinJSBridge.invoke(
 'getBrandWCPayRequest',
 {
 appId: "{{ params.appid }}",        //公眾號(hào)名稱,由商戶傳入
 timeStamp: "{{ params.timeStamp }}", //時(shí)間戳,自1970年以來的秒數(shù)
 nonceStr: "{{ params.nonceStr }}",  //隨機(jī)串
 package: "prepay_id={{ params.prepay_id }}",  //預(yù)支付id
 signType: "MD5",  //微信簽名方式
 paySign: "{{ params.sign }}"     //微信簽名
 },
 function (res) {
 //支付成功后返回 get_brand_wcpay_request:ok
 if (res.err_msg == "get_brand_wcpay_request:ok") {
 // 跳轉(zhuǎn)到支付成功的頁面
 window.location.href = '#';
 {#alert('支付成功');#}
 } else if (res.err_msg == "get_brand_wcpay_request:cancel") {
 alert("您已取消支付!");
 {#alert({{ params.machine_code }});#}
 {#window.location.href = '';#}
 } else if (res.err_msg == "get_brand_wcpay_request:fail") {
 $.each(res, function(key,value){
 alert(value);
 });
 alert("支付失敗!");
 }
 }
 );
 }
?
 function callpay() {
 if (typeof WeixinJSBridge == "undefined") {
 if (document.addEventListener) {
 document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
 } else if (document.attachEvent) {
 document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
 document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
 }
 } else {
 onBridgeReady();
 }
 }
?
</script>
 </body>
</html>

pay_settings.py

import hashlib
import time
import requests
from collections import OrderedDict
from random import Random
from bs4 import BeautifulSoup
?
?
APP_ID = "xxx"  # 公眾賬號(hào)appid
MCH_ID = "xxx"  # 商戶號(hào)
API_KEY = "xxx"  # 微信商戶平臺(tái)(pay.weixin.qq.com) -->賬戶設(shè)置 -->API安全 -->密鑰設(shè)置,設(shè)置完成后把密鑰復(fù)制到這里
APP_SECRECT = "xxx"
UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"  # url是微信下單api
NOTIFY_URL = "http://xxx/wxpay/pay_result/"  # 微信支付結(jié)果回調(diào)接口,需要你自定義
CREATE_IP = 'xxx'  # 你服務(wù)器上的ip
?
?
# 生成隨機(jī)字符串
def random_str(randomlength=8):
 """
 生成隨機(jī)字符串
 :param randomlength: 字符串長(zhǎng)度
 :return:
 """
 str = ''
 chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
 length = len(chars) - 1
 random = Random()
 for i in range(randomlength):
 str+=chars[random.randint(0, length)]
 return str
?
?
def order_num(phone):
 """
 生成掃碼付款訂單,
 :param phone: 
 :return:
 """
 local_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
 result = phone + 'T' + local_time + random_str(5)
 return result
?
?
def get_sign(data_dict, key):
 # 簽名函數(shù),參數(shù)為簽名的數(shù)據(jù)和密鑰
 params_list = sorted(data_dict.items(), key=lambda e: e[0], reverse=False)  # 參數(shù)字典倒排序?yàn)榱斜? params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=' + key
 # 組織參數(shù)字符串并在末尾添加商戶交易密鑰
 md5 = hashlib.md5()  # 使用MD5加密模式
 md5.update(params_str.encode('utf-8'))  # 將參數(shù)字符串傳入
 sign = md5.hexdigest().upper()  # 完成加密并轉(zhuǎn)為大寫
 return sign
?
?
def trans_dict_to_xml(data_dict):  # 定義字典轉(zhuǎn)XML的函數(shù)
 data_xml = []
 for k in sorted(data_dict.keys()):  # 遍歷字典排序后的key
 v = data_dict.get(k)  # 取出字典中key對(duì)應(yīng)的value
 if k == 'detail' and not v.startswith('<![CDATA['):  # 添加XML標(biāo)記
 v = '<![CDATA[{}]]>'.format(v)
 data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
 return '<xml>{}</xml>'.format(''.join(data_xml)).encode('utf-8')  # 返回XML,并轉(zhuǎn)成utf-8,解決中文的問題
?
?
def trans_xml_to_dict(data_xml):
 soup = BeautifulSoup(data_xml, features='xml')
 xml = soup.find('xml')  # 解析XML
 if not xml:
 return {}
 data_dict = dict([(item.name, item.text) for item in xml.find_all()])
 return data_dict
?
?
def wx_pay_unifiedorde(detail):
 """
 訪問溫馨支付統(tǒng)一下單接口
 :param detail:
 :return:
 """
 detail['sign'] = get_sign(detail, API_KEY)
 # print(detail)
 xml = trans_dict_to_xml(detail)  # 轉(zhuǎn)換字典為XML
 response = requests.request('post', UFDODER_URL, data=xml)  # 以POST方式向微信公眾平臺(tái)服務(wù)器發(fā)起請(qǐng)求
 # data_dict = trans_xml_to_dict(response.content)  # 將請(qǐng)求返回的數(shù)據(jù)轉(zhuǎn)為字典
 return response.content
?
?
def get_redirect_url():
 """
 獲取微信返回的重定向的url
 :return: url,其中攜帶code
 """
 WeChatcode = 'https://open.weixin.qq.com/connect/oauth2/authorize'
 urlinfo = OrderedDict()
 urlinfo['appid'] = APP_ID
 urlinfo['redirect_uri'] = 'http://xxx/wxjsapipay/?getInfo=yes'  # 設(shè)置重定向路由
 urlinfo['response_type'] = 'code'
 urlinfo['scope'] = 'snsapi_base'  # 只獲取基本信息
 urlinfo['state'] = 'mywxpay'   # 自定義的狀態(tài)碼
?
 info = requests.get(url=WeChatcode, params=urlinfo)
 return info.url
?
?
def get_openid(code,state):
 """
 獲取微信的openid
 :param code:
 :param state:
 :return:
 """
?
 if code and state and state == 'mywxpay':
?
 WeChatcode = 'https://api.weixin.qq.com/sns/oauth2/access_token'
 urlinfo = OrderedDict()
 urlinfo['appid'] = APP_ID
 urlinfo['secret'] = APP_SECRECT
 urlinfo['code'] = code
 urlinfo['grant_type'] = 'authorization_code'
 info = requests.get(url=WeChatcode, params=urlinfo)
 info_dict = eval(info.content.decode('utf-8'))
?
 return info_dict['openid']
 return None
?
?
def get_jsapi_params(openid):
 """
 獲取微信的Jsapi支付需要的參數(shù)
 :param openid: 用戶的openid
 :return:
 """
?
 total_fee = 1  # 付款金額,單位是分,必須是整數(shù)
?
 params = {
 'appid': APP_ID,  # APPID
 'mch_id': MCH_ID,  # 商戶號(hào)
 'nonce_str': random_str(16),  # 隨機(jī)字符串
 'out_trade_no': order_num('123'),  # 訂單編號(hào),可自定義
 'total_fee': total_fee,  # 訂單總金額
 'spbill_create_ip': CREATE_IP,  # 發(fā)送請(qǐng)求服務(wù)器的IP地址
 'openid': openid,
 'notify_url': NOTIFY_URL,  # 支付成功后微信回調(diào)路由
 'body': 'xxx公司',  # 商品描述
 'trade_type': 'JSAPI',  # 公眾號(hào)支付類型
 }
 # print(params)
 # 調(diào)用微信統(tǒng)一下單支付接口url
 notify_result = wx_pay_unifiedorde(params)
?
 params['prepay_id'] = trans_xml_to_dict(notify_result)['prepay_id']
 params['timeStamp'] = int(time.time())
 params['nonceStr'] = random_str(16)
 params['sign'] = get_sign({'appId': APP_ID,
 "timeStamp": params['timeStamp'],
 'nonceStr': params['nonceStr'],
 'package': 'prepay_id=' + params['prepay_id'],
 'signType': 'MD5',
 },
 API_KEY)

 return params

views.py

from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render
from django.views import View
from pay_settings import *
?
?
class WxJsAPIPay(View):
 def get(self, request, *args, **kwargs):
 """
 用戶點(diǎn)擊一個(gè)路由或者掃碼進(jìn)入這個(gè)views.py中的函數(shù),首先獲取用戶的openid,
 使用jsapi方式支付需要此參數(shù)
 :param self:
 :param request:
 :param args:
 :param kwargs:
 :return:
 """
 getInfo = request.GET.get('getInfo', None)
 openid = request.COOKIES.get('openid', '')
 if not openid:
 if getInfo != 'yes':
 # 構(gòu)造一個(gè)url,攜帶一個(gè)重定向的路由參數(shù),
 # 然后訪問微信的一個(gè)url,微信會(huì)回調(diào)你設(shè)置的重定向路由,并攜帶code參數(shù)
 return HttpResponseRedirect(get_redirect_url())
 elif getInfo == 'yes':
 # 我設(shè)置的重定向路由還是回到這個(gè)函數(shù)中,其中設(shè)置了一個(gè)getInfo=yes的參數(shù)
 # 獲取用戶的openid
 openid = get_openid(request.GET.get('code'), request.GET.get('state', ''))
 if not openid:
 return HttpResponse('獲取用戶openid失敗')
 response = render_to_response('wx_js_pay.html', context={'params': get_jsapi_params(openid)})
 response.set_cookie('openid', openid, expires=60 * 60 * 24 *30)
 return response
?
 else:
 return HttpResponse('獲取機(jī)器編碼失敗')
 else:
 return render(request, 'wx_js_pay.html', context={'params': get_jsapi_params(openid)})
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容