Browse Source

Merge branch 'test' of http://192.168.136.99:3000/servers/ASJServer into locky

locky 8 tháng trước cách đây
mục cha
commit
dbb1c995ee

+ 4 - 1
Ansjer/urls.py

@@ -32,7 +32,8 @@ from Controller.CustomCustomer import CustomCustomerController
 from Controller.MessagePush import EquipmentMessagePush
 from Controller.SensorGateway import SensorGatewayController, EquipmentFamilyController
 from Controller.Surveys import CloudStorageController
-from Controller.UserDevice import UserDeviceShareController, UserSubscriptionController, DeviceVersionInfoController
+from Controller.UserDevice import UserDeviceShareController, UserSubscriptionController, DeviceVersionInfoController, \
+    APNConfigController
 from Controller.CampaignController import AppCampaignController, AdDepartmentController
 from Controller.UserDevice import SerialNumberCheckController
 from Model import views  # 定时任务,不要删除该行代码
@@ -290,6 +291,8 @@ urlpatterns = [
     re_path('^weChatMiniProgram/', include("Ansjer.server_urls.wechat_mini_program_url")),
     # 根据版本号获取设备配置信息
     re_path('open/device/configuration/(?P<operation>.*)', DeviceVersionInfoController.DeviceVersionInfoView.as_view()),
+    # 获取APN配置信息
+    re_path('APNConfig/(?P<operation>.*)', APNConfigController.APNConfigView.as_view()),
 
 
 

+ 37 - 30
Controller/InAppPurchaseController.py

@@ -8,7 +8,7 @@ import threading
 
 import requests
 from appstoreserverlibrary.models.Environment import Environment
-from appstoreserverlibrary.api_client import AppStoreServerAPIClient, GetTransactionHistoryVersion
+from appstoreserverlibrary.api_client import AppStoreServerAPIClient, GetTransactionHistoryVersion, APIException
 from appstoreserverlibrary.models.AccountTenure import AccountTenure
 from appstoreserverlibrary.models.ConsumptionRequest import ConsumptionRequest
 from appstoreserverlibrary.models.ConsumptionStatus import ConsumptionStatus
@@ -145,7 +145,8 @@ class InAppPurchaseView(View):
                 logger.info(f"苹果内购认证交易订单orderID:{order_id}, 没有transaction_id")
                 pay_result_url = CommonService.get_payment_status_url(lang, 'fail')
                 return response.json(0, {'url': pay_result_url})
-            logger.info(f"苹果内购认证交易订单orderID:{order_id}, transaction_id:{transaction_id}, 时间戳: {int(time.time())}")
+            logger.info(
+                f"苹果内购认证交易订单orderID:{order_id}, transaction_id:{transaction_id}, 时间戳: {int(time.time())}")
 
             OrderPayLog.objects.create(order_id=order_id, order_no=transaction_id,
                                        business_name=f"内购验单",
@@ -153,22 +154,15 @@ class InAppPurchaseView(View):
                                        access_result="SUCCESS")
 
             # 查询交易信息
-            transaction_info = ""
-            attempts = 0
-            while attempts < 6:
-                try:
-                    transaction_info = client.get_transaction_info(transaction_id)
-                    break
-                except ConnectionError as err:
-                    attempts += 1
-                    if attempts == 5:
-                        OrderPayLog.objects.create(order_id=order_id, order_no=transaction_id,
-                                                   business_name=f"{order_id}获取transactionInfo超时",
-                                                   created_time=int(time.time()), updated_time=int(time.time()),
-                                                   access_result="ERROR")
-                        return response.json(5)
-                    logger.info(
-                        f"订单orderId:{order_id}, transaction_id:{transaction_id}, 第{attempts}次获取支付信息超时")
+            try:
+                transaction_info = client.get_transaction_info(transaction_id)
+            except APIException:
+                in_app_purchase = InAppPurchase(bundle_id=bundle_id, user_id=user_id)
+                # AppStoreServerAPIClient 用于查询交易信息
+                client = in_app_purchase.client
+                # SignedDataVerifier 用于解析查询到的交易信息
+                signed_data_verifier = in_app_purchase.verifier
+                transaction_info = client.get_transaction_info(transaction_id)
 
             signed_transaction_info = transaction_info.signedTransactionInfo
             # 解析交易信息
@@ -241,7 +235,6 @@ class InAppPurchaseView(View):
                 redis_obj.del_data(key_coupon)
                 CouponModel.objects.filter(id=c_id).update(use_status=2, update_time=now_time)
 
-
             # 修改订阅状态
             if payload.rawType == "Auto-Renewable Subscription":
                 original_transaction_id = payload.originalTransactionId
@@ -278,8 +271,11 @@ class InAppPurchaseView(View):
             return response.json(0, {'url': pay_result_url})
         except Exception as e:
             redis_obj.del_data(redis_key)
-            LOGGER.info('苹果内购认证交易接口异常:{}'.
+            logger.info('苹果内购认证交易接口异常:{}'.
                         format('error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))))
+            OrderPayLog.objects.create(order_id=order_id, business_name=f"内购验单异常",
+                                       created_time=int(time.time()), updated_time=int(time.time()),
+                                       access_result="ERROR")
             pay_result_url = CommonService.get_payment_status_url(lang, 'fail')
             return response.json(0, {'url': pay_result_url})
 
@@ -566,7 +562,8 @@ class InAppPurchaseView(View):
                     # 查旧订单消息
                     ord_order_qs = Order_Model.objects.filter(original_transaction_id=original_transaction_id)
                     if not ord_order_qs.exists():
-                        logger.info(f"App Store服务器通知未查询到旧订单信息, originalTransactionId:{original_transaction_id}, 返回状态 400")
+                        logger.info(
+                            f"App Store服务器通知未查询到旧订单信息, originalTransactionId:{original_transaction_id}, 返回状态 400")
                         return HttpResponse(status=400)
 
                     # 解决云存充值成功, 由于一些原因返回500 导致苹果未扣款的问题
@@ -679,14 +676,18 @@ class InAppPurchaseView(View):
                 uid = orders_qs[0].UID
                 now_time = int(time.time())
                 put_time = now_time + 11.5 * 60 * 60
-                in_app_refund_qs = InAppRefund.objects.filter(transaction_id=transaction_id)
+                in_app_refund_qs = InAppRefund.objects.filter(transaction_id=transaction_id).values("refund_progress")
                 if in_app_refund_qs.exists():
-                    in_app_refund_qs.update(refund_progress=0, updated_time=now_time,
-                                            put_time=put_time, app_account_token=app_account_token)
-                InAppRefund.objects.create(transaction_id=transaction_id, orderID=orderID,
-                                           uid=uid, app_type=app_type, created_time=now_time,
-                                           updated_time=now_time, put_time=put_time,
-                                           app_account_token=app_account_token)
+                    if in_app_refund_qs[0]["refund_progress"] == 4:
+                        in_app_refund_qs.update(refund_progress=0, updated_time=now_time,
+                                                put_time=put_time, app_account_token=app_account_token)
+                    else:
+                        in_app_refund_qs.update(updated_time=now_time, app_account_token=app_account_token)
+                else:
+                    InAppRefund.objects.create(transaction_id=transaction_id, orderID=orderID,
+                                               uid=uid, app_type=app_type, created_time=now_time,
+                                               updated_time=now_time, put_time=put_time,
+                                               app_account_token=app_account_token)
                 return HttpResponse(status=200)
 
             elif str(decoded_payload.rawNotificationType) == "DID_CHANGE_RENEWAL_STATUS":
@@ -748,7 +749,7 @@ class InAppPurchaseView(View):
                                                                                  refund_progress=2)
                 return HttpResponse(status=200)
 
-            elif str(decoded_payload.rawNotificationType) == "REFUND_DECLINED":
+            elif str(decoded_payload.rawNotificationType) == "REFUND_REVERSED":
                 # 一种通知类型,表示 App Store 由于客户提出的争议而撤销了先前批准的退款。如果您的应用程序因相关退款而撤销了内容或服务,则需要恢复这些内容或服务。
                 # 此通知类型可适用于任何应用程序内购买类型:消耗品、非消耗品、不可续订订阅和自动续订订阅。对于自动续订,当 App Store 撤销退款时,续订日期保持不变。
                 decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
@@ -756,6 +757,7 @@ class InAppPurchaseView(View):
                 transaction_id = decoded_transaction_information.transactionId
                 logger.info(
                     'App Store服务器通知,撤销了批准的退款,不恢复套餐,手动处理 transaction_id:{}'.format(transaction_id))
+                return HttpResponse(status=200)
 
             elif str(decoded_payload.rawNotificationType) == "REFUND_DECLINED":
                 decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
@@ -764,7 +766,10 @@ class InAppPurchaseView(View):
                 orders_qs = Order_Model.objects.filter(transaction_id=transaction_id)
                 if not orders_qs.exists():
                     return HttpResponse(status=400)
-                InAppRefund.objects.filter(transaction_id=transaction_id).update(refund_progress=3)
+                logger.info(
+                    'App Store服务器通知, 不批准退款, transaction_id:{}'.format(transaction_id))
+                InAppRefund.objects.filter(transaction_id=transaction_id).update(refund_progress=3,
+                                                                                 updated_time=int(time.time()))
 
             else:
                 logger.info(f"App Store服务器通知decoded_payload.rawNotificationType 未处理")
@@ -932,5 +937,7 @@ class InAppPurchaseView(View):
             client.send_consumption_data(transaction_id, consumption_request)
             logger.info(f'内购退款消费数据提交, 订单orderID:{orderID}, transaction_id:{transaction_id}')
             in_app_refund.refund_progress = 1
+            in_app_refund.updated_time = int(time.time())
+            in_app_refund.put_time = int(time.time())
             in_app_refund.save()
         return HttpResponse(status=200)

+ 100 - 33
Controller/ShopifyController.py

@@ -70,6 +70,21 @@ class ShopifyMultipass:
         # 返回响应的JSON数据
         return response.json()
 
+    @staticmethod
+    def get_timezone_by_country(iso2):
+        timezone_map = {
+            "us": "America/New_York",  # 美国时区(例如:纽约)
+            "jp": "Asia/Tokyo",  # 日本时区
+            "de": "Europe/Berlin",  # 德国时区
+            "uk": "Europe/London",  # 英国时区
+            "eu": "Europe/Paris",  # 欧盟时区(默认设为法国巴黎)
+        }
+        # 获取当前时间并格式化时间戳
+        now = datetime.now(pytz.timezone(timezone_map[iso2]))
+        timestamp = now.strftime('%Y-%m-%dT%H:%M:%S%z')
+        timestamp = timestamp[:-2] + ':' + timestamp[-2:]
+        return timestamp
+
 
 class ShopifyView(View):
     def get(self, request, *args, **kwargs):
@@ -129,56 +144,95 @@ class ShopifyView(View):
         if not check_password(password, users['password']):
             return response.json(111)
 
-        # 获取当前时间并格式化时间戳
-        now = datetime.now(pytz.timezone('America/New_York'))
-        timestamp = now.strftime('%Y-%m-%dT%H:%M:%S%z')
-        timestamp = timestamp[:-2] + ':' + timestamp[-2:]
-
-        customer_data = {
-            "email": email,
-            "created_at": timestamp,
-        }
-
         # 根据条件选择配置键
         if shopify_country:
+            timestamp = ShopifyMultipass.get_timezone_by_country(shopify_country)
             secret_key = f"{shopify_country}_multipass_secret"
-            store_name_key = f"{shopify_country}_store_name"
-        elif account_iso2 == "jp":
-            secret_key = "jp_multipass_secret"
-            store_name_key = "jp_store_name"
-        elif account_iso2 == "de":
-            secret_key = "de_multipass_secret"
-            store_name_key = "de_store_name"
+            multipass_secret = SHOPIFY_CONFIG[secret_key]
+            if shopify_country == "eu":
+                customer_data = {
+                    "email": email,
+                    "created_at": timestamp,
+                }
+                token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
+                redirect_url = f"https://eu.zositech.com/account/login/multipass/{token}"
+            elif shopify_country == "uk":
+                customer_data = {
+                    "email": email,
+                    "created_at": timestamp,
+                }
+                token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
+                redirect_url = f"https://www.zositech.co.uk/account/login/multipass/{token}"
+            elif shopify_country == "us":
+                customer_data = {
+                    "email": email,
+                    "created_at": timestamp,
+                }
+                token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
+                redirect_url = f"https://www.zositech.com/account/login/multipass/{token}"
+            else:
+                customer_data = {
+                    "email": email,
+                    "created_at": timestamp,
+                }
+                token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
+                redirect_url = f"https://www.zositech.{shopify_country}/account/login/multipass/{token}"
+            return response.json(0, redirect_url)
+        elif account_iso2 in ["de", "jp"]:
+            timestamp = ShopifyMultipass.get_timezone_by_country(account_iso2)
+            secret_key = f"{account_iso2}_multipass_secret"
+            multipass_secret = SHOPIFY_CONFIG[secret_key]
+            customer_data = {
+                "email": email,
+                "created_at": timestamp,
+            }
+            token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
+            redirect_url = f"https://www.zositech.{account_iso2}/account/login/multipass/{token}"
+            return response.json(0, redirect_url)
         elif account_iso2 == "uk":
-            secret_key = "uk_multipass_secret"
-            store_name_key = "uk_store_name"
+            timestamp = ShopifyMultipass.get_timezone_by_country(account_iso2)
+            secret_key = f"{account_iso2}_multipass_secret"
+            multipass_secret = SHOPIFY_CONFIG[secret_key]
+            customer_data = {
+                "email": email,
+                "created_at": timestamp,
+            }
+            token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
+            redirect_url = f"https://www.zositech.co.uk/account/login/multipass/{token}"
+            return response.json(0, redirect_url)
         elif CONFIG_INFO == CONFIG_EUR:
+            timestamp = ShopifyMultipass.get_timezone_by_country("eu")
             secret_key = "eu_multipass_secret"
-            store_name_key = "eu_store_name"
+            multipass_secret = SHOPIFY_CONFIG[secret_key]
+            customer_data = {
+                "email": email,
+                "created_at": timestamp,
+            }
+            token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
+            redirect_url = f"https://eu.zositech.com/account/login/multipass/{token}"
+            return response.json(0, redirect_url)
         elif CONFIG_INFO == CONFIG_US:
+            timestamp = ShopifyMultipass.get_timezone_by_country("us")
             secret_key = "us_multipass_secret"
             multipass_secret = SHOPIFY_CONFIG[secret_key]
+            customer_data = {
+                "email": email,
+                "created_at": timestamp,
+            }
             token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
             redirect_url = f"https://www.zositech.com/account/login/multipass/{token}"
             return response.json(0, redirect_url)
         else:
             return response.json(444)
 
-        # 获取配置并生成重定向URL
-        multipass_secret = SHOPIFY_CONFIG[secret_key]
-        store_name = SHOPIFY_CONFIG[store_name_key]
-        token = ShopifyMultipass.generate_multipass_token(multipass_secret, customer_data)
-        redirect_url = f"https://{store_name}.zositech.com/account/login/multipass/{token}"
-
-        return response.json(0, redirect_url)
-
     @staticmethod
     def shopify_register(request_dict, response):
         email = request_dict.get("email", None)
+        country_code = request_dict.get("countryCode", None)
         password = request_dict.get("password", None)
         authcode = request_dict.get("authCode", None)
 
-        if not all([email, password, authcode]):
+        if not all([email, password, authcode, country_code]):
             return response.json(444)
         data_valid = DataValid()
         if data_valid.email_validate(email) is not True:
@@ -210,6 +264,12 @@ class ShopifyView(View):
         # 创建用户
         password = make_password(password)
         new_userID = CommonService.getUserID(μs=False, setOTAID=True)
+        country_code = country_code.upper()
+        countr_qs = CountryModel.objects.filter(country_code=country_code).values("id")
+        if countr_qs.exists():
+            region_country = countr_qs[0]['id']
+        else:
+            return response.json(173)
         user_data = {
             "username": email,
             "NickName": email,
@@ -218,6 +278,8 @@ class ShopifyView(View):
             "userID": new_userID,
             "is_active": True,
             "user_isValid": True,
+            "region_country": region_country,
+            "region_status": 1
         }
         Device_User.objects.create(**user_data)
         reds.del_data(key=email + '_identifyingCode')
@@ -328,8 +390,13 @@ class ShopifyView(View):
 
         user_region_id = user_qs.values_list('region_country', flat=True).first()
         country_code = CountryModel.objects.filter(id=user_region_id).values_list("country_code", flat=True).first()
-
-        return response.json(0, country_code)
+        if country_code:
+            return response.json(0, country_code)
+        elif CONFIG_INFO == CONFIG_EUR:
+            return response.json(0, "uk")
+        elif CONFIG_INFO == CONFIG_US:
+            return response.json(0, "us")
+        return response.json(173)
 
     @staticmethod
     def get_country_domain_list(request_dict, response):
@@ -349,10 +416,10 @@ class ShopifyView(View):
             language = lang_qs[0]
             country_qs = CountryLanguageModel.objects.filter(language_id=language.id)
             country_qs = country_qs.annotate(api=F('country__region__zosi_api'), country_code=F('country__country_code'))
-            country_qs = country_qs.values('country_id', 'country_name', 'country_code', 'api').order_by('country_id')
+            country_qs = country_qs.values('country_id', 'country_name', 'country_code', 'api').order_by('country_name')
             country_list = []
             for country in country_qs:
-                country['api'] = country['api'] + 'shopify/shopifyRegister'
+                country['api'] = country['api']
                 country_list.append(country)
             return response.json(0, country_list)
         except Exception as e:

+ 88 - 0
Controller/UserDevice/APNConfigController.py

@@ -0,0 +1,88 @@
+import json
+
+from django.http import QueryDict
+from django.views import View
+from Ansjer.config import LOGGER
+
+from Model.models import DeviceVersionInfo, CountryAPN, LanguageModel, CountryLanguageModel, CountryModel
+from Object.Enums.RedisKeyConstant import RedisKeyConstant
+from Object.RedisObject import RedisObject
+from Object.ResponseObject import ResponseObject
+from Object.TokenObject import TokenObject
+from Service.CommonService import CommonService
+
+
+class APNConfigView(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('en')
+        tko = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
+        if tko.code != 0:
+            return response.json(tko.code)
+        response.lang = tko.lang
+        userID = tko.userID
+        if operation == 'APNConfigList':
+            return self.get_apn_config(request_dict, response)
+        elif operation == 'CountryCodeList':
+            return self.get_country_code_list(request_dict, response)
+        else:
+            return response.json(414)
+
+    @staticmethod
+    def get_apn_config(request_dict, response):
+        # 从请求字典中获取uid和version
+        country_code = request_dict.get('countryCode', None)
+        if not country_code:
+            return response.json(444)
+        country_code = country_code.upper()
+        country_apn_qs = CountryAPN.objects.filter(iso=country_code).values('apn_name', 'apn_address', 'username', 'password',
+                                                                   'auth_type')
+        country_apn_list = []
+        for country_apn in country_apn_qs:
+            country_apn_list.append({
+                'apnName': country_apn['apn_name'] if country_apn['apn_name'] else '',
+                'apnAddress': country_apn['apn_address'] if country_apn['apn_address'] else '',
+                'username': country_apn['username'] if country_apn['username'] else '',
+                'password': country_apn['password'] if country_apn['password'] else '',
+                'authType': country_apn['auth_type'] if country_apn['auth_type'] else '',
+            })
+
+        return response.json(0, country_apn_list)
+
+    @staticmethod
+    def get_country_code_list(request_dict, response):
+        lang = request_dict.get('lang', 'en')
+
+        # 获取语言 ID
+        language = LanguageModel.objects.filter(lang=lang).first()
+
+        if not language:
+            language = LanguageModel.objects.filter(lang='en').first()
+
+        # 查询tb_country_language和tb_country,获取国家名称和国家iso2代码
+        countries = CountryLanguageModel.objects.filter(language=language).select_related('country').order_by(
+            'country__country_name')
+
+        # 返回国家名和对应的 country_code
+        country_iso_list = [{
+            'countryName': country.country_name,
+            'countryCode': country.country.country_code
+        } for country in countries]
+
+        return response.json(0, country_iso_list)
+
+
+
+
+
+
+

+ 16 - 0
Model/models.py

@@ -5664,3 +5664,19 @@ class WeChatMiniProgram(models.Model):
         db_table = 'wechat_mini_program'
         verbose_name = '微信小程序'
 
+
+class CountryAPN(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增标记ID')
+    iso = models.CharField(max_length=100, verbose_name="国家代码")
+    apn_name = models.CharField(max_length=100, verbose_name="APN名称")
+    apn_address = models.CharField(max_length=100, verbose_name="APN地址")
+    username = models.CharField(max_length=100, blank=True, null=True, verbose_name="用户名")
+    password = models.CharField(max_length=100, blank=True, null=True, verbose_name="密码")
+    # 鉴权类型: 无、PAP、CHAP、PAP\PHCP
+    auth_type = models.CharField(max_length=50, blank=True, null=True, verbose_name="APN鉴权类型")
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'country_apn'
+        verbose_name = 'APN配置表'

+ 7 - 4
Object/AppleInAppPurchaseSubscriptionObject.py

@@ -14,7 +14,7 @@ ENV = Environment.SANDBOX if CONFIG_INFO == CONFIG_TEST else Environment.PRODUCT
 
 
 class InAppConfig:
-    def __init__(self, bundle_id: str):
+    def __init__(self, bundle_id: str, user_id=""):
         """
        初始化 AppConfig,加载与指定 bundle_id 对应的配置。
        :param bundle_id: 应用的 bundle ID,用于检索相关配置。
@@ -27,16 +27,19 @@ class InAppConfig:
         self.key_filename = config['key_filename']
         self.cert_names = config['cert_names']
         self.app_apple_id = config.get("app_apple_id")
-        self.environment = ENV
+        if user_id == "167083887153913800138000":
+            self.environment = Environment.SANDBOX
+        else:
+            self.environment = ENV
 
 
 class InAppPurchase:
-    def __init__(self, bundle_id="com.ansjer.zccloud"):
+    def __init__(self, bundle_id="com.ansjer.zccloud", user_id=""):
         """
         初始化 InAppSubscription,加载私钥并初始化客户端和解码器。
         :param app_config: 包含内购相关配置的 AppConfig 实例。
         """
-        app_config = InAppConfig(bundle_id)
+        app_config = InAppConfig(bundle_id, user_id)
         self.bundle_id = bundle_id
         self.config = app_config
         self.private_key = self._load_private_key(self.config.key_filename)