merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.c (21502B)


      1 /*
      2   This file is part of TALER
      3   (C) 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 Affero 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 Affero General Public License
     14   along with TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file src/backend/taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.c
     18  * @brief Handle POST /private/accounts/$H_WIRE/kycauth requests
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include <jansson.h>
     23 #include <microhttpd.h>
     24 #include <gnunet/gnunet_json_lib.h>
     25 #include <taler/taler_json_lib.h>
     26 #include <taler/taler_mhd_lib.h>
     27 #include <taler/taler_bank_service.h>
     28 #include "taler-merchant-httpd.h"
     29 #include "taler-merchant-httpd_exchanges.h"
     30 #include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
     31 #include "merchant-database/get_account.h"
     32 #include "taler/taler_util.h"
     33 
     34 
     35 /**
     36  * Processing phases for a kycauth POST request.
     37  */
     38 enum KycAuthPhase
     39 {
     40   /**
     41    * Parse the request body and URL path.
     42    */
     43   KP_PARSE = 0,
     44 
     45   /**
     46    * Fetch exchange /keys (and optionally register with bank gateway).
     47    * The connection is suspended in this phase.
     48    */
     49   KP_CHECK_EXCHANGES,
     50 
     51   /**
     52    * Return the prepared response to the client.
     53    */
     54   KP_RETURN_RESPONSE,
     55 
     56   /**
     57    * Return MHD_YES to finish handling.
     58    */
     59   KP_END_YES,
     60 
     61   /**
     62    * Return MHD_NO to close the connection.
     63    */
     64   KP_END_NO
     65 };
     66 
     67 
     68 struct ExchangeAccount
     69 {
     70 
     71   /**
     72    * Payto URI of the exchange, saved from /keys.
     73    */
     74   struct TALER_FullPayto payto;
     75 
     76   /**
     77    * Pending bank gateway registration handle, or NULL.
     78    */
     79   struct TALER_BANK_RegistrationHandle *brh;
     80 
     81   /**
     82    * Context we are operating in.
     83    */
     84   struct KycAuthContext *kac;
     85 
     86   /**
     87    * Transfer subjects to use for the account.
     88    */
     89   struct TALER_BANK_TransferSubject *subjects;
     90 
     91   /**
     92    * Length of the @e subjects array.
     93    */
     94   unsigned int num_subjects;
     95 
     96   /**
     97    * Expiration time for the given @e ts.
     98    */
     99   struct GNUNET_TIME_Timestamp expiration;
    100 };
    101 
    102 
    103 /**
    104  * Per-request context for POST /private/accounts/$H_WIRE/kycauth.
    105  */
    106 struct KycAuthContext
    107 {
    108 
    109   /**
    110    * Kept in doubly-linked list.
    111    */
    112   struct KycAuthContext *next;
    113 
    114   /**
    115    * Kept in doubly-linked list.
    116    */
    117   struct KycAuthContext *prev;
    118 
    119   /**
    120    * MHD connection we are handling.
    121    */
    122   struct MHD_Connection *connection;
    123 
    124   /**
    125    * Handler context for this request.
    126    */
    127   struct TMH_HandlerContext *hc;
    128 
    129   /**
    130    * Prepared HTTP response to return, or NULL.
    131    */
    132   struct MHD_Response *response;
    133 
    134   /**
    135    * Exchange URL from the request body.
    136    */
    137   char *exchange_url;
    138 
    139   /**
    140    * Payto URI of our merchant bank account (from DB).
    141    */
    142   struct TALER_FullPayto payto_uri;
    143 
    144   /**
    145    * Pending /keys lookup operation with the exchange.
    146    */
    147   struct TMH_EXCHANGES_KeysOperation *fo;
    148 
    149   /**
    150    * Tiny amount from exchange /keys, used as the transfer amount.
    151    */
    152   struct TALER_Amount tiny_amount;
    153 
    154   /**
    155    * Array of exchange wire account payto URIs, saved from /keys.
    156    * Length is @e exchange_accounts_len.
    157    */
    158   struct ExchangeAccount *exchange_accounts;
    159 
    160   /**
    161    * Number of entries in @e exchange_accounts.
    162    */
    163   unsigned int exchange_accounts_len;
    164 
    165   /**
    166    * Number of active registrations in @e exchange_accounts.
    167    */
    168   unsigned int active_registrations;
    169 
    170   /**
    171    * HTTP status code to send with @e response.
    172    * UINT_MAX indicates a hard error (return MHD_NO).
    173    */
    174   unsigned int response_code;
    175 
    176   /**
    177    * Current processing phase.
    178    */
    179   enum KycAuthPhase phase;
    180 
    181   /**
    182    * #GNUNET_NO if the connection is not suspended,
    183    * #GNUNET_YES if the connection is suspended,
    184    * #GNUNET_SYSERR if suspended and being force-resumed during shutdown.
    185    */
    186   enum GNUNET_GenericReturnValue suspended;
    187 
    188 };
    189 
    190 
    191 /**
    192  * Head of the doubly-linked list of active kycauth contexts.
    193  */
    194 static struct KycAuthContext *kac_head;
    195 
    196 /**
    197  * Tail of the doubly-linked list of active kycauth contexts.
    198  */
    199 static struct KycAuthContext *kac_tail;
    200 
    201 
    202 void
    203 TMH_force_kac_resume (void)
    204 {
    205   for (struct KycAuthContext *kac = kac_head;
    206        NULL != kac;
    207        kac = kac->next)
    208   {
    209     if (NULL != kac->fo)
    210     {
    211       TMH_EXCHANGES_keys4exchange_cancel (kac->fo);
    212       kac->fo = NULL;
    213     }
    214     if (GNUNET_YES == kac->suspended)
    215     {
    216       kac->suspended = GNUNET_SYSERR;
    217       MHD_resume_connection (kac->connection);
    218     }
    219   }
    220 }
    221 
    222 
    223 /**
    224  * Resume processing of @a kac after an async operation completed.
    225  *
    226  * @param[in,out] kac context to resume
    227  */
    228 static void
    229 kac_resume (struct KycAuthContext *kac)
    230 {
    231   GNUNET_assert (GNUNET_YES == kac->suspended);
    232   kac->suspended = GNUNET_NO;
    233   MHD_resume_connection (kac->connection);
    234   TALER_MHD_daemon_trigger ();
    235 }
    236 
    237 
    238 /**
    239  * Clean up a kycauth context, freeing all resources.
    240  *
    241  * @param cls a `struct KycAuthContext` to clean up
    242  */
    243 static void
    244 kac_cleanup (void *cls)
    245 {
    246   struct KycAuthContext *kac = cls;
    247 
    248   if (NULL != kac->fo)
    249   {
    250     TMH_EXCHANGES_keys4exchange_cancel (kac->fo);
    251     kac->fo = NULL;
    252   }
    253   if (NULL != kac->response)
    254   {
    255     MHD_destroy_response (kac->response);
    256     kac->response = NULL;
    257   }
    258   GNUNET_free (kac->exchange_url);
    259   GNUNET_free (kac->payto_uri.full_payto);
    260   for (unsigned int i = 0; i < kac->exchange_accounts_len; i++)
    261   {
    262     struct ExchangeAccount *acc = &kac->exchange_accounts[i];
    263 
    264     if (NULL != acc->brh)
    265     {
    266       TALER_BANK_registration_cancel (acc->brh);
    267       acc->brh = NULL;
    268     }
    269     for (unsigned int j = 0; j<acc->num_subjects; j++)
    270       TALER_BANK_transfer_subject_free (&acc->subjects[j]);
    271     GNUNET_free (acc->subjects);
    272     GNUNET_free (acc->payto.full_payto);
    273   }
    274   GNUNET_free (kac->exchange_accounts);
    275   GNUNET_CONTAINER_DLL_remove (kac_head,
    276                                kac_tail,
    277                                kac);
    278   GNUNET_free (kac);
    279 }
    280 
    281 
    282 /**
    283  * Convert a @a ts (transfer subject) to a JSON object.
    284  * The resulting object has a "type" field discriminating the format,
    285  * matching the JSON format returned by the bank gateway's /registration.
    286  *
    287  * @param acc general account details
    288  * @param ts the specific transfer subject to convert
    289  * @return freshly allocated JSON object, or NULL on error
    290  */
    291 static json_t *
    292 kyc_transfer_subject_to_json (const struct ExchangeAccount *acc,
    293                               const struct TALER_BANK_TransferSubject *ts)
    294 {
    295   struct KycAuthContext *kac = acc->kac;
    296   json_t *ret;
    297 
    298   switch (ts->format)
    299   {
    300   case TALER_BANK_SUBJECT_FORMAT_SIMPLE:
    301     {
    302       struct GNUNET_Buffer buf = { 0 };
    303       char *subject;
    304 
    305       GNUNET_buffer_write_str (&buf,
    306                                "KYC:");
    307       GNUNET_buffer_write_data_encoded (&buf,
    308                                         &kac->hc->instance->merchant_pub,
    309                                         sizeof (kac->hc->instance->merchant_pub));
    310       subject = GNUNET_buffer_reap_str (&buf);
    311       ret = GNUNET_JSON_PACK (
    312         GNUNET_JSON_pack_string ("type",
    313                                  "SIMPLE"),
    314         TALER_JSON_pack_amount ("credit_amount",
    315                                 &kac->tiny_amount),
    316         GNUNET_JSON_pack_string ("subject",
    317                                  subject));
    318       GNUNET_free (subject);
    319     }
    320     return ret;
    321   case TALER_BANK_SUBJECT_FORMAT_URI:
    322     return GNUNET_JSON_PACK (
    323       GNUNET_JSON_pack_string ("type",
    324                                "URI"),
    325       TALER_JSON_pack_amount ("credit_amount",
    326                               &kac->tiny_amount),
    327       GNUNET_JSON_pack_string ("uri",
    328                                ts->details.uri.uri));
    329   case TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL:
    330     return GNUNET_JSON_PACK (
    331       GNUNET_JSON_pack_string ("type",
    332                                "CH_QR_BILL"),
    333       TALER_JSON_pack_amount ("credit_amount",
    334                               &ts->details.ch_qr_bill.credit_amount),
    335       GNUNET_JSON_pack_string ("qr_reference_number",
    336                                ts->details.ch_qr_bill.qr_reference_number));
    337   }
    338   GNUNET_break (0);
    339   return NULL;
    340 }
    341 
    342 
    343 /**
    344  * Generate the final "OK" response for the request in @a kac.
    345  * Builds the wire_instructions JSON array from the saved exchange accounts
    346  * and the given transfer subject.
    347  *
    348  * @param[in,out] kac request with all the details ready to build a response
    349  */
    350 static void
    351 generate_ok_response (struct KycAuthContext *kac)
    352 {
    353   json_t *arr;
    354 
    355   arr = json_array ();
    356   GNUNET_assert (NULL != arr);
    357   for (unsigned int i = 0; i < kac->exchange_accounts_len; i++)
    358   {
    359     const struct ExchangeAccount *acc = &kac->exchange_accounts[i];
    360     json_t *subj;
    361     json_t *entry;
    362 
    363     for (unsigned int j = 0; j < acc->num_subjects; j++)
    364     {
    365       subj = kyc_transfer_subject_to_json (acc,
    366                                            &acc->subjects[j]);
    367       GNUNET_assert (NULL != subj);
    368       entry = GNUNET_JSON_PACK (
    369         TALER_JSON_pack_amount ("amount",
    370                                 &kac->tiny_amount),
    371         TALER_JSON_pack_full_payto ("target_payto",
    372                                     acc->payto),
    373         GNUNET_JSON_pack_object_steal ("subject",
    374                                        subj),
    375         GNUNET_JSON_pack_timestamp ("expiration",
    376                                     acc->expiration));
    377       GNUNET_assert (NULL != entry);
    378       GNUNET_assert (0 ==
    379                      json_array_append_new (arr,
    380                                             entry));
    381     }
    382   }
    383   kac->response_code = MHD_HTTP_OK;
    384   kac->response = TALER_MHD_MAKE_JSON_PACK (
    385     GNUNET_JSON_pack_array_steal ("wire_instructions",
    386                                   arr));
    387   kac->phase = KP_RETURN_RESPONSE;
    388   kac_resume (kac);
    389 }
    390 
    391 
    392 /**
    393  * Callback invoked with the result of the bank gateway /registration request.
    394  *
    395  * @param cls our `struct KycAuthContext`
    396  * @param rr response details
    397  */
    398 static void
    399 registration_cb (void *cls,
    400                  const struct TALER_BANK_RegistrationResponse *rr)
    401 {
    402   struct ExchangeAccount *acc = cls;
    403   struct KycAuthContext *kac = acc->kac;
    404 
    405   acc->brh = NULL;
    406   kac->active_registrations--;
    407   if (MHD_HTTP_OK != rr->http_status)
    408   {
    409     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    410                 "Bank gateway registration failed with HTTP status %u\n",
    411                 rr->http_status);
    412     if (NULL == kac->response)
    413     {
    414       kac->response_code = MHD_HTTP_BAD_GATEWAY;
    415       kac->response = TALER_MHD_make_error (
    416         TALER_EC_MERCHANT_POST_ACCOUNTS_KYCAUTH_BANK_GATEWAY_UNREACHABLE,
    417         "bank gateway /registration failed");
    418       kac->phase = KP_RETURN_RESPONSE;
    419       kac_resume (kac);
    420     }
    421     return;
    422   }
    423   GNUNET_free (acc->subjects);
    424   acc->num_subjects = rr->details.ok.num_subjects;
    425   acc->subjects = GNUNET_new_array (acc->num_subjects,
    426                                     struct TALER_BANK_TransferSubject);
    427   for (unsigned int i = 0; i<acc->num_subjects; i++)
    428   {
    429     TALER_BANK_transfer_subject_copy (&acc->subjects[i],
    430                                       &rr->details.ok.subjects[i]);
    431   }
    432   acc->expiration = rr->details.ok.expiration;
    433   if (0 != kac->active_registrations)
    434     return; /* wait for more replies */
    435   generate_ok_response (kac);
    436 }
    437 
    438 
    439 /**
    440  * Callback invoked with the /keys of the selected exchange.
    441  *
    442  * Saves tiny_amount and exchange account payto URIs from the keys, then
    443  * either calls the bank gateway /registration (if wire_transfer_gateway is
    444  * present) or directly constructs the SIMPLE-format response.
    445  *
    446  * @param cls our `struct KycAuthContext`
    447  * @param keys the exchange's key material, or NULL on failure
    448  * @param exchange internal exchange handle (unused)
    449  */
    450 static void
    451 process_keys_cb (void *cls,
    452                  struct TALER_EXCHANGE_Keys *keys,
    453                  struct TMH_Exchange *exchange)
    454 {
    455   struct KycAuthContext *kac = cls;
    456   struct TMH_MerchantInstance *mi = kac->hc->instance;
    457 
    458   (void) exchange;
    459   kac->fo = NULL;
    460   if (NULL == keys)
    461   {
    462     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    463                 "Could not get /keys from exchange `%s'\n",
    464                 kac->exchange_url);
    465     GNUNET_assert (NULL == kac->response);
    466     kac->response_code = MHD_HTTP_BAD_GATEWAY;
    467     kac->response = TALER_MHD_make_error (
    468       TALER_EC_MERCHANT_POST_ACCOUNTS_KYCAUTH_EXCHANGE_UNREACHABLE,
    469       kac->exchange_url);
    470     kac->phase = KP_RETURN_RESPONSE;
    471     kac_resume (kac);
    472     return;
    473   }
    474   if (! keys->tiny_amount_available)
    475   {
    476     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    477                 "Exchange `%s' did not provide tiny_amount in /keys\n",
    478                 kac->exchange_url);
    479     GNUNET_assert (NULL == kac->response);
    480     kac->response_code = MHD_HTTP_BAD_GATEWAY;
    481     kac->response = TALER_MHD_make_error (
    482       TALER_EC_MERCHANT_POST_ACCOUNTS_EXCHANGE_TOO_OLD,
    483       "exchange /keys missing tiny_amount");
    484     kac->phase = KP_RETURN_RESPONSE;
    485     kac_resume (kac);
    486     return;
    487   }
    488 
    489   /* Save tiny_amount and exchange wire accounts from keys */
    490   kac->tiny_amount = keys->tiny_amount;
    491   kac->exchange_accounts_len = keys->accounts_len;
    492   if (0 == keys->accounts_len)
    493   {
    494     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    495                 "Exchange `%s' did not provide any wire accounts in /keys\n",
    496                 kac->exchange_url);
    497     GNUNET_assert (NULL == kac->response);
    498     kac->response_code = MHD_HTTP_BAD_GATEWAY;
    499     kac->response = TALER_MHD_make_error (
    500       TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS,
    501       "exchange /keys missing wire accounts");
    502     kac->phase = KP_RETURN_RESPONSE;
    503     kac_resume (kac);
    504     return;
    505   }
    506 
    507   kac->exchange_accounts
    508     = GNUNET_new_array (keys->accounts_len,
    509                         struct ExchangeAccount);
    510   for (unsigned int i = 0; i < keys->accounts_len; i++)
    511   {
    512     struct ExchangeAccount *acc = &kac->exchange_accounts[i];
    513     struct TALER_PreparedTransferAuthorizationPrivateKeyP apk;
    514 
    515     GNUNET_CRYPTO_eddsa_key_create (&apk.eddsa_priv);
    516     acc->kac = kac;
    517     acc->payto.full_payto
    518       = GNUNET_strdup (keys->accounts[i].fpayto_uri.full_payto);
    519     acc->subjects = GNUNET_new (struct TALER_BANK_TransferSubject);
    520     acc->num_subjects = 1;
    521     acc->subjects[0].format = TALER_BANK_SUBJECT_FORMAT_SIMPLE;
    522     acc->expiration = GNUNET_TIME_UNIT_FOREVER_TS;
    523     if (NULL == keys->accounts[i].prepared_transfer_url)
    524       continue;
    525     acc->brh = TALER_BANK_registration (
    526       TMH_curl_ctx,
    527       keys->accounts[i].prepared_transfer_url,
    528       &keys->accounts[i].fpayto_uri,
    529       &kac->tiny_amount,
    530       TALER_BANK_REGISTRATION_TYPE_KYC,
    531       (const union TALER_AccountPublicKeyP *) &mi->merchant_pub,
    532       // FIXME: turn this into an option when modernizing the BANK API...
    533       &apk,
    534       false, /* recurrent */
    535       &registration_cb,
    536       acc);
    537     if (NULL == acc->brh)
    538     {
    539       GNUNET_break (0);
    540       GNUNET_assert (NULL == kac->response);
    541       kac->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    542       kac->response = TALER_MHD_make_error (
    543         TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    544         "could not start bank gateway registration");
    545       kac->phase = KP_RETURN_RESPONSE;
    546       kac_resume (kac);
    547       return;
    548     }
    549     kac->active_registrations++;
    550   }
    551   if (0 != kac->active_registrations)
    552     return;
    553   generate_ok_response (kac);
    554 }
    555 
    556 
    557 /**
    558  * Process the PARSE phase: validate the URL path, parse the request body,
    559  * and look up the merchant account in the database.
    560  *
    561  * @param[in,out] kac context to process
    562  */
    563 static void
    564 phase_parse (struct KycAuthContext *kac)
    565 {
    566   const char *h_wire_s = kac->hc->infix;
    567   struct TALER_MerchantWireHashP h_wire;
    568   const char *exchange_url;
    569   enum GNUNET_GenericReturnValue res;
    570   struct GNUNET_JSON_Specification spec[] = {
    571     GNUNET_JSON_spec_string ("exchange_url",
    572                              &exchange_url),
    573     GNUNET_JSON_spec_end ()
    574   };
    575 
    576   GNUNET_assert (NULL != h_wire_s);
    577   if (GNUNET_OK !=
    578       GNUNET_STRINGS_string_to_data (h_wire_s,
    579                                      strlen (h_wire_s),
    580                                      &h_wire,
    581                                      sizeof (h_wire)))
    582   {
    583     GNUNET_break_op (0);
    584     kac->response_code = MHD_HTTP_BAD_REQUEST;
    585     kac->response = TALER_MHD_make_error (
    586       TALER_EC_MERCHANT_GENERIC_H_WIRE_MALFORMED,
    587       h_wire_s);
    588     kac->phase = KP_RETURN_RESPONSE;
    589     return;
    590   }
    591 
    592   res = TALER_MHD_parse_json_data (kac->connection,
    593                                    kac->hc->request_body,
    594                                    spec);
    595   if (GNUNET_OK != res)
    596   {
    597     kac->phase = (GNUNET_NO == res) ? KP_END_YES : KP_END_NO;
    598     return;
    599   }
    600 
    601   /* Look up the merchant account in the database */
    602   {
    603     struct TALER_MERCHANTDB_AccountDetails ad = { 0 };
    604     enum GNUNET_DB_QueryStatus qs;
    605 
    606     qs = TALER_MERCHANTDB_get_account (TMH_db,
    607                                        kac->hc->instance->settings.id,
    608                                        &h_wire,
    609                                        &ad);
    610     if (0 > qs)
    611     {
    612       GNUNET_break (0);
    613       GNUNET_JSON_parse_free (spec);
    614       kac->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    615       kac->response = TALER_MHD_make_error (
    616         TALER_EC_GENERIC_DB_FETCH_FAILED,
    617         "get_account");
    618       kac->phase = KP_RETURN_RESPONSE;
    619       return;
    620     }
    621     if (0 == qs)
    622     {
    623       GNUNET_JSON_parse_free (spec);
    624       kac->response_code = MHD_HTTP_NOT_FOUND;
    625       kac->response = TALER_MHD_make_error (
    626         TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN,
    627         h_wire_s);
    628       kac->phase = KP_RETURN_RESPONSE;
    629       return;
    630     }
    631     kac->payto_uri.full_payto = GNUNET_strdup (ad.payto_uri.full_payto);
    632     /* Free fields we do not need */
    633     GNUNET_free (ad.payto_uri.full_payto);
    634     GNUNET_free (ad.credit_facade_url);
    635     GNUNET_free (ad.extra_wire_subject_metadata);
    636     json_decref (ad.credit_facade_credentials);
    637   }
    638 
    639   kac->exchange_url = GNUNET_strdup (exchange_url);
    640   GNUNET_JSON_parse_free (spec);
    641   kac->phase = KP_CHECK_EXCHANGES;
    642 }
    643 
    644 
    645 /**
    646  * Process the CHECK_EXCHANGES phase: start an async /keys fetch from
    647  * the requested exchange and suspend the connection.
    648  *
    649  * @param[in,out] kac context to process
    650  */
    651 static void
    652 phase_check_exchanges (struct KycAuthContext *kac)
    653 {
    654   kac->fo = TMH_EXCHANGES_keys4exchange (kac->exchange_url,
    655                                          false,
    656                                          &process_keys_cb,
    657                                          kac);
    658   if (NULL == kac->fo)
    659   {
    660     GNUNET_break_op (0);
    661     kac->response_code = MHD_HTTP_BAD_REQUEST;
    662     kac->response = TALER_MHD_make_error (
    663       TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNTRUSTED,
    664       kac->exchange_url);
    665     kac->phase = KP_RETURN_RESPONSE;
    666     return;
    667   }
    668   MHD_suspend_connection (kac->connection);
    669   kac->suspended = GNUNET_YES;
    670 }
    671 
    672 
    673 /**
    674  * Process the RETURN_RESPONSE phase: queue the prepared response into MHD.
    675  *
    676  * @param[in,out] kac context to process
    677  */
    678 static void
    679 phase_return_response (struct KycAuthContext *kac)
    680 {
    681   enum MHD_Result ret;
    682 
    683   if (UINT_MAX == kac->response_code)
    684   {
    685     GNUNET_break (0);
    686     kac->phase = KP_END_NO;
    687     return;
    688   }
    689   GNUNET_assert (0 != kac->response_code);
    690   ret = MHD_queue_response (kac->connection,
    691                             kac->response_code,
    692                             kac->response);
    693   kac->phase = (MHD_YES == ret) ? KP_END_YES : KP_END_NO;
    694 }
    695 
    696 
    697 enum MHD_Result
    698 TMH_private_post_accounts_H_WIRE_kycauth (
    699   const struct TMH_RequestHandler *rh,
    700   struct MHD_Connection *connection,
    701   struct TMH_HandlerContext *hc)
    702 {
    703   struct KycAuthContext *kac = hc->ctx;
    704 
    705   (void) rh;
    706   GNUNET_assert (NULL != hc->infix);
    707   if (NULL == kac)
    708   {
    709     kac = GNUNET_new (struct KycAuthContext);
    710     kac->connection = connection;
    711     kac->hc = hc;
    712     hc->ctx = kac;
    713     hc->cc = &kac_cleanup;
    714     GNUNET_CONTAINER_DLL_insert (kac_head,
    715                                  kac_tail,
    716                                  kac);
    717   }
    718 
    719   while (1)
    720   {
    721     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    722                 "Processing POST /private/accounts/$H_WIRE/kycauth"
    723                 " in phase %d\n",
    724                 (int) kac->phase);
    725     switch (kac->phase)
    726     {
    727     case KP_PARSE:
    728       phase_parse (kac);
    729       break;
    730     case KP_CHECK_EXCHANGES:
    731       phase_check_exchanges (kac);
    732       break;
    733     case KP_RETURN_RESPONSE:
    734       phase_return_response (kac);
    735       break;
    736     case KP_END_YES:
    737       return MHD_YES;
    738     case KP_END_NO:
    739       return MHD_NO;
    740     default:
    741       GNUNET_assert (0);
    742       return MHD_NO;
    743     }
    744 
    745     switch (kac->suspended)
    746     {
    747     case GNUNET_SYSERR:
    748       /* Shutdown in progress */
    749       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    750                   "Processing POST /private/accounts/$H_WIRE/kycauth"
    751                   " ends due to shutdown in phase %d\n",
    752                   (int) kac->phase);
    753       return MHD_NO;
    754     case GNUNET_NO:
    755       /* Not suspended, continue with next phase */
    756       break;
    757     case GNUNET_YES:
    758       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    759                   "Processing POST /private/accounts/$H_WIRE/kycauth"
    760                   " suspended in phase %d\n",
    761                   (int) kac->phase);
    762       return MHD_YES;
    763     }
    764   }
    765   GNUNET_assert (0);
    766   return MHD_YES;
    767 }
    768 
    769 
    770 /* end of taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c */