initial commit
This commit is contained in:
		
				commit
				
					
						da54a59ca2
					
				
			
		
					 13 changed files with 1318 additions and 0 deletions
				
			
		
							
								
								
									
										345
									
								
								ucloud_pay.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								ucloud_pay.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,345 @@
 | 
			
		|||
import json
 | 
			
		||||
import time
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from uuid import uuid4
 | 
			
		||||
 | 
			
		||||
from flask import Flask, request
 | 
			
		||||
from flask_restful import Resource, Api
 | 
			
		||||
 | 
			
		||||
from config import etcd_client as client, config as config
 | 
			
		||||
from stripe_utils import StripeUtils
 | 
			
		||||
from ldap_manager import LdapManager
 | 
			
		||||
from schemas import (
 | 
			
		||||
    make_return_message, ValidationException, UserRegisterPaymentSchema,
 | 
			
		||||
    AddProductSchema, ProductOrderSchema, OrderListSchema, create_schema
 | 
			
		||||
)
 | 
			
		||||
from helper import (
 | 
			
		||||
    get_plan_id_from_product, get_user_friendly_product, get_order_id,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger()
 | 
			
		||||
logger.setLevel(logging.DEBUG)
 | 
			
		||||
log_formater = logging.Formatter('[%(filename)s:%(lineno)d] %(message)s')
 | 
			
		||||
 | 
			
		||||
stream_logger = logging.StreamHandler()
 | 
			
		||||
stream_logger.setLevel(logging.DEBUG)
 | 
			
		||||
stream_logger.setFormatter(log_formater)
 | 
			
		||||
 | 
			
		||||
logger.addHandler(stream_logger)
 | 
			
		||||
 | 
			
		||||
app = Flask(__name__)
 | 
			
		||||
api = Api(app)
 | 
			
		||||
INIT_ORDER_ID = 0
 | 
			
		||||
 | 
			
		||||
ldap_manager = LdapManager(server=config['ldap']['server'], admin_dn=config['ldap']['admin_dn'],
 | 
			
		||||
                           admin_password=config['ldap']['admin_password'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def calculate_charges(specification, data):
 | 
			
		||||
    one_time_charge = 0
 | 
			
		||||
    recurring_charge = 0
 | 
			
		||||
    for feature_name, feature_detail in specification['features'].items():
 | 
			
		||||
        if feature_detail['constant']:
 | 
			
		||||
            data[feature_name] = 1
 | 
			
		||||
 | 
			
		||||
        if feature_detail['unit']['type'] != 'str':
 | 
			
		||||
            one_time_charge += feature_detail['one_time_fee']
 | 
			
		||||
            recurring_charge += (
 | 
			
		||||
                    feature_detail['price_per_unit_per_period'] * data[feature_name] /
 | 
			
		||||
                    feature_detail['unit']['value']
 | 
			
		||||
            )
 | 
			
		||||
    return one_time_charge, recurring_charge
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListProducts(Resource):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get():
 | 
			
		||||
        products = client.get_prefix('/v1/products/', value_in_json=False)
 | 
			
		||||
        prod_dict = {}
 | 
			
		||||
        for p in products:
 | 
			
		||||
            p = json.loads(p.value)
 | 
			
		||||
            prod_dict[p['usable-id']] = {
 | 
			
		||||
                'name': p['name'],
 | 
			
		||||
                'description': p['description'],
 | 
			
		||||
                'active': p['active']
 | 
			
		||||
            }
 | 
			
		||||
        logger.debug('Products = {}'.format(prod_dict))
 | 
			
		||||
        return prod_dict, 200
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AddProduct(Resource):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def post():
 | 
			
		||||
        data = request.json
 | 
			
		||||
        logger.debug('Got data: {}'.format(str(data)))
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            validator = AddProductSchema(data)
 | 
			
		||||
            validator.is_valid()
 | 
			
		||||
        except ValidationException as err:
 | 
			
		||||
            return make_return_message(err, 400)
 | 
			
		||||
        else:
 | 
			
		||||
            if ldap_manager.is_password_valid(data['email'], data['password']):
 | 
			
		||||
                try:
 | 
			
		||||
                    user = ldap_manager.get('(mail={})'.format(data['email']))[0]
 | 
			
		||||
                    user = json.loads(user.entry_to_json())
 | 
			
		||||
                    uid, ou, *dc = user['dn'].replace('ou=', '').replace('dc=', '').replace('uid=', '').split(',')
 | 
			
		||||
                except Exception as err:
 | 
			
		||||
                    logger.error(str(err))
 | 
			
		||||
                    return {'message': 'No such user exists'}
 | 
			
		||||
                else:
 | 
			
		||||
                    if ou != config['ldap']['internal_user_ou']:
 | 
			
		||||
                        logger.error('User (email=%s) does not have access to create product', validator.email)
 | 
			
		||||
                        return {'message': 'Forbidden'}, 403
 | 
			
		||||
                    else:
 | 
			
		||||
                        product_uuid = uuid4().hex
 | 
			
		||||
                        product_key = '/v1/products/{}'.format(product_uuid)
 | 
			
		||||
                        product_value = validator.specs
 | 
			
		||||
                        product_value['uuid'] = product_uuid
 | 
			
		||||
 | 
			
		||||
                        logger.debug('Adding product data: {}'.format(str(product_value)))
 | 
			
		||||
                        client.put(product_key, product_value, value_in_json=True)
 | 
			
		||||
                        return {'message': 'Product created'}, 200
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                return {'message': 'Wrong Credentials'}, 403
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserRegisterPayment(Resource):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def post():
 | 
			
		||||
        data = request.json
 | 
			
		||||
        logger.debug('Got data: {}'.format(str(data)))
 | 
			
		||||
        try:
 | 
			
		||||
            validator = UserRegisterPaymentSchema(data)
 | 
			
		||||
            validator.is_valid()
 | 
			
		||||
        except ValidationException as err:
 | 
			
		||||
            return make_return_message(err, 400)
 | 
			
		||||
        else:
 | 
			
		||||
            last4 = data['card_number'].strip()[-4:]
 | 
			
		||||
 | 
			
		||||
            if ldap_manager.is_password_valid(validator.email, validator.password):
 | 
			
		||||
                stripe_utils = StripeUtils()
 | 
			
		||||
 | 
			
		||||
                # Does customer already exist ?
 | 
			
		||||
                stripe_customer = stripe_utils.get_stripe_customer_from_email(validator.email)
 | 
			
		||||
 | 
			
		||||
                # Does customer already exist ?
 | 
			
		||||
                if stripe_customer is not None:
 | 
			
		||||
                    logger.debug('Customer {} exists already'.format(validator.email))
 | 
			
		||||
 | 
			
		||||
                    # Check if the card already exists
 | 
			
		||||
                    ce_response = stripe_utils.card_exists(
 | 
			
		||||
                            stripe_customer.id, cc_number=data['card_number'],
 | 
			
		||||
                            exp_month=int(data['expiry_month']),
 | 
			
		||||
                            exp_year=int(data['expiry_year']),
 | 
			
		||||
                            cvc=data['cvc'])
 | 
			
		||||
 | 
			
		||||
                    if ce_response['response_object']:
 | 
			
		||||
                        message = 'The given card ending in {} exists already.'.format(last4)
 | 
			
		||||
                        return make_return_message(message, 400)
 | 
			
		||||
 | 
			
		||||
                    elif ce_response['response_object'] is False:
 | 
			
		||||
                        # Associate card with user
 | 
			
		||||
                        logger.debug('Adding card ending in {}'.format(last4))
 | 
			
		||||
                        token_response = stripe_utils.get_token_from_card(
 | 
			
		||||
                            data['card_number'], data['cvc'], data['expiry_month'],
 | 
			
		||||
                            data['expiry_year']
 | 
			
		||||
                        )
 | 
			
		||||
                        if token_response['response_object']:
 | 
			
		||||
                            logger.debug('Token {}'.format(token_response['response_object'].id))
 | 
			
		||||
                            resp = stripe_utils.associate_customer_card(
 | 
			
		||||
                                stripe_customer.id, token_response['response_object'].id
 | 
			
		||||
                            )
 | 
			
		||||
                            if resp['response_object']:
 | 
			
		||||
                                return make_return_message(
 | 
			
		||||
                                    'Card ending in {} registered as your payment source'.format(last4)
 | 
			
		||||
                                )
 | 
			
		||||
                        else:
 | 
			
		||||
                            return make_return_message('Error with payment gateway. Contact support', 400)
 | 
			
		||||
                    else:
 | 
			
		||||
                        return make_return_message('Error: {}'.format(ce_response['error']), 400)
 | 
			
		||||
                else:
 | 
			
		||||
                    # Stripe customer does not exist, create a new one
 | 
			
		||||
                    logger.debug('Customer {} does not exist, creating new'.format(validator.email))
 | 
			
		||||
                    token_response = stripe_utils.get_token_from_card(
 | 
			
		||||
                        validator.card_number, validator.cvc, validator.expiry_month,
 | 
			
		||||
                        validator.expiry_year
 | 
			
		||||
                    )
 | 
			
		||||
                    if token_response['response_object']:
 | 
			
		||||
                        logger.debug('Token {}'.format(token_response['response_object'].id))
 | 
			
		||||
 | 
			
		||||
                        # Create stripe customer
 | 
			
		||||
                        stripe_customer_resp = stripe_utils.create_customer(
 | 
			
		||||
                            name=validator.card_holder_name,
 | 
			
		||||
                            token=token_response['response_object'].id,
 | 
			
		||||
                            email=validator.email
 | 
			
		||||
                        )
 | 
			
		||||
                        stripe_customer = stripe_customer_resp['response_object']
 | 
			
		||||
 | 
			
		||||
                        if stripe_customer:
 | 
			
		||||
                            logger.debug('Created stripe customer {}'.format(stripe_customer.id))
 | 
			
		||||
                            return make_return_message(
 | 
			
		||||
                                'Card ending in {} registered as your payment source'.format(last4)
 | 
			
		||||
                            )
 | 
			
		||||
                        else:
 | 
			
		||||
                            return make_return_message('Error with card. Contact support', 400)
 | 
			
		||||
                    else:
 | 
			
		||||
                        return make_return_message('Error with payment gateway. Contact support', 400)
 | 
			
		||||
            else:
 | 
			
		||||
                return make_return_message('Wrong Credentials', 403)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProductOrder(Resource):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def post():
 | 
			
		||||
        data = request.json
 | 
			
		||||
        try:
 | 
			
		||||
            validator = ProductOrderSchema(data)
 | 
			
		||||
            validator.is_valid()
 | 
			
		||||
        except ValidationException as err:
 | 
			
		||||
            return make_return_message(err, 400)
 | 
			
		||||
        else:
 | 
			
		||||
            if ldap_manager.is_password_valid(validator.email, validator.password):
 | 
			
		||||
                stripe_utils = StripeUtils()
 | 
			
		||||
                logger.debug('Product ID = {}'.format(validator.product_id))
 | 
			
		||||
 | 
			
		||||
                # Validate the given product is ok
 | 
			
		||||
                product = client.get('/v1/products/{}'.format(validator.product_id), value_in_json=True)
 | 
			
		||||
                if not product:
 | 
			
		||||
                    return make_return_message('Invalid Product', 400)
 | 
			
		||||
 | 
			
		||||
                product = product.value
 | 
			
		||||
 | 
			
		||||
                customer_previous_orders = client.get_prefix(
 | 
			
		||||
                    '/v1/user/{}'.format(validator.email), value_in_json=True
 | 
			
		||||
                )
 | 
			
		||||
                membership = next(filter(lambda o: o.value['product'] == 'membership', customer_previous_orders), None)
 | 
			
		||||
                if membership is None and data['product_id'] != 'membership':
 | 
			
		||||
                    return make_return_message('Please buy membership first to use this facility')
 | 
			
		||||
 | 
			
		||||
                logger.debug('Got product {}'.format(product))
 | 
			
		||||
 | 
			
		||||
                # Check the user has a payment source added
 | 
			
		||||
                stripe_customer = stripe_utils.get_stripe_customer_from_email(validator.email)
 | 
			
		||||
 | 
			
		||||
                if not stripe_customer or len(stripe_customer.sources) == 0:
 | 
			
		||||
                    return make_return_message('Please register first.', 400)
 | 
			
		||||
 | 
			
		||||
                try:
 | 
			
		||||
                    product_schema = create_schema(product, data)
 | 
			
		||||
                    product_schema = product_schema()
 | 
			
		||||
                    product_schema.is_valid()
 | 
			
		||||
                except ValidationException as err:
 | 
			
		||||
                    return make_return_message(err, 400)
 | 
			
		||||
                else:
 | 
			
		||||
                    transformed_data = product_schema.return_data()
 | 
			
		||||
                    logger.debug('Tranformed data: {}'.format(transformed_data))
 | 
			
		||||
                    one_time_charge, recurring_charge = calculate_charges(product, transformed_data)
 | 
			
		||||
                    recurring_charge = int(recurring_charge)
 | 
			
		||||
 | 
			
		||||
                    # Initiate a one-time/subscription based on product type
 | 
			
		||||
                    if recurring_charge > 0:
 | 
			
		||||
                        logger.debug('Product {} is recurring payment'.format(product['name']))
 | 
			
		||||
                        plan_id = get_plan_id_from_product(product)
 | 
			
		||||
                        res = stripe_utils.get_or_create_stripe_plan(
 | 
			
		||||
                            product_name=product['name'],
 | 
			
		||||
                            stripe_plan_id=plan_id, amount=recurring_charge,
 | 
			
		||||
                            interval=product['recurring_period'],
 | 
			
		||||
                        )
 | 
			
		||||
                        if res['response_object']:
 | 
			
		||||
                            logger.debug('Obtained plan {}'.format(plan_id))
 | 
			
		||||
                            subscription_res = stripe_utils.subscribe_customer_to_plan(
 | 
			
		||||
                                stripe_customer.id,
 | 
			
		||||
                                [{'plan': plan_id}]
 | 
			
		||||
                            )
 | 
			
		||||
                            subscription_obj = subscription_res['response_object']
 | 
			
		||||
                            if subscription_obj is None or subscription_obj.status != 'active':
 | 
			
		||||
                                return make_return_message(
 | 
			
		||||
                                    'Error subscribing to plan. Detail: {}'.format(subscription_res['error']), 400
 | 
			
		||||
                                )
 | 
			
		||||
                            else:
 | 
			
		||||
                                order_obj = {
 | 
			
		||||
                                    'order_id': get_order_id(),
 | 
			
		||||
                                    'ordered_at': int(time.time()),
 | 
			
		||||
                                    'product': product['usable-id'],
 | 
			
		||||
                                }
 | 
			
		||||
                                client.put('/v1/user/{}/orders'.format(validator.email), order_obj, value_in_json=True)
 | 
			
		||||
                                order_obj['ordered_at'] = datetime.fromtimestamp(order_obj['ordered_at']).strftime('%c')
 | 
			
		||||
                                return make_return_message('Order Successful. Order Details: {}'.format(order_obj))
 | 
			
		||||
                        else:
 | 
			
		||||
                            logger.error('Could not create plan {}'.format(plan_id))
 | 
			
		||||
 | 
			
		||||
                    elif recurring_charge == 0 and one_time_charge > 0:
 | 
			
		||||
                        logger.debug('Product {} is one-time payment'.format(product['name']))
 | 
			
		||||
                        charge_response = stripe_utils.make_charge(
 | 
			
		||||
                            amount=one_time_charge,
 | 
			
		||||
                            customer=stripe_customer.id
 | 
			
		||||
                        )
 | 
			
		||||
                        stripe_onetime_charge = charge_response.get('response_object')
 | 
			
		||||
 | 
			
		||||
                        # Check if the payment was approved
 | 
			
		||||
                        if not stripe_onetime_charge:
 | 
			
		||||
                            msg = charge_response.get('error')
 | 
			
		||||
                            return make_return_message(
 | 
			
		||||
                                'Error subscribing to plan. Details: {}'.format(msg), 400
 | 
			
		||||
                            )
 | 
			
		||||
 | 
			
		||||
                        order_obj = {
 | 
			
		||||
                            'order_id': get_order_id(),
 | 
			
		||||
                            'ordered_at': int(time.time()),
 | 
			
		||||
                            'product': product['usable-id'],
 | 
			
		||||
                        }
 | 
			
		||||
                        client.put(
 | 
			
		||||
                            '/v1/user/{}/orders'.format(validator.email),order_obj,
 | 
			
		||||
                            value_in_json=True
 | 
			
		||||
                        )
 | 
			
		||||
                        order_obj['ordered_at'] = datetime.fromtimestamp(order_obj['ordered_at']).strftime('%c')
 | 
			
		||||
                        return {'message': 'Order successful', 'order_details': order_obj}, 200
 | 
			
		||||
            else:
 | 
			
		||||
                return make_return_message('Wrong Credentials', 400)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OrderList(Resource):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get():
 | 
			
		||||
        data = request.json
 | 
			
		||||
        try:
 | 
			
		||||
            validator = OrderListSchema(data)
 | 
			
		||||
            validator.is_valid()
 | 
			
		||||
        except ValidationException as err:
 | 
			
		||||
            return make_return_message(err, 400)
 | 
			
		||||
        else:
 | 
			
		||||
            print(validator.email, validator.password)
 | 
			
		||||
            if not ldap_manager.is_password_valid(validator.email, validator.password):
 | 
			
		||||
                return {'message': 'Wrong Credentials'}, 403
 | 
			
		||||
 | 
			
		||||
            orders = client.get_prefix('/v1/user/{}/orders'.format(validator.email), value_in_json=True)
 | 
			
		||||
            orders_dict = {
 | 
			
		||||
                order.value['order_id']: {
 | 
			
		||||
                    'ordered-at': datetime.fromtimestamp(order.value['ordered_at']).strftime('%c'),
 | 
			
		||||
                    'product': order.value['product']
 | 
			
		||||
                }
 | 
			
		||||
                for order in orders
 | 
			
		||||
            }
 | 
			
		||||
            # for p in orders:
 | 
			
		||||
            #     order_dict = p.value
 | 
			
		||||
            #     order_dict['ordered_at'] = datetime.fromtimestamp(
 | 
			
		||||
            #         order_dict['ordered_at']).strftime('%c')
 | 
			
		||||
            #     order_dict['product'] = order_dict['product']['name']
 | 
			
		||||
            #     orders_dict[order_dict['order_id']] = order_dict
 | 
			
		||||
            logger.debug('Orders = {}'.format(orders_dict))
 | 
			
		||||
            return orders_dict, 200
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
api.add_resource(ListProducts, '/product/list')
 | 
			
		||||
api.add_resource(AddProduct, '/product/add')
 | 
			
		||||
api.add_resource(ProductOrder, '/product/order')
 | 
			
		||||
api.add_resource(UserRegisterPayment, '/user/register_payment')
 | 
			
		||||
api.add_resource(OrderList, '/order/list')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    app.run(host='::', port=config['app']['port'], debug=True)
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue