diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 1888973..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,36 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -[//]: <> (Unreleased) - -## [0.5.3] - 2019-10-13 - -### Added -Handle errors when undefined configs more gracefully - - -## [0.5.2] - 2019-09-19 - -### Added -Handling one time payment option for one-time products - - -## [0.5.1] - 2019-09-19 - -### Changed -Fix minor issues in 0.5.0 - -## [0.5.0] - 2019-09-19 -### Added - -Features described in [\#7125](https://redmine.ungleich.ch/issues/7125) - -- Users (inlcuding anonymous users) can list products -- Authenticated users (with valid credentials) can: - - register a payment method - - make an order - - list their orders -- Admin user can create products diff --git a/config.py b/config.py index 2d7dcbc..578fc82 100644 --- a/config.py +++ b/config.py @@ -1,7 +1,7 @@ import logging from etcd3_wrapper import Etcd3Wrapper -from decouple import config, UndefinedValueError +from decouple import config logging.basicConfig( level=logging.DEBUG, @@ -11,11 +11,7 @@ logging.basicConfig( datefmt='%Y-%m-%d:%H:%M:%S', ) -# load configs + +etcd_client = Etcd3Wrapper(host=config("ETCD_HOST"), port=config("ETCD_PORT")) APP_PORT = config("APP_PORT", 5000) -try: - etcd_client = Etcd3Wrapper(host=config("ETCD_HOST"), port=config("ETCD_PORT")) - STRIPE_API_PRIVATE_KEY = config("STRIPE_API_PRIVATE_KEY") -except UndefinedValueError as uve: - print(str(uve)) - exit(1) +STRIPE_API_PRIVATE_KEY = config("STRIPE_API_PRIVATE_KEY") diff --git a/ucloud_pay.py b/ucloud_pay.py index 1269c0d..d181e0a 100644 --- a/ucloud_pay.py +++ b/ucloud_pay.py @@ -2,7 +2,7 @@ import binascii import json import requests -from decouple import config, Csv, UndefinedValueError +from decouple import config, Csv from datetime import datetime from flask import Flask, request from flask_restful import Resource, Api @@ -16,16 +16,6 @@ import time app = Flask(__name__) api = Api(app) -# load configs -OTP_SERVER = config("OTP_SERVER", "") -OTP_VERIFY_ENDPOINT = config("OTP_VERIFY_ENDPOINT", "verify/") - -try: - INIT_ORDER_ID = config("INIT_ORDER_ID") - REALM_ALLOWED = config("REALM_ALLOWED", cast=Csv(str)) -except UndefinedValueError as uve: - print(str(uve)) - exit(1) def check_otp(name, realm, token): try: @@ -42,54 +32,48 @@ def check_otp(name, realm, token): response = requests.post( "{OTP_SERVER}{OTP_VERIFY_ENDPOINT}".format( - OTP_SERVER=OTP_SERVER, - OTP_VERIFY_ENDPOINT=OTP_VERIFY_ENDPOINT, + OTP_SERVER=config("OTP_SERVER", ""), + OTP_VERIFY_ENDPOINT=config("OTP_VERIFY_ENDPOINT", "verify/"), ), - json=data, + data=data, ) return response.status_code - def get_plan_id_from_product(product): plan_id = "ucloud-v1-" plan_id += product["name"].strip().replace(' ', '-') + "-" plan_id += product["type"] return plan_id - def get_order_id(): order_id_kv = client.get("/v1/last_order_id") if order_id_kv is not None: order_id = int(order_id_kv.value) + 1 else: - order_id = INIT_ORDER_ID + order_id = config("INIT_ORDER_ID") client.put("/v1/last_order_id", str(order_id)) return "OR-{}".format(order_id) - def get_pricing(price_in_chf_cents, product_type, recurring_period): if product_type == "recurring": - return "CHF {}/{}".format( + return "CHF {}/ {}".format( price_in_chf_cents/100, recurring_period ) elif product_type == "one-time": - return "CHF {} (One time charge)".format(price_in_chf_cents/100) - + return "CHF {}".format(price_in_chf_cents/100) def get_user_friendly_product(product_dict): - uf_product = { + return { "name": product_dict["name"], "description": product_dict["description"], "product_id": product_dict["product_id"], "pricing": get_pricing(product_dict["price"], product_dict["type"], - product_dict["recurring_period"]) - } - if product_dict["type"] == "recurring": - uf_product["minimum_subscription_period"] = ( + product_dict["recurring_period"]), + "minimum_subscription_period": product_dict["minimum_subscription_period"] - ) - return uf_product + } + class ListProducts(Resource): @@ -110,6 +94,7 @@ class AddProduct(Resource): def post(): data = request.json logging.debug("Got data: {}".format(str(data))) + REALM_ALLOWED = config("REALM_ALLOWED", cast=Csv(str)) logging.debug("REALM_ALLOWED = {}".format(REALM_ALLOWED)) if data["realm"] not in REALM_ALLOWED: logging.error( @@ -125,7 +110,7 @@ class AddProduct(Resource): product_uuid = uuid4().hex product_key = "/v1/products/{}".format(product_uuid) product_value = { - "product_id": product_uuid, + "product_id": product_key, "name": data["product_name"], "description": data["product_description"], "type": data["product_type"], @@ -141,7 +126,7 @@ class AddProduct(Resource): client.put(product_key, product_value, value_in_json=True) return {"message": "Product {} created. Product ID = {}".format( - data['product_name'], product_uuid + data['product_name'], product_key )}, 200 except KeyError as ke: logging.error("KeyError occurred. details = {}".format(str(ke))) @@ -366,38 +351,6 @@ class ProductOrder(Resource): "Product {} is one-time " "payment".format(product_obj["type"]) ) - charge_response = stripe_utils.make_charge( - amount=product_obj['price'], - 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') - logging.error("Could not make a one time payment") - logging.error("Details = {}".format(msg)) - return {"message": "Error subscribing to plan. " - "Details: {}".format(msg)}, 400 - - order_obj = { - "order_id": get_order_id(), - "ordered_at": int(time.time()), - "product": product_obj, - } - client.put("/v1/user/{}/orders".format( - data['name']), json.dumps(order_obj), - value_in_json=True) - order_obj["ordered_at"] = datetime.fromtimestamp( - order_obj["ordered_at"]).strftime("%c") - order_obj["product"] = get_user_friendly_product( - product_obj - ) - logging.debug(str(order_obj)) - return {"message": "Order successful", - "order_details": order_obj}, 200 - - except KeyError as key_error: logging.error("Key error occurred") logging.error(str(key_error)) @@ -418,11 +371,14 @@ class OrderList(Resource): orders_dict = {} for p in orders: order_dict = json.loads(p.value) + logging.debug("order_dict = " + str(order_dict)) + logging.debug("type p.value = " + str(type(p.value))) + logging.debug("p.value = " + str(p.value)) order_dict["ordered_at"] = datetime.fromtimestamp( order_dict["ordered_at"]).strftime("%c") order_dict["product"] = get_user_friendly_product( order_dict["product"]) - orders_dict[order_dict["order_id"]] = order_dict + orders_dict[p.key] = order_dict logging.debug("Orders = {}".format(orders_dict)) return orders_dict, 200