From 945d92aa1b298b3b702140aa199f77a4d8fb48d1 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Sun, 9 Aug 2020 00:15:04 +0530 Subject: design doc for payment flow --- design-documents/007-payment.rst | 143 +++++++++++++++++++++++++++++++++++++++ design-documents/fees.rst | 120 ++++++++++++++++++++++++++++++++ design-documents/index.rst | 1 + 3 files changed, 264 insertions(+) create mode 100644 design-documents/007-payment.rst create mode 100644 design-documents/fees.rst (limited to 'design-documents') diff --git a/design-documents/007-payment.rst b/design-documents/007-payment.rst new file mode 100644 index 00000000..206d1f77 --- /dev/null +++ b/design-documents/007-payment.rst @@ -0,0 +1,143 @@ +Design Doc 007: Specification of the Payment Flow +################################################# + +Summary +======= + +This design document describes how the payment flow works in the browser, and how +features like session IDs, re-purchase detection and refunds interact. + +Requirements +============ + +* The payment flow must both support wallets that are integrated in the browser, + as well as external wallets (mobile phone, command line) +* The initiator of the payment can be a Website or another channel, + such as an e-mail or a messaging service. +* For paid digital content, there should be a reasonable technical barrier to + sharing the content with unauthorized users +* A simple API should be offered to shops +* Sharing of links or re-visiting of bookmarks should result in well-defined, + behavior instead of random, ugly error messages. + +Proposed Solution +================= + +Session-bound payment flow for Web resources +-------------------------------------------- + +In this payment flow, the user initiates the payment by navigating to a +paywalled Web resource. Let *resource-URL* be the URL of the paywalled resource. + +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 +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 content 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 +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.** +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 content for *resource name*. **Terminate.** +9. Otherwise, the *order-status* is unpaid. Redirect the client to *client-order-status-URL*. **Terminate.** + + +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}``: + +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**. +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 + + :: + + {backendBaseUrl}/orders/{order-ID}?h_contract={contract-hash}&session_id={session-ID} + +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}``: + +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 the *contract-hash* request parameter does not match the contract terms hash of *ord*, + return a 403 Forbidden response. **Terminate.** + 2. If *ord* has refunds that are not picked up, 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.** + + 3. Prompt the URI + + :: + + taler{proto_suffix}://pay/{/merchant_prefix*}/{order-id}/{session-id} + + Redirect to the *fulfillment-URL* of *ord* once + payment has been proven under *session-ID*. + + **Terminate.** + + +3. If *order-ID* identifies an unpaid order *ord*, 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 + + :: + + taler{proto_suffix}://pay/{/merchant_prefix*}/{order-id}/{session-ID}?c={claim-token} + + 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* + + **Terminate.** + + +Discussion / Q&A +================ + +Covered Scenarios +----------------- + +* **Re-purchase detection**. Let's say a detached wallet has already successfully paid for a resource URL. + A browser navigates to the resource URL. The storefront will generate a new order and assign a session ID. + Upon scanning the QR code, the wallet will detect that it already has puchased the resource (checked via the fulfillment URL). + It will then prove the payment of the **old** order ID under the **new** session ID. + + +Problematic Scenarios +--------------------- + +Let's say I bought some article a few months ago and I lost my wallet. I still have the augmented fulfillment URL +for the article bookmarked. When I re-visit the URL, I will be prompted via QR code, but I can *never* prove +that I already paid, because I lost my wallet! + +In this case, it might make sense to include some "make new purchase" link on the client order status page. +It's not clear if this is a common/important scenario though. + +But we might want to make clear on the client order status page that it's showing a QR code for something +that was already paid. diff --git a/design-documents/fees.rst b/design-documents/fees.rst new file mode 100644 index 00000000..96459423 --- /dev/null +++ b/design-documents/fees.rst @@ -0,0 +1,120 @@ +Design Doc 003: Fee Structure Metrics +##################################### + +.. note:: + + This design document is currently a draft, it + does not reflect any implementation decisions yet. + +Summary +======= + +We discuss criterea for evaluating an exchange's denomination and fee structure. + +Motivation +========== + +Currently, users of a Taler wallet can't easily judge the fee structure of an +exchange and what it will mean for them. Thus we want to define some metrics +that allow a user to make more informed decisions. + +Similarly, the fee structure metrics might be used by exchange operators +as a senity check. + +An auditor might also enforce ranges on these metrics as a condition for +auditing a denomination structure. + +Requirements +============ + +* Privacy: A denomination structure that has too many denominations + will result in reduced anonymity. +* Efficiency: A denomination structure that has too few denominations + is inefficient. +* Predictability: + + * The amount of fees for an operation should not come + as a total surprise for the user. + + * The user should know beforehand when they have to be online + to refresh their balance, and they should know how much + this will cost them. + +The metrics for the exchange should be advisory, i.e. an informed user should +be able to accept the withdrawal anyway. + +The exchange's business interests may be in conflict with (1) a transparency of +cost for the user and and (2) restrictions on denomination/fee structures. +Thus the metrics should still allow some degree of variability between +exchanges. + +We make the assumption that wallet always perfer operations with better +privacy. For example, a single coin should only be used for at most one +spend operation and at most one refresh operation. + +Proposed Solution +================= + +The following yes/no criteria are applied to determine if a denomination/fee structure +is *reasonable*: + +* For a denomination of value ``v``, either + + (a) ``v`` must be the the smallest offered denomination, or + (b) there must be another denomination with a value of at least ``v / 10``. + +Is there a relationship between the smallest denomination and the size of fees? + +Idea: When when only doing spends on a coin that are a multiple of the smallest spending amount, +we constrain the number of coins that are refreshed into. + +When evaluating the e2e cost of a denomiation, look at: + * the cost of withdrawing the coin by itself and spending it fully, directly + * the cost of withdrawing the coin, directly refreshing it into the next smallest + fully fitting currency (or use greedy fit!) and add up the withdraw, refresh and re-withdraw fees. + * percentage is always computed with sum against the full coin's value + * smallest coins are an exception + +Actually, smallest spendable amount should be a multiple of the smallest coins, since +we still need to deal with fees even at that level + + +Refund fees should not be higher than if the user got a refund through +a different channel than Taler and then purchased the product again. + + + +The following metrics are also defined: + +* Shortest time from withdrawal expiration to deposit expiration. +* Upkeep, i.e. how much does it cost the customer to keep coins in their wallet + per time period, on average over a long period. +* Assurance period, i.e. the time span for which the exchange has already announced + its fees. +* Spending amount range. +* End-to-end fee range. This is the minimum/maximum cost to withdraw electronic cash + (within the spending range), spend some of it and then obtain change for the transaction. +* Merchant contribution range. This is the percentage of the end-to-end fees that the merchant + can cover if they choose to do so. + + +Alternatives +============ + +* Users can manually study the fee structure. +* The auditor could impose a fee structure and not + allow any variability between exchanges + +Drawbacks +========= + +* The approach does not work well in some special-purpose deployments, + where the coin structure is taylored to the products of merchants, + and refreshing would never even happen. + +* The approach also does not consider more "creative" fee structures, + such as ones where coins that are valid for a longer time have higher + fees. + +Discussion / Q&A +================ diff --git a/design-documents/index.rst b/design-documents/index.rst index 37f81bd4..fda8b2e5 100644 --- a/design-documents/index.rst +++ b/design-documents/index.rst @@ -16,3 +16,4 @@ and protocol. 004-wallet-withdrawal-flow 005-wallet-backup-sync 006-anastasis-ux + 007-payment -- cgit v1.2.3