merchant

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

taler_merchant_pay_service.c (32423B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free
      7   Software 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 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.LIB.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler_merchant_pay_service.c
     18  * @brief Implementation of the the ideology
     19  *        from the pay_service as copy of
     20  *        merchant_api_post_order_pay.c
     21  * @author Bohdan Potuzhnyi
     22  */
     23 #include "platform.h"
     24 #include <curl/curl.h>
     25 #include <gnunet/gnunet_common.h>
     26 #include <gnunet/gnunet_json_lib.h>
     27 #include <jansson.h>
     28 #include <microhttpd.h>
     29 #include <gnunet/gnunet_util_lib.h>
     30 #include <gnunet/gnunet_curl_lib.h>
     31 #include "taler_merchant_service.h"
     32 #include "taler_merchant_pay_service.h"
     33 #include "merchant_api_common.h"
     34 #include "merchant_api_curl_defaults.h"
     35 #include <stdio.h>
     36 #include <taler/taler_json_lib.h>
     37 #include <taler/taler_signatures.h>
     38 #include <taler/taler_exchange_service.h>
     39 #include <taler/taler_curl_lib.h>
     40 
     41 /**
     42  * @brief A Pay Handle
     43  */
     44 struct TALER_MERCHANT_OrderPayHandle
     45 {
     46   /**
     47    * Reference to the GNUNET CURL execution context.
     48    */
     49   struct GNUNET_CURL_Context   *ctx;
     50 
     51   /**
     52    * Callback to invoke with the payment result ("pay" mode).
     53    */
     54   TALER_MERCHANT_OrderPayCallback cb;
     55 
     56   /**
     57    * Closure data for @a cb.
     58    */
     59   TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *cb_cls;
     60 
     61   /* Mandatory scalars: */
     62 
     63   /**
     64    * Base URL of the merchant service.
     65    */
     66   char *merchant_url;
     67 
     68   /**
     69    * Identifier of the order being paid.
     70    */
     71   char *order_id;
     72 
     73   /**
     74    * Session identifier for this payment attempt.
     75    */
     76   char *session_id;
     77 
     78   /**
     79    * Timestamp when the payment request was created.
     80    */
     81   struct GNUNET_TIME_Timestamp timestamp;
     82 
     83   /**
     84    * Deadline after which refunds are no longer allowed.
     85    */
     86   struct GNUNET_TIME_Timestamp refund_deadline;
     87 
     88   /**
     89    * Wire hash for communicating payment details.
     90    */
     91   struct TALER_MerchantWireHashP h_wire;
     92 
     93   /**
     94    * Indicates whether @a h_wire has been set.
     95    */
     96   bool has_h_wire;
     97 
     98   /* Wallet mode fields: */
     99 
    100   /**
    101    * Indicates whether a contract hash was provided.
    102    */
    103   bool has_h_contract;
    104 
    105   /**
    106    * Hash of the private contract terms (wallet mode only).
    107    */
    108   struct TALER_PrivateContractHashP h_contract_terms;
    109 
    110   /**
    111    * Indicates whether the merchant public key was provided.
    112    */
    113   bool has_merchant_pub;
    114 
    115   /**
    116    * Merchant’s public key for verifying signatures (wallet mode).
    117    */
    118   struct TALER_MerchantPublicKeyP merchant_pub;
    119 
    120   /**
    121    * Indicates whether a choice index was provided.
    122    */
    123   bool has_choice_index;
    124 
    125   /**
    126    * Selected index of the contract choice (for token operations).
    127    */
    128   int choice_index;
    129 
    130   /**
    131    * Legacy: pointer to the amount structure for strcmp checks.
    132    */
    133   const struct TALER_Amount *amount;
    134 
    135   /**
    136    * Legacy: pointer to the maximum fee structure for strcmp checks.
    137    */
    138   const struct TALER_Amount *max_fee;
    139 
    140   /* Raw arrays as passed in via set_options(): */
    141 
    142   /**
    143    * Coins used for payment.
    144    */
    145   struct
    146   {
    147     /**
    148      * Number of coins provided.
    149      */
    150     unsigned int num_coins;
    151     /**
    152      * Array of coins to spend.
    153      */
    154     const struct TALER_MERCHANT_PayCoin *coins;
    155   } coins;
    156 
    157   /**
    158    * Input tokens to use (wallet mode).
    159    */
    160   struct
    161   {
    162     /**
    163      * Number of tokens provided.
    164      */
    165     unsigned int num_tokens;
    166     /**
    167      * Array of tokens to redeem.
    168      */
    169     const struct TALER_MERCHANT_UseToken *tokens;
    170   } input_tokens;
    171 
    172   /**
    173    * Output tokens expected from the merchant.
    174    */
    175   struct
    176   {
    177     /**
    178      * Number of output tokens expected.
    179      */
    180     unsigned int num_output_tokens;
    181     /**
    182      * Array of expected output tokens.
    183      */
    184     const struct TALER_MERCHANT_OutputToken *output_tokens;
    185   } output_tokens;
    186 
    187   /**
    188    * JSON array of token envelope events (from Donau).
    189    */
    190   json_t *tokens_evs;
    191 
    192   /* Computed once both choice_index and tokens_evs are available: */
    193 
    194   /**
    195    * JSON object containing wallet-specific data payload.
    196    */
    197   json_t *wallet_data;
    198 
    199   /**
    200    * Hash code of @a wallet_data for integrity checks.
    201    */
    202   struct GNUNET_HashCode wallet_data_hash;
    203 
    204   /**
    205    * JSON body being constructed for the HTTP POST.
    206    */
    207   json_t *body;
    208 
    209   /* Final URL and CURL plumbing: */
    210 
    211   /**
    212    * Fully formed URL for the POST /order/$ID/pay request.
    213    */
    214   char *url;
    215 
    216   /**
    217    * CURL post context managing headers and body.
    218    */
    219   struct TALER_CURL_PostContext post_ctx;
    220 
    221   /**
    222    * Handle for the asynchronous CURL job.
    223    */
    224   struct GNUNET_CURL_Job *job;
    225 
    226   /**
    227    * Flags indicating which payment options have been set.
    228    */
    229   bool field_seen[TALER_MERCHANT_OrderPayOptionType_LENGTH];
    230 
    231   /**
    232    * True if operating in wallet mode (using tokens/contracts).
    233    */
    234   bool am_wallet;
    235 
    236   /**
    237    * Raw JSON data of `donau` for `wallet_data`.
    238    */
    239   json_t *donau_data;
    240 };
    241 
    242 /**
    243  * Parse blindly signed output tokens from response.
    244  *
    245  * @param token_sigs the JSON array with the token signatures. Can be NULL.
    246  * @param tokens where to store the parsed tokens.
    247  * @param num_tokens where to store the length of the @a tokens array.
    248  */
    249 static enum GNUNET_GenericReturnValue
    250 parse_tokens (const json_t *token_sigs,
    251               struct TALER_MERCHANT_OutputToken **tokens,
    252               unsigned int *num_tokens)
    253 {
    254   GNUNET_array_grow (*tokens,
    255                      *num_tokens,
    256                      json_array_size (token_sigs));
    257 
    258   for (unsigned int i = 0; i<(*num_tokens); i++)
    259   {
    260     struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i];
    261     struct GNUNET_JSON_Specification spec[] = {
    262       TALER_JSON_spec_blinded_token_issue_sig ("blind_sig",
    263                                                &token->blinded_sig),
    264       GNUNET_JSON_spec_end ()
    265     };
    266     const json_t *jtoken
    267       = json_array_get (token_sigs,
    268                         i);
    269 
    270     if (NULL == jtoken)
    271     {
    272       GNUNET_break (0);
    273       return GNUNET_SYSERR;
    274     }
    275     if (GNUNET_OK !=
    276         GNUNET_JSON_parse (jtoken,
    277                            spec,
    278                            NULL, NULL))
    279     {
    280       GNUNET_break (0);
    281       return GNUNET_SYSERR;
    282     }
    283   }
    284 
    285   return GNUNET_YES;
    286 }
    287 
    288 
    289 /**
    290  * Function called when we're done processing the
    291  * HTTP /pay request.
    292  *
    293  * @param cls the `struct TALER_MERCHANT_Pay`
    294  * @param response_code HTTP response code, 0 on error
    295  * @param resp response body, NULL if not in JSON
    296  */
    297 static void
    298 handle_finished (void *cls,
    299                  long response_code,
    300                  const void *resp)
    301 {
    302   struct TALER_MERCHANT_OrderPayHandle *oph = cls;
    303   const json_t *json = resp;
    304   struct TALER_MERCHANT_PayResponse pr = {
    305     .hr.http_status = (unsigned int) response_code,
    306     .hr.reply = json
    307   };
    308 
    309   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    310               "Received /pay response with status code %u\n",
    311               (unsigned int) response_code);
    312 
    313   json_dumpf (json,
    314               stderr,
    315               JSON_INDENT (2));
    316 
    317   oph->job = NULL;
    318   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    319               "/pay completed with response code %u\n",
    320               (unsigned int) response_code);
    321   switch (response_code)
    322   {
    323   case 0:
    324     pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    325     break;
    326   case MHD_HTTP_OK:
    327     if (oph->am_wallet)
    328     {
    329       const json_t *token_sigs = NULL;
    330       struct GNUNET_JSON_Specification spec[] = {
    331         GNUNET_JSON_spec_fixed_auto ("sig",
    332                                      &pr.details.ok.merchant_sig),
    333         GNUNET_JSON_spec_mark_optional (
    334           GNUNET_JSON_spec_string ("pos_confirmation",
    335                                    &pr.details.ok.pos_confirmation),
    336           NULL),
    337         GNUNET_JSON_spec_mark_optional (
    338           GNUNET_JSON_spec_array_const ("token_sigs",
    339                                         &token_sigs),
    340           NULL),
    341         GNUNET_JSON_spec_end ()
    342       };
    343 
    344       if (GNUNET_OK !=
    345           GNUNET_JSON_parse (json,
    346                              spec,
    347                              NULL, NULL))
    348       {
    349         GNUNET_break_op (0);
    350         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    351         pr.hr.http_status = 0;
    352         pr.hr.hint = "sig field missing in response";
    353         break;
    354       }
    355 
    356       if (GNUNET_OK !=
    357           parse_tokens (token_sigs,
    358                         &pr.details.ok.tokens,
    359                         &pr.details.ok.num_tokens))
    360       {
    361         GNUNET_break_op (0);
    362         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    363         pr.hr.http_status = 0;
    364         pr.hr.hint = "failed to parse token_sigs field in response";
    365         break;
    366       }
    367 
    368       if (GNUNET_OK !=
    369           TALER_merchant_pay_verify (&oph->h_contract_terms,
    370                                      &oph->merchant_pub,
    371                                      &pr.details.ok.merchant_sig))
    372       {
    373         GNUNET_break_op (0);
    374         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    375         pr.hr.http_status = 0;
    376         pr.hr.hint = "signature invalid";
    377       }
    378     }
    379     break;
    380   /* Tolerating Not Acceptable because sometimes
    381      * - especially in tests - we might want to POST
    382      * coins one at a time.  */
    383   case MHD_HTTP_NOT_ACCEPTABLE:
    384     pr.hr.ec = TALER_JSON_get_error_code (json);
    385     pr.hr.hint = TALER_JSON_get_error_hint (json);
    386     break;
    387   case MHD_HTTP_BAD_REQUEST:
    388     pr.hr.ec = TALER_JSON_get_error_code (json);
    389     pr.hr.hint = TALER_JSON_get_error_hint (json);
    390     /* This should never happen, either us
    391      * or the merchant is buggy (or API version conflict);
    392      * just pass JSON reply to the application */
    393     break;
    394   case MHD_HTTP_PAYMENT_REQUIRED:
    395     /* was originally paid, but then refunded */
    396     pr.hr.ec = TALER_JSON_get_error_code (json);
    397     pr.hr.hint = TALER_JSON_get_error_hint (json);
    398     break;
    399   case MHD_HTTP_FORBIDDEN:
    400     pr.hr.ec = TALER_JSON_get_error_code (json);
    401     pr.hr.hint = TALER_JSON_get_error_hint (json);
    402     break;
    403   case MHD_HTTP_NOT_FOUND:
    404     pr.hr.ec = TALER_JSON_get_error_code (json);
    405     pr.hr.hint = TALER_JSON_get_error_hint (json);
    406     /* Nothing really to verify, this should never
    407        happen, we should pass the JSON reply to the
    408        application */
    409     break;
    410   case MHD_HTTP_REQUEST_TIMEOUT:
    411     pr.hr.ec = TALER_JSON_get_error_code (json);
    412     pr.hr.hint = TALER_JSON_get_error_hint (json);
    413     /* The merchant couldn't generate a timely response, likely because
    414        it itself waited too long on the exchange.
    415        Pass on to application. */
    416     break;
    417   case MHD_HTTP_CONFLICT:
    418     TALER_MERCHANT_parse_error_details_ (json,
    419                                          MHD_HTTP_CONFLICT,
    420                                          &pr.hr);
    421     break;
    422   case MHD_HTTP_GONE:
    423     TALER_MERCHANT_parse_error_details_ (json,
    424                                          response_code,
    425                                          &pr.hr);
    426     /* The merchant says we are too late, the offer has expired or some
    427        denomination key of a coin involved has expired.
    428        Might be a disagreement in timestamps? Still, pass on to application. */
    429     break;
    430   case MHD_HTTP_PRECONDITION_FAILED:
    431     TALER_MERCHANT_parse_error_details_ (json,
    432                                          response_code,
    433                                          &pr.hr);
    434     /* Nothing really to verify, the merchant is blaming us for failing to
    435        satisfy some constraint (likely it does not like our exchange because
    436        of some disagreement on the PKI).  We should pass the JSON reply to the
    437        application */
    438     break;
    439   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    440     {
    441       json_t *ebus = json_object_get (json,
    442                                       "exchange_base_urls");
    443       if (NULL == ebus)
    444       {
    445         GNUNET_break_op (0);
    446         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    447         pr.hr.http_status = 0;
    448         pr.hr.hint = "failed to parse exchange_base_urls field in response";
    449         break;
    450       }
    451       {
    452         size_t alen = json_array_size (ebus);
    453         const char *ebua[GNUNET_NZL (alen)];
    454         size_t idx;
    455         json_t *jebu;
    456         bool ok = true;
    457 
    458         GNUNET_assert (alen <= UINT_MAX);
    459         json_array_foreach (ebus, idx, jebu)
    460         {
    461           ebua[idx] = json_string_value (jebu);
    462           if (NULL == ebua[idx])
    463           {
    464             GNUNET_break_op (0);
    465             pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    466             pr.hr.http_status = 0;
    467             pr.hr.hint = "non-string value in exchange_base_urls in response";
    468             ok = false;
    469             break;
    470           }
    471         }
    472         if (! ok)
    473           break;
    474         pr.details.unavailable_for_legal_reasons.num_exchanges
    475           = (unsigned int) alen;
    476         pr.details.unavailable_for_legal_reasons.exchanges
    477           = ebua;
    478         oph->cb (oph->cb_cls,
    479                  &pr);
    480         TALER_MERCHANT_order_pay_cancel1 (oph);
    481         return;
    482       }
    483     }
    484     break;
    485   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    486     TALER_MERCHANT_parse_error_details_ (json,
    487                                          response_code,
    488                                          &pr.hr);
    489     /* Server had an internal issue; we should retry,
    490        but this API leaves this to the application */
    491     break;
    492   case MHD_HTTP_BAD_GATEWAY:
    493     /* Nothing really to verify, the merchant is blaming the exchange.
    494        We should pass the JSON reply to the application */
    495     TALER_MERCHANT_parse_error_details_ (json,
    496                                          response_code,
    497                                          &pr.hr);
    498     break;
    499   case MHD_HTTP_SERVICE_UNAVAILABLE:
    500     TALER_MERCHANT_parse_error_details_ (json,
    501                                          response_code,
    502                                          &pr.hr);
    503     /* Exchange couldn't respond properly; the retry is
    504        left to the application */
    505     break;
    506   case MHD_HTTP_GATEWAY_TIMEOUT:
    507     TALER_MERCHANT_parse_error_details_ (json,
    508                                          response_code,
    509                                          &pr.hr);
    510     /* Exchange couldn't respond in a timely fashion;
    511        the retry is left to the application */
    512     break;
    513   default:
    514     TALER_MERCHANT_parse_error_details_ (json,
    515                                          response_code,
    516                                          &pr.hr);
    517     /* unexpected response code */
    518     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    519                 "Unexpected response code %u/%d\n",
    520                 (unsigned int) response_code,
    521                 (int) pr.hr.ec);
    522     GNUNET_break_op (0);
    523     break;
    524   }
    525   oph->cb (oph->cb_cls,
    526            &pr);
    527 
    528   if (pr.details.ok.tokens)
    529   {
    530     GNUNET_free (pr.details.ok.tokens);
    531     pr.details.ok.tokens = NULL;
    532     pr.details.ok.num_tokens = 0;
    533   }
    534 
    535   TALER_MERCHANT_order_pay_cancel1 (oph);
    536 }
    537 
    538 
    539 /**
    540  * @brief Create and initialize a new payment handle
    541  *
    542  * Allocates a TALER_MERCHANT_OrderPayHandle, sets up its context and
    543  * prepares an empty JSON body for the /orders/$ID/pay request.
    544  */
    545 struct TALER_MERCHANT_OrderPayHandle *
    546 TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx,
    547                                  TALER_MERCHANT_OrderPayCallback cb,
    548                                  TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE
    549                                  *cb_cls)
    550 {
    551   struct TALER_MERCHANT_OrderPayHandle *ph =
    552     GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
    553   ph->ctx             = ctx;
    554   ph->cb              = cb;
    555   ph->cb_cls          = cb_cls;
    556   ph->body            = json_object ();
    557   GNUNET_assert (ph->body);
    558   return ph;
    559 }
    560 
    561 
    562 /**
    563  * @brief Cancel and free a payment handle
    564  *
    565  * Aborts any in-flight CURL job, releases all JSON objects and internal
    566  * buffers, and frees the handle structure itself.
    567  */
    568 void
    569 TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph)
    570 {
    571   if (ph->job)
    572     GNUNET_CURL_job_cancel (ph->job);
    573   ph->job = NULL;
    574 
    575   TALER_curl_easy_post_finished (&ph->post_ctx);
    576 
    577   if (ph->body)
    578     json_decref (ph->body);
    579   ph->body = NULL;
    580 
    581   if (ph->wallet_data)
    582     json_decref (ph->wallet_data);
    583   ph->wallet_data = NULL;
    584 
    585   if (ph->tokens_evs)
    586     json_decref (ph->tokens_evs);
    587   ph->tokens_evs = NULL;
    588 
    589   if (ph->donau_data)
    590     json_decref (ph->donau_data);
    591 
    592   GNUNET_free (ph->url);
    593   GNUNET_free (ph->merchant_url);
    594   GNUNET_free (ph->session_id);
    595   GNUNET_free (ph->order_id);
    596   GNUNET_free (ph);
    597 }
    598 
    599 
    600 /**
    601  * @brief Store a JSON snippet under a payment option key
    602  *
    603  * Ensures that an option of type @a ot has not already been set,
    604  * then merges @a snippet into the handle's JSON @c body. Marks the
    605  * field_seen flag and frees @a snippet.
    606  *
    607  * @param ph   payment handle receiving the snippet
    608  * @param ot   option type under which to store @a snippet
    609  * @param snippet JSON object representing the option payload
    610  * @return #TALER_MERCHANT_OPOEC_OK if stored; appropriate error code otherwise
    611  */
    612 static enum TALER_MERCHANT_OrderPayErrorCode
    613 store_json_option (struct TALER_MERCHANT_OrderPayHandle *ph,
    614                    enum TALER_MERCHANT_OrderPayOptionType ot,
    615                    json_t *snippet)
    616 {
    617   if (ph->field_seen[ot])
    618   {
    619     json_decref (snippet);
    620     return TALER_MERCHANT_OPOEC_DUPLICATE_OPTION;
    621   }
    622   ph->field_seen[ot] = true;
    623   GNUNET_assert (0 == json_object_update (ph->body,
    624                                           snippet));
    625   json_decref (snippet);
    626   return TALER_MERCHANT_OPOEC_OK;
    627 }
    628 
    629 
    630 /**
    631  * @brief Apply user-supplied options to a payment handle
    632  *
    633  * Iterates through a NULL-terminated array of #TALER_MERCHANT_OrderPayOption
    634  * entries, validates and stores each into @a ph. Handles JSON packing and
    635  * internal state updates for coins, tokens, deadlines, Donau data, etc.
    636  */
    637 enum TALER_MERCHANT_OrderPayErrorCode
    638 TALER_MERCHANT_order_pay_set_options (
    639   struct TALER_MERCHANT_OrderPayHandle *ph,
    640   const struct TALER_MERCHANT_OrderPayOption options[],
    641   size_t max_options)
    642 {
    643   for (size_t i = 0; i < max_options
    644        && options[i].ot != TALER_MERCHANT_OrderPayOptionType_END; i++)
    645   {
    646     const struct TALER_MERCHANT_OrderPayOption *o = &options[i];
    647 
    648     switch (o->ot)
    649     {
    650     case TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL:
    651       ph->merchant_url = GNUNET_strdup (o->details.merchant_url);
    652       break;
    653 
    654     case TALER_MERCHANT_OrderPayOptionType_SESSION_ID:
    655       ph->session_id = GNUNET_strdup (o->details.session_id);
    656       /* add straight into JSON body */
    657       {
    658         json_t *js = GNUNET_JSON_PACK (GNUNET_JSON_pack_string ("session_id",
    659                                                                 o->details.
    660                                                                 session_id));
    661         enum TALER_MERCHANT_OrderPayErrorCode ec =
    662           store_json_option (ph,
    663                              o->ot,
    664                              js);
    665         if (TALER_MERCHANT_OPOEC_OK != ec)
    666           return ec;
    667         break;
    668       }
    669 
    670     case TALER_MERCHANT_OrderPayOptionType_ORDER_ID:
    671       {
    672         ph->order_id = GNUNET_strdup (o->details.order_id);
    673         break;
    674       }
    675 
    676     case TALER_MERCHANT_OrderPayOptionType_H_CONTRACT:
    677       {
    678         ph->h_contract_terms = *o->details.h_contract;
    679         ph->has_h_contract = true;
    680 
    681         break;
    682       }
    683 
    684     case TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX:
    685       ph->choice_index = o->details.choice_index;
    686       ph->has_choice_index = true;
    687       break;
    688 
    689     case TALER_MERCHANT_OrderPayOptionType_AMOUNT:
    690       {
    691         ph->amount = &o->details.amount;
    692         break;
    693       }
    694 
    695     case TALER_MERCHANT_OrderPayOptionType_MAX_FEE:
    696       {
    697         ph->max_fee = &o->details.max_fee;
    698         break;
    699       }
    700 
    701     case TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB:
    702       {
    703         ph->merchant_pub = o->details.merchant_pub;
    704         ph->has_merchant_pub = true;
    705 
    706         break;
    707       }
    708 
    709     case TALER_MERCHANT_OrderPayOptionType_TIMESTAMP:
    710       {
    711         ph->timestamp = o->details.timestamp;
    712         break;
    713       }
    714 
    715     case TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE:
    716       {
    717         ph->refund_deadline = o->details.refund_deadline;
    718         break;
    719       }
    720 
    721     case TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE:
    722       {
    723         /* FIXME: This one comes from the merchant_api_post_order_pay
    724          no idea do we still need it or not? */
    725         break;
    726       }
    727 
    728     case TALER_MERCHANT_OrderPayOptionType_H_WIRE:
    729       {
    730         ph->h_wire = o->details.h_wire;
    731         ph->has_h_wire = true;
    732         break;
    733       }
    734 
    735     case TALER_MERCHANT_OrderPayOptionType_COINS:
    736       /* stash for later signing */
    737       ph->coins.num_coins = o->details.coins.num_coins;
    738       ph->coins.coins = o->details.coins.coins;
    739       break;
    740 
    741     case TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS:
    742       /* stash for later signing */
    743       ph->input_tokens.num_tokens = o->details.input_tokens.num_tokens;
    744       ph->input_tokens.tokens = o->details.input_tokens.tokens;
    745       break;
    746 
    747     case TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS:
    748       /* store JSON array directly, *and* stash for hash */
    749       ph->output_tokens.num_output_tokens =
    750         o->details.output_tokens.num_output_tokens;
    751       ph->output_tokens.output_tokens = o->details.output_tokens.output_tokens;
    752       {
    753         /* build and store tokens_evs */
    754         json_t *arr = json_array ();
    755 
    756         GNUNET_assert (NULL != arr);
    757         for (unsigned j = 0; j < ph->output_tokens.num_output_tokens; j++)
    758         {
    759           const struct TALER_MERCHANT_OutputToken *otk =
    760             &ph->output_tokens.output_tokens[j];
    761           json_t *je = GNUNET_JSON_PACK (TALER_JSON_pack_token_envelope (NULL,
    762                                                                          &otk->
    763                                                                          envelope));
    764           GNUNET_assert (0 ==
    765                          json_array_append_new (arr,
    766                                                 je));
    767         }
    768 
    769         ph->tokens_evs = arr;
    770 
    771         ph->field_seen[o->ot] = true;
    772       }
    773       break;
    774 
    775     case TALER_MERCHANT_OrderPayOptionType_DONAU_URL:
    776       if (NULL == ph->donau_data)
    777         ph->donau_data = json_object ();
    778       GNUNET_assert (0 ==
    779                      json_object_set_new (
    780                        ph->donau_data,
    781                        "url",
    782                        json_string (o->details.donau_url)));
    783       break;
    784 
    785     case TALER_MERCHANT_OrderPayOptionType_DONAU_YEAR:
    786       {
    787         if (ph->donau_data == NULL)
    788           ph->donau_data = json_object ();
    789         GNUNET_assert (0 == json_object_set_new (
    790                          ph->donau_data,
    791                          "year",
    792                          json_integer ((json_int_t) o->details.donau_year)));
    793         break;
    794       }
    795 
    796     case TALER_MERCHANT_OrderPayOptionType_DONAU_BUDIS:
    797       {
    798         if (ph->donau_data == NULL)
    799           ph->donau_data = json_object ();
    800         GNUNET_assert (0 == json_object_set_new (
    801                          ph->donau_data,
    802                          "budikeypairs",
    803                          json_incref (o->details.donau_budis_json)));
    804         break;
    805       }
    806 
    807     default:
    808       return TALER_MERCHANT_OPOEC_UNKNOWN_OPTION;
    809     }
    810   }
    811   return TALER_MERCHANT_OPOEC_OK;
    812 }
    813 
    814 
    815 /**
    816  * @brief Dispatch the /orders/$ID/pay request
    817  *
    818  * Validates that all mandatory parameters (merchant_url, order_id, coins)
    819  * have been set, builds the final JSON payload, constructs the URL,
    820  * and issues an asynchronous HTTP POST. The payment handle's callback
    821  * will receive completion notifications.
    822  */
    823 enum TALER_MERCHANT_OrderPayErrorCode
    824 TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph)
    825 {
    826   /* all the old mandatory checks */
    827   if ( (! ph->merchant_url) ||
    828        (! ph->order_id) )
    829   {
    830     GNUNET_break (0);
    831     return TALER_MERCHANT_OPOEC_MISSING_MANDATORY;
    832   }
    833   if (GNUNET_YES !=
    834       TALER_amount_cmp_currency (ph->amount,
    835                                  ph->max_fee))
    836   {
    837     GNUNET_break (0);
    838     return TALER_MERCHANT_OPOEC_INVALID_VALUE;
    839   }
    840 
    841   /* build wallet_data hash for signing coins & tokens */
    842   if (ph->has_choice_index)
    843   {
    844     /* base fields */
    845     json_t *wd = GNUNET_JSON_PACK (
    846       GNUNET_JSON_pack_int64 ("choice_index",
    847                               ph->choice_index),
    848       GNUNET_JSON_pack_allow_null (
    849         GNUNET_JSON_pack_array_incref ("tokens_evs",
    850                                        ph->tokens_evs))
    851       );
    852 
    853     /* Putting prepared donau_data into the wallet_data */
    854     if (ph->donau_data)
    855       GNUNET_assert (0 == json_object_set_new (
    856                        wd,
    857                        "donau",
    858                        json_incref (ph->donau_data)));
    859 
    860     ph->wallet_data = wd;
    861 
    862     TALER_json_hash (ph->wallet_data,
    863                      &ph->wallet_data_hash);
    864 
    865     store_json_option (ph,
    866                        TALER_MERCHANT_OrderPayOptionType_WALLET_DATA,
    867                        GNUNET_JSON_PACK (
    868                          GNUNET_JSON_pack_object_incref ("wallet_data",
    869                                                          ph->wallet_data)));
    870   }
    871 
    872   /* sign coins AND build the “coins” JSON in one pass */
    873   {
    874     struct TALER_Amount total_fee;
    875     struct TALER_Amount total_amount;
    876     json_t *arr = json_array ();
    877 
    878     GNUNET_assert (NULL != arr);
    879     for (unsigned i = 0; i < ph->coins.num_coins; i++)
    880     {
    881       const struct TALER_MERCHANT_PayCoin *c = &ph->coins.coins[i];
    882       struct TALER_MERCHANT_PaidCoin pc;
    883       json_t *je;
    884 
    885       /* sign  */
    886       struct TALER_Amount fee;
    887       struct TALER_DenominationHashP h_denom_pub;
    888 
    889       TALER_denom_pub_hash (&c->denom_pub,
    890                             &h_denom_pub);
    891       if (0 > TALER_amount_subtract (&fee,
    892                                      &c->amount_with_fee,
    893                                      &c->amount_without_fee))
    894         return TALER_MERCHANT_OPOEC_INVALID_VALUE;
    895 
    896 
    897       TALER_wallet_deposit_sign (&c->amount_with_fee,
    898                                  &fee,
    899                                  &ph->h_wire,
    900                                  &ph->h_contract_terms,
    901                                  (NULL != ph->wallet_data)
    902                                 ? &ph->wallet_data_hash
    903                                 : NULL,
    904                                  c->h_age_commitment,
    905                                  NULL,
    906                                  &h_denom_pub,
    907                                  ph->timestamp,
    908                                  &ph->merchant_pub,
    909                                  ph->refund_deadline,
    910                                  &c->coin_priv,
    911                                  &pc.coin_sig);
    912 
    913       pc.denom_pub = c->denom_pub;
    914       pc.denom_sig = c->denom_sig;
    915       pc.denom_value = c->denom_value;
    916       pc.amount_with_fee = c->amount_with_fee;
    917       pc.amount_without_fee = c->amount_without_fee;
    918       pc.exchange_url = c->exchange_url;
    919       GNUNET_CRYPTO_eddsa_key_get_public (&c->coin_priv.eddsa_priv,
    920                                           &pc.coin_pub.eddsa_pub);
    921 
    922       /* JSON  */
    923       je = GNUNET_JSON_PACK (TALER_JSON_pack_amount ("contribution",
    924                                                      &pc.amount_with_fee),
    925                              GNUNET_JSON_pack_data_auto ("coin_pub",
    926                                                          &pc.coin_pub),
    927                              GNUNET_JSON_pack_string ("exchange_url",
    928                                                       pc.exchange_url),
    929                              GNUNET_JSON_pack_data_auto ("h_denom",
    930                                                          &h_denom_pub),
    931                              TALER_JSON_pack_denom_sig ("ub_sig",
    932                                                         &pc.denom_sig),
    933                              GNUNET_JSON_pack_data_auto ("coin_sig",
    934                                                          &pc.coin_sig));
    935       GNUNET_assert (0 ==
    936                      json_array_append_new (arr,
    937                                             je));
    938 
    939       /* optional totals if you need them later
    940        (kept here because they existed in the legacy code) */
    941       if (0 == i)
    942       {
    943         total_fee = fee;
    944         total_amount = pc.amount_with_fee;
    945       }
    946       else
    947       {
    948         if ( (0 >
    949               TALER_amount_add (&total_fee,
    950                                 &total_fee,
    951                                 &fee)) ||
    952              (0 >
    953               TALER_amount_add (&total_amount,
    954                                 &total_amount,
    955                                 &pc.amount_with_fee)) )
    956         {
    957           return TALER_MERCHANT_OPOEC_INVALID_VALUE;
    958         }
    959       }
    960     }
    961 
    962     /* Putting coins to the body*/
    963     {
    964       enum TALER_MERCHANT_OrderPayErrorCode ec =
    965         store_json_option (ph,
    966                            TALER_MERCHANT_OrderPayOptionType_COINS,
    967                            GNUNET_JSON_PACK (
    968                              GNUNET_JSON_pack_array_steal ("coins",
    969                                                            arr)
    970                              ));
    971       if (TALER_MERCHANT_OPOEC_OK != ec)
    972       {
    973         return ec;
    974       }
    975     }
    976   }
    977 
    978   /* sign & pack input_tokens into used_tokens array in body */
    979   if (ph->input_tokens.num_tokens > 0)
    980   {
    981     struct TALER_MERCHANT_UsedToken ut[ph->input_tokens.num_tokens];
    982     json_t *arr = json_array ();
    983 
    984     GNUNET_assert (NULL != arr);
    985     for (unsigned i = 0; i < ph->input_tokens.num_tokens; i++)
    986     {
    987       json_t *je;
    988       const struct TALER_MERCHANT_UseToken *in = &ph->input_tokens.tokens[i];
    989       struct TALER_MERCHANT_UsedToken *t = &ut[i];
    990 
    991       TALER_wallet_token_use_sign (&ph->h_contract_terms,
    992                                    &ph->wallet_data_hash,
    993                                    &in->token_priv,
    994                                    &t->token_sig);
    995 
    996       t->ub_sig = in->ub_sig;
    997       t->issue_pub = in->issue_pub;
    998 
    999       GNUNET_CRYPTO_eddsa_key_get_public (&in->token_priv.private_key,
   1000                                           &t->token_pub.public_key);
   1001 
   1002       je = GNUNET_JSON_PACK (
   1003         GNUNET_JSON_pack_data_auto ("token_sig",
   1004                                     &t->token_sig),
   1005         TALER_JSON_pack_token_issue_sig ("ub_sig",
   1006                                          &t->ub_sig),
   1007         GNUNET_JSON_pack_data_auto ("h_issue",
   1008                                     &t->issue_pub.public_key->pub_key_hash),
   1009         GNUNET_JSON_pack_data_auto ("token_pub",
   1010                                     &t->token_pub)
   1011         );
   1012       GNUNET_assert (0 ==
   1013                      json_array_append_new (arr,
   1014                                             je));
   1015     }
   1016 
   1017     store_json_option (ph,
   1018                        TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS,
   1019                        GNUNET_JSON_PACK (
   1020                          GNUNET_JSON_pack_array_steal ("tokens",
   1021                                                        arr)
   1022                          )
   1023                        );
   1024   }
   1025 
   1026 
   1027   /* post the request */
   1028   {
   1029     char *path;
   1030     CURL *eh;
   1031     GNUNET_asprintf (&path,
   1032                      "orders/%s/pay",
   1033                      ph->order_id);
   1034     ph->url = TALER_url_join (ph->merchant_url,
   1035                               path,
   1036                               NULL);
   1037     GNUNET_free (path);
   1038 
   1039     if (NULL == ph->url)
   1040     {
   1041       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1042                   "Could not construct request URL.\n");
   1043       json_decref (ph->body);
   1044       GNUNET_free (ph);
   1045       return TALER_MERCHANT_OPOEC_URL_FAILURE;
   1046     }
   1047 
   1048     eh = TALER_MERCHANT_curl_easy_get_ (ph->url);
   1049     if (GNUNET_OK !=
   1050         TALER_curl_easy_post (&ph->post_ctx,
   1051                               eh,
   1052                               ph->body))
   1053     {
   1054       GNUNET_break (0);
   1055       curl_easy_cleanup (eh);
   1056       GNUNET_free (ph->url);
   1057       GNUNET_free (ph);
   1058       return TALER_MERCHANT_OPOEC_CURL_FAILURE;
   1059     }
   1060 
   1061     ph->job = GNUNET_CURL_job_add2 (ph->ctx,
   1062                                     eh,
   1063                                     ph->post_ctx.headers,
   1064                                     &handle_finished,
   1065                                     ph);
   1066 
   1067     ph->am_wallet = true;
   1068     return TALER_MERCHANT_OPOEC_OK;
   1069   }
   1070 }