From f13f7cd567907af24921f3d36ec57f174038a6e5 Mon Sep 17 00:00:00 2001 From: Steve Nyemba Date: Thu, 26 Dec 2019 16:56:16 -0600 Subject: [PATCH] bug fixes --- src/api/index.py | 223 +++++++++++++++++++++------------- src/api/static/css/dialog.css | 10 ++ src/api/static/js/plans.js | 7 ++ src/api/store.py | 17 ++- src/api/templates/plans.html | 213 +++++++++++++++++++++++++------- src/api/templates/signup.html | 82 ++++++++++--- 6 files changed, 404 insertions(+), 148 deletions(-) create mode 100644 src/api/static/css/dialog.css create mode 100644 src/api/static/js/plans.js diff --git a/src/api/index.py b/src/api/index.py index 3c5b3ab..d3c32f7 100755 --- a/src/api/index.py +++ b/src/api/index.py @@ -10,11 +10,12 @@ The store must be prefixed with an authentication module, because customers must be signed in before making a purchase. """ from __future__ import division -from flask import Flask, request, session, render_template,Response +from flask import Flask, request, session, render_template +from flask_session import Session from flask_cors import CORS -# import Domain -# from User import User -# from couchdbkit import Server, Document +from flask_pymongo import PyMongo +from flask_mongo_sessions import MongoDBSessionInterface + import stripe import json # from StringIO import StringIO @@ -25,6 +26,9 @@ from transport import factory # from store import Store import store from datetime import datetime +import numpy as np +import requests + # from utils.transport import Couchdb, CouchdbReader PORT = 8100 if 'port' not in PARAMS else int(PARAMS['port']) ; path = PARAMS['path'] #os.environ['CONFIG'] @@ -41,9 +45,8 @@ stripe.api_key = stripe_keys['secret_key'].strip() app = Flask(__name__) -# CORS(app) -# COUCHDB = Server(uri=CONFIG['couchdb']['uri']) ; -# SYS_STORE = CONFIG['couchdb'] +CORS(app) + SYS_STORE = CONFIG['store'] @app.route("/") def index(): @@ -66,18 +69,67 @@ def get_plans_json(id): # Log this shit or not pass return "[]",HEADER + +@app.route("/store",methods=['POST','PUT']) +def to_session(): + """ + This function will store/persist session variables for reuse + """ + info = request.json + if info : + for key in info : + session[key] = info[key] + return "{}" @app.route("/signup",methods=['GET','POST']) -def get_stripe(): +def signup(): + """ + This function will signup a user to a plan or upgrade them if necessary + The assumption here is one product - one plan + :request.form stripeToken (optional) + """ if request.method == 'GET' : + if 'auth' not in session : + return "0",403 _object = {"data-key":CONFIG['stripe']['pub'].strip()} - return json.dumps(_object) + return json.dumps(_object),{"Content-Type":"application/json"} else: - print (request.args) - return 1 + # + # We are receiving a call from stripe framework and we need create a charge for this + # @NOTE: https://stripe.com/docs/payments/accept-a-payment-charges#web-create-charge + token = request.form['stripeToken'] if 'stripeToken' in request.form else None + email = session['auth']['user']['uid'] + plan_id = session['plan']['id'] + id = session['product']['name'] + mystore = store.factory.instance(name=id,email=email) + user = mystore.user + plans = user.plan.info() + plans = [plans] if isinstance(plans,dict) else plans + + has_plan = [1 for item in plans if item['id'] == plan_id] + if np.sum(has_plan) == 0 and len(plans) == 0: + + mystore.subscribe(id=plan_id,email=email) + elif np.sum(has_plan)==0 and len(plans) > 0: + # + # We should upgrade/downgrade the current plan + # + mystore.plan.upgrade(plan_id,email) + uri = session['product']['metadata']['uri'] if 'uri' in session['product']['metadata'] else '#status' + + # + # @NOTE: + # This function assumes one product one plan, operations outside of this scope will require a new function + return """ + + """.replace(":uri",uri) @app.route("/ui/signup/",methods=['GET','PUT']) -def signup(id): +def signup_form(id): """ + This function loads the signup form with cloud-view authentication support + This allows us to know who is signing up or upgrading their plan """ mystore = store.factory.instance(name=id) @@ -92,6 +144,10 @@ def signup(id): return render_template("signup.html",**args) @app.route("/ui/") def get_plans_ui(id): + """ + This function loads the form for signup and information about the various plans for a given product. + :id name of the products + """ mystore = store.factory.instance(name=id) # # sort plans by @@ -102,7 +158,28 @@ def get_plans_ui(id): args['theme'] = 'theme-clouds' # print (mystore.product.keys()) session['cloud-view'] = CONFIG['cloud-view'] + session['product'] = mystore.product return render_template('plans.html',**args) +@app.route("/goto/",methods=['POST','GET']) +def redirect(id ) : + """ + This function will redirect to a given product page given a payload. The payload is intended to set the a session + :info + """ + + mystore = store.factory.instance(name=id) + args = {"context":CONTEXT,"product":mystore.product} + HEADER = {"Location":mystore.product['metadata']['uri']} + info = request.json + if info : + session['auth'] = info['auth'] + session['plan'] = info['plan'] + session['product'] = mystore.product + return "1",HEADER + else: + + return "0",403 + pass @app.route("/init/",methods=['POST']) def init(product): """ @@ -116,34 +193,7 @@ def init(product): # uid = request.headers['uid'] plan_id = request.headers['pid'] if 'pid' in request.headers else None - # - # We should just pull the factory method and get a storage handler to handle the logs - # - # store = dict(CONFIG['couchdb']) - # store['dbname'] = product - # user = User(stripe=stripe,store=store,product=product) - # user.subscribe(uid,plan_id) - - # sub = None - # if 'auid' in request.headers : - # auid = request.headers['auid'] - # user.update(emails=auid) - # user.post() - - - # store = dict(CONFIG['couchdb'],**{}) - # store['dbname'] = product - # store['uid'] = 'logs' - - # sub = user.get(uid,'subscriptions') - # features = {} - # for id in sub : - # if sub[id]['active'] is True : - # features[id] = sub[id] - - # user.refresh(uid) - # session['key'] = user.user_key - # session['uid'] = uid + mystore = store.factory.instance(name=product,email=uid) @@ -153,54 +203,56 @@ def init(product): mystore.plan.subscribe(email = user.info()['email']) - features = user.plan.info()['metadata']['features'] if 'features' in user.plan.info()['metadata'] else {} - print (features) + features = user.plan.info()['metadata']['features'] if 'features' in user.plan.info()['metadata'] else {} + # + #@TODO: Log that uid, has initiated use of product + # return json.dumps(features),200 -@app.route('/subscribe/',methods=['POST']) -def subscribe(product): - """ - This function subscribes a user to a given service for an application - This function guarantees not to duplicate subscriptions - @resource name name of the application {cloud-music} - @header key service/plan - """ +# @app.route('/subscribe/',methods=['POST']) +# def subscribe(product): +# """ +# This function subscribes a user to a given service for an application +# This function guarantees not to duplicate subscriptions +# @resource name name of the application {cloud-music} +# @header key service/plan +# """ - # - # The name is the full name of the service - # - resp = "0" - user = User(stripe=stripe,store=CONFIG['couchdb'],product=product) - if 'stripeToken' in request.form : - stripeToken = request.form['stripeToken'] - uid = request.form['stripeEmail'] - tokenType = request.form['stripeTokenType'] - amount = request.form['amount'] - pid = request.form['plan'] +# # +# # The name is the full name of the service +# # +# resp = "0" +# user = User(stripe=stripe,store=CONFIG['couchdb'],product=product) +# if 'stripeToken' in request.form : +# stripeToken = request.form['stripeToken'] +# uid = request.form['stripeEmail'] +# tokenType = request.form['stripeTokenType'] +# amount = request.form['amount'] +# pid = request.form['plan'] - else: - pid = request.headers['pid'] if 'pid' in request.headers else None - uid = request.headers['uid'] - stripeToken = None - user.subscribe(uid,pid,stripeToken) - if 'auid' in request.headers : +# else: +# pid = request.headers['pid'] if 'pid' in request.headers else None +# uid = request.headers['uid'] +# stripeToken = None +# user.subscribe(uid,pid,stripeToken) +# if 'auid' in request.headers : - if request.headers['auid'].startswith('[') or request.headers['auid'].startswith("{") : - auid = json.loads(request.headers['auid']) - else: - auid = [request.headers['auid']] - user.update(emails=auid) - user.refresh(uid) +# if request.headers['auid'].startswith('[') or request.headers['auid'].startswith("{") : +# auid = json.loads(request.headers['auid']) +# else: +# auid = [request.headers['auid']] +# user.update(emails=auid) +# user.refresh(uid) - reader = CouchdbReader(uri=SYS_STORE['uri'],dbname=product,uid=uid,create=False) - plans = reader.view('users/active_plan',key=user.user_key) #me['_id']) - #session['plans'] = plans - session['key'] = user.user_key - session['uid'] = uid - session['active-plans'] = [item['id'] for item in plans] - print (session['active-plans']) - return (json.dumps(plans),200) +# reader = CouchdbReader(uri=SYS_STORE['uri'],dbname=product,uid=uid,create=False) +# plans = reader.view('users/active_plan',key=user.user_key) #me['_id']) +# #session['plans'] = plans +# session['key'] = user.user_key +# session['uid'] = uid +# session['active-plans'] = [item['id'] for item in plans] +# print (session['active-plans']) +# return (json.dumps(plans),200) def get_plans(product) : lproducts = stripe.Product.list() @@ -342,9 +394,16 @@ def is_customer (app_name): if __name__ == '__main__' : # # setup mongodb session management (not sure why) + app.config['SESSION_TYPE'] = 'mongodb' + app.config['MONGO_URI'] = 'mongodb://localhost:27017' + app.config['SESSION_MONGODB_DB'] = 'sessions' + app.config['SESSION_MONGODB_COLLECT'] = 'store' + mongo = PyMongo(app) + app.session_interface = MongoDBSessionInterface(app,mongo.db, 'store') + app.debug = True ; app.secret_key = '360-8y-[0v@t10n]+kr81v17y' app.config['MAX_CONTENT_LENGTH'] = 1600 * 1024 * 1024 - + Session(app) app.run(port=PORT,threaded=True,host='0.0.0.0') # app.run() #'0.0.0.0',PORT,True,threaded=True) diff --git a/src/api/static/css/dialog.css b/src/api/static/css/dialog.css new file mode 100644 index 0000000..1d60e1e --- /dev/null +++ b/src/api/static/css/dialog.css @@ -0,0 +1,10 @@ +.dialog { + widows: 500px; +} +.dialog input[type=text]{ + padding:8px; + width:100px; +} +.dialog .fa-check {color:#20B2AA; font-size:18px;} +.dialog .fa-times{color:maroon} +.dialog .title { display:grid; grid-template-columns: auto 32px; grid-gap:2px; gap:2px} diff --git a/src/api/static/js/plans.js b/src/api/static/js/plans.js new file mode 100644 index 0000000..7ff40e3 --- /dev/null +++ b/src/api/static/js/plans.js @@ -0,0 +1,7 @@ +/*** + * This file has the functionalities to manage plans i.e signup users, bind controls and establish operational patterns + */ +var Plans = {} +Plans.get = function(id){} +Plans.ui = {} +Plans.ui.bind = function(){} \ No newline at end of file diff --git a/src/api/store.py b/src/api/store.py index fc48e77..cf42313 100644 --- a/src/api/store.py +++ b/src/api/store.py @@ -11,7 +11,7 @@ import json class User : def __init__(self,email,plans): self.plans = plans - self.me = {"plan":[],"customer":{},"payments":[]} + self.me = {"plan":[],"customer":{},"payments":[],"plan2sub":{}} self.init(email) self.format() #-- creating properties pass @@ -58,6 +58,7 @@ class User : self.plan = void() self.plan.info = lambda: self.me["plan"] + self.plan.sub_id = lambda: self.me['plan2sub'].get(self.plan.info()['id'],{}) # self.user.plan.cancel = lambda id: stripe.subscription.Subscription.delete(self.customer['id'],prorate=True,invoice_now=True) self.card = void () @@ -66,7 +67,7 @@ class User : self.card.charge = self.charge self.card.html = lambda: [{} for card in self.me["payments"]] self.card.delete = lambda index: stripe.customer.Customer.delete_source(self.me["customer"]['id'],self.me["payment"][index]) - + def init(self,email): # # get customer information @@ -89,6 +90,12 @@ class User : # self.plan.subscribe(free_plan['id'],email) # self.customer = {"email":email,"id":customers['id']} self.me["customer"] = {"email":email,"id":customer['id']} + lsub = customer.subscriptions.data + for sub in lsub : + + id = sub.plan.id + self.me['plan2sub'][id] = sub.id + # # extract payments available, if 'sources' in customer and 'data' in customer['sources'] : @@ -258,10 +265,11 @@ class Plans(Store) : if not self.user and email: self.user = User(email,self.plans) - sub_id = self.user.plan.info()['id'] + # sub_id = self.user.plan.info()['id'] + sub_id = self.user.plan.sub_id() try: self.cancel(sub_id) - self.subscribe(id,email) + r = self.subscribe(id=id,email=email) # # We need to reload everything self.user.init(email) @@ -306,6 +314,7 @@ class Plans(Store) : # customer=self.user.info()['id'], #if not email else email, # items=[plan] # ) + self.user.init(email) return 1 else: diff --git a/src/api/templates/plans.html b/src/api/templates/plans.html index 11fa07e..b069761 100755 --- a/src/api/templates/plans.html +++ b/src/api/templates/plans.html @@ -3,7 +3,9 @@ - + + + + + @@ -17,12 +12,12 @@ Signup {{product.replace('-',' ')}} - + + +
+
+
Signup
+
+
+ +
+
+

+

+
+ + +
+ +
+
+ +
+ +

+

+ +

+
+ + +
+

+ +
+ +
+
+
+
+ +
+
+ Redirecting +
+
+
+ + + \ No newline at end of file