exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

bank_api_registration.c (11042B)


      1 /*
      2   This file is part of TALER
      3   Copyright (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 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 bank-lib/bank_api_registration.c
     19  * @brief Implementation of the POST /registration request of the bank's HTTP API
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include "bank_api_common.h"
     24 #include <microhttpd.h> /* just for HTTP status codes */
     25 #include "taler/taler_signatures.h"
     26 #include "taler/taler_curl_lib.h"
     27 
     28 
     29 /**
     30  * @brief A /registration Handle
     31  */
     32 struct TALER_BANK_RegistrationHandle
     33 {
     34 
     35   /**
     36    * The URL for this request.
     37    */
     38   char *request_url;
     39 
     40   /**
     41    * POST context.
     42    */
     43   struct TALER_CURL_PostContext post_ctx;
     44 
     45   /**
     46    * Handle for the request.
     47    */
     48   struct GNUNET_CURL_Job *job;
     49 
     50   /**
     51    * Function to call with the result.
     52    */
     53   TALER_BANK_RegistrationCallback cb;
     54 
     55   /**
     56    * Closure for @a cb.
     57    */
     58   void *cb_cls;
     59 
     60 };
     61 
     62 
     63 /**
     64  * Parse the "subject" field of a successful /registration response.
     65  * The field is a JSON object discriminated by "type".
     66  *
     67  * @param subject_json the JSON object to parse (the inner "subject" value)
     68  * @param[out] ts set to the parsed transfer subject on success
     69  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
     70  */
     71 static enum GNUNET_GenericReturnValue
     72 parse_transfer_subject (const json_t *subject_json,
     73                         struct TALER_BANK_TransferSubject *ts)
     74 {
     75   const char *type_str;
     76   struct GNUNET_JSON_Specification type_spec[] = {
     77     GNUNET_JSON_spec_string ("type",
     78                              &type_str),
     79     GNUNET_JSON_spec_end ()
     80   };
     81 
     82   if (GNUNET_OK !=
     83       GNUNET_JSON_parse (subject_json,
     84                          type_spec,
     85                          NULL,
     86                          NULL))
     87   {
     88     GNUNET_break_op (0);
     89     return GNUNET_SYSERR;
     90   }
     91 
     92   if (0 == strcasecmp (type_str,
     93                        "SIMPLE"))
     94   {
     95     struct GNUNET_JSON_Specification spec[] = {
     96       TALER_JSON_spec_amount_any (
     97         "credit_amount",
     98         &ts->details.simple.credit_amount),
     99       GNUNET_JSON_spec_string (
    100         "subject",
    101         (const char **) &ts->details.simple.subject),
    102       GNUNET_JSON_spec_end ()
    103     };
    104 
    105     if (GNUNET_OK !=
    106         GNUNET_JSON_parse (subject_json,
    107                            spec,
    108                            NULL, NULL))
    109     {
    110       GNUNET_break_op (0);
    111       return GNUNET_SYSERR;
    112     }
    113     ts->format = TALER_BANK_SUBJECT_FORMAT_SIMPLE;
    114     return GNUNET_OK;
    115   }
    116   if (0 == strcasecmp (type_str,
    117                        "URI"))
    118   {
    119     struct GNUNET_JSON_Specification spec[] = {
    120       GNUNET_JSON_spec_string (
    121         "uri",
    122         (const char **) &ts->details.uri.uri),
    123       GNUNET_JSON_spec_end ()
    124     };
    125 
    126     if (GNUNET_OK !=
    127         GNUNET_JSON_parse (subject_json,
    128                            spec,
    129                            NULL, NULL))
    130     {
    131       GNUNET_break_op (0);
    132       return GNUNET_SYSERR;
    133     }
    134     ts->format = TALER_BANK_SUBJECT_FORMAT_URI;
    135     return GNUNET_OK;
    136   }
    137   if (0 == strcasecmp (type_str,
    138                        "CH_QR_BILL"))
    139   {
    140     struct GNUNET_JSON_Specification spec[] = {
    141       TALER_JSON_spec_amount_any (
    142         "credit_amount",
    143         &ts->details.ch_qr_bill.credit_amount),
    144       GNUNET_JSON_spec_string (
    145         "qr_reference_number",
    146         (const char **) &ts->details.ch_qr_bill.qr_reference_number),
    147       GNUNET_JSON_spec_end ()
    148     };
    149 
    150     if (GNUNET_OK !=
    151         GNUNET_JSON_parse (subject_json,
    152                            spec,
    153                            NULL, NULL))
    154     {
    155       GNUNET_break_op (0);
    156       return GNUNET_SYSERR;
    157     }
    158     ts->format = TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL;
    159     return GNUNET_OK;
    160   }
    161   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    162               "Unknown transfer subject type `%s'\n",
    163               type_str);
    164   GNUNET_break_op (0);
    165   return GNUNET_SYSERR;
    166 }
    167 
    168 
    169 /**
    170  * Function called when we're done processing the HTTP POST /registration
    171  * request.
    172  *
    173  * @param cls the `struct TALER_BANK_RegistrationHandle`
    174  * @param response_code HTTP response code, 0 on error
    175  * @param response parsed JSON result, NULL on error
    176  */
    177 static void
    178 handle_registration_finished (void *cls,
    179                               long response_code,
    180                               const void *response)
    181 {
    182   struct TALER_BANK_RegistrationHandle *rh = cls;
    183   const json_t *j = response;
    184   struct TALER_BANK_RegistrationResponse rr = {
    185     .http_status = response_code,
    186     .response = response
    187   };
    188 
    189   rh->job = NULL;
    190   switch (response_code)
    191   {
    192   case 0:
    193     rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    194     break;
    195   case MHD_HTTP_OK:
    196     {
    197       const json_t *subjects;
    198       struct GNUNET_JSON_Specification spec[] = {
    199         GNUNET_JSON_spec_array_const ("subjects",
    200                                       &subjects),
    201         GNUNET_JSON_spec_timestamp ("expiration",
    202                                     &rr.details.ok.expiration),
    203         GNUNET_JSON_spec_end ()
    204       };
    205 
    206       if (GNUNET_OK !=
    207           GNUNET_JSON_parse (j,
    208                              spec,
    209                              NULL, NULL))
    210       {
    211         GNUNET_break_op (0);
    212         rr.http_status = 0;
    213         rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    214         break;
    215       }
    216 
    217       {
    218         size_t n = json_array_size (subjects);
    219         struct TALER_BANK_TransferSubject ts[GNUNET_NZL (n)];
    220         size_t i;
    221         const json_t *subject;
    222 
    223         json_array_foreach ((json_t *) subjects, i, subject)
    224         {
    225           if (GNUNET_OK !=
    226               parse_transfer_subject (subject,
    227                                       &ts[i]))
    228           {
    229             GNUNET_break_op (0);
    230             rr.http_status = 0;
    231             rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    232             break;
    233           }
    234         }
    235         if (MHD_HTTP_OK == rr.http_status)
    236         {
    237           rr.details.ok.num_subjects = n;
    238           rr.details.ok.subjects = ts;
    239           rh->cb (rh->cb_cls,
    240                   &rr);
    241           TALER_BANK_registration_cancel (rh);
    242           return;
    243         }
    244       }
    245     }
    246     break;
    247   case MHD_HTTP_BAD_REQUEST:
    248     /* Either we or the service is buggy, or there is an API version conflict. */
    249     GNUNET_break_op (0);
    250     rr.ec = TALER_JSON_get_error_code (j);
    251     break;
    252   case MHD_HTTP_CONFLICT:
    253     /* Covers TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
    254        TALER_EC_BANK_UNSUPPORTED_SUBJECT_FORMAT,
    255        TALER_EC_BANK_DERIVATION_REUSE, and
    256        TALER_EC_BANK_BAD_SIGNATURE. */
    257     rr.ec = TALER_JSON_get_error_code (j);
    258     break;
    259   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    260     /* Server had an internal issue; we should retry, but this API
    261        leaves that to the application. */
    262     rr.ec = TALER_JSON_get_error_code (j);
    263     break;
    264   default:
    265     /* unexpected response code */
    266     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    267                 "Unexpected response code %u\n",
    268                 (unsigned int) response_code);
    269     GNUNET_break (0);
    270     rr.ec = TALER_JSON_get_error_code (j);
    271     break;
    272   }
    273   rh->cb (rh->cb_cls,
    274           &rr);
    275   TALER_BANK_registration_cancel (rh);
    276 }
    277 
    278 
    279 struct TALER_BANK_RegistrationHandle *
    280 TALER_BANK_registration (
    281   struct GNUNET_CURL_Context *ctx,
    282   const char *base_url,
    283   const struct TALER_Amount *credit_amount,
    284   enum TALER_BANK_RegistrationType type,
    285   const union TALER_AccountPublicKeyP *account_pub,
    286   const struct TALER_ReserveMapAuthorizationPrivateKeyP *authorization_priv,
    287   bool recurrent,
    288   TALER_BANK_RegistrationCallback res_cb,
    289   void *res_cb_cls)
    290 {
    291   struct TALER_ReserveMapAuthorizationPublicKeyP authorization_pub;
    292   struct TALER_ReserveMapAuthorizationSignatureP authorization_sig;
    293   struct TALER_BANK_RegistrationHandle *rh;
    294   const char *type_str;
    295   json_t *reg_obj;
    296   CURL *eh;
    297 
    298   TALER_wallet_reserve_map_authorization_sign (account_pub,
    299                                                authorization_priv,
    300                                                &authorization_sig);
    301   GNUNET_CRYPTO_eddsa_key_get_public (&authorization_priv->eddsa_priv,
    302                                       &authorization_pub.eddsa_pub);
    303 
    304   switch (type)
    305   {
    306   case TALER_BANK_REGISTRATION_TYPE_RESERVE:
    307     type_str = "reserve";
    308     break;
    309   case TALER_BANK_REGISTRATION_TYPE_KYC:
    310     type_str = "kyc";
    311     break;
    312   default:
    313     GNUNET_break (0);
    314     return NULL;
    315   }
    316 
    317   reg_obj = GNUNET_JSON_PACK (
    318     TALER_JSON_pack_amount ("credit_amount",
    319                             credit_amount),
    320     GNUNET_JSON_pack_string ("type",
    321                              type_str),
    322     GNUNET_JSON_pack_string ("alg",
    323                              "EdDSA"),
    324     GNUNET_JSON_pack_data_auto ("account_pub",
    325                                 account_pub),
    326     GNUNET_JSON_pack_data_auto ("authorization_pub",
    327                                 &authorization_pub),
    328     GNUNET_JSON_pack_data_auto ("authorization_sig",
    329                                 &authorization_sig),
    330     GNUNET_JSON_pack_bool ("recurrent",
    331                            recurrent));
    332   rh = GNUNET_new (struct TALER_BANK_RegistrationHandle);
    333   rh->cb = res_cb;
    334   rh->cb_cls = res_cb_cls;
    335   rh->request_url = TALER_url_join (base_url,
    336                                     "registration",
    337                                     NULL);
    338   if (NULL == rh->request_url)
    339   {
    340     GNUNET_free (rh);
    341     json_decref (reg_obj);
    342     return NULL;
    343   }
    344   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    345               "Requesting wire transfer subject registration at `%s'\n",
    346               rh->request_url);
    347   eh = curl_easy_init ();
    348   if ( (NULL == eh) ||
    349        (CURLE_OK !=
    350         curl_easy_setopt (eh,
    351                           CURLOPT_URL,
    352                           rh->request_url)) ||
    353        (GNUNET_OK !=
    354         TALER_curl_easy_post (&rh->post_ctx,
    355                               eh,
    356                               reg_obj)) )
    357   {
    358     GNUNET_break (0);
    359     TALER_BANK_registration_cancel (rh);
    360     if (NULL != eh)
    361       curl_easy_cleanup (eh);
    362     json_decref (reg_obj);
    363     return NULL;
    364   }
    365   json_decref (reg_obj);
    366   rh->job = GNUNET_CURL_job_add2 (ctx,
    367                                   eh,
    368                                   rh->post_ctx.headers,
    369                                   &handle_registration_finished,
    370                                   rh);
    371   return rh;
    372 }
    373 
    374 
    375 void
    376 TALER_BANK_registration_cancel (
    377   struct TALER_BANK_RegistrationHandle *rh)
    378 {
    379   if (NULL != rh->job)
    380   {
    381     GNUNET_CURL_job_cancel (rh->job);
    382     rh->job = NULL;
    383   }
    384   TALER_curl_easy_post_finished (&rh->post_ctx);
    385   GNUNET_free (rh->request_url);
    386   GNUNET_free (rh);
    387 }
    388 
    389 
    390 /* end of bank_api_registration.c */