LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-orders-ID-pay.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 371 579 64.1 %
Date: 2021-08-30 06:54:17 Functions: 15 17 88.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2014-2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify
       6             :   it under the terms of the GNU Affero General Public License as
       7             :   published by the Free Software Foundation; either version 3,
       8             :   or (at your option) any later version.
       9             : 
      10             :   TALER is distributed in the hope that it will be useful, but
      11             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :   GNU General Public License for more details.
      14             : 
      15             :   You should have received a copy of the GNU General Public
      16             :   License along with TALER; see the file COPYING.  If not,
      17             :   see <http://www.gnu.org/licenses/>
      18             : */
      19             : 
      20             : /**
      21             :  * @file taler-merchant-httpd_post-orders-ID-pay.c
      22             :  * @brief handling of POST /orders/$ID/pay requests
      23             :  * @author Marcello Stanisci
      24             :  * @author Christian Grothoff
      25             :  * @author Florian Dold
      26             :  */
      27             : #include "platform.h"
      28             : #include <taler/taler_dbevents.h>
      29             : #include <taler/taler_signatures.h>
      30             : #include <taler/taler_json_lib.h>
      31             : #include <taler/taler_exchange_service.h>
      32             : #include "taler-merchant-httpd_auditors.h"
      33             : #include "taler-merchant-httpd_exchanges.h"
      34             : #include "taler-merchant-httpd_helper.h"
      35             : #include "taler-merchant-httpd_post-orders-ID-pay.h"
      36             : #include "taler-merchant-httpd_private-get-orders.h"
      37             : 
      38             : 
      39             : /**
      40             :  * How often do we retry the (complex!) database transaction?
      41             :  */
      42             : #define MAX_RETRIES 5
      43             : 
      44             : /**
      45             :  * Maximum number of coins that we allow per transaction
      46             :  */
      47             : #define MAX_COIN_ALLOWED_COINS 1024
      48             : 
      49             : /**
      50             :  * Information we keep for an individual call to the pay handler.
      51             :  */
      52             : struct PayContext;
      53             : 
      54             : /**
      55             :  * Information kept during a pay request for each coin.
      56             :  */
      57             : struct DepositConfirmation
      58             : {
      59             : 
      60             :   /**
      61             :    * Reference to the main PayContext
      62             :    */
      63             :   struct PayContext *pc;
      64             : 
      65             :   /**
      66             :    * Handle to the deposit operation we are performing for
      67             :    * this coin, NULL after the operation is done.
      68             :    */
      69             :   struct TALER_EXCHANGE_DepositHandle *dh;
      70             : 
      71             :   /**
      72             :    * URL of the exchange that issued this coin.
      73             :    */
      74             :   char *exchange_url;
      75             : 
      76             :   /**
      77             :    * Hash of the denomination of this coin.
      78             :    */
      79             :   struct GNUNET_HashCode h_denom;
      80             : 
      81             :   /**
      82             :    * Amount this coin contributes to the total purchase price.
      83             :    * This amount includes the deposit fee.
      84             :    */
      85             :   struct TALER_Amount amount_with_fee;
      86             : 
      87             :   /**
      88             :    * Fee charged by the exchange for the deposit operation of this coin.
      89             :    */
      90             :   struct TALER_Amount deposit_fee;
      91             : 
      92             :   /**
      93             :    * Fee charged by the exchange for the refund operation of this coin.
      94             :    */
      95             :   struct TALER_Amount refund_fee;
      96             : 
      97             :   /**
      98             :    * Wire fee charged by the exchange of this coin.
      99             :    */
     100             :   struct TALER_Amount wire_fee;
     101             : 
     102             :   /**
     103             :    * Public key of the coin.
     104             :    */
     105             :   struct TALER_CoinSpendPublicKeyP coin_pub;
     106             : 
     107             :   /**
     108             :    * Signature using the @e denom key over the @e coin_pub.
     109             :    */
     110             :   struct TALER_DenominationSignature ub_sig;
     111             : 
     112             :   /**
     113             :    * Signature of the coin's private key over the contract.
     114             :    */
     115             :   struct TALER_CoinSpendSignatureP coin_sig;
     116             : 
     117             :   /**
     118             :    * Offset of this coin into the `dc` array of all coins in the
     119             :    * @e pc.
     120             :    */
     121             :   unsigned int index;
     122             : 
     123             :   /**
     124             :    * true if we found this coin in the database.
     125             :    */
     126             :   bool found_in_db;
     127             : 
     128             : };
     129             : 
     130             : 
     131             : /**
     132             :  * Information we keep for an individual call to the /pay handler.
     133             :  */
     134             : struct PayContext
     135             : {
     136             : 
     137             :   /**
     138             :    * Stored in a DLL.
     139             :    */
     140             :   struct PayContext *next;
     141             : 
     142             :   /**
     143             :    * Stored in a DLL.
     144             :    */
     145             :   struct PayContext *prev;
     146             : 
     147             :   /**
     148             :    * Array with @e coins_cnt coins we are despositing.
     149             :    */
     150             :   struct DepositConfirmation *dc;
     151             : 
     152             :   /**
     153             :    * MHD connection to return to
     154             :    */
     155             :   struct MHD_Connection *connection;
     156             : 
     157             :   /**
     158             :    * Details about the client's request.
     159             :    */
     160             :   struct TMH_HandlerContext *hc;
     161             : 
     162             :   /**
     163             :    * What wire method (of the @e mi) was selected by the wallet?
     164             :    * Set in #parse_pay().
     165             :    */
     166             :   struct TMH_WireMethod *wm;
     167             : 
     168             :   /**
     169             :    * Task called when the (suspended) processing for
     170             :    * the /pay request times out.
     171             :    * Happens when we don't get a response from the exchange.
     172             :    */
     173             :   struct GNUNET_SCHEDULER_Task *timeout_task;
     174             : 
     175             :   /**
     176             :    * Response to return, NULL if we don't have one yet.
     177             :    */
     178             :   struct MHD_Response *response;
     179             : 
     180             :   /**
     181             :    * Handle for operation to lookup /keys (and auditors) from
     182             :    * the exchange used for this transaction; NULL if no operation is
     183             :    * pending.
     184             :    */
     185             :   struct TMH_EXCHANGES_FindOperation *fo;
     186             : 
     187             :   /**
     188             :    * URL of the exchange used for the last @e fo.
     189             :    */
     190             :   const char *current_exchange;
     191             : 
     192             :   /**
     193             :    * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state.
     194             :    */
     195             :   void *json_parse_context;
     196             : 
     197             :   /**
     198             :    * Optional session id given in @e root.
     199             :    * NULL if not given.
     200             :    */
     201             :   char *session_id;
     202             : 
     203             :   /**
     204             :    * Transaction ID given in @e root.
     205             :    */
     206             :   const char *order_id;
     207             : 
     208             :   /**
     209             :    * Fulfillment URL from the contract, or NULL if we don't have one.
     210             :    */
     211             :   char *fulfillment_url;
     212             : 
     213             :   /**
     214             :    * Serial number of this order in the database (set once we did the lookup).
     215             :    */
     216             :   uint64_t order_serial;
     217             : 
     218             :   /**
     219             :    * Hashed proposal.
     220             :    */
     221             :   struct GNUNET_HashCode h_contract_terms;
     222             : 
     223             :   /**
     224             :    * "h_wire" from @e contract_terms.  Used to identify
     225             :    * the instance's wire transfer method.
     226             :    */
     227             :   struct GNUNET_HashCode h_wire;
     228             : 
     229             :   /**
     230             :    * Maximum fee the merchant is willing to pay, from @e root.
     231             :    * Note that IF the total fee of the exchange is higher, that is
     232             :    * acceptable to the merchant if the customer is willing to
     233             :    * pay the difference
     234             :    * (i.e. amount - max_fee <= actual-amount - actual-fee).
     235             :    */
     236             :   struct TALER_Amount max_fee;
     237             : 
     238             :   /**
     239             :    * Maximum wire fee the merchant is willing to pay, from @e root.
     240             :    * Note that IF the total fee of the exchange is higher, that is
     241             :    * acceptable to the merchant if the customer is willing to
     242             :    * pay the amorized difference.  Wire fees are charged over an
     243             :    * aggregate of several translations, hence unlike the deposit
     244             :    * fees, they are amortized over several customer's transactions.
     245             :    * The contract specifies under @e wire_fee_amortization how many
     246             :    * customer's transactions he expects the wire fees to be amortized
     247             :    * over on average.  Thus, if the wire fees are larger than
     248             :    * @e max_wire_fee, each customer is expected to contribute
     249             :    * $\frac{actual-wire-fee - max_wire_fee}{wire_fee_amortization}$.
     250             :    * The customer's contribution may be further reduced by the
     251             :    * difference between @e max_fee and the sum of the deposit fees.
     252             :    *
     253             :    * Default is that the merchant is unwilling to pay any wire fees.
     254             :    */
     255             :   struct TALER_Amount max_wire_fee;
     256             : 
     257             :   /**
     258             :    * Amount from @e root.  This is the amount the merchant expects
     259             :    * to make, minus @e max_fee.
     260             :    */
     261             :   struct TALER_Amount amount;
     262             : 
     263             :   /**
     264             :    * Considering all the coins with the "found_in_db" flag
     265             :    * set, what is the total amount we were so far paid on
     266             :    * this contract?
     267             :    */
     268             :   struct TALER_Amount total_paid;
     269             : 
     270             :   /**
     271             :    * Considering all the coins with the "found_in_db" flag
     272             :    * set, what is the total amount we had to pay in deposit
     273             :    * fees so far on this contract?
     274             :    */
     275             :   struct TALER_Amount total_fees_paid;
     276             : 
     277             :   /**
     278             :    * Considering all the coins with the "found_in_db" flag
     279             :    * set, what is the total amount we already refunded?
     280             :    */
     281             :   struct TALER_Amount total_refunded;
     282             : 
     283             :   /**
     284             :    * Wire transfer deadline. How soon would the merchant like the
     285             :    * wire transfer to be executed?
     286             :    */
     287             :   struct GNUNET_TIME_Absolute wire_transfer_deadline;
     288             : 
     289             :   /**
     290             :    * Timestamp from @e contract_terms.
     291             :    */
     292             :   struct GNUNET_TIME_Absolute timestamp;
     293             : 
     294             :   /**
     295             :    * Refund deadline from @e contract_terms.
     296             :    */
     297             :   struct GNUNET_TIME_Absolute refund_deadline;
     298             : 
     299             :   /**
     300             :    * Deadline for the customer to pay for this proposal.
     301             :    */
     302             :   struct GNUNET_TIME_Absolute pay_deadline;
     303             : 
     304             :   /**
     305             :    * Number of transactions that the wire fees are expected to be
     306             :    * amortized over.  Never zero, defaults (conservateively) to 1.
     307             :    * May be higher if merchants expect many small transactions to
     308             :    * be aggregated and thus wire fees to be reasonably amortized
     309             :    * due to aggregation.
     310             :    */
     311             :   uint32_t wire_fee_amortization;
     312             : 
     313             :   /**
     314             :    * Number of coins this payment is made of.  Length
     315             :    * of the @e dc array.
     316             :    */
     317             :   unsigned int coins_cnt;
     318             : 
     319             :   /**
     320             :    * How often have we retried the 'main' transaction?
     321             :    */
     322             :   unsigned int retry_counter;
     323             : 
     324             :   /**
     325             :    * Number of transactions still pending.  Initially set to
     326             :    * @e coins_cnt, decremented on each transaction that
     327             :    * successfully finished.
     328             :    */
     329             :   unsigned int pending;
     330             : 
     331             :   /**
     332             :    * Number of transactions still pending for the currently selected
     333             :    * exchange.  Initially set to the number of coins started at the
     334             :    * exchange, decremented on each transaction that successfully
     335             :    * finished.  Once it hits zero, we pick the next exchange.
     336             :    */
     337             :   unsigned int pending_at_ce;
     338             : 
     339             :   /**
     340             :    * HTTP status code to use for the reply, i.e 200 for "OK".
     341             :    * Special value UINT_MAX is used to indicate hard errors
     342             :    * (no reply, return #MHD_NO).
     343             :    */
     344             :   unsigned int response_code;
     345             : 
     346             :   /**
     347             :    * #GNUNET_NO if the @e connection was not suspended,
     348             :    * #GNUNET_YES if the @e connection was suspended,
     349             :    * #GNUNET_SYSERR if @e connection was resumed to as
     350             :    * part of #MH_force_pc_resume during shutdown.
     351             :    */
     352             :   enum GNUNET_GenericReturnValue suspended;
     353             : 
     354             :   /**
     355             :    * true if we already tried a forced /keys download.
     356             :    */
     357             :   bool tried_force_keys;
     358             : 
     359             : };
     360             : 
     361             : 
     362             : /**
     363             :  * Head of active pay context DLL.
     364             :  */
     365             : static struct PayContext *pc_head;
     366             : 
     367             : /**
     368             :  * Tail of active pay context DLL.
     369             :  */
     370             : static struct PayContext *pc_tail;
     371             : 
     372             : 
     373             : /**
     374             :  * Compute the timeout for a /pay request based on the number of coins
     375             :  * involved.
     376             :  *
     377             :  * @param num_coins number of coins
     378             :  * @returns timeout for the /pay request
     379             :  */
     380             : static struct GNUNET_TIME_Relative
     381          22 : get_pay_timeout (unsigned int num_coins)
     382             : {
     383             :   struct GNUNET_TIME_Relative t;
     384             : 
     385             :   /* FIXME:  Do some benchmarking to come up with a better timeout.
     386             :    * We've increased this value so the wallet integration test passes again
     387             :    * on my (Florian) machine.
     388             :    */
     389          22 :   t = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
     390          22 :                                      15 * (1 + (num_coins / 5)));
     391             : 
     392          22 :   return t;
     393             : }
     394             : 
     395             : 
     396             : /**
     397             :  * Abort all pending /deposit operations.
     398             :  *
     399             :  * @param pc pay context to abort
     400             :  */
     401             : static void
     402          44 : abort_active_deposits (struct PayContext *pc)
     403             : {
     404          44 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     405             :               "Aborting pending /deposit operations\n");
     406          92 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
     407             :   {
     408          48 :     struct DepositConfirmation *dci = &pc->dc[i];
     409             : 
     410          48 :     if (NULL != dci->dh)
     411             :     {
     412           0 :       TALER_EXCHANGE_deposit_cancel (dci->dh);
     413           0 :       dci->dh = NULL;
     414             :     }
     415             :   }
     416          44 : }
     417             : 
     418             : 
     419             : /**
     420             :  * Force all pay contexts to be resumed as we are about
     421             :  * to shut down MHD.
     422             :  */
     423             : void
     424          16 : TMH_force_pc_resume ()
     425             : {
     426          16 :   for (struct PayContext *pc = pc_head;
     427             :        NULL != pc;
     428           0 :        pc = pc->next)
     429             :   {
     430           0 :     abort_active_deposits (pc);
     431           0 :     if (NULL != pc->timeout_task)
     432             :     {
     433           0 :       GNUNET_SCHEDULER_cancel (pc->timeout_task);
     434           0 :       pc->timeout_task = NULL;
     435             :     }
     436           0 :     if (GNUNET_YES == pc->suspended)
     437             :     {
     438           0 :       pc->suspended = GNUNET_SYSERR;
     439           0 :       MHD_resume_connection (pc->connection);
     440             :     }
     441             :   }
     442          16 : }
     443             : 
     444             : 
     445             : /**
     446             :  * Resume the given pay context and send the given response.
     447             :  * Stores the response in the @a pc and signals MHD to resume
     448             :  * the connection.  Also ensures MHD runs immediately.
     449             :  *
     450             :  * @param pc payment context
     451             :  * @param response_code response code to use
     452             :  * @param response response data to send back
     453             :  */
     454             : static void
     455          22 : resume_pay_with_response (struct PayContext *pc,
     456             :                           unsigned int response_code,
     457             :                           struct MHD_Response *response)
     458             : {
     459          22 :   abort_active_deposits (pc);
     460          22 :   pc->response_code = response_code;
     461          22 :   pc->response = response;
     462          22 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     463             :               "Resuming /pay handling as exchange interaction is done (%u)\n",
     464             :               response_code);
     465          22 :   if (NULL != pc->timeout_task)
     466             :   {
     467          22 :     GNUNET_SCHEDULER_cancel (pc->timeout_task);
     468          22 :     pc->timeout_task = NULL;
     469             :   }
     470          22 :   GNUNET_assert (GNUNET_YES == pc->suspended);
     471          22 :   pc->suspended = GNUNET_NO;
     472          22 :   MHD_resume_connection (pc->connection);
     473          22 :   TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
     474          22 : }
     475             : 
     476             : 
     477             : /**
     478             :  * Resume payment processing with an error.
     479             :  *
     480             :  * @param pc operation to resume
     481             :  * @param http_status http status code to return
     482             :  * @param ec taler error code to return
     483             :  * @param msg human readable error message
     484             :  */
     485             : static void
     486           3 : resume_pay_with_error (struct PayContext *pc,
     487             :                        unsigned int http_status,
     488             :                        enum TALER_ErrorCode ec,
     489             :                        const char *msg)
     490             : {
     491           3 :   resume_pay_with_response (pc,
     492             :                             http_status,
     493             :                             TALER_MHD_make_error (ec,
     494             :                                                   msg));
     495           3 : }
     496             : 
     497             : 
     498             : /**
     499             :  * Custom cleanup routine for a `struct PayContext`.
     500             :  *
     501             :  * @param cls the `struct PayContext` to clean up.
     502             :  */
     503             : static void
     504          22 : pay_context_cleanup (void *cls)
     505             : {
     506          22 :   struct PayContext *pc = cls;
     507             : 
     508          22 :   if (NULL != pc->timeout_task)
     509             :   {
     510           0 :     GNUNET_SCHEDULER_cancel (pc->timeout_task);
     511           0 :     pc->timeout_task = NULL;
     512             :   }
     513          22 :   abort_active_deposits (pc);
     514          46 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
     515             :   {
     516          24 :     struct DepositConfirmation *dc = &pc->dc[i];
     517             : 
     518          24 :     if (NULL != dc->ub_sig.rsa_signature)
     519             :     {
     520          24 :       GNUNET_CRYPTO_rsa_signature_free (dc->ub_sig.rsa_signature);
     521          24 :       dc->ub_sig.rsa_signature = NULL;
     522             :     }
     523          24 :     GNUNET_free (dc->exchange_url);
     524             :   }
     525          22 :   GNUNET_free (pc->dc);
     526          22 :   if (NULL != pc->fo)
     527             :   {
     528           0 :     TMH_EXCHANGES_find_exchange_cancel (pc->fo);
     529           0 :     pc->fo = NULL;
     530             :   }
     531          22 :   if (NULL != pc->response)
     532             :   {
     533           0 :     MHD_destroy_response (pc->response);
     534           0 :     pc->response = NULL;
     535             :   }
     536          22 :   GNUNET_free (pc->fulfillment_url);
     537          22 :   GNUNET_free (pc->session_id);
     538          22 :   GNUNET_CONTAINER_DLL_remove (pc_head,
     539             :                                pc_tail,
     540             :                                pc);
     541          22 :   GNUNET_free (pc);
     542          22 : }
     543             : 
     544             : 
     545             : /**
     546             :  * Find the exchange we need to talk to for the next
     547             :  * pending deposit permission.
     548             :  *
     549             :  * @param pc payment context we are processing
     550             :  */
     551             : static void
     552             : find_next_exchange (struct PayContext *pc);
     553             : 
     554             : 
     555             : /**
     556             :  * Execute the DB transaction.  If required (from
     557             :  * soft/serialization errors), the transaction can be
     558             :  * restarted here.
     559             :  *
     560             :  * @param pc payment context to transact
     561             :  */
     562             : static void
     563             : execute_pay_transaction (struct PayContext *pc);
     564             : 
     565             : 
     566             : /**
     567             :  * Callback to handle a deposit permission's response.
     568             :  *
     569             :  * @param cls a `struct DepositConfirmation` (i.e. a pointer
     570             :  *   into the global array of confirmations and an index for this call
     571             :  *   in that array). That way, the last executed callback can detect
     572             :  *   that no other confirmations are on the way, and can pack a response
     573             :  *   for the wallet
     574             :  * @param hr HTTP response code details
     575             :  * @param deposit_timestamp time when the exchange generated the deposit confirmation
     576             :  * @param exchange_sig signature from the exchange over the deposit confirmation
     577             :  * @param exchange_pub which key did the exchange use to create the @a exchange_sig
     578             :  */
     579             : static void
     580          22 : deposit_cb (void *cls,
     581             :             const struct TALER_EXCHANGE_HttpResponse *hr,
     582             :             struct GNUNET_TIME_Absolute deposit_timestamp,
     583             :             const struct TALER_ExchangeSignatureP *exchange_sig,
     584             :             const struct TALER_ExchangePublicKeyP *exchange_pub)
     585             : {
     586          22 :   struct DepositConfirmation *dc = cls;
     587          22 :   struct PayContext *pc = dc->pc;
     588             : 
     589          22 :   dc->dh = NULL;
     590          22 :   GNUNET_assert (GNUNET_YES == pc->suspended);
     591          22 :   pc->pending_at_ce--;
     592          22 :   if (MHD_HTTP_OK != hr->http_status)
     593             :   {
     594           4 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     595             :                 "Deposit operation failed with HTTP code %u/%d\n",
     596             :                 hr->http_status,
     597             :                 (int) hr->ec);
     598             :     /* Transaction failed */
     599           4 :     if (5 == hr->http_status / 100)
     600             :     {
     601             :       /* internal server error at exchange */
     602           0 :       resume_pay_with_response (pc,
     603             :                                 MHD_HTTP_BAD_GATEWAY,
     604           0 :                                 TALER_MHD_MAKE_JSON_PACK (
     605             :                                   TALER_JSON_pack_ec (
     606             :                                     TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
     607             :                                   TMH_pack_exchange_reply (hr)));
     608             :     }
     609           4 :     else if (NULL == hr->reply)
     610             :     {
     611             :       /* We can't do anything meaningful here, the exchange did something wrong */
     612           0 :       resume_pay_with_response (
     613             :         pc,
     614             :         MHD_HTTP_BAD_GATEWAY,
     615           0 :         TALER_MHD_MAKE_JSON_PACK (
     616             :           TALER_JSON_pack_ec (
     617             :             TALER_EC_MERCHANT_GENERIC_EXCHANGE_REPLY_MALFORMED),
     618             :           TMH_pack_exchange_reply (hr)));
     619             :     }
     620             :     else
     621             :     {
     622             :       /* Forward error, adding the "coin_pub" for which the
     623             :          error was being generated */
     624           4 :       if (TALER_EC_EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS == hr->ec)
     625             :       {
     626           4 :         resume_pay_with_response (
     627             :           pc,
     628             :           MHD_HTTP_CONFLICT,
     629           4 :           TALER_MHD_MAKE_JSON_PACK (
     630             :             TALER_JSON_pack_ec (
     631             :               TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS),
     632             :             TMH_pack_exchange_reply (hr),
     633             :             GNUNET_JSON_pack_data_auto ("coin_pub",
     634             :                                         &dc->coin_pub)));
     635             :       }
     636             :       else
     637             :       {
     638           0 :         resume_pay_with_response (
     639             :           pc,
     640             :           MHD_HTTP_BAD_GATEWAY,
     641           0 :           TALER_MHD_MAKE_JSON_PACK (
     642             :             TALER_JSON_pack_ec (
     643             :               TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
     644             :             TMH_pack_exchange_reply (hr),
     645             :             GNUNET_JSON_pack_data_auto ("coin_pub",
     646             :                                         &dc->coin_pub)));
     647             :       }
     648             :     }
     649           4 :     return;
     650             :   }
     651             : 
     652             :   /* store result to DB */
     653          18 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     654             :               "Storing successful payment %s (%s) at instance `%s'\n",
     655             :               pc->hc->infix,
     656             :               GNUNET_h2s (&pc->h_contract_terms),
     657             :               pc->hc->instance->settings.id);
     658          18 :   TMH_db->preflight (TMH_db->cls);
     659             :   {
     660             :     enum GNUNET_DB_QueryStatus qs;
     661             : 
     662          18 :     qs = TMH_db->insert_deposit (TMH_db->cls,
     663          18 :                                  pc->hc->instance->settings.id,
     664             :                                  deposit_timestamp,
     665          18 :                                  &pc->h_contract_terms,
     666          18 :                                  &dc->coin_pub,
     667          18 :                                  dc->exchange_url,
     668          18 :                                  &dc->amount_with_fee,
     669          18 :                                  &dc->deposit_fee,
     670          18 :                                  &dc->refund_fee,
     671          18 :                                  &dc->wire_fee,
     672          18 :                                  &pc->wm->h_wire,
     673             :                                  exchange_sig,
     674             :                                  exchange_pub);
     675          18 :     if (0 > qs)
     676             :     {
     677             :       /* Special report if retries insufficient */
     678           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     679             :       {
     680           0 :         execute_pay_transaction (pc);
     681           0 :         return;
     682             :       }
     683             :       /* Always report on hard error as well to enable diagnostics */
     684           0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     685             :       /* Forward error including 'proof' for the body */
     686           0 :       resume_pay_with_error (pc,
     687             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     688             :                              TALER_EC_GENERIC_DB_STORE_FAILED,
     689             :                              "deposit");
     690           0 :       return;
     691             :     }
     692             :   }
     693             : 
     694          18 :   dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */
     695          18 :   pc->pending--;
     696             : 
     697          18 :   if (0 != pc->pending_at_ce)
     698           1 :     return; /* still more to do with current exchange */
     699          17 :   find_next_exchange (pc);
     700             : }
     701             : 
     702             : 
     703             : /**
     704             :  * Function called with the result of our exchange lookup.
     705             :  *
     706             :  * @param cls the `struct PayContext`
     707             :  * @param hr HTTP response details
     708             :  * @param exchange_handle NULL if exchange was not found to be acceptable
     709             :  * @param payto_uri payto://-URI of the exchange
     710             :  * @param wire_fee current applicable fee for dealing with @a exchange_handle,
     711             :  *        NULL if not available
     712             :  * @param exchange_trusted true if this exchange is
     713             :  *        trusted by config
     714             :  */
     715             : static void
     716          21 : process_pay_with_exchange (void *cls,
     717             :                            const struct TALER_EXCHANGE_HttpResponse *hr,
     718             :                            struct TALER_EXCHANGE_Handle *exchange_handle,
     719             :                            const char *payto_uri,
     720             :                            const struct TALER_Amount *wire_fee,
     721             :                            bool exchange_trusted)
     722             : {
     723          21 :   struct PayContext *pc = cls;
     724          21 :   struct TMH_HandlerContext *hc = pc->hc;
     725             :   const struct TALER_EXCHANGE_Keys *keys;
     726             : 
     727             :   (void) payto_uri;
     728          21 :   pc->fo = NULL;
     729          21 :   GNUNET_assert (GNUNET_YES == pc->suspended);
     730          21 :   if (NULL == hr)
     731             :   {
     732           0 :     resume_pay_with_response (
     733             :       pc,
     734             :       MHD_HTTP_GATEWAY_TIMEOUT,
     735           0 :       TALER_MHD_MAKE_JSON_PACK (
     736             :         TALER_JSON_pack_ec (TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT)));
     737           0 :     return;
     738             :   }
     739          21 :   if ( (MHD_HTTP_OK != hr->http_status) ||
     740             :        (NULL == exchange_handle) )
     741             :   {
     742           0 :     resume_pay_with_response (
     743             :       pc,
     744             :       MHD_HTTP_BAD_GATEWAY,
     745           0 :       TALER_MHD_MAKE_JSON_PACK (
     746             :         TALER_JSON_pack_ec (
     747             :           TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE),
     748             :         TMH_pack_exchange_reply (hr)));
     749           0 :     return;
     750             :   }
     751          21 :   keys = TALER_EXCHANGE_get_keys (exchange_handle);
     752          21 :   if (NULL == keys)
     753             :   {
     754           0 :     GNUNET_break (0); /* should not be possible if HTTP status is #MHD_HTTP_OK */
     755           0 :     resume_pay_with_error (pc,
     756             :                            MHD_HTTP_BAD_GATEWAY,
     757             :                            TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE,
     758             :                            NULL);
     759           0 :     return;
     760             :   }
     761             : 
     762             :   /* Initiate /deposit operation for all coins of
     763             :      the current exchange (!) */
     764          21 :   GNUNET_assert (0 == pc->pending_at_ce);
     765          44 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
     766             :   {
     767          23 :     struct DepositConfirmation *dc = &pc->dc[i];
     768             :     const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
     769             :     enum TALER_ErrorCode ec;
     770             :     unsigned int http_status;
     771             : 
     772          23 :     if (NULL != dc->dh)
     773           1 :       continue; /* we were here before (can happen due to
     774             :                    tried_force_keys logic), don't go again */
     775          23 :     if (dc->found_in_db)
     776           1 :       continue;
     777          22 :     if (0 != strcmp (dc->exchange_url,
     778             :                      pc->current_exchange))
     779           0 :       continue;
     780             :     denom_details
     781          22 :       = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
     782          22 :                                                      &dc->h_denom);
     783          22 :     if (NULL == denom_details)
     784             :     {
     785           0 :       if (! pc->tried_force_keys)
     786             :       {
     787             :         /* let's try *forcing* a re-download of /keys from the exchange.
     788             :            Maybe the wallet has seen /keys that we missed. */
     789           0 :         pc->tried_force_keys = true;
     790           0 :         pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange,
     791           0 :                                               pc->wm->wire_method,
     792             :                                               GNUNET_YES,
     793             :                                               &process_pay_with_exchange,
     794             :                                               pc);
     795           0 :         if (NULL != pc->fo)
     796           0 :           return;
     797             :       }
     798             :       /* Forcing failed or we already did it, give up */
     799           0 :       resume_pay_with_response (
     800             :         pc,
     801             :         MHD_HTTP_BAD_REQUEST,
     802           0 :         TALER_MHD_MAKE_JSON_PACK (
     803             :           TALER_JSON_pack_ec (
     804             :             TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND),
     805             :           GNUNET_JSON_pack_data_auto ("h_denom_pub",
     806             :                                       &dc->h_denom),
     807             :           GNUNET_JSON_pack_allow_null (
     808             :             GNUNET_JSON_pack_object_incref (
     809             :               "exchange_keys",
     810             :               (json_t *) TALER_EXCHANGE_get_keys_raw (exchange_handle)))));
     811           0 :       return;
     812             :     }
     813          22 :     if (GNUNET_OK !=
     814          22 :         TMH_AUDITORS_check_dk (exchange_handle,
     815             :                                denom_details,
     816             :                                exchange_trusted,
     817             :                                &http_status,
     818             :                                &ec))
     819             :     {
     820           0 :       if (! pc->tried_force_keys)
     821             :       {
     822             :         /* let's try *forcing* a re-download of /keys from the exchange.
     823             :            Maybe the wallet has seen auditors that we missed. */
     824           0 :         pc->tried_force_keys = true;
     825           0 :         pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange,
     826           0 :                                               pc->wm->wire_method,
     827             :                                               GNUNET_YES,
     828             :                                               &process_pay_with_exchange,
     829             :                                               pc);
     830           0 :         if (NULL != pc->fo)
     831           0 :           return;
     832             :       }
     833           0 :       resume_pay_with_response (
     834             :         pc,
     835             :         http_status,
     836           0 :         TALER_MHD_MAKE_JSON_PACK (
     837             :           TALER_JSON_pack_ec (ec),
     838             :           GNUNET_JSON_pack_data_auto ("h_denom_pub",
     839             :                                       &denom_details->h_key)));
     840           0 :       return;
     841             :     }
     842             : 
     843          22 :     dc->deposit_fee = denom_details->fee_deposit;
     844          22 :     dc->refund_fee = denom_details->fee_refund;
     845          22 :     dc->wire_fee = *wire_fee;
     846          22 :     GNUNET_assert (NULL != pc->wm);
     847          22 :     GNUNET_assert (NULL != pc->wm->j_wire);
     848          22 :     TMH_db->preflight (TMH_db->cls);
     849          44 :     dc->dh = TALER_EXCHANGE_deposit (exchange_handle,
     850          22 :                                      &dc->amount_with_fee,
     851             :                                      pc->wire_transfer_deadline,
     852          22 :                                      pc->wm->j_wire,
     853          22 :                                      &pc->h_contract_terms,
     854          22 :                                      &dc->coin_pub,
     855          22 :                                      &dc->ub_sig,
     856             :                                      &denom_details->key,
     857             :                                      pc->timestamp,
     858          22 :                                      &hc->instance->merchant_pub,
     859             :                                      pc->refund_deadline,
     860          22 :                                      &dc->coin_sig,
     861             :                                      &deposit_cb,
     862             :                                      dc,
     863             :                                      &ec);
     864          22 :     if (NULL == dc->dh)
     865             :     {
     866             :       /* Signature was invalid or some other constraint was not satisfied.  If
     867             :          the exchange was unavailable, we'd get that information in the
     868             :          callback. */
     869           0 :       GNUNET_break_op (0);
     870           0 :       resume_pay_with_response (
     871             :         pc,
     872             :         TALER_ErrorCode_get_http_status_safe (ec),
     873           0 :         TALER_MHD_MAKE_JSON_PACK (
     874             :           TALER_JSON_pack_ec (ec),
     875             :           GNUNET_JSON_pack_uint64 ("coin_idx",
     876             :                                    i)));
     877           0 :       return;
     878             :     }
     879          22 :     if (TMH_force_audit)
     880           0 :       TALER_EXCHANGE_deposit_force_dc (dc->dh);
     881          22 :     pc->pending_at_ce++;
     882             :   }
     883             : }
     884             : 
     885             : 
     886             : /**
     887             :  * Find the exchange we need to talk to for the next
     888             :  * pending deposit permission.
     889             :  *
     890             :  * @param pc payment context we are processing
     891             :  */
     892             : static void
     893          38 : find_next_exchange (struct PayContext *pc)
     894             : {
     895          38 :   GNUNET_assert (0 == pc->pending_at_ce);
     896          57 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
     897             :   {
     898          40 :     struct DepositConfirmation *dc = &pc->dc[i];
     899             : 
     900          40 :     if (dc->found_in_db)
     901          19 :       continue;
     902             : 
     903          21 :     pc->current_exchange = dc->exchange_url;
     904          42 :     pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange,
     905          21 :                                           pc->wm->wire_method,
     906             :                                           GNUNET_NO,
     907             :                                           &process_pay_with_exchange,
     908             :                                           pc);
     909          21 :     if (NULL == pc->fo)
     910             :     {
     911           0 :       GNUNET_break (0);
     912           0 :       resume_pay_with_error (pc,
     913             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     914             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LOOKUP_FAILED,
     915             :                              "Failed to lookup exchange by URL");
     916           0 :       return;
     917             :     }
     918          21 :     return;
     919             :   }
     920          17 :   pc->current_exchange = NULL;
     921             :   /* We are done with all the HTTP requests, go back and try
     922             :      the 'big' database transaction! (It should work now!) */
     923          17 :   GNUNET_assert (0 == pc->pending);
     924          17 :   execute_pay_transaction (pc);
     925             : }
     926             : 
     927             : 
     928             : /**
     929             :  * Function called with information about a coin that was deposited.
     930             :  *
     931             :  * @param cls closure
     932             :  * @param exchange_url exchange where @a coin_pub was deposited
     933             :  * @param coin_pub public key of the coin
     934             :  * @param amount_with_fee amount the exchange will deposit for this coin
     935             :  * @param deposit_fee fee the exchange will charge for this coin
     936             :  * @param refund_fee fee the exchange will charge for refunding this coin
     937             :  * @param wire_fee wire fee the exchange of this coin charges
     938             :  */
     939             : static void
     940          21 : check_coin_paid (void *cls,
     941             :                  const char *exchange_url,
     942             :                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
     943             :                  const struct TALER_Amount *amount_with_fee,
     944             :                  const struct TALER_Amount *deposit_fee,
     945             :                  const struct TALER_Amount *refund_fee,
     946             :                  const struct TALER_Amount *wire_fee)
     947             : {
     948          21 :   struct PayContext *pc = cls;
     949             : 
     950          45 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
     951             :   {
     952          24 :     struct DepositConfirmation *dc = &pc->dc[i];
     953             : 
     954          24 :     if (dc->found_in_db)
     955           1 :       continue; /* processed earlier, skip "expensive" memcmp() */
     956             :     /* Get matching coin from results*/
     957          23 :     if ( (0 != GNUNET_memcmp (coin_pub,
     958          20 :                               &dc->coin_pub)) ||
     959             :          (0 !=
     960          20 :           strcmp (exchange_url,
     961          40 :                   dc->exchange_url)) ||
     962          20 :          (0 != TALER_amount_cmp (amount_with_fee,
     963          20 :                                  &dc->amount_with_fee)) )
     964           3 :       continue; /* does not match, skip */
     965          20 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     966             :                 "Deposit of coin `%s' already in our DB.\n",
     967             :                 TALER_B2S (coin_pub));
     968             : 
     969          20 :     GNUNET_assert (0 <=
     970             :                    TALER_amount_add (&pc->total_paid,
     971             :                                      &pc->total_paid,
     972             :                                      amount_with_fee));
     973          20 :     GNUNET_assert (0 <=
     974             :                    TALER_amount_add (&pc->total_fees_paid,
     975             :                                      &pc->total_fees_paid,
     976             :                                      deposit_fee));
     977          20 :     dc->deposit_fee = *deposit_fee;
     978          20 :     dc->refund_fee = *refund_fee;
     979          20 :     dc->wire_fee = *wire_fee;
     980          20 :     dc->amount_with_fee = *amount_with_fee;
     981          20 :     dc->found_in_db = true;
     982          20 :     pc->pending--;
     983             :   }
     984          21 : }
     985             : 
     986             : 
     987             : /**
     988             :  * Function called with information about a refund.  Check if this coin was
     989             :  * claimed by the wallet for the transaction, and if so add the refunded
     990             :  * amount to the pc's "total_refunded" amount.
     991             :  *
     992             :  * @param cls closure with a `struct PayContext`
     993             :  * @param coin_pub public coin from which the refund comes from
     994             :  * @param refund_amount refund amount which is being taken from @a coin_pub
     995             :  */
     996             : static void
     997           0 : check_coin_refunded (void *cls,
     998             :                      const struct TALER_CoinSpendPublicKeyP *coin_pub,
     999             :                      const struct TALER_Amount *refund_amount)
    1000             : {
    1001           0 :   struct PayContext *pc = cls;
    1002             : 
    1003             :   /* We look at refunds here that apply to the coins
    1004             :      that the customer is currently trying to pay us with.
    1005             : 
    1006             :      Such refunds are not "normal" refunds, but abort-pay refunds, which are
    1007             :      given in the case that the wallet aborts the payment.
    1008             :      In the case the wallet then decides to complete the payment *after* doing
    1009             :      an abort-pay refund (an unusual but possible case), we need
    1010             :      to make sure that existing refunds are accounted for. */
    1011             : 
    1012           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    1013             :   {
    1014           0 :     struct DepositConfirmation *dc = &pc->dc[i];
    1015             :     /* Get matching coins from results.  */
    1016           0 :     if (0 != GNUNET_memcmp (coin_pub,
    1017             :                             &dc->coin_pub))
    1018           0 :       continue;
    1019           0 :     GNUNET_assert (0 <=
    1020             :                    TALER_amount_add (&pc->total_refunded,
    1021             :                                      &pc->total_refunded,
    1022             :                                      refund_amount));
    1023           0 :     break;
    1024             :   }
    1025           0 : }
    1026             : 
    1027             : 
    1028             : /**
    1029             :  * Check whether the amount paid is sufficient to cover the price.
    1030             :  *
    1031             :  * @param pc payment context to check
    1032             :  * @return true if the payment is sufficient, false if it is
    1033             :  *         insufficient
    1034             :  */
    1035             : static bool
    1036          18 : check_payment_sufficient (struct PayContext *pc)
    1037             : {
    1038             :   struct TALER_Amount acc_fee;
    1039             :   struct TALER_Amount acc_amount;
    1040             :   struct TALER_Amount final_amount;
    1041             :   struct TALER_Amount wire_fee_delta;
    1042             :   struct TALER_Amount wire_fee_customer_contribution;
    1043             :   struct TALER_Amount total_wire_fee;
    1044             :   struct TALER_Amount total_needed;
    1045             : 
    1046          18 :   if (0 == pc->coins_cnt)
    1047             :   {
    1048           0 :     return ((0 == pc->amount.value) &&
    1049           0 :             (0 == pc->amount.fraction));
    1050             :   }
    1051             : 
    1052          18 :   acc_fee = pc->dc[0].deposit_fee;
    1053          18 :   total_wire_fee = pc->dc[0].wire_fee;
    1054          18 :   acc_amount = pc->dc[0].amount_with_fee;
    1055             : 
    1056             :   /**
    1057             :    * This loops calculates what are the deposit fee / total
    1058             :    * amount with fee / and wire fee, for all the coins.
    1059             :    */
    1060          19 :   for (unsigned int i = 1; i<pc->coins_cnt; i++)
    1061             :   {
    1062           1 :     struct DepositConfirmation *dc = &pc->dc[i];
    1063             : 
    1064           1 :     GNUNET_assert (dc->found_in_db);
    1065           1 :     if ( (0 >
    1066           1 :           TALER_amount_add (&acc_fee,
    1067           1 :                             &dc->deposit_fee,
    1068           1 :                             &acc_fee)) ||
    1069             :          (0 >
    1070           1 :           TALER_amount_add (&acc_amount,
    1071           1 :                             &dc->amount_with_fee,
    1072             :                             &acc_amount)) )
    1073             :     {
    1074           0 :       GNUNET_break (0);
    1075             :       /* Overflow in these amounts? Very strange. */
    1076           0 :       resume_pay_with_error (pc,
    1077             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1078             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
    1079             :                              "Overflow adding up amounts");
    1080           0 :       return false;
    1081             :     }
    1082           1 :     if (1 ==
    1083           1 :         TALER_amount_cmp (&dc->deposit_fee,
    1084           1 :                           &dc->amount_with_fee))
    1085             :     {
    1086           0 :       GNUNET_break_op (0);
    1087           0 :       resume_pay_with_error (pc,
    1088             :                              MHD_HTTP_BAD_REQUEST,
    1089             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_FEES_EXCEED_PAYMENT,
    1090             :                              "Deposit fees exceed coin's contribution");
    1091           0 :       return false;
    1092             :     }
    1093             : 
    1094             :     /* If exchange differs, add wire fee */
    1095             :     {
    1096           1 :       bool new_exchange = true;
    1097             : 
    1098           1 :       for (unsigned int j = 0; j<i; j++)
    1099           1 :         if (0 == strcasecmp (dc->exchange_url,
    1100           1 :                              pc->dc[j].exchange_url))
    1101             :         {
    1102           1 :           new_exchange = false;
    1103           1 :           break;
    1104             :         }
    1105             : 
    1106           1 :       if (! new_exchange)
    1107           1 :         continue;
    1108             : 
    1109           0 :       if (GNUNET_OK !=
    1110           0 :           TALER_amount_cmp_currency (&total_wire_fee,
    1111           0 :                                      &dc->wire_fee))
    1112             :       {
    1113           0 :         GNUNET_break_op (0);
    1114           0 :         resume_pay_with_error (pc,
    1115             :                                MHD_HTTP_CONFLICT,
    1116             :                                TALER_EC_GENERIC_CURRENCY_MISMATCH,
    1117             :                                total_wire_fee.currency);
    1118           0 :         return false;
    1119             :       }
    1120           0 :       if (0 >
    1121           0 :           TALER_amount_add (&total_wire_fee,
    1122             :                             &total_wire_fee,
    1123           0 :                             &dc->wire_fee))
    1124             :       {
    1125           0 :         GNUNET_break (0);
    1126           0 :         resume_pay_with_error (pc,
    1127             :                                MHD_HTTP_INTERNAL_SERVER_ERROR,
    1128             :                                TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED,
    1129             :                                "could not add exchange wire fee to total");
    1130           0 :         return false;
    1131             :       }
    1132             :     }
    1133             :   } /* deposit loop */
    1134             : 
    1135          18 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1136             :               "Amount received from wallet: %s\n",
    1137             :               TALER_amount2s (&acc_amount));
    1138          18 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1139             :               "Deposit fee for all coins: %s\n",
    1140             :               TALER_amount2s (&acc_fee));
    1141          18 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1142             :               "Total wire fee: %s\n",
    1143             :               TALER_amount2s (&total_wire_fee));
    1144          18 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1145             :               "Max wire fee: %s\n",
    1146             :               TALER_amount2s (&pc->max_wire_fee));
    1147          18 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1148             :               "Deposit fee limit for merchant: %s\n",
    1149             :               TALER_amount2s (&pc->max_fee));
    1150          18 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1151             :               "Total refunded amount: %s\n",
    1152             :               TALER_amount2s (&pc->total_refunded));
    1153             : 
    1154             :   /* Now compare exchange wire fee compared to
    1155             :    * what we are willing to pay */
    1156          18 :   if (GNUNET_YES !=
    1157          18 :       TALER_amount_cmp_currency (&total_wire_fee,
    1158          18 :                                  &pc->max_wire_fee))
    1159             :   {
    1160           0 :     GNUNET_break (0);
    1161           0 :     resume_pay_with_error (pc,
    1162             :                            MHD_HTTP_CONFLICT,
    1163             :                            TALER_EC_GENERIC_CURRENCY_MISMATCH,
    1164             :                            total_wire_fee.currency);
    1165           0 :     return false;
    1166             :   }
    1167             : 
    1168          18 :   switch (TALER_amount_subtract (&wire_fee_delta,
    1169             :                                  &total_wire_fee,
    1170          18 :                                  &pc->max_wire_fee))
    1171             :   {
    1172           0 :   case TALER_AAR_RESULT_POSITIVE:
    1173             :     /* Actual wire fee is indeed higher than our maximum,
    1174             :        compute how much the customer is expected to cover!  */
    1175           0 :     TALER_amount_divide (&wire_fee_customer_contribution,
    1176             :                          &wire_fee_delta,
    1177             :                          pc->wire_fee_amortization);
    1178           0 :     break;
    1179          18 :   case TALER_AAR_RESULT_ZERO:
    1180             :   case TALER_AAR_INVALID_NEGATIVE_RESULT:
    1181             :     /* Wire fee threshold is still above the wire fee amount.
    1182             :        Customer is not going to contribute on this.  */
    1183          18 :     GNUNET_assert (GNUNET_OK ==
    1184             :                    TALER_amount_set_zero (total_wire_fee.currency,
    1185             :                                           &wire_fee_customer_contribution));
    1186          18 :     break;
    1187           0 :   default:
    1188           0 :     GNUNET_assert (0);
    1189             :   }
    1190             : 
    1191             :   /* add wire fee contribution to the total fees */
    1192          18 :   if (0 >
    1193          18 :       TALER_amount_add (&acc_fee,
    1194             :                         &acc_fee,
    1195             :                         &wire_fee_customer_contribution))
    1196             :   {
    1197           0 :     GNUNET_break (0);
    1198           0 :     resume_pay_with_error (pc,
    1199             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1200             :                            TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
    1201             :                            "Overflow adding up amounts");
    1202           0 :     return false;
    1203             :   }
    1204          18 :   if (-1 == TALER_amount_cmp (&pc->max_fee,
    1205             :                               &acc_fee))
    1206             :   {
    1207             :     /**
    1208             :      * Sum of fees of *all* the different exchanges of all the coins are
    1209             :      * higher than the fixed limit that the merchant is willing to pay.  The
    1210             :      * difference must be paid by the customer.
    1211             :      *///
    1212             :     struct TALER_Amount excess_fee;
    1213             : 
    1214             :     /* compute fee amount to be covered by customer */
    1215           0 :     GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
    1216             :                    TALER_amount_subtract (&excess_fee,
    1217             :                                           &acc_fee,
    1218             :                                           &pc->max_fee));
    1219             :     /* add that to the total */
    1220           0 :     if (0 >
    1221           0 :         TALER_amount_add (&total_needed,
    1222             :                           &excess_fee,
    1223           0 :                           &pc->amount))
    1224             :     {
    1225           0 :       GNUNET_break (0);
    1226           0 :       resume_pay_with_error (pc,
    1227             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1228             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
    1229             :                              "Overflow adding up amounts");
    1230           0 :       return false;
    1231             :     }
    1232             :   }
    1233             :   else
    1234             :   {
    1235             :     /* Fees are fully covered by the merchant, all we require
    1236             :        is that the total payment is not below the contract's amount */
    1237          18 :     total_needed = pc->amount;
    1238             :   }
    1239             : 
    1240             :   /* Do not count refunds towards the payment */
    1241          18 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1242             :               "Subtracting total refunds from paid amount: %s\n",
    1243             :               TALER_amount2s (&pc->total_refunded));
    1244          18 :   if (0 >
    1245          18 :       TALER_amount_subtract (&final_amount,
    1246             :                              &acc_amount,
    1247          18 :                              &pc->total_refunded))
    1248             :   {
    1249           0 :     GNUNET_break (0);
    1250           0 :     resume_pay_with_error (pc,
    1251             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1252             :                            TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDS_EXCEED_PAYMENTS,
    1253             :                            "refunded amount exceeds total payments");
    1254           0 :     return false;
    1255             :   }
    1256             : 
    1257          18 :   if (-1 == TALER_amount_cmp (&final_amount,
    1258             :                               &total_needed))
    1259             :   {
    1260             :     /* acc_amount < total_needed */
    1261           3 :     if (-1 < TALER_amount_cmp (&acc_amount,
    1262             :                                &total_needed))
    1263             :     {
    1264           0 :       GNUNET_break_op (0);
    1265           0 :       resume_pay_with_error (pc,
    1266             :                              MHD_HTTP_PAYMENT_REQUIRED,
    1267             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDED,
    1268             :                              "contract not paid up due to refunds");
    1269             :     }
    1270           3 :     else if (-1 < TALER_amount_cmp (&acc_amount,
    1271           3 :                                     &pc->amount))
    1272             :     {
    1273           0 :       GNUNET_break_op (0);
    1274           0 :       resume_pay_with_error (pc,
    1275             :                              MHD_HTTP_NOT_ACCEPTABLE,
    1276             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES,
    1277             :                              "contract not paid up due to fees (client may have calculated them badly)");
    1278             :     }
    1279             :     else
    1280             :     {
    1281           3 :       GNUNET_break_op (0);
    1282           3 :       resume_pay_with_error (pc,
    1283             :                              MHD_HTTP_NOT_ACCEPTABLE,
    1284             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT,
    1285             :                              "payment insufficient");
    1286             :     }
    1287           3 :     return false;
    1288             :   }
    1289          15 :   return true;
    1290             : }
    1291             : 
    1292             : 
    1293             : /**
    1294             :  * Use database to notify other clients about the
    1295             :  * payment being completed.
    1296             :  *
    1297             :  * @param pc context to trigger notification for
    1298             :  */
    1299             : static void
    1300          15 : trigger_payment_notification (struct PayContext *pc)
    1301             : {
    1302             :   {
    1303          15 :     struct TMH_OrderPayEventP pay_eh = {
    1304          15 :       .header.size = htons (sizeof (pay_eh)),
    1305          15 :       .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
    1306          15 :       .merchant_pub = pc->hc->instance->merchant_pub
    1307             :     };
    1308             : 
    1309          15 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1310             :                 "Notifying clients about payment of order %s\n",
    1311             :                 pc->order_id);
    1312          15 :     GNUNET_CRYPTO_hash (pc->order_id,
    1313             :                         strlen (pc->order_id),
    1314             :                         &pay_eh.h_order_id);
    1315          15 :     TMH_db->event_notify (TMH_db->cls,
    1316             :                           &pay_eh.header,
    1317             :                           NULL,
    1318             :                           0);
    1319             :   }
    1320          15 :   if ( (NULL != pc->session_id) &&
    1321          15 :        (NULL != pc->fulfillment_url) )
    1322             :   {
    1323          14 :     struct TMH_SessionEventP session_eh = {
    1324          14 :       .header.size = htons (sizeof (session_eh)),
    1325          14 :       .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
    1326          14 :       .merchant_pub = pc->hc->instance->merchant_pub
    1327             :     };
    1328             : 
    1329          14 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1330             :                 "Notifying clients about session change to %s for %s\n",
    1331             :                 pc->session_id,
    1332             :                 pc->fulfillment_url);
    1333          14 :     GNUNET_CRYPTO_hash (pc->session_id,
    1334          14 :                         strlen (pc->session_id),
    1335             :                         &session_eh.h_session_id);
    1336          14 :     GNUNET_CRYPTO_hash (pc->fulfillment_url,
    1337          14 :                         strlen (pc->fulfillment_url),
    1338             :                         &session_eh.h_fulfillment_url);
    1339          14 :     TMH_db->event_notify (TMH_db->cls,
    1340             :                           &session_eh.header,
    1341             :                           NULL,
    1342             :                           0);
    1343             :   }
    1344          15 : }
    1345             : 
    1346             : 
    1347             : /**
    1348             :  *
    1349             :  *
    1350             :  */
    1351             : static void
    1352          39 : execute_pay_transaction (struct PayContext *pc)
    1353             : {
    1354          39 :   struct TMH_HandlerContext *hc = pc->hc;
    1355          39 :   const char *instance_id = hc->instance->settings.id;
    1356             : 
    1357             :   /* Avoid re-trying transactions on soft errors forever! */
    1358          39 :   if (pc->retry_counter++ > MAX_RETRIES)
    1359             :   {
    1360           0 :     GNUNET_break (0);
    1361           0 :     resume_pay_with_error (pc,
    1362             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1363             :                            TALER_EC_GENERIC_DB_SOFT_FAILURE,
    1364             :                            NULL);
    1365           0 :     return;
    1366             :   }
    1367          39 :   GNUNET_assert (GNUNET_YES == pc->suspended);
    1368             : 
    1369             :   /* Initialize some amount accumulators
    1370             :      (used in check_coin_paid(), check_coin_refunded()
    1371             :      and check_payment_sufficient()). */
    1372          39 :   GNUNET_break (GNUNET_OK ==
    1373             :                 TALER_amount_set_zero (pc->amount.currency,
    1374             :                                        &pc->total_paid));
    1375          39 :   GNUNET_break (GNUNET_OK ==
    1376             :                 TALER_amount_set_zero (pc->amount.currency,
    1377             :                                        &pc->total_fees_paid));
    1378          39 :   GNUNET_break (GNUNET_OK ==
    1379             :                 TALER_amount_set_zero (pc->amount.currency,
    1380             :                                        &pc->total_refunded));
    1381          81 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    1382          42 :     pc->dc[i].found_in_db = false;
    1383          39 :   pc->pending = pc->coins_cnt;
    1384             : 
    1385             :   /* First, try to see if we have all we need already done */
    1386          39 :   TMH_db->preflight (TMH_db->cls);
    1387          39 :   if (GNUNET_OK !=
    1388          39 :       TMH_db->start (TMH_db->cls,
    1389             :                      "run pay"))
    1390             :   {
    1391           0 :     GNUNET_break (0);
    1392           0 :     resume_pay_with_error (pc,
    1393             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1394             :                            TALER_EC_GENERIC_DB_START_FAILED,
    1395             :                            NULL);
    1396           0 :     return;
    1397             :   }
    1398             : 
    1399             :   {
    1400             :     enum GNUNET_DB_QueryStatus qs;
    1401             : 
    1402             :     /* Check if some of these coins already succeeded for _this_ contract.  */
    1403          39 :     qs = TMH_db->lookup_deposits (TMH_db->cls,
    1404             :                                   instance_id,
    1405          39 :                                   &pc->h_contract_terms,
    1406             :                                   &check_coin_paid,
    1407             :                                   pc);
    1408          39 :     if (0 > qs)
    1409             :     {
    1410           0 :       TMH_db->rollback (TMH_db->cls);
    1411           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    1412             :       {
    1413           0 :         execute_pay_transaction (pc);
    1414           0 :         return;
    1415             :       }
    1416             :       /* Always report on hard error as well to enable diagnostics */
    1417           0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    1418           0 :       resume_pay_with_error (pc,
    1419             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1420             :                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    1421             :                              "lookup deposits");
    1422           0 :       return;
    1423             :     }
    1424             :   }
    1425             : 
    1426             : 
    1427             :   {
    1428             :     enum GNUNET_DB_QueryStatus qs;
    1429             : 
    1430             :     /* Check if we refunded some of the coins */
    1431          39 :     qs = TMH_db->lookup_refunds (TMH_db->cls,
    1432             :                                  instance_id,
    1433          39 :                                  &pc->h_contract_terms,
    1434             :                                  &check_coin_refunded,
    1435             :                                  pc);
    1436          39 :     if (0 > qs)
    1437             :     {
    1438           0 :       TMH_db->rollback (TMH_db->cls);
    1439           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    1440             :       {
    1441           0 :         execute_pay_transaction (pc);
    1442           0 :         return;
    1443             :       }
    1444             :       /* Always report on hard error as well to enable diagnostics */
    1445           0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    1446           0 :       resume_pay_with_error (pc,
    1447             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1448             :                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    1449             :                              "lookup refunds");
    1450           0 :       return;
    1451             :     }
    1452             :   }
    1453             : 
    1454             :   /* Check if there are coins that still need to be processed */
    1455          39 :   if (0 != pc->pending)
    1456             :   {
    1457             :     /* we made no DB changes, so we can just rollback */
    1458          21 :     TMH_db->rollback (TMH_db->cls);
    1459             : 
    1460             :     /* Ok, we need to first go to the network to process more coins.
    1461             :        We that interaction in *tiny* transactions (hence the rollback
    1462             :        above). */
    1463          21 :     find_next_exchange (pc);
    1464          21 :     return;
    1465             :   }
    1466             : 
    1467             :   /* 0 == pc->pending: all coins processed, let's see if that was enough */
    1468          18 :   if (! check_payment_sufficient (pc))
    1469             :   {
    1470             :     /* check_payment_sufficient() will have queued an error already.
    1471             :        We need to still abort the transaction. */
    1472           3 :     TMH_db->rollback (TMH_db->cls);
    1473           3 :     return;
    1474             :   }
    1475             :   /* Payment succeeded, save in database */
    1476          15 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1477             :               "Order `%s' (%s) was fully paid\n",
    1478             :               pc->order_id,
    1479             :               GNUNET_h2s (&pc->h_contract_terms));
    1480             :   {
    1481             :     enum GNUNET_DB_QueryStatus qs;
    1482             : 
    1483          15 :     qs = TMH_db->mark_contract_paid (TMH_db->cls,
    1484             :                                      instance_id,
    1485          15 :                                      &pc->h_contract_terms,
    1486          15 :                                      pc->session_id);
    1487          15 :     if (qs < 0)
    1488             :     {
    1489           0 :       TMH_db->rollback (TMH_db->cls);
    1490           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    1491             :       {
    1492           0 :         execute_pay_transaction (pc);
    1493           0 :         return;
    1494             :       }
    1495           0 :       GNUNET_break (0);
    1496           0 :       resume_pay_with_error (pc,
    1497             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1498             :                              TALER_EC_GENERIC_DB_STORE_FAILED,
    1499             :                              "mark contract paid");
    1500           0 :       return;
    1501             :     }
    1502             :   }
    1503             : 
    1504             :   {
    1505             :     enum GNUNET_DB_QueryStatus qs;
    1506             : 
    1507             :     /* Now commit! */
    1508          15 :     qs = TMH_db->commit (TMH_db->cls);
    1509          15 :     if (0 > qs)
    1510             :     {
    1511             :       /* commit failed */
    1512           0 :       TMH_db->rollback (TMH_db->cls);
    1513           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    1514             :       {
    1515           0 :         execute_pay_transaction (pc);
    1516           0 :         return;
    1517             :       }
    1518           0 :       GNUNET_break (0);
    1519           0 :       resume_pay_with_error (pc,
    1520             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1521             :                              TALER_EC_GENERIC_DB_COMMIT_FAILED,
    1522             :                              NULL);
    1523           0 :       return;
    1524             :     }
    1525          15 :     trigger_payment_notification (pc);
    1526             :   }
    1527             : 
    1528             :   /* Generate response (payment successful) */
    1529             :   {
    1530             :     struct GNUNET_CRYPTO_EddsaSignature sig;
    1531             : 
    1532             :     /* Sign on our end (as the payment did go through, even if it may
    1533             :        have been refunded already) */
    1534             :     {
    1535          15 :       struct TALER_PaymentResponsePS mr = {
    1536          15 :         .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK),
    1537          15 :         .purpose.size = htonl (sizeof (mr)),
    1538             :         .h_contract_terms = pc->h_contract_terms
    1539             :       };
    1540             : 
    1541          15 :       GNUNET_CRYPTO_eddsa_sign (&pc->hc->instance->merchant_priv.eddsa_priv,
    1542             :                                 &mr,
    1543             :                                 &sig);
    1544             :     }
    1545             : 
    1546             :     /* Build the response */
    1547          15 :     resume_pay_with_response (
    1548             :       pc,
    1549             :       MHD_HTTP_OK,
    1550          15 :       TALER_MHD_MAKE_JSON_PACK (
    1551             :         GNUNET_JSON_pack_data_auto ("sig",
    1552             :                                     &sig)));
    1553             :   }
    1554             : }
    1555             : 
    1556             : 
    1557             : /**
    1558             :  * Try to parse the pay request into the given pay context.
    1559             :  * Schedules an error response in the connection on failure.
    1560             :  *
    1561             :  * @param connection HTTP connection we are receiving payment on
    1562             :  * @param[in,out] hc context with further information about the request
    1563             :  * @param pc context we use to handle the payment
    1564             :  * @return #GNUNET_OK on success,
    1565             :  *         #GNUNET_NO on failure (response was queued with MHD)
    1566             :  *         #GNUNET_SYSERR on hard error (MHD connection must be dropped)
    1567             :  */
    1568             : static enum GNUNET_GenericReturnValue
    1569          22 : parse_pay (struct MHD_Connection *connection,
    1570             :            struct TMH_HandlerContext *hc,
    1571             :            struct PayContext *pc)
    1572             : {
    1573             :   /* First, parse request */
    1574             :   {
    1575          22 :     const char *session_id = NULL;
    1576             :     json_t *coins;
    1577             :     struct GNUNET_JSON_Specification spec[] = {
    1578          22 :       GNUNET_JSON_spec_json ("coins",
    1579             :                              &coins),
    1580          22 :       GNUNET_JSON_spec_mark_optional (
    1581             :         GNUNET_JSON_spec_string ("session_id",
    1582             :                                  &session_id)),
    1583          22 :       GNUNET_JSON_spec_end ()
    1584             :     };
    1585             : 
    1586             :     {
    1587             :       enum GNUNET_GenericReturnValue res;
    1588             : 
    1589          22 :       res = TALER_MHD_parse_json_data (connection,
    1590          22 :                                        hc->request_body,
    1591             :                                        spec);
    1592          22 :       if (GNUNET_YES != res)
    1593             :       {
    1594           0 :         GNUNET_break_op (0);
    1595           0 :         return res;
    1596             :       }
    1597             :     }
    1598             : 
    1599             :     /* copy session ID (if set) */
    1600          22 :     if (NULL != session_id)
    1601             :     {
    1602          11 :       pc->session_id = GNUNET_strdup (session_id);
    1603             :     }
    1604             :     else
    1605             :     {
    1606             :       /* use empty string as default if client didn't specify it */
    1607          11 :       pc->session_id = GNUNET_strdup ("");
    1608             :     }
    1609             : 
    1610          22 :     if (! json_is_array (coins))
    1611             :     {
    1612           0 :       GNUNET_break_op (0);
    1613           0 :       GNUNET_JSON_parse_free (spec);
    1614           0 :       return (MHD_YES == TALER_MHD_reply_with_error (connection,
    1615             :                                                      MHD_HTTP_BAD_REQUEST,
    1616             :                                                      TALER_EC_GENERIC_PARAMETER_MISSING,
    1617             :                                                      "'coins' must be an array"))
    1618             :               ? GNUNET_NO
    1619           0 :               : GNUNET_SYSERR;
    1620             :     }
    1621             : 
    1622          22 :     pc->coins_cnt = json_array_size (coins);
    1623          22 :     if (pc->coins_cnt > MAX_COIN_ALLOWED_COINS)
    1624             :     {
    1625           0 :       GNUNET_break_op (0);
    1626           0 :       GNUNET_JSON_parse_free (spec);
    1627           0 :       return (MHD_YES == TALER_MHD_reply_with_error (connection,
    1628             :                                                      MHD_HTTP_BAD_REQUEST,
    1629             :                                                      TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1630             :                                                      "'coins' array too long"))
    1631             :               ? GNUNET_NO
    1632           0 :               : GNUNET_SYSERR;
    1633             :     }
    1634             : 
    1635             :     /* note: 1 coin = 1 deposit confirmation expected */
    1636          22 :     pc->dc = GNUNET_new_array (pc->coins_cnt,
    1637             :                                struct DepositConfirmation);
    1638             : 
    1639             :     /* This loop populates the array 'dc' in 'pc' */
    1640             :     {
    1641             :       unsigned int coins_index;
    1642             :       json_t *coin;
    1643          46 :       json_array_foreach (coins, coins_index, coin)
    1644             :       {
    1645          24 :         struct DepositConfirmation *dc = &pc->dc[coins_index];
    1646             :         const char *exchange_url;
    1647             :         struct GNUNET_JSON_Specification ispec[] = {
    1648          24 :           GNUNET_JSON_spec_fixed_auto ("coin_sig",
    1649             :                                        &dc->coin_sig),
    1650          24 :           GNUNET_JSON_spec_fixed_auto ("coin_pub",
    1651             :                                        &dc->coin_pub),
    1652          24 :           TALER_JSON_spec_denomination_signature ("ub_sig",
    1653             :                                                   &dc->ub_sig),
    1654          24 :           GNUNET_JSON_spec_fixed_auto ("h_denom",
    1655             :                                        &dc->h_denom),
    1656          24 :           TALER_JSON_spec_amount ("contribution",
    1657             :                                   TMH_currency,
    1658             :                                   &dc->amount_with_fee),
    1659          24 :           GNUNET_JSON_spec_string ("exchange_url",
    1660             :                                    &exchange_url),
    1661          24 :           GNUNET_JSON_spec_end ()
    1662             :         };
    1663             :         enum GNUNET_GenericReturnValue res;
    1664             : 
    1665          24 :         res = TALER_MHD_parse_json_data (connection,
    1666             :                                          coin,
    1667             :                                          ispec);
    1668          24 :         if (GNUNET_YES != res)
    1669             :         {
    1670           0 :           GNUNET_break_op (0);
    1671           0 :           GNUNET_JSON_parse_free (spec);
    1672           0 :           return res;
    1673             :         }
    1674          26 :         for (unsigned int j = 0; j<coins_index; j++)
    1675             :         {
    1676           2 :           if (0 ==
    1677           2 :               GNUNET_memcmp (&dc->coin_pub,
    1678             :                              &pc->dc[j].coin_pub))
    1679             :           {
    1680           0 :             GNUNET_break_op (0);
    1681             :             return (MHD_YES ==
    1682           0 :                     TALER_MHD_reply_with_error (connection,
    1683             :                                                 MHD_HTTP_BAD_REQUEST,
    1684             :                                                 TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1685             :                                                 "duplicate coin in list"))
    1686             :              ? GNUNET_NO
    1687           0 :              : GNUNET_SYSERR;
    1688             :           }
    1689             :         }
    1690          24 :         dc->exchange_url = GNUNET_strdup (exchange_url);
    1691          24 :         dc->index = coins_index;
    1692          24 :         dc->pc = pc;
    1693             : 
    1694          24 :         if (0 !=
    1695          24 :             strcasecmp (dc->amount_with_fee.currency,
    1696             :                         TMH_currency))
    1697             :         {
    1698           0 :           GNUNET_break_op (0);
    1699           0 :           GNUNET_JSON_parse_free (spec);
    1700           0 :           return TALER_MHD_reply_with_error (connection,
    1701             :                                              MHD_HTTP_CONFLICT,
    1702             :                                              TALER_EC_GENERIC_CURRENCY_MISMATCH,
    1703             :                                              TMH_currency);
    1704             :         }
    1705             :       }
    1706             :     }
    1707          22 :     GNUNET_JSON_parse_free (spec);
    1708             :   }
    1709             : 
    1710             :   /* obtain contract terms */
    1711             :   {
    1712             :     enum GNUNET_DB_QueryStatus qs;
    1713          22 :     json_t *contract_terms = NULL;
    1714             : 
    1715          22 :     qs = TMH_db->lookup_contract_terms (TMH_db->cls,
    1716          22 :                                         hc->instance->settings.id,
    1717             :                                         pc->order_id,
    1718             :                                         &contract_terms,
    1719             :                                         &pc->order_serial,
    1720             :                                         NULL);
    1721          22 :     if (0 > qs)
    1722             :     {
    1723             :       /* single, read-only SQL statements should never cause
    1724             :          serialization problems */
    1725           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
    1726             :       /* Always report on hard error to enable diagnostics */
    1727           0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    1728             :       return (MHD_YES ==
    1729           0 :               TALER_MHD_reply_with_error (connection,
    1730             :                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    1731             :                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    1732             :                                           "contract terms"))
    1733             :              ? GNUNET_NO
    1734           0 :              : GNUNET_SYSERR;
    1735             :     }
    1736          22 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1737             :     {
    1738             :       return (MHD_YES ==
    1739           0 :               TALER_MHD_reply_with_error (connection,
    1740             :                                           MHD_HTTP_NOT_FOUND,
    1741             :                                           TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
    1742             :                                           pc->order_id))
    1743             :              ? GNUNET_NO
    1744           0 :              : GNUNET_SYSERR;
    1745             :     }
    1746             : 
    1747             :     /* hash contract (needed later) */
    1748          22 :     if (GNUNET_OK !=
    1749          22 :         TALER_JSON_contract_hash (contract_terms,
    1750             :                                   &pc->h_contract_terms))
    1751             :     {
    1752           0 :       GNUNET_break (0);
    1753           0 :       json_decref (contract_terms);
    1754             :       return (MHD_YES ==
    1755           0 :               TALER_MHD_reply_with_error (connection,
    1756             :                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    1757             :                                           TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
    1758             :                                           NULL))
    1759             :              ? GNUNET_NO
    1760           0 :              : GNUNET_SYSERR;
    1761             :     }
    1762          22 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1763             :                 "Handling payment for order `%s' with contract hash `%s'\n",
    1764             :                 pc->order_id,
    1765             :                 GNUNET_h2s (&pc->h_contract_terms));
    1766             : 
    1767             :     /* basic sanity check on the contract */
    1768          22 :     if (NULL == json_object_get (contract_terms,
    1769             :                                  "merchant"))
    1770             :     {
    1771             :       /* invalid contract */
    1772           0 :       GNUNET_break (0);
    1773           0 :       json_decref (contract_terms);
    1774             :       return (MHD_YES ==
    1775           0 :               TALER_MHD_reply_with_error (connection,
    1776             :                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    1777             :                                           TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_MERCHANT_FIELD_MISSING,
    1778             :                                           NULL))
    1779             :              ? GNUNET_NO
    1780           0 :              : GNUNET_SYSERR;
    1781             :     }
    1782             : 
    1783             :     /* Get details from contract and check fundamentals */
    1784             :     {
    1785          22 :       const char *fulfillment_url = NULL;
    1786             :       struct GNUNET_JSON_Specification espec[] = {
    1787          22 :         TALER_JSON_spec_amount ("amount",
    1788             :                                 TMH_currency,
    1789             :                                 &pc->amount),
    1790          22 :         GNUNET_JSON_spec_mark_optional (
    1791             :           GNUNET_JSON_spec_string ("fulfillment_url",
    1792             :                                    &fulfillment_url)),
    1793          22 :         TALER_JSON_spec_amount ("max_fee",
    1794             :                                 TMH_currency,
    1795             :                                 &pc->max_fee),
    1796          22 :         TALER_JSON_spec_amount ("max_wire_fee",
    1797             :                                 TMH_currency,
    1798             :                                 &pc->max_wire_fee),
    1799          22 :         GNUNET_JSON_spec_uint32 ("wire_fee_amortization",
    1800             :                                  &pc->wire_fee_amortization),
    1801          22 :         TALER_JSON_spec_absolute_time ("timestamp",
    1802             :                                        &pc->timestamp),
    1803          22 :         TALER_JSON_spec_absolute_time ("refund_deadline",
    1804             :                                        &pc->refund_deadline),
    1805          22 :         TALER_JSON_spec_absolute_time ("pay_deadline",
    1806             :                                        &pc->pay_deadline),
    1807          22 :         TALER_JSON_spec_absolute_time ("wire_transfer_deadline",
    1808             :                                        &pc->wire_transfer_deadline),
    1809          22 :         GNUNET_JSON_spec_fixed_auto ("h_wire",
    1810             :                                      &pc->h_wire),
    1811          22 :         GNUNET_JSON_spec_end ()
    1812             :       };
    1813             :       enum GNUNET_GenericReturnValue res;
    1814             : 
    1815          22 :       res = TALER_MHD_parse_internal_json_data (connection,
    1816             :                                                 contract_terms,
    1817             :                                                 espec);
    1818          22 :       if (NULL != fulfillment_url)
    1819          21 :         pc->fulfillment_url = GNUNET_strdup (fulfillment_url);
    1820          22 :       json_decref (contract_terms);
    1821          22 :       if (GNUNET_YES != res)
    1822             :       {
    1823           0 :         GNUNET_break (0);
    1824           0 :         return res;
    1825             :       }
    1826             :     }
    1827             : 
    1828          22 :     if (pc->wire_transfer_deadline.abs_value_us <
    1829          22 :         pc->refund_deadline.abs_value_us)
    1830             :     {
    1831             :       /* This should already have been checked when creating the order! */
    1832           0 :       GNUNET_break (0);
    1833           0 :       return TALER_MHD_reply_with_error (connection,
    1834             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    1835             :                                          TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE,
    1836             :                                          NULL);
    1837             :     }
    1838             : 
    1839          22 :     if (GNUNET_TIME_absolute_is_past (pc->pay_deadline))
    1840             :     {
    1841             :       /* too late */
    1842             :       return (MHD_YES ==
    1843           0 :               TALER_MHD_reply_with_error (connection,
    1844             :                                           MHD_HTTP_GONE,
    1845             :                                           TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED,
    1846             :                                           NULL))
    1847             :              ? GNUNET_NO
    1848           0 :              : GNUNET_SYSERR;
    1849             :     }
    1850             :   }
    1851             : 
    1852             :   /* Make sure wire method (still) exists for this instance */
    1853             :   {
    1854             :     struct TMH_WireMethod *wm;
    1855             : 
    1856          22 :     wm = hc->instance->wm_head;
    1857          22 :     while (0 != GNUNET_memcmp (&pc->h_wire,
    1858             :                                &wm->h_wire))
    1859           0 :       wm = wm->next;
    1860          22 :     if (NULL == wm)
    1861             :     {
    1862           0 :       GNUNET_break (0);
    1863           0 :       return TALER_MHD_reply_with_error (connection,
    1864             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    1865             :                                          TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_HASH_UNKNOWN,
    1866             :                                          NULL);
    1867             :     }
    1868          22 :     pc->wm = wm;
    1869             :   }
    1870             : 
    1871          22 :   return GNUNET_OK;
    1872             : }
    1873             : 
    1874             : 
    1875             : /**
    1876             :  * Handle a timeout for the processing of the pay request.
    1877             :  *
    1878             :  * @param cls our `struct PayContext`
    1879             :  */
    1880             : static void
    1881           0 : handle_pay_timeout (void *cls)
    1882             : {
    1883           0 :   struct PayContext *pc = cls;
    1884             : 
    1885           0 :   pc->timeout_task = NULL;
    1886           0 :   GNUNET_assert (GNUNET_YES == pc->suspended);
    1887           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1888             :               "Resuming pay with error after timeout\n");
    1889           0 :   if (NULL != pc->fo)
    1890             :   {
    1891           0 :     TMH_EXCHANGES_find_exchange_cancel (pc->fo);
    1892           0 :     pc->fo = NULL;
    1893             :   }
    1894           0 :   resume_pay_with_error (pc,
    1895             :                          MHD_HTTP_GATEWAY_TIMEOUT,
    1896             :                          TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
    1897             :                          NULL);
    1898           0 : }
    1899             : 
    1900             : 
    1901             : MHD_RESULT
    1902          44 : TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
    1903             :                         struct MHD_Connection *connection,
    1904             :                         struct TMH_HandlerContext *hc)
    1905             : {
    1906          44 :   struct PayContext *pc = hc->ctx;
    1907             : 
    1908          44 :   GNUNET_assert (NULL != hc->infix);
    1909             : 
    1910          44 :   if (NULL == pc)
    1911             :   {
    1912          22 :     pc = GNUNET_new (struct PayContext);
    1913          22 :     GNUNET_CONTAINER_DLL_insert (pc_head,
    1914             :                                  pc_tail,
    1915             :                                  pc);
    1916          22 :     pc->connection = connection;
    1917          22 :     pc->hc = hc;
    1918          22 :     pc->order_id = hc->infix;
    1919          22 :     hc->ctx = pc;
    1920          22 :     hc->cc = &pay_context_cleanup;
    1921             :   }
    1922          44 :   if (GNUNET_SYSERR == pc->suspended)
    1923           0 :     return MHD_NO; /* during shutdown, we don't generate any more replies */
    1924          44 :   GNUNET_assert (GNUNET_NO == pc->suspended);
    1925          44 :   if (0 != pc->response_code)
    1926             :   {
    1927             :     MHD_RESULT res;
    1928             : 
    1929             :     /* We are *done* processing the request, just queue the response (!) */
    1930          22 :     if (UINT_MAX == pc->response_code)
    1931             :     {
    1932           0 :       GNUNET_break (0);
    1933           0 :       return MHD_NO; /* hard error */
    1934             :     }
    1935          22 :     res = MHD_queue_response (connection,
    1936             :                               pc->response_code,
    1937             :                               pc->response);
    1938          22 :     MHD_destroy_response (pc->response);
    1939          22 :     pc->response = NULL;
    1940          22 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1941             :                 "Queueing response (%u) for POST /orders/$ID/pay (%s).\n",
    1942             :                 (unsigned int) pc->response_code,
    1943             :                 res ? "OK" : "FAILED");
    1944          22 :     return res;
    1945             :   }
    1946             :   {
    1947             :     enum GNUNET_GenericReturnValue ret;
    1948             : 
    1949          22 :     ret = parse_pay (connection,
    1950             :                      hc,
    1951             :                      pc);
    1952          22 :     if (GNUNET_OK != ret)
    1953             :       return (GNUNET_NO == ret)
    1954             :              ? MHD_YES
    1955           0 :              : MHD_NO;
    1956             :   }
    1957             : 
    1958             :   /* Payment not finished, suspend while we interact with the exchange */
    1959          22 :   MHD_suspend_connection (connection);
    1960          22 :   pc->suspended = GNUNET_YES;
    1961          22 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1962             :               "Suspending pay handling while working with the exchange\n");
    1963          22 :   GNUNET_assert (NULL == pc->timeout_task);
    1964          22 :   pc->timeout_task =
    1965          22 :     GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->coins_cnt),
    1966             :                                   &handle_pay_timeout,
    1967             :                                   pc);
    1968          22 :   execute_pay_transaction (pc);
    1969          22 :   return MHD_YES;
    1970             : }
    1971             : 
    1972             : 
    1973             : /* end of taler-merchant-httpd_post-orders-ID-pay.c */

Generated by: LCOV version 1.14