Example #1
0
def get_subscription_info(request: AuthedHttpRequest,
                          team_id: str) -> JsonResponse:
    account = get_account_or_404(team_id=team_id, user=request.user)

    subscription_status = account.get_subscription_blocker()

    if subscription_status is not None:
        if subscription_status.kind == "trial_expired":
            return JsonResponse({"type": "TRIAL_EXPIRED"})

        if subscription_status.kind == "seats_exceeded":
            stripe_info = account.stripe_customer_info()
            license_count = 0
            if stripe_info and stripe_info.subscription_quantity:
                license_count = stripe_info.subscription_quantity
            active_user_count: int = len(
                UserPullRequestActivity.get_active_users_in_last_30_days(
                    account=account))
            return JsonResponse({
                "type": "SUBSCRIPTION_OVERAGE",
                "activeUserCount": active_user_count,
                "licenseCount": license_count,
            })

        if subscription_status.kind == "subscription_expired":
            return JsonResponse({"type": "SUBSCRIPTION_EXPIRED"})

    return JsonResponse({"type": "VALID_SUBSCRIPTION"})
Example #2
0
def start_checkout(request: AuthedHttpRequest, team_id: str) -> HttpResponse:
    payload = StartCheckoutModal.parse_obj(request.POST.dict())
    account = get_account_or_404(team_id=team_id, user=request.user)
    # if available, using the existing customer_id allows us to pre-fill the
    # checkout form with their email.
    customer_id = account.stripe_customer_id or None

    # https://stripe.com/docs/api/checkout/sessions/create
    session = stripe.checkout.Session.create(
        client_reference_id=account.id,
        customer=customer_id,
        # cards only work with subscriptions and StripeCustomerInformation
        # depends on credit cards
        # (payment_method_card_{brand,exp_month,exp_year,last4}).
        payment_method_types=["card"],
        subscription_data={
            "items": [{
                "plan": plan_id_from_period(period=payload.planPeriod),
                "quantity": payload.seatCount,
            }],
        },
        success_url=
        f"{settings.KODIAK_WEB_APP_URL}/t/{account.id}/usage?install_complete=1",
        cancel_url=
        f"{settings.KODIAK_WEB_APP_URL}/t/{account.id}/usage?start_subscription=1",
    )
    return JsonResponse(
        dict(
            stripeCheckoutSessionId=session.id,
            stripePublishableApiKey=settings.STRIPE_PUBLISHABLE_API_KEY,
        ))
Example #3
0
def accounts(request: AuthedHttpRequest) -> HttpResponse:
    return JsonResponse([
        dict(id=x.id,
             name=x.github_account_login,
             profileImgUrl=x.profile_image())
        for x in Account.objects.filter(
            memberships__user=request.user).order_by("github_account_login")
    ], )
Example #4
0
def oauth_complete(request: HttpRequest) -> HttpResponse:
    """
    OAuth callback handler from GitHub.
    We get a code from GitHub that we can use with our client secret to get an
    OAuth token for a GitHub user. The GitHub OAuth token only expires when the
    user uninstalls the app.
    https://developer.github.com/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps/#2-users-are-redirected-back-to-your-site-by-github
    """
    if request.method == "POST":
        login_result = process_login_request(request)
        return JsonResponse(asdict(login_result))
    return HttpResponse()
Example #5
0
def activity(request: AuthedHttpRequest, team_id: str) -> HttpResponse:
    account = get_account_or_404(team_id=team_id, user=request.user)
    kodiak_activity_labels = []
    kodiak_activity_approved = []
    kodiak_activity_merged = []
    kodiak_activity_updated = []

    total_labels = []
    total_opened = []
    total_merged = []
    total_closed = []
    for day_activity in PullRequestActivity.objects.filter(
            github_installation_id=account.github_installation_id).order_by(
                "date"):
        kodiak_activity_labels.append(day_activity.date)
        kodiak_activity_approved.append(day_activity.kodiak_approved)
        kodiak_activity_merged.append(day_activity.kodiak_merged)
        kodiak_activity_updated.append(day_activity.kodiak_updated)
        total_labels.append(day_activity.date)
        total_opened.append(day_activity.total_opened)
        total_merged.append(day_activity.total_merged)
        total_closed.append(day_activity.total_closed)

    active_merge_queues = [
        dict(owner=repo.owner, repo=repo.repo, queues=queues)
        for repo, queues in get_active_merge_queues(
            install_id=str(account.github_installation_id)).items()
    ]

    return JsonResponse(
        dict(
            kodiakActivity=dict(
                labels=kodiak_activity_labels,
                datasets=dict(
                    approved=kodiak_activity_approved,
                    merged=kodiak_activity_merged,
                    updated=kodiak_activity_updated,
                ),
            ),
            pullRequestActivity=dict(
                labels=total_labels,
                datasets=dict(opened=total_opened,
                              merged=total_merged,
                              closed=total_closed),
            ),
            activeMergeQueues=active_merge_queues,
        ))
Example #6
0
def fetch_proration(request: AuthedHttpRequest, team_id: str) -> HttpResponse:
    payload = FetchProrationModal.parse_obj(request.POST.dict())
    account = get_account_or_404(user=request.user, team_id=team_id)

    stripe_plan_id = plan_id_from_period(period=payload.subscriptionPeriod)

    customer_info = account.stripe_customer_info()
    if customer_info is not None:
        proration_date = int(time.time())
        return JsonResponse(
            dict(
                proratedCost=customer_info.preview_proration(
                    timestamp=proration_date,
                    subscription_quantity=payload.subscriptionQuantity,
                    plan_id=stripe_plan_id,
                ),
                prorationTime=proration_date,
            ))
    return HttpResponse(status=500)
Example #7
0
def modify_payment_details(request: AuthedHttpRequest,
                           team_id: str) -> HttpResponse:
    account = get_account_or_404(team_id=team_id, user=request.user)
    if not request.user.can_edit_subscription(account):
        raise PermissionDenied
    session = stripe.checkout.Session.create(
        client_reference_id=account.id,
        customer=account.stripe_customer_id or None,
        mode="setup",
        payment_method_types=["card"],
        success_url=
        f"{settings.KODIAK_WEB_APP_URL}/t/{account.id}/usage?install_complete=1",
        cancel_url=
        f"{settings.KODIAK_WEB_APP_URL}/t/{account.id}/usage?modify_subscription=1",
    )
    return JsonResponse(
        dict(
            stripeCheckoutSessionId=session.id,
            stripePublishableApiKey=settings.STRIPE_PUBLISHABLE_API_KEY,
        ))
Example #8
0
def current_account(request: AuthedHttpRequest, team_id: str) -> HttpResponse:
    account = get_account_or_404(team_id=team_id, user=request.user)
    return JsonResponse(
        dict(
            user=dict(
                id=request.user.id,
                name=request.user.github_login,
                profileImgUrl=request.user.profile_image(),
            ),
            org=dict(
                id=account.id,
                name=account.github_account_login,
                profileImgUrl=account.profile_image(),
            ),
            accounts=[
                dict(
                    id=x.id,
                    name=x.github_account_login,
                    profileImgUrl=x.profile_image(),
                )
                for x in Account.objects.filter(memberships__user=request.user)
            ],
        ))
Example #9
0
def sync_accounts(request: AuthedHttpRequest) -> HttpResponse:
    try:
        request.user.sync_accounts()
    except SyncAccountsError:
        return JsonResponse(dict(ok=False))
    return JsonResponse(dict(ok=True))
Example #10
0
def usage_billing(request: AuthedHttpRequest, team_id: str) -> HttpResponse:
    account = get_account_or_404(user=request.user, team_id=team_id)
    active_users = UserPullRequestActivity.get_active_users_in_last_30_days(
        account)
    subscription = None
    trial = None
    if account.trial_start and account.trial_expiration and account.trial_started_by:
        trial = dict(
            startDate=account.trial_start,
            endDate=account.trial_expiration,
            expired=account.trial_expired(),
            startedBy=dict(
                id=account.trial_started_by.id,
                name=account.trial_started_by.github_login,
                profileImgUrl=account.trial_started_by.profile_image(),
            ),
        )
    stripe_customer_info = account.stripe_customer_info()
    if stripe_customer_info:
        customer_address = None
        if stripe_customer_info.customer_address_line1 is not None:
            customer_address = dict(
                line1=stripe_customer_info.customer_address_line1,
                city=stripe_customer_info.customer_address_city,
                country=stripe_customer_info.customer_address_country,
                line2=stripe_customer_info.customer_address_line2,
                postalCode=stripe_customer_info.customer_address_postal_code,
                state=stripe_customer_info.customer_address_state,
            )
        plan_interval = ("year" if stripe_customer_info.plan_interval == "year"
                         else "month")
        brand_title = (stripe_customer_info.payment_method_card_brand.title()
                       if stripe_customer_info.payment_method_card_brand
                       is not None else None)
        subscription = dict(
            seats=stripe_customer_info.subscription_quantity,
            nextBillingDate=stripe_customer_info.next_billing_date,
            expired=stripe_customer_info.expired,
            cost=dict(
                totalCents=stripe_customer_info.plan_amount *
                stripe_customer_info.subscription_quantity,
                perSeatCents=stripe_customer_info.plan_amount,
                currency=stripe_customer_info.customer_currency
                or DEFAULT_CURRENCY,
                planInterval=plan_interval,
            ),
            billingEmail=stripe_customer_info.customer_email,
            contactEmails=account.contact_emails,
            customerName=stripe_customer_info.customer_name,
            customerAddress=customer_address,
            cardInfo=
            f"{brand_title} ({stripe_customer_info.payment_method_card_last4})",
            viewerIsOrgOwner=request.user.is_admin(account),
            viewerCanModify=request.user.can_edit_subscription(account),
            limitBillingAccessToOwners=account.limit_billing_access_to_owners,
        )

    subscription_quantity = (stripe_customer_info.subscription_quantity
                             if stripe_customer_info else 0)
    # we assign seats to users in order of first active for the last 30 days.
    allowed_user_ids = {
        user.github_id
        for user in sorted(active_users, key=lambda x: x.first_active_at)
        [:subscription_quantity]
    }
    active_user_with_license_info = [
        dict(
            id=active_user.github_id,
            name=active_user.github_login,
            profileImgUrl=active_user.profile_image(),
            interactions=active_user.days_active,
            lastActiveDate=active_user.last_active_at.isoformat(),
            firstActiveDate=active_user.first_active_at.isoformat(),
            hasSeatLicense=(active_user.github_id in allowed_user_ids),
        ) for active_user in active_users
    ]
    subscription_exemption = None

    if account.subscription_exempt:
        subscription_exemption = dict(
            message=account.subscription_exempt_message)

    return JsonResponse(
        dict(
            accountCanSubscribe=account.can_subscribe(),
            subscription=subscription,
            trial=trial,
            activeUsers=active_user_with_license_info,
            subscriptionExemption=subscription_exemption,
        ))
Example #11
0
def healthcheck(request: HttpRequest) -> HttpResponse:
    return JsonResponse({"ok": True})
Example #12
0
def ping(request: HttpRequest) -> HttpResponse:
    return JsonResponse({"ok": True})