PaymentCycle.py 37 KB

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