Эх сурвалжийг харах

Merge branch 'cloud_storage_dev' into lang

# Conflicts:
#	Ansjer/urls.py
lang 3 жил өмнө
parent
commit
8a927ef53a

+ 1 - 1
Ansjer/urls.py

@@ -18,7 +18,7 @@ from Controller import FeedBack, EquipmentOTA, EquipmentInfo, AdminManage, AppIn
     VerifyCodeController, FileController, UIDController, LogController, SalesController, \
     VerifyCodeController, FileController, UIDController, LogController, SalesController, \
     OrderTaskController, HistoryUIDController, UIDManageUserController, SerialNumberController, CompanyController, \
     OrderTaskController, HistoryUIDController, UIDManageUserController, SerialNumberController, CompanyController, \
     RegionController, VPGController, LanguageController, TestController, DeviceConfirmRegion, S3GetStsController, \
     RegionController, VPGController, LanguageController, TestController, DeviceConfirmRegion, S3GetStsController, \
-    DetectControllerV2, ShadowController, TestDetectController, PcInfo, PctestController, DeviceDebug, \
+    DetectControllerV2, ShadowController, TestDetectController, PcInfo, PctestController, DeviceDebug, PaymentCycle, \
     DeviceLogController
     DeviceLogController
 from AdminController import UserManageController, RoleController, MenuController, TestServeController, \
 from AdminController import UserManageController, RoleController, MenuController, TestServeController, \
     ServeManagementController, LogManagementController, DeviceManagementController, VersionManagementController
     ServeManagementController, LogManagementController, DeviceManagementController, VersionManagementController

+ 19 - 3
Controller/CloudStorage.py

@@ -52,6 +52,7 @@ from Service.CommonService import CommonService
 from Object.m3u8generate import PlaylistGenerator
 from Object.m3u8generate import PlaylistGenerator
 from Object.WechatPayObject import WechatPayObject
 from Object.WechatPayObject import WechatPayObject
 from django.db.models import Q, F, Count
 from django.db.models import Q, F, Count
+from Controller.PaymentCycle import Paypal
 from Ansjer.config import SERVER_TYPE
 from Ansjer.config import SERVER_TYPE
 from Service.ModelService import ModelService
 from Service.ModelService import ModelService
 
 
@@ -216,7 +217,7 @@ class CloudStorageView(View):
         qs = qs.values("id", "title", "content", "price", "day", "currency", "bucket__storeDay",
         qs = qs.values("id", "title", "content", "price", "day", "currency", "bucket__storeDay",
                        "bucket__bucket", "bucket__area", "commodity_code",
                        "bucket__bucket", "bucket__area", "commodity_code",
                        "commodity_type", "is_discounts", "virtual_price", "expire",
                        "commodity_type", "is_discounts", "virtual_price", "expire",
-                       "discount_price", "discount_content", "symbol")
+                       "discount_price", "discount_content", "symbol","cycle_config_id")
 
 
         if qs.exists():
         if qs.exists():
             ql = list(qs)
             ql = list(qs)
@@ -229,7 +230,8 @@ class CloudStorageView(View):
                 for key, val in enumerate(items_list):
                 for key, val in enumerate(items_list):
                     pay_types = Pay_Type.objects.filter(store_meal=items_list[key]['id']).values("id", "payment")
                     pay_types = Pay_Type.objects.filter(store_meal=items_list[key]['id']).values("id", "payment")
                     items_list[key]['pay_type'] = list(pay_types)
                     items_list[key]['pay_type'] = list(pay_types)
-
+                    items_list[key]['is_pay_cycle'] = 1 if items_list[key]['cycle_config_id'] else 0
+                    del items_list[key]['cycle_config_id']
                 res_c = {'area': area, 'items': items_list}
                 res_c = {'area': area, 'items': items_list}
                 res.append(res_c)
                 res.append(res_c)
             #是否促销
             #是否促销
@@ -1130,7 +1132,7 @@ class CloudStorageView(View):
         smqs = Store_Meal.objects.filter(id=rank, pay_type=pay_type, lang__lang=lang, is_show=0). \
         smqs = Store_Meal.objects.filter(id=rank, pay_type=pay_type, lang__lang=lang, is_show=0). \
             values('currency', 'price', 'lang__content', 'day',
             values('currency', 'price', 'lang__content', 'day',
                    'commodity_type', 'lang__title',
                    'commodity_type', 'lang__title',
-                   'expire', 'commodity_code', 'discount_price', 'bucket__mold')
+                   'expire', 'commodity_code', 'discount_price', 'bucket__mold', 'cycle_config_id')
         if not smqs.exists():
         if not smqs.exists():
             return response.json(173)
             return response.json(173)
         currency = smqs[0]['currency']
         currency = smqs[0]['currency']
@@ -1157,6 +1159,20 @@ class CloudStorageView(View):
 
 
         orderID = CommonService.createOrderID()
         orderID = CommonService.createOrderID()
         if pay_type == 1:
         if pay_type == 1:
+            # 订阅周期扣款
+            if(smqs[0]['cycle_config_id']):
+                subInfo = Paypal.subscriptions(store_info=smqs[0],lang=lang,orderID=orderID)
+                if not subInfo:
+                    return response.json(10048)
+                Order_Model.objects.create(orderID=orderID, UID=uid, channel=channel, userID_id=userID,
+                                           desc=content, payType=pay_type, payTime=nowTime,
+                                           price=price, currency=currency, addTime=nowTime, updTime=nowTime,
+                                           pay_url=subInfo['url'], isSelectDiscounts=is_select_discount,
+                                           commodity_code=commodity_code, commodity_type=commodity_type,
+                                           rank_id=rank, plan_id=subInfo['plan_id'])
+                return response.json(0, {"redirectUrl": subInfo['url'], "orderID": orderID})
+
+            #正常扣款
             cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
             cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
             if lang != 'cn':
             if lang != 'cn':
                 cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
                 cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)

+ 249 - 0
Controller/PaymentCycle.py

@@ -0,0 +1,249 @@
+from Ansjer.config import PAYPAL_CRD,SERVER_DOMAIN,SERVER_DOMAIN_SSL
+from Model.models import PayCycleConfigModel,Order_Model, Store_Meal, UID_Bucket, PromotionRuleModel, Unused_Uid_Meal,Device_Info
+from Service.CommonService import CommonService
+from django.http import JsonResponse, HttpResponseRedirect, HttpResponse
+import requests
+import time
+from Object.ResponseObject import ResponseObject
+import paypalrestsdk
+from paypalrestsdk import BillingAgreement
+from django.views.generic.base import View
+from django.db import transaction
+from Controller import CloudStorage
+from django.db.models import Q, F, Count
+
+#周期扣款相关
+class Paypal:
+    def subscriptions(store_info,lang,orderID):
+        cycle_config = PayCycleConfigModel.objects.filter(id=store_info['cycle_config_id']).values()
+        if not cycle_config:
+            return False
+        cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+        if lang != 'cn':
+            cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+        return_url = "{SERVER_DOMAIN_SSL}payCycle/paypalCycleReturn?lang={lang}". \
+            format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, lang=lang)
+        # call_sub_url = "http://binbin.uicp.vip/cloudstorage/dopaypalcallback?orderID={orderID}".format(
+        # SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, orderID=orderID)
+        BillingPlan = {
+            "description": orderID,
+            "merchant_preferences": {
+                "auto_bill_amount": "YES",
+                "cancel_url": cal_url,  # 取消协议url
+                "initial_fail_amount_action": "CANCEL",
+                "max_fail_attempts": "1",  # 允许的最大失败付款尝试次数
+                "return_url": return_url,  # 客户批准协议的url
+                # "notify_url": "http://www.notify.com",  #通知客户协议已创建的 URL。只读并保留供将来使用。
+                "setup_fee": {
+                    "currency": store_info['currency'],
+                    "value": store_info['price'],
+                }
+            },
+            "name": store_info['lang__content'],
+            "payment_definitions": [
+                {
+                    "amount": {
+                        "currency": store_info['currency'],
+                        "value": store_info['price']
+                    },
+                    # "charge_models": [
+                    #     {
+                    #         "amount": {
+                    #             "currency": "USD",
+                    #             "value": "20"
+                    #         },
+                    #         "type": "TAX"   #税金
+                    #     }
+                    # ],
+                    "cycles": cycle_config[0]['cycles'],
+                    "frequency": cycle_config[0]['frequency'],
+                    "frequency_interval": cycle_config[0]['frequencyInterval'],
+                    "name": store_info['lang__title'],
+                    "type": "REGULAR"
+                },
+            ],
+            "type": "INFINITE",
+        }
+        paypalrestsdk.configure(PAYPAL_CRD)
+        billing_plan = paypalrestsdk.BillingPlan(BillingPlan)
+        if billing_plan.create():
+            billing_plan.activate()  # 激活
+            plan_id = billing_plan.id
+        else:
+            print(billing_plan.error)
+            return False
+
+        now_time = int(time.time())
+        start_date_timestamp = CommonService.calcMonthLater(1, now_time) - (5 * 86400) #下次扣款为下个月提前5天扣款
+        start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ")
+        #订阅
+        billingAgreement = {
+            "name": store_info['lang__content'],
+            "description": orderID,
+            "start_date": start_date_str,
+            "plan": {
+                "id": plan_id
+            },
+            "payer": {
+                "payment_method": "paypal"
+            },
+        }
+        billing_agreement = paypalrestsdk.BillingAgreement(billingAgreement)
+        # print(billing_agreement.create())
+        if billing_agreement.create():
+            for link in billing_agreement.links:
+                if link.rel == "approval_url":
+                    return {"plan_id": plan_id, "url": link.href}
+        else:
+            print(billing_agreement.error)
+            return False
+
+class PaypalCycleNotify(View):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        response = ResponseObject()
+        if operation is None:
+            return response.json(444, 'error path')
+        elif operation == 'paypalCycleReturn':  # paypal成功订阅回调
+            return self.do_paypal_cycle_return(request_dict, response)
+        elif operation == 'paypalCycleNotify':  # paypal 周期付款回调
+            return self.do_paypal_webhook_notify(request_dict, response)
+    def do_paypal_cycle_return(self, request_dict, response):
+        lang = request_dict.get('lang', 'en')
+        token = request_dict.get('token',None)
+        paypalrestsdk.configure(PAYPAL_CRD)
+        billing_agreement = paypalrestsdk.BillingAgreement()
+        billing_agreement_response = billing_agreement.execute(token)
+        if billing_agreement_response.error:
+            print(billing_agreement_response.error)
+            red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+            if lang != 'cn':
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+            return HttpResponseRedirect(red_url)
+
+        orderID = billing_agreement_response.description
+        agreement_id = billing_agreement_response.id
+        promotion_rule_id = ''
+        try:
+            order_qs = Order_Model.objects.filter(orderID=orderID, status=0)
+
+            if not orderID:
+                print("not orderID")
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                if lang != 'cn':
+                    red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                return HttpResponseRedirect(red_url)
+
+            order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
+                                         "userID__userID",
+                                         "userID__username")
+            userid = order_list[0]['userID__userID']
+            username = order_list[0]['userID__username']
+            UID = order_list[0]['UID']
+            channel = order_list[0]['channel']
+            rank = order_list[0]['rank']
+            smqs = Store_Meal.objects.filter(id=rank). \
+                values("day", "bucket_id", "bucket__storeDay", "expire")
+            bucketId = smqs[0]['bucket_id']
+            if not smqs.exists():
+                print("not smqs")
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                if lang != 'cn':
+                    red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                return HttpResponseRedirect(red_url)
+            # ##
+            ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay", "bucket__region",
+                                                             "endTime", "use_status")
+            expire = smqs[0]['expire']
+
+            if order_list[0]['isSelectDiscounts'] == 1:
+                expire = smqs[0]['expire'] * 2
+            # 是否有促销
+            nowTime = int(time.time())
+            promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
+                                                          endTime__gte=nowTime).values('id','ruleConfig')
+            if promotion.exists():
+                promotion_rule_id = promotion[0]['id']
+                expire = expire * 2
+            with transaction.atomic():
+                if ubqs.exists():
+                    ubq = ubqs[0]
+                    if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId:  #套餐使用中并且相同套餐叠加过期时间
+                        endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
+                        UID_Bucket.objects.filter(id=ubq['id']).update \
+                            (uid=UID, channel=channel, bucket_id=bucketId,
+                             endTime=endTime, updateTime=nowTime)
+                    else:     #已过期或者不相同的套餐加入未使用的关联套餐表
+                        has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
+                        nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
+                        if promotion.exists():
+                            nums = nums + 1
+                        if has_unused.exists():
+                            Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
+                        else:
+                            Unused_Uid_Meal.objects.create(uid=UID,channel=channel,addTime=nowTime,num=nums,
+                                                           expire=smqs[0]['expire'],bucket_id=bucketId)
+                        UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
+                    uid_bucket_id = ubq['id']
+                else:
+                    endTime = CommonService.calcMonthLater(expire)
+                    ub_cqs = UID_Bucket.objects.create \
+                        (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
+                         updateTime=nowTime,use_status=1)
+                    uid_bucket_id = ub_cqs.id
+
+                dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
+                if dvq.exists():
+                    dvq_set_update_dict = {
+                        'vodPrimaryUserID': userid,
+                        'vodPrimaryMaster': username
+                    }
+                    dvq.update(**dvq_set_update_dict)
+
+                # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
+                # if not uid_main_exist.exists():
+                #     uid_main_dict = {
+                #         'UID': UID,
+                #         'user_id': userid
+                #     }
+                #     UIDMainUser.objects.create(**uid_main_dict)
+
+                order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,
+                                promotion_rule_id=promotion_rule_id,agreement_id=agreement_id)
+                datetime = time.strftime("%Y-%m-%d", time.localtime())
+                sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + UID + '设备在' + datetime + '已成功订阅云存套餐',
+                                     'Dear customer,you already subscribed the cloud storage package successfully for device ' + UID + ' on ' + time.strftime(
+                                         "%b %dth,%Y", time.localtime())]
+
+                CloudStorage.CloudStorageView.do_vod_msg_Notice(self, UID, channel, userid, lang, sys_msg_text_list, 'SMS_219738485')
+
+                # return response.json(0)
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                if lang != 'cn':
+                    red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+                return HttpResponseRedirect(red_url)
+        except Exception as e:
+            print(repr(e))
+            if order_qs:
+                order_qs.update(status=10, promotion_rule_id=promotion_rule_id)
+            red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+            if lang != 'cn':
+                red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
+            return HttpResponseRedirect(red_url)
+
+
+    def do_paypal_webhook_notify(self, request_dict, response):
+        paymentId = request_dict.get('paymentId', None)
+        PayerID = request_dict.get('PayerID', None)
+        lang = request_dict.get('lang', 'en')
+        token = request_dict.get('token',None)
+        paypalrestsdk.configure(PAYPAL_CRD)

+ 21 - 5
Model/models.py

@@ -472,9 +472,8 @@ class Store_Meal(models.Model):
     is_show = models.SmallIntegerField(default=0, verbose_name=u'该套餐是否隐藏 [0=否,1是]')
     is_show = models.SmallIntegerField(default=0, verbose_name=u'该套餐是否隐藏 [0=否,1是]')
     # lang = models.CharField(default='', max_length=20, verbose_name='语言/国家')
     # lang = models.CharField(default='', max_length=20, verbose_name='语言/国家')
     lang = models.ManyToManyField(to='Lang', verbose_name='套餐语言', db_table='store_meal_lang')
     lang = models.ManyToManyField(to='Lang', verbose_name='套餐语言', db_table='store_meal_lang')
-
+    cycle_config_id = models.IntegerField(null=True, verbose_name='周期付款配置表id')
     # 备用字段
     # 备用字段
-    spare_1 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段1')
     spare_2 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段2')
     spare_2 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段2')
     spare_3 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段3')
     spare_3 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段3')
     spare_4 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段4')
     spare_4 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段4')
@@ -623,12 +622,12 @@ class Order_Model(models.Model):
     pay_url = models.CharField(max_length=2000, default='', verbose_name='支付url')
     pay_url = models.CharField(max_length=2000, default='', verbose_name='支付url')
     paypal = models.CharField(max_length=500, null=True, blank=True, verbose_name='支付批准url')
     paypal = models.CharField(max_length=500, null=True, blank=True, verbose_name='支付批准url')
     promotion_rule_id = models.CharField(blank=True, max_length=64, default='', verbose_name='促销id')
     promotion_rule_id = models.CharField(blank=True, max_length=64, default='', verbose_name='促销id')
+    plan_id = models.CharField(default='', blank=True, max_length=64, verbose_name=u'paypal计划id')
+    agreement_id = models.CharField(default='', blank=True, max_length=64, verbose_name=u'paypal协议id')
 
 
     # 备用字段
     # 备用字段
-    spare_1 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段1')
-    spare_2 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段2')
-    spare_3 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段3')
     spare_4 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段4')
     spare_4 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段4')
+    spare_2 = models.CharField(default='', blank=True, max_length=64, verbose_name=u'备用字段2')
 
 
     def __str__(self):
     def __str__(self):
         return self.orderID
         return self.orderID
@@ -639,6 +638,23 @@ class Order_Model(models.Model):
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
         ordering = ('-orderID',)
         ordering = ('-orderID',)
 
 
+class PayCycleConfigModel(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='主键')
+    # name = models.CharField(default='',max_length=200, verbose_name='计划名字')
+    # tax = models.CharField(max_length=2000, default='', verbose_name='税金')
+    cycles = models.IntegerField(verbose_name='周期:0代表无限期', default=0)
+    frequency = models.CharField(max_length=50,verbose_name='频率,MONTH,WEEK,YEAR', default=0)
+    frequencyInterval = models.IntegerField(default=0, verbose_name='频率周期')
+    remark = models.CharField(max_length=1000, default='', verbose_name='备注')
+
+    def __str__(self):
+        return self.id
+
+    class Meta:
+        db_table = 'pay_cycle_config'
+        verbose_name = u'周期付款计划'
+        verbose_name_plural = verbose_name
+
 class PromotionRuleModel(models.Model):
 class PromotionRuleModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='主键')
     id = models.AutoField(primary_key=True, verbose_name='主键')
     ruleName = models.TextField(default='', verbose_name='规则名字')                     #json格式, 例: {"cn":"黑色星期五","en":"Black Friday"}
     ruleName = models.TextField(default='', verbose_name='规则名字')                     #json格式, 例: {"cn":"黑色星期五","en":"Black Friday"}

+ 2 - 0
Object/ResponseObject.py

@@ -104,6 +104,7 @@ class ResponseObject(object):
             10045: 'Already the latest version',
             10045: 'Already the latest version',
             10046: 'Sorry, users who have activated cloud storage packages do not support logout at the moment, please contact customer service',
             10046: 'Sorry, users who have activated cloud storage packages do not support logout at the moment, please contact customer service',
             10047: 'Please delete all devices under your account first',
             10047: 'Please delete all devices under your account first',
+            10048: 'Subscribe to the failure',
         }
         }
         data_cn = {
         data_cn = {
             0: '成功',
             0: '成功',
@@ -201,6 +202,7 @@ class ResponseObject(object):
             10045: '当前为最新版本',
             10045: '当前为最新版本',
             10046: '已开通云存的用户,暂不支持注销,请联系客服',
             10046: '已开通云存的用户,暂不支持注销,请联系客服',
             10047: '请先删除您当前帐户下的所有设备',
             10047: '请先删除您当前帐户下的所有设备',
+            10048: '订阅失败',
         }
         }
         if self.lang == 'cn':
         if self.lang == 'cn':
             msg = data_cn
             msg = data_cn