| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 | from Ansjer.config import PAYPAL_CRD,SERVER_DOMAIN,SERVER_DOMAIN_SSLfrom Model.models import PayCycleConfigModel,Order_Model, Store_Meal, UID_Bucket, PromotionRuleModel, Unused_Uid_Meal,Device_Infofrom Service.CommonService import CommonServicefrom django.http import JsonResponse, HttpResponseRedirect, HttpResponseimport requestsimport timefrom Object.ResponseObject import ResponseObjectimport paypalrestsdkfrom paypalrestsdk import BillingAgreementfrom django.views.generic.base import Viewfrom django.db import transactionfrom Controller import CloudStoragefrom django.db.models import Q, F, Countfrom paypalrestsdk.notifications import WebhookEventimport logging#周期扣款相关class Paypal:    def subscriptions(store_info,lang,orderID):        cycle_config = PayCycleConfigModel.objects.filter(id=store_info['cycle_config_id']).values()        if not cycle_config:            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)        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": store_info['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:            print(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:            print(billing_agreement.error)            return Falseclass 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)    def do_paypal_cycle_return(self, request_dict, response):        lang = request_dict.get('lang', 'en')        token = request_dict.get('token',None)        paypalrestsdk.configure(PAYPAL_CRD)        billing_agreement = paypalrestsdk.BillingAgreement()        billing_agreement_response = billing_agreement.execute(token)        if billing_agreement_response.error:            print(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        agreement_id = billing_agreement_response.id        promotion_rule_id = ''        try:            order_qs = Order_Model.objects.filter(orderID=orderID, status=0)            if not orderID:                print("not orderID")                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)            order_list = order_qs.values("UID", "channel", "commodity_code", "rank", "isSelectDiscounts",                                         "userID__userID",                                         "userID__username")            userid = order_list[0]['userID__userID']            username = order_list[0]['userID__username']            UID = order_list[0]['UID']            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():                print("not smqs")                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            # 是否有促销            nowTime = int(time.time())            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)                order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,                                promotion_rule_id=promotion_rule_id,agreement_id=agreement_id)                datetime = time.strftime("%Y-%m-%d", time.localtime())                sys_msg_text_list = ['温馨提示:尊敬的客户,您的' + UID + '设备在' + datetime + '已成功订阅云存套餐',                                     'Dear customer,you already subscribed the cloud storage package successfully for device ' + UID + ' on ' + time.strftime(                                         "%b %dth,%Y", time.localtime())]                CloudStorage.CloudStorageView.do_vod_msg_Notice(self, 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)                return HttpResponseRedirect(red_url)        except Exception as e:            print(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)    def do_paypal_webhook_notify(self, request_dict, request, response):        paymentId = request_dict.get('paymentId', None)        PayerID = request_dict.get('PayerID', None)        lang = request_dict.get('lang', 'en')        token = request_dict.get('token',None)        import json        logger = logging.getLogger('info')        json_str = request.body.decode("utf-8")        logger.info('json_str----------------')        logger.info(json_str)        return response.json(json_str)        paypalrestsdk.configure(PAYPAL_CRD)        # The payload body sent in the webhook event        event_body = '{"id":"WH-0G2756385H040842W-5Y612302CV158622M","create_time":"2015-05-18T15:45:13Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 20.0 USD","resource":{"id":"4EU7004268015634R","create_time":"2015-05-18T15:44:02Z","update_time":"2015-05-18T15:44:21Z","amount":{"total":"20.00","currency":"USD"},"payment_mode":"INSTANT_TRANSFER","state":"completed","protection_eligibility":"ELIGIBLE","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","parent_payment":"PAY-86C81811X5228590KKVNARQQ","transaction_fee":{"value":"0.88","currency":"USD"},"links":[{"href":"https://api.sandbox.paypal.com/v1/payments/sale/4EU7004268015634R","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/payments/sale/4EU7004268015634R/refund","rel":"refund","method":"POST"},{"href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-86C81811X5228590KKVNARQQ","rel":"parent_payment","method":"GET"}]},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-0G2756385H040842W-5Y612302CV158622M","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-0G2756385H040842W-5Y612302CV158622M/resend","rel":"resend","method":"POST"}]}'        # Paypal-Transmission-Id in webhook payload header        transmission_id = "dfb3be50-fd74-11e4-8bf3-77339302725b"        # Paypal-Transmission-Time in webhook payload header        timestamp = "2015-05-18T15:45:13Z"        # Webhook id created        webhook_id = "4JH86294D6297924G"        # Paypal-Transmission-Sig in webhook payload header        actual_signature = "thy4/U002quzxFavHPwbfJGcc46E8rc5jzgyeafWm5mICTBdY/8rl7WJpn8JA0GKA+oDTPsSruqusw+XXg5RLAP7ip53Euh9Xu3UbUhQFX7UgwzE2FeYoY6lyRMiiiQLzy9BvHfIzNIVhPad4KnC339dr6y2l+mN8ALgI4GCdIh3/SoJO5wE64Bh/ueWtt8EVuvsvXfda2Le5a2TrOI9vLEzsm9GS79hAR/5oLexNz8UiZr045Mr5ObroH4w4oNfmkTaDk9Rj0G19uvISs5QzgmBpauKr7Nw++JI0pr/v5mFctQkoWJSGfBGzPRXawrvIIVHQ9Wer48GR2g9ZiApWg=="        # Paypal-Cert-Url in webhook payload header        cert_url = 'https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-a5cafa77'        # PayPal-Auth-Algo in webhook payload header        auth_algo = 'sha256'        response = WebhookEvent.verify(            transmission_id, timestamp, webhook_id, event_body, cert_url, actual_signature, auth_algo)        print(response)
 |