支付文档https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3
html
js
// pages/login/login.js
Page({
/**
* 页面的初始数据
*/
data: {
phone: '',
openid: ''
},
// 获取input数据
inputPhone: function (e) {
// 获取input信息
var phone = e.detail.value
this.setData({
phone: phone
})
},
// 登录
getUserProfile: function (e) {
// 获取openid
wx.login({
success: res => {
this.setData({
openid: res.code
})
}
})
// 向后台发送数据
wx.request({
url: 'http://127.0.0.1:8023/login/',
data: {
phone: this.data.phone,
openid: this.data.openid
},
dataType: "json",
method: "POST",
success: (result) => {
console.log(result.data)
},
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
html
{{item.title}} - {{item.price}}
js
// pages/index/index.js
Page({
/**
* 页面的初始数据
*/
data: {
goodsList: [],
seletedId: ''
},
// 选中物品的id
changeGoods: function (e) {
// console.log(e.detail.value)
this.setData({
seletedId: e.detail.value
})
// console.log(this.data.seletedId)
},
// 点击购买
doPayment: function () {
this.data.seletedId
// 向后台发送一个请求,生成一大堆数据
// 获取这一大堆数据,然后弹出支付二维码
wx.request({
url: 'http://127.0.0.1:8023/payment/',
data: {
goodsId: this.data.seletedId
},
method: 'POST',
dataType: 'json',
success: (res) => {
console.log(res.data);
// 生成二维码
wx.requestPayment({
'timeStamp': res.data.timeStamp,
'nonceStr': res.data.nonceStr,
'package': res.data.package,
'signType': res.data.signType,
'paySign': res.data.paySign,
'success': function (res) {
// 进行逻辑判断
},
'fail': function (res) {
},
'complete': function (res) {
}
})
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 物品渲染
wx.request({
url: 'http://127.0.0.1:8023/index/',
method: 'GET',
dataType: 'json',
success: (res) => {
this.setData({
goodsList: res.data
})
}
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
{
"pages": [
"pages/index/index",
"pages/login/login"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle": "black"
},
"tabBar": {
"backgroundColor": "#fafafa",
"borderStyle": "white",
"color": "#666",
"selectedColor": "#b4282d",
"position": "bottom",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/login/login",
"text": "登录"
}
]
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.IndexData.as_view()),
path('login/', views.LoginView.as_view()),
path('payment/', views.PaymentView.as_view()),
path('pay/notify/', views.NotifyView.as_view()),
]
import time
import random
import requests
from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import ListAPIView
from xml.etree import ElementTree as ET
from app01 import models
class IndexDataModelSerializer(serializers.ModelSerializer):
"""首页序列化器"""
class Meta:
model = models.Goods
fields = '__all__'
class IndexData(ListAPIView):
"""首页渲染"""
queryset = models.Goods.objects.all()
serializer_class = IndexDataModelSerializer
class LoginView(APIView):
"""登录视图"""
def post(self, request, *args, **kwargs):
# 从前端获取数据
phone = request.data.get('phone')
wx_code = request.data.get('openid')
# print(wx_code)
if not wx_code:
return Response('无openid')
# print(wx_code)
# openid的获取:需要拿着wx_code去微信申请
# https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
# login服务端,发送请求的路由地址
# https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
info = {
'appid': "微信小程序appid", # 微信小程序appid
'secret': "微信小程序密钥key", # 微信小程序密钥key
'js_code': wx_code, # openid
'grant_type': "authorization_code", # 默认值即可
}
result = requests.get(url='https://api.weixin.qq.com/sns/jscode2session', params=info)
openid = result.json()['openid']
models.UserInfo.objects.get_or_create(phone=phone, defaults={'openid': openid})
return Response('ok')
# 进行加密
def md5(string):
"""md5加密"""
import hashlib
m = hashlib.md5()
m.update(string.encode('utf-8'))
return m.hexdigest()
# 支付通知
class PaymentView(APIView):
"""支付视图及操作"""
def post(self, request, *args, **kwargs):
# 前端传送过来的商品id
goods_id = request.data.get('goodsId')
order_random_string = str(int(time.time()))
# 获取用户对象,应该是当前登录用户,这里写死了
user_object = models.UserInfo.objects.filter(id=1).first() # user_object.openid
# 商品的对象
goods_object = models.Goods.objects.filter(id=goods_id).first() # goods_object.price
# 创建一个订单
order_object = models.Order.objects.create(goods=goods_object, user=user_object, uid=order_random_string,
status=1)
# 按照微信的规则,去生成支付需要的一大堆的数据
# https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
# print(order_random_string)
# ###################### 1.调用支付统一下单 ######################
info = {
# 小程序id T
'appid': '小程序id',
# 商户关联id T
'mch_id': '商户关联id',
# 设备号 F
'device_info': 'ymq-text',
# 随机字符串 T
'nonce_str': "".join([chr(random.randint(65, 90)) for _ in range(12)]),
# 签名类型 F
'sign_type': "MD5",
# 商品描述 F
'body': "支付测试",
# 商品详情 F
'detail': '这是一个商品详细描述信息.',
# 附加数据 F
'attach': '河南彭于晏',
# 商户订单号 T
'out_trade_no': order_random_string,
# 标价金额 T
'total_fee': goods_object.price, # 总金额
# 终端IP(用户ip) F
'spbill_create_ip': request.META.get('REMOTE_ADDR'),
# # 支付成功之后,微信异步通知(post) T
'notify_url': "http://106.14.42.256:8023/pay/notify/",
# 交易类型 T
'trade_type': 'JSAPI',
# 用户标识(trade_type=JSAPI,此参数必传)
'openid': user_object.openid
}
# 1.1 签名
# 对字典中的key按照ASCII码从小到大排序
# 将排完序的值拼接 stringA = appid=wx55cca0b94f723dc7&mch_id=1526049051
# 让stringA和key拼接:stringSignTemp = stringA+"&key=192006250b4c09247ec02edce69f6a2d" key为商户平台设置的密钥key
# MD5(stringSignTemp)
# 将密文转换为大写
# 得到签名 sign
# 把签名再添加到info中 info['sign'] = sign值
# 商户密钥key
pay_key = "key为商户平台设置的密钥key"
#
temp = "&".join(["{0}={1}".format(k, info[k]) for k in sorted(info)] + ["{0}={1}".format("key", pay_key, ), ])
sign = md5(temp).upper()
info['sign'] = sign
# 1.2 向 https://api.mch.weixin.qq.com/pay/unifiedorder 发请求 (json转换为xml)
xml_string = "{0} ".format("".join(["<{0}>{1}{0}>".format(k, v) for k, v in info.items()]))
prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml_string.encode('utf-8'))
# 1.3 从结果xml中提取 prepay_id
root = ET.XML(prepay.content.decode('utf-8'))
prepay_dict = {child.tag: child.text for child in root}
prepay_id = prepay_dict['prepay_id']
# ####################### 2.再次签名 #######################
info_dict = {
# 小程序的appid
'appId': "小程序的appid",
'timeStamp': str(int(time.time())), # 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
'nonceStr': "".join([chr(random.randint(65, 90)) for _ in range(12)]), # 随机字符串,长度为32个字符以下。
'package': 'prepay_id={0}'.format(prepay_id), # 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*
'signType': 'MD5', # 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
}
temp = "&".join(
["{0}={1}".format(k, info_dict[k]) for k in sorted(info_dict)] + ["{0}={1}".format("key", pay_key, ), ])
sign2 = md5(temp).upper()
info_dict['paySign'] = sign2
# print(info_dict)
return Response(info_dict)
class NotifyView(APIView):
"""
支付完成之后的通知
"""
def post(self, request, *args, **kwargs):
# 1. 获取结果把结果XML转换为字典格式
root = ET.XML(request.body.decode('utf-8'))
result = {child.tag: child.text for child in root}
# 2. 校验签名是否正确,防止恶意请求。
sign = result.pop('sign')
# key为商户平台设置的密钥key
key = "key为商户平台设置的密钥key"
temp = "&".join(
["{0}={1}".format(k, result[k]) for k in sorted(result)] + ["{0}={1}".format("key", key, ), ])
local_sign = md5(temp).upper()
# 签名一致
if local_sign == sign:
# 根据订单号,把数据库的订单状态修改为支付成功
out_trade_no = result.get('out_trade_no')
# 如果支付成功,修改订单状态
models.Order.objects.filter(uid=out_trade_no).update(status=2)
response = """ """
return Response(response)