InAppPurchaseController.py 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. # @Author : Rocky
  2. # @File : InAppPurchaseController.py
  3. # @Time : 2024/6/21 9:10
  4. import logging
  5. import time
  6. import json
  7. import threading
  8. import requests
  9. from appstoreserverlibrary.models.Environment import Environment
  10. from appstoreserverlibrary.api_client import AppStoreServerAPIClient, GetTransactionHistoryVersion
  11. from appstoreserverlibrary.models.AccountTenure import AccountTenure
  12. from appstoreserverlibrary.models.ConsumptionRequest import ConsumptionRequest
  13. from appstoreserverlibrary.models.ConsumptionStatus import ConsumptionStatus
  14. from appstoreserverlibrary.models.DeliveryStatus import DeliveryStatus
  15. from appstoreserverlibrary.models.Environment import Environment
  16. from appstoreserverlibrary.models.LifetimeDollarsPurchased import LifetimeDollarsPurchased
  17. from appstoreserverlibrary.models.LifetimeDollarsRefunded import LifetimeDollarsRefunded
  18. from appstoreserverlibrary.models.Platform import Platform
  19. from appstoreserverlibrary.models.PlayTime import PlayTime
  20. from appstoreserverlibrary.models.RefundPreference import RefundPreference
  21. from appstoreserverlibrary.models.UserStatus import UserStatus
  22. from appstoreserverlibrary.receipt_utility import ReceiptUtility
  23. from django.views import View
  24. from django.http import HttpResponse
  25. from Ansjer.config import LOGGER, CONFIG_INFO, CONFIG_TEST, PAY_TYPE_IN_APP_PURCHASE, BASE_DIR, CONFIG_US
  26. from Controller.CheckUserData import DataValid
  27. from Model.models import Order_Model, Store_Meal, Device_Info, UID_Bucket, Unused_Uid_Meal, AiService, Device_User, \
  28. SysMsgModel, DeviceApplePackage, InAppPurchasePackage, InAppRefund, OrderPayLog, CouponModel
  29. from Object.AWS.S3Email import S3Email
  30. from Object.AliSmsObject import AliSmsObject
  31. from Object.AppleInAppPurchaseSubscriptionObject import InAppPurchase
  32. from Object.Enums.RedisKeyConstant import RedisKeyConstant
  33. from Object.RedisObject import RedisObject
  34. from Service.CommonService import CommonService
  35. ENV = Environment.SANDBOX if CONFIG_INFO == CONFIG_TEST else Environment.PRODUCTION
  36. logger = logging.getLogger('apple_pay')
  37. class InAppPurchaseView(View):
  38. def get(self, request, *args, **kwargs):
  39. request.encoding = 'utf-8'
  40. operation = kwargs.get('operation')
  41. return self.validation(request.GET, request, operation)
  42. def post(self, request, *args, **kwargs):
  43. request.encoding = 'utf-8'
  44. operation = kwargs.get('operation')
  45. return self.validation(request.POST, request, operation)
  46. def validation(self, request_dict, request, operation):
  47. if operation == 'AppStoreServerNotifications': # App Store服务器通知(用于转发通知)
  48. return self.app_store_server_notifications(request, request_dict)
  49. elif operation == 'AppStoreServerNotificationsVsees': # App Store服务器通知(用于转发通知)
  50. return self.app_store_server_notifications_vsees(request, request_dict)
  51. elif operation == 'vseesNotifications':
  52. return self.vsees_notifications(request)
  53. elif operation == 'serverNotifications': # App Store服务器通知
  54. return self.server_notifications(request)
  55. elif operation == 'putRefundOrder': # App Store服务器通知
  56. return self.put_refund_order()
  57. token_code, user_id, response = CommonService.verify_token_get_user_id(request_dict, request)
  58. if token_code != 0:
  59. return response.json(token_code)
  60. if operation == 'verifyTransaction': # 认证交易
  61. return self.verify_transaction(user_id, request_dict, response)
  62. @classmethod
  63. def verify_transaction(cls, user_id, request_dict, response):
  64. """
  65. 认证交易
  66. @param user_id: 用户id
  67. @param request_dict: 请求参数
  68. @request_dict receipt: 收据
  69. @param response: 响应对象
  70. @return: response
  71. """
  72. receipt = request_dict.get('receipt', None)
  73. transaction_identifier = request_dict.get('transactionIdentifier', "")
  74. original_transaction_identifier = request_dict.get('originalTransactionIdentifier', "")
  75. order_id = request_dict.get('orderID', None)
  76. uid = request_dict.get('uid', None)
  77. lang = request_dict.get('lang', 'en')
  78. channel = request_dict.get('channel', None)
  79. app_type = request_dict.get('app_type', 1)
  80. logger.info(
  81. f"苹果内购认证交易订单orderID:{order_id},"
  82. f"transaction_id: {transaction_identifier},"
  83. f"original_transaction_id: {original_transaction_identifier},"
  84. f"receipt: {receipt}, app_type: {app_type}"
  85. )
  86. if not all([uid, channel, order_id]):
  87. return response.json(444)
  88. # redis加锁,防止订单重复
  89. redis_obj = RedisObject()
  90. redis_key = order_id + 'in_app_purchase'
  91. is_lock = redis_obj.CONN.setnx(redis_key, 1)
  92. if not is_lock:
  93. return response.json(5)
  94. redis_obj.CONN.expire(redis_key, 60)
  95. try:
  96. # 检查商品id是否正确
  97. app_type = int(app_type)
  98. if app_type == 1:
  99. bundle_id = "com.ansjer.zccloud"
  100. elif app_type == 2:
  101. bundle_id = "com.cloudlife.commissionf"
  102. else:
  103. return response.json(444, "app_type不存在")
  104. # 验证订单是否存在
  105. order_qs = Order_Model.objects.filter(orderID=order_id, UID=uid, app_type=app_type).values("rank_id",
  106. "transaction_id",
  107. "coupon_id")
  108. if not order_qs.exists():
  109. return response.json(173)
  110. if order_qs[0]["transaction_id"]:
  111. return response.json(0)
  112. if UID_Bucket.objects.filter(orderId=order_id).exists():
  113. return response.json(0)
  114. if Unused_Uid_Meal.objects.filter(order_id=order_id).exists():
  115. return response.json(0)
  116. # 实例化订阅类
  117. in_app_purchase = InAppPurchase(bundle_id=bundle_id)
  118. # ReceiptUtility 用于解析收据为transaction_id
  119. receipt_util = in_app_purchase.receipt_util
  120. # AppStoreServerAPIClient 用于查询交易信息
  121. client = in_app_purchase.client
  122. # SignedDataVerifier 用于解析查询到的交易信息
  123. signed_data_verifier = in_app_purchase.verifier
  124. # 解析收据(循环扣款时不需要这一步, 直接获取transaction_id)
  125. transaction_id = transaction_identifier
  126. if transaction_identifier == "":
  127. transaction_id = receipt_util.extract_transaction_id_from_app_receipt(receipt)
  128. if not transaction_id:
  129. logger.info(f"苹果内购认证交易订单orderID:{order_id}, 没有transaction_id")
  130. pay_result_url = CommonService.get_payment_status_url(lang, 'fail')
  131. return response.json(0, {'url': pay_result_url})
  132. logger.info(f"苹果内购认证交易订单orderID:{order_id}, transaction_id:{transaction_id}, 时间戳: {int(time.time())}")
  133. OrderPayLog.objects.create(order_id=order_id, order_no=transaction_id,
  134. business_name=f"内购验单",
  135. created_time=int(time.time()), updated_time=int(time.time()),
  136. access_result="SUCCESS")
  137. # 查询交易信息
  138. transaction_info = ""
  139. attempts = 0
  140. while attempts < 6:
  141. try:
  142. transaction_info = client.get_transaction_info(transaction_id)
  143. break
  144. except ConnectionError as err:
  145. attempts += 1
  146. if attempts == 5:
  147. OrderPayLog.objects.create(order_id=order_id, order_no=transaction_id,
  148. business_name=f"{order_id}获取transactionInfo超时",
  149. created_time=int(time.time()), updated_time=int(time.time()),
  150. access_result="ERROR")
  151. return response.json(5)
  152. logger.info(
  153. f"订单orderId:{order_id}, transaction_id:{transaction_id}, 第{attempts}次获取支付信息超时")
  154. signed_transaction_info = transaction_info.signedTransactionInfo
  155. # 解析交易信息
  156. payload = signed_data_verifier.verify_and_decode_signed_transaction(signed_transaction_info)
  157. # 获取交易的商品id
  158. product_id = payload.productId if payload and payload.productId else None
  159. if not product_id:
  160. logger.info(f"苹果内购认证交易订单orderID:{order_id}, product_id获取失败")
  161. pay_result_url = CommonService.get_payment_status_url(lang, 'fail')
  162. return response.json(0, {'url': pay_result_url})
  163. in_app_purchase_package_qs = InAppPurchasePackage.objects.filter(product_id=product_id)
  164. if not in_app_purchase_package_qs.exists():
  165. logger.info(f"苹果内购认证交易订单orderID:{order_id}, InAppPurchasePackage表未查询到product_id")
  166. return response.json(173, "内购商品id不存在")
  167. # 验证套餐是否存在
  168. store_qs = Store_Meal.objects.filter(id=order_qs[0]['rank_id']).values(
  169. 'id', 'currency', 'price', 'lang__content', 'day', 'commodity_type', 'lang__title', 'expire',
  170. 'commodity_code', 'discount_price', 'bucket_id', 'bucket__mold', 'cycle_config_id', 'is_ai')
  171. if not store_qs.exists():
  172. return response.json(173, "云存套餐不存在")
  173. # 验证内购套餐是否存在
  174. in_app_purchase_package_qs = InAppPurchasePackage.objects.filter(product_id=product_id)
  175. if not in_app_purchase_package_qs.exists():
  176. return response.json(173, "内购套餐不存在")
  177. # 循环扣款
  178. if original_transaction_identifier != "" and in_app_purchase_package_qs[0].package_type == 1:
  179. device_apple_package_qs = DeviceApplePackage.objects.filter(
  180. original_transaction_id=original_transaction_identifier)
  181. if device_apple_package_qs.exists():
  182. # 第一种情况: 套餐已过期再次订阅
  183. if device_apple_package_qs[0].uid == uid and device_apple_package_qs[0].subscription_status == 2:
  184. # 使用App Store服务器通知接口订阅
  185. Order_Model.objects.filter(orderID=order_id).delete()
  186. pay_result_url = CommonService.get_payment_status_url(lang, 'success')
  187. return response.json(0, {'url': pay_result_url})
  188. # 第二种情况: 套餐未过期已取消再次订阅
  189. elif device_apple_package_qs[0].uid == uid and device_apple_package_qs[0].subscription_status == 3:
  190. # 使用App Store服务器通知接口修改订阅状态
  191. Order_Model.objects.filter(orderID=order_id).delete()
  192. pay_result_url = CommonService.get_payment_status_url(lang, 'success')
  193. return response.json(0, {'url': pay_result_url})
  194. # 第三种情况: 首次订阅
  195. elif device_apple_package_qs[0].uid == uid and device_apple_package_qs[0].subscription_status == 0:
  196. logger.info(f"苹果内购认证交易订单orderID:{order_id}, 用户首次订阅")
  197. else:
  198. logger.info(
  199. f"错误调用此借口,orderID:{order_id}, uid:{uid}, 订阅状态:{device_apple_package_qs[0].subscription_status}")
  200. pay_result_url = CommonService.get_payment_status_url(lang, 'fail')
  201. return response.json(0, {'url': pay_result_url})
  202. else:
  203. return response.json(173, "内购套餐未分配")
  204. # 设备开通云存
  205. now_time = int(time.time())
  206. uid_bucket_id = cls.enable_cloud(channel, now_time, order_id, store_qs, uid)
  207. if order_qs[0]["coupon_id"]:
  208. c_id = order_qs[0]["coupon_id"]
  209. key_coupon = RedisKeyConstant.COUPON_ID_LOCK.value + c_id
  210. redis_obj.del_data(key_coupon)
  211. CouponModel.objects.filter(id=c_id).update(use_status=2, update_time=now_time)
  212. # 修改订阅状态
  213. if payload.rawType == "Auto-Renewable Subscription":
  214. original_transaction_id = payload.originalTransactionId
  215. in_app_purchase_package = in_app_purchase_package_qs.values('id').first()
  216. package_id = in_app_purchase_package['id']
  217. DeviceApplePackage.objects.filter(userID=user_id, uid=uid, package_id=package_id).update(
  218. subscription_status=1, original_transaction_id=original_transaction_id,
  219. )
  220. order_qs.update(status=1, uid_bucket_id=uid_bucket_id,
  221. transaction_id=transaction_id, create_vod=1,
  222. payTime=now_time, updTime=now_time,
  223. original_transaction_id=original_transaction_id)
  224. else:
  225. order_qs.update(status=1, uid_bucket_id=uid_bucket_id,
  226. transaction_id=transaction_id, create_vod=1,
  227. payTime=now_time, updTime=now_time)
  228. # 构建云存套餐消息
  229. sys_msg_text_list = cls.cloud_storage_message(uid)
  230. # 发送云存套餐购买消息
  231. asy = threading.Thread(target=cls.do_vod_msg_notice,
  232. args=(uid, user_id, lang, sys_msg_text_list))
  233. asy.start()
  234. redis_obj.del_data(redis_key)
  235. pay_result_url = CommonService.get_payment_status_url(lang, 'success')
  236. OrderPayLog.objects.create(order_id=order_id, order_no=transaction_id,
  237. business_name=f"内购充值成功",
  238. created_time=int(time.time()), updated_time=int(time.time()),
  239. access_result="SUCCESS")
  240. return response.json(0, {'url': pay_result_url})
  241. except Exception as e:
  242. redis_obj.del_data(redis_key)
  243. logger.info('苹果内购认证交易接口异常:{}'.
  244. format('error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))))
  245. OrderPayLog.objects.create(order_id=order_id, business_name=f"内购验单异常",
  246. created_time=int(time.time()), updated_time=int(time.time()), access_result="ERROR")
  247. pay_result_url = CommonService.get_payment_status_url(lang, 'fail')
  248. return response.json(0, {'url': pay_result_url})
  249. @classmethod
  250. def cloud_storage_message(cls, uid):
  251. # 发送云存开通信息
  252. date_time = time.strftime("%Y-%m-%d", time.localtime())
  253. # 如果存在序列号,消息提示用序列号
  254. device_info_qs = Device_Info.objects.filter(UID=uid).values('serial_number', 'Type')
  255. serial_number = device_info_qs[0]['serial_number']
  256. device_type = device_info_qs[0]['Type']
  257. if serial_number:
  258. device_name = CommonService.get_full_serial_number(uid, serial_number, device_type)
  259. else:
  260. device_name = uid
  261. sys_msg_text_list = [
  262. '温馨提示:尊敬的客户,您的{}设备在{}已成功购买云存套餐'.format(device_name, date_time),
  263. 'Dear customer,you already subscribed the cloud storage package successfully for device {} on '.
  264. format(device_name, time.strftime('%b %dth,%Y', time.localtime()))]
  265. return sys_msg_text_list
  266. @classmethod
  267. def enable_cloud(cls, channel, now_time, order_id, store_qs, uid):
  268. bucket_id = store_qs[0]['bucket_id']
  269. is_ai = store_qs[0]['is_ai']
  270. expire = store_qs[0]['expire']
  271. end_time = CommonService.calcMonthLater(expire)
  272. # 查询设备是否已开过云存
  273. use_flag = True
  274. uid_bucket_qs = UID_Bucket.objects.filter(uid=uid). \
  275. values('id', 'bucket_id', 'bucket__region', 'endTime', 'use_status')
  276. if uid_bucket_qs.exists():
  277. uid_bucket = uid_bucket_qs.first()
  278. uid_bucket_id = uid_bucket['id']
  279. # 有正在使用的套餐,创建为未使用套餐
  280. if uid_bucket['use_status'] == 1 and uid_bucket['endTime'] > now_time:
  281. Unused_Uid_Meal.objects.create(
  282. uid=uid, channel=channel, addTime=now_time, order_id=order_id, expire=expire, is_ai=is_ai,
  283. bucket_id=bucket_id)
  284. UID_Bucket.objects.filter(id=uid_bucket_id).update(has_unused=1)
  285. use_flag = False
  286. # 无正在使用套餐,直接使用套餐
  287. else:
  288. UID_Bucket.objects.filter(id=uid_bucket_id).update(
  289. channel=channel, bucket_id=bucket_id, endTime=end_time, updateTime=now_time, use_status=1,
  290. orderId=order_id)
  291. else:
  292. uid_bucket = UID_Bucket.objects.create(
  293. uid=uid, channel=channel, bucket_id=bucket_id, endTime=end_time, use_status=1, orderId=order_id,
  294. addTime=now_time, updateTime=now_time)
  295. uid_bucket_id = uid_bucket.id
  296. # 开通AI服务
  297. if is_ai and use_flag:
  298. ai_service = AiService.objects.filter(uid=uid, channel=channel)
  299. # 有正在使用的套餐,叠加套餐时间,否则创建
  300. if ai_service.exists():
  301. ai_service.update(updTime=now_time, use_status=1, orders_id=order_id, endTime=end_time)
  302. else:
  303. AiService.objects.create(
  304. uid=uid, channel=channel, detect_status=1, use_status=1, orders_id=order_id,
  305. addTime=now_time, updTime=now_time, endTime=end_time)
  306. return uid_bucket_id
  307. @classmethod
  308. def do_vod_msg_notice(cls, uid, user_id, lang, sys_msg_text_list):
  309. """
  310. 发送云存开通信息
  311. @param uid: uid
  312. @param user_id: 用户id
  313. @param lang: 语言
  314. @param sys_msg_text_list: 消息列表
  315. @return: response
  316. """
  317. if lang == 'cn':
  318. sys_msg_text = sys_msg_text_list[0]
  319. else:
  320. sys_msg_text = sys_msg_text_list[1]
  321. now_time = int(time.time())
  322. create_data = {
  323. 'userID_id': user_id,
  324. 'msg': sys_msg_text,
  325. 'addTime': now_time,
  326. 'updTime': now_time,
  327. 'uid': uid,
  328. 'eventType': 0
  329. }
  330. SysMsgModel.objects.create(**create_data)
  331. # 不接收邮件用户
  332. if user_id == '167015836969813800138000':
  333. return
  334. user_qs = Device_User.objects.filter(userID=user_id)
  335. if user_qs.exists():
  336. user = user_qs.first()
  337. username = user.username
  338. data_valid = DataValid()
  339. if data_valid.email_validate(username):
  340. S3Email().faEmail(sys_msg_text, username)
  341. elif data_valid.mobile_validate(username):
  342. # 如果存在序列号,消息提示用序列号
  343. device_info_qs = Device_Info.objects.filter(UID=uid).values('serial_number', 'Type')
  344. if device_info_qs.exists():
  345. serial_number = device_info_qs[0]['serial_number']
  346. device_type = device_info_qs[0]['Type']
  347. if serial_number:
  348. device_name = CommonService.get_full_serial_number(uid, serial_number, device_type)
  349. else:
  350. device_name = uid
  351. params = '{"devname":"%s","submittime":"%s"}' % (
  352. device_name, time.strftime("%Y-%m-%d", time.localtime()))
  353. cls.send_message(username, params, 'SMS_219738485')
  354. @staticmethod
  355. def send_message(phone, params, temp_msg):
  356. """
  357. 发送手机消息
  358. @param phone: 用户名
  359. @param params: 消息参数
  360. @param temp_msg: sms码
  361. """
  362. sign_ms = '周视'
  363. ali_sms = AliSmsObject()
  364. ali_sms.send_code_sms_cloud(phone=phone, params=params, sign_name=sign_ms, temp_msg=temp_msg)
  365. @classmethod
  366. def app_store_server_notifications(cls, request, request_dict):
  367. logger = logging.getLogger('apple_pay')
  368. if request.method != 'POST':
  369. logger.info(f'App Store服务器通知不是post请求, 参数{request_dict}')
  370. return HttpResponse(status=400)
  371. try:
  372. request_data = json.loads(request.body)
  373. except json.JSONDecodeError:
  374. logger.info('无法解析请求体为JSON')
  375. return HttpResponse(status=400)
  376. request_data['bundleId'] = 'com.ansjer.zccloud'
  377. updated_request_body = json.dumps(request_data)
  378. if CONFIG_INFO == CONFIG_TEST:
  379. logger.info('测试环境, App Store服务器通知发送到测试服')
  380. response_test = requests.post(url="https://test.zositechc.cn/inAppPurchase/serverNotifications",
  381. json=updated_request_body)
  382. return HttpResponse(status=response_test.status_code)
  383. response_us = requests.post(url="https://www.dvema.com/inAppPurchase/serverNotifications",
  384. json=updated_request_body)
  385. status_code = response_us.status_code
  386. if status_code != 200:
  387. response_eu = requests.post(url="https://api.zositeche.com/inAppPurchase/serverNotifications",
  388. json=updated_request_body)
  389. status_code = response_eu.status_code
  390. if status_code == 200:
  391. return HttpResponse(status=200)
  392. return HttpResponse(status=400)
  393. @classmethod
  394. def server_notifications(cls, request):
  395. try:
  396. logger.info('App Store服务器通知请求类型:{}'.format(request.method))
  397. logger.info('App Store服务器通知参数:{}'.format(request.POST))
  398. logger.info('App Store服务器通知请求body:{}'.format(request.body))
  399. payload = json.loads(json.loads(request.body))
  400. logger.info('App Store服务器通知payload:{}'.format(payload))
  401. # 获取 signedPayload
  402. signed_payload = payload.get('signedPayload')
  403. bundle_id = payload.get('bundleId')
  404. if not signed_payload:
  405. return HttpResponse(status=400)
  406. if bundle_id == "com.ansjer.zccloud":
  407. app_type = 1
  408. else:
  409. app_type = 2
  410. in_app_purchase_obj = InAppPurchase(bundle_id=bundle_id)
  411. # AppStoreServerAPIClient 用于查询交易信息
  412. client = in_app_purchase_obj.client
  413. # SignedDataVerifier 用于解析查询到的交易信息
  414. signed_data_verifier = in_app_purchase_obj.verifier
  415. # 验证签名并解码 payload
  416. decoded_payload = signed_data_verifier.verify_and_decode_notification(signed_payload)
  417. logger.info(f"App Store服务器通知解码后decoded_payload:{decoded_payload}")
  418. raw_notification_type = str(decoded_payload.rawNotificationType)
  419. raw_subtype = str(decoded_payload.rawSubtype)
  420. logger.info(f"App Store服务器通知, 大类型{raw_notification_type}, 小类型{raw_subtype}")
  421. if str(decoded_payload.rawNotificationType) == "DID_RENEW":
  422. # 续订
  423. decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
  424. decoded_payload.data.signedTransactionInfo)
  425. # originalTransactionId 原始购买的交易标识符
  426. original_transaction_id = decoded_transaction_information.originalTransactionId
  427. transaction_id = decoded_transaction_information.transactionId
  428. logger.info(f"App Store服务器通知, 续订originalTransactionId{original_transaction_id}")
  429. if not original_transaction_id:
  430. logger.info(f"App Store服务器通知 originalTransactionId原始购买的交易标识符为空, 返回状态 400")
  431. return HttpResponse(status=400)
  432. ord_order = Order_Model.objects.filter(original_transaction_id=original_transaction_id).order_by(
  433. '-addTime').values("channel", "UID", "payType", "userID_id", "rank_id")
  434. if not ord_order.exists():
  435. logger.info(
  436. f"App Store服务器通知, 未查询到旧订单信息, originalTransactionId:{original_transaction_id}, 返回状态 400")
  437. return HttpResponse(status=400)
  438. channel = ord_order[0]["channel"]
  439. uid = ord_order[0]["UID"]
  440. pay_type = ord_order[0]["payType"]
  441. user_id = ord_order[0]["userID_id"]
  442. store_qs = Store_Meal.objects.filter(id=ord_order[0]["rank_id"]). \
  443. values(
  444. 'id', 'currency', 'price', 'lang__content', 'day', 'commodity_type', 'lang__title',
  445. 'expire', 'lang__lang',
  446. 'commodity_code', 'discount_price', 'bucket_id', 'bucket__mold', 'cycle_config_id', 'is_ai')
  447. if not store_qs.exists():
  448. logger.info(f"App Store服务器通知云存套餐不存在, 返回状态 400")
  449. return HttpResponse(status=400)
  450. # 解决云存充值成功, 由于一些原因返回500 导致苹果未扣款的问题
  451. if Order_Model.objects.filter(transaction_id=transaction_id, status=1).exists():
  452. logger.info(f"App Store服务器通知云存续订订单已存在, transactionId:{transaction_id} 返回状态 200")
  453. return HttpResponse(status=200)
  454. order_id = CommonService.createOrderID()
  455. rank_id = store_qs[0]['id']
  456. currency = store_qs[0]['currency']
  457. price = store_qs[0]['price']
  458. is_ai = store_qs[0]['is_ai']
  459. content = store_qs[0]['lang__content']
  460. commodity_code = store_qs[0]['commodity_code']
  461. commodity_type = store_qs[0]['commodity_type']
  462. lang = store_qs[0]['lang__lang']
  463. order_type = 1 if is_ai else 0
  464. now_time = int(time.time())
  465. store_meal_qs = Store_Meal.objects.filter(id=rank_id, lang__lang='cn', is_show=0). \
  466. values('lang__title', 'lang__content')
  467. if store_meal_qs.exists():
  468. store_meal_name = store_meal_qs[0]['lang__title'] + '-' + store_meal_qs[0]['lang__content']
  469. else:
  470. store_meal_name = '未知套餐'
  471. # 创建订单
  472. order = Order_Model.objects.create(
  473. orderID=order_id, UID=uid, channel=channel, userID_id=user_id, desc=content,
  474. payType=pay_type, payTime=now_time, price=price, currency=currency, addTime=now_time,
  475. updTime=now_time, order_type=order_type, commodity_code=commodity_code,
  476. commodity_type=commodity_type, rank_id=rank_id, ai_rank_id=1, status=1,
  477. store_meal_name=store_meal_name, app_type=app_type
  478. )
  479. # 充值云存套餐
  480. uid_bucket_id = cls.enable_cloud(channel, now_time, order_id, store_qs, uid)
  481. # 修改订单信息
  482. order.uid_bucket_id = uid_bucket_id
  483. order.transaction_id = transaction_id
  484. order.original_transaction_id = original_transaction_id
  485. order.save()
  486. # 构建云存套餐消息
  487. sys_msg_text_list = cls.cloud_storage_message(uid)
  488. cls.do_vod_msg_notice(uid, user_id, lang, sys_msg_text_list)
  489. return HttpResponse(status=200)
  490. elif str(decoded_payload.rawNotificationType) == "SUBSCRIBED":
  491. # 处理订阅 ---> 首次充值逻辑写在了认证交易
  492. if decoded_payload.rawSubtype == "RESUBSCRIBE":
  493. decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
  494. decoded_payload.data.signedTransactionInfo)
  495. # originalTransactionId 原始购买的交易标识符
  496. original_transaction_id = decoded_transaction_information.originalTransactionId
  497. transaction_id = decoded_transaction_information.transactionId
  498. app_account_token = decoded_transaction_information.appAccountToken
  499. app_account_token = json.loads(app_account_token)
  500. logger.info(
  501. f"App Store服务器通知,再次订阅originalTransactionId原始购买的交易标识符{original_transaction_id}")
  502. if not original_transaction_id:
  503. logger.info(f"App Store服务器通知 originalTransactionId原始购买的交易标识符为空, 返回状态 400")
  504. return HttpResponse(status=400)
  505. # 查旧订单消息
  506. ord_order_qs = Order_Model.objects.filter(original_transaction_id=original_transaction_id)
  507. if not ord_order_qs.exists():
  508. logger.info(f"App Store服务器通知未查询到旧订单信息, originalTransactionId:{original_transaction_id}, 返回状态 400")
  509. return HttpResponse(status=400)
  510. # 解决云存充值成功, 由于一些原因返回500 导致苹果未扣款的问题
  511. if Order_Model.objects.filter(transaction_id=transaction_id, status=1).exists():
  512. logger.info(
  513. f"App Store服务器通知云存续订订单已存在, transactionId:{transaction_id} 返回状态 200")
  514. return HttpResponse(status=200)
  515. ord_order = ord_order_qs.order_by('-addTime').values("channel", "UID", "payType", "userID_id")
  516. channel = ord_order[0]["channel"]
  517. uid = ord_order[0]["UID"]
  518. pay_type = ord_order[0]["payType"]
  519. user_id = ord_order[0]["userID_id"]
  520. new_user_id = app_account_token["user_id"]
  521. if new_user_id != user_id:
  522. uid = app_account_token["UID"]
  523. user_id = new_user_id
  524. # 用产品id找到使用的套餐
  525. product_id = decoded_transaction_information.productId
  526. rank_id = InAppPurchasePackage.objects.filter(product_id=product_id).values("rank")[0]["rank"]
  527. store_qs = Store_Meal.objects.filter(id=rank_id). \
  528. values(
  529. 'id', 'currency', 'price', 'lang__content', 'day', 'commodity_type', 'lang__title',
  530. 'expire', 'lang__lang',
  531. 'commodity_code', 'discount_price', 'bucket_id', 'bucket__mold', 'cycle_config_id', 'is_ai')
  532. if not store_qs.exists():
  533. logger.info(f"App Store服务器通知云存套餐不存在, 返回状态 400")
  534. return HttpResponse(status=400)
  535. order_id = CommonService.createOrderID()
  536. rank_id = store_qs[0]['id']
  537. currency = store_qs[0]['currency']
  538. price = store_qs[0]['price']
  539. is_ai = store_qs[0]['is_ai']
  540. content = store_qs[0]['lang__content']
  541. commodity_code = store_qs[0]['commodity_code']
  542. commodity_type = store_qs[0]['commodity_type']
  543. lang = store_qs[0]['lang__lang']
  544. order_type = 1 if is_ai else 0
  545. now_time = int(time.time())
  546. store_meal_qs = Store_Meal.objects.filter(id=rank_id, lang__lang='cn', is_show=0). \
  547. values('lang__title', 'lang__content')
  548. if store_meal_qs.exists():
  549. store_meal_name = store_meal_qs[0]['lang__title'] + '-' + store_meal_qs[0]['lang__content']
  550. else:
  551. store_meal_name = '未知套餐'
  552. order = Order_Model.objects.create(
  553. orderID=order_id, UID=uid, channel=channel, userID_id=user_id, desc=content,
  554. payType=pay_type, payTime=now_time, price=price, currency=currency, addTime=now_time,
  555. updTime=now_time, order_type=order_type, commodity_code=commodity_code,
  556. commodity_type=commodity_type, rank_id=rank_id, ai_rank_id=1, status=1,
  557. store_meal_name=store_meal_name, app_type=app_type
  558. )
  559. # 充值云存套餐
  560. uid_bucket_id = cls.enable_cloud(channel, now_time, order_id, store_qs, uid)
  561. # 修改订单信息
  562. order.uid_bucket_id = uid_bucket_id
  563. order.transaction_id = transaction_id
  564. order.original_transaction_id = original_transaction_id
  565. order.save()
  566. DeviceApplePackage.objects.filter(userID=user_id, uid=uid).update(subscription_status=1,
  567. update_time=int(time.time()))
  568. # 构建云存套餐消息
  569. sys_msg_text_list = cls.cloud_storage_message(uid)
  570. cls.do_vod_msg_notice(uid, user_id, lang, sys_msg_text_list)
  571. return HttpResponse(status=200)
  572. elif str(decoded_payload.rawNotificationType) == "EXPIRED":
  573. # 一种通知类型,与其子类型一起表示订阅已过期。如果subtype为
  574. # VOLUNTARY(自愿),则表示订阅在用户禁用订阅续订后过期。如果subtype是
  575. # BILLING_RETRY(计费重试),则表示订阅过期,因为计费重试期结束时没有成功的计费交易。如果subtype为
  576. # PRICE_INCREASE,则表示订阅已过期,因为客户不同意需要客户同意的价格上涨。如果subtype为
  577. # PRODUCT_NOT_FOR_SALE,则表示订阅已过期,因为在订阅尝试续订时,产品已不可购买。
  578. # 没有子类型的通知表示订阅因其他原因过期。
  579. decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
  580. decoded_payload.data.signedTransactionInfo)
  581. # originalTransactionId 原始购买的交易标识符
  582. original_transaction_id = decoded_transaction_information.originalTransactionId
  583. if not original_transaction_id:
  584. logger.info(f"App Store服务器通知 originalTransactionId原始购买的交易标识符为空, 返回状态 400")
  585. return HttpResponse(status=400)
  586. device_apple_package_qs = DeviceApplePackage.objects.filter(
  587. original_transaction_id=original_transaction_id)
  588. if not device_apple_package_qs.exists():
  589. return HttpResponse(status=400)
  590. device_apple_package_qs.update(subscription_status=2, update_time=int(time.time()))
  591. elif str(decoded_payload.rawNotificationType) == "CONSUMPTION_REQUEST":
  592. # 一种通知类型,指示客户发起了消费型 App 内购买项目或自动续期订阅的退款请求,并且 App Store 要求您提供消费数据。有关详细信息,请参阅发送消耗信息。
  593. decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
  594. decoded_payload.data.signedTransactionInfo)
  595. transaction_id = decoded_transaction_information.transactionId
  596. app_account_token = decoded_transaction_information.appAccountToken
  597. if not app_account_token:
  598. app_account_token = ""
  599. orders_qs = Order_Model.objects.filter(transaction_id=transaction_id)
  600. if not orders_qs.exists():
  601. return HttpResponse(status=400)
  602. orderID = orders_qs[0].orderID
  603. uid = orders_qs[0].UID
  604. now_time = int(time.time())
  605. put_time = now_time + 11.5 * 60 * 60
  606. in_app_refund_qs = InAppRefund.objects.filter(transaction_id=transaction_id)
  607. if in_app_refund_qs.exists():
  608. in_app_refund_qs.update(refund_progress=0, updated_time=now_time,
  609. put_time=put_time, app_account_token=app_account_token)
  610. InAppRefund.objects.create(transaction_id=transaction_id, orderID=orderID,
  611. uid=uid, app_type=app_type, created_time=now_time,
  612. updated_time=now_time, put_time=put_time,
  613. app_account_token=app_account_token)
  614. return HttpResponse(status=200)
  615. elif str(decoded_payload.rawNotificationType) == "DID_CHANGE_RENEWAL_STATUS":
  616. decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
  617. decoded_payload.data.signedTransactionInfo)
  618. original_transaction_id = decoded_transaction_information.originalTransactionId
  619. if not original_transaction_id:
  620. logger.info(f"App Store服务器通知 originalTransactionId原始购买的交易标识符为空, 返回状态 400")
  621. return HttpResponse(status=400)
  622. if decoded_payload.rawSubtype == "AUTO_RENEW_ENABLED":
  623. # 自动续订被开启
  624. device_apple_package_qs = DeviceApplePackage.objects.filter(
  625. original_transaction_id=original_transaction_id)
  626. if not device_apple_package_qs.exists():
  627. return HttpResponse(status=400)
  628. device_apple_package_qs.update(subscription_status=1, update_time=int(time.time()))
  629. else:
  630. # 自动续订被禁用
  631. device_apple_package_qs = DeviceApplePackage.objects.filter(
  632. original_transaction_id=original_transaction_id)
  633. if not device_apple_package_qs.exists():
  634. return HttpResponse(status=400)
  635. device_apple_package_qs.update(subscription_status=3, update_time=int(time.time()))
  636. elif str(decoded_payload.rawNotificationType) == "REFUND":
  637. # 一种通知类型,表示 App Store 成功退还了消耗性应用内购买、非消耗性应用内购买、自动续订或不可续订的交易。
  638. # revocationDate 包含退款交易的时间戳。originalTransactionId 和 productId 用于标识原始交易和产品。revocationReason 包含原因。
  639. # 要请求客户所有退款交易的列表,请参阅 App Store 服务器 API 中的获取退款历史记录。
  640. decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
  641. decoded_payload.data.signedTransactionInfo)
  642. transaction_id = decoded_transaction_information.transactionId
  643. logger.info('App Store服务器通知退款, transaction_id:{}'.format(transaction_id))
  644. orders_qs = Order_Model.objects.filter(transaction_id=transaction_id)
  645. if not orders_qs.exists():
  646. return HttpResponse(status=400)
  647. orders_qs.update(status=5, updTime=int(time.time()))
  648. orderID = orders_qs[0].orderID
  649. uid = orders_qs[0].UID
  650. uid_bucket_qs = UID_Bucket.objects.filter(uid=uid, orderId=orderID, use_status=1,
  651. endTime__gt=int(time.time()))
  652. unused_uid_meal_qs = Unused_Uid_Meal.objects.filter(order_id=orderID)
  653. ai_service_qs = AiService.objects.filter(uid=uid, orders=orderID, use_status=1,
  654. endTime__gt=int(time.time()))
  655. if unused_uid_meal_qs.exists():
  656. unused_uid_meal_qs.delete()
  657. if uid_bucket_qs.exists():
  658. uid_bucket_qs.update(status=0, use_status=2, endTime=int(time.time()),
  659. updateTime=int(time.time()))
  660. if ai_service_qs.exists():
  661. ai_service_qs.update(detect_status=0, use_status=2, endTime=int(time.time()),
  662. updTime=int(time.time()))
  663. # 关闭ai
  664. msg = {'commandType': 'AIDisable'}
  665. thing_name = CommonService.query_serial_with_uid(uid) # 存在序列号则为使用序列号作为物品名
  666. topic_name = 'ansjer/generic/{}'.format(thing_name)
  667. req_success = CommonService.req_publish_mqtt_msg(thing_name, topic_name, msg)
  668. logger.info(f'App Store服务器通知用户退款, 关闭AI:{req_success}')
  669. InAppRefund.objects.filter(transaction_id=transaction_id).update(updated_time=int(time.time()),
  670. refund_progress=2)
  671. return HttpResponse(status=200)
  672. elif str(decoded_payload.rawNotificationType) == "REFUND_REVERSED":
  673. # 一种通知类型,表示 App Store 由于客户提出的争议而撤销了先前批准的退款。如果您的应用程序因相关退款而撤销了内容或服务,则需要恢复这些内容或服务。
  674. # 此通知类型可适用于任何应用程序内购买类型:消耗品、非消耗品、不可续订订阅和自动续订订阅。对于自动续订,当 App Store 撤销退款时,续订日期保持不变。
  675. decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
  676. decoded_payload.data.signedTransactionInfo)
  677. transaction_id = decoded_transaction_information.transactionId
  678. logger.info(
  679. 'App Store服务器通知,撤销了批准的退款,不恢复套餐,手动处理 transaction_id:{}'.format(transaction_id))
  680. return HttpResponse(status=200)
  681. elif str(decoded_payload.rawNotificationType) == "REFUND_DECLINED":
  682. decoded_transaction_information = signed_data_verifier.verify_and_decode_signed_transaction(
  683. decoded_payload.data.signedTransactionInfo)
  684. transaction_id = decoded_transaction_information.transactionId
  685. orders_qs = Order_Model.objects.filter(transaction_id=transaction_id)
  686. if not orders_qs.exists():
  687. return HttpResponse(status=400)
  688. logger.info(
  689. 'App Store服务器通知, 不批准退款, transaction_id:{}'.format(transaction_id))
  690. InAppRefund.objects.filter(transaction_id=transaction_id).update(refund_progress=3)
  691. else:
  692. logger.info(f"App Store服务器通知decoded_payload.rawNotificationType 未处理")
  693. return HttpResponse(status=200)
  694. except Exception as e:
  695. logger.info('App Store服务器通知异常:{}'.
  696. format('error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))))
  697. return HttpResponse(status=500)
  698. @classmethod
  699. def app_store_server_notifications_vsees(cls, request, request_dict):
  700. logger = logging.getLogger('apple_pay')
  701. if request.method != 'POST':
  702. logger.info(f'App Store服务器通知不是post请求, 参数{request_dict}')
  703. return HttpResponse(status=400)
  704. try:
  705. request_data = json.loads(request.body)
  706. except json.JSONDecodeError:
  707. logger.error('无法解析请求体为JSON')
  708. return HttpResponse(status=400)
  709. request_data['bundleId'] = 'com.ansjer.zccloud'
  710. updated_request_body = json.dumps(request_data)
  711. if CONFIG_INFO == CONFIG_TEST:
  712. logger.info('测试环境, App Store服务器通知发送到测试服')
  713. response_test = requests.post(url="https://test.zositechc.cn/inAppPurchase/serverNotifications",
  714. json=updated_request_body)
  715. return HttpResponse(status=response_test.status_code)
  716. response_us = requests.post(url="https://www.dvema.com/inAppPurchase/serverNotifications",
  717. json=updated_request_body)
  718. status_code = response_us.status_code
  719. if status_code != 200:
  720. response_eu = requests.post(url="https://api.zositeche.com/inAppPurchase/serverNotifications",
  721. json=updated_request_body)
  722. status_code = response_eu.status_code
  723. if status_code == 200:
  724. return HttpResponse(status=200)
  725. return HttpResponse(status=400)
  726. @classmethod
  727. def vsees_notifications(cls, request):
  728. logger = logging.getLogger('apple_pay')
  729. logger.info('Vsees: App Store服务器通知请求类型:{}'.format(request.method))
  730. logger.info('Vsees: App Store服务器通知参数:{}'.format(request.POST))
  731. logger.info('Vsees: App Store服务器通知请求body:{}'.format(request.body))
  732. payload = json.loads(request.body.decode('utf-8'))
  733. logger.info('Vsees: App Store服务器通知payload:{}'.format(payload))
  734. # 获取 signedPayload
  735. signed_payload = payload.get('signedPayload')
  736. if not signed_payload:
  737. return HttpResponse(status=400)
  738. in_app_purchase = InAppPurchase(bundle_id="com.cloudlife.commissionf")
  739. # SignedDataVerifier 用于解析查询到的交易信息
  740. verifier = in_app_purchase.verifier
  741. decoded_payload = verifier.verify_and_decode_notification(signed_payload)
  742. logger.info('Vsees: App Store服务器通知decoded_payload: {}'.format(decoded_payload))
  743. status_code = 200
  744. if str(decoded_payload.rawNotificationType) == "REFUND":
  745. # 一种通知类型,表示 App Store 成功退还了消耗性应用内购买、非消耗性应用内购买、自动续订或不可续订的交易。
  746. # revocationDate 包含退款交易的时间戳。originalTransactionId 和 productId 用于标识原始交易和产品。revocationReason 包含原因。
  747. # 要请求客户所有退款交易的列表,请参阅 App Store 服务器 API 中的获取退款历史记录。
  748. # 1. 找套餐 使用 transaction_id 找orders
  749. decoded_transaction_information = verifier.verify_and_decode_signed_transaction(
  750. decoded_payload.data.signedTransactionInfo)
  751. transaction_id = decoded_transaction_information.transactionId
  752. logger.info('Vsees: App Store服务器通知退款, transaction_id:{}'.format(transaction_id))
  753. orders_qs = Order_Model.objects.filter(transaction_id=transaction_id)
  754. # 2. 查找云存套餐使用表 和 云存套餐
  755. if orders_qs.exists():
  756. orders_qs.update(status=11)
  757. orderID = orders_qs[0].orderID
  758. uid = orders_qs[0].UID
  759. user_id = orders_qs[0].userID
  760. # 3. 未使用则删除未使用套餐表,已使用过则删除设备正在使用套餐,并关闭设备云存
  761. uid_bucket_qs = UID_Bucket.objects.filter(uid=uid, orderId=orderID, use_status=1,
  762. endTime__gt=int(time.time()))
  763. unused_uid_meal_qs = Unused_Uid_Meal.objects.filter(order_id=orderID)
  764. ai_service_qs = AiService.objects.filter(uid=uid, orderId=orderID, use_status=1,
  765. endTime__gt=int(time.time()))
  766. if unused_uid_meal_qs.exists():
  767. unused_uid_meal_qs.delete()
  768. if uid_bucket_qs.exists():
  769. uid_bucket_qs.update(status=0, use_status=2, endTime=int(time.time()), updateTime=int(time.time()))
  770. if ai_service_qs.exists():
  771. ai_service_qs.update(detect_status=0, use_status=2, endTime=int(time.time()),
  772. updTime=int(time.time()))
  773. # 关闭ai
  774. msg = {'commandType': 'AIDisable'}
  775. thing_name = CommonService.query_serial_with_uid(uid) # 存在序列号则为使用序列号作为物品名
  776. topic_name = 'ansjer/generic/{}'.format(thing_name)
  777. req_success = CommonService.req_publish_mqtt_msg(thing_name, topic_name, msg)
  778. LOGGER.info(f'App Store服务器通知用户退款, 关闭AI:{req_success}')
  779. # 4.发送邮件告知用户退款
  780. email_content = f'{CONFIG_INFO}用户{user_id}, 订单:{orderID}, 设备{uid}退款'
  781. S3Email().faEmail(email_content, 'servers@ansjer.com')
  782. else:
  783. if CONFIG_INFO == CONFIG_US:
  784. url = "https://api.zositeche.com/inAppPurchase/AppStoreServerNotifications"
  785. eur_response = requests.post(url=url, json=json.loads(request.body))
  786. status_code = eur_response.status_code
  787. return HttpResponse(status=status_code)
  788. @staticmethod
  789. def put_refund_order():
  790. put_time = int(time.time())
  791. in_app_refund_qs = InAppRefund.objects.filter(refund_progress=0, put_time__lt=put_time)
  792. for in_app_refund in in_app_refund_qs:
  793. transaction_id = in_app_refund.transaction_id
  794. app_type = in_app_refund.app_type
  795. if app_type == 1:
  796. bundle_id = "com.ansjer.zccloud"
  797. elif app_type == 2:
  798. bundle_id = "com.cloudlife.commissionf"
  799. else:
  800. return HttpResponse(status=200)
  801. in_app_purchase_obj = InAppPurchase(bundle_id=bundle_id)
  802. # AppStoreServerAPIClient 用于查询交易信息
  803. client = in_app_purchase_obj.client
  804. orderID = in_app_refund.orderID
  805. app_account_token = in_app_refund.app_account_token
  806. unused_uid_meal_qs = Unused_Uid_Meal.objects.filter(order_id=orderID)
  807. uid_bucket_qs = UID_Bucket.objects.filter(orderId=orderID, endTime__gt=int(time.time()))
  808. if unused_uid_meal_qs.exists():
  809. consumptionStatus = ConsumptionStatus.NOT_CONSUMED
  810. deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
  811. elif uid_bucket_qs.exists():
  812. consumptionStatus = ConsumptionStatus.PARTIALLY_CONSUMED
  813. deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
  814. elif UID_Bucket.objects.filter(orderId=orderID, endTime__lt=int(time.time())):
  815. consumptionStatus = ConsumptionStatus.FULLY_CONSUMED
  816. deliveryStatus = DeliveryStatus.DELIVERED_AND_WORKING_PROPERLY
  817. else:
  818. consumptionStatus = ConsumptionStatus.UNDECLARED
  819. deliveryStatus = DeliveryStatus.DID_NOT_DELIVER_FOR_OTHER_REASON
  820. if in_app_refund.refund_preference == 1:
  821. refundPreference = RefundPreference.PREFER_GRANT
  822. else:
  823. refundPreference = RefundPreference.PREFER_DECLINE
  824. consumption_request = ConsumptionRequest(
  825. customerConsented=True,
  826. consumptionStatus=consumptionStatus,
  827. platform=Platform.UNDECLARED,
  828. sampleContentProvided=True,
  829. deliveryStatus=deliveryStatus,
  830. appAccountToken=app_account_token,
  831. accountTenure=AccountTenure.UNDECLARED,
  832. playTime=PlayTime.UNDECLARED,
  833. lifetimeDollarsRefunded=LifetimeDollarsRefunded.UNDECLARED,
  834. lifetimeDollarsPurchased=LifetimeDollarsPurchased.UNDECLARED,
  835. userStatus=UserStatus.ACTIVE,
  836. refundPreference=refundPreference,
  837. )
  838. client.send_consumption_data(transaction_id, consumption_request)
  839. logger.info(f'内购退款消费数据提交, 订单orderID:{orderID}, transaction_id:{transaction_id}')
  840. in_app_refund.refund_progress = 1
  841. in_app_refund.updated_time = int(time.time())
  842. in_app_refund.put_time = int(time.time())
  843. in_app_refund.save()
  844. return HttpResponse(status=200)