PaymentCycle.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. from Ansjer.config import PAYPAL_CRD,SERVER_DOMAIN,SERVER_DOMAIN_SSL,PAYPAL_WEB_HOOK_ID
  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. import sys
  8. from Object.ResponseObject import ResponseObject
  9. import paypalrestsdk
  10. from paypalrestsdk import BillingAgreement
  11. from django.views.generic.base import View
  12. from django.db import transaction
  13. from Controller import CloudStorage
  14. from django.db.models import Q, F, Count
  15. from paypalrestsdk.notifications import WebhookEvent
  16. import logging
  17. import json
  18. from paypalrestsdk import BillingPlan
  19. #周期扣款相关
  20. class Paypal:
  21. #检查是否有重复订阅
  22. def checkSubscriptions(userID,uid,rank):
  23. hasOrder = Order_Model.objects.filter(userID=userID,UID=uid,rank=rank).values('agreement_id','orderID').order_by('addTime')[0:1]
  24. if not hasOrder.exists() or hasOrder[0]['agreement_id'] == '':
  25. return True
  26. paypalrestsdk.configure(PAYPAL_CRD)
  27. billing_agreement = paypalrestsdk.BillingAgreement.find(hasOrder[0]['agreement_id'])
  28. if billing_agreement.state == 'Active':
  29. return False
  30. return True
  31. def subscriptions(store_info,lang,orderID,price):
  32. cycle_config = PayCycleConfigModel.objects.filter(id=store_info['cycle_config_id']).values()
  33. if not cycle_config:
  34. return False
  35. cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  36. if lang != 'cn':
  37. cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  38. return_url = "{SERVER_DOMAIN_SSL}payCycle/paypalCycleReturn?lang={lang}". \
  39. format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, lang=lang)
  40. # call_sub_url = "http://binbin.uicp.vip/cloudstorage/dopaypalcallback?orderID={orderID}".format(
  41. # SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, orderID=orderID)
  42. # exit(price)
  43. BillingPlan = {
  44. "description": orderID,
  45. "merchant_preferences": {
  46. "auto_bill_amount": "YES",
  47. "cancel_url": cal_url, # 取消协议url
  48. "initial_fail_amount_action": "CANCEL",
  49. "max_fail_attempts": "1", # 允许的最大失败付款尝试次数
  50. "return_url": return_url, # 客户批准协议的url
  51. # "notify_url": "http://www.notify.com", #通知客户协议已创建的 URL。只读并保留供将来使用。
  52. "setup_fee": {
  53. "currency": store_info['currency'],
  54. "value": price,
  55. }
  56. },
  57. "name": store_info['lang__content'],
  58. "payment_definitions": [
  59. {
  60. "amount": {
  61. "currency": store_info['currency'],
  62. "value": store_info['price']
  63. },
  64. # "charge_models": [
  65. # {
  66. # "amount": {
  67. # "currency": "USD",
  68. # "value": "20"
  69. # },
  70. # "type": "TAX" #税金
  71. # }
  72. # ],
  73. "cycles": cycle_config[0]['cycles'],
  74. "frequency": cycle_config[0]['frequency'],
  75. "frequency_interval": cycle_config[0]['frequencyInterval'],
  76. "name": store_info['lang__title'],
  77. "type": "REGULAR"
  78. },
  79. ],
  80. "type": "INFINITE",
  81. }
  82. paypalrestsdk.configure(PAYPAL_CRD)
  83. billing_plan = paypalrestsdk.BillingPlan(BillingPlan)
  84. if billing_plan.create():
  85. billing_plan.activate() # 激活
  86. plan_id = billing_plan.id
  87. else:
  88. print(billing_plan.error)
  89. return False
  90. now_time = int(time.time())
  91. if cycle_config[0]['frequency'] == "DAY":
  92. start_date_timestamp = now_time + 86400 - 3600 # 下次扣款为明天,提前1个小时扣款
  93. start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ")
  94. elif cycle_config[0]['frequency'] == "MONTH":
  95. start_date_timestamp = CommonService.calcMonthLater(1, now_time) - (5 * 86400) #下次扣款为下个月提前5天扣款
  96. start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ")
  97. #订阅
  98. billingAgreement = {
  99. "name": store_info['lang__content'],
  100. "description": orderID,
  101. "start_date": start_date_str,
  102. "plan": {
  103. "id": plan_id
  104. },
  105. "payer": {
  106. "payment_method": "paypal"
  107. },
  108. }
  109. billing_agreement = paypalrestsdk.BillingAgreement(billingAgreement)
  110. # print(billing_agreement.create())
  111. if billing_agreement.create():
  112. for link in billing_agreement.links:
  113. if link.rel == "approval_url":
  114. return {"plan_id": plan_id, "url": link.href}
  115. else:
  116. print(billing_agreement.error)
  117. return False
  118. class PaypalCycleNotify(View):
  119. def get(self, request, *args, **kwargs):
  120. request.encoding = 'utf-8'
  121. operation = kwargs.get('operation')
  122. return self.validation(request.GET, request, operation)
  123. def post(self, request, *args, **kwargs):
  124. request.encoding = 'utf-8'
  125. operation = kwargs.get('operation')
  126. return self.validation(request.POST, request, operation)
  127. def validation(self, request_dict, request, operation):
  128. response = ResponseObject()
  129. if operation is None:
  130. return response.json(444, 'error path')
  131. elif operation == 'paypalCycleReturn': # paypal成功订阅回调
  132. return self.do_paypal_cycle_return(request_dict, response)
  133. elif operation == 'paypalCycleNotify': # paypal 周期付款回调
  134. return self.do_paypal_webhook_notify(request_dict,request, response)
  135. elif operation == 'test': # paypal 周期付款回调
  136. return self.do_test(request_dict,request, response)
  137. def do_paypal_cycle_return(self, request_dict, response):
  138. lang = request_dict.get('lang', 'en')
  139. token = request_dict.get('token',None)
  140. paypalrestsdk.configure(PAYPAL_CRD)
  141. billing_agreement = paypalrestsdk.BillingAgreement()
  142. billing_agreement_response = billing_agreement.execute(token)
  143. if billing_agreement_response.error:
  144. print(billing_agreement_response.error)
  145. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  146. if lang != 'cn':
  147. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  148. return HttpResponseRedirect(red_url)
  149. orderID = billing_agreement_response.description
  150. agreement_id = billing_agreement_response.id
  151. promotion_rule_id = ''
  152. order_qs = Order_Model.objects.filter(orderID=orderID, status=0)
  153. order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
  154. "userID__userID",
  155. "userID__username", 'coupon_id')
  156. try:
  157. if not orderID:
  158. print("not orderID")
  159. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  160. if lang != 'cn':
  161. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  162. return HttpResponseRedirect(red_url)
  163. userid = order_list[0]['userID__userID']
  164. username = order_list[0]['userID__username']
  165. UID = order_list[0]['UID']
  166. channel = order_list[0]['channel']
  167. rank = order_list[0]['rank']
  168. smqs = Store_Meal.objects.filter(id=rank). \
  169. values("day", "bucket_id", "bucket__storeDay", "expire")
  170. bucketId = smqs[0]['bucket_id']
  171. if not smqs.exists():
  172. print("not smqs")
  173. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  174. if lang != 'cn':
  175. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  176. return HttpResponseRedirect(red_url)
  177. # ##
  178. ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay", "bucket__region",
  179. "endTime", "use_status")
  180. expire = smqs[0]['expire']
  181. if order_list[0]['isSelectDiscounts'] == 1:
  182. expire = smqs[0]['expire'] * 2
  183. # 是否有促销
  184. nowTime = int(time.time())
  185. promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
  186. endTime__gte=nowTime).values('id','ruleConfig')
  187. if promotion.exists():
  188. promotion_rule_id = promotion[0]['id']
  189. expire = expire * 2
  190. with transaction.atomic():
  191. if ubqs.exists():
  192. ubq = ubqs[0]
  193. if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: #套餐使用中并且相同套餐叠加过期时间
  194. endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
  195. UID_Bucket.objects.filter(id=ubq['id']).update \
  196. (uid=UID, channel=channel, bucket_id=bucketId,
  197. endTime=endTime, updateTime=nowTime)
  198. else: #已过期或者不相同的套餐加入未使用的关联套餐表
  199. has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
  200. nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
  201. if promotion.exists():
  202. nums = nums + 1
  203. if has_unused.exists():
  204. Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
  205. else:
  206. Unused_Uid_Meal.objects.create(uid=UID,channel=channel,addTime=nowTime,num=nums,
  207. expire=smqs[0]['expire'],bucket_id=bucketId)
  208. UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
  209. uid_bucket_id = ubq['id']
  210. else:
  211. endTime = CommonService.calcMonthLater(expire)
  212. ub_cqs = UID_Bucket.objects.create \
  213. (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
  214. updateTime=nowTime,use_status=1)
  215. uid_bucket_id = ub_cqs.id
  216. dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
  217. if dvq.exists():
  218. dvq_set_update_dict = {
  219. 'vodPrimaryUserID': userid,
  220. 'vodPrimaryMaster': username
  221. }
  222. dvq.update(**dvq_set_update_dict)
  223. # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
  224. # if not uid_main_exist.exists():
  225. # uid_main_dict = {
  226. # 'UID': UID,
  227. # 'user_id': userid
  228. # }
  229. # UIDMainUser.objects.create(**uid_main_dict)
  230. # 核销coupon
  231. if order_list[0]['coupon_id']:
  232. CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=1)
  233. order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,
  234. promotion_rule_id=promotion_rule_id,agreement_id=agreement_id)
  235. datetime = time.strftime("%Y-%m-%d", time.localtime())
  236. sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + UID + '设备在' + datetime + '已成功订阅云存套餐',
  237. 'Dear customer,you already subscribed the cloud storage package successfully for device ' + UID + ' on ' + time.strftime(
  238. "%b %dth,%Y", time.localtime())]
  239. CloudStorage.CloudStorageView.do_vod_msg_Notice(self, UID, channel, userid, lang, sys_msg_text_list, 'SMS_219738485')
  240. # return response.json(0)
  241. red_url = "{SERVER_DOMAIN_SSL}web/paid2/success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  242. if lang != 'cn':
  243. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  244. if order_list[0]['coupon_id'] != '':
  245. CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2)
  246. return HttpResponseRedirect(red_url)
  247. except Exception as e:
  248. print(repr(e))
  249. logger = logging.getLogger('info')
  250. logger.info('notify------cycle----paypal')
  251. logger.info(repr(e))
  252. logger.info(sys.exc_info())
  253. if order_qs:
  254. order_qs.update(status=10, promotion_rule_id=promotion_rule_id)
  255. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  256. if lang != 'cn':
  257. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  258. return HttpResponseRedirect(red_url)
  259. def do_paypal_webhook_notify(self, request_dict, request, response):
  260. logger = logging.getLogger('info')
  261. json_agreement_str = request.body.decode("utf-8")
  262. json_obj = json.loads(json_agreement_str)
  263. header = request.META
  264. paypal_body = json_obj.get('resource')
  265. billing_agreement_id = paypal_body.get('billing_agreement_id')
  266. amount = paypal_body.get('amount')
  267. if not billing_agreement_id:
  268. return HttpResponse('success')
  269. transmission_id = header.get('HTTP_PAYPAL_TRANSMISSION_ID',None)
  270. transmission_time = header.get('HTTP_PAYPAL_TRANSMISSION_TIME',None)
  271. cert_url = header.get('HTTP_PAYPAL_CERT_URL',None)
  272. transmission_sig = header.get('HTTP_PAYPAL_TRANSMISSION_SIG',None)
  273. auth_algo = header.get('HTTP_PAYPAL_AUTH_ALGO',None)
  274. resource_type = json_obj.get('resource_type')
  275. # self.get_plan_desc('P-4CG284532S612303METMEINY')
  276. if resource_type == 'sale' and paypal_body.get('state') == 'completed':
  277. paypalrestsdk.configure(PAYPAL_CRD)
  278. response = paypalrestsdk.WebhookEvent.verify(
  279. transmission_id, transmission_time, PAYPAL_WEB_HOOK_ID, json_agreement_str, cert_url, transmission_sig, auth_algo)
  280. logger.info('-----------------------verify')
  281. logger.info(response)
  282. if response:
  283. try:
  284. agreement_id = paypal_body.get('billing_agreement_id')
  285. billing_agreement = paypalrestsdk.BillingAgreement.find(agreement_id)
  286. # 订阅续费订单(如果完成周期数`不是0, 则说明是续费订单,)
  287. if billing_agreement.agreement_details.cycles_completed != '0':
  288. return HttpResponse('fail')
  289. orderID = billing_agreement.description
  290. order_qs = Order_Model.objects.filter(orderID=orderID, status=1)
  291. if not order_qs:
  292. return HttpResponse('fail')
  293. order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
  294. "userID__userID","uid_bucket_id",
  295. "userID__username",'plan_id','addTime','desc','payType','currency','commodity_type')
  296. nowTime = int(time.time())
  297. userid = order_list[0]['userID__userID']
  298. username = order_list[0]['userID__username']
  299. UID = order_list[0]['UID']
  300. channel = order_list[0]['channel']
  301. rank = order_list[0]['rank']
  302. smqs = Store_Meal.objects.filter(id=rank). \
  303. values("day", "bucket_id", "bucket__storeDay", "expire")
  304. bucketId = smqs[0]['bucket_id']
  305. if not smqs.exists():
  306. return HttpResponse('fail')
  307. # ##
  308. ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay",
  309. "bucket__region",
  310. "endTime", "use_status")
  311. expire = smqs[0]['expire']
  312. # if order_list[0]['isSelectDiscounts'] == 1:
  313. # expire = smqs[0]['expire'] * 2
  314. # 是否有促销
  315. # nowTime = int(time.time())
  316. # promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
  317. # endTime__gte=nowTime).values('id', 'ruleConfig')
  318. # if promotion.exists():
  319. # promotion_rule_id = promotion[0]['id']
  320. # expire = expire * 2
  321. with transaction.atomic():
  322. if ubqs.exists():
  323. ubq = ubqs[0]
  324. if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: # 套餐使用中并且相同套餐叠加过期时间
  325. endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
  326. UID_Bucket.objects.filter(id=ubq['id']).update \
  327. (uid=UID, channel=channel, bucket_id=bucketId,
  328. endTime=endTime, updateTime=nowTime)
  329. else: # 已过期或者不相同的套餐加入未使用的关联套餐表
  330. has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
  331. # nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
  332. # if promotion.exists():
  333. nums = 1
  334. if has_unused.exists():
  335. Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
  336. else:
  337. Unused_Uid_Meal.objects.create(uid=UID, channel=channel, addTime=nowTime, num=nums,
  338. expire=smqs[0]['expire'], bucket_id=bucketId)
  339. UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
  340. uid_bucket_id = ubq['id']
  341. else:
  342. endTime = CommonService.calcMonthLater(expire)
  343. ub_cqs = UID_Bucket.objects.create \
  344. (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
  345. updateTime=nowTime, use_status=1)
  346. uid_bucket_id = ub_cqs.id
  347. dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
  348. if dvq.exists():
  349. dvq_set_update_dict = {
  350. 'vodPrimaryUserID': userid,
  351. 'vodPrimaryMaster': username
  352. }
  353. dvq.update(**dvq_set_update_dict)
  354. # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
  355. # if not uid_main_exist.exists():
  356. # uid_main_dict = {
  357. # 'UID': UID,
  358. # 'user_id': userid
  359. # }
  360. # UIDMainUser.objects.create(**uid_main_dict)
  361. orderID = CommonService.createOrderID()
  362. Order_Model.objects.create(orderID=orderID, UID=UID, channel=channel, userID_id=userid,
  363. desc=order_list[0]['desc'], payType=order_list[0]['payType'], payTime=nowTime,
  364. price=amount.get('total'), currency=order_list[0]['currency'], addTime=nowTime, updTime=nowTime,
  365. pay_url='', isSelectDiscounts=0,
  366. commodity_code=order_list[0]['commodity_code'], commodity_type=order_list[0]['commodity_type'],
  367. rank_id=rank, paymentID='', coupon_id='',uid_bucket_id=uid_bucket_id,status=1,agreement_id=agreement_id,plan_id=order_list[0]['plan_id'])
  368. datetime = time.strftime("%Y-%m-%d", time.localtime())
  369. sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + UID + '设备在' + datetime + '已成功续订云存套餐',
  370. 'Dear customer,you already subscribed the cloud storage package successfully for device ' + UID + ' on ' + time.strftime(
  371. "%b %dth,%Y", time.localtime())]
  372. if order_list[0]['payType'] == 1:
  373. lang = 'en'
  374. else:
  375. lang = 'cn'
  376. CloudStorage.CloudStorageView.do_vod_msg_Notice(self, UID, channel, userid, lang,
  377. sys_msg_text_list, 'SMS_219738485')
  378. logger.info('-----------------------result')
  379. logger.info('success')
  380. return HttpResponse('success')
  381. except Exception as e:
  382. print(e)
  383. return HttpResponse('fail')
  384. return HttpResponse('fail')
  385. def do_test(self, request_dict, request, response):
  386. paypalrestsdk.configure(PAYPAL_CRD)
  387. billing_agreement = paypalrestsdk.BillingAgreement
  388. billing_agreement = billing_agreement.find("I-HT38K76XPMGJ")
  389. print("Got Billing Agreement Details for Billing Agreement[%s]" % (
  390. billing_agreement.id))
  391. exit()
  392. #normal_pay
  393. # json_str = '{"id":"WH-8SU832847J141682K-0FF265943E8692615","event_version":"1.0","create_time":"2022-01-10T06:31:49.863Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 0.02 USD","resource":{"amount":{"total":"0.02","currency":"USD","details":{"subtotal":"0.02"}},"payment_mode":"INSTANT_TRANSFER","create_time":"2022-01-10T06:31:45Z","transaction_fee":{"currency":"USD","value":"0.02"},"parent_payment":"PAYID-MHN5E5Y1RH70069CT417990V","update_time":"2022-01-10T06:31:45Z","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","application_context":{"related_qualifiers":[{"id":"0FJ93448LU7282046","type":"CART"}]},"protection_eligibility":"ELIGIBLE","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v1/payments/sale/6N498138TH641260G"},{"method":"POST","rel":"refund","href":"https://api.sandbox.paypal.com/v1/payments/sale/6N498138TH641260G/refund"},{"method":"GET","rel":"parent_payment","href":"https://api.sandbox.paypal.com/v1/payments/payment/PAYID-MHN5E5Y1RH70069CT417990V"}],"id":"6N498138TH641260G","state":"completed","invoice_number":""},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-8SU832847J141682K-0FF265943E8692615","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-8SU832847J141682K-0FF265943E8692615/resend","rel":"resend","method":"POST"}]}'
  394. json_agreement_str = '{"id":"WH-9BE23393R5338163R-48P08088YL173821A","event_version":"1.0","create_time":"2022-01-10T10:27:42.925Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 0.02 USD","resource":{"billing_agreement_id":"I-K8PCK2NJC6N6","amount":{"total":"0.02","currency":"USD","details":{"subtotal":"0.02"}},"payment_mode":"INSTANT_TRANSFER","update_time":"2022-01-10T10:27:19Z","create_time":"2022-01-10T10:27:19Z","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/4H259512Y67055105"},{"method":"POST","rel":"refund","href":"https://api.sandbox.paypal.com/v1/payments/sale/4H259512Y67055105/refund"}],"id":"4H259512Y67055105","state":"completed","invoice_number":""},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-9BE23393R5338163R-48P08088YL173821A","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-9BE23393R5338163R-48P08088YL173821A/resend","rel":"resend","method":"POST"}]}'
  395. header = {'wsgi.file_wrapper': '<class gunicorn.http.wsgi.FileWrapper>', 'wsgi.version': '(1, 0)', 'HTTP_CONNECTION': 'close', 'wsgi.url_scheme': 'http', 'HTTP_PAYPAL_CERT_URL': 'https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-7a8abba8', 'HTTP_PAYPAL_TRANSMISSION_ID': '022fbbc0-7139-11ec-afa1-0114a54fc1fc', 'SERVER_NAME': '0.0.0.0', 'HTTP_CORRELATION_ID': 'be4c80f0a6c05', 'REMOTE_ADDR': '127.0.0.1', 'HTTP_PAYPAL_TRANSMISSION_SIG': 'IM3Xwyjw5YUgBKPsgyjPdMAh6DSFTtqdwy8zbJBXBhFyB77B6mEqnRfhtEgwwBhag6HsStmKBGIScFhs5Nuraru7DbT4+7Tu5fNx3oQIHeHtR/FYZoQcv86bjZ9cq+Xo04HmhUfgBAsSetS+CuY5TsN60d1m8Hld1MTDjk1UuSbk8HA3dBLiMzWT7wUw3/SUau/C7TtLnWGmdJlkFne+b/5s0+HsuXn3wQQCDIHO0sBMBo72NdlyMlLIunSdoEJ61pKi2U1jQ6qqe/59IrY2q4ufx9D6JZ4bUB6z3NQZ+Gm7zrlKabT6HkVovLJbuBgRgRWWUoY02CuVXZ9w4AzVNQ==', 'REMOTE_PORT': '58060', 'HTTP_ACCEPT': '*/*', 'CONTENT_TYPE': 'application/json', 'HTTP_USER_AGENT': 'PayPal/AUHR-214.0-56015767', 'SCRIPT_NAME': '', 'HTTP_X_FORWARDED_FOR': '173.0.80.117', 'HTTP_HOST': 'test.zositechc.cn:443', 'wsgi.multiprocess': True, 'SERVER_PROTOCOL': 'HTTP/1.0', 'PATH_INFO': '/payCycle/paypalCycleNotify', 'SERVER_SOFTWARE': 'gunicorn/19.7.1', 'wsgi.input': '<gunicorn.http.body.Body object at 0x7fb966cddfd0>', 'REQUEST_METHOD': 'POST', 'wsgi.errors': '<gunicorn.http.wsgi.WSGIErrorsWrapper object at 0x7fb966cdda90>', 'CONTENT_LENGTH': '1226', 'wsgi.run_once': False, 'HTTP_X_B3_SPANID': 'e8ede80526720f95', 'HTTP_PAYPAL_AUTH_ALGO': 'SHA256withRSA', 'QUERY_STRING': '', 'HTTP_PAYPAL_TRANSMISSION_TIME': '2022-01-09T10:43:40Z', 'wsgi.multithread': False, 'HTTP_HTTP_X_FORWARDED_FOR': '173.0.80.117', 'HTTP_X_REAL_IP': '173.0.80.117', 'RAW_URI': '/payCycle/paypalCycleNotify', 'HTTP_PAYPAL_AUTH_VERSION': 'v2', 'gunicorn.socket': '<socket.socket fd=51, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(127.0.0.1, 8082), raddr=(127.0.0.1, 58060)>', 'SERVER_PORT': '8082'}
  396. json_obj = json.loads(json_agreement_str)
  397. paypal_body = json_obj.get('resource')
  398. billing_agreement_id = paypal_body.get('billing_agreement_id')
  399. amount = paypal_body.get('amount')
  400. if not billing_agreement_id:
  401. return HttpResponse('success')
  402. nowTime = int(time.time())
  403. transmission_id = header.get('HTTP_PAYPAL_TRANSMISSION_ID',None)
  404. transmission_time = header.get('HTTP_PAYPAL_TRANSMISSION_TIME',None)
  405. webhook_id = '6TS30758D98835230'
  406. cert_url = header.get('HTTP_PAYPAL_CERT_URL',None)
  407. transmission_sig = header.get('HTTP_PAYPAL_TRANSMISSION_SIG',None)
  408. auth_algo = header.get('HTTP_PAYPAL_AUTH_ALGO',None)
  409. resource_type = json_obj.get('resource_type')
  410. # return HttpResponse(resource_type)
  411. transmission_id = 'f42509f0-71ff-11ec-a473-05e6d85b61e7'
  412. transmission_time = '2022-01-10T10:27:46Z'
  413. webhook_id = '3J888119TD851704M'
  414. cert_url = 'https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-7a8abba8'
  415. transmission_sig = 'R6sBDhsoq5+FRQHWe+8tSeKJMlRDnt9F2SlWlWVVEfDu9mvQ0zKl74bwcN1zMbvH4o7fWVNbwkcPW70/t4O0YBsj9BcMwL8hDxcuWuHp20RBzaI2dlBpdPEke19wr/fhJKGZCDYuvptV2RJGCSePBn3gKs7hkY5ribELPDqHuajlgVxMmoXm/+CHrMmPo6gSGgTuEMzEn4/ENuj3uJoCkcYqsFx3tUHg6eakUvQ+vYAyflRx9hX7QXEQHp15PWLgGzHkm9zGmnX6YoG5keo5MbJEYh9LfHJjmHmHVErvOtHebJxfTEDZwGoqw+WHr3KqnP4L1gaUj7XIXsQzbiFTBg=='
  416. auth_algo = 'SHA256withRSA'
  417. resource_type = 'sale'
  418. self.get_plan_desc('P-4CG284532S612303METMEINY')
  419. if resource_type == 'sale' and paypal_body.get('state') == 'completed':
  420. # paypalrestsdk.configure(PAYPAL_CRD)
  421. # response = paypalrestsdk.WebhookEvent.verify(
  422. # transmission_id, transmission_time, webhook_id, json_agreement_str, cert_url, transmission_sig, auth_algo)
  423. response = True
  424. if response:
  425. try:
  426. agreement_id = paypal_body.get('billing_agreement_id')
  427. order_qs = Order_Model.objects.filter(agreement_id=agreement_id, status=1)
  428. if not order_qs:
  429. return HttpResponse('failss')
  430. order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
  431. "userID__userID","uid_bucket_id",
  432. "userID__username",'plan_id','addTime','desc','payType','currency','commodity_type')
  433. plan_id = order_list[0]['plan_id']
  434. # plan_cycle = self.get_plan_desc(plan_id)
  435. # 订阅续费订单(如果查到的本地订单已经付过了且包中的完成周期数`不是0, 则说明是续费订单, 本地可以新建一个订单标记是续费的)
  436. nowTime = int(time.time())
  437. if(order_list[0]['addTime']+600 > nowTime):
  438. return HttpResponse('success')
  439. userid = order_list[0]['userID__userID']
  440. username = order_list[0]['userID__username']
  441. UID = order_list[0]['UID']
  442. channel = order_list[0]['channel']
  443. rank = order_list[0]['rank']
  444. smqs = Store_Meal.objects.filter(id=rank). \
  445. values("day", "bucket_id", "bucket__storeDay", "expire")
  446. bucketId = smqs[0]['bucket_id']
  447. if not smqs.exists():
  448. return HttpResponse('fail')
  449. # ##
  450. ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay",
  451. "bucket__region",
  452. "endTime", "use_status")
  453. expire = smqs[0]['expire']
  454. # if order_list[0]['isSelectDiscounts'] == 1:
  455. # expire = smqs[0]['expire'] * 2
  456. # 是否有促销
  457. # nowTime = int(time.time())
  458. # promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
  459. # endTime__gte=nowTime).values('id', 'ruleConfig')
  460. # if promotion.exists():
  461. # promotion_rule_id = promotion[0]['id']
  462. # expire = expire * 2
  463. with transaction.atomic():
  464. if ubqs.exists():
  465. ubq = ubqs[0]
  466. if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: # 套餐使用中并且相同套餐叠加过期时间
  467. endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
  468. UID_Bucket.objects.filter(id=ubq['id']).update \
  469. (uid=UID, channel=channel, bucket_id=bucketId,
  470. endTime=endTime, updateTime=nowTime)
  471. else: # 已过期或者不相同的套餐加入未使用的关联套餐表
  472. has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
  473. # nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
  474. # if promotion.exists():
  475. nums = 1
  476. if has_unused.exists():
  477. Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
  478. else:
  479. Unused_Uid_Meal.objects.create(uid=UID, channel=channel, addTime=nowTime, num=nums,
  480. expire=smqs[0]['expire'], bucket_id=bucketId)
  481. UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
  482. uid_bucket_id = ubq['id']
  483. else:
  484. endTime = CommonService.calcMonthLater(expire)
  485. ub_cqs = UID_Bucket.objects.create \
  486. (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
  487. updateTime=nowTime, use_status=1)
  488. uid_bucket_id = ub_cqs.id
  489. dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
  490. if dvq.exists():
  491. dvq_set_update_dict = {
  492. 'vodPrimaryUserID': userid,
  493. 'vodPrimaryMaster': username
  494. }
  495. dvq.update(**dvq_set_update_dict)
  496. # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
  497. # if not uid_main_exist.exists():
  498. # uid_main_dict = {
  499. # 'UID': UID,
  500. # 'user_id': userid
  501. # }
  502. # UIDMainUser.objects.create(**uid_main_dict)
  503. orderID = CommonService.createOrderID()
  504. Order_Model.objects.create(orderID=orderID, UID=UID, channel=channel, userID_id=userid,
  505. desc=order_list[0]['desc'], payType=order_list[0]['payType'], payTime=nowTime,
  506. price=amount.get('total'), currency=order_list[0]['currency'], addTime=nowTime, updTime=nowTime,
  507. pay_url='', isSelectDiscounts=0,
  508. commodity_code=order_list[0]['commodity_code'], commodity_type=order_list[0]['commodity_type'],
  509. rank_id=rank, paymentID='', coupon_id='',uid_bucket_id=uid_bucket_id,status=1,agreement_id=agreement_id,plan_id=order_list[0]['plan_id'])
  510. datetime = time.strftime("%Y-%m-%d", time.localtime())
  511. sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + UID + '设备在' + datetime + '已成功续订云存套餐',
  512. 'Dear customer,you already subscribed the cloud storage package successfully for device ' + UID + ' on ' + time.strftime(
  513. "%b %dth,%Y", time.localtime())]
  514. if order_list[0]['payType'] == 1:
  515. lang = 'en'
  516. else:
  517. lang = 'cn'
  518. CloudStorage.CloudStorageView.do_vod_msg_Notice(self, UID, channel, userid, lang,
  519. sys_msg_text_list, 'SMS_219738485')
  520. return HttpResponse('success')
  521. except Exception as e:
  522. print(e)
  523. return HttpResponse('fail')
  524. return HttpResponse('fail')
  525. def get_plan_desc(self,plan_id):
  526. paypalrestsdk.configure(PAYPAL_CRD)
  527. billing_plan = paypalrestsdk.BillingPlan.find(plan_id)
  528. print("Got Billing Plan Details for Billing Plan[%s]" % (billing_plan.id))
  529. exit()