学习猿地 python教程 django教程5 华为云+阿里云支付

# 华为云服务器+Nginx+Python3.7+Django2.2+支付宝支付接入部署

> 本次分享内容共分四个主要部署步骤

> 1.Django框架项目搭建部署

> 2.本地内网穿透测试

> 3.阿里支付宝支付接口部署

> 4.华为云服务器部署(弹性云服务器ECS)

## 一,搭建django项目基本结构

### 1.创建当前项目的虚拟环境

` python3 -m venv venv `

### 2.安装依赖环境

> ` pip install -r requirement.txt`

```

Django==2.2.6

Pillow==6.2.0

pkg-resources==0.0.0

pycrypto==2.6.1

pycryptodomex==3.7.2

python-alipay-sdk==1.10.1

pytz==2019.3

sqlparse==0.3.0

```

### 3.完成项目基本结构开发(略...)

> 按照基本模型和路由参考,完成以下基本项目功能:

> 1.商品列表页:需要完成数据的查询及模板中的数据展示

> 2.商品下单: 在商品列表页可以对商品进行下单购买,及对应的订单数据入库操作

> 3.订单列表: 可以查看到当前所有订单及订单的支付状态

> 4.完成发起支付请求,支付回调地址的视图函数定义(代码可以暂时不写)

模型 models.py

```python

from django.db import models

from django.utils.html import format_html

# Create your models here.

# 书籍模型

class Books(models.Model):

    # 书名

    name = models.CharField(max_length=30)

    # 价格

    price = models.FloatField()

    # 数量

    num = models.IntegerField(default=5)

    # 封面

    img_url = models.ImageField(upload_to="./static/uploads/",null=True)

    def loadimg(self):

        return format_html('' %(self.img_url,))

class Order(models.Model):

    # 订单号

    ordercode = models.IntegerField()

    # 下单用户id

    user = models.CharField(max_length=5,default='测试用户')

    # 购买产品id

    bookid = models.IntegerField()

    # 产品名称

    bookname = models.CharField(max_length=50)

    # 应付金额

    monery = models.FloatField()

    # 支付方式 0 支付宝

    paytype = models.IntegerField(default=0)

    # 支付状态 0未支付  1 已支付

    paystatus = models.IntegerField(default=0)

    # 订单创建时间

    ordertime = models.DateTimeField(auto_now_add=True)

    # 订单支付时间

    paytime = models.DateTimeField(null=True)

```

路由及对应视图函数 urls.py

```python

# 商品列表

path('',views.index),

# 创建订单

path('order/create', views.create_order,name="createOrder"),

# 发起支付请求

path('order/pay', views.order_pay_request,name="orderpay"),

# 支付宝回调地址

path('order/pay_result', views.order_pay_result,name="order_pay_result"),

# 订单列表,支付成功后的跳转页面

path('order/list', views.orderlist,name="orderlist"),

# 订单删除

path('order/delete', views.orderdel,name="orderdel"),

```

## 二,使用ngrok|花生壳内网穿透

> 推荐使用花生壳进行内网穿透测试

> http://service.oray.com/question/1664.html

## 三,支付宝接入

#### 1.登陆支付宝开放平台创建支付宝沙箱环境

> 支付宝开放平台 https://openhome.alipay.com/platform/appDaily.htm?tab=info

> 支付文档 https://docs.open.alipay.com/200/105311

#### 2.创建密钥

>  1.生成应用公钥和秘钥

>  2.把应用公钥赋值并配置到当前的沙箱环境中

>  3.配置完公钥后,沙箱环境配置会给一个支付宝公钥,复制并保存

>  4.在项目根目录中创建keys文件目录,存储应用私钥(rsa_private_key.txt)和支付宝公钥(rsa_public_key.txt)

ubuntu生成密钥和公钥

```shell

#打开终端输入 openssl

# 输入以下命令创建密钥

genrsa -out rsa_private_key.txt 2048

# 输入以下命令创建公钥

rsa -in rsa_private_key.txt -pubout -out rsa_public_key.txt

#输入 exit  推出 openssl

# ls 查看当前目录下创建的密钥和公钥

rsa_private_key.txt  rsa_public_key.txt

```

windows10 可以安装支付宝开放平台助手,创建密钥

> https://docs.open.alipay.com/291/105971

注意:在项目中配置 keys应用  私钥和支付宝公钥 放进来

```python

    1,在项目中 创建 keys 目录 里面放入 秘钥文件

    2,创建 rsa_private_key.txt 放入秘钥,加开始和结束的标记

        -----BEGIN RSA PRIVATE KEY-----

        .....

        -----END RSA PRIVATE KEY-----

    3,创建 rsa_public_key.txt 放入秘钥,加开始和结束的标记

        -----BEGIN PUBLIC KEY-----

        ....

        -----END PUBLIC KEY-----

```

#### 3. 项目中支付宝接口的配置 settings.py

1.参考以下配置在项目中进行支付宝相关配置

```python

# 支付宝相关配置

# APPID

# 沙箱APPID,生产环境须更改为应用APPID。

ALIPAY_APPID = "0000000000011111100"

# 网关

# 沙箱网关,生产环境须更改为正式网关。

ALIPAY_URL = "https://openapi.alipaydev.com/gateway.do"

# 正式网关,开发环境勿使用。

# ALIPAY_URL = "https://openapi.alipay.com/gateway.do"

# 回调通知地址

ALIPAY_NOTIFY_URL = "http://mv23102380.imwork.net/order/pay_result"

# 支付后的跳转地址

ALIPAY_RETURN_URL = 'http://mv23102380.imwork.net/order/pay_result'

# 应用私钥

APP_PRIVATE_KEY_PATH = os.path.join(BASE_DIR, 'keys/rsa_private_key.txt')

# 支付宝公钥

ALIPAY_PUBLIC_KEY_PATH = os.path.join(BASE_DIR, 'keys/rsa_public_key.txt')

```

#### 4.发起支付请求

1. 完成支付请求前基本开发

```python

# 首页

def index(request):

    data = Books.objects.all()

    return render(request,'index.html',{'data':data})

# 创建订单,发起支付请求

def create_order(request):

    # 接受表单数据

    id = request.POST.get('id')

    # 获取对象

    obj = Books.objects.get(id=id)

    # 检测库存

    if obj.num <= 0:

        return JsonResponse({'code':1,'msg':'当前商品已经售空'})

    # 创建订单

    # 订单号,购买产品id,应付金额

    data = {

        'ordercode':int(time.time())+random.randint(10000,99999),

        'bookid':obj.id,

        'bookname':obj.name,

        'monery':obj.price

    }

    orderobj = Order(**data)

    orderobj.save()

    print(f'订单创建成功,\r\n订单信息:{data}')

    # return HttpResponse('创建订单,发起支付请求')

    return order_pay_request(orderobj)

# 支付成功后的跳转页面

def orderlist(request):

    # 获取所有的订单数据

    data = Order.objects.all()

    for i in data:

        i.img = Books.objects.get(id=i.bookid).img_url

    return render(request,'orderlist.html',{'data':data})

# 订单删除

def orderdel(request):

    oid = request.GET.get('oid')

    obj = Order.objects.get(id=oid)

    obj.delete()

    return HttpResponseRedirect(reverse('orderlist'))

```

2. 导入支付宝支付接口类

> 在项目根目录创建utils包文件夹,创建pay.py模块写入支付接口类

> web\utils\pay.py

```python

from datetime import datetime

from Crypto.PublicKey import RSA

from Crypto.Signature import PKCS1_v1_5

from Crypto.Hash import SHA256

from urllib.parse import quote_plus

from urllib.parse import urlparse, parse_qs

from base64 import decodebytes, encodebytes

import json

class AliPay(object):

    """

    支付宝支付接口(PC端支付接口)

    """

    def __init__(self, appid, app_notify_url, app_private_key_path,

                alipay_public_key_path, return_url, debug=False):

        self.appid = appid

        self.app_notify_url = app_notify_url

        self.app_private_key_path = app_private_key_path

        self.app_private_key = None

        self.return_url = return_url

        with open(self.app_private_key_path) as fp:

            self.app_private_key = RSA.importKey(fp.read())

        self.alipay_public_key_path = alipay_public_key_path

        with open(self.alipay_public_key_path) as fp:

            self.alipay_public_key = RSA.importKey(fp.read())

        if debug is True:

            self.__gateway = "https://openapi.alipaydev.com/gateway.do"

        else:

            self.__gateway = "https://openapi.alipay.com/gateway.do"

    def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):

        biz_content = {

            "subject": subject,

            "out_trade_no": out_trade_no,

            "total_amount": total_amount,

            "product_code": "FAST_INSTANT_TRADE_PAY",

            # "qr_pay_mode":4

        }

        biz_content.update(kwargs)

        data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)

        return self.sign_data(data)

    def build_body(self, method, biz_content, return_url=None):

        data = {

            "app_id": self.appid,

            "method": method,

            "charset": "utf-8",

            "sign_type": "RSA2",

            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),

            "version": "1.0",

            "biz_content": biz_content

        }

        if return_url is not None:

            data["notify_url"] = self.app_notify_url

            data["return_url"] = self.return_url

        return data

    def sign_data(self, data):

        data.pop("sign", None)

        # 排序后的字符串

        unsigned_items = self.ordered_data(data)

        unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)

        sign = self.sign(unsigned_string.encode("utf-8"))

        # ordered_items = self.ordered_data(data)

        quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)

        # 获得最终的订单信息字符串

        signed_string = quoted_string + "&sign=" + quote_plus(sign)

        return signed_string

    def ordered_data(self, data):

        complex_keys = []

        for key, value in data.items():

            if isinstance(value, dict):

                complex_keys.append(key)

        # 将字典类型的数据dump出来

        for key in complex_keys:

            data[key] = json.dumps(data[key], separators=(',', ':'))

        return sorted([(k, v) for k, v in data.items()])

    def sign(self, unsigned_string):

        # 开始计算签名

        key = self.app_private_key

        signer = PKCS1_v1_5.new(key)

        signature = signer.sign(SHA256.new(unsigned_string))

        # base64 编码,转换为unicode表示并移除回车

        sign = encodebytes(signature).decode("utf8").replace("\n", "")

        return sign

    def _verify(self, raw_content, signature):

        # 开始计算签名

        key = self.alipay_public_key

        signer = PKCS1_v1_5.new(key)

        digest = SHA256.new()

        digest.update(raw_content.encode("utf8"))

        if signer.verify(digest, decodebytes(signature.encode("utf8"))):

            return True

        return False

    def verify(self, data, signature):

        if "sign_type" in data:

            sign_type = data.pop("sign_type")

        # 排序后的字符串

        unsigned_items = self.ordered_data(data)

        message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)

        return self._verify(message, signature)

```

3. 支付请求及回调函数封装

```python

# 发起支付请求   

def order_pay_request(orderobj):

    # 获取支付对象

    alipay = Get_AliPay_Object()

    # 生成支付的url

    query_params = alipay.direct_pay(

        subject=orderobj.bookname,  # 商品简单描述

        out_trade_no = orderobj.ordercode,# 用户购买的商品订单号

        total_amount = orderobj.monery,  # 交易金额(单位: 元 保留俩位小数)

    )

    # 支付宝网关地址(沙箱应用)

    pay_url = settings.ALIPAY_URL+"?{0}".format(query_params) 


    print('正在发起支付请求...')


    # 页面重定向到支付页面

    return HttpResponseRedirect(pay_url)

# 支付宝回调地址

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt

def order_pay_result(request):

    # 获取对象

    alipay = Get_AliPay_Object()

    if request.method == "POST":

        # 检测是否支付成功

        # 去请求体中获取所有返回的数据:状态/订单号

        from urllib.parse import parse_qs

        # name&age=123....

        body_str = request.body.decode('utf-8')

        post_data = parse_qs(body_str)

        post_dict = {}

        for k, v in post_data.items():

            post_dict[k] = v[0]

        sign = post_dict.pop('sign', None)

        status = alipay.verify(post_dict, sign)

        print('------------------开始------------------')

        print('POST验证', status)

        print(post_dict)

        out_trade_no = post_dict['out_trade_no']


        # 修改订单状态

        ass = {'paystatus':1,'paytime':post_dict['gmt_payment']}

        print(ass)

        Order.objects.filter(ordercode=out_trade_no).update(**ass)

        print('------------------结束------------------')

        # 修改订单状态:获取订单号

        return HttpResponse('success')

    else:

        params = request.GET.dict()

        sign = params.pop('sign', None)

        status = alipay.verify(params, sign)

        print('==================开始==================')

        print('GET验证', status)

        print('==================结束==================')

        return HttpResponse('')

# 支付宝对象创建方法

from web import settings

from utils.pay import AliPay


# AliPay 对象实例化

def Get_AliPay_Object():

    alipay = AliPay(

        appid=settings.ALIPAY_APPID,# APPID (沙箱应用)

        app_notify_url=settings.ALIPAY_NOTIFY_URL, # 回调通知地址

        return_url=settings.ALIPAY_RETURN_URL,# 支付完成后的跳转地址

        app_private_key_path=settings.APP_PRIVATE_KEY_PATH, # 应用私钥

        alipay_public_key_path=settings.ALIPAY_PUBLIC_KEY_PATH,  # 支付宝公钥

        debug=True,  # 默认False,

    )

    return alipay

```

## 四.上线华为云服务器部署(弹性云服务器ECS)

环境配置:

ubuntu 18.04

Python 3.6.8(python3.7亦可)

nginx version: nginx/1.14.0 (Ubuntu)

#### 1. 购买华为云服务器

> 文档 https://support.huaweicloud.com/ecs/index.html

#### 2. 上传到华为云服务器,安装依赖环境,启动项目测试

#### 3. 搭建uwsgi启动项目测试

    1.安装uwsgi

    sudo pip3 install uwsgi --upgrade


    2.安装完成后使用命令测试

    先进入项目目录,启动命令

    uwsgi --http :80 --chdir /home/alipay/web  --module web.wsgi --home /home/alipay/venv/bin


    # --home 指定virtualenv 路径,如果没有可以去掉。web.wsgi 指的是 web/wsgi.py 文件


    3.访问测试,启动成功后

    127.0.0.1:8080

#### 4. 配置uwsgi文件启动项目

    第一步:创建一个uwsgi.ini文件


    第二步:在django项目同级目录创建script目录,用于存放配置脚本等等

    /home/alipay/web/

        script/  web/ db.sqlite3 manage.py uwsgi.ini


    第三步:编辑uwsgi.ini文件内容如下: 目录参考个人目录进行修改

```shell

# uwsig使用配置文件启动

[uwsgi]

# 项目目录

chdir=/home/alipay/web/

# 指定项目的application

module=web.wsgi:application

# 指定sock的文件路径

socket=/home/alipay/web/script/uwsgi.sock

# 进程个数

workers=5

pidfile=/home/alipay/web/script/uwsgi.pid

# 指定IP端口

http=0.0.0.0:8000

# 指定静态文件

#static-map=/static=/home/alipay/web/static/

# 启动uwsgi的用户名和用户组

uid=www-data

gid=www-data

# 启用主进程

master=true

# 自动移除unix Socket和pid文件当服务停止的时候

vacuum=true

# 序列化接受的内容,如果可能的话

thunder-lock=true

# 启用线程

enable-threads=true

# 设置自中断时间

harakiri=30

# 设置缓冲

post-buffering=4096

# 设置日志目录

daemonize=/home/alipay/web/script/uwsgi.log

# 权限

chmod-socket = 666

chown-socket = www-data

```

#### 5.执行命令,启动项目测试

    uwsgi --ini uwsgi.ini

    在浏览器访问127.0.0.1:8000

#### 6,安装nginx

1.安装nginx

sudo apt-get install python-dev nginx

2.安装完成后,可以使用通过浏览器访问公网IP测试

3.创建项目的配置文件,或者直接修改原nginx配置文件都可以

vim /etc/nginx/sites-available/webtest.conf

```nginx文件内容根据个人情况自行调整

server {

    listen      80;

    server_name localtion;

    charset    utf-8;

    client_max_body_size 75M;

    location / {

        uwsgi_pass  unix:///home/alipay/web/script/uwsgi.sock;

        include    /etc/nginx/uwsgi_params;

    }


    #location /media  {

    #    alias /path/to/project/media;

    #}

    location /static {

        alias /home/alipay/web/static/;

    }

}

```

4.创建完配置文件后创建软连接

sudo ln -s /etc/nginx/sites-available/webtest.conf /etc/nginx/sites-enabled/webtest.conf

5.启动nginx,重新加载配置文件

nginx -s reload

#### 注意:

1.给当前项目设置访问权限。www-data

2.注意在nginx的配置中uwsgi_pass这一项

uwsgi_pass  unix:///home/yc/web/script/uwsgi.sock;

它需要找到你在启动uwsgi后的sock文件,并且要注意权限

3.在使用uwsgi --ini uwsgi.ini启动后会创建 .sock文件

那么在后面更新代码时不需要把 script目录下的文件都删除

只需要重启即可

uwsgi --reload xxx.pid


掌握学习方法,不如会弯道超车!

学习猿地:成就自己的只需一套精品!

你可能感兴趣的:(学习猿地 python教程 django教程5 华为云+阿里云支付)