From 0b81762350e7bb9d096ab240a5318cd0c829fe34 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 30 Jan 2018 11:49:25 -0800 Subject: [PATCH] billing: Move most Stripe code to its own file. We'll handle the error-handling in a separate commit, as it's still entangled with the view function. --- zilencer/lib/__init__.py | 0 zilencer/lib/stripe.py | 63 ++++++++++++++++++++++++++++++++++++++++ zilencer/views.py | 61 ++------------------------------------ 3 files changed, 66 insertions(+), 58 deletions(-) create mode 100644 zilencer/lib/__init__.py create mode 100644 zilencer/lib/stripe.py diff --git a/zilencer/lib/__init__.py b/zilencer/lib/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zilencer/lib/stripe.py b/zilencer/lib/stripe.py new file mode 100644 index 0000000000..9c141d0084 --- /dev/null +++ b/zilencer/lib/stripe.py @@ -0,0 +1,63 @@ + +import logging +import os + +from django.conf import settings +import stripe +from stripe.error import CardError, RateLimitError, InvalidRequestError, \ + AuthenticationError, APIConnectionError, StripeError + +from zerver.lib.logging_util import log_to_file +from zerver.models import Realm, UserProfile +from zilencer.models import Customer +from zproject.settings import get_secret + +STRIPE_SECRET_KEY = get_secret('stripe_secret_key') +STRIPE_PUBLISHABLE_KEY = get_secret('stripe_publishable_key') +stripe.api_key = STRIPE_SECRET_KEY + +BILLING_LOG_PATH = os.path.join('/var/log/zulip' + if not settings.DEVELOPMENT + else settings.DEVELOPMENT_LOG_DIRECTORY, + 'billing.log') +billing_logger = logging.getLogger('zilencer.stripe') +log_to_file(billing_logger, BILLING_LOG_PATH) +log_to_file(logging.getLogger('stripe'), BILLING_LOG_PATH) + +def count_stripe_cards(realm: Realm) -> int: + try: + customer_obj = Customer.objects.get(realm=realm) + cards = stripe.Customer.retrieve(customer_obj.stripe_customer_id).sources.all(object="card") + return len(cards["data"]) + except Customer.DoesNotExist: + return 0 + +def save_stripe_token(user: UserProfile, token: str) -> int: + """Returns total number of cards.""" + # The card metadata doesn't show up in Dashboard but can be accessed + # using the API. + card_metadata = {"added_user_id": user.id, "added_user_email": user.email} + try: + customer_obj = Customer.objects.get(realm=user.realm) + customer = stripe.Customer.retrieve(customer_obj.stripe_customer_id) + billing_logger.info("Adding card on customer %s: source=%r, metadata=%r", + customer_obj.stripe_customer_id, token, card_metadata) + card = customer.sources.create(source=token, metadata=card_metadata) + customer.default_source = card.id + customer.save() + return len(customer.sources.all(object="card")["data"]) + except Customer.DoesNotExist: + customer_metadata = {"string_id": user.realm.string_id} + # Description makes it easier to identify customers in Stripe dashboard + description = "{} ({})".format(user.realm.name, user.realm.string_id) + billing_logger.info("Creating customer: source=%r, description=%r, metadata=%r", + token, description, customer_metadata) + customer = stripe.Customer.create(source=token, + description=description, + metadata=customer_metadata) + + card = customer.sources.all(object="card")["data"][0] + card.metadata = card_metadata + card.save() + Customer.objects.create(realm=user.realm, stripe_customer_id=customer.id) + return 1 diff --git a/zilencer/views.py b/zilencer/views.py index 9b05d4a1eb..2f95bb18b2 100644 --- a/zilencer/views.py +++ b/zilencer/views.py @@ -1,6 +1,4 @@ -import logging -import os from typing import Any, Dict, Optional, Text, Union, cast from django.http import HttpRequest, HttpResponse @@ -10,13 +8,9 @@ from django.shortcuts import render from django.conf import settings from django.views.decorators.http import require_GET from django.views.decorators.csrf import csrf_exempt -import stripe -from stripe.error import CardError, RateLimitError, InvalidRequestError, \ - AuthenticationError, APIConnectionError, StripeError from zerver.decorator import require_post, zulip_login_required from zerver.lib.exceptions import JsonableError -from zerver.lib.logging_util import log_to_file from zerver.lib.push_notifications import send_android_push_notification, \ send_apple_push_notification from zerver.lib.request import REQ, has_request_variables @@ -24,20 +18,9 @@ from zerver.lib.response import json_error, json_success from zerver.lib.validator import check_int from zerver.models import UserProfile, Realm from zerver.views.push_notifications import validate_token -from zilencer.models import RemotePushDeviceToken, RemoteZulipServer, Customer -from zproject.settings import get_secret - -STRIPE_SECRET_KEY = get_secret('stripe_secret_key') -STRIPE_PUBLISHABLE_KEY = get_secret('stripe_publishable_key') -stripe.api_key = STRIPE_SECRET_KEY - -BILLING_LOG_PATH = os.path.join('/var/log/zulip' - if not settings.DEVELOPMENT - else settings.DEVELOPMENT_LOG_DIRECTORY, - 'billing.log') -billing_logger = logging.getLogger('zilencer.stripe') -log_to_file(billing_logger, BILLING_LOG_PATH) -log_to_file(logging.getLogger('stripe'), BILLING_LOG_PATH) +from zilencer.lib.stripe import STRIPE_PUBLISHABLE_KEY, count_stripe_cards, \ + save_stripe_token, CardError, StripeError, billing_logger +from zilencer.models import RemotePushDeviceToken, RemoteZulipServer def validate_entity(entity: Union[UserProfile, RemoteZulipServer]) -> None: if not isinstance(entity, RemoteZulipServer): @@ -121,44 +104,6 @@ def remote_server_notify_push(request: HttpRequest, entity: Union[UserProfile, R return json_success() -def count_stripe_cards(realm: Realm) -> int: - try: - customer_obj = Customer.objects.get(realm=realm) - cards = stripe.Customer.retrieve(customer_obj.stripe_customer_id).sources.all(object="card") - return len(cards["data"]) - except Customer.DoesNotExist: - return 0 - -def save_stripe_token(user: UserProfile, token: str) -> int: - """Returns total number of cards.""" - # The card metadata doesn't show up in Dashboard but can be accessed - # using the API. - card_metadata = {"added_user_id": user.id, "added_user_email": user.email} - try: - customer_obj = Customer.objects.get(realm=user.realm) - customer = stripe.Customer.retrieve(customer_obj.stripe_customer_id) - billing_logger.info("Adding card on customer %s: source=%r, metadata=%r", - customer_obj.stripe_customer_id, token, card_metadata) - card = customer.sources.create(source=token, metadata=card_metadata) - customer.default_source = card.id - customer.save() - return len(customer.sources.all(object="card")["data"]) - except Customer.DoesNotExist: - customer_metadata = {"string_id": user.realm.string_id} - # Description makes it easier to identify customers in Stripe dashboard - description = "{} ({})".format(user.realm.name, user.realm.string_id) - billing_logger.info("Creating customer: source=%r, description=%r, metadata=%r", - token, description, customer_metadata) - customer = stripe.Customer.create(source=token, - description=description, - metadata=customer_metadata) - - card = customer.sources.all(object="card")["data"][0] - card.metadata = card_metadata - card.save() - Customer.objects.create(realm=user.realm, stripe_customer_id=customer.id) - return 1 - @zulip_login_required def add_payment_method(request: HttpRequest) -> HttpResponse: user = request.user