[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.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-taler-merchant-demos] branch master updated: fixes #8137,
gnunet <=