PaymentCycle.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. import traceback
  2. from Ansjer.config import PAYPAL_CRD, SERVER_DOMAIN, SERVER_DOMAIN_SSL, PAYPAL_WEB_HOOK_ID, PAYPAL_WEB_HOOK_ID_TWO
  3. from Model.models import PayCycleConfigModel, Store_Meal, UID_Bucket, PromotionRuleModel, \
  4. Unused_Uid_Meal, Device_Info, CouponModel, Order_Model, PaypalWebHookEvent
  5. from Service.CommonService import CommonService
  6. from django.http import JsonResponse, HttpResponseRedirect, HttpResponse
  7. import requests
  8. import time
  9. import sys
  10. from Object.TokenObject import TokenObject
  11. from Object.UidTokenObject import UidTokenObject
  12. from Object.ResponseObject import ResponseObject
  13. import paypalrestsdk
  14. from paypalrestsdk import BillingAgreement
  15. from django.views.generic.base import View
  16. from django.db import transaction
  17. from Controller import CloudStorage
  18. from django.db.models import Q, F, Count
  19. from paypalrestsdk.notifications import WebhookEvent
  20. import logging
  21. import json
  22. from paypalrestsdk import BillingPlan
  23. import datetime as date_time
  24. # 周期扣款相关
  25. class Paypal:
  26. # 检查是否有重复订阅
  27. def checkSubscriptions(userID, uid, rank):
  28. hasOrder = Order_Model.objects.filter(UID=uid, rank=rank)
  29. hasOrder = hasOrder.filter(~Q(agreement_id='')).values('agreement_id', 'orderID').order_by('-addTime')[0:1]
  30. if not hasOrder.exists():
  31. return True
  32. paypalrestsdk.configure(PAYPAL_CRD)
  33. billing_agreement = paypalrestsdk.BillingAgreement.find(hasOrder[0]['agreement_id'])
  34. if billing_agreement.state == 'Active':
  35. return False
  36. return True
  37. def subscriptions(store_info, lang, orderID, price):
  38. logger = logging.getLogger('pay')
  39. cycle_config = PayCycleConfigModel.objects.filter(id=store_info['cycle_config_id']).values()
  40. if not cycle_config:
  41. logger.info('----创建订阅失败----')
  42. logger.info('订阅配置失败')
  43. return False
  44. cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  45. if lang != 'cn':
  46. cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  47. return_url = "{SERVER_DOMAIN_SSL}payCycle/paypalCycleReturn?lang={lang}". \
  48. format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, lang=lang)
  49. # call_sub_url = "http://binbin.uicp.vip/cloudstorage/dopaypalcallback?orderID={orderID}".format(
  50. # SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, orderID=orderID)
  51. # exit(price)
  52. BillingPlan = {
  53. "description": orderID,
  54. "merchant_preferences": {
  55. "auto_bill_amount": "YES",
  56. "cancel_url": cal_url, # 取消协议url
  57. "initial_fail_amount_action": "CANCEL",
  58. "max_fail_attempts": "1", # 允许的最大失败付款尝试次数
  59. "return_url": return_url, # 客户批准协议的url
  60. # "notify_url": "http://www.notify.com", #通知客户协议已创建的 URL。只读并保留供将来使用。
  61. "setup_fee": {
  62. "currency": store_info['currency'],
  63. "value": price,
  64. }
  65. },
  66. "name": store_info['lang__content'],
  67. "payment_definitions": [
  68. {
  69. "amount": {
  70. "currency": store_info['currency'],
  71. "value": store_info['price']
  72. },
  73. # "charge_models": [
  74. # {
  75. # "amount": {
  76. # "currency": "USD",
  77. # "value": "20"
  78. # },
  79. # "type": "TAX" #税金
  80. # }
  81. # ],
  82. "cycles": cycle_config[0]['cycles'],
  83. "frequency": cycle_config[0]['frequency'],
  84. "frequency_interval": cycle_config[0]['frequencyInterval'],
  85. "name": store_info['lang__title'],
  86. "type": "REGULAR"
  87. },
  88. ],
  89. "type": "INFINITE",
  90. }
  91. paypalrestsdk.configure(PAYPAL_CRD)
  92. billing_plan = paypalrestsdk.BillingPlan(BillingPlan)
  93. if billing_plan.create():
  94. billing_plan.activate() # 激活
  95. plan_id = billing_plan.id
  96. else:
  97. logger.info('----创建计划失败----')
  98. logger.info(billing_plan.error)
  99. return False
  100. now_time = int(time.time())
  101. if cycle_config[0]['frequency'] == "DAY":
  102. start_date_timestamp = now_time + 86400 - 3600 # 下次扣款为明天,提前1个小时扣款
  103. start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ")
  104. elif cycle_config[0]['frequency'] == "MONTH":
  105. start_date_timestamp = CommonService.calcMonthLater(1, now_time) - (5 * 86400) # 下次扣款为下个月提前5天扣款
  106. start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ")
  107. # 订阅
  108. billingAgreement = {
  109. "name": store_info['lang__content'],
  110. "description": orderID,
  111. "start_date": start_date_str,
  112. "plan": {
  113. "id": plan_id
  114. },
  115. "payer": {
  116. "payment_method": "paypal"
  117. },
  118. }
  119. billing_agreement = paypalrestsdk.BillingAgreement(billingAgreement)
  120. # print(billing_agreement.create())
  121. if billing_agreement.create():
  122. for link in billing_agreement.links:
  123. if link.rel == "approval_url":
  124. return {"plan_id": plan_id, "url": link.href}
  125. else:
  126. logger.info('----创建订阅失败----')
  127. logger.info(billing_agreement.error)
  128. return False
  129. class PaypalCycleNotify(View):
  130. def get(self, request, *args, **kwargs):
  131. request.encoding = 'utf-8'
  132. operation = kwargs.get('operation')
  133. return self.validation(request.GET, request, operation)
  134. def post(self, request, *args, **kwargs):
  135. request.encoding = 'utf-8'
  136. operation = kwargs.get('operation')
  137. return self.validation(request.POST, request, operation)
  138. def validation(self, request_dict, request, operation):
  139. response = ResponseObject()
  140. if operation is None:
  141. return response.json(444, 'error path')
  142. elif operation == 'paypalCycleReturn': # paypal成功订阅回调
  143. return self.do_paypal_cycle_return(request_dict, response)
  144. elif operation == 'paypalCycleNotify': # paypal 周期付款回调
  145. return self.do_paypal_webhook_notify(request_dict, request, response)
  146. elif operation == 'subscriptionBreakNotify': # paypal 订阅相关回调
  147. return self.do_subscription_break_notify(request_dict, request, response)
  148. def do_paypal_cycle_return(self, request_dict, response):
  149. lang = request_dict.get('lang', 'en')
  150. token = request_dict.get('token', None)
  151. logger = logging.getLogger('pay')
  152. logger.info('--------进入paypay首次订阅付款回调--------')
  153. logger.info(request_dict)
  154. paypalrestsdk.configure(PAYPAL_CRD)
  155. billing_agreement = paypalrestsdk.BillingAgreement()
  156. billing_agreement_response = billing_agreement.execute(token)
  157. if billing_agreement_response.error:
  158. logger.info('----付款失败----')
  159. logger.info(billing_agreement_response.error)
  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. orderID = billing_agreement_response.description
  165. state = billing_agreement_response.state
  166. nowTime = int(time.time())
  167. promotion_rule_id = ''
  168. logger.info('----订阅详情----')
  169. logger.info(billing_agreement_response)
  170. agreement_id = billing_agreement_response.id
  171. order_qs = Order_Model.objects.filter(orderID=orderID, status=0)
  172. order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
  173. "userID__userID",
  174. "userID__username", 'coupon_id')
  175. if not orderID:
  176. logger.info('----订阅订单号失效----')
  177. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  178. if lang != 'cn':
  179. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  180. return HttpResponseRedirect(red_url)
  181. UID = order_list[0]['UID']
  182. if state != 'Active':
  183. order_qs.update(status=2, promotion_rule_id=promotion_rule_id)
  184. logger.info('----UID:{UID},用户名:{last_time} {first_time}首次订阅付款失败----'.format
  185. (UID=UID,
  186. last_time=billing_agreement_response.payer.payer_info.last_name,
  187. first_time=billing_agreement_response.payer.payer_info.first_time,
  188. ))
  189. logger.info('billing_agreement_state')
  190. logger.info(state)
  191. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  192. if lang != 'cn':
  193. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  194. return HttpResponseRedirect(red_url)
  195. try:
  196. userid = order_list[0]['userID__userID']
  197. username = order_list[0]['userID__username']
  198. channel = order_list[0]['channel']
  199. rank = order_list[0]['rank']
  200. smqs = Store_Meal.objects.filter(id=rank). \
  201. values("day", "bucket_id", "bucket__storeDay", "expire")
  202. bucketId = smqs[0]['bucket_id']
  203. if not smqs.exists():
  204. logger.info('----订阅套餐失效----')
  205. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  206. if lang != 'cn':
  207. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  208. return HttpResponseRedirect(red_url)
  209. # ##
  210. ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay", "bucket__region",
  211. "endTime", "use_status")
  212. expire = smqs[0]['expire']
  213. if order_list[0]['isSelectDiscounts'] == 1:
  214. expire = smqs[0]['expire'] * 2
  215. # 是否有促销
  216. promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
  217. endTime__gte=nowTime).values('id', 'ruleConfig')
  218. if promotion.exists():
  219. promotion_rule_id = promotion[0]['id']
  220. expire = expire * 2
  221. with transaction.atomic():
  222. if ubqs.exists():
  223. ubq = ubqs[0]
  224. if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: # 套餐使用中并且相同套餐叠加过期时间
  225. endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
  226. UID_Bucket.objects.filter(id=ubq['id']).update \
  227. (uid=UID, channel=channel, bucket_id=bucketId,
  228. endTime=endTime, updateTime=nowTime)
  229. else: # 已过期或者不相同的套餐加入未使用的关联套餐表
  230. has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
  231. nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
  232. if promotion.exists():
  233. nums = nums + 1
  234. if has_unused.exists():
  235. Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
  236. else:
  237. Unused_Uid_Meal.objects.create(uid=UID, channel=channel, addTime=nowTime, num=nums,
  238. expire=smqs[0]['expire'], bucket_id=bucketId)
  239. UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
  240. uid_bucket_id = ubq['id']
  241. else:
  242. endTime = CommonService.calcMonthLater(expire)
  243. ub_cqs = UID_Bucket.objects.create \
  244. (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
  245. updateTime=nowTime, use_status=1)
  246. uid_bucket_id = ub_cqs.id
  247. dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
  248. if dvq.exists():
  249. dvq_set_update_dict = {
  250. 'vodPrimaryUserID': userid,
  251. 'vodPrimaryMaster': username
  252. }
  253. dvq.update(**dvq_set_update_dict)
  254. # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
  255. # if not uid_main_exist.exists():
  256. # uid_main_dict = {
  257. # 'UID': UID,
  258. # 'user_id': userid
  259. # }
  260. # UIDMainUser.objects.create(**uid_main_dict)
  261. # 核销coupon
  262. if order_list[0]['coupon_id']:
  263. CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2, update_time=nowTime)
  264. order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,
  265. promotion_rule_id=promotion_rule_id, agreement_id=agreement_id)
  266. # 如果存在序列号,消息提示用序列号
  267. device_name = CommonService.query_serial_with_uid(uid=UID)
  268. datetime = time.strftime("%Y-%m-%d", time.localtime())
  269. sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + device_name + '设备在' + datetime + '已成功订阅云存套餐',
  270. 'Dear customer,you already subscribed the cloud storage package successfully for device ' + device_name + ' on ' + time.strftime(
  271. "%b %dth,%Y", time.localtime())]
  272. CloudStorage.CloudStorageView().do_vod_msg_Notice(UID, channel, userid, lang, sys_msg_text_list,
  273. 'SMS_219738485')
  274. # return response.json(0)
  275. red_url = "{SERVER_DOMAIN_SSL}web/paid2/success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  276. if lang != 'cn':
  277. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  278. logger.info('{UID}成功开通paypal自动续费:----'.format(UID=UID))
  279. return HttpResponseRedirect(red_url)
  280. except Exception as e:
  281. print(repr(e))
  282. logger.info('do_paypal_cycle_return支付失败:----')
  283. logger.info('{UID}开通paypal自动续费失败'.format(UID=UID))
  284. logger.info("错误行数:{errLine}".format(errLine=e.__traceback__.tb_lineno))
  285. logger.info(repr(e))
  286. if order_qs:
  287. order_qs.update(status=10, promotion_rule_id=promotion_rule_id)
  288. red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  289. if lang != 'cn':
  290. red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
  291. return HttpResponseRedirect(red_url)
  292. @staticmethod
  293. def paypal_webhook_log(**params):
  294. """
  295. webhook日志存库
  296. @param params:
  297. @return:
  298. """
  299. logger = logging.getLogger('pay')
  300. try:
  301. params['agreement_desc'] = 'webhook'
  302. PaypalWebHookEvent.objects.create(**params)
  303. logger.info('《Webhook日志存库Success......》')
  304. return True
  305. except Exception as e:
  306. logger.info(e.args)
  307. ex = traceback.format_exc()
  308. logger.info(ex)
  309. return True
  310. def do_paypal_webhook_notify(self, request_dict, request, response):
  311. logger = logging.getLogger('pay')
  312. logger.info('--------进入周期扣款钩子--------')
  313. json_agreement_str = request.body.decode("utf-8")
  314. json_obj = json.loads(json_agreement_str)
  315. header = request.META
  316. paypal_body = json_obj.get('resource')
  317. logger.info('----主体信息----')
  318. logger.info(json_agreement_str)
  319. logger.info('----周期扣款头部信息----')
  320. logger.info(header)
  321. try:
  322. transmission_id = header.get('HTTP_PAYPAL_TRANSMISSION_ID', None)
  323. transmission_time = header.get('HTTP_PAYPAL_TRANSMISSION_TIME', None)
  324. cert_url = header.get('HTTP_PAYPAL_CERT_URL', None)
  325. transmission_sig = header.get('HTTP_PAYPAL_TRANSMISSION_SIG', None)
  326. auth_algo = header.get('HTTP_PAYPAL_AUTH_ALGO', None)
  327. event_type = json_obj.get('event_type')
  328. summary = json_obj.get('summary')
  329. resource_type = json_obj.get('resource_type')
  330. billing_agreement_id = paypal_body.get('billing_agreement_id')
  331. paypal_transaction_id = paypal_body.get('id')
  332. amount = paypal_body.get('amount')
  333. PaypalWebHookEventInsert = {
  334. 'webhook_event_id': json_obj.get('id'),
  335. 'resource_type': json_obj.get('resource_type'),
  336. 'event_type': 1,
  337. 'summary': summary,
  338. 'trade_no': paypal_transaction_id,
  339. 'resource': json_agreement_str,
  340. 'created_time': int(time.time()),
  341. }
  342. self.paypal_webhook_log(**PaypalWebHookEventInsert)
  343. if event_type != 'PAYMENT.SALE.COMPLETED':
  344. logger.info('----钩子异常----')
  345. self.find_subscription_transactions(billing_agreement_id)
  346. if resource_type == 'sale' and paypal_body.get('state') == 'completed':
  347. paypalrestsdk.configure(PAYPAL_CRD)
  348. response = paypalrestsdk.WebhookEvent.verify(
  349. transmission_id, transmission_time, PAYPAL_WEB_HOOK_ID, json_agreement_str, cert_url,
  350. transmission_sig, auth_algo)
  351. logger.info('----验证签名----')
  352. logger.info(response)
  353. if not response:
  354. return HttpResponse('Fail', status=500)
  355. else:
  356. logger.info('----付款状态有误----')
  357. logger.info(resource_type)
  358. logger.info(paypal_body.get('state'))
  359. return HttpResponse('Fail', status=500)
  360. if not billing_agreement_id:
  361. # 记录钩子日志
  362. PaypalWebHookEvent.objects.create(**PaypalWebHookEventInsert)
  363. # 普通支付,更新paypal交易id
  364. paymentID = paypal_body.get('parent_payment')
  365. if paymentID and paypal_transaction_id:
  366. Order_Model.objects.filter(paymentID=paymentID).update(
  367. updTime=int(time.time()),
  368. trade_no=paypal_transaction_id
  369. )
  370. logger.info('----paypal_transaction_id----更新成功')
  371. logger.info('----paypal_transaction_id----:')
  372. logger.info(paypal_transaction_id)
  373. return HttpResponse('success')
  374. else:
  375. logger.info('----paymentID and transactions_id某个为空')
  376. logger.info(paymentID)
  377. logger.info(paypal_transaction_id)
  378. return HttpResponse('fail', status=500)
  379. agreement_id = paypal_body.get('billing_agreement_id')
  380. billing_agreement = paypalrestsdk.BillingAgreement.find(agreement_id)
  381. logger.info(type(billing_agreement))
  382. # 记录钩子日志
  383. PaypalWebHookEventInsert['agreement_desc'] = repr(billing_agreement)
  384. PaypalWebHookEventInsert['agreement_id'] = agreement_id
  385. PaypalWebHookEventInsert['orderID'] = billing_agreement.description
  386. PaypalWebHookEvent.objects.create(**PaypalWebHookEventInsert)
  387. # 订阅续费订单(如果完成周期数`==0,则是自动续费第一次扣款。否则说明是续费订单)
  388. if billing_agreement.agreement_details.cycles_completed == '0':
  389. logger.info('----订阅详情----')
  390. logger.info(billing_agreement)
  391. logger.info('订阅续费订单完成周期数==0,结束')
  392. # 更新order表,paypal的商家交易号
  393. Order_Model.objects.filter(orderID=billing_agreement.description).update(
  394. updTime=int(time.time()),
  395. trade_no=paypal_transaction_id
  396. )
  397. return HttpResponse('success')
  398. oldOrderID = billing_agreement.description
  399. order_qs = Order_Model.objects.filter(orderID=oldOrderID, status=1)
  400. if not order_qs:
  401. return HttpResponse('fail', status=500)
  402. order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",
  403. "userID__userID", "uid_bucket_id",
  404. "userID__username", 'plan_id', 'addTime', 'desc', 'payType', 'currency',
  405. 'commodity_type', 'updTime')
  406. nowTime = int(time.time())
  407. if order_list[0]['addTime'] + 9200 > nowTime: # 避免续费订单重复支付
  408. logger.info('----paypal重复异步回调----')
  409. return HttpResponse('success')
  410. logger.info('----走入订阅过程----')
  411. userid = order_list[0]['userID__userID']
  412. username = order_list[0]['userID__username']
  413. UID = order_list[0]['UID']
  414. channel = order_list[0]['channel']
  415. rank = order_list[0]['rank']
  416. smqs = Store_Meal.objects.filter(id=rank). \
  417. values("day", "bucket_id", "bucket__storeDay", "expire")
  418. bucketId = smqs[0]['bucket_id']
  419. if not smqs.exists():
  420. return HttpResponse('fail', status=500)
  421. # ##
  422. ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay",
  423. "bucket__region",
  424. "endTime", "use_status")
  425. expire = smqs[0]['expire']
  426. # if order_list[0]['isSelectDiscounts'] == 1:
  427. # expire = smqs[0]['expire'] * 2
  428. # 是否有促销
  429. # nowTime = int(time.time())
  430. # promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime,
  431. # endTime__gte=nowTime).values('id', 'ruleConfig')
  432. # if promotion.exists():
  433. # promotion_rule_id = promotion[0]['id']
  434. # expire = expire * 2
  435. with transaction.atomic():
  436. if ubqs.exists():
  437. ubq = ubqs[0]
  438. if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: # 套餐使用中并且相同套餐叠加过期时间
  439. endTime = CommonService.calcMonthLater(expire, ubq['endTime'])
  440. UID_Bucket.objects.filter(id=ubq['id']).update \
  441. (uid=UID, channel=channel, bucket_id=bucketId,
  442. endTime=endTime, updateTime=nowTime)
  443. else: # 已过期或者不相同的套餐加入未使用的关联套餐表
  444. has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id")
  445. # nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1
  446. # if promotion.exists():
  447. nums = 1
  448. if has_unused.exists():
  449. Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums)
  450. else:
  451. Unused_Uid_Meal.objects.create(uid=UID, channel=channel, addTime=nowTime, num=nums,
  452. expire=smqs[0]['expire'], bucket_id=bucketId)
  453. UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1)
  454. uid_bucket_id = ubq['id']
  455. else:
  456. endTime = CommonService.calcMonthLater(expire)
  457. ub_cqs = UID_Bucket.objects.create \
  458. (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime,
  459. updateTime=nowTime, use_status=1)
  460. uid_bucket_id = ub_cqs.id
  461. dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='')
  462. if dvq.exists():
  463. dvq_set_update_dict = {
  464. 'vodPrimaryUserID': userid,
  465. 'vodPrimaryMaster': username
  466. }
  467. dvq.update(**dvq_set_update_dict)
  468. # uid_main_exist = UIDMainUser.objects.filter(UID=UID)
  469. # if not uid_main_exist.exists():
  470. # uid_main_dict = {
  471. # 'UID': UID,
  472. # 'user_id': userid
  473. # }
  474. # UIDMainUser.objects.create(**uid_main_dict)
  475. orderID = CommonService.createOrderID()
  476. store_meal_qs = Store_Meal.objects.filter(id=rank, lang__lang='cn', is_show=0).values('lang__title',
  477. 'lang__content')
  478. if store_meal_qs.exists():
  479. store_meal_name = store_meal_qs[0]['lang__title'] + '-' + store_meal_qs[0]['lang__content']
  480. else:
  481. store_meal_name = '未知套餐'
  482. Order_Model.objects.create(orderID=orderID, UID=UID, channel=channel, userID_id=userid,
  483. desc=order_list[0]['desc'], payType=order_list[0]['payType'],
  484. payTime=nowTime,
  485. price=amount.get('total'), currency=order_list[0]['currency'],
  486. addTime=nowTime, updTime=nowTime,
  487. pay_url='', isSelectDiscounts=0,
  488. commodity_code=order_list[0]['commodity_code'],
  489. commodity_type=order_list[0]['commodity_type'], rank_id=rank, paymentID='',
  490. coupon_id='', uid_bucket_id=uid_bucket_id, status=1,
  491. agreement_id=agreement_id, store_meal_name=store_meal_name,
  492. plan_id=order_list[0]['plan_id'], ai_rank_id=1,
  493. trade_no=paypal_transaction_id)
  494. # 如果存在序列号,消息提示用序列号
  495. device_name = CommonService.query_serial_with_uid(uid=UID)
  496. datetime = time.strftime("%Y-%m-%d", time.localtime())
  497. sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + device_name + '设备在' + datetime + '已成功续订云存套餐',
  498. 'Dear customer,you already subscribed the cloud storage package successfully for device ' + device_name + ' on ' + time.strftime(
  499. "%b %dth,%Y", time.localtime())]
  500. if order_list[0]['payType'] == 1:
  501. lang = 'en'
  502. else:
  503. lang = 'cn'
  504. CloudStorage.CloudStorageView().do_vod_msg_Notice(UID, channel, userid, lang,
  505. sys_msg_text_list, 'SMS_219738485')
  506. logger.info('----周期扣款结果----')
  507. logger.info('success')
  508. # 更新agreement
  509. billing_agreement_update_attributes = [
  510. {
  511. "op": "replace",
  512. "path": "/",
  513. "value": {
  514. "description": orderID,
  515. }
  516. }
  517. ]
  518. billing_agreement.replace(billing_agreement_update_attributes)
  519. logger.info('{UID}成功续费paypal:----'.format(UID=UID))
  520. return HttpResponse('success')
  521. except Exception as e:
  522. print(e)
  523. logger.info('----周期扣款钩子失败----')
  524. logger.info('do_paypal_webhook_notify支付失败:----')
  525. logger.info("错误行数:{errLine}".format(errLine=e.__traceback__.tb_lineno))
  526. logger.info(repr(e))
  527. return HttpResponse('fail', status=500)
  528. @staticmethod
  529. def find_subscription_transactions(billing_agreement_id):
  530. """
  531. 列出当前订阅扣款事务
  532. @param billing_agreement_id: 协议id
  533. @return:
  534. """
  535. if not billing_agreement_id:
  536. return False
  537. logger = logging.getLogger('pay')
  538. try:
  539. paypalrestsdk.configure(PAYPAL_CRD)
  540. billing_agreement = paypalrestsdk.BillingAgreement.find(billing_agreement_id)
  541. today = date_time.date.today()
  542. oneday = date_time.timedelta(days=1)
  543. yesterday = today - oneday
  544. start_date = yesterday.strftime('%Y-%m-%d')
  545. end_date = today.strftime('%Y-%m-%d')
  546. transactions = billing_agreement.search_transactions(start_date, end_date)
  547. logger.info('--->列出当前扣款事务{}'.format(transactions))
  548. except Exception as e:
  549. logger.info('出错了~查询订阅的事务异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  550. def do_subscription_break_notify(self, request_dict, request, response):
  551. logger = logging.getLogger('pay')
  552. logger.info('--------进入订阅失败,付款失败,暂停--------')
  553. json_agreement_str = request.body.decode("utf-8")
  554. json_obj = json.loads(json_agreement_str)
  555. header = request.META
  556. paypal_body = json_obj.get('resource')
  557. logger.info('----主体信息----')
  558. logger.info(json_agreement_str)
  559. logger.info('----进入订阅失败头部信息----')
  560. logger.info(header)
  561. try:
  562. transmission_id = header.get('HTTP_PAYPAL_TRANSMISSION_ID', None)
  563. transmission_time = header.get('HTTP_PAYPAL_TRANSMISSION_TIME', None)
  564. cert_url = header.get('HTTP_PAYPAL_CERT_URL', None)
  565. transmission_sig = header.get('HTTP_PAYPAL_TRANSMISSION_SIG', None)
  566. auth_algo = header.get('HTTP_PAYPAL_AUTH_ALGO', None)
  567. event_type = json_obj.get('event_type')
  568. summary = json_obj.get('summary')
  569. resource_type = json_obj.get('resource_type')
  570. paypal_transaction_id = paypal_body.get('id')
  571. amount = paypal_body.get('amount')
  572. # self.get_plan_desc('P-4CG284532S612303METMEINY')
  573. paypalrestsdk.configure(PAYPAL_CRD)
  574. response = paypalrestsdk.WebhookEvent.verify(
  575. transmission_id, transmission_time, PAYPAL_WEB_HOOK_ID_TWO, json_agreement_str, cert_url,
  576. transmission_sig, auth_algo)
  577. logger.info('----验证签名----')
  578. logger.info(response)
  579. if not response:
  580. return HttpResponse('Fail', status=500)
  581. event_type_code = 0
  582. billing_agreement_id = ''
  583. if event_type == 'PAYMENT.SALE.COMPLETED':
  584. event_type_code = 1
  585. billing_agreement_id = paypal_body.get('billing_agreement_id')
  586. elif event_type == 'PAYMENT.SALE.REVERSED':
  587. billing_agreement_id = paypal_body.get('billing_agreement_id')
  588. event_type_code = 2
  589. elif event_type == 'BILLING.SUBSCRIPTION.CANCELLED':
  590. billing_agreement_id = paypal_body.get('id')
  591. event_type_code = 3
  592. elif event_type == 'BILLING.SUBSCRIPTION.SUSPENDED':
  593. billing_agreement_id = paypal_body.get('id')
  594. event_type_code = 4
  595. elif event_type == 'BILLING.SUBSCRIPTION.PAYMENT.FAILED':
  596. billing_agreement_id = paypal_body.get('id')
  597. event_type_code = 5
  598. elif event_type == 'PAYMENT.SALE.REFUNDED':
  599. billing_agreement_id = paypal_body.get('billing_agreement_id')
  600. event_type_code = 6
  601. PaypalWebHookEventInsert = {
  602. 'webhook_event_id': json_obj.get('id'),
  603. 'resource_type': resource_type,
  604. 'event_type': event_type_code,
  605. 'summary': summary,
  606. 'trade_no': paypal_transaction_id,
  607. 'resource': json_agreement_str,
  608. 'created_time': int(time.time()),
  609. }
  610. if not billing_agreement_id:
  611. # 记录钩子日志
  612. PaypalWebHookEvent.objects.create(**PaypalWebHookEventInsert)
  613. return HttpResponse('success')
  614. billing_agreement = paypalrestsdk.BillingAgreement.find(billing_agreement_id)
  615. # 记录钩子日志
  616. PaypalWebHookEventInsert['agreement_desc'] = repr(billing_agreement)
  617. PaypalWebHookEventInsert['agreement_id'] = billing_agreement_id
  618. PaypalWebHookEventInsert['orderID'] = billing_agreement.description
  619. PaypalWebHookEvent.objects.create(**PaypalWebHookEventInsert)
  620. return HttpResponse('success')
  621. except Exception as e:
  622. print(e)
  623. logger.info('----进入订阅失败----')
  624. logger.info('do_paypal_webhook_notify支付失败:----')
  625. logger.info("错误行数:{errLine}".format(errLine=e.__traceback__.tb_lineno))
  626. logger.info(repr(e))
  627. return HttpResponse('fail', status=500)
  628. def get_plan_desc(self, plan_id):
  629. paypalrestsdk.configure(PAYPAL_CRD)
  630. billing_plan = paypalrestsdk.BillingPlan.find(plan_id)
  631. print("Got Billing Plan Details for Billing Plan[%s]" % (billing_plan.id))
  632. exit()
  633. class payCycle(View):
  634. def get(self, request, *args, **kwargs):
  635. request.encoding = 'utf-8'
  636. operation = kwargs.get('operation')
  637. return self.validation(request.GET, request, operation)
  638. def post(self, request, *args, **kwargs):
  639. request.encoding = 'utf-8'
  640. operation = kwargs.get('operation')
  641. return self.validation(request.POST, request, operation)
  642. def validation(self, request_dict, request, operation):
  643. response = ResponseObject()
  644. token = request_dict.get('token', None)
  645. # 设备主键uid
  646. tko = TokenObject(token)
  647. response.lang = tko.lang
  648. if tko.code != 0:
  649. return response.json(tko.code)
  650. userID = tko.userID
  651. if operation is None:
  652. return response.json(444, 'error path')
  653. elif operation == 'queryPayCycle': # paypal成功订阅回调
  654. return self.do_query_pay_cycle(request_dict, userID, response)
  655. elif operation == 'cancelPayCycle': # 取消自动续费
  656. return self.do_cancel_pay_cycle(request_dict, userID, response)
  657. def do_query_pay_cycle(self, request_dict, userID, response):
  658. lang = request_dict.get('lang', 'en')
  659. uid = request_dict.get('uid', None)
  660. orderObject = Order_Model.objects.filter(userID=userID, status=1, rank__lang__lang=lang).annotate(
  661. rank__title=F('rank__lang__title'), rank__content=F('rank__lang__content'))
  662. if uid:
  663. orderObject = orderObject.filter(UID=uid)
  664. orderObject = orderObject.filter(~Q(agreement_id=''))
  665. if not orderObject.exists():
  666. return response.json(0, {'data': [], 'count': 0})
  667. orderQuery = orderObject.values("orderID", "UID", "channel", "desc", "price", "currency",
  668. "addTime",
  669. "updTime", "paypal", "rank__day", "payType",
  670. "rank__price", "status",
  671. "rank__lang__content", "rank__lang__title", "rank__currency",
  672. "rank_id", "rank__expire", "agreement_id").order_by('addTime')
  673. new_data = []
  674. values = []
  675. for d in orderQuery:
  676. if d['agreement_id'] not in values:
  677. new_data.append(d)
  678. values.append(d['agreement_id'])
  679. count = len(new_data)
  680. return response.json(0, {'data': new_data, 'count': count})
  681. def do_cancel_pay_cycle(self, request_dict, userID, response):
  682. orderID = request_dict.get('orderID', 'None')
  683. orderObject = Order_Model.objects.filter(orderID=orderID)
  684. orderObject = orderObject.filter(~Q(agreement_id='')).values("agreement_id")
  685. if not orderObject.exists():
  686. return response.json(800)
  687. paypalrestsdk.configure(PAYPAL_CRD)
  688. BILLING_AGREEMENT_ID = orderObject[0]['agreement_id']
  689. try:
  690. billing_agreement = paypalrestsdk.BillingAgreement.find(BILLING_AGREEMENT_ID)
  691. if billing_agreement.state != 'Active':
  692. Order_Model.objects.filter(agreement_id=BILLING_AGREEMENT_ID).update(agreement_id='')
  693. return response.json(0)
  694. cancel_note = {"note": "Canceling the agreement"}
  695. if billing_agreement.cancel(cancel_note):
  696. Order_Model.objects.filter(agreement_id=BILLING_AGREEMENT_ID).update(agreement_id='')
  697. return response.json(0)
  698. else:
  699. return response.json(10052)
  700. except Exception as e:
  701. return response.json(10052)