Procházet zdrojové kódy

苹果内购退款查询、退款意见编辑、提交退款

linhaohong před 9 měsíci
rodič
revize
02022126cb

+ 82 - 1
AdminController/ServeManagementController.py

@@ -12,6 +12,7 @@ import paypalrestsdk
 import requests
 import xlrd
 import xlwt
+from django.core.paginator import Paginator
 from django.db import transaction, connection
 from django.db.models import F, Sum, Count, Q
 from django.http import HttpResponse, StreamingHttpResponse
@@ -25,7 +26,8 @@ from Model.models import VodBucketModel, CDKcontextModel, Store_Meal, Order_Mode
     UID_Bucket, ExperienceContextModel, Lang, CloudLogModel, UidSetModel, Unused_Uid_Meal, \
     Device_Info, DeviceTypeModel, UnicomComboOrderInfo, AiService, CountryModel, CouponLang, CouponConfigModel, \
     CouponCombo, CouponModel, Device_User, AbnormalOrder, DailyReconciliation, StsCrdModel, LogModel, \
-    InAppPurchasePackage
+    InAppPurchasePackage, InAppRefund
+from Object.AppleInAppPurchaseSubscriptionObject import InAppPurchase
 from Object.ResponseObject import ResponseObject
 from Object.TokenObject import TokenObject
 from Object.UnicomObject import UnicomObjeect
@@ -159,6 +161,12 @@ class serveManagement(View):
                 return self.deviceAttritionAlert(request_dict, response)
             elif operation == 'deactivationPackage':  # 停用套餐
                 return self.deactivationPackage(request_dict, response)
+
+            # 苹果内购退款
+            elif operation == 'purchaseRefundList':
+                return self.purchase_refund_list(request_dict, response)
+            elif operation == 'editRefundPreference':
+                return self.edit_refund_preference(request_dict, response)
             else:
                 return response.json(404)
 
@@ -2811,3 +2819,76 @@ class serveManagement(View):
         except Exception as e:
             meg = '异常详情,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e))
             return response.json(500, meg)
+
+    @staticmethod
+    def purchase_refund_list(request_dict, response):
+        """
+        苹果内购退款列表
+        :param request_dict:
+        :param response:
+        :return:
+        """
+        order_id = request_dict.get("orderID", None)
+        uid = request_dict.get("uid", None)
+        apple_order = request_dict.get("appleOrder", None)
+        app_type = request_dict.get("appType", None)
+        page = int(request_dict.get('pageNo', 0))  # 默认值设为 0
+        page_size = int(request_dict.get('pageSize', 10))  # 默认值设为 10
+
+        in_app_refund_qs = InAppRefund.objects.all()
+        if order_id:
+            in_app_refund_qs = in_app_refund_qs.filter(orderID=order_id)
+        if uid:
+            in_app_refund_qs = in_app_refund_qs.filter(uid=order_id)
+        if apple_order and app_type:
+            app_type = int(app_type)
+            if app_type == 1:
+                bundle_id = "com.ansjer.zccloud"
+            elif app_type == 2:
+                bundle_id = "com.ansjer.zccloud"
+            else:
+                return response.json(0)
+            in_app_purchase_obj = InAppPurchase(bundle_id=bundle_id)
+            client = in_app_purchase_obj.client
+            signed_data_verifier = in_app_purchase_obj.verifier
+            order_lookup_response = client.look_up_order_id(apple_order)
+            signed_transactions = order_lookup_response.signedTransactions
+            if signed_transactions:
+                transaction_payload = signed_data_verifier.verify_and_decode_signed_transaction(signed_transactions[0])
+                transaction_id = transaction_payload.transactionId
+                in_app_refund_qs = in_app_refund_qs.filter(transaction_id=transaction_id)
+            else:
+                return response.json(800)
+        # 分页
+        paginator = Paginator(in_app_refund_qs.order_by("-created_time"), page_size)  # 每页显示 page_size 条
+        in_app_refund_page = paginator.get_page(page)
+
+        in_app_refund_list = []
+        for in_app_refund in in_app_refund_page:
+            in_app_refund_list.append(
+                {
+                    "refundId": in_app_refund.id,
+                    "orderID": in_app_refund.orderID,
+                    "uid": in_app_refund.uid,
+                    "refundPreference": in_app_refund.refund_preference,
+                    "refundProgress": in_app_refund.refund_progress,
+                    "appType": in_app_refund.product_type,
+                    "createTime": in_app_refund.created_time,
+                    "updateTime": in_app_refund.updated_time,
+                    "putTime": in_app_refund.put_time,
+                }
+            )
+        return response.json(0, {
+                "inAppRefundList": in_app_refund_list,
+                "total": paginator.count,
+            })
+
+    @staticmethod
+    def edit_refund_preference(request_dict, response):
+        refund_id = request_dict.get("refundId", None)
+        refund_preference = request_dict.get("refundPreference", None)
+        if not all([refund_id, refund_preference]):
+            return response.json(444)
+        InAppRefund.objects.filter(id=refund_id).update(refund_preference=refund_preference)
+        return response.json(0)
+

+ 69 - 34
Controller/InAppPurchaseController.py

@@ -57,6 +57,8 @@ class InAppPurchaseView(View):
             return self.vsees_notifications(request)
         elif operation == 'serverNotifications':  # App Store服务器通知
             return self.server_notifications(request)
+        elif operation == 'putRefundOrder':  # App Store服务器通知
+            return self.put_refund_order(request)
         token_code, user_id, response = CommonService.verify_token_get_user_id(request_dict, request)
         if token_code != 0:
             return response.json(token_code)
@@ -191,6 +193,9 @@ class InAppPurchaseView(View):
                         pay_result_url = CommonService.get_payment_status_url(lang, 'fail')
                         return response.json(0, {'url': pay_result_url})
 
+                else:
+                    return response.json(173, "内购套餐未分配")
+
             # 设备开通云存
             now_time = int(time.time())
             uid_bucket_id = cls.enable_cloud(channel, now_time, order_id, store_qs, uid)
@@ -607,44 +612,19 @@ class InAppPurchaseView(View):
                 decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
                     decoded_payload.data.signedTransactionInfo)
                 transaction_id = decoded_transaction_information.transactionId
+                app_account_token = decoded_transaction_information.appAccountToken
+                if not app_account_token:
+                    app_account_token = ""
                 orders_qs = Order_Model.objects.filter(transaction_id=transaction_id)
                 if not orders_qs.exists():
                     return HttpResponse(status=400)
                 orderID = orders_qs[0].orderID
-                unused_uid_meal_qs = Unused_Uid_Meal.objects.filter(order_id=orderID)
-                uid_bucket_qs = UID_Bucket.objects.filter(orderId=orderID, endTime__gt=int(time.time()))
-                if unused_uid_meal_qs.exists():
-                    consumptionStatus = ConsumptionStatus.NOT_CONSUMED
-                    deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
-                elif uid_bucket_qs.exists():
-                    consumptionStatus = ConsumptionStatus.PARTIALLY_CONSUMED
-                    deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
-                elif UID_Bucket.objects.filter(orderId=orderID, endTime__lt=int(time.time())):
-                    consumptionStatus = ConsumptionStatus.FULLY_CONSUMED
-                    deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
-                else:
-                    consumptionStatus = ConsumptionStatus.UNDECLARED
-                    deliveryStatus = DeliveryStatus.DID_NOT_DELIVER_FOR_OTHER_REASON
-                in_app_refund_qs = InAppRefund.objects.filter(transaction_id=transaction_id).exists()
-                refundPreference = RefundPreference.PREFER_DECLINE
-                if in_app_refund_qs.exists():
-                    if in_app_refund_qs[0].refund_preference == 1:
-                        refundPreference = RefundPreference.PREFER_GRANT
-                consumption_request = ConsumptionRequest(
-                    customerConsented=True,
-                    consumptionStatus=consumptionStatus,
-                    platform=Platform.UNDECLARED,
-                    sampleContentProvided=True,
-                    deliveryStatus=deliveryStatus,
-                    appAccountToken="",
-                    accountTenure=AccountTenure.UNDECLARED,
-                    playTime=PlayTime.UNDECLARED,
-                    lifetimeDollarsRefunded=LifetimeDollarsRefunded.UNDECLARED,
-                    lifetimeDollarsPurchased=LifetimeDollarsPurchased.UNDECLARED,
-                    userStatus=UserStatus.ACTIVE,
-                    refundPreference=refundPreference,
-                )
-                client.send_consumption_data(transaction_id, consumption_request)
+                uid = orders_qs[0].UID
+                now_time = int(time.time())
+                put_time = now_time + 11.5 * 60 * 60
+                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":
@@ -823,3 +803,58 @@ class InAppPurchaseView(View):
                     status_code = eur_response.status_code
 
         return HttpResponse(status=status_code)
+
+    @staticmethod
+    def put_refund_order(response):
+        put_time = int(time.time())
+        in_app_refund_qs = InAppRefund.objects.filter(refund_progress=0, put_time__lt=put_time)
+        for in_app_refund in in_app_refund_qs:
+            transaction_id = in_app_refund.transaction_id
+            app_type = in_app_refund.app_type
+            if app_type == 1:
+                bundle_id = "com.ansjer.zccloud"
+            elif app_type == 2:
+                bundle_id = "com.ansjer.zccloud"
+            else:
+                return response.json(0)
+            in_app_purchase_obj = InAppPurchase(bundle_id=bundle_id)
+            # AppStoreServerAPIClient 用于查询交易信息
+            client = in_app_purchase_obj.client
+            orderID = in_app_refund.orderID
+            app_account_token = in_app_refund.app_account_token
+            unused_uid_meal_qs = Unused_Uid_Meal.objects.filter(order_id=orderID)
+            uid_bucket_qs = UID_Bucket.objects.filter(orderId=orderID, endTime__gt=int(time.time()))
+            if unused_uid_meal_qs.exists():
+                consumptionStatus = ConsumptionStatus.NOT_CONSUMED
+                deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
+            elif uid_bucket_qs.exists():
+                consumptionStatus = ConsumptionStatus.PARTIALLY_CONSUMED
+                deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
+            elif UID_Bucket.objects.filter(orderId=orderID, endTime__lt=int(time.time())):
+                consumptionStatus = ConsumptionStatus.FULLY_CONSUMED
+                deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
+            else:
+                consumptionStatus = ConsumptionStatus.UNDECLARED
+                deliveryStatus = DeliveryStatus.DID_NOT_DELIVER_FOR_OTHER_REASON
+
+            if in_app_refund.refund_preference == 1:
+                refundPreference = RefundPreference.PREFER_GRANT
+            else:
+                refundPreference = RefundPreference.PREFER_DECLINE
+
+            consumption_request = ConsumptionRequest(
+                customerConsented=True,
+                consumptionStatus=consumptionStatus,
+                platform=Platform.UNDECLARED,
+                sampleContentProvided=True,
+                deliveryStatus=deliveryStatus,
+                appAccountToken=app_account_token,
+                accountTenure=AccountTenure.UNDECLARED,
+                playTime=PlayTime.UNDECLARED,
+                lifetimeDollarsRefunded=LifetimeDollarsRefunded.UNDECLARED,
+                lifetimeDollarsPurchased=LifetimeDollarsPurchased.UNDECLARED,
+                userStatus=UserStatus.ACTIVE,
+                refundPreference=refundPreference,
+            )
+            client.send_consumption_data(transaction_id, consumption_request)
+        return response.json(0)

+ 6 - 0
Model/models.py

@@ -5318,12 +5318,18 @@ class UserSetStatus(models.Model):
 
 class InAppRefund(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='自增标记ID')
+    uid = models.CharField(default='', max_length=32, db_index=True, verbose_name='设备uid')
     transaction_id = models.CharField(default='', max_length=32, verbose_name='苹果事务id')
     orderID = models.CharField(blank=True, max_length=20, verbose_name=u'订单id')
+    app_type = models.SmallIntegerField(default=0, verbose_name='应用类型') # 1是zosiSmart 2是Vsees
     # 0不同意 1同意
     refund_preference = models.IntegerField(default=0, verbose_name='是否同意退款')
+    # 0申请退款 1已提交退款建议 2退款成功 3退款失败
+    refund_progress = models.IntegerField(default=0, verbose_name='退款进度')
+    app_account_token = models.TextField(blank=True, default='', verbose_name=u'参数内容')
     created_time = models.IntegerField(default=0, verbose_name='创建时间')
     updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+    put_time = models.IntegerField(default=0, verbose_name='提交时间')
 
     class Meta:
         db_table = 'in_app_refund'