Python笔记_83_支付_支付宝sdk的使用_后端提供接口

支付-支付宝

支付宝开发平台登录

https://open.alipay.com/platform/home.htm

[外链图片转存失败(img-N7zW8W0Q-1566909855138)(assets/1554189376336.png)]

[外链图片转存失败(img-e62yWka2-1566909855139)(assets/1554189403316.png)]

[外链图片转存失败(img-OUZaTn2h-1566909855140)(assets/1554189677144.png)]

沙箱环境

  • 是支付宝提供给开发者的模拟支付的环境

  • 沙箱环境跟真实环境是分开的,项目上线时必须切换对应的配置服务器地址和开发者ID和密钥。

  • 沙箱应用开发文档:https://docs.open.alipay.com

  • 沙箱账号:https://openhome.alipay.com/platform/appDaily.htm?tab=account>

    [外链图片转存失败(img-VWRn9lXA-1566909855140)(assets/%E8%BF%9B%E5%85%A5%E6%B2%99%E7%AE%B1%E7%8E%AF%E5%A2%83.png)]

真实的支付宝网关:   https://openapi.alipay.com/gateway.do
	
沙箱的支付宝网关:   https://openapi.alipaydev.com/gateway.do

支付宝开发者文档

电脑网站支付流程

时序图[ 时间顺序流程图 ]

[外链图片转存失败(img-icfX4VSu-1566909855142)(assets/%E7%94%B5%E8%84%91%E7%BD%91%E7%AB%99%E6%94%AF%E4%BB%98%E6%B5%81%E7%A8%8B%E5%9B%BE1.png)]

[外链图片转存失败(img-0BmvCoGQ-1566909855142)(assets/1566548142736.png)]

开发支付功能

cd luffyapi/apps
python ../../manage.py startapp payments

注册子应用

INSTALLED_APPS = [
	...
    'payments',
]

配置秘钥

1. 生成应用的私钥和公钥

下载对应系统的秘钥生成工具: https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1

windows操作系统

生成如下,安装软件时需要管理员身份来安装.

[外链图片转存失败(img-aQfQj2se-1566909855144)(assets/1566548916758.png)]

Linux系统

生成如下:

openssl
OpenSSL> genrsa -out app_private_key.pem   2048  # 私钥
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
OpenSSL> exit

应用公钥复制粘贴到支付宝网站页面中.

点击修改以后,粘贴进去

[外链图片转存失败(img-d8C1K9Lz-1566909855144)(assets/1554192143494.png)]

2. 保存应用私钥文件

payments应用中新建keys目录,用来保存秘钥文件。

将应用私钥文件app_private_key.pem复制到payment/keys目录下。

windows系统生成的私钥必须在上下两行加上以下标识:

-----BEGIN RSA PRIVATE KEY-----
私钥
-----END RSA PRIVATE KEY-----
3. 保存支付宝公钥到项目中

payments/key目录下新建alipay_public_key.pem文件,用于保存支付宝的公钥文件。

将支付宝的公钥内容复制到alipay_public_key.pem文件中

[外链图片转存失败(img-DDjUzlOQ-1566909855145)(assets/%E6%94%AF%E4%BB%98%E5%AE%9D%E5%85%AC%E9%92%A5.png)]

-----BEGIN PUBLIC KEY-----
公钥
-----END PUBLIC KEY-----

在这里插入图片描述

4. 使用支付宝的sdk开发支付接口

SDK:https://docs.open.alipay.com/270/106291/

python版本的支付宝SDK文档:https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

安装命令:

pip install python-alipay-sdk --upgrade

后端提供发起支付的接口url地址

from rest_framework.views import APIView
from alipay import AliPay
from django.conf import settings
from orders.models import Order
from rest_framework.response import Response
from rest_framework import status
class AliapyAPIView(APIView):
    def post(self,request,order_number):
        """生成支付宝支付链接的地址"""
        # 接受订单信息
        try:
            # order_status=0 表示未支付宝
            order = Order.objects.get(order_number=order_number,order_status=0)
        except Order.DoesNotExist:
            return Response({"message":"对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid= settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug = settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no=order.order_number, # 订单号
            total_amount=float(order.real_price),   # 订单总金额[单位:元]
            subject=order.order_title,       # 订单标题
            return_url=settings.ALIAPY_CONFIG["return_url"], # 同步通知地址
            notify_url=settings.ALIAPY_CONFIG["notify_url"],  # 异步通知地址
        )

        pay_url = settings.ALIAPY_CONFIG["gateway_url"] + order_string

        return Response({"pay_url":pay_url})
在配置文件中编辑支付宝的配置信息[实际的值根据自己的账号而定]

setttins/dev.py,代码:

# 支付宝配置信息
ALIAPY_CONFIG = {
    # "gateway_url": "https://openapi.alipay.com/gateway.do?", # 真实支付宝网关地址
    "gateway_url": "https://openapi.alipaydev.com/gateway.do?", # 沙箱支付宝网关地址
    "appid": "20*********3592",
    "app_notify_url": None,
    "app_private_key_path": os.path.join(os.path.dirname(BASE_DIR), "luffyapi/apps/payments/key/app_private_key.pem"),
    "alipay_public_key_path": os.path.join(os.path.dirname(BASE_DIR), "luffyapi/apps/payments/key/alipay_public_key.pem"),
    "sign_type": "RSA2",
    "debug": False,
    "return_url": "http://www.luffycity.cn:8080/pay/result",
    "notify_url": "http://api.luffycity.cn:8000/pay/result",
}

注册url地址:payments/urls.py,代码:

from django.urls import path, re_path
from . import views
urlpatterns = [
    re_path(r'(?P<order_number>\d+)/alipay/', views.AliapyAPIView.as_view() ),
]

总路由,代码:

    path('payments/', include("payments.urls")),
前端点击"支付宝支付",会生成订单,生成订单返回订单号以后,再次请求后端的获取支付api的url地址
<template>
  <div class="cart">
   ...
              </el-col>
              <el-col :span="8" class="count">实付款: <span>¥{{(real_total - credit/credit_to_money).toFixed(2)}}</span></el-col>
              <el-col :span="4" class="cart-pay"><span @click="payHander">去支付</span></el-col>
            </el-row>
        </div>
    </div>
    <Footer/>
  </div>
</template>

<script>
  import Header from "./common/Header"
  import Footer from "./common/Footer"
  export default {
    name:"Order",
    data(){
      return {
          user_credit: localStorage.user_credit || sessionStorage.user_credit,  // 用户积分
          credit_to_money: localStorage.credit_to_money || sessionStorage.credit_to_money,  // 积分换算比例
          course_list:[],     // 勾选商品
          pay_type: 1,        // 支付方式
          use_credit: false,  // 是否使用了优惠券
          credit: 0,          // 积分
          use_coupon: false,  // 优惠券ID,0表示没有使用优惠券
          coupon: 0,          // 优惠券ID,0表示没有使用优惠券
          coupon_list:[],     // 优惠券列表
          total: 0,           // 购物车中商品总金额
          real_total: 0,      // 实付金额
      }
    },
    components:{
      Header,
      Footer,
    },
    created(){
      this.check_user_login();
      this.get_selected_course();
      this.get_user_coupon();
    },
    watch:{
        coupon(){
            // 每次用户优惠券时,总价格都要重新计算
            this.total = this.get_total();
            this.real_total = this.get_total(true);
        },
        use_coupon(){
            if(!this.use_coupon){
                // 当用户不使用优惠券时,把用户当前选择的优惠券ID重置为0
                this.coupon = 0;
            }
            // 每次用户优惠券时,总价格都要重新计算
            this.total = this.get_total();
            this.real_total = this.get_total(true);
        },
        use_credit(){
            if(!this.use_credit){
                // 地方用户不适用积分抵扣时,把用户当前设置的抵扣积分进行重置
                this.credit = 0;
            }

            // 每次用户优惠券时,总价格都要重新计算
            this.total = this.get_total();
            this.real_total = this.get_total(true);
        }
    },
    methods: {
     ...
      payHander(){
          // 生成订单
          this.$axios.post(`${this.$settings.Host}/orders/`,{
              pay_type: this.pay_type,
              credit: this.credit,
              coupon: this.coupon
          },{
              headers:{
                "Authorization":"jwt " + this.check_user_login(),
              }
          }).then(response=>{
              // 下单成功,获取支付链接
              this.get_alipay_payment_url(response.data.order_number);

          }).catch(error=>{
              console.log(error.response);
          })

      },
      get_alipay_payment_url(order_number){
          // 获取支付宝的支付地址
          this.$axios.post(`${this.$settings.Host}/payments/${order_number}/alipay/`).then(response=>{
              console.log(response.data.pay_url);
              // 页面跳转
              location.href=response.data.pay_url;

          }).catch(error=>{
              console.log(error.response);
          })
      }
    }
  }
</script>

完成了上面的功能以后,我们就可以在沙箱环境中进行支付宝的付款了,但是我们会接受到支付宝界面那边跳转回来的同步通知结果,跳转回到我们的客户端页面,

返回的结果.,我们不能保证一定是支付宝返回的,所以我们需要对照官方文档,查看这些参数的作用,

https://docs.open.alipay.com/api_1/alipay.trade.page.pay

http://www.luffycity.cn:8080/pay/result?charset=utf-8&out_trade_no=201908231722270000013680&method=alipay.trade.page.pay.return&total_amount=809.10&sign=D%2BjTb1QHIG5OGrCMgzDAkb6FhgbvuwFMAGHZ6TpCTJyk7EhpzdxXYNX5MLJzYmveGkcjC59n7HrgTGz1vIa3LsMdzTL6ixxHLtE03hFQX4z6voSNmHkic3GRuNGn1hBN0OJLKUsLax%2FxA24DY8UUZ7%2FndQizAC5wUQtMRatG0Jx1z5gaN1xB0u5sEnTSHfHLqaUgIR8Bg8hHT8KAkTuxkn7kOZOORDhICTPkKizgy0E%2F6bjIupbG6qC1aKpUrsokz6BdjbORtQntpN9zZPOZzCzejvjKGrdgXnYc3tQeBXU%2Fch7y3LYol0OW5ickYBuZkXLhNAjsUG9yX1EO0Tnw2w%3D%3D&trade_no=2019082322001439881000079299&auth_app_id=2016091600523592&version=1.0&app_id=2016091600523592&sign_type=RSA2&seller_id=2088102175868026&timestamp=2019-08-23%2017%3A24%3A37

支付成功的模板

Success.vue,代码;

<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="">重要!微信扫码关注获得学习通知&amp;课程更新提醒!否则将严重影响学习进度和课程体验!</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>

路由router.index.js

    {
      name: 'Success',
      path: '/pay/result',
      component: Success
    },

后端接受支付结果

支付宝会返回的参数如下列表:

http://www.luffycity.cn:8080?
charset=utf-8&
out_trade_no=2019040217080000000010976&
method=alipay.trade.page.pay.return&
total_amount=1206.44&
sign=XKJG5826fH%2F9%2B3jCWw2ODjlc%2FuGLfqmr5RnimSAqrh%2B5bFkWcbLDh5V6VYtMqCpwnYp3FuGPqEeUeRO6WK62Qz0Q5nQGOA394IdxPfTOzry7PXuwYf41PCbDq53yg7vCYrobz4Tt8uajeADJLJwIsL%2F%2B88vbDEISUDUujL4442kl3oLh3EDD8DxZc2LLsv1Z%2FEFGJMfcTA47A4T7qmjB%2BbLKJetZZBISdt9RDL0q8A%2BAfb8B3Ux1nq%2F0EiNGiwIlWC1pvUCHK2UXMJW3kmgU9P9Zoujrj4ER28oieQt6Rt4gQXeah5uYtAMkftWfZpiyu%2FjUkr6iRx%2B4mP5IFz4Uew%3D%3D&
trade_no=2019040222001439881000005802&
auth_app_id=2016091600523592&
version=1.0&
app_id=2016091600523592&
sign_type=RSA2&
seller_id=2088102175868026&
timestamp=2019-04-02%2017%3A13%3A15

后端实现处理支付宝通知的视图,代码:

payments/views.py

from rest_framework.views import APIView
from alipay import AliPay
from django.conf import settings
from orders.models import Order
from rest_framework.response import Response
from rest_framework import status
from datetime import datetime
from coupon.models import UserCoupon
from django.db import transaction
import logging

log = logging.getLogger("django")

class AliapyAPIView(APIView):
    def post(self,request,order_number):
        """生成支付宝支付链接的地址"""
        # 接受订单信息
        try:
            # order_status=0 表示未支付宝
            order = Order.objects.get(order_number=order_number,order_status=0)
        except Order.DoesNotExist:
            return Response({"message":"对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid= settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug = settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no=order.order_number, # 订单号
            total_amount=float(order.real_price),   # 订单总金额[单位:元]
            subject=order.order_title,       # 订单标题
            return_url=settings.ALIAPY_CONFIG["return_url"], # 同步通知地址
            notify_url=settings.ALIAPY_CONFIG["notify_url"],  # 异步通知地址
        )

        pay_url = settings.ALIAPY_CONFIG["gateway_url"] + order_string

        return Response({"pay_url":pay_url})


class AlipayResultAPIView(APIView):
    """
    支付宝支付结果的通知处理
    """
    def get(self,request):
        # for rest_framework users
        data = request.query_params.dict()

        signature = data.pop("sign")

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid= settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug = settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # verification
        success = alipay.verify(data, signature)
        if success:

            # 修改订单状态
            out_trade_no = data.get("out_trade_no")
            try:
                order = Order.objects.get(order_number=out_trade_no, order_status=0)
            except Order.DoesNotExist:
                return Response({"message": "对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

            with transaction.atomic():
                # 记录事务的回滚点
                save_id = transaction.savepoint()

                order.order_status = 1
                order.pay_time = datetime.now()
                order.save()

                # 如果订单中使用了优惠券,则优惠券的使用状态要调整
                if order.coupon > 0:
                    user_coupon_id = order.coupon
                    try:
                        user_coupon = UserCoupon.objects.get(pk=user_coupon_id,is_use=False)
                        user_coupon.is_use = True
                        user_coupon.save()

                    except UserCoupon.DoesNotExist:
                        log.error("生成订单支付结果有误!优惠券发生异常!")
                        transaction.savepoint_rollback(save_id)

                # 如果用户使用了积分,则扣除相应积分
                if order.credit > 0:
                    user = order.user
                    user.credit = user.credit - order.credit
                    if user.credit > 0:
                        user.save()
                    else:
                        log.error("生成订单支付结果有误!积分计算有误!")
                        transaction.savepoint_rollback(save_id)

                # 记录用户购买商品的记录信息


            data = {
                "order_number": order.order_number,
                "pay_time": order.pay_time,
                "real_price": order.real_price,
            }

            return Response(data)

        else:
            return Response({"message":"支付失败!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def post(self,request):

        return Response({"message":"ok"})

后端完成 支付宝支付结果的处理并更新订单和购买记录

users/models.py,模型代码:

from courses.models import Course
class UserCourse(BaseModel):
    pay_choices = (
        (1, '用户购买'),
        (2, '免费活动'),
        (3, '活动赠品'),
        (4, '系统赠送'),
    )
    user = models.ForeignKey(User, related_name='user_courses', on_delete=models.DO_NOTHING,verbose_name="用户")
    course = models.ForeignKey(Course, related_name='course_users', on_delete=models.DO_NOTHING, verbose_name="课程")
    trade_no = models.CharField(max_length=128, null=True,blank=True, verbose_name="支付平台的账单号")
    buy_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name="购买方式")
    pay_time = models.DateTimeField(null=True,blank=True, verbose_name="购买时间")
    out_time = models.DateTimeField(null=True,blank=True, verbose_name="过期时间")

    class Meta:
        db_table = 'ly_user_course'
        verbose_name = '课程购买记录'
        verbose_name_plural = verbose_name

数据迁移

python manage.py makemigrations
python manage.py migrate
实现同步结果通知的API接口

payment/views.py,视图代码:

from rest_framework.views import APIView
from alipay import AliPay
from django.conf import settings
from orders.models import Order
from rest_framework.response import Response
from rest_framework import status
from datetime import datetime
from coupon.models import UserCoupon
from django.db import transaction
from users.models import UserCourse
from courses.models import CourseExpire
from users.models import User
import logging

log = logging.getLogger("django")

class AliapyAPIView(APIView):
    def post(self,request,order_number):
        """生成支付宝支付链接的地址"""
        # 接受订单信息
        try:
            # order_status=0 表示未支付宝
            order = Order.objects.get(order_number=order_number,order_status=0)
        except Order.DoesNotExist:
            return Response({"message":"对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid= settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug = settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no=order.order_number, # 订单号
            total_amount=float(order.real_price),   # 订单总金额[单位:元]
            subject=order.order_title,       # 订单标题
            return_url=settings.ALIAPY_CONFIG["return_url"], # 同步通知地址
            notify_url=settings.ALIAPY_CONFIG["notify_url"],  # 异步通知地址
        )

        pay_url = settings.ALIAPY_CONFIG["gateway_url"] + order_string

        return Response({"pay_url":pay_url})


class AlipayResultAPIView(APIView):
    """
    支付宝支付结果的通知处理
    """
    def get(self,request):
        # for rest_framework users
        data = request.query_params.dict()

        signature = data.pop("sign")

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid= settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug = settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # verification
        success = alipay.verify(data, signature)
        if success:

            # 修改订单状态
            out_trade_no = data.get("out_trade_no")
            try:
                order = Order.objects.get(order_number=out_trade_no, order_status=0)
            except Order.DoesNotExist:
                return Response({"message": "对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

            with transaction.atomic():
                # 记录事务的回滚点
                save_id = transaction.savepoint()

                order.order_status = 1
                order.pay_time = datetime.now()
                order.save()

                # 如果订单中使用了优惠券,则优惠券的使用状态要调整
                if order.coupon > 0:
                    user_coupon_id = order.coupon
                    try:
                        user_coupon = UserCoupon.objects.get(pk=user_coupon_id,is_use=False)
                        user_coupon.is_use = True
                        user_coupon.save()

                    except UserCoupon.DoesNotExist:
                        log.error("生成订单支付结果有误!优惠券发生异常!")
                        transaction.savepoint_rollback(save_id)

                # 如果用户使用了积分,则扣除相应积分
                if order.credit > 0:
                    user = User.objects.get(pk=order.user_id)
                    user.credit = user.credit - order.credit
                    if user.credit > 0:
                        user.save()
                    else:
                        log.error("生成订单支付结果有误!积分计算有误!")
                        transaction.savepoint_rollback(save_id)

                # 记录用户购买商品的记录信息
                order_course = order.order_courses.all()
                for item in order_course:

                    # 获取本次购买课程的有效期选项
                    try:
                        """有效期选项"""
                        course_expire = CourseExpire.objects.get(expire_time=item.expire, course=item.course)
                        expire = course_expire.expire_time
                        timer = expire * 24 * 60 * 60
                        out_timestamp = order.pay_time.timestamp() + timer
                        # 把数值时间戳转变成日期对象
                        out_time = datetime.fromtimestamp(out_timestamp)

                    except CourseExpire.DoesNotExist:
                        """永久有效,默认过期时间200年后"""
                        out_time = "2199-01-01 00:00:00"

                    """
                        判断之前当前用户是否购买过同一商品,如果购买了同一商品,则在前面的过期时间基础上增加时间
                        过期时间,也需要判断,如果现在已经过期了,则购买完课程以后的过期时间 = 现在 + 有效期
                                          如果现在没有过期,则购买完课程以后的过期时间 = 过期时间 + 有效期
                                          
                        购买完成,我们扣除了积分,但是我们也要针对本次消费的积分进行积分流水记录! Credit
                    """
                    UserCourse.objects.create(
                        user=user,
                        course=item.course,
                        trade_no=data.get("trade_no"),
                        buy_type=1,
                        pay_time=order.pay_time,
                        out_time=out_time,
                        orders=0,
                    )

            data = {
                "order_number": order.order_number,
                "pay_time": order.pay_time,
                "real_price": order.real_price,
            }

            return Response(data)

        else:
            return Response({"message":"支付失败!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def post(self,request):

        return Response({"message":"ok"})

payment/urls.py,路由代码:

from django.urls import path, re_path
from . import views
urlpatterns = [
    re_path(r'(?P<order_number>\d+)/alipay/', views.AliapyAPIView.as_view() ),
    path(r'alipay/result/', views.AlipayResultAPIView.as_view() ),
]

客户端转发支付宝平台返回的同步通知结果,代码:
Success.vue

<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="">重要!微信扫码关注获得学习通知&amp;课程更新提醒!否则将严重影响学习进度和课程体验!</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(){
      // 把地址栏上面的支付结果,转发给后端
      this.get_result();
    },
    methods:{
       get_result(){
           this.$axios.get(`${this.$settings.Host}/payments/alipay/result/`+ location.search).then(response=>{
               console.log(response.data);
          }).catch(error=>{
              console.log(error.response);
          });
       }
    },
    components:{
      Header,
      Footer,
    }
  }
</script>

接受异步支付结果

from rest_framework.views import APIView
from alipay import AliPay
from django.conf import settings
from orders.models import Order
from rest_framework.response import Response
from rest_framework import status
from datetime import datetime
from coupon.models import UserCoupon
from django.db import transaction
from users.models import UserCourse
from courses.models import CourseExpire
from users.models import User
import logging

log = logging.getLogger("django")

class AliapyAPIView(APIView):
    def post(self,request,order_number):
        """生成支付宝支付链接的地址"""
        # 接受订单信息
        try:
            # order_status=0 表示未支付宝
            order = Order.objects.get(order_number=order_number,order_status=0)
        except Order.DoesNotExist:
            return Response({"message":"对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid= settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug = settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no=order.order_number, # 订单号
            total_amount=float(order.real_price),   # 订单总金额[单位:元]
            subject=order.order_title,       # 订单标题
            return_url=settings.ALIAPY_CONFIG["return_url"], # 同步通知地址
            notify_url=settings.ALIAPY_CONFIG["notify_url"],  # 异步通知地址
        )

        pay_url = settings.ALIAPY_CONFIG["gateway_url"] + order_string

        return Response({"pay_url":pay_url})


class AlipayResultAPIView(APIView):
    """
    支付宝支付结果的通知处理
    """
    def get(self,request):
        # for rest_framework users
        data = request.query_params.dict()

        # 修改订单状态和购买记录,消费记录
        return self.result(data)

    def post(self,request):
        """处理异步通知结果
        1. 线下开发是不起作用的,因为外网的支付宝服务器无法访问我们的局域网地址
        2. 如果使用了代理服务器,通过代理方式提供对外访问时,异步通知会在转发请求的过程中存在丢失数据的可能
        """
        data = request.data.dict()

        return self.result(data)

    def result(self,data):
        signature = data.pop("sign")

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid=settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug=settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # verification
        success = alipay.verify(data, signature)
        if success:

            # 修改订单状态
            out_trade_no = data.get("out_trade_no")
            try:
                order = Order.objects.get(order_number=out_trade_no, order_status=0)
            except Order.DoesNotExist:
                return Response({"message": "对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

            with transaction.atomic():
                # 记录事务的回滚点
                save_id = transaction.savepoint()

                order.order_status = 1
                order.pay_time = datetime.now()
                order.save()

                # 如果订单中使用了优惠券,则优惠券的使用状态要调整
                if order.coupon > 0:
                    user_coupon_id = order.coupon
                    try:
                        user_coupon = UserCoupon.objects.get(pk=user_coupon_id, is_use=False)
                        user_coupon.is_use = True
                        user_coupon.save()

                    except UserCoupon.DoesNotExist:
                        log.error("生成订单支付结果有误!优惠券发生异常!")
                        transaction.savepoint_rollback(save_id)

                user = User.objects.get(pk=order.user_id)
                # 如果用户使用了积分,则扣除相应积分
                if order.credit > 0:
                    user.credit = user.credit - order.credit
                    if user.credit > 0:
                        user.save()
                    else:
                        log.error("生成订单支付结果有误!积分计算有误!")
                        transaction.savepoint_rollback(save_id)

                # 记录用户购买商品的记录信息
                order_course = order.order_courses.all()
                for item in order_course:

                    # 获取本次购买课程的有效期选项
                    try:
                        """有效期选项"""
                        course_expire = CourseExpire.objects.get(expire_time=item.expire, course=item.course)
                        expire = course_expire.expire_time
                        timer = expire * 24 * 60 * 60
                        out_timestamp = order.pay_time.timestamp() + timer
                        # 把数值时间戳转变成日期对象
                        out_time = datetime.fromtimestamp(out_timestamp)

                    except CourseExpire.DoesNotExist:
                        """永久有效,默认过期时间200年后"""
                        out_time = "2199-01-01 00:00:00"

                    """
                        判断之前当前用户是否购买过同一商品,如果购买了同一商品,则在前面的过期时间基础上增加时间
                        过期时间,也需要判断,如果现在已经过期了,则购买完课程以后的过期时间 = 现在 + 有效期
                                          如果现在没有过期,则购买完课程以后的过期时间 = 过期时间 + 有效期

                        购买完成,我们扣除了积分,但是我们也要针对本次消费的积分进行积分流水记录! Credit
                    """
                    UserCourse.objects.create(
                        user=user,
                        course=item.course,
                        trade_no=data.get("trade_no"),
                        buy_type=1,
                        pay_time=order.pay_time,
                        out_time=out_time,
                        orders=0,
                    )

            data = {
                "order_number": order.order_number,
                "pay_time": order.pay_time,
                "real_price": order.real_price,
            }

            return Response(data)

        else:
            return Response({"message": "支付失败!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
支付成功页面展示当前订单信息

api后端视图增加返回数据信息包括课程信息:

from rest_framework.views import APIView
from alipay import AliPay
from django.conf import settings
from orders.models import Order
from rest_framework.response import Response
from rest_framework import status
from datetime import datetime
from coupon.models import UserCoupon
from django.db import transaction
from users.models import UserCourse
from courses.models import CourseExpire
from users.models import User
import logging

log = logging.getLogger("django")

class AliapyAPIView(APIView):
    def post(self,request,order_number):
        """生成支付宝支付链接的地址"""
        # 接受订单信息
        try:
            # order_status=0 表示未支付宝
            order = Order.objects.get(order_number=order_number,order_status=0)
        except Order.DoesNotExist:
            return Response({"message":"对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid= settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug = settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no=order.order_number, # 订单号
            total_amount=float(order.real_price),   # 订单总金额[单位:元]
            subject=order.order_title,       # 订单标题
            return_url=settings.ALIAPY_CONFIG["return_url"], # 同步通知地址
            notify_url=settings.ALIAPY_CONFIG["notify_url"],  # 异步通知地址
        )

        pay_url = settings.ALIAPY_CONFIG["gateway_url"] + order_string

        return Response({"pay_url":pay_url})


class AlipayResultAPIView(APIView):
    """
    支付宝支付结果的通知处理
    """
    def get(self,request):
        # for rest_framework users
        data = request.query_params.dict()

        # 修改订单状态和购买记录,消费记录
        return self.result(data)

    def post(self,request):
        """处理异步通知结果
        1. 线下开发是不起作用的,因为外网的支付宝服务器无法访问我们的局域网地址
        2. 如果使用了代理服务器,通过代理方式提供对外访问时,异步通知会在转发请求的过程中存在丢失数据的可能
        """
        data = request.data.dict()

        return self.result(data)

    def result(self,data):
        signature = data.pop("sign")

        # 创建支付宝的sdk对象
        alipay = AliPay(
            appid=settings.ALIAPY_CONFIG["appid"],
            app_notify_url=settings.ALIAPY_CONFIG["app_notify_url"],  # 默认回调url
            app_private_key_path=settings.ALIAPY_CONFIG["app_private_key_path"],
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_path=settings.ALIAPY_CONFIG["alipay_public_key_path"],
            sign_type=settings.ALIAPY_CONFIG["sign_type"],  # RSA 或者 RSA2
            debug=settings.ALIAPY_CONFIG["debug"],  # 默认False
        )

        # verification
        success = alipay.verify(data, signature)
        if success:

            # 修改订单状态
            out_trade_no = data.get("out_trade_no")
            try:
                order = Order.objects.get(order_number=out_trade_no, order_status=0)
            except Order.DoesNotExist:
                return Response({"message": "对不起当前订单不存在或者已经支付了!"}, status=status.HTTP_400_BAD_REQUEST)

            with transaction.atomic():
                # 记录事务的回滚点
                save_id = transaction.savepoint()

                order.order_status = 1
                order.pay_time = datetime.now()
                order.save()

                # 如果订单中使用了优惠券,则优惠券的使用状态要调整
                if order.coupon > 0:
                    user_coupon_id = order.coupon
                    try:
                        user_coupon = UserCoupon.objects.get(pk=user_coupon_id, is_use=False)
                        user_coupon.is_use = True
                        user_coupon.save()

                    except UserCoupon.DoesNotExist:
                        log.error("生成订单支付结果有误!优惠券发生异常!")
                        transaction.savepoint_rollback(save_id)

                user = User.objects.get(pk=order.user_id)
                # 如果用户使用了积分,则扣除相应积分
                if order.credit > 0:
                    user.credit = user.credit - order.credit
                    if user.credit > 0:
                        user.save()
                    else:
                        log.error("生成订单支付结果有误!积分计算有误!")
                        transaction.savepoint_rollback(save_id)

                # 记录用户购买商品的记录信息
                order_course = order.order_courses.all()
                course_list = []
                for item in order_course:

                    # 获取本次购买课程的有效期选项
                    try:
                        """有效期选项"""
                        course_expire = CourseExpire.objects.get(expire_time=item.expire, course=item.course)
                        expire = course_expire.expire_time
                        timer = expire * 24 * 60 * 60
                        out_timestamp = order.pay_time.timestamp() + timer
                        # 把数值时间戳转变成日期对象
                        out_time = datetime.fromtimestamp(out_timestamp)

                    except CourseExpire.DoesNotExist:
                        """永久有效,默认过期时间200年后"""
                        out_time = "2199-01-01 00:00:00"

                    """
                        判断之前当前用户是否购买过同一商品,如果购买了同一商品,则在前面的过期时间基础上增加时间
                        过期时间,也需要判断,如果现在已经过期了,则购买完课程以后的过期时间 = 现在 + 有效期
                                          如果现在没有过期,则购买完课程以后的过期时间 = 过期时间 + 有效期

                        购买完成,我们扣除了积分,但是我们也要针对本次消费的积分进行积分流水记录! Credit
                    """
                    UserCourse.objects.create(
                        user=user,
                        course=item.course,
                        trade_no=data.get("trade_no"),
                        buy_type=1,
                        pay_time=order.pay_time,
                        out_time=out_time,
                        orders=0,
                    )

                    course_list.append({
                        "id": item.course.id,
                        "name": item.course.name,
                    })

            data = {
                "order_number": order.order_number,
                "pay_time": order.pay_time,
                "real_price": order.real_price,
                "course_list": course_list,
            }

            return Response(data)

        else:
            return Response({"message": "支付失败!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

客户端中Success.vue,代码:

<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">您已成功购买 {{result.course_list.length}} 门课程!</p>
              <p class="tips2">你还可以加入QQ群 <span>747556033</span> 学习交流</p>
          </div>
        </div>
        <div class="order-info">
            <p class="info1"><b>付款时间:</b><span>{{result.pay_time|timeformat}}</span></p>
            <p class="info2"><b>付款金额:</b><span >¥{{result.real_price}}</span></p>
            <p class="info3"><b>课程信息:</b>
              <span v-for="course in result.course_list">《{{course.name}}》</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="">重要!微信扫码关注获得学习通知&amp;课程更新提醒!否则将严重影响学习进度和课程体验!</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 {
        result:{},
      };
    },
    created(){
      // 把地址栏上面的支付结果,转发给后端
      this.get_result();
    },
    filters:{
        timeformat(time){
            // 时间格式化
            // 2019/04/02 10:27
            let current_obj = new Date(time);
            // 年份
            let Y = current_obj.getFullYear();
            // 月份
            let m = current_obj.getMonth()+1;
            m = m<10?"0"+m:m;
            // 日期
            let d = current_obj.getDate();
            d = d<10?"0"+d:d;
            // 小时
            let H = current_obj.getHours();
            H = H<10?"0"+H:H;
            // 分钟
            let i = current_obj.getMinutes();
            i = i<10?"0"+i:i;
            // 秒
            let s = current_obj.getSeconds();
            s = s<10?"0"+s:s;

            return `${Y}/${m}/${d} ${H}:${i}`;
        }
    },
    methods:{
       get_result(){
           this.$axios.get(`${this.$settings.Host}/payments/alipay/result/`+ location.search).then(response=>{
               this.result = response.data
          }).catch(error=>{
              console.log(error.response);
          });
       }
    },
    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>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值