064-kyc-operation-algo.rst (5799B)
1 DD 64: Algorithm for transactions with KYC checks 2 ################################################# 3 4 Summary 5 ======= 6 7 This design document specifies the algorithm that wallets and merchants should 8 use for processing and (re-)trying transactions where KYC checks may apply. 9 10 Motivation 11 ========== 12 13 The exchange requires the customer to pass KYC checks and satisfy AML rules for 14 various transactions. However, the corresponding rules are dynamic and may also be 15 hidden from the client for regulatory purposes. 16 17 Requirements 18 ============ 19 20 * Minimize the time time the user has to wait (=> long-poll efficiently) 21 * Minimize the number of requests that the exchange has do process (=> do not quickly 22 retry when it's clear the the request is unlikely to succeed now) 23 24 Proposed Solution 25 ================= 26 27 28 Steps for processing operation of type ``op`` and amount ``amt``: 29 30 31 Initialization 32 ^^^^^^^^^^^^^^ 33 34 Initialize the following variables: 35 36 * ``last_check_status := null`` 37 38 * Last HTTP status of the kyc-check request. 39 A status of ``0`` indicates a network failure or request timeout 40 and is distinct from ``null``, which indicates that no check 41 request has been made. 42 43 * ``last_check_code := null`` 44 45 * Taler error code of the last kyc-check request or ``null`` if no such 46 request has been made or the last request didn't return an error code. 47 48 * ``last_rule_gen := null`` 49 * ``last_aml_review := null`` 50 * ``last_deny := if isZeroLimited(op, amt) then now() else null`` 51 * ``last_bad_kyc_auth := false`` 52 * ``account_keypair := getCurrentAccountKeyPair(op)`` 53 54 Processing 55 ^^^^^^^^^^ 56 57 1. If ``last_deny`` is ``null`` or more than ``1h`` ago, 58 make a request for ``op`` at the exchange. Let ``resp`` be the response. 59 60 * If the request succeeds, *halt*. 61 * If the request fails with ``451``, set ``last_deny := now()`` and ``last_bad_kyc_auth := resp.bad_kyc_auth``. 62 * Otherwise, finish processing operation with result ``BACKOFF``. 63 64 2. Request the ``/kyc-check/...`` endpoint applicable for ``op`` with ``account_keypair`` the following parameters: 65 66 * If ``last_check_status == null``: Make request without long-polling. 67 * If ``last_check_status in [403 Forbidden, 409 Conflict]`` or ``last_bad_kyc_auth == true && last_check_status == 404``: Long-poll. Add query parameter ``lpt=1`` 68 * If ``last_aml_review == true``: Long-poll. Add query parameter ``lpt=2``. If ``last_rule_gen != null``, add 69 query parameter ``min_rule=last_rule_gen``. 70 * Otherwise: Long-poll. If ``last_rule_gen != null``, add ``min_rule=last_rule_gen`` 71 to the query parameters. 72 73 3. Handle the ``/kyc-check/...`` response: 74 75 * Set ``same_resp := resp.status == last_check_status and resp.code == last_check_status and resp.rule_gen == last_rule_gen``. 76 * Set ``last_check_status := resp.status``, ``last_check_code := resp.code``, ``last_rule_gen := resp.rule_gen`` 77 * If ``same_resp == true``: finish processing operation with result ``BACKOFF``. 78 * If ``resp.status == 204 No Content``: Set ``last_deny := null``. Finish processing operation with result ``PROGRESS`` (effectively 79 re-trying at step 1). 80 * If ``resp.status == 200 Ok``: Set ``last_deny := null``. Finish with result ``PROGRESS`` 81 * If ``resp.status == 202 Accepted``: Go to step 4. 82 * If ``resp.status == 403 Forbidden``: Check if the private key for the 83 indicated public key is available. If, set ``account_keypair`` to that key pair and finish with result ``PROGRESS``. 84 Otherwise, finish with result ``BACKOFF``. 85 * If ``resp.status == 404 Not Found``: 86 87 * If ``last_bad_kyc_auth == true``, finish 88 processing with result ``BACKOFF`` (transition asking the user for KYC auth). 89 * Otherwise, go to step 4, with exposed limits set to the default limits. 90 91 * Otherwise (unhandled status), finish processing with result ``BACKOFF``. 92 93 4. Handle exposed limits applicable to the account: 94 95 * If the exposed limits do not deny ``op``, set ``last_deny := null`` and finish processing operation with result ``PROGRESS``. 96 * If the exposed limits deny ``op`` as ``verboten``, set ``last_deny := now()`` and transition the transaction 97 to a ``failed`` state (Showing an error message 98 indicating that the operation is forbidden due to legal 99 restrictions on the payment service provider). Finish processing operation with result ``PROGRESS``. 100 * Otherwise (merely a threshold over some timeframe is currently 101 violated), compute the time ``t`` when the operation may be allowed again. 102 Finish processing operation with result ``AGAIN_AT(t)``. 103 104 **KYC auth state**: The user should be instructed to do a KYC auth transfer if 105 ``last_check_status in [403 Forbidden, 409 Conflict]`` or ``last_bad_kyc_auth == true && last_check_status == 404``. 106 107 Additional Considerations 108 ^^^^^^^^^^^^^^^^^^^^^^^^^ 109 110 * Ensure the long-polling interval specified in the request 111 works with the middleware. If we detect that a request timed 112 out before the specified long-polling interval, use a shorter 113 timeout argument in the HTTP request the next time 114 (but do still keep exponentially 115 backing off the actual request frequency). 116 117 * Lower the retry back-off for the transaction to zero if: 118 119 * the user manually reviews the KYC auth wire transfer instructions, or 120 * the user manually reviews KYC information page instructions, or 121 * if ``op`` is a deposit and ``lpt=1`` and a withdraw succeeded 122 from the same account 123 124 * Especially in step 4 (limit violated in timeframe), 125 the user should be offered the option to retry. 126 127 128 Definition of Done 129 ================== 130 131 N/A 132 133 Alternatives 134 ============ 135 136 N/A 137 138 Drawbacks 139 ========= 140 141 N/A 142 143 Discussion / Q&A 144 ================ 145 146 (This should be filled in with results from discussions on mailing lists / personal communication.)