summaryrefslogtreecommitdiff
path: root/design-documents
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-08-09 12:16:56 +0200
committerChristian Grothoff <christian@grothoff.org>2020-08-09 12:16:56 +0200
commit551739c033f29fd21d6110def4f8ad87615b1b93 (patch)
treebe7bb0d43f0afee538993e8d199ef0ae6acdb8cd /design-documents
parent6d4a4b27e92a82daac882949febcf0a674f17d71 (diff)
downloaddocs-551739c033f29fd21d6110def4f8ad87615b1b93.tar.gz
docs-551739c033f29fd21d6110def4f8ad87615b1b93.tar.bz2
docs-551739c033f29fd21d6110def4f8ad87615b1b93.zip
points for discussion
Diffstat (limited to 'design-documents')
-rw-r--r--design-documents/007-payment.rst100
1 files changed, 70 insertions, 30 deletions
diff --git a/design-documents/007-payment.rst b/design-documents/007-payment.rst
index 5294103..aacd163 100644
--- a/design-documents/007-payment.rst
+++ b/design-documents/007-payment.rst
@@ -23,6 +23,8 @@ Requirements
Proposed Solution
=================
+
+
Session-bound payment flow for Web resources
--------------------------------------------
@@ -35,103 +37,133 @@ Storefront
When *resource-URL* is requested, the storefront runs the following steps:
1. Extract the the *order-ID* (or null) and *resource name* from the *resource-URL*.
-2. Extract the *session-ID* (or null) from the request's signed cookie
+2. Extract the *session-ID* (or null) from the request's signed cookie.
+ -------- DISCUSS: 'signed'? Since when are cookies signed???
3. If *session-ID* and *order-ID* is non-null and the storefront's
*session-payment-cache* contains the tuple (*order-ID*, *resource-name*, *session-ID*),
- return to the client the information for *resource name*. **Terminate.**
-4. If the *session-ID* is null, assign a fresh session ID and set it in a cookie to be sent with the response
+ return to the client the resource associated with *resource name*. **Terminate.**
+4. If the *session-ID* is null, assign a fresh session ID and set it in a cookie to be sent with the response.
5. If *order-ID* is null, create a new order for *resource-name* by doing a ``POST /private/orders`` to
the merchant backend. Store the new order ID as *order-ID*.
6. Check the status of the payment for *order-ID* under *session-ID* by doing a ``GET /private/orders/{order-ID}?session_id={session-ID}``.
This results in the *order-status*, *refund-amount* and the *client-order-status-URL*.
7. If the *order-status* is paid and *refund-amount* is non-zero,
return to the client the refund info page for *resource name*. **Terminate.**
+ ---------- DISCUSS: what is a 'refund info page'? What should be on it? Explain better!
8. If the *order-status* is paid, store the tuple (*order-ID*, *resource-name*, *session-ID*) in *session-payment-cache*
- and return to the client the information for *resource name*. **Terminate.**
+ and return to the client the resource associated with *resource name*. **Terminate.**
9. Otherwise, the *order-status* is unpaid. Redirect the client to *client-order-status-URL*. **Terminate.**
.. note::
- When a refund is given, the corresponding tuple must be removed from the *session-page-cache*.
+ The use of a *session-page-cache* is optional, but recommended for performance. When a refund is given, the corresponding tuple must be removed from the *session-page-cache*.
Backend Private Order Status
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The merchant backend runs the following steps to generate the
*client-order-status-URL* when processing a request for ``GET
-/private/orders/{order-ID}?session_id={session-ID}``:
+/private/orders/{order-ID}?session_id={session-ID}&timeout_ms={timeout}``:
-1. Let *session-ID* be the session ID of the request (note: **not** the last paid session ID)
-2. If *order-ID* does not identify an existing order, return an error. **Terminate**.
+1. Let *session-ID* be the session ID of the request or null if not given (note: **not** the last paid session ID)
+2. If *order-ID* does not identify an existing order, return a 40 Not Found response. **Terminate**.
3. If *order-ID* identifies an order that is *unclaimed* and has claim token *claim-token*, return the URL
::
{backendBaseUrl}/orders/{order-ID}?token={claim-token}&session_id={session-ID}
- **Terminate.**
-4. If *order-ID* identifies an order that is either *claimed* or *paid* and has contract terms hash *contract-hash*, return the URL
+ (if no claim-token was generated, omit that parameter from the above URI). **Terminate.**
+
+4. Here *order-ID* identifies an order that is *claimed*. If the order is *unpaid*, wait until timeout or payment.
+
+5. If the order remains unpaid or was paid for a different *session-ID*, obtain the contract terms hash *contract-hash* and return the URL
::
{backendBaseUrl}/orders/{order-ID}?h_contract={contract-hash}&session_id={session-ID}
+ together with the status *unpaid*. (If *session-ID* is null, it does not matter for which session the contract was paid.) **Terminate.**
+
+6. Here *order-ID* must now identify an order that is *paid* or *refunded*. Obtain the contract terms hash *contract-hash* and return the URL
+
+ ::
+
+ {backendBaseUrl}/orders/{order-ID}?h_contract={contract-hash}&session_id={session-ID}
+
+ together with the status *paid* or *refunded* (and if applicable, with details about the applied refunds). **Terminate.**
+
+
+
Backend Client Order Status Page
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The merchant backend runs the following steps to generate the HTML page for
``GET /orders/{order-ID}?session_id={session-ID}&claim_token={claim-token}&h_contract={contract-hash}``:
+------- NOTE: currently the argument is just 'token', not 'claim_token'! ----------
-1. If *order-ID* does not identify an existing order, render an error page. **Terminate.**
-2. If *order-ID* identifies a paid order *ord*, run these steps:
+1. If *order-ID* does not identify an existing order, render a 404 Not Found response. **Terminate.**
+2. If *order-ID* identifies a paid order (where the *session-id* matches the one from the payment), run these steps:
- 1. If the *contract-hash* request parameter does not match the contract terms hash of *ord*,
+ 1. If the *contract-hash* request parameter does not match the contract terms hash of the order,
return a 403 Forbidden response. **Terminate.**
- 2. If *ord* has refunds that are not picked up, prompt the URI
+
+ 2. If the order has granted refunds that have not been obtained by the wallet yet, prompt the URI
::
taler{proto_suffix}://refund/{/merchant_prefix*}/{order-id}/{session-id}
- Redirect to the *fulfillment-URL* of *ord* once the refund is picked up and
- payment has been proven under *session-ID*.
-
- **Terminate.**
+ The generated Web site should long-poll until all refunds have been obtained,
+ then redirect to the *fulfillment-URL* of the order once the refunds have been
+ obtained. **Terminate.**
+ ----- FIXME: IIRC our long-polling API does only allow waiting for the granted refund amount, not for the *obtained* refund amount. => API change?
- 3. Prompt the URI
+ 3. Here the order has been paid. -------- what about refunded???
+ Prompt the URI ------ ???why? don't we redirect to fulfillment URL???????
::
taler{proto_suffix}://pay/{/merchant_prefix*}/{order-id}/{session-id}
- Redirect to the *fulfillment-URL* of *ord* once
+ Redirect to the *fulfillment-URL* of the order once
payment has been proven under *session-ID*.
**Terminate.**
-3. If *order-ID* identifies an unpaid order *ord*, run these steps:
+3. If *order-ID* identifies an *unpaid* order, run these steps:
- 1. If the the *claim-token* request parameter does not matches the claim token of
- *ord*, return a 403 Forbidden response. **Terminate**.
- 2. Prompt the URI
+ 1. If the the *claim-token* request parameter does not match the claim token of
+ the order, return a 403 Forbidden response. **Terminate**.
+ ----- DISCUSS: unpaid vs. unclaimed: do we check token or h_contract? ------
- ::
+ 2. If there is a non-null *already-paid-order-ID* for *session-ID* stored under the current order,
+ redirect to the *fulfillment-URL* of *already-paid-order-ID*. **Terminate**.
- taler{proto_suffix}://pay/{/merchant_prefix*}/{order-id}/{session-ID}?c={claim-token}
+ 3. Prompt the URI
- Redirect to the *fulfillment-URL* of *ord* once
- payment has been proven under *session-ID*.
+ ::
- If there is a non-null *already-paid-order-ID* for *session-ID*, redirect
- to the *fulfillment-URL* of *already-paid-order-ID*
+ taler{proto_suffix}://pay/{/merchant_prefix*}/{order-id}/{session-ID}?c={claim-token}
+ The generated Web site should long-poll to check for the payment happening.
+ It should then redirect to the *fulfillment-URL* of the order once
+ payment has been proven under *session-ID*, or possibly redirect to the
+ *already-paid-order-ID*. Which of these happens depends on the (long-polled) JSON replies.
**Terminate.**
Discussion / Q&A
================
+Notes
+-----
+
+* The *timeout_ms* argument is expected to be ignored when generating HTML.
+ Long-polling simply makes no sense if a browser accesses the site directly.
+
+
Covered Scenarios
-----------------
@@ -144,6 +176,14 @@ Covered Scenarios
Problematic Scenarios
---------------------
+Link sharing
+^^^^^^^^^^^^
+
+Right now, sharing the /orders/{order-ID} link with the session ID will
+allow someone who did not purchase the order to still get a 'paid' response
+from the backend. (Will fulfillment then work? => Check!)
+
+
Bookmarks of Lost Purchases / Social Sharing of Fulfillment URLs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^