def post_anonymised_customer_info(): user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if user_info(flask.session) or guest_token: advantage = AdvantageContracts( session, user_token or guest_token, token_type=("Macaroon" if user_token else "Bearer"), api_url=api_url, ) if not flask.request.is_json: return flask.jsonify({"error": "JSON required"}), 400 account_id = flask.request.json.get("account_id") if not account_id: return flask.jsonify({"error": "account_id required"}), 400 address = flask.request.json.get("address") if not address: return flask.jsonify({"error": "address required"}), 400 tax_id = flask.request.json.get("tax_id") return advantage.put_anonymous_customer_info(account_id, address, tax_id) else: return flask.jsonify({"error": "authentication required"}), 401
def post_customer_info(): if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=flask.current_app.config["CONTRACTS_API_URL"], ) if not flask.request.is_json: return flask.jsonify({"error": "JSON required"}), 400 payment_method_id = flask.request.json.get("payment_method_id") if not payment_method_id: return flask.jsonify({"error": "payment_method_id required"}), 400 account_id = flask.request.json.get("account_id") if not account_id: return flask.jsonify({"error": "account_id required"}), 400 address = flask.request.json.get("address") name = flask.request.json.get("name") tax_id = flask.request.json.get("tax_id") return advantage.put_customer_info( account_id, payment_method_id, address, name, tax_id ) else: return flask.jsonify({"error": "authentication required"}), 401
def accept_renewal(renewal_id): if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=flask.current_app.config["CONTRACTS_API_URL"], ) return advantage.accept_renewal(renewal_id) else: return flask.jsonify({"error": "authentication required"}), 401
def get_purchase(purchase_id): user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") if user_info(flask.session) or guest_token: advantage = AdvantageContracts( session, user_token or guest_token, token_type=("Macaroon" if user_token else "Bearer"), api_url=flask.current_app.config["CONTRACTS_API_URL"], ) return advantage.get_purchase(purchase_id) else: return flask.jsonify({"error": "authentication required"}), 401
def ensure_purchase_account(): """ Returns an object with the ID of an account a user can make purchases on. If the user is not logged in, the object also contains an auth token required for subsequent calls to the contract API. """ is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if not flask.request.is_json: return flask.jsonify({"error": "JSON required"}), 400 auth_token = None if user_info(flask.session): auth_token = flask.session["authentication_token"] advantage = AdvantageContracts( session, auth_token, api_url=api_url, ) request = flask.request.json try: account = advantage.ensure_purchase_account( email=request.get("email"), account_name=request.get("account_name"), payment_method_id=request.get("payment_method_id"), ) except UnauthorizedError as err: # This kind of errors are handled js side. return err.asdict(), 200 except HTTPError as err: flask.current_app.extensions["sentry"].captureException() return err.response.content, 500 # The guest authentication token is included in the response only when the # user is not logged in. token = account.get("token") if token: flask.session["guest_authentication_token"] = token return flask.jsonify(account), 200
def post_renewal_preview(renewal_id): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=flask.current_app.config["CONTRACTS_API_URL"], ) try: preview = advantage.post_renewal_preview(renewal_id=renewal_id) except HTTPError as http_error: flask.current_app.extensions["sentry"].captureException() return ( http_error.response.content, 500, ) return flask.jsonify(preview), 200
def accept_renewal(renewal_id): is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=api_url, ) return advantage.accept_renewal(renewal_id) else: return flask.jsonify({"error": "authentication required"}), 401
def post_stripe_invoice_id(tx_type, tx_id, invoice_id): user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if user_info(flask.session) or guest_token: advantage = AdvantageContracts( session, user_token or guest_token, token_type=("Macaroon" if user_token else "Bearer"), api_url=api_url, ) return advantage.post_stripe_invoice_id(tx_type, tx_id, invoice_id) else: return flask.jsonify({"error": "authentication required"}), 401
def post_renewal_preview(renewal_id): is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=api_url, ) try: preview = advantage.post_renewal_preview(renewal_id=renewal_id) except HTTPError as http_error: flask.current_app.extensions["sentry"].captureException() return ( http_error.response.content, 500, ) return flask.jsonify(preview), 200
def advantage_view(): accounts = None personal_account = None enterprise_contracts = {} entitlements = {} open_subscription = flask.request.args.get("subscription", None) stripe_publishable_key = os.getenv( "STRIPE_PUBLISHABLE_KEY", "pk_live_68aXqowUeX574aGsVck8eiIE" ) if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=flask.current_app.config["CONTRACTS_API_URL"], ) try: accounts = advantage.get_accounts() except HTTPError as http_error: if http_error.response.status_code == 401: # We got an unauthorized request, so we likely # need to re-login to refresh the macaroon flask.current_app.extensions["sentry"].captureException( extra={ "session_keys": flask.session.keys(), "request_url": http_error.request.url, "request_headers": http_error.request.headers, "response_headers": http_error.response.headers, "response_body": http_error.response.json(), "response_code": http_error.response.json()["code"], "response_message": http_error.response.json()[ "message" ], } ) empty_session(flask.session) return flask.render_template("advantage/index.html") raise http_error for account in accounts: account["contracts"] = advantage.get_account_contracts(account) for contract in account["contracts"]: contract["token"] = advantage.get_contract_token(contract) machines = advantage.get_contract_machines(contract).get( "machines" ) contract["machineCount"] = 0 if machines: contract["machineCount"] = len(machines) if contract["contractInfo"].get("origin", "") == "free": personal_account = account personal_account["free_token"] = contract["token"] for entitlement in contract["contractInfo"][ "resourceEntitlements" ]: if entitlement["type"] == "esm-infra": entitlements["esm-infra"] = True elif entitlement["type"] == "esm-apps": entitlements["esm-apps"] = True elif entitlement["type"] == "livepatch": entitlements["livepatch"] = True elif entitlement["type"] == "fips": entitlements["fips"] = True elif entitlement["type"] == "cc-eal": entitlements["cc-eal"] = True personal_account["entitlements"] = entitlements else: entitlements = {} for entitlement in contract["contractInfo"][ "resourceEntitlements" ]: contract["supportLevel"] = "-" if entitlement["type"] == "esm-infra": entitlements["esm-infra"] = True elif entitlement["type"] == "esm-apps": entitlements["esm-apps"] = True elif entitlement["type"] == "livepatch": entitlements["livepatch"] = True elif entitlement["type"] == "fips": entitlements["fips"] = True elif entitlement["type"] == "cc-eal": entitlements["cc-eal"] = True elif entitlement["type"] == "support": contract["supportLevel"] = entitlement[ "affordances" ]["supportLevel"] contract["entitlements"] = entitlements created_at = dateutil.parser.parse( contract["contractInfo"]["createdAt"] ) contract["contractInfo"][ "createdAtFormatted" ] = created_at.strftime("%d %B %Y") contract["contractInfo"]["status"] = "active" if "effectiveTo" in contract["contractInfo"]: effective_to = dateutil.parser.parse( contract["contractInfo"]["effectiveTo"] ) contract["contractInfo"][ "effectiveToFormatted" ] = effective_to.strftime("%d %B %Y") time_now = datetime.utcnow().replace(tzinfo=pytz.utc) if effective_to < time_now: contract["contractInfo"]["status"] = "expired" contract["contractInfo"][ "expired_restart_date" ] = time_now - timedelta(days=1) date_difference = effective_to - time_now contract["expiring"] = date_difference.days <= 30 contract["contractInfo"][ "daysTillExpiry" ] = date_difference.days contract["renewal"] = make_renewal( advantage, contract["contractInfo"] ) enterprise_contract = enterprise_contracts.setdefault( contract["accountInfo"]["name"], [] ) # If a subscription id is present and this contract # matches add it to the start of the list if contract["contractInfo"]["id"] == open_subscription: enterprise_contract.insert(0, contract) else: enterprise_contract.append(contract) return flask.render_template( "advantage/index.html", accounts=accounts, enterprise_contracts=enterprise_contracts, personal_account=personal_account, open_subscription=open_subscription, stripe_publishable_key=stripe_publishable_key, )
def advantage_shop_view(): account = previous_purchase_id = None is_test_backend = flask.request.args.get("test_backend", False) stripe_publishable_key = os.getenv("STRIPE_LIVE_PUBLISHABLE_KEY", "pk_live_68aXqowUeX574aGsVck8eiIE") api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: stripe_publishable_key = os.getenv( "STRIPE_TEST_PUBLISHABLE_KEY", "pk_test_yndN9H0GcJffPe0W58Nm64cM00riYG4N46", ) api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=api_url, ) if flask.session.get("guest_authentication_token"): flask.session.pop("guest_authentication_token") try: account = advantage.get_purchase_account() except HTTPError as err: code = err.response.status_code if code == 401: # We got an unauthorized request, so we likely # need to re-login to refresh the macaroon flask.current_app.extensions["sentry"].captureException( extra={ "session_keys": flask.session.keys(), "request_url": err.request.url, "request_headers": err.request.headers, "response_headers": err.response.headers, "response_body": err.response.json(), "response_code": err.response.json()["code"], "response_message": err.response.json()["message"], }) empty_session(flask.session) return flask.render_template( "advantage/subscribe/index.html", account=None, previous_purchase_id=None, product_listings=[], stripe_publishable_key=stripe_publishable_key, is_test_backend=is_test_backend, ) if code != 404: raise # There is no purchase account yet for this user. # One will need to be created later, but this is an expected # condition. else: advantage = AdvantageContracts(session, None, api_url=api_url) if account is not None: resp = advantage.get_account_subscriptions_for_marketplace( account["id"], "canonical-ua") subs = resp.get("subscriptions") if subs: previous_purchase_id = subs[0].get("lastPurchaseID") listings_response = advantage.get_marketplace_product_listings( "canonical-ua") product_listings = listings_response.get("productListings") if not product_listings: # For the time being, no product listings means the shop has not been # activated, so fallback to shopify. This should become an error later. return flask.redirect("https://buy.ubuntu.com/") products = {pr["id"]: pr for pr in listings_response["products"]} listings = [] for listing in product_listings: if "price" not in listing: continue listing["product"] = products[listing["productID"]] listings.append(listing) return flask.render_template( "advantage/subscribe/index.html", account=account, previous_purchase_id=previous_purchase_id, product_listings=listings, stripe_publishable_key=stripe_publishable_key, is_test_backend=is_test_backend, )
def post_advantage_subscriptions(preview): is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") if user_info(flask.session) or guest_token: advantage = AdvantageContracts( session, user_token or guest_token, token_type=("Macaroon" if user_token else "Bearer"), api_url=api_url, ) else: return flask.jsonify({"error": "authentication required"}), 401 payload = flask.request.json if not payload: return flask.jsonify({}), 400 account_id = payload.get("account_id") previous_purchase_id = payload.get("previous_purchase_id") last_subscription = {} if not guest_token: try: subscriptions = ( advantage.get_account_subscriptions_for_marketplace( account_id=account_id, marketplace="canonical-ua")) except HTTPError: flask.current_app.extensions["sentry"].captureException( extra={"payload": payload}) return ( flask.jsonify( {"error": "could not retrieve account subscriptions"}), 500, ) if subscriptions: last_subscription = subscriptions["subscriptions"][0] # If there is a subscription we get the current metric # value for each product listing so we can generate a # purchase request with updated quantities later. subscribed_quantities = {} if "purchasedProductListings" in last_subscription: for item in last_subscription["purchasedProductListings"]: product_listing_id = item["productListing"]["id"] subscribed_quantities[product_listing_id] = item["value"] purchase_items = [] for product in payload.get("products"): product_listing_id = product["product_listing_id"] metric_value = product["quantity"] + subscribed_quantities.get( product_listing_id, 0) purchase_items.append({ "productListingID": product_listing_id, "metric": "active-machines", "value": metric_value, }) purchase_request = { "accountID": account_id, "purchaseItems": purchase_items, "previousPurchaseID": previous_purchase_id, } try: if not preview: purchase = advantage.purchase_from_marketplace( marketplace="canonical-ua", purchase_request=purchase_request) else: purchase = advantage.preview_purchase_from_marketplace( marketplace="canonical-ua", purchase_request=purchase_request) except HTTPError as http_error: flask.current_app.extensions["sentry"].captureException( extra={ "purchase_request": purchase_request, "api_response": http_error.response.json(), }) return ( flask.jsonify({"error": "could not complete this purchase"}), 500, ) return flask.jsonify(purchase), 200