merchant

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

merchant_api_post-private-accounts.c (9604B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2023-2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Lesser General Public License as
      7   published by the Free Software Foundation; either version 2.1,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU Lesser General Public License for more details.
     14 
     15   You should have received a copy of the GNU Lesser General
     16   Public License along with TALER; see the file COPYING.LGPL.
     17   If not, see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file merchant_api_post-private-accounts.c
     21  * @brief Implementation of the POST /private/accounts request
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/platform.h"
     25 #include <curl/curl.h>
     26 #include <jansson.h>
     27 #include <microhttpd.h> /* just for HTTP status codes */
     28 #include <gnunet/gnunet_util_lib.h>
     29 #include <gnunet/gnunet_curl_lib.h>
     30 #include <taler/taler-merchant/post-private-accounts.h>
     31 #include "merchant_api_curl_defaults.h"
     32 #include "merchant_api_common.h"
     33 #include <taler/taler_json_lib.h>
     34 #include <taler/taler_curl_lib.h>
     35 
     36 
     37 /**
     38  * Handle for a POST /private/accounts operation.
     39  */
     40 struct TALER_MERCHANT_PostPrivateAccountsHandle
     41 {
     42   /**
     43    * Base URL of the merchant backend.
     44    */
     45   char *base_url;
     46 
     47   /**
     48    * The full URL for this request.
     49    */
     50   char *url;
     51 
     52   /**
     53    * Handle for the request.
     54    */
     55   struct GNUNET_CURL_Job *job;
     56 
     57   /**
     58    * Function to call with the result.
     59    */
     60   TALER_MERCHANT_PostPrivateAccountsCallback cb;
     61 
     62   /**
     63    * Closure for @a cb.
     64    */
     65   TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_RESULT_CLOSURE *cb_cls;
     66 
     67   /**
     68    * Reference to the execution context.
     69    */
     70   struct GNUNET_CURL_Context *ctx;
     71 
     72   /**
     73    * Minor context that holds body and headers.
     74    */
     75   struct TALER_CURL_PostContext post_ctx;
     76 
     77   /**
     78    * Payto URI to add.
     79    */
     80   struct TALER_FullPayto payto_uri;
     81 
     82   /**
     83    * Optional credit facade URL.
     84    */
     85   char *credit_facade_url;
     86 
     87   /**
     88    * Optional credit facade credentials (JSON).
     89    */
     90   json_t *credit_facade_credentials;
     91 
     92   /**
     93    * Optional extra wire subject metadata (JSON).
     94    */
     95   char *extra_wire_subject_metadata;
     96 };
     97 
     98 
     99 /**
    100  * Function called when we're done processing the
    101  * HTTP POST /private/accounts request.
    102  *
    103  * @param cls the `struct TALER_MERCHANT_PostPrivateAccountsHandle`
    104  * @param response_code HTTP response code, 0 on error
    105  * @param response response body, NULL if not in JSON
    106  */
    107 static void
    108 handle_post_account_finished (void *cls,
    109                               long response_code,
    110                               const void *response)
    111 {
    112   struct TALER_MERCHANT_PostPrivateAccountsHandle *pah = cls;
    113   const json_t *json = response;
    114   struct TALER_MERCHANT_PostPrivateAccountsResponse apr = {
    115     .hr.http_status = (unsigned int) response_code,
    116     .hr.reply = json
    117   };
    118 
    119   pah->job = NULL;
    120   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    121               "POST /private/accounts completed with response code %u\n",
    122               (unsigned int) response_code);
    123   switch (response_code)
    124   {
    125   case 0:
    126     apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    127     break;
    128   case MHD_HTTP_OK:
    129     {
    130       struct GNUNET_JSON_Specification spec[] = {
    131         GNUNET_JSON_spec_fixed_auto ("h_wire",
    132                                      &apr.details.ok.h_wire),
    133         GNUNET_JSON_spec_fixed_auto ("salt",
    134                                      &apr.details.ok.salt),
    135         GNUNET_JSON_spec_end ()
    136       };
    137 
    138       if (GNUNET_OK !=
    139           GNUNET_JSON_parse (json,
    140                              spec,
    141                              NULL, NULL))
    142       {
    143         GNUNET_break_op (0);
    144         apr.hr.http_status = 0;
    145         apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    146         break;
    147       }
    148     }
    149     break;
    150   case MHD_HTTP_ACCEPTED:
    151     if (GNUNET_OK !=
    152         TALER_MERCHANT_parse_mfa_challenge_response_ (
    153           json,
    154           &apr.details.accepted))
    155     {
    156       GNUNET_break_op (0);
    157       apr.hr.http_status = 0;
    158       apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    159     }
    160     break;
    161   case MHD_HTTP_BAD_REQUEST:
    162     GNUNET_break_op (0);
    163     apr.hr.ec = TALER_JSON_get_error_code (json);
    164     apr.hr.hint = TALER_JSON_get_error_hint (json);
    165     break;
    166   case MHD_HTTP_FORBIDDEN:
    167     apr.hr.ec = TALER_JSON_get_error_code (json);
    168     apr.hr.hint = TALER_JSON_get_error_hint (json);
    169     break;
    170   case MHD_HTTP_NOT_FOUND:
    171     apr.hr.ec = TALER_JSON_get_error_code (json);
    172     apr.hr.hint = TALER_JSON_get_error_hint (json);
    173     break;
    174   case MHD_HTTP_CONFLICT:
    175     apr.hr.ec = TALER_JSON_get_error_code (json);
    176     apr.hr.hint = TALER_JSON_get_error_hint (json);
    177     break;
    178   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    179     apr.hr.ec = TALER_JSON_get_error_code (json);
    180     apr.hr.hint = TALER_JSON_get_error_hint (json);
    181     break;
    182   default:
    183     TALER_MERCHANT_parse_error_details_ (json,
    184                                          response_code,
    185                                          &apr.hr);
    186     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    187                 "Unexpected response code %u/%d\n",
    188                 (unsigned int) response_code,
    189                 (int) apr.hr.ec);
    190     GNUNET_break_op (0);
    191     break;
    192   }
    193   pah->cb (pah->cb_cls,
    194            &apr);
    195   if (MHD_HTTP_ACCEPTED == response_code)
    196     TALER_MERCHANT_mfa_challenge_response_free (
    197       &apr.details.accepted);
    198   TALER_MERCHANT_post_private_accounts_cancel (pah);
    199 }
    200 
    201 
    202 struct TALER_MERCHANT_PostPrivateAccountsHandle *
    203 TALER_MERCHANT_post_private_accounts_create (
    204   struct GNUNET_CURL_Context *ctx,
    205   const char *url,
    206   struct TALER_FullPayto payto_uri)
    207 {
    208   struct TALER_MERCHANT_PostPrivateAccountsHandle *pah;
    209 
    210   pah = GNUNET_new (struct TALER_MERCHANT_PostPrivateAccountsHandle);
    211   pah->ctx = ctx;
    212   pah->base_url = GNUNET_strdup (url);
    213   pah->payto_uri.full_payto = GNUNET_strdup (payto_uri.full_payto);
    214   return pah;
    215 }
    216 
    217 
    218 enum GNUNET_GenericReturnValue
    219 TALER_MERCHANT_post_private_accounts_set_options_ (
    220   struct TALER_MERCHANT_PostPrivateAccountsHandle *pah,
    221   unsigned int num_options,
    222   const struct TALER_MERCHANT_PostPrivateAccountsOptionValue *options)
    223 {
    224   for (unsigned int i = 0; i < num_options; i++)
    225   {
    226     switch (options[i].option)
    227     {
    228     case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_END:
    229       return GNUNET_OK;
    230     case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_CREDIT_FACADE_URL:
    231       GNUNET_free (pah->credit_facade_url);
    232       pah->credit_facade_url = GNUNET_strdup (options[i].details.credit_facade_url);
    233       break;
    234     case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_CREDIT_FACADE_CREDENTIALS:
    235       json_decref (pah->credit_facade_credentials);
    236       pah->credit_facade_credentials
    237         = json_incref ((json_t *) options[i].details.credit_facade_credentials);
    238       break;
    239     case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_EXTRA_WIRE_SUBJECT_METADATA:
    240       GNUNET_free (pah->extra_wire_subject_metadata);
    241       pah->extra_wire_subject_metadata
    242         = GNUNET_strdup (options[i].details.extra_wire_subject_metadata);
    243       break;
    244     default:
    245       GNUNET_break (0);
    246       return GNUNET_SYSERR;
    247     }
    248   }
    249   return GNUNET_OK;
    250 }
    251 
    252 
    253 enum TALER_ErrorCode
    254 TALER_MERCHANT_post_private_accounts_start (
    255   struct TALER_MERCHANT_PostPrivateAccountsHandle *pah,
    256   TALER_MERCHANT_PostPrivateAccountsCallback cb,
    257   TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_RESULT_CLOSURE *cb_cls)
    258 {
    259   json_t *req_obj;
    260   CURL *eh;
    261 
    262   pah->cb = cb;
    263   pah->cb_cls = cb_cls;
    264   pah->url = TALER_url_join (pah->base_url,
    265                              "private/accounts",
    266                              NULL);
    267   if (NULL == pah->url)
    268     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    269   req_obj = GNUNET_JSON_PACK (
    270     TALER_JSON_pack_full_payto (
    271       "payto_uri",
    272       pah->payto_uri),
    273     GNUNET_JSON_pack_allow_null (
    274       GNUNET_JSON_pack_string (
    275         "credit_facade_url",
    276         pah->credit_facade_url)),
    277     GNUNET_JSON_pack_allow_null (
    278       GNUNET_JSON_pack_object_incref (
    279         "credit_facade_credentials",
    280         pah->credit_facade_credentials)),
    281     GNUNET_JSON_pack_allow_null (
    282       GNUNET_JSON_pack_string (
    283         "extra_wire_subject_metadata",
    284         pah->extra_wire_subject_metadata)));
    285   eh = TALER_MERCHANT_curl_easy_get_ (pah->url);
    286   if ( (NULL == eh) ||
    287        (GNUNET_OK !=
    288         TALER_curl_easy_post (&pah->post_ctx,
    289                               eh,
    290                               req_obj)) )
    291   {
    292     GNUNET_break (0);
    293     json_decref (req_obj);
    294     if (NULL != eh)
    295       curl_easy_cleanup (eh);
    296     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    297   }
    298   json_decref (req_obj);
    299   pah->job = GNUNET_CURL_job_add2 (pah->ctx,
    300                                    eh,
    301                                    pah->post_ctx.headers,
    302                                    &handle_post_account_finished,
    303                                    pah);
    304   if (NULL == pah->job)
    305     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    306   return TALER_EC_NONE;
    307 }
    308 
    309 
    310 void
    311 TALER_MERCHANT_post_private_accounts_cancel (
    312   struct TALER_MERCHANT_PostPrivateAccountsHandle *pah)
    313 {
    314   if (NULL != pah->job)
    315   {
    316     GNUNET_CURL_job_cancel (pah->job);
    317     pah->job = NULL;
    318   }
    319   TALER_curl_easy_post_finished (&pah->post_ctx);
    320   GNUNET_free (pah->payto_uri.full_payto);
    321   GNUNET_free (pah->url);
    322   GNUNET_free (pah->credit_facade_url);
    323   json_decref (pah->credit_facade_credentials);
    324   GNUNET_free (pah->extra_wire_subject_metadata);
    325   GNUNET_free (pah->base_url);
    326   GNUNET_free (pah);
    327 }
    328 
    329 
    330 /* end of merchant_api_post-private-accounts.c */