PaymentCycle.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. from Ansjer.config import PAYPAL_CRD,SERVER_DOMAIN,SERVER_DOMAIN_SSL
  2. from Model.models import PayCycleConfigModel,Order_Model, Store_Meal, UID_Bucket, PromotionRuleModel, Unused_Uid_Meal,Device_Info, CouponModel
  3. from Service.CommonService import CommonService
  4. from django.http import JsonResponse, HttpResponseRedirect, HttpResponse
  5. import requests
  6. import time
  7. from Object.ResponseObject import ResponseObject
  8. import paypalrestsdk
  9. from paypalrestsdk import BillingAgreement
  10. from django.views.generic.base import View
  11. from django.db import transaction
  12. from Controller import CloudStorage
  13. from django.db.models import Q, F, Count
  14. from paypalrestsdk.notifications import WebhookEvent
  15. import logging
  16. import json
  17. #周期扣款相关
  18. class Paypal:
  19. def subscriptions(store_info,lang,orderID):
  20. cycle_config = PayCycleConfigModel.objects.filter(id=store_info['cycle_config_id']).values()
  21. if not cycle_config:
  22. return False
  23. cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  24. if lang != 'cn':
  25. cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  26. return_url = "{SERVER_DOMAIN_SSL}payCycle/paypalCycleReturn?lang={lang}". \
  27. format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, lang=lang)
  28. # call_sub_url = "http://binbin.uicp.vip/cloudstorage/dopaypalcallback?orderID={orderID}".format(
  29. # SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, orderID=orderID)
  30. BillingPlan = {
  31. "description": orderID,
  32. "merchant_preferences": {
  33. "auto_bill_amount": "YES",
  34. "cancel_url": cal_url, # 取消协议url
  35. "initial_fail_amount_action": "CANCEL",
  36. "max_fail_attempts": "1", # 允许的最大失败付款尝试次数
  37. "return_url": return_url, # 客户批准协议的url
  38. # "notify_url": "http://www.notify.com", #通知客户协议已创建的 URL。只读并保留供将来使用。
  39. "setup_fee": {
  40. "currency": store_info['currency'],
  41. "value": store_info['price'],
  42. }
  43. },
  44. "name": store_info['lang__content'],
  45. "payment_definitions": [
  46. {
  47. "amount": {
  48. "currency": store_info['currency'],
  49. "value": store_info['price']
  50. },
  51. # "charge_models": [
  52. # {
  53. # "amount": {
  54. # "currency": "USD",
  55. # "value": "20"
  56. # },
  57. # "type": "TAX" #税金
  58. # }
  59. # ],
  60. "cycles": cycle_config[0]['cycles'],
  61. "frequency": cycle_config[0]['frequency'],
  62. "frequency_interval": cycle_config[0]['frequencyInterval'],
  63. "name": store_info['lang__title'],
  64. "type": "REGULAR"
  65. },
  66. ],
  67. "type": "INFINITE",
  68. }
  69. paypalrestsdk.configure(PAYPAL_CRD)
  70. billing_plan = paypalrestsdk.BillingPlan(BillingPlan)
  71. if billing_plan.create():
  72. billing_plan.activate() # 激活
  73. plan_id = billing_plan.id
  74. else:
  75. print(billing_plan.error)
  76. return False
  77. now_time = int(time.time())
  78. if cycle_config[0]['frequency'] == "DAY":
  79. start_date_timestamp = now_time + 86400 - 3600 # 下次扣款为明天,提前1个小时扣款
  80. start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ")
  81. elif cycle_config[0]['frequency'] == "MONTH":
  82. start_date_timestamp = CommonService.calcMonthLater(1, now_time) - (5 * 86400) #下次扣款为下个月提前5天扣款
  83. start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ")
  84. #订阅
  85. billingAgreement = {
  86. "name": store_info['lang__content'],
  87. "description": orderID,
  88. "start_date": start_date_str,
  89. "plan": {
  90. "id": plan_id
  91. },
  92. "payer": {
  93. "payment_method": "paypal"
  94. },
  95. }
  96. billing_agreement = paypalrestsdk.BillingAgreement(billingAgreement)
  97. # print(billing_agreement.create())
  98. if billing_agreement.create():
  99. for link in billing_agreement.links:
  100. if link.rel == "approval_url":
  101. return {"plan_id": plan_id, "url": link.href}
  102. else:
  103. print(billing_agreement.error)
  104. return False
  105. class PaypalCycleNotify(View):
  106. def get(self, request, *args, **kwargs):
  107. request.encoding = 'utf-8'
  108. operation = kwargs.get('operation')
  109. return self.validation(request.GET, request, operation)
  110. def post(self, request, *args, **kwargs):
  111. request.encoding = 'utf-8'
  112. operation = kwargs.get('operation')
  113. return self.validation(request.POST, request, operation)
  114. def validation(self, request_dict, request, operation):
  115. response = ResponseObject()
  116. if operation is None:
  117. return response.json(444, 'error path')
  118. elif operation == 'paypalCycleReturn': # paypal成功订阅回调
  119. return self.do_paypal_cycle_return(request_dict, response)
  120. elif operation == 'paypalCycleNotify': # paypal 周期付款回调
  121. return self.do_paypal_webhook_notify(request_dict,request, response)
  122. elif operation == 'test': # paypal 周期付款回调
  123. return self.do_test(request_dict,request, response)
  124. def do_paypal_cycle_return(self, request_dict, response):
  125. lang = request_dict.get('lang', 'en')
  126. token = request_dict.get('token',None)
  127. paypalrestsdk.configure(PAYPAL_CRD)
  128. billing_agreement = paypalrestsdk.BillingAgreement()
  129. billing_agreement_response = billing_agreement.execute(token)
  130. if billing_agreement_response.error:
  131. print(billing_agreement_response.error)
  132. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  133. if lang != 'cn':
  134. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  135. return HttpResponseRedirect(red_url)
  136. orderID = billing_agreement_response.description
  137. agreement_id = billing_agreement_response.id
  138. promotion_rule_id = ''
  139. try:
  140. order_qs = Order_Model.objects.filter(orderID=orderID, status=0)
  141. if not orderID:
  142. print("not orderID")
  143. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  144. if lang != 'cn':
  145. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  146. return HttpResponseRedirect(red_url)
  147. order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
  148. "userID__userID",
  149. "userID__username",'coupon_id')
  150. userid = order_list[0]['userID__userID']
  151. username = order_list[0]['userID__username']
  152. UID = order_list[0]['UID']
  153. channel = order_list[0]['channel']
  154. rank = order_list[0]['rank']
  155. smqs = Store_Meal.objects.filter(id=rank). \
  156. values("day", "bucket_id", "bucket__storeDay", "expire")
  157. bucketId = smqs[0]['bucket_id']
  158. if not smqs.exists():
  159. print("not smqs")
  160. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  161. if lang != 'cn':
  162. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  163. return HttpResponseRedirect(red_url)
  164. # ##
  165. ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay", "bucket__region",
  166. "endTime", "use_status")
  167. expire = smqs[0]['expire']
  168. if order_list[0]['isSelectDiscounts'] == 1:
  169. expire = smqs[0]['expire'] * 2
  170. # 是否有促销
  171. nowTime = int(time.time())
  172. promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
  173. endTime__gte=nowTime).values('id','ruleConfig')
  174. if promotion.exists():
  175. promotion_rule_id = promotion[0]['id']
  176. expire = expire * 2
  177. with transaction.atomic():
  178. if ubqs.exists():
  179. ubq = ubqs[0]
  180. if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: #套餐使用中并且相同套餐叠加过期时间
  181. endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
  182. UID_Bucket.objects.filter(id=ubq['id']).update \
  183. (uid=UID, channel=channel, bucket_id=bucketId,
  184. endTime=endTime, updateTime=nowTime)
  185. else: #已过期或者不相同的套餐加入未使用的关联套餐表
  186. has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
  187. nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
  188. if promotion.exists():
  189. nums = nums + 1
  190. if has_unused.exists():
  191. Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
  192. else:
  193. Unused_Uid_Meal.objects.create(uid=UID,channel=channel,addTime=nowTime,num=nums,
  194. expire=smqs[0]['expire'],bucket_id=bucketId)
  195. UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
  196. uid_bucket_id = ubq['id']
  197. else:
  198. endTime = CommonService.calcMonthLater(expire)
  199. ub_cqs = UID_Bucket.objects.create \
  200. (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
  201. updateTime=nowTime,use_status=1)
  202. uid_bucket_id = ub_cqs.id
  203. dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
  204. if dvq.exists():
  205. dvq_set_update_dict = {
  206. 'vodPrimaryUserID': userid,
  207. 'vodPrimaryMaster': username
  208. }
  209. dvq.update(**dvq_set_update_dict)
  210. # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
  211. # if not uid_main_exist.exists():
  212. # uid_main_dict = {
  213. # 'UID': UID,
  214. # 'user_id': userid
  215. # }
  216. # UIDMainUser.objects.create(**uid_main_dict)
  217. # 核销coupon
  218. if order_list[0]['coupon_id']:
  219. CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=1)
  220. order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,
  221. promotion_rule_id=promotion_rule_id,agreement_id=agreement_id)
  222. datetime = time.strftime("%Y-%m-%d", time.localtime())
  223. sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + UID + '设备在' + datetime + '已成功订阅云存套餐',
  224. 'Dear customer,you already subscribed the cloud storage package successfully for device ' + UID + ' on ' + time.strftime(
  225. "%b %dth,%Y", time.localtime())]
  226. CloudStorage.CloudStorageView.do_vod_msg_Notice(self, UID, channel, userid, lang, sys_msg_text_list, 'SMS_219738485')
  227. # return response.json(0)
  228. red_url = "{SERVER_DOMAIN_SSL}web/paid2/success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  229. if lang != 'cn':
  230. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  231. return HttpResponseRedirect(red_url)
  232. except Exception as e:
  233. print(repr(e))
  234. if order_qs:
  235. order_qs.update(status=10, promotion_rule_id=promotion_rule_id)
  236. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  237. if lang != 'cn':
  238. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  239. return HttpResponseRedirect(red_url)
  240. def do_paypal_webhook_notify(self, request_dict, request, response):
  241. logger = logging.getLogger('info')
  242. json_str = request.body.decode("utf-8")
  243. logger.info('json_str----------------')
  244. logger.info(json_str)
  245. header = request.META
  246. logger.info('json_str----------------')
  247. logger.info(header)
  248. # json_str = '{"id":"WH-9K090101YP819052B-3LD205733F387905X","event_version":"1.0","create_time":"2022-01-05T08:17:36.937Z","resource_type":"plan","event_type":"BILLING.PLAN.CREATED","summary":"A billing plan was created","resource":{"merchant_preferences":{"setup_fee":{"currency":"USD","value":"0.02"},"return_url":"http://127.0.0.1:8000/payCycle/paypalCycleReturn?lang=en","cancel_url":"http://127.0.0.1:8000/web/paid2/en_fail.html","auto_bill_amount":"YES","initial_fail_amount_action":"CANCEL","max_fail_attempts":"1"},"update_time":"2022-01-05T08:17:15.829Z","create_time":"2022-01-05T08:17:15.129Z","name":"One year package","description":"20220105161712830917","links":[{"href":"https://api.sandbox.paypal.com/v1/payments/billing-plans/P-4RE38399B6473962EFFIEW6I","rel":"self","method":"GET"}],"payment_definitions":[{"id":"PD-8DC78035HJ0448121FFIEW6I","name":"Video save for 7 day","type":"REGULAR","frequency":"Day","amount":{"currency":"USD","value":"0.02"},"cycles":"0","frequency_interval":"1"}],"id":"P-4RE38399B6473962EFFIEW6I","state":"ACTIVE","type":"INFINITE"},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-9K090101YP819052B-3LD205733F387905X","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-9K090101YP819052B-3LD205733F387905X/resend","rel":"resend","method":"POST"}]}'
  249. json_obj = json.loads(json_str)
  250. transmission_id = header.get('paypal-transmission-id',None)
  251. transmission_time = header.get('paypal-transmission-id',None)
  252. webhook_id = '9RW011891A1707801'
  253. cert_url = header.get('paypal-cert-url',None)
  254. transmission_sig = header.get('paypal-transmission-sig',None)
  255. auth_algo = header.get('paypal-auth-algo',None)
  256. resource_type = json_obj.get('resource_type')
  257. json_str = '{"id":"WH-22P96595RY482870W-2B701244WV459014K","event_version":"1.0","create_time":"2022-01-06T05:56:33.126Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 0.02 USD","resource":{"billing_agreement_id":"I-CR65R6YXS3VA","amount":{"total":"0.02","currency":"USD","details":{"subtotal":"0.02"}},"payment_mode":"INSTANT_TRANSFER","update_time":"2022-01-06T05:56:18Z","create_time":"2022-01-06T05:56:18Z","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","transaction_fee":{"currency":"USD","value":"0.02"},"protection_eligibility":"ELIGIBLE","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v1/payments/sale/2FE30648Y2273061H"},{"method":"POST","rel":"refund","href":"https://api.sandbox.paypal.com/v1/payments/sale/2FE30648Y2273061H/refund"}],"id":"2FE30648Y2273061H","state":"completed","invoice_number":""},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-22P96595RY482870W-2B701244WV459014K","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-22P96595RY482870W-2B701244WV459014K/resend","rel":"resend","method":"POST"}]}'
  258. header = "{'PATH_INFO': '/payCycle/paypalCycleNotify', 'wsgi.file_wrapper': <class 'gunicorn.http.wsgi.FileWrapper'>, 'CONTENT_LENGTH': '1226', 'wsgi.multiprocess': True, 'HTTP_PAYPAL_TRANSMISSION_TIME': '2022-01-06T05:56:36Z', 'wsgi.version': (1, 0), 'SERVER_NAME': '0.0.0.0', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.0', 'REMOTE_PORT': '42322', 'HTTP_HTTP_X_FORWARDED_FOR': '173.0.80.116', 'RAW_URI': '/payCycle/paypalCycleNotify', 'HTTP_X_REAL_IP': '173.0.80.116', 'HTTP_X_B3_SPANID': 'f37d2832010b10f2', 'REMOTE_ADDR': '127.0.0.1', 'wsgi.input': <gunicorn.http.body.Body object at 0x7ff31a5b92b0>, 'HTTP_PAYPAL_TRANSMISSION_SIG': 'JI+rFKqe9YqufExp/aJxkyRWG3II+4t6CSCWLHqNrjcd/FP729G0s8lFMPixnGpUemj4+WsJTbu29G4ZR3zxl+CwrlKhPQxe0fE0mUw+/AaBa6THpnoXEVwz9spI/kbH3dNmSXePfW0D5+HyVmWgkac23icxi4G92ZmmszPBzBdcNIc3BumXGSfJwX19cchta0VY7GnD5ePvW1FstPp9eC7NyhDH11xDpvM2qZZqhxH6hahIywVNA0gyNydkYbkbjnD+hGC+HrKKMx0Tpw8eGxUHReWJIEsiw7YCPLBwZIab5PV/+L4A/LscK/JicOeDealP+SKPJICZvinLHDzK0Q==', 'HTTP_PAYPAL_TRANSMISSION_ID': '688538e0-6eb5-11ec-a473-05e6d85b61e7', 'gunicorn.socket': <socket.socket fd=24, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8082), raddr=('127.0.0.1', 42322)>, 'HTTP_PAYPAL_AUTH_ALGO': 'SHA256withRSA', 'HTTP_PAYPAL_AUTH_VERSION': 'v2', 'wsgi.url_scheme': 'http', 'wsgi.errors': <gunicorn.http.wsgi.WSGIErrorsWrapper object at 0x7ff31a5b9a58>, 'QUERY_STRING': '', 'wsgi.multithread': False, 'HTTP_CORRELATION_ID': 'b696eaa58ea49', 'HTTP_ACCEPT': '*/*', 'HTTP_USER_AGENT': 'PayPal/AUHD-214.0-56138150', 'CONTENT_TYPE': 'application/json', 'HTTP_PAYPAL_CERT_URL': 'https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-7a8abba8', 'HTTP_CONNECTION': 'close', 'SERVER_PORT': '8082', 'HTTP_HOST': 'test.zositechc.cn:443', 'HTTP_X_FORWARDED_FOR': '173.0.80.116', 'SERVER_SOFTWARE': 'gunicorn/19.7.1', 'wsgi.run_once': False, 'REQUEST_METHOD': 'POST'}"
  259. if resource_type == 'plan':
  260. paypalrestsdk.configure(PAYPAL_CRD)
  261. response = paypalrestsdk.WebhookEvent.verify(
  262. transmission_id, transmission_time, webhook_id, json_str, cert_url, transmission_sig, auth_algo)
  263. logger.info('response----------------')
  264. logger.info(response)
  265. return HttpResponse(json_obj.get('id'))
  266. def do_test(self, request_dict, request, response):
  267. json_str = '{"id":"WH-22P96595RY482870W-2B701244WV459014K","event_version":"1.0","create_time":"2022-01-06T05:56:33.126Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 0.02 USD","resource":{"billing_agreement_id":"I-CR65R6YXS3VA","amount":{"total":"0.02","currency":"USD","details":{"subtotal":"0.02"}},"payment_mode":"INSTANT_TRANSFER","update_time":"2022-01-06T05:56:18Z","create_time":"2022-01-06T05:56:18Z","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","transaction_fee":{"currency":"USD","value":"0.02"},"protection_eligibility":"ELIGIBLE","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v1/payments/sale/2FE30648Y2273061H"},{"method":"POST","rel":"refund","href":"https://api.sandbox.paypal.com/v1/payments/sale/2FE30648Y2273061H/refund"}],"id":"2FE30648Y2273061H","state":"completed","invoice_number":""},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-22P96595RY482870W-2B701244WV459014K","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-22P96595RY482870W-2B701244WV459014K/resend","rel":"resend","method":"POST"}]}'
  268. header = "{'PATH_INFO': '/payCycle/paypalCycleNotify', 'wsgi.file_wrapper': <class 'gunicorn.http.wsgi.FileWrapper'>, 'CONTENT_LENGTH': '1226', 'wsgi.multiprocess': True, 'HTTP_PAYPAL_TRANSMISSION_TIME': '2022-01-06T05:56:36Z', 'wsgi.version': (1, 0), 'SERVER_NAME': '0.0.0.0', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.0', 'REMOTE_PORT': '42322', 'HTTP_HTTP_X_FORWARDED_FOR': '173.0.80.116', 'RAW_URI': '/payCycle/paypalCycleNotify', 'HTTP_X_REAL_IP': '173.0.80.116', 'HTTP_X_B3_SPANID': 'f37d2832010b10f2', 'REMOTE_ADDR': '127.0.0.1', 'wsgi.input': <gunicorn.http.body.Body object at 0x7ff31a5b92b0>, 'HTTP_PAYPAL_TRANSMISSION_SIG': 'JI+rFKqe9YqufExp/aJxkyRWG3II+4t6CSCWLHqNrjcd/FP729G0s8lFMPixnGpUemj4+WsJTbu29G4ZR3zxl+CwrlKhPQxe0fE0mUw+/AaBa6THpnoXEVwz9spI/kbH3dNmSXePfW0D5+HyVmWgkac23icxi4G92ZmmszPBzBdcNIc3BumXGSfJwX19cchta0VY7GnD5ePvW1FstPp9eC7NyhDH11xDpvM2qZZqhxH6hahIywVNA0gyNydkYbkbjnD+hGC+HrKKMx0Tpw8eGxUHReWJIEsiw7YCPLBwZIab5PV/+L4A/LscK/JicOeDealP+SKPJICZvinLHDzK0Q==', 'HTTP_PAYPAL_TRANSMISSION_ID': '688538e0-6eb5-11ec-a473-05e6d85b61e7', 'gunicorn.socket': <socket.socket fd=24, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8082), raddr=('127.0.0.1', 42322)>, 'HTTP_PAYPAL_AUTH_ALGO': 'SHA256withRSA', 'HTTP_PAYPAL_AUTH_VERSION': 'v2', 'wsgi.url_scheme': 'http', 'wsgi.errors': <gunicorn.http.wsgi.WSGIErrorsWrapper object at 0x7ff31a5b9a58>, 'QUERY_STRING': '', 'wsgi.multithread': False, 'HTTP_CORRELATION_ID': 'b696eaa58ea49', 'HTTP_ACCEPT': '*/*', 'HTTP_USER_AGENT': 'PayPal/AUHD-214.0-56138150', 'CONTENT_TYPE': 'application/json', 'HTTP_PAYPAL_CERT_URL': 'https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-7a8abba8', 'HTTP_CONNECTION': 'close', 'SERVER_PORT': '8082', 'HTTP_HOST': 'test.zositechc.cn:443', 'HTTP_X_FORWARDED_FOR': '173.0.80.116', 'SERVER_SOFTWARE': 'gunicorn/19.7.1', 'wsgi.run_once': False, 'REQUEST_METHOD': 'POST'}"
  269. json_obj = json.loads(json_str)
  270. # transmission_id = header.get('HTTP_PAYPAL_TRANSMISSION_ID',None)
  271. # transmission_time = header.get('HTTP_PAYPAL_TRANSMISSION_TIME',None)
  272. # webhook_id = '6TS30758D98835230'
  273. # cert_url = header.get('HTTP_PAYPAL_CERT_URL',None)
  274. # transmission_sig = header.get('HTTP_PAYPAL_TRANSMISSION_SIG',None)
  275. # auth_algo = header.get('HTTP_PAYPAL_AUTH_ALGO',None)
  276. # resource_type = json_obj.get('resource_type')
  277. transmission_id = '688538e0-6eb5-11ec-a473-05e6d85b61e7'
  278. transmission_time = '2022-01-06T05:56:36Z'
  279. webhook_id = '6TS30758D98835230'
  280. cert_url = 'https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-7a8abba8'
  281. transmission_sig = 'JI+rFKqe9YqufExp/aJxkyRWG3II+4t6CSCWLHqNrjcd/FP729G0s8lFMPixnGpUemj4+WsJTbu29G4ZR3zxl+CwrlKhPQxe0fE0mUw+/AaBa6THpnoXEVwz9spI/kbH3dNmSXePfW0D5+HyVmWgkac23icxi4G92ZmmszPBzBdcNIc3BumXGSfJwX19cchta0VY7GnD5ePvW1FstPp9eC7NyhDH11xDpvM2qZZqhxH6hahIywVNA0gyNydkYbkbjnD+hGC+HrKKMx0Tpw8eGxUHReWJIEsiw7YCPLBwZIab5PV/+L4A/LscK/JicOeDealP+SKPJICZvinLHDzK0Q=='
  282. auth_algo = 'SHA256withRSA'
  283. resource_type = json_obj.get('resource_type')
  284. if resource_type == 'sale':
  285. paypalrestsdk.configure(PAYPAL_CRD)
  286. response = paypalrestsdk.WebhookEvent.verify(
  287. transmission_id, transmission_time, webhook_id, json_str, cert_url, transmission_sig, auth_algo)
  288. if response:
  289. try:
  290. agreement_id = json_obj.get('resource')['billing_agreement_id']
  291. order_qs = Order_Model.objects.filter(agreement_id=agreement_id, status=0)
  292. if not order_qs:
  293. return False
  294. order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
  295. "userID__userID",
  296. "userID__username")
  297. return HttpResponse(order_list)
  298. userid = order_list[0]['userID__userID']
  299. username = order_list[0]['userID__username']
  300. UID = order_list[0]['UID']
  301. channel = order_list[0]['channel']
  302. rank = order_list[0]['rank']
  303. smqs = Store_Meal.objects.filter(id=rank). \
  304. values("day", "bucket_id", "bucket__storeDay", "expire")
  305. bucketId = smqs[0]['bucket_id']
  306. if not smqs.exists():
  307. print("not smqs")
  308. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  309. if lang != 'cn':
  310. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(
  311. SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  312. return HttpResponseRedirect(red_url)
  313. # ##
  314. ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay",
  315. "bucket__region",
  316. "endTime", "use_status")
  317. expire = smqs[0]['expire']
  318. if order_list[0]['isSelectDiscounts'] == 1:
  319. expire = smqs[0]['expire'] * 2
  320. # 是否有促销
  321. nowTime = int(time.time())
  322. promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
  323. endTime__gte=nowTime).values('id', 'ruleConfig')
  324. if promotion.exists():
  325. promotion_rule_id = promotion[0]['id']
  326. expire = expire * 2
  327. with transaction.atomic():
  328. if ubqs.exists():
  329. ubq = ubqs[0]
  330. if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: # 套餐使用中并且相同套餐叠加过期时间
  331. endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
  332. UID_Bucket.objects.filter(id=ubq['id']).update \
  333. (uid=UID, channel=channel, bucket_id=bucketId,
  334. endTime=endTime, updateTime=nowTime)
  335. else: # 已过期或者不相同的套餐加入未使用的关联套餐表
  336. has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
  337. nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
  338. if promotion.exists():
  339. nums = nums + 1
  340. if has_unused.exists():
  341. Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
  342. else:
  343. Unused_Uid_Meal.objects.create(uid=UID, channel=channel, addTime=nowTime, num=nums,
  344. expire=smqs[0]['expire'], bucket_id=bucketId)
  345. UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
  346. uid_bucket_id = ubq['id']
  347. else:
  348. endTime = CommonService.calcMonthLater(expire)
  349. ub_cqs = UID_Bucket.objects.create \
  350. (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
  351. updateTime=nowTime, use_status=1)
  352. uid_bucket_id = ub_cqs.id
  353. dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
  354. if dvq.exists():
  355. dvq_set_update_dict = {
  356. 'vodPrimaryUserID': userid,
  357. 'vodPrimaryMaster': username
  358. }
  359. dvq.update(**dvq_set_update_dict)
  360. # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
  361. # if not uid_main_exist.exists():
  362. # uid_main_dict = {
  363. # 'UID': UID,
  364. # 'user_id': userid
  365. # }
  366. # UIDMainUser.objects.create(**uid_main_dict)
  367. order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,
  368. promotion_rule_id=promotion_rule_id, agreement_id=agreement_id)
  369. datetime = time.strftime("%Y-%m-%d", time.localtime())
  370. sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + UID + '设备在' + datetime + '已成功订阅云存套餐',
  371. 'Dear customer,you already subscribed the cloud storage package successfully for device ' + UID + ' on ' + time.strftime(
  372. "%b %dth,%Y", time.localtime())]
  373. CloudStorage.CloudStorageView.do_vod_msg_Notice(self, UID, channel, userid, lang,
  374. sys_msg_text_list, 'SMS_219738485')
  375. return True
  376. except Exception as e:
  377. print(repr(e))
  378. if order_qs:
  379. order_qs.update(status=10, promotion_rule_id=promotion_rule_id)
  380. return False
  381. return False