| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  | import re | 
					
						
							|  |  |  | import stripe | 
					
						
							|  |  |  | import stripe.error | 
					
						
							|  |  |  | import logging | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-20 00:12:11 +05:00
										 |  |  | from config import etcd_client as client, config as config | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-20 00:12:11 +05:00
										 |  |  | stripe.api_key = config.get('stripe', 'private_key') | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def handle_stripe_error(f): | 
					
						
							|  |  |  |     def handle_problems(*args, **kwargs): | 
					
						
							|  |  |  |         response = { | 
					
						
							|  |  |  |             'paid': False, | 
					
						
							|  |  |  |             'response_object': None, | 
					
						
							|  |  |  |             'error': None | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         common_message = "Currently it's not possible to make payments." | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             response_object = f(*args, **kwargs) | 
					
						
							|  |  |  |             response = { | 
					
						
							|  |  |  |                 'response_object': response_object, | 
					
						
							|  |  |  |                 'error': None | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return response | 
					
						
							|  |  |  |         except stripe.error.CardError as e: | 
					
						
							|  |  |  |             # Since it's a decline, stripe.error.CardError will be caught | 
					
						
							|  |  |  |             body = e.json_body | 
					
						
							|  |  |  |             err = body['error'] | 
					
						
							|  |  |  |             response.update({'error': err['message']}) | 
					
						
							|  |  |  |             logging.error(str(e)) | 
					
						
							|  |  |  |             return response | 
					
						
							|  |  |  |         except stripe.error.RateLimitError: | 
					
						
							|  |  |  |             response.update( | 
					
						
							|  |  |  |                 {'error': "Too many requests made to the API too quickly"}) | 
					
						
							|  |  |  |             return response | 
					
						
							|  |  |  |         except stripe.error.InvalidRequestError as e: | 
					
						
							|  |  |  |             logging.error(str(e)) | 
					
						
							|  |  |  |             response.update({'error': "Invalid parameters"}) | 
					
						
							|  |  |  |             return response | 
					
						
							|  |  |  |         except stripe.error.AuthenticationError as e: | 
					
						
							|  |  |  |             # Authentication with Stripe's API failed | 
					
						
							|  |  |  |             # (maybe you changed API keys recently) | 
					
						
							|  |  |  |             logging.error(str(e)) | 
					
						
							|  |  |  |             response.update({'error': common_message}) | 
					
						
							|  |  |  |             return response | 
					
						
							|  |  |  |         except stripe.error.APIConnectionError as e: | 
					
						
							|  |  |  |             logging.error(str(e)) | 
					
						
							|  |  |  |             response.update({'error': common_message}) | 
					
						
							|  |  |  |             return response | 
					
						
							|  |  |  |         except stripe.error.StripeError as e: | 
					
						
							|  |  |  |             # maybe send email | 
					
						
							|  |  |  |             logging.error(str(e)) | 
					
						
							|  |  |  |             response.update({'error': common_message}) | 
					
						
							|  |  |  |             return response | 
					
						
							|  |  |  |         except Exception as e: | 
					
						
							|  |  |  |             # maybe send email | 
					
						
							|  |  |  |             logging.error(str(e)) | 
					
						
							|  |  |  |             response.update({'error': common_message}) | 
					
						
							|  |  |  |             return response | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return handle_problems | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class StripeUtils(object): | 
					
						
							|  |  |  |     CURRENCY = 'chf' | 
					
						
							|  |  |  |     INTERVAL = 'month' | 
					
						
							|  |  |  |     SUCCEEDED_STATUS = 'succeeded' | 
					
						
							|  |  |  |     STRIPE_PLAN_ALREADY_EXISTS = 'Plan already exists' | 
					
						
							|  |  |  |     STRIPE_NO_SUCH_PLAN = 'No such plan' | 
					
						
							|  |  |  |     PLAN_EXISTS_ERROR_MSG = 'Plan {} exists already.\nCreating a local StripePlan now.' | 
					
						
							|  |  |  |     PLAN_DOES_NOT_EXIST_ERROR_MSG = 'Plan {} does not exist.' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-20 09:44:30 +01:00
										 |  |  |     def __init__(self, private_key): | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  |         self.stripe = stripe | 
					
						
							| 
									
										
										
										
											2020-02-20 09:44:30 +01:00
										 |  |  |         stripe.api_key = private_key | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def card_exists(self, customer, cc_number, exp_month, exp_year, cvc): | 
					
						
							|  |  |  |         token_obj = stripe.Token.create( | 
					
						
							|  |  |  |             card={ | 
					
						
							|  |  |  |                 'number': cc_number, | 
					
						
							|  |  |  |                 'exp_month': exp_month, | 
					
						
							|  |  |  |                 'exp_year': exp_year, | 
					
						
							|  |  |  |                 'cvc': cvc, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         cards = stripe.Customer.list_sources( | 
					
						
							|  |  |  |             customer, | 
					
						
							|  |  |  |             limit=20, | 
					
						
							|  |  |  |             object='card' | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for card in cards.data: | 
					
						
							|  |  |  |             if (card.fingerprint == token_obj.card.fingerprint and | 
					
						
							|  |  |  |                     int(card.exp_month) == int(exp_month) and int(card.exp_year) == int(exp_year)): | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def get_stripe_customer_from_email(email): | 
					
						
							|  |  |  |         customer = stripe.Customer.list(limit=1, email=email) | 
					
						
							|  |  |  |         return customer.data[0] if len(customer.data) == 1 else None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def update_customer_token(customer, token): | 
					
						
							|  |  |  |         customer.source = token | 
					
						
							|  |  |  |         customer.save() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def get_token_from_card(self, cc_number, cvc, expiry_month, expiry_year): | 
					
						
							|  |  |  |         token_obj = stripe.Token.create( | 
					
						
							|  |  |  |             card={ | 
					
						
							|  |  |  |                 'number': cc_number, | 
					
						
							|  |  |  |                 'exp_month': expiry_month, | 
					
						
							|  |  |  |                 'exp_year': expiry_year, | 
					
						
							|  |  |  |                 'cvc': cvc, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         return token_obj | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def associate_customer_card(self, stripe_customer_id, token, | 
					
						
							|  |  |  |                                 set_as_default=False): | 
					
						
							|  |  |  |         customer = stripe.Customer.retrieve(stripe_customer_id) | 
					
						
							|  |  |  |         card = customer.sources.create(source=token) | 
					
						
							|  |  |  |         if set_as_default: | 
					
						
							|  |  |  |             customer.default_source = card.id | 
					
						
							|  |  |  |             customer.save() | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def dissociate_customer_card(self, stripe_customer_id, card_id): | 
					
						
							|  |  |  |         customer = stripe.Customer.retrieve(stripe_customer_id) | 
					
						
							|  |  |  |         card = customer.sources.retrieve(card_id) | 
					
						
							|  |  |  |         card.delete() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def update_customer_card(self, customer_id, token): | 
					
						
							|  |  |  |         customer = stripe.Customer.retrieve(customer_id) | 
					
						
							|  |  |  |         current_card_token = customer.default_source | 
					
						
							|  |  |  |         customer.sources.retrieve(current_card_token).delete() | 
					
						
							|  |  |  |         customer.source = token | 
					
						
							|  |  |  |         customer.save() | 
					
						
							|  |  |  |         credit_card_raw_data = customer.sources.data.pop() | 
					
						
							|  |  |  |         new_card_data = { | 
					
						
							|  |  |  |             'last4': credit_card_raw_data.last4, | 
					
						
							|  |  |  |             'brand': credit_card_raw_data.brand | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return new_card_data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def get_card_details(self, customer_id): | 
					
						
							|  |  |  |         customer = stripe.Customer.retrieve(customer_id) | 
					
						
							|  |  |  |         credit_card_raw_data = customer.sources.data.pop() | 
					
						
							|  |  |  |         card_details = { | 
					
						
							|  |  |  |             'last4': credit_card_raw_data.last4, | 
					
						
							|  |  |  |             'brand': credit_card_raw_data.brand, | 
					
						
							|  |  |  |             'exp_month': credit_card_raw_data.exp_month, | 
					
						
							|  |  |  |             'exp_year': credit_card_raw_data.exp_year, | 
					
						
							|  |  |  |             'fingerprint': credit_card_raw_data.fingerprint, | 
					
						
							|  |  |  |             'card_id': credit_card_raw_data.id | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return card_details | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def get_all_invoices(self, customer_id, created_gt): | 
					
						
							|  |  |  |         return_list = [] | 
					
						
							|  |  |  |         has_more_invoices = True | 
					
						
							|  |  |  |         starting_after = False | 
					
						
							|  |  |  |         while has_more_invoices: | 
					
						
							|  |  |  |             if starting_after: | 
					
						
							|  |  |  |                 invoices = stripe.Invoice.list( | 
					
						
							|  |  |  |                     limit=10, customer=customer_id, created={'gt': created_gt}, | 
					
						
							|  |  |  |                     starting_after=starting_after | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 invoices = stripe.Invoice.list( | 
					
						
							|  |  |  |                     limit=10, customer=customer_id, created={'gt': created_gt} | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             has_more_invoices = invoices.has_more | 
					
						
							|  |  |  |             for invoice in invoices.data: | 
					
						
							|  |  |  |                 sub_ids = [] | 
					
						
							|  |  |  |                 for line in invoice.lines.data: | 
					
						
							|  |  |  |                     if line.type == 'subscription': | 
					
						
							|  |  |  |                         sub_ids.append(line.id) | 
					
						
							|  |  |  |                     elif line.type == 'invoiceitem': | 
					
						
							|  |  |  |                         sub_ids.append(line.subscription) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         sub_ids.append('') | 
					
						
							|  |  |  |                 invoice_details = { | 
					
						
							|  |  |  |                     'created': invoice.created, | 
					
						
							|  |  |  |                     'receipt_number': invoice.receipt_number, | 
					
						
							|  |  |  |                     'invoice_number': invoice.number, | 
					
						
							|  |  |  |                     'paid_at': invoice.status_transitions.paid_at if invoice.paid else 0, | 
					
						
							|  |  |  |                     'period_start': invoice.period_start, | 
					
						
							|  |  |  |                     'period_end': invoice.period_end, | 
					
						
							|  |  |  |                     'billing_reason': invoice.billing_reason, | 
					
						
							|  |  |  |                     'discount': invoice.discount.coupon.amount_off if invoice.discount else 0, | 
					
						
							|  |  |  |                     'total': invoice.total, | 
					
						
							|  |  |  |                     # to see how many line items we have in this invoice and | 
					
						
							|  |  |  |                     # then later check if we have more than 1 | 
					
						
							|  |  |  |                     'lines_data_count': len(invoice.lines.data) if invoice.lines.data is not None else 0, | 
					
						
							|  |  |  |                     'invoice_id': invoice.id, | 
					
						
							|  |  |  |                     'lines_meta_data_csv': ','.join( | 
					
						
							|  |  |  |                         [line.metadata.VM_ID if hasattr(line.metadata, 'VM_ID') else '' for line in invoice.lines.data] | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     'subscription_ids_csv': ','.join(sub_ids), | 
					
						
							|  |  |  |                     'line_items': invoice.lines.data | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 starting_after = invoice.id | 
					
						
							|  |  |  |                 return_list.append(invoice_details) | 
					
						
							|  |  |  |         return return_list | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def get_cards_details_from_token(self, token): | 
					
						
							|  |  |  |         stripe_token = stripe.Token.retrieve(token) | 
					
						
							|  |  |  |         card_details = { | 
					
						
							|  |  |  |             'last4': stripe_token.card.last4, | 
					
						
							|  |  |  |             'brand': stripe_token.card.brand, | 
					
						
							|  |  |  |             'exp_month': stripe_token.card.exp_month, | 
					
						
							|  |  |  |             'exp_year': stripe_token.card.exp_year, | 
					
						
							|  |  |  |             'fingerprint': stripe_token.card.fingerprint, | 
					
						
							|  |  |  |             'card_id': stripe_token.card.id | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return card_details | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_customer(self, stripe_cus_api_id, user, token): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             customer = stripe.Customer.retrieve(stripe_cus_api_id) | 
					
						
							|  |  |  |         except stripe.error.InvalidRequestError: | 
					
						
							|  |  |  |             customer = self.create_customer(token, user.email, user.name) | 
					
						
							|  |  |  |             user.stripecustomer.stripe_id = customer.get( | 
					
						
							|  |  |  |                 'response_object').get('id') | 
					
						
							|  |  |  |             user.stripecustomer.save() | 
					
						
							|  |  |  |         if type(customer) is dict: | 
					
						
							|  |  |  |             customer = customer['response_object'] | 
					
						
							|  |  |  |         return customer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def get_customer(self, stripe_api_cus_id): | 
					
						
							|  |  |  |         customer = stripe.Customer.retrieve(stripe_api_cus_id) | 
					
						
							|  |  |  |         # data = customer.get('response_object') | 
					
						
							|  |  |  |         return customer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							| 
									
										
										
										
											2020-01-27 13:40:57 +05:00
										 |  |  |     def create_customer(self, token, email, name=None, address=None): | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  |         if name is None or name.strip() == "": | 
					
						
							|  |  |  |             name = email | 
					
						
							|  |  |  |         customer = self.stripe.Customer.create( | 
					
						
							|  |  |  |             source=token, | 
					
						
							|  |  |  |             description=name, | 
					
						
							| 
									
										
										
										
											2020-01-27 13:40:57 +05:00
										 |  |  |             email=email, | 
					
						
							|  |  |  |             address=address | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  |         ) | 
					
						
							|  |  |  |         return customer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def make_charge(self, amount=None, customer=None): | 
					
						
							|  |  |  |         _amount = float(amount) | 
					
						
							|  |  |  |         amount = int(_amount * 100)  # stripe amount unit, in cents | 
					
						
							|  |  |  |         charge = self.stripe.Charge.create( | 
					
						
							|  |  |  |             amount=amount,  # in cents | 
					
						
							|  |  |  |             currency=self.CURRENCY, | 
					
						
							|  |  |  |             customer=customer | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         return charge | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _get_all_stripe_plans(): | 
					
						
							|  |  |  |         all_stripe_plans = client.get("/v1/stripe_plans") | 
					
						
							|  |  |  |         all_stripe_plans_set = set() | 
					
						
							|  |  |  |         if all_stripe_plans: | 
					
						
							| 
									
										
										
										
											2020-01-27 14:55:26 +05:00
										 |  |  |             all_stripe_plans_obj = all_stripe_plans.value | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  |             if all_stripe_plans_obj and len(all_stripe_plans_obj['plans']) > 0: | 
					
						
							|  |  |  |                 all_stripe_plans_set = set(all_stripe_plans_obj["plans"]) | 
					
						
							|  |  |  |         return all_stripe_plans_set | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _save_all_stripe_plans(stripe_plans): | 
					
						
							| 
									
										
										
										
											2020-01-27 14:55:26 +05:00
										 |  |  |         client.put("/v1/stripe_plans", {"plans": list(stripe_plans)}) | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def get_or_create_stripe_plan(self, product_name, amount, stripe_plan_id, | 
					
						
							|  |  |  |                                   interval=INTERVAL): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         This function checks if a StripePlan with the given | 
					
						
							|  |  |  |         stripe_plan_id already exists. If it exists then the function | 
					
						
							|  |  |  |         returns this object otherwise it creates a new StripePlan and | 
					
						
							|  |  |  |         returns the new object. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :param amount: The amount in CHF cents | 
					
						
							| 
									
										
										
										
											2020-02-19 11:59:54 +05:00
										 |  |  |         :param product_name: The name of the Stripe plan (product) to be created. | 
					
						
							| 
									
										
										
										
											2020-01-20 12:30:12 +05:00
										 |  |  |         :param stripe_plan_id: The id of the Stripe plan to be | 
					
						
							|  |  |  |                created. Use get_stripe_plan_id_string function to | 
					
						
							|  |  |  |                obtain the name of the plan to be created | 
					
						
							|  |  |  |         :param interval: The interval for subscription {month, year}. Defaults | 
					
						
							|  |  |  |                to month if not provided | 
					
						
							|  |  |  |         :return: The StripePlan object if it exists else creates a | 
					
						
							|  |  |  |                Plan object in Stripe and a local StripePlan and | 
					
						
							|  |  |  |                returns it. Returns None in case of Stripe error | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         _amount = float(amount) | 
					
						
							|  |  |  |         amount = int(_amount * 100)  # stripe amount unit, in cents | 
					
						
							|  |  |  |         all_stripe_plans = self._get_all_stripe_plans() | 
					
						
							|  |  |  |         if stripe_plan_id in all_stripe_plans: | 
					
						
							|  |  |  |             logging.debug("{} plan exists in db.".format(stripe_plan_id)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             logging.debug(("{} plan DOES NOT exist in db. " | 
					
						
							|  |  |  |                           "Creating").format(stripe_plan_id)) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 plan_obj = self.stripe.Plan.retrieve(id=stripe_plan_id) | 
					
						
							|  |  |  |                 logging.debug("{} plan exists in Stripe".format(stripe_plan_id)) | 
					
						
							|  |  |  |                 all_stripe_plans.add(stripe_plan_id) | 
					
						
							|  |  |  |             except stripe.error.InvalidRequestError as e: | 
					
						
							|  |  |  |                 if "No such plan" in str(e): | 
					
						
							|  |  |  |                     logging.debug("Plan {} does not exist in Stripe, Creating") | 
					
						
							|  |  |  |                     plan_obj = self.stripe.Plan.create( | 
					
						
							|  |  |  |                         amount=amount, | 
					
						
							|  |  |  |                         product={'name': product_name}, | 
					
						
							|  |  |  |                         interval=interval, | 
					
						
							|  |  |  |                         currency=self.CURRENCY, | 
					
						
							|  |  |  |                         id=stripe_plan_id) | 
					
						
							|  |  |  |                     logging.debug(self.PLAN_EXISTS_ERROR_MSG.format(stripe_plan_id)) | 
					
						
							|  |  |  |                     all_stripe_plans.add(stripe_plan_id) | 
					
						
							|  |  |  |             self._save_all_stripe_plans(all_stripe_plans) | 
					
						
							|  |  |  |         return stripe_plan_id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def delete_stripe_plan(self, stripe_plan_id): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Deletes the Plan in Stripe and also deletes the local db copy | 
					
						
							|  |  |  |         of the plan if it exists | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :param stripe_plan_id: The stripe plan id that needs to be | 
					
						
							|  |  |  |                deleted | 
					
						
							|  |  |  |         :return: True if the plan was deleted successfully from | 
					
						
							|  |  |  |                Stripe, False otherwise. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return_value = False | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             plan = self.stripe.Plan.retrieve(stripe_plan_id) | 
					
						
							|  |  |  |             plan.delete() | 
					
						
							|  |  |  |             return_value = True | 
					
						
							|  |  |  |             all_stripe_plans = self._get_all_stripe_plans() | 
					
						
							|  |  |  |             all_stripe_plans.remove(stripe_plan_id) | 
					
						
							|  |  |  |             self._save_all_stripe_plans(all_stripe_plans) | 
					
						
							|  |  |  |         except stripe.error.InvalidRequestError as e: | 
					
						
							|  |  |  |             if self.STRIPE_NO_SUCH_PLAN in str(e): | 
					
						
							|  |  |  |                 logging.debug( | 
					
						
							|  |  |  |                     self.PLAN_DOES_NOT_EXIST_ERROR_MSG.format(stripe_plan_id)) | 
					
						
							|  |  |  |         return return_value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def subscribe_customer_to_plan(self, customer, plans, trial_end=None): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Subscribes the given customer to the list of given plans | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :param customer: The stripe customer identifier | 
					
						
							|  |  |  |         :param plans: A list of stripe plans. | 
					
						
							|  |  |  |         :param trial_end: An integer representing when the Stripe subscription | 
					
						
							|  |  |  |                is supposed to end | 
					
						
							|  |  |  |         Ref: https://stripe.com/docs/api/python#create_subscription-items | 
					
						
							|  |  |  |               e.g. | 
					
						
							|  |  |  |                     plans = [ | 
					
						
							|  |  |  |                                 { | 
					
						
							|  |  |  |                                   "plan": "dcl-v1-cpu-2-ram-5gb-ssd-10gb", | 
					
						
							|  |  |  |                                 }, | 
					
						
							|  |  |  |                             ] | 
					
						
							|  |  |  |         :return: The subscription StripeObject | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         subscription_result = self.stripe.Subscription.create( | 
					
						
							|  |  |  |             customer=customer, items=plans, trial_end=trial_end | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         return subscription_result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def set_subscription_metadata(self, subscription_id, metadata): | 
					
						
							|  |  |  |         subscription = stripe.Subscription.retrieve(subscription_id) | 
					
						
							|  |  |  |         subscription.metadata = metadata | 
					
						
							|  |  |  |         subscription.save() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def unsubscribe_customer(self, subscription_id): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Cancels a given subscription | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :param subscription_id: The Stripe subscription id string | 
					
						
							|  |  |  |         :return: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         sub = stripe.Subscription.retrieve(subscription_id) | 
					
						
							|  |  |  |         return sub.delete() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def make_payment(self, customer, amount, token): | 
					
						
							|  |  |  |         charge = self.stripe.Charge.create( | 
					
						
							|  |  |  |             amount=amount,  # in cents | 
					
						
							|  |  |  |             currency=self.CURRENCY, | 
					
						
							|  |  |  |             customer=customer | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         return charge | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None, | 
					
						
							|  |  |  |                            price=None): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Returns the Stripe plan id string of the form | 
					
						
							|  |  |  |         `dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :param cpu: The number of cores | 
					
						
							|  |  |  |         :param ram: The size of the RAM in GB | 
					
						
							|  |  |  |         :param ssd: The size of ssd storage in GB | 
					
						
							|  |  |  |         :param hdd: The size of hdd storage in GB | 
					
						
							|  |  |  |         :param version: The version of the Stripe plans | 
					
						
							|  |  |  |         :param app: The application to which the stripe plan belongs | 
					
						
							|  |  |  |         to. By default it is 'dcl' | 
					
						
							|  |  |  |         :param price: The price for this plan | 
					
						
							|  |  |  |         :return: A string of the form `dcl-v1-cpu-2-ram-5gb-ssd-10gb` | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         dcl_plan_string = 'cpu-{cpu}-ram-{ram}gb-ssd-{ssd}gb'.format(cpu=cpu, | 
					
						
							|  |  |  |                                                                      ram=ram, | 
					
						
							|  |  |  |                                                                      ssd=ssd) | 
					
						
							|  |  |  |         if hdd is not None: | 
					
						
							|  |  |  |             dcl_plan_string = '{dcl_plan_string}-hdd-{hdd}gb'.format( | 
					
						
							|  |  |  |                 dcl_plan_string=dcl_plan_string, hdd=hdd) | 
					
						
							|  |  |  |         stripe_plan_id_string = '{app}-v{version}-{plan}'.format( | 
					
						
							|  |  |  |             app=app, | 
					
						
							|  |  |  |             version=version, | 
					
						
							|  |  |  |             plan=dcl_plan_string | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         if price is not None: | 
					
						
							|  |  |  |             stripe_plan_id_string_with_price = '{}-{}chf'.format( | 
					
						
							|  |  |  |                 stripe_plan_id_string, | 
					
						
							|  |  |  |                 round(price, 2) | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             return stripe_plan_id_string_with_price | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return stripe_plan_id_string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def get_vm_config_from_stripe_id(stripe_id): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Given a string like "dcl-v1-cpu-2-ram-5gb-ssd-10gb" return different | 
					
						
							|  |  |  |         configuration params as a dict | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         :param stripe_id|str | 
					
						
							|  |  |  |         :return: dict | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         pattern = re.compile(r'^dcl-v(\d+)-cpu-(\d+)-ram-(\d+\.?\d*)gb-ssd-(\d+)gb-?(\d*\.?\d*)(chf)?$') | 
					
						
							|  |  |  |         match_res = pattern.match(stripe_id) | 
					
						
							|  |  |  |         if match_res is not None: | 
					
						
							|  |  |  |             price = None | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 price = match_res.group(5) | 
					
						
							|  |  |  |             except IndexError: | 
					
						
							|  |  |  |                 logging.debug("Did not find price in {}".format(stripe_id)) | 
					
						
							|  |  |  |             return { | 
					
						
							|  |  |  |                 'version': match_res.group(1), | 
					
						
							|  |  |  |                 'cores': match_res.group(2), | 
					
						
							|  |  |  |                 'ram': match_res.group(3), | 
					
						
							|  |  |  |                 'ssd': match_res.group(4), | 
					
						
							|  |  |  |                 'price': price | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def get_stripe_plan_name(cpu, memory, disk_size, price): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Returns the Stripe plan name | 
					
						
							|  |  |  |         :return: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD, " \ | 
					
						
							|  |  |  |                "{price} CHF".format( | 
					
						
							|  |  |  |                     cpu=cpu, | 
					
						
							|  |  |  |                     memory=memory, | 
					
						
							|  |  |  |                     disk_size=disk_size, | 
					
						
							|  |  |  |                     price=round(price, 2) | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @handle_stripe_error | 
					
						
							|  |  |  |     def set_subscription_meta_data(self, subscription_id, meta_data): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Adds VM metadata to a subscription | 
					
						
							|  |  |  |         :param subscription_id: Stripe identifier for the subscription | 
					
						
							|  |  |  |         :param meta_data: A dict of meta data to be added | 
					
						
							|  |  |  |         :return: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         subscription = stripe.Subscription.retrieve(subscription_id) | 
					
						
							|  |  |  |         subscription.metadata = meta_data | 
					
						
							|  |  |  |         subscription.save() |