exchange_api_post-kyc-wallet.c (8556B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2021-2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_post-kyc-wallet.c 19 * @brief Implementation of the /kyc-wallet request 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include <microhttpd.h> /* just for HTTP status codes */ 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_curl_lib.h> 26 #include "taler/taler_exchange_service.h" 27 #include "taler/taler_json_lib.h" 28 #include "taler/taler-exchange/post-kyc-wallet.h" 29 #include "taler/taler_signatures.h" 30 #include "exchange_api_curl_defaults.h" 31 #include "taler/taler_curl_lib.h" 32 33 34 /** 35 * @brief A POST /kyc-wallet handle 36 */ 37 struct TALER_EXCHANGE_PostKycWalletHandle 38 { 39 40 /** 41 * Context for curl easy post. Keeps the data that must 42 * persist for Curl to make the upload. 43 */ 44 struct TALER_CURL_PostContext post_ctx; 45 46 /** 47 * The base URL for this request. 48 */ 49 char *base_url; 50 51 /** 52 * The full URL for this request, set during _start. 53 */ 54 char *url; 55 56 /** 57 * Handle for the request. 58 */ 59 struct GNUNET_CURL_Job *job; 60 61 /** 62 * Function to call with the result. 63 */ 64 TALER_EXCHANGE_PostKycWalletCallback cb; 65 66 /** 67 * Closure for @e cb. 68 */ 69 TALER_EXCHANGE_POST_KYC_WALLET_RESULT_CLOSURE *cb_cls; 70 71 /** 72 * Reference to the execution context. 73 */ 74 struct GNUNET_CURL_Context *ctx; 75 76 /** 77 * Reserve private key of the wallet. 78 */ 79 struct TALER_ReservePrivateKeyP reserve_priv; 80 81 /** 82 * Balance (or balance threshold) crossed by the wallet. 83 */ 84 struct TALER_Amount balance; 85 86 }; 87 88 89 /** 90 * Function called when we're done processing the 91 * HTTP /kyc-wallet request. 92 * 93 * @param cls the `struct TALER_EXCHANGE_PostKycWalletHandle` 94 * @param response_code HTTP response code, 0 on error 95 * @param response parsed JSON result, NULL on error 96 */ 97 static void 98 handle_kyc_wallet_finished (void *cls, 99 long response_code, 100 const void *response) 101 { 102 struct TALER_EXCHANGE_PostKycWalletHandle *pkwh = cls; 103 const json_t *j = response; 104 struct TALER_EXCHANGE_PostKycWalletResponse ks = { 105 .hr.reply = j, 106 .hr.http_status = (unsigned int) response_code 107 }; 108 109 pkwh->job = NULL; 110 switch (response_code) 111 { 112 case 0: 113 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 114 break; 115 case MHD_HTTP_OK: 116 { 117 struct GNUNET_JSON_Specification spec[] = { 118 GNUNET_JSON_spec_mark_optional ( 119 TALER_JSON_spec_amount_any ( 120 "next_threshold", 121 &ks.details.ok.next_threshold), 122 NULL), 123 GNUNET_JSON_spec_timestamp ( 124 "expiration_time", 125 &ks.details.ok.expiration_time), 126 GNUNET_JSON_spec_end () 127 }; 128 129 if (GNUNET_OK != 130 GNUNET_JSON_parse (j, 131 spec, 132 NULL, NULL)) 133 { 134 GNUNET_break_op (0); 135 ks.hr.http_status = 0; 136 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 137 break; 138 } 139 break; 140 } 141 case MHD_HTTP_NO_CONTENT: 142 break; 143 case MHD_HTTP_BAD_REQUEST: 144 ks.hr.ec = TALER_JSON_get_error_code (j); 145 /* This should never happen, either us or the exchange is buggy 146 (or API version conflict); just pass JSON reply to the application */ 147 break; 148 case MHD_HTTP_FORBIDDEN: 149 ks.hr.ec = TALER_JSON_get_error_code (j); 150 break; 151 case MHD_HTTP_NOT_FOUND: 152 ks.hr.ec = TALER_JSON_get_error_code (j); 153 break; 154 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 155 { 156 struct GNUNET_JSON_Specification spec[] = { 157 GNUNET_JSON_spec_fixed_auto ( 158 "h_payto", 159 &ks.details.unavailable_for_legal_reasons.h_payto), 160 GNUNET_JSON_spec_uint64 ( 161 "requirement_row", 162 &ks.details.unavailable_for_legal_reasons.requirement_row), 163 GNUNET_JSON_spec_end () 164 }; 165 166 if (GNUNET_OK != 167 GNUNET_JSON_parse (j, 168 spec, 169 NULL, NULL)) 170 { 171 GNUNET_break_op (0); 172 ks.hr.http_status = 0; 173 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 174 break; 175 } 176 break; 177 } 178 case MHD_HTTP_INTERNAL_SERVER_ERROR: 179 ks.hr.ec = TALER_JSON_get_error_code (j); 180 /* Server had an internal issue; we should retry, but this API 181 leaves this to the application */ 182 break; 183 default: 184 /* unexpected response code */ 185 GNUNET_break_op (0); 186 ks.hr.ec = TALER_JSON_get_error_code (j); 187 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 188 "Unexpected response code %u/%d for exchange /kyc-wallet\n", 189 (unsigned int) response_code, 190 (int) ks.hr.ec); 191 break; 192 } 193 if (NULL != pkwh->cb) 194 { 195 pkwh->cb (pkwh->cb_cls, 196 &ks); 197 pkwh->cb = NULL; 198 } 199 TALER_EXCHANGE_post_kyc_wallet_cancel (pkwh); 200 } 201 202 203 struct TALER_EXCHANGE_PostKycWalletHandle * 204 TALER_EXCHANGE_post_kyc_wallet_create ( 205 struct GNUNET_CURL_Context *ctx, 206 const char *url, 207 const struct TALER_ReservePrivateKeyP *reserve_priv, 208 const struct TALER_Amount *balance) 209 { 210 struct TALER_EXCHANGE_PostKycWalletHandle *pkwh; 211 212 pkwh = GNUNET_new (struct TALER_EXCHANGE_PostKycWalletHandle); 213 pkwh->ctx = ctx; 214 pkwh->base_url = GNUNET_strdup (url); 215 pkwh->reserve_priv = *reserve_priv; 216 pkwh->balance = *balance; 217 return pkwh; 218 } 219 220 221 enum TALER_ErrorCode 222 TALER_EXCHANGE_post_kyc_wallet_start ( 223 struct TALER_EXCHANGE_PostKycWalletHandle *pkwh, 224 TALER_EXCHANGE_PostKycWalletCallback cb, 225 TALER_EXCHANGE_POST_KYC_WALLET_RESULT_CLOSURE *cb_cls) 226 { 227 CURL *eh; 228 json_t *req; 229 struct TALER_ReservePublicKeyP reserve_pub; 230 struct TALER_ReserveSignatureP reserve_sig; 231 232 pkwh->cb = cb; 233 pkwh->cb_cls = cb_cls; 234 GNUNET_CRYPTO_eddsa_key_get_public (&pkwh->reserve_priv.eddsa_priv, 235 &reserve_pub.eddsa_pub); 236 TALER_wallet_account_setup_sign (&pkwh->reserve_priv, 237 &pkwh->balance, 238 &reserve_sig); 239 req = GNUNET_JSON_PACK ( 240 TALER_JSON_pack_amount ("balance", 241 &pkwh->balance), 242 GNUNET_JSON_pack_data_auto ("reserve_pub", 243 &reserve_pub), 244 GNUNET_JSON_pack_data_auto ("reserve_sig", 245 &reserve_sig)); 246 GNUNET_assert (NULL != req); 247 pkwh->url = TALER_url_join (pkwh->base_url, 248 "kyc-wallet", 249 NULL); 250 if (NULL == pkwh->url) 251 { 252 json_decref (req); 253 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 254 } 255 eh = TALER_EXCHANGE_curl_easy_get_ (pkwh->url); 256 if ( (NULL == eh) || 257 (GNUNET_OK != 258 TALER_curl_easy_post (&pkwh->post_ctx, 259 eh, 260 req)) ) 261 { 262 GNUNET_break (0); 263 if (NULL != eh) 264 curl_easy_cleanup (eh); 265 json_decref (req); 266 GNUNET_free (pkwh->url); 267 pkwh->url = NULL; 268 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 269 } 270 json_decref (req); 271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 272 "Requesting URL '%s'\n", 273 pkwh->url); 274 pkwh->job = GNUNET_CURL_job_add2 (pkwh->ctx, 275 eh, 276 pkwh->post_ctx.headers, 277 &handle_kyc_wallet_finished, 278 pkwh); 279 if (NULL == pkwh->job) 280 { 281 TALER_curl_easy_post_finished (&pkwh->post_ctx); 282 GNUNET_free (pkwh->url); 283 pkwh->url = NULL; 284 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 285 } 286 return TALER_EC_NONE; 287 } 288 289 290 void 291 TALER_EXCHANGE_post_kyc_wallet_cancel ( 292 struct TALER_EXCHANGE_PostKycWalletHandle *pkwh) 293 { 294 if (NULL != pkwh->job) 295 { 296 GNUNET_CURL_job_cancel (pkwh->job); 297 pkwh->job = NULL; 298 } 299 TALER_curl_easy_post_finished (&pkwh->post_ctx); 300 GNUNET_free (pkwh->url); 301 GNUNET_free (pkwh->base_url); 302 GNUNET_free (pkwh); 303 } 304 305 306 /* end of exchange_api_post-kyc-wallet.c */