def parse_active_membership(user_data: dict) -> Optional[Membership]: if not user_data or not user_data.get("data") or not user_data.get( "included"): return None if user_data["data"]["id"] in settings.PATREON_GOD_IDS: return Membership( platform=Platform.patreon, user_id=user_data["data"]["id"], full_name=user_data["data"]["attributes"]["full_name"], email=user_data["data"]["attributes"]["email"], image=user_data["data"]["attributes"]["image_url"], started_at=datetime.utcnow(), charged_at=None, expires_at=datetime.utcnow() + timedelta(days=100 * 365), lifetime_support_cents=-1, currently_entitled_amount_cents=0) log.info(f"Patreon active membership: {user_data}") for membership in user_data["included"]: if membership["attributes"]["patron_status"] == "active_patron" \ and membership["attributes"]["last_charge_status"] == "Paid": now = datetime.utcnow() membership_started_at = datetime.strptime( str(membership["attributes"] ["pledge_relationship_start"])[:10], "%Y-%m-%d" ) if membership["attributes"]["pledge_relationship_start"] else now last_charged_at = None if membership["attributes"]["last_charge_date"]: last_charged_at = datetime.strptime( str(membership["attributes"]["last_charge_date"])[:10], "%Y-%m-%d") if last_charged_at: membership_expires_at = last_charged_at + timedelta(days=45) else: membership_expires_at = first_day_of_next_month( now) + timedelta(days=15) return Membership( platform=Platform.patreon, user_id=user_data["data"]["id"], full_name=user_data["data"]["attributes"]["full_name"], email=user_data["data"]["attributes"]["email"], image=None, # user_data["data"]["attributes"]["image_url"], started_at=membership_started_at, charged_at=last_charged_at, expires_at=membership_expires_at, lifetime_support_cents=int( membership["attributes"]["lifetime_support_cents"] or 0), currently_entitled_amount_cents=int( membership["attributes"]["currently_entitled_amount_cents"] or 0), ) return None
def patreon_oauth_callback(request): code = request.GET.get("code") if not code: return render( request, "error.html", { "message": "Что-то сломалось между нами и патреоном. Так бывает. Попробуйте залогиниться еще раз." }) try: auth_data = patreon.fetch_auth_data(code) user_data = patreon.fetch_user_data(auth_data["access_token"]) except PatreonException as ex: if "invalid_grant" in str(ex): return render( request, "error.html", { "message": "Тут такое дело. Авторизация патреона — говно. " "Она не сразу понимает, что вы стали моим патроном и отдаёт мне ошибку. " "Я уже написал им в саппорт, но пока вам надо немного подождать и авторизоваться снова. " "Обычно тогда срабатывает. Если нет — напишите мне в личные сообщения на патреоне." }) return render( request, "error.html", { "message": "Не получилось загрузить ваш профиль с серверов патреона. " "Попробуйте еще раз, наверняка оно починится. " f"Но если нет, то вот текст ошибки, с которым можно пожаловаться мне в личку:", "data": str(ex) }) membership = patreon.parse_active_membership(user_data) if not membership: return render( request, "error.html", { "message": "Надо быть патроном чтобы состоять в клубе.<br>" '<a href="https://www.patreon.com/join/vas3k">Станьте им здесь!</a>' }) now = datetime.utcnow() user, is_created = User.objects.get_or_create( membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON, membership_platform_id=membership.user_id, defaults=dict( email=membership.email, full_name=membership.full_name[:120], avatar=upload_image_from_url(membership.image) if membership.image else None, membership_started_at=membership.started_at, membership_expires_at=membership.expires_at, created_at=now, updated_at=now, is_email_verified=False, is_profile_complete=False, # redirect new users to an intro page ), ) if is_created: user.balance = membership.lifetime_support_cents / 100 else: user.membership_expires_at = membership.expires_at user.balance = membership.lifetime_support_cents / 100 # TODO: remove when the real money comes in user.membership_platform_data = { "access_token": auth_data["access_token"], "refresh_token": auth_data["refresh_token"], } user.save() session = Session.objects.create( user=user, token=random_string(length=32), created_at=now, expires_at=first_day_of_next_month(now), ) redirect_to = reverse("profile", args=[user.slug]) state = request.GET.get("state") if state: redirect_to += f"?{state}" response = redirect(redirect_to) response.set_cookie( key="token", value=session.token, max_age=settings.SESSION_COOKIE_AGE, httponly=True, secure=not settings.DEBUG, ) return response