merchant

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

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


      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       TALER_JSON_spec_slug (
    129         "already_paid_order_id",
    130         &osr->details.ok.details.unpaid.already_paid_order_id),
    131       NULL),
    132     GNUNET_JSON_spec_mark_optional (
    133       TALER_JSON_spec_web_url (
    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     TALER_JSON_spec_web_url (
    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       TALER_JSON_spec_web_url (
    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_array_const (
    263         "refunds_external",
    264         &osr->details.ok.details.paid.refunds_external),
    265       NULL),
    266     GNUNET_JSON_spec_mark_optional (
    267       GNUNET_JSON_spec_timestamp ("last_payment",
    268                                   &osr->details.ok.details.paid.last_payment),
    269       NULL),
    270     TALER_JSON_spec_web_url (
    271       "order_status_url",
    272       &osr->details.ok.details.paid.order_status_url),
    273     GNUNET_JSON_spec_end ()
    274   };
    275 
    276   if (GNUNET_OK !=
    277       GNUNET_JSON_parse (osr->hr.reply,
    278                          spec,
    279                          NULL, NULL))
    280   {
    281     GNUNET_break_op (0);
    282     osr->hr.http_status = 0;
    283     osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    284     oph->cb (oph->cb_cls,
    285              osr);
    286     return;
    287   }
    288   osr->details.ok.status = TALER_MERCHANT_OSC_PAID;
    289   osr->details.ok.details.paid.exchange_hc = (unsigned int) hc32;
    290   osr->details.ok.details.paid.choice_index = ci_missing
    291                                                ? -1
    292                                                : (int) ci32;
    293   {
    294     unsigned int wts_len = (unsigned int) json_array_size (wire_details);
    295     unsigned int ref_len = (unsigned int) json_array_size (refund_details);
    296 
    297     if ( (json_array_size (wire_details) != (size_t) wts_len) ||
    298          (wts_len > MAX_WIRE_DETAILS) )
    299     {
    300       GNUNET_break (0);
    301       osr->hr.http_status = 0;
    302       osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    303       oph->cb (oph->cb_cls,
    304                osr);
    305       return;
    306     }
    307     if ( (json_array_size (refund_details) != (size_t) ref_len) ||
    308          (ref_len > MAX_REFUND_DETAILS) )
    309     {
    310       GNUNET_break (0);
    311       osr->hr.http_status = 0;
    312       osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    313       oph->cb (oph->cb_cls,
    314                osr);
    315       return;
    316     }
    317     {
    318       struct TALER_MERCHANT_WireTransfer wts[GNUNET_NZL (wts_len)];
    319       struct TALER_MERCHANT_GetPrivateOrderRefundDetail ref[
    320         GNUNET_NZL (ref_len)];
    321 
    322       memset (wts,
    323               0,
    324               sizeof (wts));
    325       for (unsigned int i = 0; i<wts_len; i++)
    326       {
    327         struct TALER_MERCHANT_WireTransfer *wt = &wts[i];
    328         const json_t *w = json_array_get (wire_details,
    329                                           i);
    330         struct GNUNET_JSON_Specification ispec[] = {
    331           TALER_JSON_spec_web_url ("exchange_url",
    332                                    &wt->exchange_url),
    333           GNUNET_JSON_spec_fixed_auto ("wtid",
    334                                        &wt->wtid),
    335           GNUNET_JSON_spec_timestamp ("execution_time",
    336                                       &wt->execution_time),
    337           TALER_JSON_spec_amount_any ("amount",
    338                                       &wt->total_amount),
    339           GNUNET_JSON_spec_mark_optional (
    340             TALER_JSON_spec_amount_any ("deposit_fee",
    341                                         &wt->deposit_fee),
    342             NULL),
    343           GNUNET_JSON_spec_bool ("confirmed",
    344                                  &wt->confirmed),
    345           GNUNET_JSON_spec_mark_optional (
    346             GNUNET_JSON_spec_uint64 ("expected_transfer_serial_id",
    347                                      &wt->expected_transfer_serial_id),
    348             NULL),
    349           GNUNET_JSON_spec_end ()
    350         };
    351 
    352         if (GNUNET_OK !=
    353             GNUNET_JSON_parse (w,
    354                                ispec,
    355                                NULL, NULL))
    356         {
    357           GNUNET_break_op (0);
    358           osr->hr.http_status = 0;
    359           osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    360           oph->cb (oph->cb_cls,
    361                    osr);
    362           return;
    363         }
    364       }
    365 
    366       for (unsigned int i = 0; i<ref_len; i++)
    367       {
    368         struct TALER_MERCHANT_GetPrivateOrderRefundDetail *ro = &ref[i];
    369         const json_t *w = json_array_get (refund_details,
    370                                           i);
    371         struct GNUNET_JSON_Specification ispec[] = {
    372           TALER_JSON_spec_amount_any ("amount",
    373                                       &ro->refund_amount),
    374           GNUNET_JSON_spec_string ("reason",
    375                                    &ro->reason),
    376           GNUNET_JSON_spec_bool ("pending",
    377                                  &ro->pending),
    378           GNUNET_JSON_spec_timestamp ("timestamp",
    379                                       &ro->refund_time),
    380           GNUNET_JSON_spec_end ()
    381         };
    382 
    383         if (GNUNET_OK !=
    384             GNUNET_JSON_parse (w,
    385                                ispec,
    386                                NULL, NULL))
    387         {
    388           GNUNET_break_op (0);
    389           osr->hr.http_status = 0;
    390           osr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    391           oph->cb (oph->cb_cls,
    392                    osr);
    393           return;
    394         }
    395       }
    396 
    397       osr->details.ok.details.paid.wts = wts;
    398       osr->details.ok.details.paid.wts_len = wts_len;
    399       osr->details.ok.details.paid.refunds = ref;
    400       osr->details.ok.details.paid.refunds_len = ref_len;
    401       oph->cb (oph->cb_cls,
    402                osr);
    403     }
    404   }
    405 }
    406 
    407 
    408 /**
    409  * Function called when we're done processing the
    410  * HTTP GET /private/orders/$ORDER_ID request.
    411  *
    412  * @param cls the `struct TALER_MERCHANT_GetPrivateOrderHandle`
    413  * @param response_code HTTP response code, 0 on error
    414  * @param response response body, NULL if not in JSON
    415  */
    416 static void
    417 handle_get_private_order_finished (void *cls,
    418                                    long response_code,
    419                                    const void *response)
    420 {
    421   struct TALER_MERCHANT_GetPrivateOrderHandle *oph = cls;
    422   const json_t *json = response;
    423   const char *order_status;
    424   struct TALER_MERCHANT_GetPrivateOrderResponse osr = {
    425     .hr.http_status = (unsigned int) response_code,
    426     .hr.reply = json
    427   };
    428 
    429   oph->job = NULL;
    430   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    431               "Got /private/orders/$ORDER_ID response with status code %u\n",
    432               (unsigned int) response_code);
    433   switch (response_code)
    434   {
    435   case MHD_HTTP_OK:
    436     /* see below */
    437     break;
    438   case MHD_HTTP_NOT_MODIFIED:
    439     oph->cb (oph->cb_cls,
    440              &osr);
    441     TALER_MERCHANT_get_private_order_cancel (oph);
    442     return;
    443   case MHD_HTTP_ACCEPTED:
    444     oph->cb (oph->cb_cls,
    445              &osr);
    446     TALER_MERCHANT_get_private_order_cancel (oph);
    447     return;
    448   case MHD_HTTP_UNAUTHORIZED:
    449     osr.hr.ec = TALER_JSON_get_error_code (json);
    450     osr.hr.hint = TALER_JSON_get_error_hint (json);
    451     oph->cb (oph->cb_cls,
    452              &osr);
    453     TALER_MERCHANT_get_private_order_cancel (oph);
    454     return;
    455   case MHD_HTTP_NOT_FOUND:
    456     osr.hr.ec = TALER_JSON_get_error_code (json);
    457     osr.hr.hint = TALER_JSON_get_error_hint (json);
    458     oph->cb (oph->cb_cls,
    459              &osr);
    460     TALER_MERCHANT_get_private_order_cancel (oph);
    461     return;
    462   case MHD_HTTP_GATEWAY_TIMEOUT:
    463     osr.hr.ec = TALER_JSON_get_error_code (json);
    464     osr.hr.hint = TALER_JSON_get_error_hint (json);
    465     oph->cb (oph->cb_cls,
    466              &osr);
    467     TALER_MERCHANT_get_private_order_cancel (oph);
    468     return;
    469   default:
    470     osr.hr.ec = TALER_JSON_get_error_code (json);
    471     osr.hr.hint = TALER_JSON_get_error_hint (json);
    472     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    473                 "Polling order status failed with HTTP status code %u/%d\n",
    474                 (unsigned int) response_code,
    475                 (int) osr.hr.ec);
    476     GNUNET_break_op (0);
    477     oph->cb (oph->cb_cls,
    478              &osr);
    479     TALER_MERCHANT_get_private_order_cancel (oph);
    480     return;
    481   }
    482 
    483   order_status = json_string_value (json_object_get (json,
    484                                                      "order_status"));
    485   if (NULL == order_status)
    486   {
    487     GNUNET_break_op (0);
    488     osr.hr.http_status = 0;
    489     osr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    490     oph->cb (oph->cb_cls,
    491              &osr);
    492     TALER_MERCHANT_get_private_order_cancel (oph);
    493     return;
    494   }
    495 
    496   if (0 == strcmp ("paid",
    497                    order_status))
    498   {
    499     handle_paid (oph,
    500                  &osr);
    501   }
    502   else if (0 == strcmp ("claimed",
    503                         order_status))
    504   {
    505     handle_claimed (oph,
    506                     &osr);
    507   }
    508   else if (0 == strcmp ("unpaid",
    509                         order_status))
    510   {
    511     handle_unpaid (oph,
    512                    &osr);
    513   }
    514   else
    515   {
    516     GNUNET_break_op (0);
    517     osr.hr.http_status = 0;
    518     osr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    519     oph->cb (oph->cb_cls,
    520              &osr);
    521   }
    522   TALER_MERCHANT_get_private_order_cancel (oph);
    523 }
    524 
    525 
    526 struct TALER_MERCHANT_GetPrivateOrderHandle *
    527 TALER_MERCHANT_get_private_order_create (
    528   struct GNUNET_CURL_Context *ctx,
    529   const char *url,
    530   const char *order_id)
    531 {
    532   struct TALER_MERCHANT_GetPrivateOrderHandle *oph;
    533 
    534   oph = GNUNET_new (struct TALER_MERCHANT_GetPrivateOrderHandle);
    535   oph->ctx = ctx;
    536   oph->base_url = GNUNET_strdup (url);
    537   oph->order_id = GNUNET_strdup (order_id);
    538   return oph;
    539 }
    540 
    541 
    542 enum GNUNET_GenericReturnValue
    543 TALER_MERCHANT_get_private_order_set_options_ (
    544   struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
    545   unsigned int num_options,
    546   const struct TALER_MERCHANT_GetPrivateOrderOptionValue *options)
    547 {
    548   for (unsigned int i = 0; i < num_options; i++)
    549   {
    550     const struct TALER_MERCHANT_GetPrivateOrderOptionValue *opt =
    551       &options[i];
    552 
    553     switch (opt->option)
    554     {
    555     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_END:
    556       return GNUNET_OK;
    557     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_SESSION_ID:
    558       GNUNET_free (oph->session_id);
    559       if (NULL != opt->details.session_id)
    560         oph->session_id = GNUNET_strdup (opt->details.session_id);
    561       break;
    562     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_TIMEOUT:
    563       oph->timeout = opt->details.timeout;
    564       break;
    565     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_TRANSFER:
    566       oph->transfer = opt->details.transfer;
    567       break;
    568     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_LP_NOT_ETAG:
    569       if (NULL != opt->details.lp_not_etag)
    570       {
    571         oph->lp_not_etag = *opt->details.lp_not_etag;
    572         oph->have_lp_not_etag = true;
    573       }
    574       break;
    575     case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_ALLOW_REFUNDED_FOR_REPURCHASE:
    576       oph->allow_refunded_for_repurchase
    577         = opt->details.allow_refunded_for_repurchase;
    578       break;
    579     default:
    580       GNUNET_break (0);
    581       return GNUNET_NO;
    582     }
    583   }
    584   return GNUNET_OK;
    585 }
    586 
    587 
    588 enum TALER_ErrorCode
    589 TALER_MERCHANT_get_private_order_start (
    590   struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
    591   TALER_MERCHANT_GetPrivateOrderCallback cb,
    592   TALER_MERCHANT_GET_PRIVATE_ORDER_RESULT_CLOSURE *cb_cls)
    593 {
    594   CURL *eh;
    595   unsigned int tms;
    596 
    597   oph->cb = cb;
    598   oph->cb_cls = cb_cls;
    599   tms = (unsigned int) (oph->timeout.rel_value_us
    600                         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
    601   {
    602     char *path;
    603     char timeout_ms[32];
    604     char etag_str[sizeof (struct GNUNET_ShortHashCode) * 2 + 1];
    605 
    606     GNUNET_snprintf (timeout_ms,
    607                      sizeof (timeout_ms),
    608                      "%u",
    609                      tms);
    610     if (oph->have_lp_not_etag)
    611     {
    612       char *end;
    613 
    614       end = GNUNET_STRINGS_data_to_string (
    615         &oph->lp_not_etag,
    616         sizeof (oph->lp_not_etag),
    617         etag_str,
    618         sizeof (etag_str) - 1);
    619       *end = '\0';
    620     }
    621     GNUNET_asprintf (&path,
    622                      "private/orders/%s",
    623                      oph->order_id);
    624     oph->url = TALER_url_join (oph->base_url,
    625                                path,
    626                                "session_id",
    627                                oph->session_id,
    628                                "timeout_ms",
    629                                (0 != tms)
    630                                ? timeout_ms
    631                                : NULL,
    632                                "transfer",
    633                                oph->transfer
    634                                ? "YES"
    635                                : NULL,
    636                                "lp_not_etag",
    637                                oph->have_lp_not_etag
    638                                ? etag_str
    639                                : NULL,
    640                                "allow_refunded_for_repurchase",
    641                                oph->allow_refunded_for_repurchase
    642                                ? "YES"
    643                                : NULL,
    644                                NULL);
    645     GNUNET_free (path);
    646   }
    647   if (NULL == oph->url)
    648     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    649   eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
    650   if (NULL == eh)
    651     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    652   if (0 != tms)
    653   {
    654     GNUNET_break (CURLE_OK ==
    655                   curl_easy_setopt (eh,
    656                                     CURLOPT_TIMEOUT_MS,
    657                                     (long) (tms + 100L)));
    658   }
    659   oph->job = GNUNET_CURL_job_add (oph->ctx,
    660                                   eh,
    661                                   &handle_get_private_order_finished,
    662                                   oph);
    663   if (NULL == oph->job)
    664     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    665   return TALER_EC_NONE;
    666 }
    667 
    668 
    669 void
    670 TALER_MERCHANT_get_private_order_cancel (
    671   struct TALER_MERCHANT_GetPrivateOrderHandle *oph)
    672 {
    673   if (NULL != oph->job)
    674   {
    675     GNUNET_CURL_job_cancel (oph->job);
    676     oph->job = NULL;
    677   }
    678   GNUNET_free (oph->url);
    679   GNUNET_free (oph->order_id);
    680   GNUNET_free (oph->session_id);
    681   GNUNET_free (oph->base_url);
    682   GNUNET_free (oph);
    683 }
    684 
    685 
    686 /* end of merchant_api_get-private-orders-ORDER_ID-new.c */