python的django环境下微信支付v3的主要流程环节代码。也还有验签部份的缺漏处的请教大神。
研究了下python的django环境下微信支付v3的主要开发环节,并成功实现微信支付和微信企业提现。里面还是有很多细节,官方的指南没从小白的角度出发,写的不够清晰。希望给有用的人一些帮助。
虽然可用,但也还有支付验签环节没完全搞懂,请教有开发过的大神。
直接上代码:
from rest_framework.decorators import api_view
from rest_framework.response import Response
import requests,hashlib,datetime,json,time,random,string
#用到的关于支付的主要模块
from Cryptodome.PublicKey import RSA
from base64 import b64encode
from Cryptodome.Signature import pkcs1_15
from Cryptodome.Hash import SHA256
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64
from xml.etree import ElementTree as ET
from XXXX(app名称) import settings #各类用到的参数存在settings.py 中
from XXXX(app名称).models import XXXXX(各数据表)
#微信支付统一下单
[@api_view](/user/api_view)(['POST'])
def payFromWx(request):
price=request.data['price']
userId=request.data['userId']
up_time=datetime.datetime.now()
#1.以交易日期生成交易号
transactionNo=str(up_time).replace('.', '').replace('-', '').replace(':', '').replace(' ', '')
#2.生成新交易记录
newTransaction=TransactionLogs.objects.create(
transaction_no =transactionNo,
transaction_status_id=1,
user_id =userId,
transaction_type_id=1,
transaction_amount=price,
created_at=up_time)
#3.生成统一下单的报文body
user=Users.objects.get(user_id=userId)
userOpenid=user.openid
url='https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi'
body={
"appid": settings.AppId,
"mchid": settings.mchId,
"description": "购买",
"out_trade_no": transactionNo,
"notify_url": "https://api.XXX.com/api/payNotify",
"amount": {"total": price*100, "currency": "CNY"},
"payer": {"openid":userOpenid },
}
data=json.dumps(body)
#4.定义生成签名的函数
def get_sign(sign_str):
rsa_key = RSA.importKey(open('XXXX/apiclient_key.pem').read())(#加载证书)
signer = pkcs1_15.new(rsa_key)
digest = SHA256.new(sign_str.encode('utf8'))
sign = b64encode(signer.sign(digest)).decode('utf-8')
return sign
#5.生成请求随机串
random_str = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
#6.生成请求时间戳
time_stamps = str(int(time.time()))
#7.生成签名串
sign_str = f"POST\n{'/v3/pay/transactions/jsapi'}\n{time_stamps}\n{random_str}\n{data}\n"
#8.生成签名
sign = get_sign(sign_str)
#9.生成HTTP请求头
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': '*/*',
'Authorization':'WECHATPAY2-SHA256-RSA2048 '+f'mchid="{settings.mchId}",nonce_str="{random_str}",signature="{sign}",timestamp="{time_stamps}",serial_no="{settings.serialNo}"'
}
#10.发送请求获得prepay_id
response = requests.post(url,data=data,headers=headers) #要加请求头
#11.应答签名验证
签名验证还没搞懂,有开发过的大神请指教。
#12.生成调起支付API需要的参数并返回前端
res = {
'timeStamp':time_stamps,
'nonceStr':random_str,
'package':'prepay_id='+response.json()['prepay_id'],
'paySign':get_sign(f"{settings.AppId}\n{time_stamps}\n{random_str}\n{'prepay_id='+response.json()['prepay_id']}\n"),
}
return Response (res)
#前端js
async payFromWx () {
const payInfo =await this.$myRequest({
url:'/api/payFromWx',
method:'POST',
data:{
price:this.price,
userId:this.userInfo.userId,
},
})
wx.requestPayment({
timeStamp: payInfo.data.timeStamp,
nonceStr: payInfo.data.nonceStr,
package: payInfo.data.package,
signType: "RSA",
paySign: payInfo.data.paySign,
success (res) {
uni.showToast({
title: '微信支付成功,预计5分钟内生效',
icon: 'none'
})
this.getUserInfo()
},
fail (res) {
console.log('fail', res)
}
})
}
完成调起支付
#自己的服务器notify_url收到支付通知后的处理
[@api_view](/user/api_view)(['POST'])
def payNotify(request):
#1.获得支付通知的参数
body=request.data
nonce=request.data['resource']['nonce']
ciphertext=request.data['resource']['ciphertext']
associated_data=request.data['resource']['associated_data']
up_time=datetime.datetime.now()
#2.获得支付通知HTTP头参数
Wechatpay_Serial=request.META['HTTP_WECHATPAY_SERIAL']
Wechatpay_Timestamp=request.META['HTTP_WECHATPAY_TIMESTAMP']
Wechatpay_Nonce=request.META['HTTP_WECHATPAY_NONCE']
Wechatpay_Signature=request.META['HTTP_WECHATPAY_SIGNATURE']
print('Wechatpay_Signature',Wechatpay_Signature)
#6.回调报文解密后取得定单号
def decrypt(nonce, ciphertext, associated_data):
key = settings.apiV3Key
key_bytes = str.encode(key)
nonce_bytes = str.encode(nonce)
ad_bytes = str.encode(associated_data)
data = base64.b64decode(ciphertext)
aesgcm = AESGCM(key_bytes)
return aesgcm.decrypt(nonce_bytes, data, ad_bytes)
payment=decrypt(nonce, ciphertext, associated_data)
payment=eval(payment.decode('utf-8'))
transactionNo=payment['out_trade_no']
#7.回调报文签名验证
#7.1.获取平台证书
url = "https://api.mch.weixin.qq.com/v3/certificates"
#7.2.定义生成签名的函数
def get_sign(sign_str):
rsa_key = RSA.importKey(open('ltjian/static/apiclient_key.pem').read())
signer = pkcs1_15.new(rsa_key)
digest = SHA256.new(sign_str.encode('utf8'))
sign = b64encode(signer.sign(digest)).decode('utf-8')
return sign
#7.3.生成请求随机串
random_str = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
#7.4.生成请求时间戳
time_stamps = str(int(time.time()))
#7.5.生成签名串
data=""
sign_str = f"GET\n{'/v3/certificates'}\n{time_stamps}\n{random_str}\n{data}\n"
#7.6.生成签名
sign = get_sign(sign_str)
#7.7.生成HTTP请求头
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'WECHATPAY2-SHA256-RSA2048 ' + f'mchid="{settings.mchId}",nonce_str="{random_str}",signature="{sign}",timestamp="{time_stamps}",serial_no="{settings.serialNo}"'
}
#7.8.发送请求获得证书
response = requests.get(url,headers=headers)
cert=response.json()
#7.9.证书解密
nonce = cert["data"][0]['encrypt_certificate']['nonce']
ciphertext = cert["data"][0]['encrypt_certificate']['ciphertext']
associated_data = cert["data"][0]['encrypt_certificate']['associated_data']
certificate=decrypt(nonce, ciphertext, associated_data)
print('certificate',certificate)
#7.10.签名验证
签名验证还没搞懂,所以整了第7部份一大段,然并没有用。有开发过的大神请指教。
#8.获得回调报文中交易号后修改已支付订单状态
transactionlog=TransactionLogs.objects.get(transaction_no=transactionNo)
transactionlog.transaction_status_id=2
transactionlog.save()
return Response ({"code": "SUCCESS","message": "成功"})#指定格式,返回给微信服务器
#企业付款至零钱 还是V2版
[@api_view](/user/api_view)(['POST'])
def withdraw(request):
amount=int(request.data['amount'])
userId=request.data['userId']
up_time=datetime.datetime.now()
#1.以交易日期生成交易号
transactionNo=str(up_time).replace('.', '').replace('-', '').replace(':', '').replace(' ', '')
#2.生成新交易记录
newTransaction=TransactionLogs.objects.create(
transaction_no =transactionNo,
transaction_status_id=1,
user_id =userId,
transaction_type_id=2,
transaction_amount=amount,
created_at =up_time)
#3.生成请求随机串
random_str = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
#4.生成企业付款的报文body
user=Users.objects.get(user_id=userId)
userOpenid=user.openid
userNickName=user.user_wxNickName
url='https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'
body={
"mch_appid":settings.AppId,
"mchid":settings.mchId,
"nonce_str":random_str,
"partner_trade_no":transactionNo,
"openid":userOpenid,
"check_name":"NO_CHECK",
"re_user_name":userNickName,
"amount":amount*100,
"desc":"用户提现",
}
#4.生成签名
stringSignTemp="&".join(["{0}={1}".format(k,body[k]) for k in sorted(body)]+["{0}={1}".format("key",settings.apiKey)])
sign=hashlib.md5(stringSignTemp.encode('utf-8')).hexdigest().upper()
body["sign"]=sign
#5.转成XML
xml_string="{0}".format("".join(["<{0}>{1}".format(k,v) for k,v in body.items()]))
#6.发送请求 要加上两个下载的证书
response = requests.post(url,data=xml_string.encode("utf-8"),cert=('XXX/apiclient_cert.pem','XXX/apiclient_key.pem'))
#7.把response从xml转换为字典
root=ET.XML(response.content.decode("utf-8"))
result={child.tag:child.text for child in root}
#8.修改订单状态
transactionNo=result['partner_trade_no']
transactionlog=TransactionLogs.objects.get(transaction_no=transactionNo)
transactionlog.transaction_status_id=2
transactionlog.save()
return Response ({'msg':"withdrawSuccess"})