• Django实现支付宝付款和微信支付


    支付宝支付和微信支付是当今互联网产品常用的功能,我使用Django Rest Framework实现了网页上支付宝支付和微信支付的一个通用服务,提供rpc接口给其他服务,包括获取支付宝支付页面url的rpc接口、支付宝支付成功异步回调http接口、获取微信支付二维码rpc接口、主动查询微信订单是否支付的rpc接口等。

    支付宝网站支付需要蚂蚁金服开放平台账号,创建应用、配置秘钥等步骤请参考:蚂蚁金服支付宝电脑网站支付快速接入

    微信网站支付需要到微信支付官网注册服务商账号,

    目录结构如下:

    1、models.py

     1 from django.db import models
     2 from django.contrib.postgres.fields import ArrayField
     3 
     4 
     5 # Create your models here.
     6 
     7 
     8 class BaseModel(models.Model):
     9     """
    10         基础模型
    11     """
    12     created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    13     updated_time = models.DateTimeField(auto_now=True, verbose_name="修改时间")
    14     created_by = models.IntegerField(verbose_name="创建人ID")
    15     updated_by = models.IntegerField(verbose_name="修改人ID")
    16     is_active = models.BooleanField(default=True, verbose_name='是否正常')
    17 
    18     class Meta:
    19         abstract = True
    20 
    21 
    22 class Alipay(BaseModel):
    23     """
    24         支付
    25     """
    26 
    27     subject = models.CharField(max_length=256, verbose_name="订单标题")
    28     out_trade_no = models.CharField(max_length=64, unique=True, verbose_name="唯一订单号")
    29     trade_no = models.CharField(default="", max_length=64, verbose_name="支付宝系统中的交易流水号")
    30     total_amount = models.DecimalField(max_digits=11, decimal_places=2, verbose_name="订单的资金总额")
    31     return_url = models.CharField(max_length=500, verbose_name="支付完成同步跳转地址")
    32     notify_url = models.CharField(max_length=500, verbose_name="支付完成异步通知rpc地址")
    33     pay_time = models.DateTimeField(null=True, blank=True, verbose_name="支付时间")
    34     pay_nos = ArrayField(models.CharField(max_length=100), default=[], verbose_name='同一订单的支付ID数组')
    35 
    36     class Meta:
    37         verbose_name = "阿里支付"
    38         verbose_name_plural = verbose_name
    39         ordering = ('-created_time',)
    40 
    41 
    42 class Wxorder(BaseModel):
    43     """
    44         订单
    45     """
    46     body = models.CharField(max_length=256, verbose_name="商品描述")
    47     out_trade_no = models.CharField(max_length=64, unique=True, verbose_name="订单号")
    48     transaction_id = models.CharField(default="", max_length=64, verbose_name="微信支付订单号")
    49     total_fee = models.BigIntegerField(verbose_name="订单的资金总额,单位为分")
    50     product_id = models.CharField(max_length=16, verbose_name="商品ID")
    51     notify_url = models.CharField(max_length=500, verbose_name="支付完成通知url")
    52     pay_time = models.DateTimeField(null=True, blank=True, verbose_name="支付时间")
    53 
    54     class Meta:
    55         verbose_name = "微信订单"
    56         verbose_name_plural = verbose_name
    57         ordering = ('-created_time',)
    58 
    59 
    60 class Wxpay(BaseModel):
    61     """
    62         微信支付
    63     """
    64     out_trade_no = models.CharField(null=True, blank=True, max_length=64, verbose_name="订单号")
    65     pay_no = models.CharField(null=True, blank=True, max_length=64, unique=True, verbose_name="支付唯一订单号")
    66     code_url = models.CharField(null=True, blank=True, max_length=100, verbose_name="二维码地址")
    67     nonce_str = models.CharField(null=True, blank=True, max_length=32, verbose_name="随机字符串")
    68 
    69     class Meta:
    70         verbose_name = "微信支付"
    71         verbose_name_plural = verbose_name
    72         ordering = ('-created_time',)

    2、serializers.py:

     1 from django.conf import settings
     2 from rest_framework import serializers
     3 
     4 from pay.models import Alipay, Wxpay
     5 
     6 
     7 class BaseSerializer(serializers.ModelSerializer):
     8     created_time = serializers.DateTimeField(format=settings.DATETIME_FORMAT, read_only=True)
     9     updated_time = serializers.DateTimeField(format=settings.DATETIME_FORMAT, read_only=True)
    10     is_active = serializers.BooleanField(read_only=True)
    11 
    12     class Meta:
    13         model = None
    14 
    15 
    16 class AlipaySerializer(BaseSerializer):
    17     """
    18         阿里支付序列化类
    19     """
    20 
    21     class Meta:
    22         model = Alipay
    23         fields = "__all__"
    24 
    25 
    26 class WxpaySerializer(BaseSerializer):
    27     """
    28         阿里支付序列化类
    29     """
    30 
    31     class Meta:
    32         model = Wxpay
    33         fields = "__all__"

    3、views.py:

      1 # -*- coding=utf-8 -*-
      2 # Create your views here.
      3 import time
      4 import dicttoxml
      5 
      6 from jsonrpc import jsonrpc_method
      7 from rest_framework.decorators import list_route
      8 from rest_framework.response import Response
      9 from rest_framework.viewsets import ModelViewSet
     10 from rest_framework.views import APIView
     11 from rest_framework_xml.parsers import XMLParser
     12 from rest_framework_xml.renderers import XMLRenderer
     13 from tokenauth.decorators import is_login
     14 
     15 from pay import utils
     16 from pay.weixin_pay import WeiXinPay, UnifiedOrderPay, OrderQuery
     17 from pay.UUIDTools import UUIDTools
     18 from pay.models import Alipay, Wxpay, Wxorder
     19 from pay.serializers import AlipaySerializer, WxpaySerializer
     20 from pay.utils import UnActiveModelMixin
     21 from pay.alipay import AliPay
     22 from PAY_SERVICE.settings.base import APPID, PRIVATE_KEY_PATH, 
     23     ALI_PUB_KEY_PATH, ALIPAY_CALLBACK_URL, 
     24     WXAPPID, WX_PAY_KEY, WX_MCH_ID, WXPAY_CALLBACK_URL
     25 
     26 NOTIFY_URL = ALIPAY_CALLBACK_URL + 'api/v1.0/pay/alipay/notify/'
     27 
     28 
     29 
     30 class AlipayViewSet(ModelViewSet):
     31     queryset = Alipay.objects.filter(is_active=True)
     32     serializer_class = AlipaySerializer
     33 
     34     @list_route(methods=['post'])
     35     def notify(self, request):
     36         """ 
     37             处理支付宝的notify_url
     38         :param request:
     39         :return:
     40         """
     41         processed_dict = {}
     42         for k, v in request.data.items():
     43             processed_dict[k] = v
     44         app_id = processed_dict.get('app_id')
     45         pay_no = processed_dict.get('out_trade_no')
     46         trade_no = processed_dict.get('trade_no')
     47         total_amount = processed_dict.get('total_amount')
     48         pay_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
     49 
     50         alipay = Alipay.objects.filter(pay_nos__contains=[pay_no]).values().first()
     51         if alipay is None:
     52             return Response("failed")
     53         if str(alipay.get('total_amount')) != str(total_amount):
     54             return Response("failed")
     55         if app_id != APPID:
     56             return Response("failed")
     57         if alipay.get('trade_no') != "":
     58             return Response("failed")
     59 
     60         sign = processed_dict.pop('sign', None)
     61 
     62         ali_pay = AliPay(
     63             appid=APPID,
     64             app_notify_url=NOTIFY_URL,
     65             app_private_key_path=PRIVATE_KEY_PATH,
     66             alipay_public_key_path=ALI_PUB_KEY_PATH,
     67             debug=True,  # 默认False,
     68             return_url=alipay.get('return_url')
     69         )
     70 
     71         is_verify = ali_pay.verify(processed_dict, sign)
     72 
     73         if is_verify is True:
     74             Alipay.objects.filter(pk=alipay.get('id')).update(pay_time=pay_time, trade_no=trade_no)
     75             ret = utils.request_thrift('TradingManager', 'notify',
     76                 settings.TRADING_RPC_IP, int(settings.TRADING_RPC_PORT),
     77                 alipay.get('out_trade_no'), str(pay_time))
     78             if ret == "success":
     79                 return Response("success")
     80 
     81 
     82 class WxpayViewSet(ModelViewSet):
     83     queryset = Wxpay.objects.filter(is_active=True)
     84     serializer_class = WxpaySerializer
     85     parser_classes = (XMLParser,)
     86     renderer_classes = (XMLRenderer,)
     87 
     88 
     89 @jsonrpc_method('pay.get_alipay_url')
     90 def get_alipay_url(request, subject, out_trade_no, total_amount, return_url, notify_url, user_id):
     91     recode = Alipay.objects.filter(out_trade_no=out_trade_no).values().first()
     92     if recode is not None:
     93         pay_no = UUIDTools.datetime_random()
     94         alipay = Alipay.objects.get(pk=recode.get('id'))
     95         alipay.pay_nos.append(pay_no)
     96         alipay.save()
     97     else:
     98         pay_no = out_trade_no
     99         Alipay.objects.create(subject=subject,
    100                               out_trade_no=out_trade_no,
    101                               total_amount=total_amount,
    102                               return_url=return_url,
    103                               notify_url=notify_url,
    104                               pay_nos=[pay_no],
    105                               created_by=user_id,
    106                               updated_by=user_id
    107                               )
    108 
    109     ali_pay = AliPay(
    110         appid=APPID,
    111         app_notify_url=NOTIFY_URL,
    112         app_private_key_path=PRIVATE_KEY_PATH,
    113         alipay_public_key_path=ALI_PUB_KEY_PATH,
    114         debug=True,  # 默认False,
    115         return_url=return_url
    116     )
    117 
    118     total_amount = "%.2f" % float(total_amount)
    119     url = ali_pay.direct_pay(
    120         subject=subject,
    121         out_trade_no=pay_no,
    122         total_amount=total_amount
    123     )
    124     # 沙箱环境网关
    125     # alipay_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    126     # 正式环境网关
    127     alipay_url = "https://openapi.alipay.com/gateway.do?{data}".format(data=url)
    128     return alipay_url
    129 
    130 
    131 @jsonrpc_method('pay.get_wxpay_url')
    132 def get_wxpay_url(request, out_trade_no, body, total_fee, notify_url, product_id, user_id):
    133     recode = Wxorder.objects.filter(out_trade_no=out_trade_no).values().first()
    134     if recode is None:
    135         Wxorder.objects.create(
    136             out_trade_no=out_trade_no,
    137             body=body,
    138             total_fee=total_fee,
    139             notify_url=notify_url,
    140             product_id=product_id,
    141             created_by=user_id,
    142             updated_by=user_id
    143         )
    144 
    145     pay_no = UUIDTools.datetime_random()
    146     pay = UnifiedOrderPay(WXAPPID, WX_MCH_ID, WX_PAY_KEY)
    147     response = pay.post(body, pay_no, total_fee,
    148                         WXPAY_CALLBACK_URL.split('://')[1].split(':')[0], WX_NOTIFY_URL)
    149     if response and response["return_code"] == "SUCCESS" and response["result_code"] == "SUCCESS":
    150         wxorder = Wxorder.objects.filter(out_trade_no=out_trade_no).values().first()
    151         Wxpay.objects.create(
    152             out_trade_no=out_trade_no,
    153             pay_no=pay_no,
    154             code_url=response.get('code_url'),
    155             nonce_str=response.get('nonce_str'),
    156             created_by=user_id,
    157             updated_by=user_id
    158         )
    159         return response.get('code_url')
    160 
    161 
    162 @jsonrpc_method('pay.wx_order_query')
    163 def wx_order_query(request, out_trade_no):
    164     wxpays = Wxpay.objects.filter(out_trade_no=out_trade_no).values()
    165     pay = OrderQuery(WXAPPID, WX_MCH_ID, WX_PAY_KEY)
    166     for wxpay in wxpays:
    167         response = pay.post(wxpay.get('pay_no'))
    168         if response and response["return_code"] == "SUCCESS" 
    169                 and response["result_code"] == "SUCCESS":
    170             trade_state = response["trade_state"]
    171             if trade_state == "SUCCESS":  # 支付成功
    172                 pay_time = response["time_end"]
    173                 transaction_id = response["transaction_id"]
    174                 Wxorder.objects.filter(out_trade_no=out_trade_no).update(
    175                     pay_time=time.strftime("%Y-%m-%d %H:%M:%S",
    176                                            time.strptime(pay_time, "%Y%m%d%H%M%S")),
    177                     transaction_id=transaction_id
    178                 )
    179                 return {"success": True, "pay_time": pay_time}
    180     return {"success": False}

    4、alipay.py:

      1 # -*- coding: utf-8 -*-
      2 
      3 # pip install pycryptodome
      4 
      5 from datetime import datetime
      6 from Crypto.PublicKey import RSA
      7 from Crypto.Signature import PKCS1_v1_5
      8 from Crypto.Hash import SHA256
      9 from base64 import b64encode, b64decode
     10 from urllib.parse import quote_plus
     11 from urllib.parse import urlparse, parse_qs
     12 from urllib.request import urlopen
     13 from base64 import decodebytes, encodebytes
     14 
     15 import json
     16 
     17 
     18 class AliPay(object):
     19     """
     20     支付宝支付接口
     21     """
     22 
     23     def __init__(self, appid, app_notify_url, app_private_key_path,
     24                  alipay_public_key_path, return_url, debug=False):
     25         self.appid = appid
     26         self.app_notify_url = app_notify_url
     27         self.app_private_key_path = app_private_key_path
     28         self.app_private_key = None
     29         self.return_url = return_url
     30         with open(self.app_private_key_path) as fp:
     31             self.app_private_key = RSA.importKey(fp.read())
     32 
     33         self.alipay_public_key_path = alipay_public_key_path
     34         with open(self.alipay_public_key_path) as fp:
     35             self.alipay_public_key = RSA.import_key(fp.read())
     36 
     37         if debug is True:
     38             self.__gateway = "https://openapi.alipaydev.com/gateway.do"
     39         else:
     40             self.__gateway = "https://openapi.alipay.com/gateway.do"
     41 
     42     def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
     43         biz_content = {
     44             "subject": subject,
     45             "out_trade_no": out_trade_no,
     46             "total_amount": total_amount,
     47             "product_code": "FAST_INSTANT_TRADE_PAY",
     48             # "qr_pay_mode":4
     49         }
     50 
     51         biz_content.update(kwargs)
     52         data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
     53         return self.sign_data(data)
     54 
     55     def build_body(self, method, biz_content, return_url=None):
     56         data = {
     57             "app_id": self.appid,
     58             "method": method,
     59             "charset": "utf-8",
     60             "sign_type": "RSA2",
     61             "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
     62             "version": "1.0",
     63             "biz_content": biz_content
     64         }
     65 
     66         if return_url is not None:
     67             data["notify_url"] = self.app_notify_url
     68             data["return_url"] = self.return_url
     69 
     70         return data
     71 
     72     def sign_data(self, data):
     73         data.pop("sign", None)
     74         # 排序后的字符串
     75         unsigned_items = self.ordered_data(data)
     76         unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
     77         sign = self.sign(unsigned_string.encode("utf-8"))
     78         ordered_items = self.ordered_data(data)
     79         quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in ordered_items)
     80 
     81         # 获得最终的订单信息字符串
     82         signed_string = quoted_string + "&sign=" + quote_plus(sign)
     83         return signed_string
     84 
     85     def ordered_data(self, data):
     86         complex_keys = []
     87         for key, value in data.items():
     88             if isinstance(value, dict):
     89                 complex_keys.append(key)
     90 
     91         # 将字典类型的数据dump出来
     92         for key in complex_keys:
     93             data[key] = json.dumps(data[key], separators=(',', ':'))
     94 
     95         return sorted([(k, v) for k, v in data.items()])
     96 
     97     def sign(self, unsigned_string):
     98         # 开始计算签名
     99         key = self.app_private_key
    100         signer = PKCS1_v1_5.new(key)
    101         signature = signer.sign(SHA256.new(unsigned_string))
    102         # base64 编码,转换为unicode表示并移除回车
    103         sign = encodebytes(signature).decode("utf8").replace("
    ", "")
    104         return sign
    105 
    106     def _verify(self, raw_content, signature):
    107         # 开始计算签名
    108         key = self.alipay_public_key
    109         signer = PKCS1_v1_5.new(key)
    110         digest = SHA256.new()
    111         digest.update(raw_content.encode("utf8"))
    112         if signer.verify(digest, decodebytes(signature.encode("utf8"))):
    113             return True
    114         return False
    115 
    116     def verify(self, data, signature):
    117         if "sign_type" in data:
    118             sign_type = data.pop("sign_type")
    119         # 排序后的字符串
    120         unsigned_items = self.ordered_data(data)
    121         message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
    122         return self._verify(message, signature)
    123 
    124 
    125 if __name__ == "__main__":
    126     alipay = AliPay(
    127         appid="2016081500252338",
    128         app_notify_url="http://projectsedus.com/",
    129         app_private_key_path="keys/private_2048.txt",
    130         alipay_public_key_path="keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    131         debug=True,  # 默认False,
    132         return_url="http://192.168.247.129:8000/"
    133     )
    134 
    135     url = alipay.direct_pay(
    136         subject="测试订单",
    137         out_trade_no="20170202126666",
    138         total_amount=1000
    139     )
    140     re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    141     print(re_url)

    5、wxpay.py:

      1 # -*- coding=utf-8 -*-
      2 
      3 import time
      4 import json
      5 import hashlib
      6 import requests
      7 
      8 from pay.utils import (smart_str, dict_to_xml, calculate_sign, random_str,
      9                        post_xml, xml_to_dict, validate_post_xml, format_url)
     10 
     11 # from local_settings import appid, mch_id, api_key
     12 
     13 
     14 OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?%s"
     15 OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?%s"
     16 
     17 
     18 class WeiXinPay(object):
     19     def __init__(self, appid, mch_id, api_key):
     20         self.appid = appid  # 微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看
     21         self.mch_id = mch_id  # 受理商ID,身份标识
     22         self.api_key = api_key  # 商户支付密钥Key。审核通过后,在微信发送的邮件中查看
     23         self.common_params = {
     24             "appid": self.appid,
     25             "mch_id": self.mch_id,
     26         }
     27         self.params = {}
     28         self.url = ""
     29         self.trade_type = ""
     30 
     31     def set_params(self, **kwargs):
     32         self.params = {}
     33         for (k, v) in kwargs.items():
     34             self.params[k] = smart_str(v)
     35 
     36         self.params["nonce_str"] = random_str(32)
     37         if self.trade_type:
     38             self.params["trade_type"] = self.trade_type
     39         self.params.update(self.common_params)
     40 
     41     def post_xml(self):
     42         sign = calculate_sign(self.params, self.api_key)
     43         xml = dict_to_xml(self.params, sign)
     44         response = post_xml(self.url, xml)
     45         return xml_to_dict(response.text)
     46 
     47     def valiate_xml(self, xml):
     48         return validate_post_xml(xml, self.appid, self.mch_id, self.api_key)
     49 
     50     def get_error_code_desc(self, error_code):
     51         error_desc = {
     52             "SYSTEMERROR": u"接口后台错误",
     53             "INVALID_TRANSACTIONID": u"无效 transaction_id",
     54             "PARAM_ERROR": u"提交参数错误",
     55             "ORDERPAID": u"订单已支付",
     56             "OUT_TRADE_NO_USED": u"商户订单号重复",
     57             "NOAUTH": u"商户无权限",
     58             "NOTENOUGH": u"余额丌足",
     59             "NOTSUPORTCARD": u"不支持卡类型",
     60             "ORDERCLOSED": u"订单已关闭",
     61             "BANKERROR": u"银行系统异常",
     62             "REFUND_FEE_INVALID": u"退款金额大亍支付金额",
     63             "ORDERNOTEXIST": u"订单不存在",
     64         }
     65         return error_desc.get(error_code.strip().upper(), u"未知错误")
     66 
     67 
     68 class UnifiedOrderPay(WeiXinPay):
     69     """发送预支付单"""
     70 
     71     def __init__(self, appid, mch_id, api_key):
     72         super(UnifiedOrderPay, self).__init__(appid, mch_id, api_key)
     73         self.url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
     74         self.trade_type = "NATIVE"
     75 
     76     def post(self, body, out_trade_no, total_fee, spbill_create_ip, notify_url, **kwargs):
     77         tmp_kwargs = {
     78             "body": body,
     79             "out_trade_no": out_trade_no,
     80             "total_fee": total_fee,
     81             "spbill_create_ip": spbill_create_ip,
     82             "notify_url": notify_url,
     83         }
     84         tmp_kwargs.update(**kwargs)
     85         self.set_params(**tmp_kwargs)
     86         return self.post_xml()[1]
     87 
     88 
     89 class OrderQuery(WeiXinPay):
     90     """订单状态查询"""
     91 
     92     def __init__(self, appid, mch_id, api_key):
     93         super(OrderQuery, self).__init__(appid, mch_id, api_key)
     94         self.url = "https://api.mch.weixin.qq.com/pay/orderquery"
     95 
     96     def post(self, out_trade_no):
     97         self.set_params(out_trade_no=out_trade_no)
     98         return self.post_xml()[1]
     99 
    100 
    101 class JsAPIOrderPay(UnifiedOrderPay):
    102     """H5页面的Js调用类"""
    103 
    104     def __init__(self, appid, mch_id, api_key, app_secret):
    105         super(JsAPIOrderPay, self).__init__(appid, mch_id, api_key)
    106         self.app_secret = app_secret
    107         self.trade_type = "JSAPI"
    108 
    109     def create_oauth_url_for_code(self, redirect_uri):
    110         url_params = {
    111             "appid": self.appid,
    112             "redirect_uri": redirect_uri,  # 一般是回调当前页面
    113             "response_type": "code",
    114             "scope": "snsapi_base",
    115             "state": "STATE#wechat_redirect"
    116         }
    117         url = format_url(url_params)
    118         return OAUTH2_AUTHORIZE_URL % url
    119 
    120     def _create_oauth_url_for_openid(self, code):
    121         url_params = {
    122             "appid": self.appid,
    123             "secret": self.app_secret,
    124             "code": code,
    125             "grant_type": "authorization_code",
    126         }
    127         url = format_url(url_params)
    128         return OAUTH2_ACCESS_TOKEN_URL % url
    129 
    130     def _get_oauth_info(self, code):
    131         """
    132         获取OAuth2的信息:access_token、expires_in、refresh_token、openid、scope
    133         返回结果为字典,可使用["xxx"]或.get("xxx", None)的方式进行读取
    134         """
    135         url = self._create_oauth_url_for_openid(code)
    136         response = requests.get(url)
    137         return response.json() if response else None
    138 
    139     def _get_openid(self, code):
    140         oauth_info = self._get_oauth_info(code)
    141         if oauth_info:
    142             return oauth_info.get("openid", None)
    143         return None
    144 
    145     def _get_json_js_api_params(self, prepay_id):
    146         js_params = {
    147             "appId": self.appid,
    148             "timeStamp": "%d" % time.time(),
    149             "nonceStr": random_str(32),
    150             "package": "prepay_id=%s" % prepay_id,
    151             "signType": "MD5",
    152         }
    153         js_params["paySign"] = calculate_sign(js_params, self.api_key)
    154         return js_params
    155 
    156     def post(self, body, out_trade_no, total_fee, spbill_create_ip, notify_url, code):
    157         if code:
    158             open_id = self._get_openid(code)
    159             if open_id:
    160                 # 直接调用基类的post方法查询prepay_id,如果成功,返回一个字典
    161                 unified_order = super(JsAPIOrderPay, self).post(body, out_trade_no, total_fee, spbill_create_ip,
    162                                                                 notify_url, open_id=open_id)
    163                 if unified_order:
    164                     prepay_id = unified_order.get("prepay_id", None)
    165                     if prepay_id:
    166                         return self._get_json_js_api_params(prepay_id)
    167         return None
    View Code

    6、utils.py:

      1 # -*- coding=utf-8 -*-
      2 import hashlib
      3 import re
      4 import types
      5 from random import Random
      6 import requests
      7 import thriftpy
      8 
      9 from django.conf import settings
     10 from django.core.exceptions import FieldDoesNotExist
     11 from django.db import models
     12 from django.db.models.fields.reverse_related import ForeignObjectRel
     13 from rest_framework.pagination import PageNumberPagination
     14 from thriftpy.rpc import make_client
     15 
     16 from pay.exception_handler import ForeignObjectRelDeleteError, ModelDontHaveIsActiveFiled, logger
     17 
     18 
     19 def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
     20     """
     21     Returns a bytestring version of 's', encoded as specified in 'encoding'.
     22     If strings_only is True, don't convert (some) non-string-like objects.
     23     """
     24     if strings_only and isinstance(s, (types.NoneType, int)):
     25         return s
     26     if not isinstance(s, str):
     27         try:
     28             return str(s)
     29         except UnicodeEncodeError:
     30             if isinstance(s, Exception):
     31                 # An Exception subclass containing non-ASCII data that doesn't
     32                 # know how to print itself properly. We shouldn't raise a
     33                 # further exception.
     34                 return ' '.join([smart_str(arg, encoding, strings_only,
     35                                            errors) for arg in s])
     36             return unicode(s).encode(encoding, errors)
     37     elif s and encoding != 'utf-8':
     38         return s.decode('utf-8', errors).encode(encoding, errors)
     39     else:
     40         return s
     41 
     42 
     43 def format_url(params, api_key=None):
     44     url = "&".join(['%s=%s' % (key, smart_str(params[key])) for key in sorted(params)])
     45     if api_key:
     46         url = '%s&key=%s' % (url, api_key)
     47     return url
     48 
     49 
     50 def calculate_sign(params, api_key):
     51     # 签名步骤一:按字典序排序参数, 在string后加入KEY
     52     url = format_url(params, api_key)
     53     # 签名步骤二:MD5加密, 所有字符转为大写
     54     return hashlib.md5(url.encode('utf-8')).hexdigest().upper()
     55 
     56 
     57 def dict_to_xml(params, sign):
     58     xml = ["<xml>", ]
     59     for (k, v) in params.items():
     60         if (v.isdigit()):
     61             xml.append('<%s>%s</%s>' % (k, v, k))
     62         else:
     63             xml.append('<%s><![CDATA[%s]]></%s>' % (k, v, k))
     64     xml.append('<sign><![CDATA[%s]]></sign></xml>' % sign)
     65     return ''.join(xml)
     66 
     67 
     68 def xml_to_dict(xml):
     69     if xml[0:5].upper() != "<XML>" and xml[-6].upper() != "</XML>":
     70         return None, None
     71 
     72     result = {}
     73     sign = None
     74     content = ''.join(xml[5:-6].strip().split('
    '))
     75 
     76     pattern = re.compile(r"<(?P<key>.+)>(?P<value>.+)</(?P=key)>")
     77     m = pattern.match(content)
     78     while (m):
     79         key = m.group("key").strip()
     80         value = m.group("value").strip()
     81         if value != "<![CDATA[]]>":
     82             pattern_inner = re.compile(r"<![CDATA[(?P<inner_val>.+)]]>")
     83             inner_m = pattern_inner.match(value)
     84             if inner_m:
     85                 value = inner_m.group("inner_val").strip()
     86             if key == "sign":
     87                 sign = value
     88             else:
     89                 result[key] = value
     90 
     91         next_index = m.end("value") + len(key) + 3
     92         if next_index >= len(content):
     93             break
     94         content = content[next_index:]
     95         m = pattern.match(content)
     96 
     97     return sign, result
     98 
     99 
    100 def validate_post_xml(xml, appid, mch_id, api_key):
    101     sign, params = xml_to_dict(xml)
    102     if (not sign) or (not params):
    103         return None
    104 
    105     remote_sign = calculate_sign(params, api_key)
    106     if sign != remote_sign:
    107         return None
    108 
    109     if params["appid"] != appid or params["mch_id"] != mch_id:
    110         return None
    111 
    112     return params
    113 
    114 
    115 def random_str(randomlength=8):
    116     chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
    117     random = Random()
    118     return "".join([chars[random.randint(0, len(chars) - 1)] for i in range(randomlength)])
    119 
    120 
    121 def post_xml(url, xml):
    122     return requests.post(url, data=xml.encode('utf-8'), verify=False)
    123 
    124 
    125 class UnActiveModelMixin(object):
    126     """
    127     删除一个对象,并不真删除,级联将对应外键对象的is_active设置为false,需要外键对象都有is_active字段.
    128     """
    129 
    130     def perform_destroy(self, instance):
    131         rel_fileds = [f for f in instance._meta.get_fields() if isinstance(f, ForeignObjectRel)]
    132 
    133         links = [f.get_accessor_name() for f in rel_fileds]
    134 
    135         for link in links:
    136             manager = getattr(instance, link, None)
    137             if not manager:
    138                 continue
    139             if isinstance(manager, models.Model):
    140                 if hasattr(manager, 'is_active') and manager.is_active:
    141                     manager.is_active = False
    142                     manager.save()
    143                     raise ForeignObjectRelDeleteError(u'{} 上有关联数据'.format(link))
    144             else:
    145                 if not manager.count():
    146                     continue
    147                 try:
    148                     manager.model._meta.get_field('is_active')
    149                     manager.filter(is_active=True).update(is_active=False)
    150                 except FieldDoesNotExist as ex:
    151                     # 理论上,级联删除的model上面应该也有is_active字段,否则代码逻辑应该有问题
    152                     logger.warn(ex)
    153                     raise ModelDontHaveIsActiveFiled(
    154                         '{}.{} 没有is_active字段, 请检查程序逻辑'.format(
    155                             manager.model.__module__,
    156                             manager.model.__class__.__name__
    157                         ))
    158         instance.is_active = False
    159         instance.save()
    160 
    161     def get_queryset(self):
    162         return self.queryset.filter(is_active=True)
    163 
    164 
    165 class StandardResultsSetPagination(PageNumberPagination):
    166     page_size_query_param = 'size'
  • 相关阅读:
    vue 拖拽移动(类似于iPhone虚拟home )
    鼠标事件-MouseEvent【转】
    JS快速排序 希尔排序 归并排序 选择排序
    JS 继承
    Centos6 iptables 防火墙设置【转】
    centos6 mongodb 安装
    操作系统中涉及的各种调度算法
    循环队列
    队列
    栈(C++)
  • 原文地址:https://www.cnblogs.com/victorwu/p/8505592.html
Copyright © 2020-2023  润新知