首頁 > 軟體

python實現微信小程式的多種支付方式

2022-04-12 19:00:33

多支付

原理

1.利用鴨子型別。規定前臺傳過來支付方式。pay_methon

2.再支付方式裡面實現pay(名字統一)方法

3.回撥函數,在支付方式裡面寫notify(名字統一)統一方法,返回的data統一格式。   

eg: data={"statu":'success',"order_id":notity_data['order_id'],"print":"0000"}

 這樣的的牛逼之處:我們在修改、新增支付方式的時候,只需要按照鴨子型別,命名一樣的函數名,寫好自己的支付方式即可。不需要改其他的程式碼

多支付介面程式碼

urls.py:

path("order/create",order.Creat.as_view()),
path("order/notify/<paymethod>",order.Notify.as_view())
# 這裡所有的支付都是走的小程式微信支付:
import importlib
class Creat(APIView):
    ...虛擬碼
    pay_methon = "Wxpay"  # 如果是PC端,可以前臺傳過來支付方式
    try:
        #pay_file是物件
        pay_file = importlib.import_module(f"app01.Pay.{pay_methon}")  # 呼叫對應的支付方式
        pay_class = getattr(pay_file, pay_methon)  # 反射機制
        order_data['open_id'] = openid # 傳的引數
        order_data['ip'] = host_ip  # 傳的引數
        data = pay_class().pay(order_data)  # 呼叫支付
    except:
        return  Response({"code":201,"msg":"未知支付方式"})
# 非同步回撥的
class Notify(APIView):
    def post(self,request,paymethod):
        pay_file = importlib.import_module(f"app01.Pay.{paymethod}")
        pay_class = getattr(pay_file,paymethod)
        data = pay_class().notify(request.data)  # 呼叫非同步回撥
        # 判斷data資料中屬性,然後修改訂單
        if data["statu"] == "success":
            models.Order.objects.filter(order_id =data['order_id']).update(pay_status =1)
            return Response(data["print"])

支付方式程式碼

Alipay支付

# Alipay支付
class Alipay:
    def pay(self,order_data):
        #統一下單方法
        pass
    def notify(self,notity_data):
        if notity_data['success'] :
            #notity_data['order_id']表示商城訂單號
            data={"statu":'success',"order_id":notity_data['order_id'],"print":"0000"}
            return   data

YLpay支付方式

# YLpay支付方式
class YLpay:
    def pay(self,order_data):
        pass
    def notify(self,request_data):
        #驗籤
        #資料處理
        pass

Wxpay支付方式

import time
from app01.wx import settings
class Wxpay:
    def pay(self,order_data):
        self.order_id = order_data["order_id"]
        self.open_id = order_data['open_id']
        self.ip = order_data['ip']
        data_body = self.get_body_data()
        import requests
        url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
        response = requests.post(url, data_body.encode("utf-8"), headers={'content-type': "application/xml"})
        res_dict = self.xml_to_dic(response.content)
        timeStamp = str(int(time.time()))
        paySign = self.get_pay_sign(res_dict, timeStamp)
        data_dic = {
            'timeStamp': timeStamp,
            'nonceStr': res_dict['nonce_str'],
            'package': f"prepay_id={res_dict['prepay_id']}",
            'signType': 'MD5',
            "paySign": paySign,
        }
        return data_dic
    def get_pay_sign(self, res_dict, timeStamp):
        data_dic = {
            'appId': res_dict['appid'],
            'timeStamp': timeStamp,
            'nonceStr': res_dict['nonce_str'],
            'package': f"prepay_id={res_dict['prepay_id']}",
            "signType": "MD5"
        }
        sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)])
        sign_str = f"{sign_str}&key={settings.pay_apikey}"
        import hashlib
        md5 = hashlib.md5()
        md5.update(sign_str.encode("utf-8"))
        sign = md5.hexdigest()
        return sign.upper()
    def xml_to_dic(self, xml_data):
        import xml.etree.ElementTree as ET
        '''
        xml to dict
        :param xml_data:
        :return:
        '''
        xml_dict = {}
        root = ET.fromstring(xml_data)
        for child in root:
            xml_dict[child.tag] = child.text
        return xml_dict
    def get_random(self):
        import random
        data = "123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP"
        nonce_str = "".join(random.sample(data, 30))
        return nonce_str
    def get_sign(self):
        data_dic = {
            "nonce_str": self.nonce_str,
            "out_trade_no": self.out_trade_no,
            "spbill_create_ip": self.spbill_create_ip,
            "notify_url": self.notify_url,
            "openid": self.open_id,
            "body": self.body,
            "trade_type": "JSAPI",
            "appid": self.appid,
            "total_fee": "1",
            "mch_id": self.mch_id
        }
        sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)])
        sign_str = f"{sign_str}&key={settings.pay_apikey}"
        import hashlib
        md5 = hashlib.md5()
        md5.update(sign_str.encode("utf-8"))
        sign = md5.hexdigest()
        return sign.upper()

    def get_body_data(self):
        self.appid = settings.AppId
        # openid=self.open_id
        self.mch_id = str(settings.pay_mchid)
        self.nonce_str = self.get_random()
        self.out_trade_no = self.order_id
        self.spbill_create_ip = self.ip
        self.notify_url = "https://www.test.com"
        self.body = "老男孩學費"
        self.sign = self.get_sign()
        body_data = f"""
           <xml>
               <appid>{self.appid}</appid>
               <mch_id>{self.mch_id}</mch_id>
               <nonce_str>{self.nonce_str}</nonce_str>
               <sign>{self.sign}</sign>
               <body>{self.body}</body>
               <out_trade_no>{self.out_trade_no}</out_trade_no>
               <total_fee>1</total_fee>
               <spbill_create_ip>{ self.spbill_create_ip}</spbill_create_ip>
               <notify_url>{self.notify_url}</notify_url>
               <openid>{self.open_id}</openid>
               <trade_type>JSAPI</trade_type> 
           </xml>"""
        return body_data


Creat下訂單

from  rest_framework.views import  APIView
from rest_framework.response import  Response
from app01.wx import wx_login
import hashlib ,time
from app01 import models
from django.core.cache import cache
from django.db import transaction
from app01.func import function_tool
import importlib
class Creat(APIView):
    @transaction.atomic
    def post(self,request):
        #小程式提交給我們的資料
        '''
        {'token': '0bb2aa1102ca9c8306133b2539c3508b',
        'remark': '',
        'province': '廣東省',
        'city': '廣州市',
        'county': '海珠區',
        'address':
        '新港中路397號',
        'phone': '020-81167888',
        'name': '張三',
        'buy_list': {'2': 1}}
        '''
        param = request.data
        if param.get("token") and param.get("buy_list"):
            user_cache = cache.get(param["token"])
            if user_cache:
                # 獲取ip
                if request.META.get("HTTP_X_FORWARDED_FOR"):
                    host_ip = request.META["HTTP_X_FROWARDED_FOR"]
                else:
                    host_ip = request.META["REMOTE_ADDR"]
                openid = user_cache.split("&")[0]  #data['openid']+"&"+data["session_key"]
                user_data = models.Wxuser.objects.filter(openid=openid).first()
                order_data = {
                    "consignee_mobile": param['phone'],
                    'consignee_name': param['name'],
                    'wxuser_id': user_data.id,
                    "memo": param['remark'],
                    "consignee_area": f"{param['province']},{param['city']},{param['county']}",
                    "consignee_address": param['address'],
                    "order_id": function_tool.get_order_id(),
                    "order_total": 0
                }
                # 1 上面的order_data 出來上面的資料,有些是需要通過購買上面列表做累加才能獲得到
                # 2 order_item 是通過buy_list裡面的商品列表,一個鍵值對就是一條記入'buy_list': {'2': 1,「1」:2}
                # 3 再每一次增加一個order_item的時候,我們都需要校驗庫存。如果有一個商品的庫存不足,我們就應該不然使用者下單
                # 4 由於第三步中進行多次增加,如果再後面的的商品庫存有問題,我們不讓他下單,但是前面的資料已經插入。
                # 所有我們要用資料庫的事務管理資料的統一。就算order_item沒有問題,order_data,插入的時候,也可能出錯,所以也要用事務
                # 5 由於並行問題,所有的使用者都會對資料的庫存進行加減,所以我們這裡再校驗庫存的時候要用鎖。
                buy_list = param.get("buy_list")
                # 獲取到buy_list是沒有商品資訊只有有id,我們先把buy_list中的所有商品查出來
                goods_key = list(buy_list.keys())
                all_product = models.Product.objects.filter(product_id__in = goods_key)
                #用for迴圈新增order_item

                sid = transaction.savepoint()
                for product in all_product:
                    # 將product.product_id 轉字串,為了通過product.product_id在buy_list獲取商品的購買數量
                    product.product_id = str(product.product_id)
                    # 獲取訂單總金額
                    order_data['order_total'] += product.price* buy_list[product.product_id]
                    for i in range(3):
                        #先查庫存,重新查庫的
                        stock = product.stock.quantity
                        #當前的庫存的庫存數量,減去購買數量,是否大於0
                        new_stock = stock-buy_list[product.product_id]
                        if new_stock < 0 :
                            #庫存不足,回滾
                            transaction.savepoint_rollback(sid)
                            return Response({"code":201,"msg": f"{product.name}庫存不足"})
                        #樂觀鎖
                        res = models.Stock.objects.filter(quantity= stock,stock_id =product.stock.stock_id).update(quantity = new_stock)
                        if not res:
                            if i == 2:
                                transaction.savepoint_rollback(sid)
                                return  Response({"code":201,"msg": "建立訂單失敗"})
                            else:
                                continue
                        else:
                            break
                    #獲取購買數量
                    new_buy_cout = product.buy_count + buy_list[product.product_id]
                    models.Product.objects.filter(product_id=product.product_id).update(buy_count =new_buy_cout)
                    #組織order_item的資料
                    order_item_data = {
                         'order_id': order_data['order_id'],
                         'product_id': product.product_id,
                         "name": product.name,
                         "image": product.image,
                         "price": product.price,
                          "nums": buy_list[product.product_id],
                          "brief": product.brief
                     }
                    models.Order_items.objects.create(**order_item_data)
                models.Order.objects.create(**order_data)
                transaction.savepoint_commit(sid)

                #所有的支付都是走的小程式微信支付:
                pay_methon = "Wxpay"
                try:
                    #pay_file是物件
                    pay_file = importlib.import_module(f"app01.Pay.{pay_methon}")
                    pay_class = getattr(pay_file, pay_methon)
                    order_data['open_id'] = openid
                    order_data['ip'] = host_ip
                    data = pay_class().pay(order_data)
                except:
                    return  Response({"code":201,"msg":"未知支付方式"})
                # 1對接小程式支付
                # 2 我們要用celery去定時檢查,該訂單在指定時間內用沒有支付,沒有支付,取消訂單,回滾庫存
                function_tool.pay_status(order_data['order_id'])
                return  Response({"code":200,"msg":"ok","data":data})

            else:
                return Response({"code": 201, "msg": "無效的token"})
        else:
            return Response({"code":202,"msg":"缺少引數"})
class Notify(APIView):
    def post(self,request,paymethod):
        pay_file = importlib.import_module(f"app01.Pay.{paymethod}")
        pay_class = getattr(pay_file,paymethod)
        data = pay_class().notify(request.data)

        # 判斷data資料中屬性,然後修改訂單
        if data["statu"] == "success":
            models.Order.objects.filter(order_id =data['order_id']).update(pay_status =1)
            return Response(data["print"])

以上就是python實現微信小程式的多種支付方式的詳細內容,更多關於python微信支付方式的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com