merchant

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

merchant_api_get-private-orders-ORDER_ID.c (20375B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2018-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 Lesser General Public License as published by the Free Software
      7   Foundation; either version 2.1, 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 Lesser General Public License for more details.
     12 
     13   You should have received a copy of the GNU Lesser General Public License along with
     14   TALER; see the file COPYING.LGPL.  If not, see
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file src/lib/merchant_api_get-private-orders-ORDER_ID.c
     19  * @brief Implementation of the GET /private/orders/$ORDER_ID request
     20  * @author Christian Grothoff
     21  */
     22 #include "platform.h"
     23 #include <curl/curl.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h> /* just for HTTP status codes */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <gnunet/gnunet_curl_lib.h>
     28 #include <taler/merchant/get-private-orders-ORDER_ID.h>
     29 #include "merchant_api_curl_defaults.h"
     30 #include <taler/taler_json_lib.h>
     31 
     32 
     33 /**
     34  * Maximum number of refund details we return.
     35  */
     36 #define MAX_REFUND_DETAILS 1024
     37 
     38 /**
     39  * Maximum number of wire details we return.
     40  */
     41 #define MAX_WIRE_DETAILS 1024
     42 
     43 
     44 /**
     45  * Handle for a GET /private/orders/$ORDER_ID operation.
     46  */
     47 struct TALER_MERCHANT_GetPrivateOrderHandle
     48 {
     49   /**
     50    * Base URL of the merchant backend.
     51    */
     52   char *base_url;
     53 
     54   /**
     55    * The full URL for this request.
     56    */
     57   char *url;
     58 
     59   /**
     60    * Handle for the request.
     61    */
     62   struct GNUNET_CURL_Job *job;
     63 
     64   /**
     65    * Function to call with the result.
     66    */
     67   TALER_MERCHANT_GetPrivateOrderCallback cb;
     68 
     69   /**
     70    * Closure for @a cb.
     71    */
     72   TALER_MERCHANT_GET_PRIVATE_ORDER_RESULT_CLOSURE *cb_cls;
     73 
     74   /**
     75    * Reference to the execution context.
     76    */
     77   struct GNUNET_CURL_Context *ctx;
     78 
     79   /**
     80    * Order ID.
     81    */
     82   char *order_id;
     83 
     84   /**
     85    * Session ID for repurchase detection, or NULL.
     86    */
     87   char *session_id;
     88 
     89   /**
     90    * Long polling timeout.
     91    */
     92   struct GNUNET_TIME_Relative timeout;
     93 
     94   /**
     95    * Long-poll ETag to suppress unchanged responses.
     96    */
     97   struct GNUNET_ShortHashCode lp_not_etag;
     98 
     99   /**
    100    * True if @e lp_not_etag was set.
    101    */
    102   bool have_lp_not_etag;
    103 
    104   /**
    105    * If true, try to obtain wire transfer status from the exchange.
    106    */
    107   bool transfer;
    108 
    109   /**
    110    * If true, allow refunded orders under already_paid_order_id.
    111    */
    112   bool allow_refunded_for_repurchase;
    113 };
    114 
    115 
    116 /**
    117  * Handle HTTP OK response for an unpaid order.
    118  *
    119  * @param oph handle for the request
    120  * @param[in,out] osr HTTP response we got
    121  */
    122 static void
    123 handle_unpaid (struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
    124                struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    125 {
    126   struct GNUNET_JSON_Specification spec[] = {
    127     GNUNET_JSON_spec_mark_optional (
    128       GNUNET_JSON_spec_string (
    129         "already_paid_order_id",
    130         &osr->details.ok.details.unpaid.already_paid_order_id),
    131       NULL),
    132     GNUNET_JSON_spec_mark_optional (
    133       GNUNET_JSON_spec_string (
    134         "already_paid_fulfillment_url",
    135         &osr->details.ok.details.unpaid.already_paid_fulfillment_url),
    136       NULL),
    137     GNUNET_JSON_spec_string (
    138       "taler_pay_uri",
    139       &osr->details.ok.details.unpaid.taler_pay_uri),
    140     GNUNET_JSON_spec_string (
    141       "summary",
    142       &osr->details.ok.details.unpaid.summary),
    143     GNUNET_JSON_spec_mark_optional (
    144       GNUNET_JSON_spec_object_const (
    145         "proto_contract_terms",
    146         &osr->details.ok.details.unpaid.proto_contract_terms),
    147       NULL),
    148     GNUNET_JSON_spec_string (
    149       "order_status_url",
    150       &osr->details.ok.details.unpaid.order_status_url),
    151     GNUNET_JSON_spec_timestamp (
    152       "creation_time",
    153       &osr->details.ok.details.unpaid.creation_time),
    154     GNUNET_JSON_spec_mark_optional (
    155       GNUNET_JSON_spec_timestamp (
    156         "pay_deadline",
    157         &osr->details.ok.details.unpaid.pay_deadline),
    158       NULL),
    159     GNUNET_JSON_spec_end ()
    160   };
    161 
    162   if (GNUNET_OK !=
    163       GNUNET_JSON_parse (osr->hr.reply,
    164                          spec,
    165                          NULL, NULL))
    166   {
    167     GNUNET_break_op (0);
    168     osr->hr.http_status = 0;
    169     osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    170     oph->cb (oph->cb_cls,
    171              osr);
    172     return;
    173   }
    174   osr->details.ok.status = TALER_MERCHANT_OSC_UNPAID;
    175   oph->cb (oph->cb_cls,
    176            osr);
    177 }
    178 
    179 
    180 /**
    181  * Handle HTTP OK response for a claimed order.
    182  *
    183  * @param oph handle for the request
    184  * @param[in,out] osr HTTP response we got
    185  */
    186 static void
    187 handle_claimed (struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
    188                 struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    189 {
    190   struct GNUNET_JSON_Specification spec[] = {
    191     GNUNET_JSON_spec_object_const (
    192       "contract_terms",
    193       &osr->details.ok.details.claimed.contract_terms),
    194     GNUNET_JSON_spec_mark_optional (
    195       GNUNET_JSON_spec_string (
    196         "order_status_url",
    197         &osr->details.ok.details.claimed.order_status_url),
    198       NULL),
    199     GNUNET_JSON_spec_end ()
    200   };
    201 
    202   if (GNUNET_OK !=
    203       GNUNET_JSON_parse (osr->hr.reply,
    204                          spec,
    205                          NULL, NULL))
    206   {
    207     GNUNET_break_op (0);
    208     osr->hr.http_status = 0;
    209     osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    210     oph->cb (oph->cb_cls,
    211              osr);
    212     return;
    213   }
    214   osr->details.ok.status = TALER_MERCHANT_OSC_CLAIMED;
    215   oph->cb (oph->cb_cls,
    216            osr);
    217 }
    218 
    219 
    220 /**
    221  * Handle HTTP OK response for a paid order.
    222  *
    223  * @param oph handle for the request
    224  * @param[in,out] osr HTTP response we got
    225  */
    226 static void
    227 handle_paid (struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
    228              struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    229 {
    230   uint32_t hc32;
    231   uint32_t ci32;
    232   bool ci_missing;
    233   const json_t *wire_details;
    234   const json_t *refund_details;
    235   struct GNUNET_JSON_Specification spec[] = {
    236     GNUNET_JSON_spec_bool ("refunded",
    237                            &osr->details.ok.details.paid.refunded),
    238     GNUNET_JSON_spec_bool ("refund_pending",
    239                            &osr->details.ok.details.paid.refund_pending),
    240     GNUNET_JSON_spec_bool ("wired",
    241                            &osr->details.ok.details.paid.wired),
    242     TALER_JSON_spec_amount_any ("deposit_total",
    243                                 &osr->details.ok.details.paid.deposit_total),
    244     TALER_JSON_spec_ec ("exchange_code",
    245                         &osr->details.ok.details.paid.exchange_ec),
    246     GNUNET_JSON_spec_uint32 ("exchange_http_status",
    247                              &hc32),
    248     TALER_JSON_spec_amount_any ("refund_amount",
    249                                 &osr->details.ok.details.paid.refund_amount),
    250     GNUNET_JSON_spec_object_const (
    251       "contract_terms",
    252       &osr->details.ok.details.paid.contract_terms),
    253     GNUNET_JSON_spec_mark_optional (
    254       GNUNET_JSON_spec_uint32 ("choice_index",
    255                                &ci32),
    256       &ci_missing),
    257     GNUNET_JSON_spec_array_const ("wire_details",
    258                                   &wire_details),
    259     GNUNET_JSON_spec_array_const ("refund_details",
    260                                   &refund_details),
    261     GNUNET_JSON_spec_mark_optional (
    262       GNUNET_JSON_spec_timestamp ("last_payment",
    263                                   &osr->details.ok.details.paid.last_payment),
    264       NULL),
    265     GNUNET_JSON_spec_string (
    266       "order_status_url",
    267       &osr->details.ok.details.paid.order_status_url),
    268     GNUNET_JSON_spec_end ()
    269   };
    270 
    271   if (GNUNET_OK !=
    272       GNUNET_JSON_parse (osr->hr.reply,
    273                          spec,
    274                          NULL, NULL))
    275   {
    276     GNUNET_break_op (0);
    277     osr->hr.http_status = 0;
    278     osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    279     oph->cb (oph->cb_cls,
    280              osr);
    281     return;
    282   }
    283   osr->details.ok.status = TALER_MERCHANT_OSC_PAID;
    284   osr->details.ok.details.paid.exchange_hc = (unsigned int) hc32;
    285   osr->details.ok.details.paid.choice_index = ci_missing
    286                                                ? -1
    287                                                : (int) ci32;
    288   {
    289     unsigned int wts_len = (unsigned int) json_array_size (wire_details);
    290     unsigned int ref_len = (unsigned int) json_array_size (refund_details);
    291 
    292     if ( (json_array_size (wire_details) != (size_t) wts_len) ||
    293          (wts_len > MAX_WIRE_DETAILS) )
    294     {
    295       GNUNET_break (0);
    296       osr->hr.http_status = 0;
    297       osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    298       oph->cb (oph->cb_cls,
    299                osr);
    300       return;
    301     }
    302     if ( (json_array_size (refund_details) != (size_t) ref_len) ||
    303          (ref_len > MAX_REFUND_DETAILS) )
    304     {
    305       GNUNET_break (0);
    306       osr->hr.http_status = 0;
    307       osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    308       oph->cb (oph->cb_cls,
    309                osr);
    310       return;
    311     }
    312     {
    313       struct TALER_MERCHANT_WireTransfer wts[GNUNET_NZL (wts_len)];
    314       struct TALER_MERCHANT_GetPrivateOrderRefundDetail ref[
    315         GNUNET_NZL (ref_len)];
    316 
    317       memset (wts,
    318               0,
    319               sizeof (wts));
    320       for (unsigned int i = 0; i<wts_len; i++)
    321       {
    322         struct TALER_MERCHANT_WireTransfer *wt = &wts[i];
    323         const json_t *w = json_array_get (wire_details,
    324                                           i);
    325         struct GNUNET_JSON_Specification ispec[] = {
    326           TALER_JSON_spec_web_url ("exchange_url",
    327                                    &wt->exchange_url),
    328           GNUNET_JSON_spec_fixed_auto ("wtid",
    329                                        &wt->wtid),
    330           GNUNET_JSON_spec_timestamp ("execution_time",
    331                                       &wt->execution_time),
    332           TALER_JSON_spec_amount_any ("amount",
    333                                       &wt->total_amount),
    334           GNUNET_JSON_spec_mark_optional (
    335             TALER_JSON_spec_amount_any ("deposit_fee",
    336                                         &wt->deposit_fee),
    337             NULL),
    338           GNUNET_JSON_spec_bool ("confirmed",
    339                                  &wt->confirmed),
    340           GNUNET_JSON_spec_mark_optional (
    341             GNUNET_JSON_spec_uint64 ("expected_transfer_serial_id",
    342                                      &wt->expected_transfer_serial_id),
    343             NULL),
    344           GNUNET_JSON_spec_end ()
    345         };
    346 
    347         if (GNUNET_OK !=
    348             GNUNET_JSON_parse (w,
    349                                ispec,
    350                                NULL, NULL))
    351         {
    352           GNUNET_break_op (0);
    353           osr->hr.http_status = 0;
    354           osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    355           oph->cb (oph->cb_cls,
    356                    osr);
    357           return;
    358         }
    359       }
    360 
    361       for (unsigned int i = 0; i<ref_len; i++)
    362       {
    363         struct TALER_MERCHANT_GetPrivateOrderRefundDetail *ro = &ref[i];
    364         const json_t *w = json_array_get (refund_details,
    365                                           i);
    366         struct GNUNET_JSON_Specification ispec[] = {
    367           TALER_JSON_spec_amount_any ("amount",
    368                                       &ro->refund_amount),
    369           GNUNET_JSON_spec_string ("reason",
    370                                    &ro->reason),
    371           GNUNET_JSON_spec_bool ("pending",
    372                                  &ro->pending),
    373           GNUNET_JSON_spec_timestamp ("timestamp",
    374                                       &ro->refund_time),
    375           GNUNET_JSON_spec_end ()
    376         };
    377 
    378         if (GNUNET_OK !=
    379             GNUNET_JSON_parse (w,
    380                                ispec,
    381                                NULL, NULL))
    382         {
    383           GNUNET_break_op (0);
    384           osr->hr.http_status = 0;
    385           osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    386           oph->cb (oph->cb_cls,
    387                    osr);
    388           return;
    389         }
    390       }
    391 
    392       osr->details.ok.details.paid.wts = wts;
    393       osr->details.ok.details.paid.wts_len = wts_len;
    394       osr->details.ok.details.paid.refunds = ref;
    395       osr->details.ok.details.paid.refunds_len = ref_len;
    396       oph->cb (oph->cb_cls,
    397                osr);
    398     }
    399   }
    400 }
    401 
    402 
    403 /**
    404  * Function called when we're done processing the
    405  * HTTP GET /private/orders/$ORDER_ID request.
    406  *
    407  * @param cls the `struct TALER_MERCHANT_GetPrivateOrderHandle`
    408  * @param response_code HTTP response code, 0 on error
    409  * @param response response body, NULL if not in JSON
    410  */
    411 static void
    412 handle_get_private_order_finished (void *cls,
    413                                    long response_code,
    414                                    const void *response)
    415 {
    416   struct TALER_MERCHANT_GetPrivateOrderHandle *oph = cls;
    417   const json_t *json = response;
    418   const char *order_status;
    419   struct TALER_MERCHANT_GetPrivateOrderResponse osr = {
    420     .hr.http_status = (unsigned int) response_code,
    421     .hr.reply = json
    422   };
    423 
    424   oph->job = NULL;
    425   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    426               "Got /private/orders/$ORDER_ID response with status code %u\n",
    427               (unsigned int) response_code);
    428   switch (response_code)
    429   {
    430   case MHD_HTTP_OK:
    431     /* see below */
    432     break;
    433   case MHD_HTTP_NOT_MODIFIED:
    434     oph->cb (oph->cb_cls,
    435              &osr);
    436     TALER_MERCHANT_get_private_order_cancel (oph);
    437     return;
    438   case MHD_HTTP_ACCEPTED:
    439     oph->cb (oph->cb_cls,
    440              &osr);
    441     TALER_MERCHANT_get_private_order_cancel (oph);
    442     return;
    443   case MHD_HTTP_UNAUTHORIZED:
    444     osr.hr.ec = TALER_JSON_get_error_code (json);
    445     osr.hr.hint = TALER_JSON_get_error_hint (json);
    446     oph->cb (oph->cb_cls,
    447              &osr);
    448     TALER_MERCHANT_get_private_order_cancel (oph);
    449     return;
    450   case MHD_HTTP_NOT_FOUND:
    451     osr.hr.ec = TALER_JSON_get_error_code (json);
    452     osr.hr.hint = TALER_JSON_get_error_hint (json);
    453     oph->cb (oph->cb_cls,
    454              &osr);
    455     TALER_MERCHANT_get_private_order_cancel (oph);
    456     return;
    457   case MHD_HTTP_GATEWAY_TIMEOUT:
    458     osr.hr.ec = TALER_JSON_get_error_code (json);
    459     osr.hr.hint = TALER_JSON_get_error_hint (json);
    460     oph->cb (oph->cb_cls,
    461              &osr);
    462     TALER_MERCHANT_get_private_order_cancel (oph);
    463     return;
    464   default:
    465     osr.hr.ec = TALER_JSON_get_error_code (json);
    466     osr.hr.hint = TALER_JSON_get_error_hint (json);
    467     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    468                 "Polling order status failed with HTTP status code %u/%d\n",
    469                 (unsigned int) response_code,
    470                 (int) osr.hr.ec);
    471     GNUNET_break_op (0);
    472     oph->cb (oph->cb_cls,
    473              &osr);
    474     TALER_MERCHANT_get_private_order_cancel (oph);
    475     return;
    476   }
    477 
    478   order_status = json_string_value (json_object_get (json,
    479                                                      "order_status"));
    480   if (NULL == order_status)
    481   {
    482     GNUNET_break_op (0);
    483     osr.hr.http_status = 0;
    484     osr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    485     oph->cb (oph->cb_cls,
    486              &osr);
    487     TALER_MERCHANT_get_private_order_cancel (oph);
    488     return;
    489   }
    490 
    491   if (0 == strcmp ("paid",
    492                    order_status))
    493   {
    494     handle_paid (oph,
    495                  &osr);
    496   }
    497   else if (0 == strcmp ("claimed",
    498                         order_status))
    499   {
    500     handle_claimed (oph,
    501                     &osr);
    502   }
    503   else if (0 == strcmp ("unpaid",
    504                         order_status))
    505   {
    506     handle_unpaid (oph,
    507                    &osr);
    508   }
    509   else
    510   {
    511     GNUNET_break_op (0);
    512     osr.hr.http_status = 0;
    513     osr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    514     oph->cb (oph->cb_cls,
    515              &osr);
    516   }
    517   TALER_MERCHANT_get_private_order_cancel (oph);
    518 }
    519 
    520 
    521 struct TALER_MERCHANT_GetPrivateOrderHandle *
    522 TALER_MERCHANT_get_private_order_create (
    523   struct GNUNET_CURL_Context *ctx,
    524   const char *url,
    525   const char *order_id)
    526 {
    527   struct TALER_MERCHANT_GetPrivateOrderHandle *oph;
    528 
    529   oph = GNUNET_new (struct TALER_MERCHANT_GetPrivateOrderHandle);
    530   oph->ctx = ctx;
    531   oph->base_url = GNUNET_strdup (url);
    532   oph->order_id = GNUNET_strdup (order_id);
    533   return oph;
    534 }
    535 
    536 
    537 enum GNUNET_GenericReturnValue
    538 TALER_MERCHANT_get_private_order_set_options_ (
    539   struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
    540   unsigned int num_options,
    541   const struct TALER_MERCHANT_GetPrivateOrderOptionValue *options)
    542 {
    543   for (unsigned int i = 0; i < num_options; i++)
    544   {
    545     const struct TALER_MERCHANT_GetPrivateOrderOptionValue *opt =
    546       &options[i];
    547 
    548     switch (opt->option)
    549     {
    550     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_END:
    551       return GNUNET_OK;
    552     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_SESSION_ID:
    553       GNUNET_free (oph->session_id);
    554       if (NULL != opt->details.session_id)
    555         oph->session_id = GNUNET_strdup (opt->details.session_id);
    556       break;
    557     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_TIMEOUT:
    558       oph->timeout = opt->details.timeout;
    559       break;
    560     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_TRANSFER:
    561       oph->transfer = opt->details.transfer;
    562       break;
    563     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_LP_NOT_ETAG:
    564       if (NULL != opt->details.lp_not_etag)
    565       {
    566         oph->lp_not_etag = *opt->details.lp_not_etag;
    567         oph->have_lp_not_etag = true;
    568       }
    569       break;
    570     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_ALLOW_REFUNDED_FOR_REPURCHASE:
    571       oph->allow_refunded_for_repurchase
    572         = opt->details.allow_refunded_for_repurchase;
    573       break;
    574     default:
    575       GNUNET_break (0);
    576       return GNUNET_NO;
    577     }
    578   }
    579   return GNUNET_OK;
    580 }
    581 
    582 
    583 enum TALER_ErrorCode
    584 TALER_MERCHANT_get_private_order_start (
    585   struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
    586   TALER_MERCHANT_GetPrivateOrderCallback cb,
    587   TALER_MERCHANT_GET_PRIVATE_ORDER_RESULT_CLOSURE *cb_cls)
    588 {
    589   CURL *eh;
    590   unsigned int tms;
    591 
    592   oph->cb = cb;
    593   oph->cb_cls = cb_cls;
    594   tms = (unsigned int) (oph->timeout.rel_value_us
    595                         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
    596   {
    597     char *path;
    598     char timeout_ms[32];
    599     char etag_str[sizeof (struct GNUNET_ShortHashCode) * 2 + 1];
    600 
    601     GNUNET_snprintf (timeout_ms,
    602                      sizeof (timeout_ms),
    603                      "%u",
    604                      tms);
    605     if (oph->have_lp_not_etag)
    606     {
    607       char *end;
    608 
    609       end = GNUNET_STRINGS_data_to_string (
    610         &oph->lp_not_etag,
    611         sizeof (oph->lp_not_etag),
    612         etag_str,
    613         sizeof (etag_str) - 1);
    614       *end = '\0';
    615     }
    616     GNUNET_asprintf (&path,
    617                      "private/orders/%s",
    618                      oph->order_id);
    619     oph->url = TALER_url_join (oph->base_url,
    620                                path,
    621                                "session_id",
    622                                oph->session_id,
    623                                "timeout_ms",
    624                                (0 != tms)
    625                                ? timeout_ms
    626                                : NULL,
    627                                "transfer",
    628                                oph->transfer
    629                                ? "YES"
    630                                : NULL,
    631                                "lp_not_etag",
    632                                oph->have_lp_not_etag
    633                                ? etag_str
    634                                : NULL,
    635                                "allow_refunded_for_repurchase",
    636                                oph->allow_refunded_for_repurchase
    637                                ? "YES"
    638                                : NULL,
    639                                NULL);
    640     GNUNET_free (path);
    641   }
    642   if (NULL == oph->url)
    643     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    644   eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
    645   if (NULL == eh)
    646     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    647   if (0 != tms)
    648   {
    649     GNUNET_break (CURLE_OK ==
    650                   curl_easy_setopt (eh,
    651                                     CURLOPT_TIMEOUT_MS,
    652                                     (long) (tms + 100L)));
    653   }
    654   oph->job = GNUNET_CURL_job_add (oph->ctx,
    655                                   eh,
    656                                   &handle_get_private_order_finished,
    657                                   oph);
    658   if (NULL == oph->job)
    659     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    660   return TALER_EC_NONE;
    661 }
    662 
    663 
    664 void
    665 TALER_MERCHANT_get_private_order_cancel (
    666   struct TALER_MERCHANT_GetPrivateOrderHandle *oph)
    667 {
    668   if (NULL != oph->job)
    669   {
    670     GNUNET_CURL_job_cancel (oph->job);
    671     oph->job = NULL;
    672   }
    673   GNUNET_free (oph->url);
    674   GNUNET_free (oph->order_id);
    675   GNUNET_free (oph->session_id);
    676   GNUNET_free (oph->base_url);
    677   GNUNET_free (oph);
    678 }
    679 
    680 
    681 /* end of merchant_api_get-private-orders-ORDER_ID-new.c */