码迷,mamicode.com
首页 > Windows程序 > 详细

Django框架中使用支付宝在线支付API

时间:2019-02-09 14:39:59      阅读:741      评论:0      收藏:0      [点我收藏+]

标签:upd   time()   查看   height   ams   修改   排序   hub   charset   

一.蚂蚁金服开发平台申请测试账号

  a. 登陆蚂蚁金服开放平台https://open.alipay.com/platform/manageHome.htm,在“开发中心”—“研发服务”下拉处选择沙箱作为测试环境。

  技术图片

  

  b. 填写相关信息,成功申请沙箱账号后进入沙箱环境页面

  技术图片

 

  c.下载沙箱钱包(目前仅支持安卓手机)

  技术图片

 

  d. 使用沙箱账号处的买家账号登陆下载好的沙箱钱包

  技术图片

二.制作公钥和密钥

  a.制作应用私钥和公钥 

    在沙箱应用处点击查看应用公钥

    技术图片

 

 

    在显示的应用公钥处点击修改

     技术图片

 

     在点击查看密钥生成方法

    技术图片

    

    根据教程生成密钥和公钥

    技术图片

    

    得到一个RSA密钥文件夹,里面有两个文件

    技术图片

  b. 制作支付宝公钥

    将应用公钥复制上传,点击保存会自动生成支付宝公钥

    技术图片

      

    查看支付宝公钥

      技术图片

 

三.Django程序开发

  a.创建一个django项目

    技术图片

  b.配置支付宝公钥应用私钥

    在私钥和公钥头围加上标记便于python识别

    技术图片

  c.代码编写

    首先安装依赖  pip3 install pycryptodome 

    SDK官方加密代码,可以在github上找到

 

技术图片
# _*_ coding=utf-8 _*_
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 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)
alipay.py

 

技术图片
from django.contrib import admin
from django.urls import path
from api import views


urlpatterns = [
    path(admin/, admin.site.urls),
    path(index/, views.index),
    path(result/, views.pay_result),
    path(update_order/, views.update_order),


]
urls.py
技术图片
import time
from urllib.parse import parse_qs
from django.conf import settings
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from utils.alipay import AliPay


def aliPay():
    obj = AliPay(
        appid="2016092400581835",                              # 支付宝沙箱里面的APPID
        app_notify_url="http://127.0.0.1:8800/update_order/",  # 如果支付成功,支付宝会向这个地址发送POST请求(校验是否支付已经完成),此地址要能够在公网进行访问
        return_url="http://127.0.0.1:8800/result/",            # 如果支付成功,重定向回到你的网站的地址。
        alipay_public_key_path="keys/alipay_public_2048.txt",  # 支付宝公钥
        app_private_key_path="keys/app_private_2048.txt",      # 应用私钥
        debug=True,  # 默认False,True表示使用沙箱环境测试
    )

    # 优化:在settings里面的设置后使用
    # obj = AliPay(
    #     appid=settings.APPID,
    #     app_notify_url=settings.NOTIFY_URL,
    #     return_url=settings.RETURN_URL,
    #     alipay_public_key_path=settings.PUB_KEY_PATH,
    #     app_private_key_path=settings.PRI_KEY_PATH,
    #     debug=True,
    # )
    return obj


@csrf_exempt
def index(request):
    if request.method == "GET":
        return render(request, index.html)

    # 实例化SDK里面的类AliPay
    alipay = aliPay()

    # 对购买的数据进行加密
    money = float(request.POST.get(price))  # 保留俩位小数
    out_trade_no = "x2" + str(time.time())  # 商户订单号
    # 1. 在数据库创建一条数据:状态(待支付)

    query_params = alipay.direct_pay(
        subject="充气式Saber",  # 商品简单描述
        out_trade_no=out_trade_no,  # 商户订单号
        total_amount=money,  # 交易金额(单位: 元 保留俩位小数)
    )
    # 拼接url,转到支付宝支付页面
    pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)

    return redirect(pay_url)


@csrf_exempt
def update_order(request):
    """
    支付成功后,支付宝向该地址发送的POST请求(用于修改订单状态)
    :param request:
    :return:
    """
    if request.method == POST:
        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]

        alipay = aliPay()

        sign = post_dict.pop(sign, None)
        status = alipay.verify(post_dict, sign)
        if status:
            # 1.修改订单状态
            out_trade_no = post_dict.get(out_trade_no)
            print(out_trade_no)
            # 2. 根据订单号将数据库中的数据进行更新

            return HttpResponse(支付成功)
        else:
            return HttpResponse(支付失败)
    return HttpResponse(‘‘)


@csrf_exempt
def pay_result(request):
    """
    支付完成后,跳转回的地址
    :param request:
    :return:
    """
    params = request.GET.dict()
    sign = params.pop(sign, None)

    alipay = aliPay()

    status = alipay.verify(params, sign)

    if status:
        return HttpResponse(支付成功)
    return HttpResponse(支付失败)
views.py

 

 

 

  d.进行测试

    启动项目,访问index页面,输入支付金额,点击支付

    技术图片 

 

    使用下载的沙箱钱包扫描付款

    技术图片

 

  

   付款成功

  技术图片

  

  最后会重定向到return_url

    技术图片

 

注意:配置文件信息可以在setting中设置,要完成全部测试需要有一个公网IP和服务器,来测试支付宝向app_notify_url发送的POST请求

ALLOWED_HOSTS = [*, ]
# 支付相关配置
APPID = "***6082500309412"
NOTIFY_URL = "http://127.0.0.1/update_order/"
RETURN_URL = "http://127.0.0.1/pay_result/"
PRI_KEY_PATH = "keys/app_private_2048.txt"
PUB_KEY_PATH = "keys/alipay_public_2048.txt"

 

 

  

  

   

 

 

  

 

Django框架中使用支付宝在线支付API

标签:upd   time()   查看   height   ams   修改   排序   hub   charset   

原文地址:https://www.cnblogs.com/zivli/p/10357345.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!