gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-merchant-demos] branch master updated: fixes #8137


From: gnunet
Subject: [taler-taler-merchant-demos] branch master updated: fixes #8137
Date: Thu, 08 Feb 2024 21:45:23 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository taler-merchant-demos.

The following commit(s) were added to refs/heads/master by this push:
     new 200b658  fixes #8137
200b658 is described below

commit 200b658d3152774345a4dbd6adfb10f62581233a
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Feb 8 17:45:16 2024 -0300

    fixes #8137
---
 talermerchantdemos/blog/blog.py           | 291 +++++++++++++++---------------
 talermerchantdemos/httpcommon/__init__.py |  17 +-
 2 files changed, 158 insertions(+), 150 deletions(-)

diff --git a/talermerchantdemos/blog/blog.py b/talermerchantdemos/blog/blog.py
index 14c8bdd..e0bd06e 100644
--- a/talermerchantdemos/blog/blog.py
+++ b/talermerchantdemos/blog/blog.py
@@ -40,6 +40,7 @@ from talermerchantdemos.httpcommon import (
     backend_get,
     backend_get_with_status,
     backend_post,
+    backend_payment_url,
     self_localized,
     Deadline,
     BackendException,
@@ -324,15 +325,16 @@ def render_article(article_name, lang, data, order_id, 
refundable):
 # @param article_name which article the order is for
 # @param lang which language to use
 #
-def post_order(article_name, lang):
+def post_order(article_name, article_url, session_id, lang):
     article_info = ARTICLES[lang].get(article_name)
     summary = f"Essay: {article_info.title}"
     order = dict(
         amount=ARTICLE_AMOUNT,
         extra=dict(article_name=article_name),
-        fulfillment_url=flask.request.base_url,
-        public_reorder_url=flask.request.base_url,
+        fulfillment_url=article_url,
+        public_reorder_url=article_url,
         summary=summary,
+        session_id=session_id,
         # FIXME: add support for i18n of summary!
         # 10 minutes time for a refund
         wire_transfer_deadline=dict(t_s=int(time.time() + 15 * 30)),
@@ -375,169 +377,160 @@ def article(article_name, lang=None, data=None):
     # 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")
     new_if_refunded = request.args.get("new_if_refunded")
+    current_order_id = flask.request.cookies.get("order_id")
+    article_url = flask.request.base_url
 
-    # 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
-
-    # 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.
-    status, pay_status = backend_get_with_status(
-        BACKEND_URL,
-        f"private/orders/{order_id}",
-        params=dict(session_id=session_id),
-        auth_token=APIKEY,
-    )
-
-    if status == 404:
-        order_resp = post_order(article_name, lang)
-        order_id = order_resp["order_id"]
-        pay_status = backend_get(
-            BACKEND_URL,
-            f"private/orders/{order_id}",
-            params=dict(session_id=session_id),
-            auth_token=APIKEY,
+      # If expect_state = yes then session_id should be set already
+      # this is a way to check that the client supports cookies
+      if maybe_expect_state == "yes":
+        error_page = flask.render_template(
+            "blog-error.html.j2",
+            page_title=gettext("GNU Taler Demo: Error"),
+            message=gettext("Please enable cookies."),
         )
-    elif status != 200:
+        return flask.make_response(error_page, 412)
+      
+      # first time setting session_id
+      # check if browser support cookies with a flag
+      session_id = flask.session["session_id"] = str(uuid.uuid4())
+      return flask.redirect(req_add_cookie_check(), code=302)
+    
+    # If session is present then we know that cookies are enabled
+    # remove the flag if present
+    if maybe_expect_state == "yes":
+      return flask.redirect(req_rm_cookie_check(), code=302)
+
+    ############################
+    # user has a session and cookie works
+    #
+    # check if we can already render the article
+    ############################
+
+    # if an order_id is present then render if paid or refunded
+    if current_order_id is not None:
+      status, current_order = backend_get_with_status(
+          BACKEND_URL,
+          f"private/orders/{current_order_id}",
+          params=dict(session_id=session_id),
+          auth_token=APIKEY,
+      )
+
+      if status == 200:
+        if current_order.get("order_status") == "paid" and not 
current_order.get("refunded"):
+          return render_article(
+              article_name,
+              lang,
+              data,
+              current_order_id,
+              current_order.get("refundable")
+          )
+        
+        # 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")
+
+        # FIXME: ignoring becuase of https://bugs.gnunet.org/view.php?id=8353
+        ai = None
+        au = None
+        if ai is not None:
+          print("== Merchant says 'see other': ", ai, au)
+          response = flask.redirect(article_url)
+          response.set_cookie(
+              "order_id",
+              order_id,
+              path=urllib.parse.quote(url_for ('index') + 
f"essay/{article_name}")
+          )
+          response.set_cookie(
+              "order_id",
+              order_id,
+              path=urllib.parse.quote(url_for ('index') + 
f"{lang}/essay/{article_name}")
+          )
+          return response
+        
+        # If new_if_refunded == "yes" the user already acknowledge the
+        # state of the current order and is asking for a new one.
+        if current_order.get("refunded") and new_if_refunded != "yes":
+          return flask.render_template(
+              "blog-article-refunded.html.j2",
+              page_title=gettext("GNU Taler Demo: Refunded"),
+              article_name=article_name,
+              article_lang=lang,
+              order_id=current_order_id,
+          )
+      
+      elif status != 404: 
+      # not found may be normal, could means that 
+      # merchant forgot about the order becuase
+      # it was a long time without being paid
         raise BackendException(
-            message=gettext("Backend returned error status"),
-            backend_status=status,
-            backend_json=pay_status,
-        )
-    order_status = pay_status.get("order_status")
-
-    if order_status == "claimed":
-        if not lang:
-            err_abort(403, message=gettext("Direct access forbidden"))
-        # Order already claimed, must setup fresh order
-        order_resp = post_order(article_name, lang)
-        order_id = order_resp["order_id"]
-        pay_status = backend_get(
-            BACKEND_URL,
-            f"private/orders/{order_id}",
-            params=dict(session_id=session_id),
-            auth_token=APIKEY,
-        )
-        order_status = pay_status.get("order_status")
-
-    if order_status == "paid" and not pay_status["refunded"]:
-        return render_article(
-            article_name,
-            lang,
-            data,
-            order_id,
-            refundable(pay_status)
+          message=gettext("Backend returned error status"),
+          backend_status=status,
+          backend_json=current_order,
         )
 
-    #If the order is refunded show the status, unless the user is already aware
-    #and notified using the query parameter to create a new order
-    if order_status == "paid" and pay_status.get("refunded"):
-        if new_if_refunded == "yes":
-            order_resp = post_order(article_name, lang)
-            order_id = order_resp["order_id"]
-            pay_status = backend_get(
-                BACKEND_URL,
-                f"private/orders/{order_id}",
-                params=dict(),
-                auth_token=APIKEY,
-            )
-            # Order ID is fresh, thus setting the cookies now, and
-            # redirecting the client to the cookie support check.
-            redirect_url = pay_status["order_status_url"]
-            response = flask.redirect(redirect_url)
-            response.set_cookie(
-                "order_id",
-                order_id,
-                path=urllib.parse.quote(url_for ('index') + 
f"essay/{article_name}")
-            )
-            response.set_cookie(
-                "order_id",
-                order_id,
-                path=urllib.parse.quote(url_for ('index') + 
f"{lang}/essay/{article_name}")
-            )
-            return response
-
-        else:
-            return flask.render_template(
-                "blog-article-refunded.html.j2",
-                page_title=gettext("GNU Taler Demo: Refunded"),
-                article_name=article_name,
-                article_lang=lang,
-                order_id=order_id,
-            )
+          
+    # Current order is not present or unpaid
+    # Check if there is a paid but not refunded order in this session
+    list_resp = backend_get(
+        BACKEND_URL,
+        f"private/orders",
+        params=dict(session_id=session_id, fulfillment_url=article_url, 
refunded="no"),
+        auth_token=APIKEY,
+    )
     
-    # 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)
+    already_paid_order = None
+    for order in list_resp.get("orders"):
+      if order.get("paid"):
+        already_paid_order = order
+        break
+
+    if already_paid_order is not None:
+        # Found one, now this is the current order.
+        print("== Already paid order found", already_paid_order)
+        order_id = already_paid_order.get("order_id")
+        response = flask.redirect(article_url)
         response.set_cookie(
             "order_id",
-            ai,
+            order_id,
             path=urllib.parse.quote(url_for ('index') + 
f"essay/{article_name}")
         )
         response.set_cookie(
             "order_id",
-            ai,
+            order_id,
             path=urllib.parse.quote(url_for ('index') + 
f"{lang}/essay/{article_name}")
         )
         return response
-
-    # 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"]
-    # 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"):
-        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())
+  
+    ############################
+    # We couln't find a paid order
+    #
+    # Note that it could be the case that the user is still paying
+    # an order with another device, in other browser on the same
+    # session or claimed in the same brower. 
+    # Still, creating an order is cheap and we can safely redirect 
+    # to a payment page and relay on repurchase detection to avoid
+    # double payments.
+    #
+    # create a new order and ask for payment
+    ############################
+      
+    order_resp = post_order(article_name, article_url, session_id, lang)
+    order_id = order_resp["order_id"]
+    token = order_resp["token"]
+
+    redirect_url = backend_payment_url(
+      BACKEND_URL,
+      f"orders/{order_id}",
+      session_id,
+      token
+    )
+    print("new order URL", redirect_url)
+    response = flask.redirect(redirect_url)
     response.set_cookie(
         "order_id",
         order_id,
diff --git a/talermerchantdemos/httpcommon/__init__.py 
b/talermerchantdemos/httpcommon/__init__.py
index 518eb1b..d2238ff 100644
--- a/talermerchantdemos/httpcommon/__init__.py
+++ b/talermerchantdemos/httpcommon/__init__.py
@@ -1,6 +1,6 @@
 import flask
 import requests
-from urllib.parse import urljoin
+from urllib.parse import urljoin, urlencode, urlparse, urlunparse
 from flask import request, url_for
 from datetime import datetime
 import time
@@ -117,6 +117,21 @@ def get_locale():
         return "en"
     return lang
 
+##
+# Construct the payment URL where customer can pay the order
+#
+# @param backend_url where the backend is located
+# @param order_id id of the order already created
+# @param session_id session in which the order is going to be paid
+# @param token if the order requires a token 
+# @return the JSON response from the backend, or a error response
+#         if something unexpected happens.
+def backend_payment_url(backend_url, endpoint, session_id, token):
+  final_url = urljoin(backend_url, endpoint)
+  query = urlencode({"token":token, "session_id":session_id})
+  redirect_url = urlparse(final_url)._replace(query=query)
+  return urlunparse(redirect_url)
+    
 
 ##
 # Helper function used inside Jinja2 logic to create a links

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]