Many more changes
This commit is contained in:
parent
da54a59ca2
commit
1a76d2b5f3
11 changed files with 386 additions and 283 deletions
371
ucloud_pay.py
371
ucloud_pay.py
|
|
@ -1,5 +1,4 @@
|
|||
import json
|
||||
import time
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
|
|
@ -10,60 +9,27 @@ 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
|
||||
from helper import get_plan_id_from_product, calculate_charges
|
||||
|
||||
|
||||
class ListProducts(Resource):
|
||||
@staticmethod
|
||||
def get():
|
||||
products = client.get_prefix('/v1/products/', value_in_json=False)
|
||||
products = [
|
||||
product
|
||||
for product in [json.loads(p.value) for p in products]
|
||||
if product['active']
|
||||
]
|
||||
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
|
||||
|
|
@ -72,174 +38,170 @@ class ListProducts(Resource):
|
|||
class AddProduct(Resource):
|
||||
@staticmethod
|
||||
def post():
|
||||
data = request.json
|
||||
logger.debug('Got data: {}'.format(str(data)))
|
||||
data = request.get_json(silent=True) or {}
|
||||
|
||||
try:
|
||||
logger.debug('Got data: {}'.format(str(data)))
|
||||
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'}
|
||||
cleaned_values = validator.get_cleaned_values()
|
||||
previous_product = cleaned_values.get('product', None)
|
||||
if previous_product:
|
||||
if not cleaned_values['update']:
|
||||
return make_return_message('Product already exists. Pass --update to update the product.')
|
||||
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
|
||||
|
||||
product_uuid = previous_product.pop('uuid')
|
||||
else:
|
||||
return {'message': 'Wrong Credentials'}, 403
|
||||
product_uuid = uuid4().hex
|
||||
|
||||
product_value = cleaned_values['specs']
|
||||
|
||||
product_key = '/v1/products/{}'.format(product_uuid)
|
||||
product_value['uuid'] = product_uuid
|
||||
|
||||
logger.debug('Adding product data: {}'.format(str(product_value)))
|
||||
client.put(product_key, product_value, value_in_json=True)
|
||||
if not previous_product:
|
||||
return make_return_message('Product created.')
|
||||
else:
|
||||
return make_return_message('Product updated.')
|
||||
|
||||
|
||||
class UserRegisterPayment(Resource):
|
||||
@staticmethod
|
||||
def post():
|
||||
data = request.json
|
||||
logger.debug('Got data: {}'.format(str(data)))
|
||||
data = request.get_json(silent=True) or {}
|
||||
|
||||
try:
|
||||
logger.debug('Got data: {}'.format(str(data)))
|
||||
validator = UserRegisterPaymentSchema(data)
|
||||
validator.is_valid()
|
||||
except ValidationException as err:
|
||||
return make_return_message(err, 400)
|
||||
else:
|
||||
cleaned_values = validator.get_cleaned_values()
|
||||
last4 = data['card_number'].strip()[-4:]
|
||||
|
||||
if ldap_manager.is_password_valid(validator.email, validator.password):
|
||||
stripe_utils = StripeUtils()
|
||||
stripe_utils = StripeUtils()
|
||||
|
||||
# Does customer already exist ?
|
||||
stripe_customer = stripe_utils.get_stripe_customer_from_email(validator.email)
|
||||
# Does customer already exist ?
|
||||
stripe_customer = stripe_utils.get_stripe_customer_from_email(cleaned_values['user']['mail'])
|
||||
|
||||
# Does customer already exist ?
|
||||
if stripe_customer is not None:
|
||||
logger.debug('Customer {} exists already'.format(validator.email))
|
||||
# Does customer already exist ?
|
||||
if stripe_customer is not None:
|
||||
logger.debug('Customer {}-{} exists already'.format(
|
||||
cleaned_values['username'], cleaned_values['user']['mail'])
|
||||
)
|
||||
|
||||
# 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'])
|
||||
# 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)
|
||||
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))
|
||||
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(
|
||||
validator.card_number, validator.cvc, validator.expiry_month,
|
||||
validator.expiry_year
|
||||
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))
|
||||
|
||||
# Create stripe customer
|
||||
stripe_customer_resp = stripe_utils.create_customer(
|
||||
name=validator.card_holder_name,
|
||||
token=token_response['response_object'].id,
|
||||
email=validator.email
|
||||
resp = stripe_utils.associate_customer_card(
|
||||
stripe_customer.id, token_response['response_object'].id
|
||||
)
|
||||
stripe_customer = stripe_customer_resp['response_object']
|
||||
|
||||
if stripe_customer:
|
||||
logger.debug('Created stripe customer {}'.format(stripe_customer.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 card. Contact support', 400)
|
||||
else:
|
||||
return make_return_message('Error with payment gateway. Contact support', 400)
|
||||
else:
|
||||
return make_return_message('Error: {}'.format(ce_response['error']), 400)
|
||||
else:
|
||||
return make_return_message('Wrong Credentials', 403)
|
||||
# Stripe customer does not exist, create a new one
|
||||
logger.debug(
|
||||
'Customer {} does not exist, creating new'.format(cleaned_values['user']['mail'])
|
||||
)
|
||||
token_response = stripe_utils.get_token_from_card(
|
||||
cleaned_values['card_number'], cleaned_values['cvc'],
|
||||
cleaned_values['expiry_month'], cleaned_values['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=cleaned_values['card_holder_name'],
|
||||
token=token_response['response_object'].id,
|
||||
email=cleaned_values['user']['mail'],
|
||||
address=cleaned_values['address']
|
||||
)
|
||||
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)
|
||||
|
||||
|
||||
class ProductOrder(Resource):
|
||||
@staticmethod
|
||||
def post():
|
||||
data = request.json
|
||||
data = request.get_json(silent=True) or {}
|
||||
|
||||
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))
|
||||
cleaned_values = validator.get_cleaned_values()
|
||||
stripe_utils = StripeUtils()
|
||||
|
||||
# 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 = cleaned_values['product']
|
||||
|
||||
product = product.value
|
||||
# Check the user has a payment source added
|
||||
stripe_customer = stripe_utils.get_stripe_customer_from_email(cleaned_values['user']['mail'])
|
||||
|
||||
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')
|
||||
if not stripe_customer or len(stripe_customer.sources) == 0:
|
||||
return make_return_message('Please register your payment method first.', 400)
|
||||
|
||||
logger.debug('Got product {}'.format(product))
|
||||
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.get_cleaned_values()
|
||||
logger.debug('Tranformed data: {}'.format(transformed_data))
|
||||
one_time_charge, recurring_charge = calculate_charges(product, transformed_data)
|
||||
recurring_charge = int(recurring_charge)
|
||||
|
||||
# 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)
|
||||
if not cleaned_values['pay']:
|
||||
return make_return_message(
|
||||
'You would be charged {} CHF one time and {} CHF every {}. '
|
||||
'Add --pay to command to order.'.format(
|
||||
one_time_charge, recurring_charge, product['recurring_period']
|
||||
)
|
||||
)
|
||||
|
||||
with client.client.lock('product-order') as lock:
|
||||
# Initiate a one-time/subscription based on product type
|
||||
if recurring_charge > 0:
|
||||
logger.debug('Product {} is recurring payment'.format(product['name']))
|
||||
|
|
@ -262,13 +224,26 @@ class ProductOrder(Resource):
|
|||
)
|
||||
else:
|
||||
order_obj = {
|
||||
'order_id': get_order_id(),
|
||||
'ordered_at': int(time.time()),
|
||||
'order-id': uuid4().hex,
|
||||
'ordered-at': datetime.now().isoformat(),
|
||||
'product': product['usable-id'],
|
||||
'one-time-price': one_time_charge,
|
||||
'recurring-price': recurring_charge,
|
||||
'recurring-period': product['recurring_period']
|
||||
}
|
||||
client.put(
|
||||
'/v1/user/{}/orders/{}'.format(
|
||||
cleaned_values['username'], order_obj['order-id']
|
||||
),
|
||||
order_obj, value_in_json=True
|
||||
)
|
||||
product['quantity'] -= 1
|
||||
client.put('/v1/products/{}'.format(product['uuid']), product, value_in_json=True)
|
||||
|
||||
return {
|
||||
'message': 'Order Successful.',
|
||||
**order_obj
|
||||
}
|
||||
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))
|
||||
|
||||
|
|
@ -283,63 +258,71 @@ class ProductOrder(Resource):
|
|||
# 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
|
||||
)
|
||||
return make_return_message('Error subscribing to plan. Details: {}'.format(msg), 400)
|
||||
|
||||
order_obj = {
|
||||
'order_id': get_order_id(),
|
||||
'ordered_at': int(time.time()),
|
||||
'order-id': uuid4().hex,
|
||||
'ordered-at': datetime.now().isoformat(),
|
||||
'product': product['usable-id'],
|
||||
'one-time-price': one_time_charge,
|
||||
}
|
||||
client.put(
|
||||
'/v1/user/{}/orders'.format(validator.email),order_obj,
|
||||
value_in_json=True
|
||||
'/v1/user/{}/orders/{}'.format(cleaned_values['username'], order_obj['order-id']),
|
||||
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)
|
||||
product['quantity'] -= 1
|
||||
client.put('/v1/products/{}'.format(product['uuid']), product, value_in_json=True)
|
||||
|
||||
return {'message': 'Order successful', **order_obj}, 200
|
||||
|
||||
|
||||
class OrderList(Resource):
|
||||
@staticmethod
|
||||
def get():
|
||||
data = request.json
|
||||
def post():
|
||||
data = request.get_json(silent=True) or {}
|
||||
|
||||
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)
|
||||
cleaned_values = validator.get_cleaned_values()
|
||||
orders = client.get_prefix(
|
||||
'/v1/user/{}/orders'.format(cleaned_values['username']), value_in_json=True
|
||||
)
|
||||
orders_dict = {
|
||||
order.value['order_id']: {
|
||||
'ordered-at': datetime.fromtimestamp(order.value['ordered_at']).strftime('%c'),
|
||||
'product': order.value['product']
|
||||
order.value['order-id']: {
|
||||
**order.value
|
||||
}
|
||||
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')
|
||||
return {'orders': orders_dict}, 200
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='::', port=config['app']['port'], debug=True)
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.DEBUG)
|
||||
log_formater = logging.Formatter('[%(filename)s:%(lineno)d] %(message)s')
|
||||
|
||||
stream_logger = logging.StreamHandler()
|
||||
stream_logger.setFormatter(log_formater)
|
||||
|
||||
# file_logger = logging.FileHandler('log.txt')
|
||||
# file_logger.setLevel(logging.DEBUG)
|
||||
# file_logger.setFormatter(log_formater)
|
||||
|
||||
logger.addHandler(stream_logger)
|
||||
# logger.addHandler(file_logger)
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
api = Api(app)
|
||||
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')
|
||||
|
||||
app.run(host='::', port=config['app']['port'], debug=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue