summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMS <ms@taler.net>2023-04-28 18:27:47 +0200
committerMS <ms@taler.net>2023-04-28 18:27:47 +0200
commit943100018fc087e3293e93722c2ebc183607bb2f (patch)
tree6deb8da37827cf7d79d15f8186b536833f30d7cf
parent6fa8d80b8d6873935ab14956b4ba62932bdd2a14 (diff)
downloadtaler-merchant-demos-943100018fc087e3293e93722c2ebc183607bb2f.tar.gz
taler-merchant-demos-943100018fc087e3293e93722c2ebc183607bb2f.tar.bz2
taler-merchant-demos-943100018fc087e3293e93722c2ebc183607bb2f.zip
Addressing #6860
-rw-r--r--talermerchantdemos/blog/blog.py91
1 files changed, 77 insertions, 14 deletions
diff --git a/talermerchantdemos/blog/blog.py b/talermerchantdemos/blog/blog.py
index 263c7cf..3074f91 100644
--- a/talermerchantdemos/blog/blog.py
+++ b/talermerchantdemos/blog/blog.py
@@ -46,6 +46,25 @@ from talermerchantdemos.httpcommon import (
get_locale,
)
+def req_add_cookie_check():
+ current_url = list(urllib.parse.urlparse(flask.request.base_url))
+ args_writable = flask.request.args.copy()
+ # Adding the used param.
+ args_writable.update(dict(expect_state="yes"))
+ current_url[4] = urllib.parse.urlencode(args_writable)
+ # Stringify the result.
+ return urllib.parse.urlunparse(current_url)
+
+
+def req_rm_cookie_check():
+ current_url = list(urllib.parse.urlparse(flask.request.base_url))
+ args_writable = flask.request.args.copy()
+ # Stripping the used param.
+ args_writable.pop("expect_state")
+ current_url[4] = urllib.parse.urlencode(args_writable)
+ # Stringify the result.
+ return urllib.parse.urlunparse(current_url)
+
def err_abort(abort_status_code, **params):
"""
@@ -351,22 +370,42 @@ def post_order(article_name, lang):
def article(article_name, lang=None, data=None):
# We use an explicit session ID so that each payment (or payment replay) is
# bound to a browser. This forces re-play and prevents sharing the article
- # by just sharing the URL.
+ # by just sharing the URL. flask.session is transparently set by Flask, when
+ # the user agent supports cookies. All the key-value pairs associated to it
+ # are only stored in the server.
session_id = flask.session.get("session_id")
order_id = flask.request.cookies.get("order_id")
+ # Check if cookies are expected for this request.
+ maybe_expect_state = request.args.get("expect_state")
+ if maybe_expect_state == "yes":
+ if not order_id:
+ error_page = flask.render_template(
+ "blog-error.html.j2",
+ page_title=gettext("GNU Taler Demo: Error"),
+ message=gettext("Please enable cookies."),
+ )
+ return flask.make_response(error_page, 412)
+ # Cookies enabled and found, redirect once again by
+ # stripping the "expect_state" parameter.
+ return flask.redirect(req_rm_cookie_check(), code=302)
+
+ # Whenever we set one session ID, we nullify the order ID (regardless
+ # of it being found in the cookies or not).
if not session_id:
session_id = flask.session["session_id"] = str(uuid.uuid4())
+ # This command ensures that fresh sessions are tied to fresh order IDs.
order_id = None
- ##
- # First-timer; generate order first.
+
+ # Merchant backend wasn't asked already to generate the order under
+ # this session, so doing it now.
if not order_id:
if not lang:
err_abort(403, message=gettext("Direct access forbidden"))
order_resp = post_order(article_name, lang)
order_id = order_resp["order_id"]
- # Ask the backend for the status of the payment
+ # Ask the backend for the status of the payment.
pay_status = backend_get(
BACKEND_URL,
f"private/orders/{order_id}",
@@ -374,6 +413,7 @@ def article(article_name, lang=None, data=None):
auth_token=APIKEY,
)
order_status = pay_status.get("order_status")
+
if order_status == "claimed":
if not lang:
err_abort(403, message=gettext("Direct access forbidden"))
@@ -387,7 +427,6 @@ def article(article_name, lang=None, data=None):
auth_token=APIKEY,
)
order_status = pay_status.get("order_status")
- # This really must be 'unpaid' now...
if order_status == "paid":
refunded = pay_status["refunded"]
@@ -399,15 +438,28 @@ def article(article_name, lang=None, data=None):
order_id=order_id,
)
response = render_article(
- article_name, lang, data, order_id, refundable(pay_status)
+ article_name,
+ lang,
+ data,
+ order_id,
+ refundable(pay_status)
)
return response
- # Check if the customer came on this page via the
- # re-purchase detection mechanism
+ # Checking repurchase case. That happens when the client
+ # visits this page in the same session where the article
+ # was paid already.
ai = pay_status.get("already_paid_order_id")
au = pay_status.get("already_paid_fulfillment_url")
+
+ # If the condition below holds, then the browser gets
+ # the paid order ID in the cookies, and the protocol starts
+ # again from that associated fulfillment URL (likely to be
+ # this page).
if ai is not None and au is not None:
+ # NOT appending the "expect_state" URI param to check
+ # cookies, because at this point the user agent DID show
+ # a session, so their cookies must be enabled.
response = flask.redirect(au)
response.set_cookie(
"order_id",
@@ -421,13 +473,23 @@ def article(article_name, lang=None, data=None):
)
return response
- # Redirect the browser to a page where the wallet can
- # run the payment protocol.
+ # No claim, nor repurchase, nor paid statuses were found so far.
+ # The actual payment protocol needs to be run, and the following
+ # URL instructs the browser+wallet to run it.
redirect_url = pay_status["order_status_url"]
- LOGGER.info(
- "Redirecting (with order_id cookies) to", redirect_url
- )
- response = flask.redirect(redirect_url)
+ # The order_id cookies MIGHT already be set, if the browser
+ # passed successfully the cookie support check (see at the very
+ # top of this function), and got redirected here thereafter.
+ # In this case, we can now redirect the browser to the URL that
+ # triggers the wallet.
+
+ if flask.request.cookies.get("order_id") and order_status != "paid":
+ LOGGER.info("Redirecting (with order_id cookies) to", redirect_url)
+ return flask.redirect(redirect_url)
+
+ # Order ID is fresh, thus setting the cookies now, and
+ # redirecting the client to the cookie support check.
+ response = flask.redirect(req_add_cookie_check())
response.set_cookie(
"order_id",
order_id,
@@ -438,6 +500,7 @@ def article(article_name, lang=None, data=None):
order_id,
path=urllib.parse.quote(url_for ('index') + f"{lang}/essay/{article_name}")
)
+
return response