import datetime as date_time import json import logging import time import traceback import paypalrestsdk from django.db import transaction from django.db.models import Q, F from django.http import HttpResponseRedirect, HttpResponse from django.views.generic.base import View from Ansjer.config import PAYPAL_CRD, SERVER_DOMAIN_SSL, PAYPAL_WEB_HOOK_ID, PAYPAL_WEB_HOOK_ID_TWO, CONFIG_INFO, \ CONFIG_US, CONFIG_EUR from Controller import CloudStorage from Model.models import PayCycleConfigModel, Store_Meal, UID_Bucket, PromotionRuleModel, \ Unused_Uid_Meal, Device_Info, CouponModel, Order_Model, PaypalWebHookEvent, CountryModel, AiService from Object.ResponseObject import ResponseObject from Object.TokenObject import TokenObject from Service.CommonService import CommonService PAY_LOGGER = logging.getLogger('pay') # 周期扣款相关 class Paypal: # 检查是否有重复订阅 def checkSubscriptions(userID, uid, rank): hasOrder = Order_Model.objects.filter(UID=uid, rank=rank) hasOrder = hasOrder.filter(~Q(agreement_id='')).values('agreement_id', 'orderID').order_by('-addTime')[0:1] if not hasOrder.exists(): return True paypalrestsdk.configure(PAYPAL_CRD) billing_agreement = paypalrestsdk.BillingAgreement.find(hasOrder[0]['agreement_id']) if billing_agreement.state == 'Active': return False return True def subscriptions(store_info, lang, orderID, price): logger = logging.getLogger('pay') cycle_config = PayCycleConfigModel.objects.filter(id=store_info['cycle_config_id']).values() if not cycle_config: logger.info('----创建订阅失败----') logger.info('订阅配置失败') return False cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) if lang != 'cn': cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) return_url = "{SERVER_DOMAIN_SSL}payCycle/paypalCycleReturn?lang={lang}". \ format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, lang=lang) # call_sub_url = "http://binbin.uicp.vip/cloudstorage/dopaypalcallback?orderID={orderID}".format( # SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL, orderID=orderID) # exit(price) BillingPlan = { "description": orderID, "merchant_preferences": { "auto_bill_amount": "YES", "cancel_url": cal_url, # 取消协议url "initial_fail_amount_action": "CANCEL", "max_fail_attempts": "1", # 允许的最大失败付款尝试次数 "return_url": return_url, # 客户批准协议的url # "notify_url": "http://www.notify.com", #通知客户协议已创建的 URL。只读并保留供将来使用。 "setup_fee": { "currency": store_info['currency'], "value": price, } }, "name": store_info['lang__content'], "payment_definitions": [ { "amount": { "currency": store_info['currency'], "value": store_info['price'] }, # "charge_models": [ # { # "amount": { # "currency": "USD", # "value": "20" # }, # "type": "TAX" #税金 # } # ], "cycles": cycle_config[0]['cycles'], "frequency": cycle_config[0]['frequency'], "frequency_interval": cycle_config[0]['frequencyInterval'], "name": store_info['lang__title'], "type": "REGULAR" }, ], "type": "INFINITE", } paypalrestsdk.configure(PAYPAL_CRD) billing_plan = paypalrestsdk.BillingPlan(BillingPlan) if billing_plan.create(): billing_plan.activate() # 激活 plan_id = billing_plan.id else: logger.info('----创建计划失败----') logger.info(billing_plan.error) return False now_time = int(time.time()) if cycle_config[0]['frequency'] == "DAY": start_date_timestamp = now_time + 86400 - 3600 # 下次扣款为明天,提前1个小时扣款 start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ") elif cycle_config[0]['frequency'] == "MONTH": start_date_timestamp = CommonService.calcMonthLater(1, now_time) - (5 * 86400) # 下次扣款为下个月提前5天扣款 start_date_str = CommonService.timestamp_to_str(start_date_timestamp, "%Y-%m-%dT%H:%M:%SZ") # 订阅 billingAgreement = { "name": store_info['lang__content'], "description": orderID, "start_date": start_date_str, "plan": { "id": plan_id }, "payer": { "payment_method": "paypal" }, } billing_agreement = paypalrestsdk.BillingAgreement(billingAgreement) # print(billing_agreement.create()) if billing_agreement.create(): for link in billing_agreement.links: if link.rel == "approval_url": return {"plan_id": plan_id, "url": link.href} else: logger.info('----创建订阅失败----') logger.info(billing_agreement.error) return False class PaypalCycleNotify(View): def get(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') return self.validation(request.GET, request, operation) def post(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') return self.validation(request.POST, request, operation) def validation(self, request_dict, request, operation): response = ResponseObject() if operation is None: return response.json(444, 'error path') elif operation == 'paypalCycleReturn': # paypal成功订阅回调 return self.do_paypal_cycle_return(request_dict, response) elif operation == 'paypalCycleNotify': # paypal 周期付款回调 return self.do_paypal_webhook_notify(request_dict, request, response) elif operation == 'subscriptionBreakNotify': # paypal 订阅相关回调 return self.do_subscription_break_notify(request_dict, request, response) def do_paypal_cycle_return(self, request_dict, response): lang = request_dict.get('lang', 'en') token = request_dict.get('token', None) logger = logging.getLogger('pay') logger.info('--------进入paypay首次订阅付款回调--------') logger.info(request_dict) paypalrestsdk.configure(PAYPAL_CRD) billing_agreement = paypalrestsdk.BillingAgreement() billing_agreement_response = billing_agreement.execute(token) if billing_agreement_response.error: logger.info('----付款失败----') logger.info(billing_agreement_response.error) red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) if lang != 'cn': red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) return HttpResponseRedirect(red_url) orderID = billing_agreement_response.description state = billing_agreement_response.state nowTime = int(time.time()) promotion_rule_id = '' logger.info('----订阅详情----') logger.info(billing_agreement_response) agreement_id = billing_agreement_response.id order_qs = Order_Model.objects.filter(orderID=orderID, status=0) order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts", "userID__userID", "userID__username", 'coupon_id') if not orderID: logger.info('----订阅订单号失效----') red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) if lang != 'cn': red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) return HttpResponseRedirect(red_url) UID = order_list[0]['UID'] if state != 'Active': order_qs.update(status=2, promotion_rule_id=promotion_rule_id) logger.info('----UID:{UID},用户名:{last_time} {first_time}首次订阅付款失败----'.format (UID=UID, last_time=billing_agreement_response.payer.payer_info.last_name, first_time=billing_agreement_response.payer.payer_info.first_time, )) logger.info('billing_agreement_state') logger.info(state) red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) if lang != 'cn': red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) return HttpResponseRedirect(red_url) try: userid = order_list[0]['userID__userID'] username = order_list[0]['userID__username'] channel = order_list[0]['channel'] rank = order_list[0]['rank'] smqs = Store_Meal.objects.filter(id=rank). \ values("day", "bucket_id", "bucket__storeDay", "expire") bucketId = smqs[0]['bucket_id'] if not smqs.exists(): logger.info('----订阅套餐失效----') red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) if lang != 'cn': red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) return HttpResponseRedirect(red_url) # ## ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay", "bucket__region", "endTime", "use_status") expire = smqs[0]['expire'] if order_list[0]['isSelectDiscounts'] == 1: expire = smqs[0]['expire'] * 2 # 是否有促销 promotion = PromotionRuleModel.objects.filter(status=1, startTime__lte=nowTime, endTime__gte=nowTime).values('id', 'ruleConfig') if promotion.exists(): promotion_rule_id = promotion[0]['id'] expire = expire * 2 with transaction.atomic(): if ubqs.exists(): ubq = ubqs[0] if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: # 套餐使用中并且相同套餐叠加过期时间 endTime = CommonService.calcMonthLater(expire, ubq['endTime']) UID_Bucket.objects.filter(id=ubq['id']).update \ (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, updateTime=nowTime) else: # 已过期或者不相同的套餐加入未使用的关联套餐表 has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id") nums = 2 if order_list[0]['isSelectDiscounts'] == 1 else 1 if promotion.exists(): nums = nums + 1 if has_unused.exists(): Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums) else: Unused_Uid_Meal.objects.create(uid=UID, channel=channel, addTime=nowTime, num=nums, expire=smqs[0]['expire'], bucket_id=bucketId) UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1) uid_bucket_id = ubq['id'] else: endTime = CommonService.calcMonthLater(expire) ub_cqs = UID_Bucket.objects.create \ (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime, updateTime=nowTime, use_status=1) uid_bucket_id = ub_cqs.id dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='') if dvq.exists(): dvq_set_update_dict = { 'vodPrimaryUserID': userid, 'vodPrimaryMaster': username } dvq.update(**dvq_set_update_dict) # uid_main_exist = UIDMainUser.objects.filter(UID=UID) # if not uid_main_exist.exists(): # uid_main_dict = { # 'UID': UID, # 'user_id': userid # } # UIDMainUser.objects.create(**uid_main_dict) # 核销coupon if order_list[0]['coupon_id']: CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2, update_time=nowTime) order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id, promotion_rule_id=promotion_rule_id, agreement_id=agreement_id) # 如果存在序列号,消息提示用序列号 device_name = CommonService.query_serial_with_uid(uid=UID) datetime = time.strftime("%Y-%m-%d", time.localtime()) sys_msg_text_list = [ '温馨提示:尊敬的客户,您的' + device_name + '设备在' + datetime + '已成功订阅云存套餐', 'Dear customer,you already subscribed the cloud storage package successfully for device ' + device_name + ' on ' + time.strftime( "%b %dth,%Y", time.localtime())] CloudStorage.CloudStorageView().do_vod_msg_notice(UID, channel, userid, lang, sys_msg_text_list, 'SMS_219738485') # return response.json(0) red_url = "{SERVER_DOMAIN_SSL}web/paid2/success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) if lang != 'cn': red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_success.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) logger.info('{UID}成功开通paypal自动续费:----'.format(UID=UID)) return HttpResponseRedirect(red_url) except Exception as e: print(repr(e)) logger.info('do_paypal_cycle_return支付失败:----') logger.info('{UID}开通paypal自动续费失败'.format(UID=UID)) logger.info("错误行数:{errLine}".format(errLine=e.__traceback__.tb_lineno)) logger.info(repr(e)) if order_qs: order_qs.update(status=10, promotion_rule_id=promotion_rule_id) red_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) if lang != 'cn': red_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL) return HttpResponseRedirect(red_url) @staticmethod def paypal_webhook_log(**params): """ webhook日志存库 @param params: @return: """ logger = logging.getLogger('pay') try: params['agreement_desc'] = 'webhook' PaypalWebHookEvent.objects.create(**params) logger.info('《Webhook日志存库Success......》') return True except Exception as e: logger.info(e.args) ex = traceback.format_exc() logger.info(ex) return True def do_paypal_webhook_notify(self, request_dict, request, response): PAY_LOGGER.info('--------进入周期扣款钩子--------') if not request.body: PAY_LOGGER.info('PayPal周期扣款失败---缺失请求体') return HttpResponse('fail', status=500) json_agreement_str = request.body.decode("utf-8") json_obj = json.loads(json_agreement_str) header = request.META paypal_body = json_obj.get('resource') PAY_LOGGER.info('----请求体数据:{}----'.format(json_agreement_str)) PAY_LOGGER.info('----请求头数据:{}----'.format(header)) try: transmission_id = header.get('HTTP_PAYPAL_TRANSMISSION_ID', None) transmission_time = header.get('HTTP_PAYPAL_TRANSMISSION_TIME', None) cert_url = header.get('HTTP_PAYPAL_CERT_URL', None) transmission_sig = header.get('HTTP_PAYPAL_TRANSMISSION_SIG', None) auth_algo = header.get('HTTP_PAYPAL_AUTH_ALGO', None) event_type = json_obj.get('event_type') summary = json_obj.get('summary') resource_type = json_obj.get('resource_type') billing_agreement_id = paypal_body.get('billing_agreement_id') paypal_transaction_id = paypal_body.get('id') amount = paypal_body.get('amount') PaypalWebHookEventInsert = { 'webhook_event_id': json_obj.get('id'), 'resource_type': json_obj.get('resource_type'), 'event_type': 1, 'summary': summary, 'trade_no': paypal_transaction_id, 'resource': json_agreement_str, 'created_time': int(time.time()), } self.paypal_webhook_log(**PaypalWebHookEventInsert) if event_type != 'PAYMENT.SALE.COMPLETED': PAY_LOGGER.info('----event_type异常:{}----'.format(event_type)) if resource_type == 'sale' and paypal_body.get('state') == 'completed': paypalrestsdk.configure(PAYPAL_CRD) response = paypalrestsdk.WebhookEvent.verify( transmission_id, transmission_time, PAYPAL_WEB_HOOK_ID, json_agreement_str, cert_url, transmission_sig, auth_algo) if not response: PAY_LOGGER.info('PayPal周期扣款失败---签名验证失败') return HttpResponse('Fail', status=500) else: PAY_LOGGER.info('PayPal周期扣款失败,付款状态有误,resource_type:{},state:{}----'. format(resource_type, paypal_body.get('state'))) return HttpResponse('Fail', status=500) nowTime = int(time.time()) if not billing_agreement_id: # 记录钩子日志 PaypalWebHookEvent.objects.create(**PaypalWebHookEventInsert) # 普通支付,更新paypal交易id paymentID = paypal_body.get('parent_payment') if paymentID and paypal_transaction_id: # 查询客户地区信息,地区跟服务器配置不匹配,返回500 order_qs = Order_Model.objects.filter(paymentID=paymentID).values('UID', 'userID__region_country') if not order_qs.exists(): PAY_LOGGER.info('PayPal周期扣款失败---根据paymentID查询订单数据不存在') return HttpResponse('Fail', status=500) # 判断用户地区是否跟服务器地区匹配 uid = order_qs[0]['UID'] country_id = order_qs[0]['userID__region_country'] if not self.config_match_region(uid, country_id): return HttpResponse('Fail', status=500) order_qs.update(status=1, updTime=nowTime, trade_no=paypal_transaction_id) PAY_LOGGER.info('PayPal周期扣款成功---更新交易id:{}'.format(paypal_transaction_id)) return HttpResponse('success') else: PAY_LOGGER.info('PayPal周期扣款失败---paymentID:{}或paypal_transaction_id:{}为空'. format(paymentID, paypal_transaction_id)) return HttpResponse('fail', status=500) agreement_id = paypal_body.get('billing_agreement_id') billing_agreement = paypalrestsdk.BillingAgreement.find(agreement_id) PAY_LOGGER.info('billing_agreement:{}'.format(billing_agreement)) # 记录钩子日志 PaypalWebHookEventInsert['agreement_desc'] = repr(billing_agreement) PaypalWebHookEventInsert['agreement_id'] = agreement_id PaypalWebHookEventInsert['orderID'] = billing_agreement.description PaypalWebHookEvent.objects.create(**PaypalWebHookEventInsert) # 查询订单数据 order_id = billing_agreement.description order_qs = Order_Model.objects.filter(orderID=order_id).values('UID', 'channel', 'commodity_code', 'rank', 'isSelectDiscounts', 'plan_id', 'desc', 'payType', 'currency', 'addTime', 'commodity_type', 'updTime', 'userID__userID', 'uid_bucket_id', 'userID__username', 'userID__region_country' ) if not order_qs.exists(): PAY_LOGGER.info('PayPal周期扣款失败---根据order_id查询订单数据不存在') return HttpResponse('fail', status=500) # 判断用户地区是否跟服务器地区匹配 uid = order_qs[0]['UID'] country_id = order_qs[0]['userID__region_country'] if not self.config_match_region(uid, country_id): return HttpResponse('Fail', status=500) UID = order_qs[0]['UID'] # PayPal周期扣款首次扣款 if billing_agreement.agreement_details.cycles_completed == '0': # 更新order表,paypal的商家交易号 order_qs.update(status=1, updTime=nowTime, trade_no=paypal_transaction_id) PAY_LOGGER.info('{} PayPal周期扣款首次扣款成功'.format(UID)) return HttpResponse('success') if order_qs[0]['addTime'] + 9200 > nowTime: # 避免续费订单重复支付 PAY_LOGGER.info('{} PayPal周期扣款失败---续费订单已创建'.format(UID)) return HttpResponse('success') desc = order_qs[0]['desc'] pay_type = order_qs[0]['payType'] currency = order_qs[0]['currency'] commodity_code = order_qs[0]['commodity_code'] commodity_type = order_qs[0]['commodity_type'] plan_id = order_qs[0]['plan_id'] userid = order_qs[0]['userID__userID'] username = order_qs[0]['userID__username'] channel = order_qs[0]['channel'] rank = order_qs[0]['rank'] store_meal_qs = Store_Meal.objects.filter(id=rank).values("day", "bucket_id", "bucket__storeDay", "expire") if not store_meal_qs.exists(): PAY_LOGGER.info('{} PayPal周期扣款失败---套餐数据不存在'.format(UID)) return HttpResponse('fail', status=500) bucketId = store_meal_qs[0]['bucket_id'] expire = store_meal_qs[0]['expire'] ubqs = UID_Bucket.objects.filter(uid=UID).values("id", "bucket_id", "bucket__storeDay", "bucket__region", "endTime", "use_status") with transaction.atomic(): if ubqs.exists(): ubq = ubqs[0] if ubq['use_status'] == 1 and ubq['bucket_id'] == bucketId: # 套餐使用中并且相同套餐叠加过期时间 endTime = CommonService.calcMonthLater(expire, ubq['endTime']) UID_Bucket.objects.filter(id=ubq['id']).update \ (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, updateTime=nowTime) else: # 已过期或者不相同的套餐加入未使用的关联套餐表 has_unused = Unused_Uid_Meal.objects.filter(uid=UID, bucket_id=bucketId).values("id") nums = 1 if has_unused.exists(): Unused_Uid_Meal.objects.filter(id=has_unused[0]['id']).update(num=F('num') + nums) else: Unused_Uid_Meal.objects.create(uid=UID, channel=channel, addTime=nowTime, num=nums, expire=expire, bucket_id=bucketId) UID_Bucket.objects.filter(id=ubq['id']).update(has_unused=1) uid_bucket_id = ubq['id'] else: endTime = CommonService.calcMonthLater(expire) ub_cqs = UID_Bucket.objects.create \ (uid=UID, channel=channel, bucket_id=bucketId, endTime=endTime, addTime=nowTime, updateTime=nowTime, use_status=1) uid_bucket_id = ub_cqs.id # 开通AI服务 dvq = Device_Info.objects.filter(UID=UID, vodPrimaryUserID='', vodPrimaryMaster='') if dvq.exists(): dvq_set_update_dict = { 'vodPrimaryUserID': userid, 'vodPrimaryMaster': username } dvq.update(**dvq_set_update_dict) orderID = CommonService.createOrderID() store_meal_qs = Store_Meal.objects.filter(id=rank, lang__lang='cn', is_show=0).values('lang__title', 'lang__content') if store_meal_qs.exists(): store_meal_name = store_meal_qs[0]['lang__title'] + '-' + store_meal_qs[0]['lang__content'] else: store_meal_name = '未知套餐' Order_Model.objects.create(orderID=orderID, UID=UID, channel=channel, userID_id=userid, desc=desc, payType=pay_type, payTime=nowTime, price=amount.get('total'), currency=order_qs[0]['currency'], addTime=nowTime, updTime=nowTime, pay_url='', isSelectDiscounts=0, commodity_code=commodity_code, commodity_type=commodity_type, rank_id=rank, paymentID='', coupon_id='', uid_bucket_id=uid_bucket_id, status=1, agreement_id=agreement_id, store_meal_name=store_meal_name, plan_id=plan_id, ai_rank_id=1, trade_no=paypal_transaction_id) # 如果存在序列号,消息提示用序列号 device_name = CommonService.query_serial_with_uid(uid=UID) datetime = time.strftime("%Y-%m-%d", time.localtime()) sys_msg_text_list = [ '温馨提示:尊敬的客户,您的' + device_name + '设备在' + datetime + '已成功续订云存套餐', 'Dear customer,you already subscribed the cloud storage package successfully for device ' + device_name + ' on ' + time.strftime( "%b %dth,%Y", time.localtime())] if pay_type == 1: lang = 'en' else: lang = 'cn' CloudStorage.CloudStorageView().do_vod_msg_notice(UID, channel, userid, lang, sys_msg_text_list, 'SMS_219738485') # 更新agreement billing_agreement_update_attributes = [ { "op": "replace", "path": "/", "value": { "description": orderID, } } ] billing_agreement.replace(billing_agreement_update_attributes) PAY_LOGGER.info('{} PayPal周期扣款成功'.format(UID)) return HttpResponse('success') except Exception as e: PAY_LOGGER.info('PayPal周期扣款异常: errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e))) return HttpResponse('fail', status=500) @staticmethod def config_match_region(uid, country_id): """ 判断用户地区是否跟服务器地区匹配 @param uid: uid @param country_id: 国家表id @return: bool """ country_qs = CountryModel.objects.filter(id=country_id).values('region_id') # 不确定用户地区信息,默认美洲 if not country_qs.exists() and CONFIG_INFO == CONFIG_EUR: PAY_LOGGER.info('PayPal周期扣款失败---不确定地区的用户请求欧洲服,uid:{},country_id:{}'.format(uid, country_id)) return False region_id = country_qs[0]['region_id'] PAY_LOGGER.info('uid{}的用户地区信息: country_id:{}, region_id:{}'.format(uid, country_id, region_id)) if (CONFIG_INFO == CONFIG_US and region_id == 4) or (CONFIG_INFO == CONFIG_EUR and region_id != 4): PAY_LOGGER.info('PayPal周期扣款失败---服务器跟用户地区不匹配') return False return True def do_subscription_break_notify(self, request_dict, request, response): logger = logging.getLogger('pay') logger.info('--------进入订阅失败,付款失败,暂停--------') json_agreement_str = request.body.decode("utf-8") json_obj = json.loads(json_agreement_str) header = request.META paypal_body = json_obj.get('resource') logger.info('----主体信息----') logger.info(json_agreement_str) logger.info('----进入订阅失败头部信息----') logger.info(header) try: transmission_id = header.get('HTTP_PAYPAL_TRANSMISSION_ID', None) transmission_time = header.get('HTTP_PAYPAL_TRANSMISSION_TIME', None) cert_url = header.get('HTTP_PAYPAL_CERT_URL', None) transmission_sig = header.get('HTTP_PAYPAL_TRANSMISSION_SIG', None) auth_algo = header.get('HTTP_PAYPAL_AUTH_ALGO', None) event_type = json_obj.get('event_type') summary = json_obj.get('summary') resource_type = json_obj.get('resource_type') paypal_transaction_id = paypal_body.get('id') amount = paypal_body.get('amount') # self.get_plan_desc('P-4CG284532S612303METMEINY') paypalrestsdk.configure(PAYPAL_CRD) response = paypalrestsdk.WebhookEvent.verify( transmission_id, transmission_time, PAYPAL_WEB_HOOK_ID_TWO, json_agreement_str, cert_url, transmission_sig, auth_algo) logger.info('----验证签名----') logger.info(response) if not response: return HttpResponse('Fail', status=500) event_type_code = 0 billing_agreement_id = '' if event_type == 'PAYMENT.SALE.COMPLETED': event_type_code = 1 billing_agreement_id = paypal_body.get('billing_agreement_id') elif event_type == 'PAYMENT.SALE.REVERSED': billing_agreement_id = paypal_body.get('billing_agreement_id') event_type_code = 2 elif event_type == 'BILLING.SUBSCRIPTION.CANCELLED': billing_agreement_id = paypal_body.get('id') event_type_code = 3 elif event_type == 'BILLING.SUBSCRIPTION.SUSPENDED': billing_agreement_id = paypal_body.get('id') event_type_code = 4 elif event_type == 'BILLING.SUBSCRIPTION.PAYMENT.FAILED': billing_agreement_id = paypal_body.get('id') event_type_code = 5 elif event_type == 'PAYMENT.SALE.REFUNDED': billing_agreement_id = paypal_body.get('billing_agreement_id') event_type_code = 6 PaypalWebHookEventInsert = { 'webhook_event_id': json_obj.get('id'), 'resource_type': resource_type, 'event_type': event_type_code, 'summary': summary, 'trade_no': paypal_transaction_id, 'resource': json_agreement_str, 'created_time': int(time.time()), } if not billing_agreement_id: # 记录钩子日志 PaypalWebHookEvent.objects.create(**PaypalWebHookEventInsert) return HttpResponse('success') billing_agreement = paypalrestsdk.BillingAgreement.find(billing_agreement_id) # 记录钩子日志 PaypalWebHookEventInsert['agreement_desc'] = repr(billing_agreement) PaypalWebHookEventInsert['agreement_id'] = billing_agreement_id PaypalWebHookEventInsert['orderID'] = billing_agreement.description PaypalWebHookEvent.objects.create(**PaypalWebHookEventInsert) return HttpResponse('success') except Exception as e: print(e) logger.info('----进入订阅失败----') logger.info('do_paypal_webhook_notify支付失败:----') logger.info("错误行数:{errLine}".format(errLine=e.__traceback__.tb_lineno)) logger.info(repr(e)) return HttpResponse('fail', status=500) def get_plan_desc(self, plan_id): paypalrestsdk.configure(PAYPAL_CRD) billing_plan = paypalrestsdk.BillingPlan.find(plan_id) print("Got Billing Plan Details for Billing Plan[%s]" % (billing_plan.id)) exit() class payCycle(View): def get(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') return self.validation(request.GET, request, operation) def post(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') return self.validation(request.POST, request, operation) def validation(self, request_dict, request, operation): response = ResponseObject() token = request_dict.get('token', None) # 设备主键uid tko = TokenObject(token) response.lang = tko.lang if tko.code != 0: return response.json(tko.code) userID = tko.userID if operation is None: return response.json(444, 'error path') elif operation == 'queryPayCycle': # paypal成功订阅回调 return self.do_query_pay_cycle(request_dict, userID, response) elif operation == 'cancelPayCycle': # 取消自动续费 return self.do_cancel_pay_cycle(request_dict, userID, response) def do_query_pay_cycle(self, request_dict, userID, response): lang = request_dict.get('lang', 'en') uid = request_dict.get('uid', None) orderObject = Order_Model.objects.filter(userID=userID, status=1, rank__lang__lang=lang).annotate( rank__title=F('rank__lang__title'), rank__content=F('rank__lang__content')) if uid: orderObject = orderObject.filter(UID=uid) orderObject = orderObject.filter(~Q(agreement_id='')) if not orderObject.exists(): return response.json(0, {'data': [], 'count': 0}) orderQuery = orderObject.values("orderID", "UID", "channel", "desc", "price", "currency", "addTime", "updTime", "paypal", "rank__day", "payType", "rank__price", "status", "rank__lang__content", "rank__lang__title", "rank__currency", "rank_id", "rank__expire", "agreement_id").order_by('addTime') new_data = [] values = [] for d in orderQuery: if d['agreement_id'] not in values: new_data.append(d) values.append(d['agreement_id']) count = len(new_data) return response.json(0, {'data': new_data, 'count': count}) def do_cancel_pay_cycle(self, request_dict, userID, response): orderID = request_dict.get('orderID', 'None') orderObject = Order_Model.objects.filter(orderID=orderID) orderObject = orderObject.filter(~Q(agreement_id='')).values("agreement_id") if not orderObject.exists(): return response.json(800) paypalrestsdk.configure(PAYPAL_CRD) BILLING_AGREEMENT_ID = orderObject[0]['agreement_id'] try: billing_agreement = paypalrestsdk.BillingAgreement.find(BILLING_AGREEMENT_ID) if billing_agreement.state != 'Active': Order_Model.objects.filter(agreement_id=BILLING_AGREEMENT_ID).update(agreement_id='') return response.json(0) cancel_note = {"note": "Canceling the agreement"} if billing_agreement.cancel(cancel_note): Order_Model.objects.filter(agreement_id=BILLING_AGREEMENT_ID).update(agreement_id='') return response.json(0) else: return response.json(10052) except Exception as e: return response.json(10052)