import stripe import stripe.error import logging import datetime from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.conf import settings from django.contrib.auth import get_user_model from .models import StripeCustomer, StripeCreditCard CURRENCY = 'chf' stripe.api_key = settings.STRIPE_KEY def handle_stripe_error(f): def handle_problems(*args, **kwargs): response = { 'paid': False, 'response_object': None, 'error': None } common_message = "Currently it is not possible to make payments. Please try agin later." try: response_object = f(*args, **kwargs) return response_object except stripe.error.CardError as e: # Since it's a decline, stripe.error.CardError will be caught body = e.json_body logging.error(str(e)) raise e # For error handling. except stripe.error.RateLimitError: logging.error("Too many requests made to the API too quickly.") raise Exception(common_message) except stripe.error.InvalidRequestError as e: logging.error(str(e)) raise Exception('Invalid parameters.') except stripe.error.AuthenticationError as e: # Authentication with Stripe's API failed # (maybe you changed API keys recently) logging.error(str(e)) raise Exception(common_message) except stripe.error.APIConnectionError as e: logging.error(str(e)) raise Exception(common_message) except stripe.error.StripeError as e: # XXX: maybe send email logging.error(str(e)) raise Exception(common_message) return handle_problems def public_api_key(): return settings.STRIPE_PUBLIC_KEY def get_customer_id_for(user): try: # .get() raise if there is no matching entry. return StripeCustomer.objects.get(owner=user).stripe_id except ObjectDoesNotExist: # No entry yet - making a new one. try: customer = create_customer(user.username, user.email) uncloud_stripe_mapping = StripeCustomer.objects.create( owner=user, stripe_id=customer.id) return uncloud_stripe_mapping.stripe_id except Exception as e: return None @handle_stripe_error def create_setup_intent(customer_id): return stripe.SetupIntent.create(customer=customer_id) @handle_stripe_error def get_setup_intent(setup_intent_id): return stripe.SetupIntent.retrieve(setup_intent_id) def get_payment_method(payment_method_id): return stripe.PaymentMethod.retrieve(payment_method_id) @handle_stripe_error def create_customer(name, email): return stripe.Customer.create(name=name, email=email) @handle_stripe_error def get_customer(customer_id): return stripe.Customer.retrieve(customer_id) @handle_stripe_error def get_customer_cards(customer_id): print(f"getting cards for: {customer_id}") cards = [] stripe_cards = stripe.PaymentMethod.list( customer=customer_id, type="card", ) for stripe_card in stripe_cards["data"]: card = {} card['brand'] = stripe_card["card"]["brand"] card['last4'] = stripe_card["card"]["last4"] card['month'] = stripe_card["card"]["exp_month"] card['year'] = stripe_card["card"]["exp_year"] card['id'] = stripe_card["id"] cards.append(card) return cards def sync_cards_for_user(user): customer_id = get_customer_id_for(user) cards = get_customer_cards(customer_id) active_cards = StripeCreditCard.objects.filter(owner=user, active=True) if len(active_cards) > 0: has_active_card = True else: has_active_card = False for card in cards: active = False if not has_active_card: active = True has_active_card = True StripeCreditCard.objects.get_or_create(card_id=card['id'], owner = user, defaults = { 'last4': card['last4'], 'brand': card['brand'], 'expiry_date': datetime.date(card['year'], card['month'], 1), 'active': active } ) @handle_stripe_error def charge_customer(user, amount, currency='CHF'): # Amount is in CHF but stripes requires smallest possible unit. # https://stripe.com/docs/api/payment_intents/create#create_payment_intent-amount # FIXME: might need to be adjusted for other currencies if currency == 'CHF': adjusted_amount = int(amount * 100) else: return Exception("Programming error: unsupported currency") try: card = StripeCreditCard.objects.get(owner=user, active=True) except StripeCreditCard.DoesNotExist: raise ValidationError("No active credit card - cannot create payment") customer_id = get_customer_id_for(user) return stripe.PaymentIntent.create( amount=adjusted_amount, currency=currency, customer=customer_id, payment_method=card.card_id, off_session=True, confirm=True, )