merchant

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

merchant_api_post-orders-ORDER_ID-paid.c (8340B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020-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-orders-ORDER_ID-paid-new.c
     21  * @brief Implementation of the POST /orders/$ORDER_ID/paid 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-orders-ORDER_ID-paid.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 #include <taler/taler_signatures.h>
     36 
     37 
     38 /**
     39  * Handle for a POST /orders/$ORDER_ID/paid operation.
     40  */
     41 struct TALER_MERCHANT_PostOrdersPaidHandle
     42 {
     43   /**
     44    * Base URL of the merchant backend.
     45    */
     46   char *base_url;
     47 
     48   /**
     49    * The full URL for this request.
     50    */
     51   char *url;
     52 
     53   /**
     54    * Handle for the request.
     55    */
     56   struct GNUNET_CURL_Job *job;
     57 
     58   /**
     59    * Function to call with the result.
     60    */
     61   TALER_MERCHANT_PostOrdersPaidCallback cb;
     62 
     63   /**
     64    * Closure for @a cb.
     65    */
     66   TALER_MERCHANT_POST_ORDERS_PAID_RESULT_CLOSURE *cb_cls;
     67 
     68   /**
     69    * Reference to the execution context.
     70    */
     71   struct GNUNET_CURL_Context *ctx;
     72 
     73   /**
     74    * Minor context that holds body and headers.
     75    */
     76   struct TALER_CURL_PostContext post_ctx;
     77 
     78   /**
     79    * Order identifier.
     80    */
     81   char *order_id;
     82 
     83   /**
     84    * Session identifier.
     85    */
     86   char *session_id;
     87 
     88   /**
     89    * Hash of the contract terms.
     90    */
     91   struct TALER_PrivateContractHashP h_contract_terms;
     92 
     93   /**
     94    * Merchant signature over the session data.
     95    */
     96   struct TALER_MerchantSignatureP merchant_sig;
     97 
     98 };
     99 
    100 
    101 /**
    102  * Function called when we're done processing the
    103  * HTTP POST /orders/$ORDER_ID/paid request.
    104  *
    105  * @param cls the `struct TALER_MERCHANT_PostOrdersPaidHandle`
    106  * @param response_code HTTP response code, 0 on error
    107  * @param response response body, NULL if not in JSON
    108  */
    109 static void
    110 handle_paid_finished (void *cls,
    111                       long response_code,
    112                       const void *response)
    113 {
    114   struct TALER_MERCHANT_PostOrdersPaidHandle *poph = cls;
    115   const json_t *json = response;
    116   struct TALER_MERCHANT_PostOrdersPaidResponse opr = {
    117     .hr.http_status = (unsigned int) response_code,
    118     .hr.reply = json
    119   };
    120 
    121   poph->job = NULL;
    122   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    123               "POST /orders/$ID/paid completed with response code %u\n",
    124               (unsigned int) response_code);
    125   switch (response_code)
    126   {
    127   case 0:
    128     opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    129     break;
    130   case MHD_HTTP_OK:
    131     {
    132       struct GNUNET_JSON_Specification spec[] = {
    133         GNUNET_JSON_spec_mark_optional (
    134           GNUNET_JSON_spec_string ("pos_confirmation",
    135                                    &opr.details.ok.pos_confirmation),
    136           NULL),
    137         GNUNET_JSON_spec_bool ("refunded",
    138                                &opr.details.ok.refunded),
    139         GNUNET_JSON_spec_end ()
    140       };
    141 
    142       if (GNUNET_OK !=
    143           GNUNET_JSON_parse (json,
    144                              spec,
    145                              NULL, NULL))
    146       {
    147         opr.hr.http_status = 0;
    148         opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    149         break;
    150       }
    151     }
    152     break;
    153   case MHD_HTTP_BAD_REQUEST:
    154     opr.hr.ec = TALER_JSON_get_error_code (json);
    155     opr.hr.hint = TALER_JSON_get_error_hint (json);
    156     break;
    157   case MHD_HTTP_FORBIDDEN:
    158     opr.hr.ec = TALER_JSON_get_error_code (json);
    159     opr.hr.hint = TALER_JSON_get_error_hint (json);
    160     break;
    161   case MHD_HTTP_NOT_FOUND:
    162     opr.hr.ec = TALER_JSON_get_error_code (json);
    163     opr.hr.hint = TALER_JSON_get_error_hint (json);
    164     break;
    165   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    166     opr.hr.ec = TALER_JSON_get_error_code (json);
    167     opr.hr.hint = TALER_JSON_get_error_hint (json);
    168     break;
    169   default:
    170     TALER_MERCHANT_parse_error_details_ (json,
    171                                          response_code,
    172                                          &opr.hr);
    173     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    174                 "Unexpected response code %u/%d\n",
    175                 (unsigned int) response_code,
    176                 (int) opr.hr.ec);
    177     GNUNET_break_op (0);
    178     break;
    179   }
    180   poph->cb (poph->cb_cls,
    181             &opr);
    182   TALER_MERCHANT_post_orders_paid_cancel (poph);
    183 }
    184 
    185 
    186 struct TALER_MERCHANT_PostOrdersPaidHandle *
    187 TALER_MERCHANT_post_orders_paid_create (
    188   struct GNUNET_CURL_Context *ctx,
    189   const char *url,
    190   const char *order_id,
    191   const char *session_id,
    192   const struct TALER_PrivateContractHashP *h_contract_terms,
    193   const struct TALER_MerchantSignatureP *merchant_sig)
    194 {
    195   struct TALER_MERCHANT_PostOrdersPaidHandle *poph;
    196 
    197   poph = GNUNET_new (struct TALER_MERCHANT_PostOrdersPaidHandle);
    198   poph->ctx = ctx;
    199   poph->base_url = GNUNET_strdup (url);
    200   poph->order_id = GNUNET_strdup (order_id);
    201   poph->session_id = GNUNET_strdup (session_id);
    202   poph->h_contract_terms = *h_contract_terms;
    203   poph->merchant_sig = *merchant_sig;
    204   return poph;
    205 }
    206 
    207 
    208 enum GNUNET_GenericReturnValue
    209 TALER_MERCHANT_post_orders_paid_set_options_ (
    210   struct TALER_MERCHANT_PostOrdersPaidHandle *poph,
    211   unsigned int num_options,
    212   const struct TALER_MERCHANT_PostOrdersPaidOptionValue *options)
    213 {
    214   for (unsigned int i = 0; i < num_options; i++)
    215   {
    216     switch (options[i].option)
    217     {
    218     case TALER_MERCHANT_POST_ORDERS_PAID_OPTION_END:
    219       return GNUNET_OK;
    220     default:
    221       GNUNET_break (0);
    222       return GNUNET_SYSERR;
    223     }
    224   }
    225   return GNUNET_OK;
    226 }
    227 
    228 
    229 enum TALER_ErrorCode
    230 TALER_MERCHANT_post_orders_paid_start (
    231   struct TALER_MERCHANT_PostOrdersPaidHandle *poph,
    232   TALER_MERCHANT_PostOrdersPaidCallback cb,
    233   TALER_MERCHANT_POST_ORDERS_PAID_RESULT_CLOSURE *cb_cls)
    234 {
    235   json_t *req_obj;
    236   CURL *eh;
    237 
    238   poph->cb = cb;
    239   poph->cb_cls = cb_cls;
    240   {
    241     char *path;
    242 
    243     GNUNET_asprintf (&path,
    244                      "orders/%s/paid",
    245                      poph->order_id);
    246     poph->url = TALER_url_join (poph->base_url,
    247                                 path,
    248                                 NULL);
    249     GNUNET_free (path);
    250   }
    251   if (NULL == poph->url)
    252     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    253   req_obj = GNUNET_JSON_PACK (
    254     GNUNET_JSON_pack_data_auto ("sig",
    255                                 &poph->merchant_sig),
    256     GNUNET_JSON_pack_data_auto ("h_contract",
    257                                 &poph->h_contract_terms),
    258     GNUNET_JSON_pack_string ("session_id",
    259                              poph->session_id));
    260   eh = TALER_MERCHANT_curl_easy_get_ (poph->url);
    261   if ( (NULL == eh) ||
    262        (GNUNET_OK !=
    263         TALER_curl_easy_post (&poph->post_ctx,
    264                               eh,
    265                               req_obj)) )
    266   {
    267     GNUNET_break (0);
    268     json_decref (req_obj);
    269     if (NULL != eh)
    270       curl_easy_cleanup (eh);
    271     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    272   }
    273   json_decref (req_obj);
    274   poph->job = GNUNET_CURL_job_add2 (poph->ctx,
    275                                     eh,
    276                                     poph->post_ctx.headers,
    277                                     &handle_paid_finished,
    278                                     poph);
    279   if (NULL == poph->job)
    280     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    281   return TALER_EC_NONE;
    282 }
    283 
    284 
    285 void
    286 TALER_MERCHANT_post_orders_paid_cancel (
    287   struct TALER_MERCHANT_PostOrdersPaidHandle *poph)
    288 {
    289   if (NULL != poph->job)
    290   {
    291     GNUNET_CURL_job_cancel (poph->job);
    292     poph->job = NULL;
    293   }
    294   TALER_curl_easy_post_finished (&poph->post_ctx);
    295   GNUNET_free (poph->order_id);
    296   GNUNET_free (poph->session_id);
    297   GNUNET_free (poph->url);
    298   GNUNET_free (poph->base_url);
    299   GNUNET_free (poph);
    300 }
    301 
    302 
    303 /* end of merchant_api_post-orders-ORDER_ID-paid-new.c */