From 551739c033f29fd21d6110def4f8ad87615b1b93 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 9 Aug 2020 12:16:56 +0200 Subject: points for discussion --- design-documents/007-payment.rst | 100 +++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 30 deletions(-) (limited to 'design-documents') diff --git a/design-documents/007-payment.rst b/design-documents/007-payment.rst index 5294103d..aacd163a 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit v1.2.3