目录
1.积分
1.关于积分的表结构
1.在user表中添加credit字段 + 设计一个积分的表结构
user/models.py
class User(AbstractUser): phone = models.CharField(max_length=16,null=True,blank=True) wechat = models.CharField(max_length=16,null=True,blank=True) # 为用户表添加积分字段 每个用户都有自己积分 credit = models.IntegerField(default=0, blank=True, verbose_name="贝里") class Credit(BaseModel): """积分流水""" OPERA_OPION = ( (1, "赚取"), (2, "消费"), ) user = models.ForeignKey("User", related_name="user_credit", on_delete=models.CASCADE, verbose_name="用户") opera = models.SmallIntegerField(choices=OPERA_OPION,verbose_name="操作类型") number = models.SmallIntegerField(default=0, verbose_name="积分数值") class Meta: db_table = 'ly_credit' verbose_name = '积分流水' verbose_name_plural = verbose_name def __str__(self): return "%s %s %s 贝壳" % ( self.user.username, self.OPERA_OPION[self.opera][1], self.number )
2.设置积分和现金的兑换比例
constant.py
CREDIT_MONEY = 10 # 10积分顶一块钱
1.登录成功 给前端回复除了[用户名 id token],也要把[积分和计算公式]回复给前端
user/utils.py
def jwt_response_payload_handler(token, user=None, request=None): return { 'token': token, 'username': user.username, 'id':user.id, # 登录成功将积分和兑换比例也要返回给前端 'credit':user.credit, 'credit_to_money':contains.CREDIT_MONEY, }
2.前端将积分存起来
login.vue
if (this.remember){ localStorage.token = res.data.token; localStorage.username = res.data.username; localStorage.id = res.data.id; // 前端将积分存起来 localStorage.credit = res.data.credit; localStorage.credit_to_money = res.data.credit_to_money; sessionStorage.removeItem('token'); sessionStorage.removeItem('username'); sessionStorage.removeItem('id'); sessionStorage.removeItem('credit'); sessionStorage.removeItem('credit_to_money'); }else { sessionStorage.token = res.data.token; sessionStorage.username = res.data.username; sessionStorage.id = res.data.id; // 前端将积分存起来 sessionStorage.credit = res.data.credit; sessionStorage.credit_to_money = res.data.credit_to_money; localStorage.removeItem('token'); localStorage.removeItem('username'); localStorage.removeItem('id'); localStorage.removeItem('credit'); localStorage.removeItem('credit_to_money'); }
order.vue
<!-- html --> <p class="discount-num2" v-else><span>总积分:xxxx, <el-input-number v-model="num" @change="handleChange" :min="0" :max="10000" label="描述文字"></el-input-number>,已抵扣xxx元,本次花费xxx积分</span></p>
// js handleChange(value){ // 如果用户在积分输入框中没有输入值,那么默认使用了0个积分 if (!value){ this.num = 0 } console.log(value); },
order.vue
get_credit(){ // 从前端拿取积分和兑换比例 this.credit = localStorage.getItem('credit') || sessionStorage.getItem('credit') this.credit_to_money = localStorage.getItem('credit_to_money') || sessionStorage.getItem('credit_to_money') },
order.vue
<!-- html --> <p class="discount-num2" v-else><span>总积分:{{credit}},
order.vue
max_credit(){ // 计算此课程能使用的最大积分数:比如100元的课程最多能使用1000积分 let a = parseFloat(this.total_price) * parseFloat(this.credit_to_money); // 如果此课程能使用的最大积分数[1000]小于你的总积分数[1500]:那么你能使用的最大积分数就是[1000] if (this.credit >= a){ return a // 如果此课程能使用的最大积分数[1000]大于你的总积分数[600],那么你能使用的最大积分数就是你剩余的积分数[600] }else { return parseFloat(this.credit) }
order.vue
payhander(){ let token = localStorage.token || sessionStorage.token; this.$axios.post(`${this.$settings.Host}/order/add_money/`,{ "pay_type":this.pay_type, "coupon":this.current_coupon, "credit":this.num, // 将积分提交给后端,让后端对积分做校验 },{ headers:{ 'Authorization':'jwt ' + token } }).then((res)=>{ this.$message.success('订单已经生成,马上跳转支付页面') }).catch((error)=>{ this.$message.error(error.response.data.msg); }) }
order/serializers.py
def validate(self, attrs): # 获取前端发过来的积分 credit = attrs.get('credit') # 查询用户所拥有的的所有积分 user_credit = self.context['request'].user.credit # 如果前端积分大于用户积分,抛出异常 if credit > user_credit: raise serializers.ValidationError('积分超上限了,别乱搞') return attrs def create(self, validated_data): # 积分判断 credit = float(validated_data.get('credit',0)) # 获取前端发过来的验证之后的积分 if credit > constants.CREDIT_MONEY * total_real_price: # 如果使用积分超过你此课程能使用的最大积分 transaction.savepoint_rollback(sid) # 回滚到断点处 raise serializers.ValidationError('使用积分超过了上线,别高事情') # 积分计算 total_real_price -= credit / contains.CREDIT_MONEY
2.支付
1.准备工作
pip install python-alipay-sdk --upgrade
python3 manage.py startapp payment
openssl OpenSSL> genrsa -out app_private_key.pem 2048 # 生成私钥到指定文件中
2.
openssl OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
3.
4.
from django.urls import path,re_path from . import views urlpatterns = [ path('alipay/',views.AlipayView.as_view(),), ]
4.视图
payment/views.py
from alipay import AliPay, DCAliPay, ISVAliPay from alipay.utils import AliPayConfig class AlipayView(APIView): def get(self,request): order_number = request.query_params.get('order_number') order_obj = Order.objects.get(order_number=order_number) alipay = AliPay( appid=settings.ALIAPY_CONFIG['appid'], app_notify_url=None, # 默认回调url app_private_key_string=open(settings.ALIAPY_CONFIG['app_private_key_path']).read(), # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥, alipay_public_key_string=open(settings.ALIAPY_CONFIG['alipay_public_key_path']).read(), sign_type=settings.ALIAPY_CONFIG['sign_type'], # RSA 或者 RSA2 debug = settings.ALIAPY_CONFIG['debug'], # 默认False ) order_string = alipay.api_alipay_trade_page_pay( out_trade_no=order_obj.order_number, total_amount=float(order_obj.real_price), subject=order_obj.order_title, return_url=settings.ALIAPY_CONFIG['return_url'], notify_url=settings.ALIAPY_CONFIG['notify_url'] # 可选, 不填则使用默认notify url ) url = settings.ALIAPY_CONFIG['gateway_url'] + order_string return Response({'url': url})
5.支付宝配置信息
dev.py
# 支付宝配置信息 ALIAPY_CONFIG = { "gateway_url": "https://openapi.alipaydev.com/gateway.do?", # 沙箱支付宝网关地址 "appid": "2016093000633754", # 沙箱中那个应用id "app_notify_url": None, "app_private_key_path": os.path.join(BASE_DIR, "apps/payment/keys/app_private_key.pem"), "alipay_public_key_path": os.path.join(BASE_DIR, "apps/payment/keys/alipay_public_key.pem"), "sign_type": "RSA2", "debug": False, "return_url": "http://www.lycity.com:8080/payment/result", # 同步回调地址 "notify_url": "http://www.lyapi.com:8001/payment/result", # 异步结果通知
payhander(){ this.$axios.post(`${this.$settings.Host}/order/add_money/`,{ }).then((res)=>{ this.$message.success('订单已经生成,马上跳转支付页面') let order_number = res.data.order_number this.$axios.get(`${this.$settings.Host}/payment/alipay/?order_number=${order_number}`) .then((res)=>{ // res.data : alipay.trade...?a=1&b=2.... location.href = res.data.url; }) }).catch((error)=>{ this.$message.error(error.response.data.msg); }) }
7.支付成功的页面-初始化
Success.vue
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<template> <div class="success"> <Header/> <div class="main"> <div class="title"> <!-- <img src="../../static/images/right.svg" alt="">--> <div class="success-tips"> <p class="tips1">您已成功购买 1 门课程!</p> <p class="tips2">你还可以加入QQ群 <span>747556033</span> 学习交流</p> </div> </div> <div class="order-info"> <p class="info1"><b>付款时间:</b><span>2019/04/02 10:27</span></p> <p class="info2"><b>付款金额:</b><span >0</span></p> <p class="info3"><b>课程信息:</b><span><span>《Pycharm使用秘籍》</span></span></p> </div> <div class="wechat-code"> <!-- <img src="../../static/image/server.cf99f78.png" alt="" class="er">--> <!-- <p><img src="../../static/image/tan.svg" alt="">重要!微信扫码关注获得学习通知&课程更新提醒!否则将严重影响学习进度和课程体验!</p>--> </div> <div class="study"> <span>立即学习</span> </div> </div> <Footer/> </div> </template> <script> import Header from "./common/Header" import Footer from "./common/Footer" export default{ name:"Success", data(){ return { current_page:0, }; }, created(){ // 把地址栏上面的支付结果,转发给后端 }, components:{ Header, Footer, } } </script> <style scoped> .success{ padding-top: 80px; } .main{ height: 100%; padding-top: 25px; padding-bottom: 25px; margin: 0 auto; width: 1200px; background: #fff; } .main .title{ display: flex; -ms-flex-align: center; align-items: center; padding: 25px 40px; border-bottom: 1px solid #f2f2f2; } .main .title .success-tips{ box-sizing: border-box; } .title img{ vertical-align: middle; width: 60px; height: 60px; margin-right: 40px; } .title .success-tips{ box-sizing: border-box; } .title .tips1{ font-size: 22px; color: #000; } .title .tips2{ font-size: 16px; color: #4a4a4a; letter-spacing: 0; text-align: center; margin-top: 10px; } .title .tips2 span{ color: #ec6730; } .order-info{ padding: 25px 48px; padding-bottom: 15px; border-bottom: 1px solid #f2f2f2; } .order-info p{ display: -ms-flexbox; display: flex; margin-bottom: 10px; font-size: 16px; } .order-info p b{ font-weight: 400; color: #9d9d9d; white-space: nowrap; } .wechat-code{ display: flex; -ms-flex-align: center; align-items: center; padding: 25px 40px; border-bottom: 1px solid #f2f2f2; } .wechat-code>img{ width: 100px; height: 100px; margin-right: 15px; } .wechat-code p{ font-size: 14px; color: #d0021b; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; } .wechat-code p>img{ width: 16px; height: 16px; margin-right: 10px; } .study{ padding: 25px 40px; } .study span{ display: block; width: 140px; height: 42px; text-align: center; line-height: 42px; cursor: pointer; background: #ffc210; border-radius: 6px; font-size: 16px; color: #fff; } </style>
注册Success组件
{ path: '/payment/result/', component: Success },