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: 0 859 0.0 %
Date: 2022-08-25 06:17:04 Functions: 0 27 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    This file is part of TALER
       3             :    (C) 2014-2022 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             :  * How often do we ask the exchange again about our
      51             :  * KYC status? Very rarely, as if the user actively
      52             :  * changes it, we should usually notice anyway.
      53             :  */
      54             : #define KYC_RETRY_FREQUENCY GNUNET_TIME_UNIT_WEEKS
      55             : 
      56             : /**
      57             :  * Information we keep for an individual call to the pay handler.
      58             :  */
      59             : struct PayContext;
      60             : 
      61             : 
      62             : /**
      63             :  * Information kept during a pay request for each coin.
      64             :  */
      65             : struct DepositConfirmation
      66             : {
      67             : 
      68             :   /**
      69             :    * Reference to the main PayContext
      70             :    */
      71             :   struct PayContext *pc;
      72             : 
      73             :   /**
      74             :    * Handle to the deposit operation we are performing for
      75             :    * this coin, NULL after the operation is done.
      76             :    */
      77             :   struct TALER_EXCHANGE_DepositHandle *dh;
      78             : 
      79             :   /**
      80             :    * URL of the exchange that issued this coin.
      81             :    */
      82             :   char *exchange_url;
      83             : 
      84             :   /**
      85             :    * Details about the coin being deposited.
      86             :    */
      87             :   struct TALER_EXCHANGE_CoinDepositDetail cdd;
      88             : 
      89             :   /**
      90             :    * Fee charged by the exchange for the deposit operation of this coin.
      91             :    */
      92             :   struct TALER_Amount deposit_fee;
      93             : 
      94             :   /**
      95             :    * Fee charged by the exchange for the refund operation of this coin.
      96             :    */
      97             :   struct TALER_Amount refund_fee;
      98             : 
      99             :   /**
     100             :    * Wire fee charged by the exchange of this coin.
     101             :    */
     102             :   struct TALER_Amount wire_fee;
     103             : 
     104             :   /**
     105             :    * If a minimum age was required (i. e. pc->minimum_age is large enough),
     106             :    * this is the signature of the minimum age (as a single uint8_t), using the
     107             :    * private key to the corresponding age group.  Might be all zeroes for no
     108             :    * age attestation.
     109             :    */
     110             :   struct TALER_AgeAttestation minimum_age_sig;
     111             : 
     112             :   /**
     113             :    * If a minimum age was required (i. e. pc->minimum_age is large enough),
     114             :    * this is the age commitment (i. e. age mask and vector of EdDSA public
     115             :    * keys, one per age group) that went into the mining of the coin.  The
     116             :    * SHA256 hash of the mask and the vector of public keys was bound to the
     117             :    * key.
     118             :    */
     119             :   struct TALER_AgeCommitment age_commitment;
     120             : 
     121             :   /**
     122             :    * Age mask in the denomination that defines the age groups.  Only
     123             :    * applicable, if minimum age was required.
     124             :    */
     125             :   struct TALER_AgeMask age_mask;
     126             : 
     127             :   /**
     128             :    * Offset of this coin into the `dc` array of all coins in the
     129             :    * @e pc.
     130             :    */
     131             :   unsigned int index;
     132             : 
     133             :   /**
     134             :    * true, if no field "age_commitment" was found in the JSON blob
     135             :    */
     136             :   bool no_age_commitment;
     137             : 
     138             :   /**
     139             :    * True, if no field "minimum_age_sig" was found in the JSON blob
     140             :    */
     141             :   bool no_minimum_age_sig;
     142             : 
     143             :   /**
     144             :    * true, if no field "h_age_commitment" was found in the JSON blob
     145             :    */
     146             :   bool no_h_age_commitment;
     147             : 
     148             :   /**
     149             :    * true if we found this coin in the database.
     150             :    */
     151             :   bool found_in_db;
     152             : 
     153             :   /**
     154             :    * true if we #deposit_paid_check() matched this coin in the database.
     155             :    */
     156             :   bool matched_in_db;
     157             : 
     158             : };
     159             : 
     160             : 
     161             : /**
     162             :  * Information kept during a pay request for each exchange.
     163             :  */
     164             : struct ExchangeGroup
     165             : {
     166             : 
     167             :   /**
     168             :    * Payment context this group is part of.
     169             :    */
     170             :   struct PayContext *pc;
     171             : 
     172             :   /**
     173             :    * Handle to the batch deposit operation we are performing for this
     174             :    * exchange, NULL after the operation is done.
     175             :    */
     176             :   struct TALER_EXCHANGE_BatchDepositHandle *bdh;
     177             : 
     178             :   /**
     179             :    * Handle for operation to lookup /keys (and auditors) from
     180             :    * the exchange used for this transaction; NULL if no operation is
     181             :    * pending.
     182             :    */
     183             :   struct TMH_EXCHANGES_FindOperation *fo;
     184             : 
     185             :   /**
     186             :    * URL of the exchange that issued this coin. Aliases
     187             :    * the exchange URL of one of the coins, do not free!
     188             :    */
     189             :   const char *exchange_url;
     190             : 
     191             :   /**
     192             :    * true if we already tried a forced /keys download.
     193             :    */
     194             :   bool tried_force_keys;
     195             : };
     196             : 
     197             : 
     198             : /**
     199             :  * Information we keep for an individual call to the /pay handler.
     200             :  */
     201             : struct PayContext
     202             : {
     203             : 
     204             :   /**
     205             :    * Stored in a DLL.
     206             :    */
     207             :   struct PayContext *next;
     208             : 
     209             :   /**
     210             :    * Stored in a DLL.
     211             :    */
     212             :   struct PayContext *prev;
     213             : 
     214             :   /**
     215             :    * Array with @e num_exchange exchanges we are depositing
     216             :    * coins into.
     217             :    */
     218             :   struct ExchangeGroup **egs;
     219             : 
     220             :   /**
     221             :    * Array with @e coins_cnt coins we are despositing.
     222             :    */
     223             :   struct DepositConfirmation *dc;
     224             : 
     225             :   /**
     226             :    * MHD connection to return to
     227             :    */
     228             :   struct MHD_Connection *connection;
     229             : 
     230             :   /**
     231             :    * Details about the client's request.
     232             :    */
     233             :   struct TMH_HandlerContext *hc;
     234             : 
     235             :   /**
     236             :    * What wire method (of the @e mi) was selected by the wallet?
     237             :    * Set in #parse_pay().
     238             :    */
     239             :   struct TMH_WireMethod *wm;
     240             : 
     241             :   /**
     242             :    * Task called when the (suspended) processing for
     243             :    * the /pay request times out.
     244             :    * Happens when we don't get a response from the exchange.
     245             :    */
     246             :   struct GNUNET_SCHEDULER_Task *timeout_task;
     247             : 
     248             :   /**
     249             :    * Response to return, NULL if we don't have one yet.
     250             :    */
     251             :   struct MHD_Response *response;
     252             : 
     253             :   /**
     254             :    * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state.
     255             :    */
     256             :   void *json_parse_context;
     257             : 
     258             :   /**
     259             :    * Optional session id given in @e root.
     260             :    * NULL if not given.
     261             :    */
     262             :   char *session_id;
     263             : 
     264             :   /**
     265             :    * Transaction ID given in @e root.
     266             :    */
     267             :   const char *order_id;
     268             : 
     269             :   /**
     270             :    * Fulfillment URL from the contract, or NULL if we don't have one.
     271             :    */
     272             :   char *fulfillment_url;
     273             : 
     274             :   /**
     275             :    * Serial number of this order in the database (set once we did the lookup).
     276             :    */
     277             :   uint64_t order_serial;
     278             : 
     279             :   /**
     280             :    * Hashed proposal.
     281             :    */
     282             :   struct TALER_PrivateContractHashP h_contract_terms;
     283             : 
     284             :   /**
     285             :    * "h_wire" from @e contract_terms.  Used to identify
     286             :    * the instance's wire transfer method.
     287             :    */
     288             :   struct TALER_MerchantWireHashP h_wire;
     289             : 
     290             :   /**
     291             :    * Maximum fee the merchant is willing to pay, from @e root.
     292             :    * Note that IF the total fee of the exchange is higher, that is
     293             :    * acceptable to the merchant if the customer is willing to
     294             :    * pay the difference
     295             :    * (i.e. amount - max_fee <= actual-amount - actual-fee).
     296             :    */
     297             :   struct TALER_Amount max_fee;
     298             : 
     299             :   /**
     300             :    * Maximum wire fee the merchant is willing to pay, from @e root.
     301             :    * Note that IF the total fee of the exchange is higher, that is
     302             :    * acceptable to the merchant if the customer is willing to
     303             :    * pay the amorized difference.  Wire fees are charged over an
     304             :    * aggregate of several translations, hence unlike the deposit
     305             :    * fees, they are amortized over several customer's transactions.
     306             :    * The contract specifies under @e wire_fee_amortization how many
     307             :    * customer's transactions he expects the wire fees to be amortized
     308             :    * over on average.  Thus, if the wire fees are larger than
     309             :    * @e max_wire_fee, each customer is expected to contribute
     310             :    * $\frac{actual-wire-fee - max_wire_fee}{wire_fee_amortization}$.
     311             :    * The customer's contribution may be further reduced by the
     312             :    * difference between @e max_fee and the sum of the deposit fees.
     313             :    *
     314             :    * Default is that the merchant is unwilling to pay any wire fees.
     315             :    */
     316             :   struct TALER_Amount max_wire_fee;
     317             : 
     318             :   /**
     319             :    * Amount from @e root.  This is the amount the merchant expects
     320             :    * to make, minus @e max_fee.
     321             :    */
     322             :   struct TALER_Amount amount;
     323             : 
     324             :   /**
     325             :    * Considering all the coins with the "found_in_db" flag
     326             :    * set, what is the total amount we were so far paid on
     327             :    * this contract?
     328             :    */
     329             :   struct TALER_Amount total_paid;
     330             : 
     331             :   /**
     332             :    * Considering all the coins with the "found_in_db" flag
     333             :    * set, what is the total amount we had to pay in deposit
     334             :    * fees so far on this contract?
     335             :    */
     336             :   struct TALER_Amount total_fees_paid;
     337             : 
     338             :   /**
     339             :    * Considering all the coins with the "found_in_db" flag
     340             :    * set, what is the total amount we already refunded?
     341             :    */
     342             :   struct TALER_Amount total_refunded;
     343             : 
     344             :   /**
     345             :    * Wire transfer deadline. How soon would the merchant like the
     346             :    * wire transfer to be executed?
     347             :    */
     348             :   struct GNUNET_TIME_Timestamp wire_transfer_deadline;
     349             : 
     350             :   /**
     351             :    * Timestamp from @e contract_terms.
     352             :    */
     353             :   struct GNUNET_TIME_Timestamp timestamp;
     354             : 
     355             :   /**
     356             :    * Refund deadline from @e contract_terms.
     357             :    */
     358             :   struct GNUNET_TIME_Timestamp refund_deadline;
     359             : 
     360             :   /**
     361             :    * Deadline for the customer to pay for this proposal.
     362             :    */
     363             :   struct GNUNET_TIME_Timestamp pay_deadline;
     364             : 
     365             :   /**
     366             :    * Number of transactions that the wire fees are expected to be
     367             :    * amortized over.  Never zero, defaults (conservateively) to 1.
     368             :    * May be higher if merchants expect many small transactions to
     369             :    * be aggregated and thus wire fees to be reasonably amortized
     370             :    * due to aggregation.
     371             :    */
     372             :   uint32_t wire_fee_amortization;
     373             : 
     374             :   /**
     375             :    * Minimum age required for this purchase.
     376             :    */
     377             :   unsigned int minimum_age;
     378             : 
     379             :   /**
     380             :    * Number of coins this payment is made of.  Length
     381             :    * of the @e dc array.
     382             :    */
     383             :   unsigned int coins_cnt;
     384             : 
     385             :   /**
     386             :    * Number of exchanges involved in the payment. Length
     387             :    * of the @e eg array.
     388             :    */
     389             :   unsigned int num_exchanges;
     390             : 
     391             :   /**
     392             :    * How often have we retried the 'main' transaction?
     393             :    */
     394             :   unsigned int retry_counter;
     395             : 
     396             :   /**
     397             :    * Number of batch transactions pending.
     398             :    */
     399             :   unsigned int pending_at_eg;
     400             : 
     401             :   /**
     402             :    * Number of coin deposits pending.
     403             :    */
     404             :   unsigned int pending;
     405             : 
     406             :   /**
     407             :    * HTTP status code to use for the reply, i.e 200 for "OK".
     408             :    * Special value UINT_MAX is used to indicate hard errors
     409             :    * (no reply, return #MHD_NO).
     410             :    */
     411             :   unsigned int response_code;
     412             : 
     413             :   /**
     414             :    * #GNUNET_NO if the @e connection was not suspended,
     415             :    * #GNUNET_YES if the @e connection was suspended,
     416             :    * #GNUNET_SYSERR if @e connection was resumed to as
     417             :    * part of #MH_force_pc_resume during shutdown.
     418             :    */
     419             :   enum GNUNET_GenericReturnValue suspended;
     420             : 
     421             : };
     422             : 
     423             : 
     424             : /**
     425             :  * Active KYC operation with an exchange.
     426             :  */
     427             : struct KycContext
     428             : {
     429             :   /**
     430             :    * Kept in a DLL.
     431             :    */
     432             :   struct KycContext *next;
     433             : 
     434             :   /**
     435             :    * Kept in a DLL.
     436             :    */
     437             :   struct KycContext *prev;
     438             : 
     439             :   /**
     440             :    * Looking for the exchange.
     441             :    */
     442             :   struct TMH_EXCHANGES_FindOperation *fo;
     443             : 
     444             :   /**
     445             :    * Exchange this is about.
     446             :    */
     447             :   char *exchange_url;
     448             : 
     449             :   /**
     450             :    * Merchant instance this is for.
     451             :    */
     452             :   struct TMH_MerchantInstance *mi;
     453             : 
     454             :   /**
     455             :    * Wire method we are checking the status of.
     456             :    */
     457             :   struct TMH_WireMethod *wm;
     458             : 
     459             :   /**
     460             :    * Handle for the GET /deposits operation.
     461             :    */
     462             :   struct TALER_EXCHANGE_DepositGetHandle *dg;
     463             : 
     464             :   /**
     465             :    * Contract we are looking up.
     466             :    */
     467             :   struct TALER_PrivateContractHashP h_contract_terms;
     468             : 
     469             :   /**
     470             :    * Coin we are looking up.
     471             :    */
     472             :   struct TALER_CoinSpendPublicKeyP coin_pub;
     473             : 
     474             :   /**
     475             :    * Initial DB timestamp.
     476             :    */
     477             :   struct GNUNET_TIME_Timestamp kyc_timestamp;
     478             : 
     479             :   /**
     480             :    * Initial KYC status.
     481             :    */
     482             :   bool kyc_ok;
     483             : 
     484             : };
     485             : 
     486             : 
     487             : /**
     488             :  * Head of active pay context DLL.
     489             :  */
     490             : static struct PayContext *pc_head;
     491             : 
     492             : /**
     493             :  * Tail of active pay context DLL.
     494             :  */
     495             : static struct PayContext *pc_tail;
     496             : 
     497             : /**
     498             :  * Head of active KYC context DLL.
     499             :  */
     500             : static struct KycContext *kc_head;
     501             : 
     502             : /**
     503             :  * Tail of active KYC context DLL.
     504             :  */
     505             : static struct KycContext *kc_tail;
     506             : 
     507             : 
     508             : /**
     509             :  * Free resources used by @a kc.
     510             :  *
     511             :  * @param[in] kc object to free
     512             :  */
     513             : static void
     514           0 : destroy_kc (struct KycContext *kc)
     515             : {
     516           0 :   if (NULL != kc->fo)
     517             :   {
     518           0 :     TMH_EXCHANGES_find_exchange_cancel (kc->fo);
     519           0 :     kc->fo = NULL;
     520             :   }
     521           0 :   if (NULL != kc->dg)
     522             :   {
     523           0 :     TALER_EXCHANGE_deposits_get_cancel (kc->dg);
     524           0 :     kc->dg = NULL;
     525             :   }
     526           0 :   TMH_instance_decref (kc->mi);
     527           0 :   kc->mi = NULL;
     528           0 :   GNUNET_free (kc->exchange_url);
     529           0 :   GNUNET_CONTAINER_DLL_remove (kc_head,
     530             :                                kc_tail,
     531             :                                kc);
     532           0 :   GNUNET_free (kc);
     533           0 : }
     534             : 
     535             : 
     536             : /**
     537             :  * Compute the timeout for a /pay request based on the number of coins
     538             :  * involved.
     539             :  *
     540             :  * @param num_coins number of coins
     541             :  * @returns timeout for the /pay request
     542             :  */
     543             : static struct GNUNET_TIME_Relative
     544           0 : get_pay_timeout (unsigned int num_coins)
     545             : {
     546             :   struct GNUNET_TIME_Relative t;
     547             : 
     548             :   /* FIXME:  Do some benchmarking to come up with a better timeout.
     549             :    * We've increased this value so the wallet integration test passes again
     550             :    * on my (Florian) machine.
     551             :    */
     552           0 :   t = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
     553           0 :                                      15 * (1 + (num_coins / 5)));
     554             : 
     555           0 :   return t;
     556             : }
     557             : 
     558             : 
     559             : /**
     560             :  * Abort all pending /deposit operations.
     561             :  *
     562             :  * @param pc pay context to abort
     563             :  */
     564             : static void
     565           0 : abort_active_deposits (struct PayContext *pc)
     566             : {
     567           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     568             :               "Aborting pending /deposit operations\n");
     569           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
     570             :   {
     571           0 :     struct DepositConfirmation *dci = &pc->dc[i];
     572             : 
     573           0 :     if (NULL != dci->dh)
     574             :     {
     575           0 :       TALER_EXCHANGE_deposit_cancel (dci->dh);
     576           0 :       dci->dh = NULL;
     577             :     }
     578             :   }
     579           0 : }
     580             : 
     581             : 
     582             : void
     583           0 : TMH_force_pc_resume ()
     584             : {
     585             :   struct KycContext *kc;
     586             : 
     587           0 :   while (NULL != (kc = kc_head))
     588             :   {
     589           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     590             :                 "Aborting KYC check at %s\n",
     591             :                 kc->exchange_url);
     592           0 :     destroy_kc (kc);
     593             :   }
     594           0 :   for (struct PayContext *pc = pc_head;
     595             :        NULL != pc;
     596           0 :        pc = pc->next)
     597             :   {
     598           0 :     abort_active_deposits (pc);
     599           0 :     if (NULL != pc->timeout_task)
     600             :     {
     601           0 :       GNUNET_SCHEDULER_cancel (pc->timeout_task);
     602           0 :       pc->timeout_task = NULL;
     603             :     }
     604           0 :     if (GNUNET_YES == pc->suspended)
     605             :     {
     606           0 :       pc->suspended = GNUNET_SYSERR;
     607           0 :       MHD_resume_connection (pc->connection);
     608             :     }
     609             :   }
     610           0 : }
     611             : 
     612             : 
     613             : /**
     614             :  * Resume the given pay context and send the given response.
     615             :  * Stores the response in the @a pc and signals MHD to resume
     616             :  * the connection.  Also ensures MHD runs immediately.
     617             :  *
     618             :  * @param pc payment context
     619             :  * @param response_code response code to use
     620             :  * @param response response data to send back
     621             :  */
     622             : static void
     623           0 : resume_pay_with_response (struct PayContext *pc,
     624             :                           unsigned int response_code,
     625             :                           struct MHD_Response *response)
     626             : {
     627           0 :   abort_active_deposits (pc);
     628           0 :   pc->response_code = response_code;
     629           0 :   pc->response = response;
     630           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     631             :               "Resuming /pay handling. HTTP status for our reply is %u.\n",
     632             :               response_code);
     633           0 :   if (NULL != pc->timeout_task)
     634             :   {
     635           0 :     GNUNET_SCHEDULER_cancel (pc->timeout_task);
     636           0 :     pc->timeout_task = NULL;
     637             :   }
     638           0 :   GNUNET_assert (GNUNET_YES == pc->suspended);
     639           0 :   pc->suspended = GNUNET_NO;
     640           0 :   MHD_resume_connection (pc->connection);
     641           0 :   TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
     642           0 : }
     643             : 
     644             : 
     645             : /**
     646             :  * Resume payment processing with an error.
     647             :  *
     648             :  * @param pc operation to resume
     649             :  * @param http_status http status code to return
     650             :  * @param ec taler error code to return
     651             :  * @param msg human readable error message
     652             :  */
     653             : static void
     654           0 : resume_pay_with_error (struct PayContext *pc,
     655             :                        unsigned int http_status,
     656             :                        enum TALER_ErrorCode ec,
     657             :                        const char *msg)
     658             : {
     659           0 :   resume_pay_with_response (pc,
     660             :                             http_status,
     661             :                             TALER_MHD_make_error (ec,
     662             :                                                   msg));
     663           0 : }
     664             : 
     665             : 
     666             : /**
     667             :  * Custom cleanup routine for a `struct PayContext`.
     668             :  *
     669             :  * @param cls the `struct PayContext` to clean up.
     670             :  */
     671             : static void
     672           0 : pay_context_cleanup (void *cls)
     673             : {
     674           0 :   struct PayContext *pc = cls;
     675             : 
     676           0 :   if (NULL != pc->timeout_task)
     677             :   {
     678           0 :     GNUNET_SCHEDULER_cancel (pc->timeout_task);
     679           0 :     pc->timeout_task = NULL;
     680             :   }
     681           0 :   abort_active_deposits (pc);
     682           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
     683             :   {
     684           0 :     struct DepositConfirmation *dc = &pc->dc[i];
     685             : 
     686           0 :     TALER_denom_sig_free (&dc->cdd.denom_sig);
     687           0 :     GNUNET_free (dc->exchange_url);
     688             :   }
     689           0 :   GNUNET_free (pc->dc);
     690           0 :   for (unsigned int i = 0; i<pc->num_exchanges; i++)
     691             :   {
     692           0 :     struct ExchangeGroup *eg = pc->egs[i];
     693             : 
     694           0 :     if (NULL != eg->fo)
     695           0 :       TMH_EXCHANGES_find_exchange_cancel (eg->fo);
     696           0 :     GNUNET_free (eg);
     697             :   }
     698           0 :   GNUNET_free (pc->egs);
     699           0 :   if (NULL != pc->response)
     700             :   {
     701           0 :     MHD_destroy_response (pc->response);
     702           0 :     pc->response = NULL;
     703             :   }
     704           0 :   GNUNET_free (pc->fulfillment_url);
     705           0 :   GNUNET_free (pc->session_id);
     706           0 :   GNUNET_CONTAINER_DLL_remove (pc_head,
     707             :                                pc_tail,
     708             :                                pc);
     709           0 :   GNUNET_free (pc);
     710           0 : }
     711             : 
     712             : 
     713             : /**
     714             :  * Execute the DB transaction.  If required (from
     715             :  * soft/serialization errors), the transaction can be
     716             :  * restarted here.
     717             :  *
     718             :  * @param pc payment context to transact
     719             :  */
     720             : static void
     721             : execute_pay_transaction (struct PayContext *pc);
     722             : 
     723             : 
     724             : /**
     725             :  * Function called with detailed wire transfer data.
     726             :  *
     727             :  * @param cls a `struct KycContext *`
     728             :  * @param dr HTTP response data
     729             :  */
     730             : static void
     731           0 : deposit_get_callback (
     732             :   void *cls,
     733             :   const struct TALER_EXCHANGE_GetDepositResponse *dr)
     734             : {
     735           0 :   struct KycContext *kc = cls;
     736             :   enum GNUNET_DB_QueryStatus qs;
     737             :   struct GNUNET_TIME_Timestamp now;
     738             : 
     739           0 :   kc->dg = NULL;
     740           0 :   now = GNUNET_TIME_timestamp_get ();
     741           0 :   switch (dr->hr.http_status)
     742             :   {
     743           0 :   case MHD_HTTP_OK:
     744           0 :     qs = TMH_db->account_kyc_set_status (
     745           0 :       TMH_db->cls,
     746           0 :       kc->mi->settings.id,
     747           0 :       &kc->wm->h_wire,
     748           0 :       kc->exchange_url,
     749             :       0LL,
     750             :       NULL, /* no signature */
     751             :       NULL, /* no signature */
     752             :       now,
     753             :       true);
     754           0 :     GNUNET_break (qs > 0);
     755           0 :     break;
     756           0 :   case MHD_HTTP_ACCEPTED:
     757           0 :     qs = TMH_db->account_kyc_set_status (
     758           0 :       TMH_db->cls,
     759           0 :       kc->mi->settings.id,
     760           0 :       &kc->wm->h_wire,
     761           0 :       kc->exchange_url,
     762             :       dr->details.accepted.requirement_row,
     763             :       NULL, /* no signature */
     764             :       NULL, /* no signature */
     765             :       now,
     766           0 :       dr->details.accepted.kyc_ok);
     767           0 :     GNUNET_break (qs > 0);
     768           0 :     break;
     769           0 :   default:
     770           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     771             :                 "KYC check failed at %s with unexpected status %u\n",
     772             :                 kc->exchange_url,
     773             :                 dr->hr.http_status);
     774             :   }
     775           0 :   destroy_kc (kc);
     776           0 : }
     777             : 
     778             : 
     779             : /**
     780             :  * Function called with the result of our exchange lookup.
     781             :  *
     782             :  * @param cls the `struct KycContext`
     783             :  * @param hr HTTP response details
     784             :  * @param exchange_handle NULL if exchange was not found to be acceptable
     785             :  * @param payto_uri payto://-URI of the exchange
     786             :  * @param wire_fee current applicable fee for dealing with @a exchange_handle,
     787             :  *        NULL if not available
     788             :  * @param exchange_trusted true if this exchange is
     789             :  *        trusted by config
     790             :  */
     791             : static void
     792           0 : process_kyc_with_exchange (
     793             :   void *cls,
     794             :   const struct TALER_EXCHANGE_HttpResponse *hr,
     795             :   struct TALER_EXCHANGE_Handle *exchange_handle,
     796             :   const char *payto_uri,
     797             :   const struct TALER_Amount *wire_fee,
     798             :   bool exchange_trusted)
     799             : {
     800           0 :   struct KycContext *kc = cls;
     801             : 
     802           0 :   kc->fo = NULL;
     803           0 :   if (NULL == exchange_handle)
     804             :   {
     805           0 :     destroy_kc (kc);
     806           0 :     return;
     807             :   }
     808           0 :   kc->dg = TALER_EXCHANGE_deposits_get (exchange_handle,
     809           0 :                                         &kc->mi->merchant_priv,
     810           0 :                                         &kc->wm->h_wire,
     811           0 :                                         &kc->h_contract_terms,
     812           0 :                                         &kc->coin_pub,
     813             :                                         &deposit_get_callback,
     814             :                                         kc);
     815           0 :   if (NULL == kc->dg)
     816             :   {
     817           0 :     GNUNET_break (0);
     818           0 :     destroy_kc (kc);
     819             :   }
     820             : }
     821             : 
     822             : 
     823             : /**
     824             :  * Function called from ``account_kyc_get_status``
     825             :  * with KYC status information for this merchant.
     826             :  *
     827             :  * @param cls a `struct KycContext *`
     828             :  * @param h_wire hash of the wire account
     829             :  * @param exchange_kyc_serial serial number for the KYC process at the exchange, 0 if unknown
     830             :  * @param payto_uri payto:// URI of the merchant's bank account
     831             :  * @param exchange_url base URL of the exchange for which this is a status
     832             :  * @param last_check when did we last get an update on our KYC status from the exchange
     833             :  * @param kyc_ok true if we satisfied the KYC requirements
     834             :  */
     835             : static void
     836           0 : kyc_cb (
     837             :   void *cls,
     838             :   const struct TALER_MerchantWireHashP *h_wire,
     839             :   uint64_t exchange_kyc_serial,
     840             :   const char *payto_uri,
     841             :   const char *exchange_url,
     842             :   struct GNUNET_TIME_Timestamp last_check,
     843             :   bool kyc_ok)
     844             : {
     845           0 :   struct KycContext *kc = cls;
     846             : 
     847           0 :   kc->kyc_timestamp = last_check;
     848           0 :   kc->kyc_ok = kyc_ok;
     849           0 : }
     850             : 
     851             : 
     852             : /**
     853             :  * Check for our KYC status at @a exchange_url for the
     854             :  * payment of @a pc. First checks if we already have a
     855             :  * positive result from the exchange, and if not checks
     856             :  * with the exchange.
     857             :  *
     858             :  * @param pc payment context to use as starting point
     859             :  * @param eg exchange group of the exchange we are triggering on
     860             :  */
     861             : static void
     862           0 : check_kyc (struct PayContext *pc,
     863             :            const struct ExchangeGroup *eg)
     864             : {
     865             :   enum GNUNET_DB_QueryStatus qs;
     866             :   struct KycContext *kc;
     867             : 
     868           0 :   kc = GNUNET_new (struct KycContext);
     869           0 :   qs = TMH_db->account_kyc_get_status (TMH_db->cls,
     870           0 :                                        pc->hc->instance->settings.id,
     871           0 :                                        &pc->wm->h_wire,
     872             :                                        eg->exchange_url,
     873             :                                        &kyc_cb,
     874             :                                        kc);
     875           0 :   if (qs < 0)
     876             :   {
     877           0 :     GNUNET_break (0);
     878           0 :     GNUNET_free (kc);
     879           0 :     return;
     880             :   }
     881           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     882             :   {
     883           0 :     if (kc->kyc_ok)
     884             :     {
     885           0 :       GNUNET_free (kc);
     886           0 :       return; /* we are done */
     887             :     }
     888           0 :     if (GNUNET_TIME_relative_cmp (
     889             :           GNUNET_TIME_absolute_get_duration (
     890             :             kc->kyc_timestamp.abs_time),
     891             :           <,
     892             :           KYC_RETRY_FREQUENCY))
     893             :     {
     894           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     895             :                   "Not re-checking KYC status at `%s', as we already recently asked\n",
     896             :                   eg->exchange_url);
     897           0 :       GNUNET_free (kc);
     898           0 :       return;
     899             :     }
     900             :   }
     901           0 :   kc->mi = pc->hc->instance;
     902           0 :   kc->mi->rc++;
     903           0 :   kc->wm = pc->wm;
     904           0 :   kc->exchange_url = GNUNET_strdup (eg->exchange_url);
     905           0 :   kc->h_contract_terms = pc->h_contract_terms;
     906             :   /* find one of the coins of the batch */
     907           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
     908             :   {
     909           0 :     struct DepositConfirmation *dc = &pc->dc[i];
     910             : 
     911           0 :     if (0 != strcmp (eg->exchange_url,
     912           0 :                      pc->dc[i].exchange_url))
     913           0 :       continue;
     914           0 :     kc->coin_pub = dc->cdd.coin_pub;
     915           0 :     break;
     916             :   }
     917           0 :   GNUNET_CONTAINER_DLL_insert (kc_head,
     918             :                                kc_tail,
     919             :                                kc);
     920           0 :   kc->fo = TMH_EXCHANGES_find_exchange (kc->exchange_url,
     921             :                                         NULL,
     922             :                                         GNUNET_NO,
     923             :                                         &process_kyc_with_exchange,
     924             :                                         kc);
     925           0 :   if (NULL == kc->fo)
     926             :   {
     927           0 :     GNUNET_break (0);
     928           0 :     destroy_kc (kc);
     929             :   }
     930             : }
     931             : 
     932             : 
     933             : /**
     934             :  * Handle case where the batch deposit completed
     935             :  * with a status of #MHD_HTTP_OK.
     936             :  *
     937             :  * @param eg group that completed
     938             :  * @param dr response from the server
     939             :  */
     940             : static void
     941           0 : handle_batch_deposit_ok (struct ExchangeGroup *eg,
     942             :                          const struct TALER_EXCHANGE_BatchDepositResult *dr)
     943             : {
     944           0 :   struct PayContext *pc = eg->pc;
     945           0 :   enum GNUNET_DB_QueryStatus qs
     946             :     = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     947             : 
     948             :   /* store result to DB */
     949           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     950             :               "Storing successful payment %s (%s) at instance `%s'\n",
     951             :               pc->hc->infix,
     952             :               GNUNET_h2s (&pc->h_contract_terms.hash),
     953             :               pc->hc->instance->settings.id);
     954           0 :   for (unsigned int r = 0; r<MAX_RETRIES; r++)
     955             :   {
     956           0 :     unsigned int j = 0;
     957             : 
     958           0 :     TMH_db->preflight (TMH_db->cls);
     959           0 :     if (GNUNET_OK !=
     960           0 :         TMH_db->start (TMH_db->cls,
     961             :                        "batch-deposit-insert-confirmation"))
     962             :     {
     963           0 :       resume_pay_with_response (
     964             :         pc,
     965             :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     966           0 :         TALER_MHD_MAKE_JSON_PACK (
     967             :           TALER_JSON_pack_ec (
     968             :             TALER_EC_GENERIC_DB_START_FAILED),
     969             :           TMH_pack_exchange_reply (&dr->hr)));
     970           0 :       return;
     971             :     }
     972           0 :     for (unsigned int i = 0; i<pc->coins_cnt; i++)
     973             :     {
     974           0 :       struct DepositConfirmation *dc = &pc->dc[i];
     975             : 
     976           0 :       if (0 != strcmp (eg->exchange_url,
     977           0 :                        pc->dc[i].exchange_url))
     978           0 :         continue;
     979           0 :       if (dc->found_in_db)
     980           0 :         continue;
     981             :       /* NOTE: We might want to check if the order was fully paid concurrently
     982             :          by some other wallet here, and if so, issue an auto-refund. Right now,
     983             :          it is possible to over-pay if two wallets literally make a concurrent
     984             :          payment, as the earlier check for 'paid' is not in the same transaction
     985             :          scope as this 'insert' operation. */
     986           0 :       GNUNET_assert (j < dr->details.success.num_signatures);
     987           0 :       qs = TMH_db->insert_deposit (
     988           0 :         TMH_db->cls,
     989           0 :         pc->hc->instance->settings.id,
     990             :         dr->details.success.deposit_timestamp,
     991           0 :         &pc->h_contract_terms,
     992           0 :         &dc->cdd.coin_pub,
     993           0 :         dc->exchange_url,
     994           0 :         &dc->cdd.amount,
     995           0 :         &dc->deposit_fee,
     996           0 :         &dc->refund_fee,
     997           0 :         &dc->wire_fee,
     998           0 :         &pc->wm->h_wire,
     999           0 :         &dr->details.success.exchange_sigs[j++],
    1000             :         dr->details.success.exchange_pub);
    1001           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    1002             :       {
    1003           0 :         TMH_db->rollback (TMH_db->cls);
    1004           0 :         break;
    1005             :       }
    1006           0 :       if (0 > qs)
    1007             :       {
    1008             :         /* Always report on hard error as well to enable diagnostics */
    1009           0 :         GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    1010             :         /* Forward error including 'proof' for the body */
    1011           0 :         resume_pay_with_error (pc,
    1012             :                                MHD_HTTP_INTERNAL_SERVER_ERROR,
    1013             :                                TALER_EC_GENERIC_DB_STORE_FAILED,
    1014             :                                "insert_deposit");
    1015           0 :         return;
    1016             :       }
    1017             :     }
    1018           0 :     qs = TMH_db->commit (TMH_db->cls);
    1019           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    1020             :     {
    1021           0 :       TMH_db->rollback (TMH_db->cls);
    1022           0 :       continue;
    1023             :     }
    1024           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    1025             :     {
    1026           0 :       GNUNET_break (0);
    1027           0 :       resume_pay_with_error (pc,
    1028             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1029             :                              TALER_EC_GENERIC_DB_COMMIT_FAILED,
    1030             :                              "insert_deposit");
    1031             :     }
    1032           0 :     break; /* DB transaction succeeded */
    1033             :   } /* FOR DB retries */
    1034           0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    1035             :   {
    1036           0 :     resume_pay_with_error (pc,
    1037             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1038             :                            TALER_EC_GENERIC_DB_SOFT_FAILURE,
    1039             :                            "insert_deposit");
    1040           0 :     return;
    1041             :   }
    1042             : 
    1043             :   /* Transaction is done, mark affected coins as complete as well. */
    1044           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    1045             :   {
    1046           0 :     struct DepositConfirmation *dc = &pc->dc[i];
    1047             : 
    1048           0 :     if (0 != strcmp (eg->exchange_url,
    1049           0 :                      pc->dc[i].exchange_url))
    1050           0 :       continue;
    1051           0 :     if (dc->found_in_db)
    1052           0 :       continue;
    1053           0 :     dc->found_in_db = true;     /* well, at least NOW it'd be true ;-) */
    1054           0 :     pc->pending--;
    1055             :   }
    1056           0 :   check_kyc (pc,
    1057             :              eg);
    1058             : }
    1059             : 
    1060             : 
    1061             : /**
    1062             :  * Callback to handle a batch deposit permission's response.
    1063             :  *
    1064             :  * @param cls a `struct ExchangeGroup`
    1065             :  * @param dr HTTP response code details
    1066             :  */
    1067             : static void
    1068           0 : batch_deposit_cb (
    1069             :   void *cls,
    1070             :   const struct TALER_EXCHANGE_BatchDepositResult *dr)
    1071             : {
    1072           0 :   struct ExchangeGroup *eg = cls;
    1073           0 :   struct PayContext *pc = eg->pc;
    1074             : 
    1075           0 :   eg->bdh = NULL;
    1076           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1077             :               "Batch deposit completed with status %u\n",
    1078             :               dr->hr.http_status);
    1079           0 :   GNUNET_assert (GNUNET_YES == pc->suspended);
    1080           0 :   pc->pending_at_eg--;
    1081           0 :   switch (dr->hr.http_status)
    1082             :   {
    1083           0 :   case MHD_HTTP_OK:
    1084           0 :     handle_batch_deposit_ok (eg,
    1085             :                              dr);
    1086           0 :     if (0 == pc->pending_at_eg)
    1087           0 :       execute_pay_transaction (eg->pc);
    1088           0 :     return;
    1089           0 :   default:
    1090           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1091             :                 "Deposit operation failed with HTTP code %u/%d\n",
    1092             :                 dr->hr.http_status,
    1093             :                 (int) dr->hr.ec);
    1094             :     /* Transaction failed */
    1095           0 :     if (5 == dr->hr.http_status / 100)
    1096             :     {
    1097             :       /* internal server error at exchange */
    1098           0 :       resume_pay_with_response (pc,
    1099             :                                 MHD_HTTP_BAD_GATEWAY,
    1100           0 :                                 TALER_MHD_MAKE_JSON_PACK (
    1101             :                                   TALER_JSON_pack_ec (
    1102             :                                     TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
    1103             :                                   TMH_pack_exchange_reply (&dr->hr)));
    1104           0 :       return;
    1105             :     }
    1106           0 :     if (NULL == dr->hr.reply)
    1107             :     {
    1108             :       /* We can't do anything meaningful here, the exchange did something wrong */
    1109           0 :       resume_pay_with_response (
    1110             :         pc,
    1111             :         MHD_HTTP_BAD_GATEWAY,
    1112           0 :         TALER_MHD_MAKE_JSON_PACK (
    1113             :           TALER_JSON_pack_ec (
    1114             :             TALER_EC_MERCHANT_GENERIC_EXCHANGE_REPLY_MALFORMED),
    1115             :           TMH_pack_exchange_reply (&dr->hr)));
    1116           0 :       return;
    1117             :     }
    1118             : 
    1119             :     /* Forward error, adding the "exchange_url" for which the
    1120             :        error was being generated */
    1121           0 :     if (TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS == dr->hr.ec)
    1122             :     {
    1123           0 :       resume_pay_with_response (
    1124             :         pc,
    1125             :         MHD_HTTP_CONFLICT,
    1126           0 :         TALER_MHD_MAKE_JSON_PACK (
    1127             :           TALER_JSON_pack_ec (
    1128             :             TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS),
    1129             :           TMH_pack_exchange_reply (&dr->hr),
    1130             :           GNUNET_JSON_pack_data_auto ("exchange_url",
    1131             :                                       &eg->exchange_url)));
    1132           0 :       return;
    1133             :     }
    1134           0 :     resume_pay_with_response (
    1135             :       pc,
    1136             :       MHD_HTTP_BAD_GATEWAY,
    1137           0 :       TALER_MHD_MAKE_JSON_PACK (
    1138             :         TALER_JSON_pack_ec (
    1139             :           TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
    1140             :         TMH_pack_exchange_reply (&dr->hr),
    1141             :         GNUNET_JSON_pack_data_auto ("exchange_url",
    1142             :                                     &eg->exchange_url)));
    1143           0 :     return;
    1144             :   } /* end switch */
    1145             : }
    1146             : 
    1147             : 
    1148             : /**
    1149             :  * Function called with the result of our exchange lookup.
    1150             :  *
    1151             :  * @param cls the `struct ExchangeGroup`
    1152             :  * @param hr HTTP response details
    1153             :  * @param exchange_handle NULL if exchange was not found to be acceptable
    1154             :  * @param payto_uri payto://-URI of the exchange
    1155             :  * @param wire_fee current applicable fee for dealing with @a exchange_handle,
    1156             :  *        NULL if not available
    1157             :  * @param exchange_trusted true if this exchange is
    1158             :  *        trusted by config
    1159             :  */
    1160             : static void
    1161           0 : process_pay_with_exchange (
    1162             :   void *cls,
    1163             :   const struct TALER_EXCHANGE_HttpResponse *hr,
    1164             :   struct TALER_EXCHANGE_Handle *exchange_handle,
    1165             :   const char *payto_uri,
    1166             :   const struct TALER_Amount *wire_fee,
    1167             :   bool exchange_trusted)
    1168             : {
    1169           0 :   struct ExchangeGroup *eg = cls;
    1170           0 :   struct PayContext *pc = eg->pc;
    1171           0 :   struct TMH_HandlerContext *hc = pc->hc;
    1172             :   const struct TALER_EXCHANGE_Keys *keys;
    1173             :   unsigned int group_size;
    1174             : 
    1175             :   (void) payto_uri;
    1176           0 :   eg->fo = NULL;
    1177           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1178             :               "Processing payment with exchange %s\n",
    1179             :               (NULL == exchange_handle)
    1180             :               ? "<null>"
    1181             :               : TALER_EXCHANGE_get_base_url (exchange_handle));
    1182           0 :   GNUNET_assert (GNUNET_YES == pc->suspended);
    1183           0 :   if (NULL == hr)
    1184             :   {
    1185           0 :     pc->pending_at_eg--;
    1186           0 :     resume_pay_with_response (
    1187             :       pc,
    1188             :       MHD_HTTP_GATEWAY_TIMEOUT,
    1189           0 :       TALER_MHD_MAKE_JSON_PACK (
    1190             :         TALER_JSON_pack_ec (TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT)));
    1191           0 :     return;
    1192             :   }
    1193           0 :   if ( (MHD_HTTP_OK != hr->http_status) ||
    1194             :        (NULL == exchange_handle) )
    1195             :   {
    1196           0 :     pc->pending_at_eg--;
    1197           0 :     resume_pay_with_response (
    1198             :       pc,
    1199             :       MHD_HTTP_BAD_GATEWAY,
    1200           0 :       TALER_MHD_MAKE_JSON_PACK (
    1201             :         TALER_JSON_pack_ec (
    1202             :           TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE),
    1203             :         TMH_pack_exchange_reply (hr)));
    1204           0 :     return;
    1205             :   }
    1206           0 :   keys = TALER_EXCHANGE_get_keys (exchange_handle);
    1207           0 :   if (NULL == keys)
    1208             :   {
    1209           0 :     pc->pending_at_eg--;
    1210           0 :     GNUNET_break (0); /* should not be possible if HTTP status is #MHD_HTTP_OK */
    1211           0 :     resume_pay_with_error (pc,
    1212             :                            MHD_HTTP_BAD_GATEWAY,
    1213             :                            TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE,
    1214             :                            NULL);
    1215           0 :     return;
    1216             :   }
    1217             : 
    1218             :   /* Initiate /batch-deposit operation for all coins of
    1219             :      the current exchange (!) */
    1220           0 :   group_size = 0;
    1221           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    1222             :   {
    1223           0 :     struct DepositConfirmation *dc = &pc->dc[i];
    1224             :     const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
    1225             :     unsigned int http_status;
    1226             :     enum TALER_ErrorCode ec;
    1227           0 :     bool is_age_restricted_denom = false;
    1228             : 
    1229           0 :     if (0 != strcmp (eg->exchange_url,
    1230           0 :                      pc->dc[i].exchange_url))
    1231           0 :       continue;
    1232           0 :     if (dc->found_in_db)
    1233           0 :       continue;
    1234             : 
    1235             :     denom_details
    1236           0 :       = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
    1237           0 :                                                      &dc->cdd.h_denom_pub);
    1238           0 :     if (NULL == denom_details)
    1239             :     {
    1240           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1241             :                   "Denomination not found, re-fetching /keys\n");
    1242           0 :       if (! eg->tried_force_keys)
    1243             :       {
    1244             :         /* let's try *forcing* a re-download of /keys from the exchange.
    1245             :            Maybe the wallet has seen /keys that we missed. */
    1246           0 :         eg->tried_force_keys = true;
    1247           0 :         eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
    1248           0 :                                               pc->wm->wire_method,
    1249             :                                               GNUNET_YES,
    1250             :                                               &process_pay_with_exchange,
    1251             :                                               eg);
    1252           0 :         if (NULL != eg->fo)
    1253           0 :           return;
    1254             :       }
    1255             :       /* Forcing failed or we already did it, give up */
    1256           0 :       pc->pending_at_eg--;
    1257           0 :       resume_pay_with_response (
    1258             :         pc,
    1259             :         MHD_HTTP_BAD_REQUEST,
    1260           0 :         TALER_MHD_MAKE_JSON_PACK (
    1261             :           TALER_JSON_pack_ec (
    1262             :             TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND),
    1263             :           GNUNET_JSON_pack_data_auto ("h_denom_pub",
    1264             :                                       &dc->cdd.h_denom_pub),
    1265             :           GNUNET_JSON_pack_allow_null (
    1266             :             GNUNET_JSON_pack_object_incref (
    1267             :               "exchange_keys",
    1268             :               (json_t *) TALER_EXCHANGE_get_keys_raw (exchange_handle)))));
    1269           0 :       return;
    1270             :     }
    1271           0 :     dc->deposit_fee = denom_details->fees.deposit;
    1272           0 :     dc->refund_fee = denom_details->fees.refund;
    1273             : 
    1274           0 :     if (GNUNET_OK !=
    1275           0 :         TMH_AUDITORS_check_dk (exchange_handle,
    1276             :                                denom_details,
    1277             :                                exchange_trusted,
    1278             :                                &http_status,
    1279             :                                &ec))
    1280             :     {
    1281           0 :       if (! eg->tried_force_keys)
    1282             :       {
    1283           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1284             :                     "Denomination not audited by trusted auditor, re-fetching /keys\n");
    1285             :         /* let's try *forcing* a re-download of /keys from the exchange.
    1286             :            Maybe the wallet has seen auditors that we missed. */
    1287           0 :         eg->tried_force_keys = true;
    1288           0 :         eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
    1289           0 :                                               pc->wm->wire_method,
    1290             :                                               GNUNET_YES,
    1291             :                                               &process_pay_with_exchange,
    1292             :                                               eg);
    1293           0 :         if (NULL != eg->fo)
    1294           0 :           return;
    1295             :       }
    1296           0 :       pc->pending_at_eg--;
    1297           0 :       resume_pay_with_response (
    1298             :         pc,
    1299             :         http_status,
    1300           0 :         TALER_MHD_MAKE_JSON_PACK (
    1301             :           TALER_JSON_pack_ec (ec),
    1302             :           GNUNET_JSON_pack_data_auto ("h_denom_pub",
    1303             :                                       &denom_details->h_key)));
    1304           0 :       return;
    1305             :     }
    1306             : 
    1307             : 
    1308             :     /* Now that we have the details about the denomination, we can verify age
    1309             :      * restriction requirements, if applicable. Note that denominations with an
    1310             :      * age_mask equal to zero always pass the age verification.  */
    1311           0 :     is_age_restricted_denom = (0 != denom_details->key.age_mask.bits);
    1312             : 
    1313           0 :     if (is_age_restricted_denom &&
    1314           0 :         (0 < pc->minimum_age))
    1315           0 :     {
    1316             :       /* Minimum age given and restricted coin provided: We need to verify the
    1317             :        * minimum age */
    1318           0 :       unsigned int code = 0;
    1319             : 
    1320           0 :       if (dc->no_age_commitment)
    1321             :       {
    1322           0 :         GNUNET_break_op (0);
    1323           0 :         code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_MISSING;
    1324           0 :         goto AGE_FAIL;
    1325             :       }
    1326           0 :       dc->age_commitment.mask = denom_details->key.age_mask;
    1327           0 :       if (((int) (dc->age_commitment.num + 1)) !=
    1328           0 :           __builtin_popcount (dc->age_commitment.mask.bits))
    1329             :       {
    1330           0 :         GNUNET_break_op (0);
    1331           0 :         code =
    1332             :           TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_SIZE_MISMATCH;
    1333           0 :         goto AGE_FAIL;
    1334             :       }
    1335           0 :       if (GNUNET_OK !=
    1336           0 :           TALER_age_commitment_verify (
    1337           0 :             &dc->age_commitment,
    1338           0 :             pc->minimum_age,
    1339           0 :             &dc->minimum_age_sig))
    1340           0 :         code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_VERIFICATION_FAILED;
    1341           0 : AGE_FAIL:
    1342           0 :       if (0 < code)
    1343             :       {
    1344           0 :         pc->pending_at_eg--;
    1345           0 :         GNUNET_free (dc->age_commitment.keys);
    1346           0 :         resume_pay_with_response (
    1347             :           pc,
    1348             :           MHD_HTTP_BAD_REQUEST,
    1349           0 :           TALER_MHD_MAKE_JSON_PACK (
    1350             :             TALER_JSON_pack_ec (code),
    1351             :             GNUNET_JSON_pack_data_auto ("h_denom_pub",
    1352             :                                         &denom_details->h_key)));
    1353           0 :         return;
    1354             :       }
    1355             : 
    1356             :       /* Age restriction successfully verified!
    1357             :        * Calculate the hash of the age commitment. */
    1358           0 :       TALER_age_commitment_hash (&dc->age_commitment,
    1359             :                                  &dc->cdd.h_age_commitment);
    1360           0 :       GNUNET_free (dc->age_commitment.keys);
    1361             :     }
    1362           0 :     else if (is_age_restricted_denom && dc->no_h_age_commitment)
    1363             :     {
    1364             :       /* The contract did not ask for a minimum_age but the client paid
    1365             :        * with a coin that has age restriction enabled.  We lack the hash
    1366             :        * of the age commitment in this case in order to verify the coin
    1367             :        * and to deposit it with the exchange. */
    1368           0 :       pc->pending_at_eg--;
    1369           0 :       GNUNET_break_op (0);
    1370           0 :       resume_pay_with_response (
    1371             :         pc,
    1372             :         MHD_HTTP_BAD_REQUEST,
    1373           0 :         TALER_MHD_MAKE_JSON_PACK (
    1374             :           TALER_JSON_pack_ec (
    1375             :             TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_HASH_MISSING),
    1376             :           GNUNET_JSON_pack_data_auto ("h_denom_pub",
    1377             :                                       &denom_details->h_key)));
    1378           0 :       return;
    1379             :     }
    1380           0 :     group_size++;
    1381             :   }
    1382             : 
    1383           0 :   if (0 == group_size)
    1384             :   {
    1385           0 :     GNUNET_break (0);
    1386           0 :     pc->pending_at_eg--;
    1387           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1388             :                 "Group size zero, %u batch transactions remain pending\n",
    1389             :                 pc->pending_at_eg);
    1390           0 :     if (0 == pc->pending_at_eg)
    1391           0 :       execute_pay_transaction (pc);
    1392           0 :     return;
    1393             :   }
    1394             : 
    1395           0 :   {
    1396           0 :     struct TALER_EXCHANGE_CoinDepositDetail cdds[group_size];
    1397           0 :     struct TALER_EXCHANGE_DepositContractDetail dcd = {
    1398             :       .wire_deadline = pc->wire_transfer_deadline,
    1399           0 :       .merchant_payto_uri = pc->wm->payto_uri,
    1400           0 :       .wire_salt = pc->wm->wire_salt,
    1401             :       .h_contract_terms = pc->h_contract_terms,
    1402             :       .extension_details = NULL /* FIXME #7270 */,
    1403             :       .timestamp = pc->timestamp,
    1404           0 :       .merchant_pub = hc->instance->merchant_pub,
    1405             :       .refund_deadline = pc->refund_deadline
    1406             :     };
    1407             :     enum TALER_ErrorCode ec;
    1408             : 
    1409           0 :     for (unsigned int i = 0; i<pc->coins_cnt; i++)
    1410             :     {
    1411           0 :       struct DepositConfirmation *dc = &pc->dc[i];
    1412             : 
    1413           0 :       if (dc->found_in_db)
    1414           0 :         continue;
    1415           0 :       if (0 != strcmp (dc->exchange_url,
    1416             :                        eg->exchange_url))
    1417           0 :         continue;
    1418           0 :       cdds[i] = dc->cdd;
    1419           0 :       dc->wire_fee = *wire_fee;
    1420           0 :       GNUNET_assert (NULL != pc->wm);
    1421             :     }
    1422           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1423             :                 "Initiating batch deposit with %u coins\n",
    1424             :                 group_size);
    1425           0 :     eg->bdh = TALER_EXCHANGE_batch_deposit (exchange_handle,
    1426             :                                             &dcd,
    1427             :                                             group_size,
    1428             :                                             cdds,
    1429             :                                             &batch_deposit_cb,
    1430             :                                             eg,
    1431             :                                             &ec);
    1432           0 :     if (NULL == eg->bdh)
    1433             :     {
    1434             :       /* Signature was invalid or some other constraint was not satisfied.  If
    1435             :          the exchange was unavailable, we'd get that information in the
    1436             :          callback. */
    1437           0 :       pc->pending_at_eg--;
    1438           0 :       GNUNET_break_op (0);
    1439           0 :       resume_pay_with_response (
    1440             :         pc,
    1441             :         TALER_ErrorCode_get_http_status_safe (ec),
    1442           0 :         TALER_MHD_MAKE_JSON_PACK (
    1443             :           TALER_JSON_pack_ec (ec),
    1444             :           GNUNET_JSON_pack_string ("exchange_url",
    1445             :                                    eg->exchange_url)));
    1446           0 :       return;
    1447             :     }
    1448           0 :     if (TMH_force_audit)
    1449           0 :       TALER_EXCHANGE_batch_deposit_force_dc (eg->bdh);
    1450             :   }
    1451             : }
    1452             : 
    1453             : 
    1454             : /**
    1455             :  * Start batch deposits for all exchanges involved
    1456             :  * in this payment.
    1457             :  *
    1458             :  * @param pc payment context we are processing
    1459             :  */
    1460             : static void
    1461           0 : start_batch_deposits (struct PayContext *pc)
    1462             : {
    1463           0 :   for (unsigned int i = 0; i<pc->num_exchanges; i++)
    1464             :   {
    1465           0 :     struct ExchangeGroup *eg = pc->egs[i];
    1466           0 :     bool have_coins = false;
    1467             : 
    1468           0 :     for (unsigned int j = 0; j<pc->coins_cnt; j++)
    1469             :     {
    1470           0 :       struct DepositConfirmation *dc = &pc->dc[j];
    1471             : 
    1472           0 :       if (0 != strcmp (eg->exchange_url,
    1473           0 :                        pc->dc[j].exchange_url))
    1474           0 :         continue;
    1475           0 :       if (dc->found_in_db)
    1476           0 :         continue;
    1477           0 :       have_coins = true;
    1478           0 :       break;
    1479             :     }
    1480           0 :     if (! have_coins)
    1481           0 :       continue; /* no coins left to deposit at this exchange */
    1482           0 :     eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
    1483           0 :                                           pc->wm->wire_method,
    1484             :                                           GNUNET_NO,
    1485             :                                           &process_pay_with_exchange,
    1486             :                                           eg);
    1487           0 :     if (NULL == eg->fo)
    1488             :     {
    1489           0 :       GNUNET_break (0);
    1490           0 :       resume_pay_with_error (pc,
    1491             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1492             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LOOKUP_FAILED,
    1493             :                              "Failed to lookup exchange by URL");
    1494           0 :       return;
    1495             :     }
    1496           0 :     pc->pending_at_eg++;
    1497             :   }
    1498           0 :   if (0 == pc->pending_at_eg)
    1499           0 :     execute_pay_transaction (pc);
    1500             : }
    1501             : 
    1502             : 
    1503             : /**
    1504             :  * Function called with information about a coin that was deposited.
    1505             :  *
    1506             :  * @param cls closure
    1507             :  * @param exchange_url exchange where @a coin_pub was deposited
    1508             :  * @param coin_pub public key of the coin
    1509             :  * @param amount_with_fee amount the exchange will deposit for this coin
    1510             :  * @param deposit_fee fee the exchange will charge for this coin
    1511             :  * @param refund_fee fee the exchange will charge for refunding this coin
    1512             :  * @param wire_fee wire fee the exchange of this coin charges
    1513             :  */
    1514             : static void
    1515           0 : check_coin_paid (void *cls,
    1516             :                  const char *exchange_url,
    1517             :                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1518             :                  const struct TALER_Amount *amount_with_fee,
    1519             :                  const struct TALER_Amount *deposit_fee,
    1520             :                  const struct TALER_Amount *refund_fee,
    1521             :                  const struct TALER_Amount *wire_fee)
    1522             : {
    1523           0 :   struct PayContext *pc = cls;
    1524             : 
    1525           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    1526             :   {
    1527           0 :     struct DepositConfirmation *dc = &pc->dc[i];
    1528             : 
    1529           0 :     if (dc->found_in_db)
    1530           0 :       continue; /* processed earlier, skip "expensive" memcmp() */
    1531             :     /* Get matching coin from results*/
    1532           0 :     if ( (0 != GNUNET_memcmp (coin_pub,
    1533           0 :                               &dc->cdd.coin_pub)) ||
    1534             :          (0 !=
    1535           0 :           strcmp (exchange_url,
    1536           0 :                   dc->exchange_url)) ||
    1537           0 :          (0 != TALER_amount_cmp (amount_with_fee,
    1538           0 :                                  &dc->cdd.amount)) )
    1539           0 :       continue; /* does not match, skip */
    1540           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1541             :                 "Deposit of coin `%s' already in our DB.\n",
    1542             :                 TALER_B2S (coin_pub));
    1543             : 
    1544           0 :     GNUNET_assert (0 <=
    1545             :                    TALER_amount_add (&pc->total_paid,
    1546             :                                      &pc->total_paid,
    1547             :                                      amount_with_fee));
    1548           0 :     GNUNET_assert (0 <=
    1549             :                    TALER_amount_add (&pc->total_fees_paid,
    1550             :                                      &pc->total_fees_paid,
    1551             :                                      deposit_fee));
    1552           0 :     dc->deposit_fee = *deposit_fee;
    1553           0 :     dc->refund_fee = *refund_fee;
    1554           0 :     dc->wire_fee = *wire_fee;
    1555           0 :     dc->cdd.amount = *amount_with_fee;
    1556           0 :     dc->found_in_db = true;
    1557           0 :     pc->pending--;
    1558             :   }
    1559           0 : }
    1560             : 
    1561             : 
    1562             : /**
    1563             :  * Function called with information about a refund.  Check if this coin was
    1564             :  * claimed by the wallet for the transaction, and if so add the refunded
    1565             :  * amount to the pc's "total_refunded" amount.
    1566             :  *
    1567             :  * @param cls closure with a `struct PayContext`
    1568             :  * @param coin_pub public coin from which the refund comes from
    1569             :  * @param refund_amount refund amount which is being taken from @a coin_pub
    1570             :  */
    1571             : static void
    1572           0 : check_coin_refunded (void *cls,
    1573             :                      const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1574             :                      const struct TALER_Amount *refund_amount)
    1575             : {
    1576           0 :   struct PayContext *pc = cls;
    1577             : 
    1578             :   /* We look at refunds here that apply to the coins
    1579             :      that the customer is currently trying to pay us with.
    1580             : 
    1581             :      Such refunds are not "normal" refunds, but abort-pay refunds, which are
    1582             :      given in the case that the wallet aborts the payment.
    1583             :      In the case the wallet then decides to complete the payment *after* doing
    1584             :      an abort-pay refund (an unusual but possible case), we need
    1585             :      to make sure that existing refunds are accounted for. */
    1586             : 
    1587           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    1588             :   {
    1589           0 :     struct DepositConfirmation *dc = &pc->dc[i];
    1590             : 
    1591             :     /* Get matching coins from results.  */
    1592           0 :     if (0 != GNUNET_memcmp (coin_pub,
    1593             :                             &dc->cdd.coin_pub))
    1594           0 :       continue;
    1595           0 :     GNUNET_assert (0 <=
    1596             :                    TALER_amount_add (&pc->total_refunded,
    1597             :                                      &pc->total_refunded,
    1598             :                                      refund_amount));
    1599           0 :     break;
    1600             :   }
    1601           0 : }
    1602             : 
    1603             : 
    1604             : /**
    1605             :  * Check whether the amount paid is sufficient to cover the price.
    1606             :  *
    1607             :  * @param pc payment context to check
    1608             :  * @return true if the payment is sufficient, false if it is
    1609             :  *         insufficient
    1610             :  */
    1611             : static bool
    1612           0 : check_payment_sufficient (struct PayContext *pc)
    1613             : {
    1614             :   struct TALER_Amount acc_fee;
    1615             :   struct TALER_Amount acc_amount;
    1616             :   struct TALER_Amount final_amount;
    1617             :   struct TALER_Amount wire_fee_delta;
    1618             :   struct TALER_Amount wire_fee_customer_contribution;
    1619             :   struct TALER_Amount total_wire_fee;
    1620             :   struct TALER_Amount total_needed;
    1621             : 
    1622           0 :   if (0 == pc->coins_cnt)
    1623             :   {
    1624           0 :     return ((0 == pc->amount.value) &&
    1625           0 :             (0 == pc->amount.fraction));
    1626             :   }
    1627             : 
    1628           0 :   acc_fee = pc->dc[0].deposit_fee;
    1629           0 :   total_wire_fee = pc->dc[0].wire_fee;
    1630           0 :   acc_amount = pc->dc[0].cdd.amount;
    1631             : 
    1632             :   /**
    1633             :    * This loops calculates what are the deposit fee / total
    1634             :    * amount with fee / and wire fee, for all the coins.
    1635             :    */
    1636           0 :   for (unsigned int i = 1; i<pc->coins_cnt; i++)
    1637             :   {
    1638           0 :     struct DepositConfirmation *dc = &pc->dc[i];
    1639             : 
    1640           0 :     GNUNET_assert (dc->found_in_db);
    1641           0 :     if ( (0 >
    1642           0 :           TALER_amount_add (&acc_fee,
    1643           0 :                             &dc->deposit_fee,
    1644           0 :                             &acc_fee)) ||
    1645             :          (0 >
    1646           0 :           TALER_amount_add (&acc_amount,
    1647           0 :                             &dc->cdd.amount,
    1648             :                             &acc_amount)) )
    1649             :     {
    1650           0 :       GNUNET_break (0);
    1651             :       /* Overflow in these amounts? Very strange. */
    1652           0 :       resume_pay_with_error (pc,
    1653             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1654             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
    1655             :                              "Overflow adding up amounts");
    1656           0 :       return false;
    1657             :     }
    1658           0 :     if (1 ==
    1659           0 :         TALER_amount_cmp (&dc->deposit_fee,
    1660           0 :                           &dc->cdd.amount))
    1661             :     {
    1662           0 :       GNUNET_break_op (0);
    1663           0 :       resume_pay_with_error (pc,
    1664             :                              MHD_HTTP_BAD_REQUEST,
    1665             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_FEES_EXCEED_PAYMENT,
    1666             :                              "Deposit fees exceed coin's contribution");
    1667           0 :       return false;
    1668             :     }
    1669             : 
    1670             :     /* If exchange differs, add wire fee */
    1671             :     {
    1672           0 :       bool new_exchange = true;
    1673             : 
    1674           0 :       for (unsigned int j = 0; j<i; j++)
    1675           0 :         if (0 == strcasecmp (dc->exchange_url,
    1676           0 :                              pc->dc[j].exchange_url))
    1677             :         {
    1678           0 :           new_exchange = false;
    1679           0 :           break;
    1680             :         }
    1681             : 
    1682           0 :       if (! new_exchange)
    1683           0 :         continue;
    1684             : 
    1685           0 :       if (GNUNET_OK !=
    1686           0 :           TALER_amount_cmp_currency (&total_wire_fee,
    1687           0 :                                      &dc->wire_fee))
    1688             :       {
    1689           0 :         GNUNET_break_op (0);
    1690           0 :         resume_pay_with_error (pc,
    1691             :                                MHD_HTTP_CONFLICT,
    1692             :                                TALER_EC_GENERIC_CURRENCY_MISMATCH,
    1693             :                                total_wire_fee.currency);
    1694           0 :         return false;
    1695             :       }
    1696           0 :       if (0 >
    1697           0 :           TALER_amount_add (&total_wire_fee,
    1698             :                             &total_wire_fee,
    1699           0 :                             &dc->wire_fee))
    1700             :       {
    1701           0 :         GNUNET_break (0);
    1702           0 :         resume_pay_with_error (pc,
    1703             :                                MHD_HTTP_INTERNAL_SERVER_ERROR,
    1704             :                                TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED,
    1705             :                                "could not add exchange wire fee to total");
    1706           0 :         return false;
    1707             :       }
    1708             :     }
    1709             :   } /* deposit loop */
    1710             : 
    1711           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1712             :               "Amount received from wallet: %s\n",
    1713             :               TALER_amount2s (&acc_amount));
    1714           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1715             :               "Deposit fee for all coins: %s\n",
    1716             :               TALER_amount2s (&acc_fee));
    1717           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1718             :               "Total wire fee: %s\n",
    1719             :               TALER_amount2s (&total_wire_fee));
    1720           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1721             :               "Max wire fee: %s\n",
    1722             :               TALER_amount2s (&pc->max_wire_fee));
    1723           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1724             :               "Deposit fee limit for merchant: %s\n",
    1725             :               TALER_amount2s (&pc->max_fee));
    1726           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1727             :               "Total refunded amount: %s\n",
    1728             :               TALER_amount2s (&pc->total_refunded));
    1729             : 
    1730             :   /* Now compare exchange wire fee compared to
    1731             :    * what we are willing to pay */
    1732           0 :   if (GNUNET_YES !=
    1733           0 :       TALER_amount_cmp_currency (&total_wire_fee,
    1734           0 :                                  &pc->max_wire_fee))
    1735             :   {
    1736           0 :     GNUNET_break (0);
    1737           0 :     resume_pay_with_error (pc,
    1738             :                            MHD_HTTP_CONFLICT,
    1739             :                            TALER_EC_GENERIC_CURRENCY_MISMATCH,
    1740             :                            total_wire_fee.currency);
    1741           0 :     return false;
    1742             :   }
    1743             : 
    1744           0 :   switch (TALER_amount_subtract (&wire_fee_delta,
    1745             :                                  &total_wire_fee,
    1746           0 :                                  &pc->max_wire_fee))
    1747             :   {
    1748           0 :   case TALER_AAR_RESULT_POSITIVE:
    1749             :     /* Actual wire fee is indeed higher than our maximum,
    1750             :        compute how much the customer is expected to cover!  */
    1751           0 :     TALER_amount_divide (&wire_fee_customer_contribution,
    1752             :                          &wire_fee_delta,
    1753             :                          pc->wire_fee_amortization);
    1754           0 :     break;
    1755           0 :   case TALER_AAR_RESULT_ZERO:
    1756             :   case TALER_AAR_INVALID_NEGATIVE_RESULT:
    1757             :     /* Wire fee threshold is still above the wire fee amount.
    1758             :        Customer is not going to contribute on this.  */
    1759           0 :     GNUNET_assert (GNUNET_OK ==
    1760             :                    TALER_amount_set_zero (total_wire_fee.currency,
    1761             :                                           &wire_fee_customer_contribution));
    1762           0 :     break;
    1763           0 :   default:
    1764           0 :     GNUNET_assert (0);
    1765             :   }
    1766             : 
    1767             :   /* add wire fee contribution to the total fees */
    1768           0 :   if (0 >
    1769           0 :       TALER_amount_add (&acc_fee,
    1770             :                         &acc_fee,
    1771             :                         &wire_fee_customer_contribution))
    1772             :   {
    1773           0 :     GNUNET_break (0);
    1774           0 :     resume_pay_with_error (pc,
    1775             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1776             :                            TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
    1777             :                            "Overflow adding up amounts");
    1778           0 :     return false;
    1779             :   }
    1780           0 :   if (-1 == TALER_amount_cmp (&pc->max_fee,
    1781             :                               &acc_fee))
    1782             :   {
    1783             :     /**
    1784             :      * Sum of fees of *all* the different exchanges of all the coins are
    1785             :      * higher than the fixed limit that the merchant is willing to pay.  The
    1786             :      * difference must be paid by the customer.
    1787             :      */
    1788             :     struct TALER_Amount excess_fee;
    1789             : 
    1790             :     /* compute fee amount to be covered by customer */
    1791           0 :     GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
    1792             :                    TALER_amount_subtract (&excess_fee,
    1793             :                                           &acc_fee,
    1794             :                                           &pc->max_fee));
    1795             :     /* add that to the total */
    1796           0 :     if (0 >
    1797           0 :         TALER_amount_add (&total_needed,
    1798             :                           &excess_fee,
    1799           0 :                           &pc->amount))
    1800             :     {
    1801           0 :       GNUNET_break (0);
    1802           0 :       resume_pay_with_error (pc,
    1803             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1804             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
    1805             :                              "Overflow adding up amounts");
    1806           0 :       return false;
    1807             :     }
    1808             :   }
    1809             :   else
    1810             :   {
    1811             :     /* Fees are fully covered by the merchant, all we require
    1812             :        is that the total payment is not below the contract's amount */
    1813           0 :     total_needed = pc->amount;
    1814             :   }
    1815             : 
    1816             :   /* Do not count refunds towards the payment */
    1817           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1818             :               "Subtracting total refunds from paid amount: %s\n",
    1819             :               TALER_amount2s (&pc->total_refunded));
    1820           0 :   if (0 >
    1821           0 :       TALER_amount_subtract (&final_amount,
    1822             :                              &acc_amount,
    1823           0 :                              &pc->total_refunded))
    1824             :   {
    1825           0 :     GNUNET_break (0);
    1826           0 :     resume_pay_with_error (pc,
    1827             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1828             :                            TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDS_EXCEED_PAYMENTS,
    1829             :                            "refunded amount exceeds total payments");
    1830           0 :     return false;
    1831             :   }
    1832             : 
    1833           0 :   if (-1 == TALER_amount_cmp (&final_amount,
    1834             :                               &total_needed))
    1835             :   {
    1836             :     /* acc_amount < total_needed */
    1837           0 :     if (-1 < TALER_amount_cmp (&acc_amount,
    1838             :                                &total_needed))
    1839             :     {
    1840           0 :       GNUNET_break_op (0);
    1841           0 :       resume_pay_with_error (pc,
    1842             :                              MHD_HTTP_PAYMENT_REQUIRED,
    1843             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDED,
    1844             :                              "contract not paid up due to refunds");
    1845             :     }
    1846           0 :     else if (-1 < TALER_amount_cmp (&acc_amount,
    1847           0 :                                     &pc->amount))
    1848             :     {
    1849           0 :       GNUNET_break_op (0);
    1850           0 :       resume_pay_with_error (pc,
    1851             :                              MHD_HTTP_NOT_ACCEPTABLE,
    1852             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES,
    1853             :                              "contract not paid up due to fees (client may have calculated them badly)");
    1854             :     }
    1855             :     else
    1856             :     {
    1857           0 :       GNUNET_break_op (0);
    1858           0 :       resume_pay_with_error (pc,
    1859             :                              MHD_HTTP_NOT_ACCEPTABLE,
    1860             :                              TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT,
    1861             :                              "payment insufficient");
    1862             :     }
    1863           0 :     return false;
    1864             :   }
    1865           0 :   return true;
    1866             : }
    1867             : 
    1868             : 
    1869             : /**
    1870             :  * Use database to notify other clients about the
    1871             :  * payment being completed.
    1872             :  *
    1873             :  * @param pc context to trigger notification for
    1874             :  */
    1875             : static void
    1876           0 : trigger_payment_notification (struct PayContext *pc)
    1877             : {
    1878             :   {
    1879           0 :     struct TMH_OrderPayEventP pay_eh = {
    1880           0 :       .header.size = htons (sizeof (pay_eh)),
    1881           0 :       .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
    1882           0 :       .merchant_pub = pc->hc->instance->merchant_pub
    1883             :     };
    1884             : 
    1885           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1886             :                 "Notifying clients about payment of order %s\n",
    1887             :                 pc->order_id);
    1888           0 :     GNUNET_CRYPTO_hash (pc->order_id,
    1889             :                         strlen (pc->order_id),
    1890             :                         &pay_eh.h_order_id);
    1891           0 :     TMH_db->event_notify (TMH_db->cls,
    1892             :                           &pay_eh.header,
    1893             :                           NULL,
    1894             :                           0);
    1895             :   }
    1896           0 :   if ( (NULL != pc->session_id) &&
    1897           0 :        (NULL != pc->fulfillment_url) )
    1898             :   {
    1899           0 :     struct TMH_SessionEventP session_eh = {
    1900           0 :       .header.size = htons (sizeof (session_eh)),
    1901           0 :       .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
    1902           0 :       .merchant_pub = pc->hc->instance->merchant_pub
    1903             :     };
    1904             : 
    1905           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1906             :                 "Notifying clients about session change to %s for %s\n",
    1907             :                 pc->session_id,
    1908             :                 pc->fulfillment_url);
    1909           0 :     GNUNET_CRYPTO_hash (pc->session_id,
    1910           0 :                         strlen (pc->session_id),
    1911             :                         &session_eh.h_session_id);
    1912           0 :     GNUNET_CRYPTO_hash (pc->fulfillment_url,
    1913           0 :                         strlen (pc->fulfillment_url),
    1914             :                         &session_eh.h_fulfillment_url);
    1915           0 :     TMH_db->event_notify (TMH_db->cls,
    1916             :                           &session_eh.header,
    1917             :                           NULL,
    1918             :                           0);
    1919             :   }
    1920           0 : }
    1921             : 
    1922             : 
    1923             : /**
    1924             :  * Generate response (payment successful)
    1925             :  *
    1926             :  * @param[in,out] pc payment context where the payment was successful
    1927             :  */
    1928             : static void
    1929           0 : generate_success_response (struct PayContext *pc)
    1930             : {
    1931             :   struct GNUNET_CRYPTO_EddsaSignature sig;
    1932             : 
    1933             :   /* Sign on our end (as the payment did go through, even if it may
    1934             :      have been refunded already) */
    1935           0 :   TALER_merchant_pay_sign (&pc->h_contract_terms,
    1936           0 :                            &pc->hc->instance->merchant_priv,
    1937             :                            &sig);
    1938             :   /* Build the response */
    1939           0 :   resume_pay_with_response (
    1940             :     pc,
    1941             :     MHD_HTTP_OK,
    1942           0 :     TALER_MHD_MAKE_JSON_PACK (
    1943             :       GNUNET_JSON_pack_data_auto ("sig",
    1944             :                                   &sig)));
    1945           0 : }
    1946             : 
    1947             : 
    1948             : static void
    1949           0 : execute_pay_transaction (struct PayContext *pc)
    1950             : {
    1951           0 :   struct TMH_HandlerContext *hc = pc->hc;
    1952           0 :   const char *instance_id = hc->instance->settings.id;
    1953             : 
    1954             :   /* Avoid re-trying transactions on soft errors forever! */
    1955           0 :   if (pc->retry_counter++ > MAX_RETRIES)
    1956             :   {
    1957           0 :     GNUNET_break (0);
    1958           0 :     resume_pay_with_error (pc,
    1959             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1960             :                            TALER_EC_GENERIC_DB_SOFT_FAILURE,
    1961             :                            NULL);
    1962           0 :     return;
    1963             :   }
    1964           0 :   GNUNET_assert (GNUNET_YES == pc->suspended);
    1965             : 
    1966             :   /* Initialize some amount accumulators
    1967             :      (used in check_coin_paid(), check_coin_refunded()
    1968             :      and check_payment_sufficient()). */
    1969           0 :   GNUNET_break (GNUNET_OK ==
    1970             :                 TALER_amount_set_zero (pc->amount.currency,
    1971             :                                        &pc->total_paid));
    1972           0 :   GNUNET_break (GNUNET_OK ==
    1973             :                 TALER_amount_set_zero (pc->amount.currency,
    1974             :                                        &pc->total_fees_paid));
    1975           0 :   GNUNET_break (GNUNET_OK ==
    1976             :                 TALER_amount_set_zero (pc->amount.currency,
    1977             :                                        &pc->total_refunded));
    1978           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    1979           0 :     pc->dc[i].found_in_db = false;
    1980           0 :   pc->pending = pc->coins_cnt;
    1981             : 
    1982             :   /* First, try to see if we have all we need already done */
    1983           0 :   TMH_db->preflight (TMH_db->cls);
    1984           0 :   if (GNUNET_OK !=
    1985           0 :       TMH_db->start (TMH_db->cls,
    1986             :                      "run pay"))
    1987             :   {
    1988           0 :     GNUNET_break (0);
    1989           0 :     resume_pay_with_error (pc,
    1990             :                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1991             :                            TALER_EC_GENERIC_DB_START_FAILED,
    1992             :                            NULL);
    1993           0 :     return;
    1994             :   }
    1995             : 
    1996             :   {
    1997             :     enum GNUNET_DB_QueryStatus qs;
    1998             : 
    1999             :     /* Check if some of these coins already succeeded for _this_ contract.  */
    2000           0 :     qs = TMH_db->lookup_deposits (TMH_db->cls,
    2001             :                                   instance_id,
    2002           0 :                                   &pc->h_contract_terms,
    2003             :                                   &check_coin_paid,
    2004             :                                   pc);
    2005           0 :     if (0 > qs)
    2006             :     {
    2007           0 :       TMH_db->rollback (TMH_db->cls);
    2008           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    2009             :       {
    2010           0 :         execute_pay_transaction (pc);
    2011           0 :         return;
    2012             :       }
    2013             :       /* Always report on hard error as well to enable diagnostics */
    2014           0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    2015           0 :       resume_pay_with_error (pc,
    2016             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    2017             :                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    2018             :                              "lookup deposits");
    2019           0 :       return;
    2020             :     }
    2021             :   }
    2022             : 
    2023             : 
    2024             :   {
    2025             :     enum GNUNET_DB_QueryStatus qs;
    2026             : 
    2027             :     /* Check if we refunded some of the coins */
    2028           0 :     qs = TMH_db->lookup_refunds (TMH_db->cls,
    2029             :                                  instance_id,
    2030           0 :                                  &pc->h_contract_terms,
    2031             :                                  &check_coin_refunded,
    2032             :                                  pc);
    2033           0 :     if (0 > qs)
    2034             :     {
    2035           0 :       TMH_db->rollback (TMH_db->cls);
    2036           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    2037             :       {
    2038           0 :         execute_pay_transaction (pc);
    2039           0 :         return;
    2040             :       }
    2041             :       /* Always report on hard error as well to enable diagnostics */
    2042           0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    2043           0 :       resume_pay_with_error (pc,
    2044             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    2045             :                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    2046             :                              "lookup refunds");
    2047           0 :       return;
    2048             :     }
    2049             :   }
    2050             : 
    2051             :   /* Check if there are coins that still need to be processed */
    2052           0 :   if (0 != pc->pending)
    2053             :   {
    2054             :     /* we made no DB changes, so we can just rollback */
    2055           0 :     TMH_db->rollback (TMH_db->cls);
    2056             : 
    2057             :     /* Ok, we need to first go to the network to process more coins.
    2058             :        We that interaction in *tiny* transactions (hence the rollback
    2059             :        above). */
    2060           0 :     start_batch_deposits (pc);
    2061           0 :     return;
    2062             :   }
    2063             : 
    2064             :   /* 0 == pc->pending: all coins processed, let's see if that was enough */
    2065           0 :   if (! check_payment_sufficient (pc))
    2066             :   {
    2067             :     /* check_payment_sufficient() will have queued an error already.
    2068             :        We need to still abort the transaction. */
    2069           0 :     TMH_db->rollback (TMH_db->cls);
    2070           0 :     return;
    2071             :   }
    2072             :   /* Payment succeeded, save in database */
    2073           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2074             :               "Order `%s' (%s) was fully paid\n",
    2075             :               pc->order_id,
    2076             :               GNUNET_h2s (&pc->h_contract_terms.hash));
    2077             :   {
    2078             :     enum GNUNET_DB_QueryStatus qs;
    2079             : 
    2080           0 :     qs = TMH_db->mark_contract_paid (TMH_db->cls,
    2081             :                                      instance_id,
    2082           0 :                                      &pc->h_contract_terms,
    2083           0 :                                      pc->session_id);
    2084           0 :     if (qs < 0)
    2085             :     {
    2086           0 :       TMH_db->rollback (TMH_db->cls);
    2087           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    2088             :       {
    2089           0 :         execute_pay_transaction (pc);
    2090           0 :         return;
    2091             :       }
    2092           0 :       GNUNET_break (0);
    2093           0 :       resume_pay_with_error (pc,
    2094             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    2095             :                              TALER_EC_GENERIC_DB_STORE_FAILED,
    2096             :                              "mark contract paid");
    2097           0 :       return;
    2098             :     }
    2099             :   }
    2100             : 
    2101           0 :   TMH_notify_order_change (hc->instance,
    2102             :                            TMH_OSF_CLAIMED | TMH_OSF_PAID,
    2103             :                            pc->timestamp,
    2104             :                            pc->order_serial);
    2105             : 
    2106             :   {
    2107             :     enum GNUNET_DB_QueryStatus qs;
    2108             : 
    2109             :     /* Now commit! */
    2110           0 :     qs = TMH_db->commit (TMH_db->cls);
    2111           0 :     if (0 > qs)
    2112             :     {
    2113             :       /* commit failed */
    2114           0 :       TMH_db->rollback (TMH_db->cls);
    2115           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    2116             :       {
    2117           0 :         execute_pay_transaction (pc);
    2118           0 :         return;
    2119             :       }
    2120           0 :       GNUNET_break (0);
    2121           0 :       resume_pay_with_error (pc,
    2122             :                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    2123             :                              TALER_EC_GENERIC_DB_COMMIT_FAILED,
    2124             :                              NULL);
    2125           0 :       return;
    2126             :     }
    2127           0 :     trigger_payment_notification (pc);
    2128             :   }
    2129           0 :   generate_success_response (pc);
    2130             : }
    2131             : 
    2132             : 
    2133             : /**
    2134             :  * Try to parse the pay request into the given pay context.
    2135             :  * Schedules an error response in the connection on failure.
    2136             :  *
    2137             :  * @param[in,out] pc context we use to handle the payment
    2138             :  * @return #GNUNET_OK on success,
    2139             :  *         #GNUNET_NO on failure (response was queued with MHD)
    2140             :  *         #GNUNET_SYSERR on hard error (MHD connection must be dropped)
    2141             :  */
    2142             : static enum GNUNET_GenericReturnValue
    2143           0 : parse_pay (struct PayContext *pc)
    2144             : {
    2145           0 :   const char *session_id = NULL;
    2146             :   json_t *coins;
    2147             :   struct GNUNET_JSON_Specification spec[] = {
    2148           0 :     GNUNET_JSON_spec_json ("coins",
    2149             :                            &coins),
    2150           0 :     GNUNET_JSON_spec_mark_optional (
    2151             :       GNUNET_JSON_spec_string ("session_id",
    2152             :                                &session_id),
    2153             :       NULL),
    2154           0 :     GNUNET_JSON_spec_end ()
    2155             :   };
    2156             : 
    2157             :   {
    2158             :     enum GNUNET_GenericReturnValue res;
    2159             : 
    2160           0 :     res = TALER_MHD_parse_json_data (pc->connection,
    2161           0 :                                      pc->hc->request_body,
    2162             :                                      spec);
    2163           0 :     if (GNUNET_YES != res)
    2164             :     {
    2165           0 :       GNUNET_break_op (0);
    2166           0 :       return res;
    2167             :     }
    2168             :   }
    2169             : 
    2170             :   /* copy session ID (if set) */
    2171           0 :   if (NULL != session_id)
    2172             :   {
    2173           0 :     pc->session_id = GNUNET_strdup (session_id);
    2174             :   }
    2175             :   else
    2176             :   {
    2177             :     /* use empty string as default if client didn't specify it */
    2178           0 :     pc->session_id = GNUNET_strdup ("");
    2179             :   }
    2180             : 
    2181           0 :   if (! json_is_array (coins))
    2182             :   {
    2183           0 :     GNUNET_break_op (0);
    2184           0 :     GNUNET_JSON_parse_free (spec);
    2185             :     return (MHD_YES ==
    2186           0 :             TALER_MHD_reply_with_error (pc->connection,
    2187             :                                         MHD_HTTP_BAD_REQUEST,
    2188             :                                         TALER_EC_GENERIC_PARAMETER_MISSING,
    2189             :                                         "'coins' must be an array"))
    2190             :         ? GNUNET_NO
    2191           0 :         : GNUNET_SYSERR;
    2192             :   }
    2193             : 
    2194           0 :   pc->coins_cnt = json_array_size (coins);
    2195           0 :   if (pc->coins_cnt > MAX_COIN_ALLOWED_COINS)
    2196             :   {
    2197           0 :     GNUNET_break_op (0);
    2198           0 :     GNUNET_JSON_parse_free (spec);
    2199             :     return (MHD_YES ==
    2200           0 :             TALER_MHD_reply_with_error (
    2201             :               pc->connection,
    2202             :               MHD_HTTP_BAD_REQUEST,
    2203             :               TALER_EC_GENERIC_PARAMETER_MALFORMED,
    2204             :               "'coins' array too long"))
    2205             :         ? GNUNET_NO
    2206           0 :         : GNUNET_SYSERR;
    2207             :   }
    2208             : 
    2209             :   /* note: 1 coin = 1 deposit confirmation expected */
    2210           0 :   pc->dc = GNUNET_new_array (pc->coins_cnt,
    2211             :                              struct DepositConfirmation);
    2212             : 
    2213             :   /* This loop populates the array 'dc' in 'pc' */
    2214             :   {
    2215             :     unsigned int coins_index;
    2216             :     json_t *coin;
    2217             : 
    2218           0 :     json_array_foreach (coins, coins_index, coin)
    2219             :     {
    2220           0 :       struct DepositConfirmation *dc = &pc->dc[coins_index];
    2221             :       const char *exchange_url;
    2222             :       struct GNUNET_JSON_Specification ispec[] = {
    2223           0 :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
    2224             :                                      &dc->cdd.coin_sig),
    2225           0 :         GNUNET_JSON_spec_fixed_auto ("coin_pub",
    2226             :                                      &dc->cdd.coin_pub),
    2227           0 :         TALER_JSON_spec_denom_sig ("ub_sig",
    2228             :                                    &dc->cdd.denom_sig),
    2229           0 :         GNUNET_JSON_spec_fixed_auto ("h_denom",
    2230             :                                      &dc->cdd.h_denom_pub),
    2231           0 :         TALER_JSON_spec_amount ("contribution",
    2232             :                                 TMH_currency,
    2233             :                                 &dc->cdd.amount),
    2234           0 :         GNUNET_JSON_spec_string ("exchange_url",
    2235             :                                  &exchange_url),
    2236             :         /* if a minimum age was required, the minimum_age_sig and
    2237             :          * age_commitment must be provided */
    2238           0 :         GNUNET_JSON_spec_mark_optional (
    2239           0 :           GNUNET_JSON_spec_fixed_auto ("minimum_age_sig",
    2240             :                                        &dc->minimum_age_sig),
    2241             :           &dc->no_minimum_age_sig),
    2242           0 :         GNUNET_JSON_spec_mark_optional (
    2243             :           TALER_JSON_spec_age_commitment ("age_commitment",
    2244             :                                           &dc->age_commitment),
    2245             :           &dc->no_age_commitment),
    2246             :         /* if minimum age was not required, but coin with age restriction set
    2247             :          * was used, h_age_commitment must be provided. */
    2248           0 :         GNUNET_JSON_spec_mark_optional (
    2249           0 :           GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
    2250             :                                        &dc->cdd.h_age_commitment),
    2251             :           &dc->no_h_age_commitment),
    2252           0 :         GNUNET_JSON_spec_end ()
    2253             :       };
    2254             :       enum GNUNET_GenericReturnValue res;
    2255           0 :       bool have_eg = false;
    2256             : 
    2257           0 :       res = TALER_MHD_parse_json_data (pc->connection,
    2258             :                                        coin,
    2259             :                                        ispec);
    2260           0 :       if (GNUNET_YES != res)
    2261             :       {
    2262           0 :         GNUNET_break_op (0);
    2263           0 :         GNUNET_JSON_parse_free (spec);
    2264           0 :         return res;
    2265             :       }
    2266             : 
    2267           0 :       for (unsigned int j = 0; j<coins_index; j++)
    2268             :       {
    2269           0 :         if (0 ==
    2270           0 :             GNUNET_memcmp (&dc->cdd.coin_pub,
    2271             :                            &pc->dc[j].cdd.coin_pub))
    2272             :         {
    2273           0 :           GNUNET_break_op (0);
    2274             :           return (MHD_YES ==
    2275           0 :                   TALER_MHD_reply_with_error (pc->connection,
    2276             :                                               MHD_HTTP_BAD_REQUEST,
    2277             :                                               TALER_EC_GENERIC_PARAMETER_MALFORMED,
    2278             :                                               "duplicate coin in list"))
    2279             :                 ? GNUNET_NO
    2280           0 :                 : GNUNET_SYSERR;
    2281             :         }
    2282             :       }
    2283             : 
    2284           0 :       dc->exchange_url = GNUNET_strdup (exchange_url);
    2285           0 :       dc->index = coins_index;
    2286           0 :       dc->pc = pc;
    2287             : 
    2288           0 :       if (0 !=
    2289           0 :           strcasecmp (dc->cdd.amount.currency,
    2290             :                       TMH_currency))
    2291             :       {
    2292           0 :         GNUNET_break_op (0);
    2293           0 :         GNUNET_JSON_parse_free (spec);
    2294             :         return (MHD_YES ==
    2295           0 :                 TALER_MHD_reply_with_error (pc->connection,
    2296             :                                             MHD_HTTP_CONFLICT,
    2297             :                                             TALER_EC_GENERIC_CURRENCY_MISMATCH,
    2298             :                                             TMH_currency))
    2299             :               ? GNUNET_NO
    2300           0 :               : GNUNET_SYSERR;
    2301             :       }
    2302             : 
    2303             :       /* Check the consistency of the (potential) age restriction
    2304             :        * information. */
    2305           0 :       if (dc->no_age_commitment != dc->no_minimum_age_sig)
    2306             :       {
    2307           0 :         GNUNET_break_op (0);
    2308           0 :         GNUNET_JSON_parse_free (spec);
    2309             :         return (MHD_YES ==
    2310           0 :                 TALER_MHD_reply_with_error (
    2311             :                   pc->connection,
    2312             :                   MHD_HTTP_BAD_REQUEST,
    2313             :                   TALER_EC_GENERIC_PARAMETER_MALFORMED,
    2314             :                   "inconsistent: 'age_commitment' vs. 'minimum_age_sig'"
    2315             :                   )
    2316             :                 )
    2317             :               ? GNUNET_NO
    2318           0 :               : GNUNET_SYSERR;
    2319             :       }
    2320             : 
    2321             :       /* Setup exchange group */
    2322           0 :       for (unsigned int i = 0; i<pc->num_exchanges; i++)
    2323             :       {
    2324           0 :         if (0 ==
    2325           0 :             strcmp (pc->egs[i]->exchange_url,
    2326           0 :                     GNUNET_strdup (exchange_url)))
    2327             :         {
    2328           0 :           have_eg = true;
    2329           0 :           break;
    2330             :         }
    2331             :       }
    2332           0 :       if (! have_eg)
    2333             :       {
    2334             :         struct ExchangeGroup *eg;
    2335             : 
    2336           0 :         eg = GNUNET_new (struct ExchangeGroup);
    2337           0 :         eg->pc = pc;
    2338           0 :         eg->exchange_url = dc->exchange_url;
    2339           0 :         GNUNET_array_append (pc->egs,
    2340             :                              pc->num_exchanges,
    2341             :                              eg);
    2342             :       }
    2343             :     }
    2344             :   }
    2345           0 :   GNUNET_JSON_parse_free (spec);
    2346           0 :   return GNUNET_OK;
    2347             : }
    2348             : 
    2349             : 
    2350             : /**
    2351             :  * Function called with information about a coin that was deposited.
    2352             :  * Checks if this coin is in our list of deposits as well.
    2353             :  *
    2354             :  * @param cls closure with our `struct PayContext *`
    2355             :  * @param deposit_serial which deposit operation is this about
    2356             :  * @param exchange_url URL of the exchange that issued the coin
    2357             :  * @param amount_with_fee amount the exchange will deposit for this coin
    2358             :  * @param deposit_fee fee the exchange will charge for this coin
    2359             :  * @param h_wire hash of merchant's wire details
    2360             :  * @param coin_pub public key of the coin
    2361             :  */
    2362             : static void
    2363           0 : deposit_paid_check (
    2364             :   void *cls,
    2365             :   uint64_t deposit_serial,
    2366             :   const char *exchange_url,
    2367             :   const struct TALER_MerchantWireHashP *h_wire,
    2368             :   const struct TALER_Amount *amount_with_fee,
    2369             :   const struct TALER_Amount *deposit_fee,
    2370             :   const struct TALER_CoinSpendPublicKeyP *coin_pub)
    2371             : {
    2372           0 :   struct PayContext *pc = cls;
    2373             : 
    2374           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    2375             :   {
    2376           0 :     struct DepositConfirmation *dci = &pc->dc[i];
    2377             : 
    2378           0 :     if ( (0 ==
    2379           0 :           GNUNET_memcmp (&dci->cdd.coin_pub,
    2380           0 :                          coin_pub)) &&
    2381             :          (0 ==
    2382           0 :           strcmp (dci->exchange_url,
    2383           0 :                   exchange_url)) &&
    2384             :          (0 ==
    2385           0 :           TALER_amount_cmp (&dci->cdd.amount,
    2386             :                             amount_with_fee)) )
    2387             :     {
    2388           0 :       dci->matched_in_db = true;
    2389           0 :       break;
    2390             :     }
    2391             :   }
    2392           0 : }
    2393             : 
    2394             : 
    2395             : /**
    2396             :  * Handle case where contract was already paid. Either decides
    2397             :  * the payment is idempotent, or refunds the excess payment.
    2398             :  *
    2399             :  * @param[in,out] pc context we use to handle the payment
    2400             :  * @return #GNUNET_NO if response was queued with MHD
    2401             :  *         #GNUNET_SYSERR on hard error (MHD connection must be dropped)
    2402             :  */
    2403             : static enum GNUNET_GenericReturnValue
    2404           0 : handle_contract_paid (struct PayContext *pc)
    2405             : {
    2406             :   enum GNUNET_DB_QueryStatus qs;
    2407           0 :   bool unmatched = false;
    2408             :   json_t *refunds;
    2409             : 
    2410           0 :   qs = TMH_db->lookup_deposits_by_order (TMH_db->cls,
    2411             :                                          pc->order_serial,
    2412             :                                          &deposit_paid_check,
    2413             :                                          pc);
    2414           0 :   if (qs <= 0)
    2415             :   {
    2416           0 :     GNUNET_break (0);
    2417             :     return (MHD_YES ==
    2418           0 :             TALER_MHD_reply_with_error (pc->connection,
    2419             :                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    2420             :                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
    2421             :                                         "lookup_deposits_by_order"))
    2422             :        ? GNUNET_NO
    2423           0 :        : GNUNET_SYSERR;
    2424             :   }
    2425           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    2426             :   {
    2427           0 :     struct DepositConfirmation *dci = &pc->dc[i];
    2428             : 
    2429           0 :     if (! dci->matched_in_db)
    2430           0 :       unmatched = true;
    2431             :   }
    2432           0 :   if (! unmatched)
    2433             :   {
    2434             :     /* Everything fine, idempotent request */
    2435             :     struct GNUNET_CRYPTO_EddsaSignature sig;
    2436             : 
    2437           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2438             :                 "Idempotent pay request for order `%s', signing again\n",
    2439             :                 pc->order_id);
    2440           0 :     TALER_merchant_pay_sign (&pc->h_contract_terms,
    2441           0 :                              &pc->hc->instance->merchant_priv,
    2442             :                              &sig);
    2443             :     return (MHD_YES ==
    2444           0 :             TALER_MHD_REPLY_JSON_PACK (
    2445             :               pc->connection,
    2446             :               MHD_HTTP_OK,
    2447             :               GNUNET_JSON_pack_data_auto ("sig",
    2448             :                                           &sig)))
    2449             :        ? GNUNET_NO
    2450           0 :        : GNUNET_SYSERR;
    2451             :   }
    2452             :   /* Conflict, double-payment detected! */
    2453           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2454             :               "Client attempted to pay extra for already paid order `%s'\n",
    2455             :               pc->order_id);
    2456           0 :   refunds = json_array ();
    2457           0 :   GNUNET_assert (NULL != refunds);
    2458           0 :   for (unsigned int i = 0; i<pc->coins_cnt; i++)
    2459             :   {
    2460           0 :     struct DepositConfirmation *dci = &pc->dc[i];
    2461             :     struct TALER_MerchantSignatureP merchant_sig;
    2462             : 
    2463           0 :     if (dci->matched_in_db)
    2464           0 :       continue;
    2465           0 :     TALER_merchant_refund_sign (&dci->cdd.coin_pub,
    2466           0 :                                 &pc->h_contract_terms,
    2467             :                                 0, /* rtransaction id */
    2468           0 :                                 &dci->cdd.amount,
    2469           0 :                                 &pc->hc->instance->merchant_priv,
    2470             :                                 &merchant_sig);
    2471           0 :     GNUNET_assert (
    2472             :       0 ==
    2473             :       json_array_append_new (
    2474             :         refunds,
    2475             :         GNUNET_JSON_PACK (
    2476             :           GNUNET_JSON_pack_data_auto (
    2477             :             "coin_pub",
    2478             :             &dci->cdd.coin_pub),
    2479             :           GNUNET_JSON_pack_data_auto (
    2480             :             "merchant_sig",
    2481             :             &merchant_sig),
    2482             :           TALER_JSON_pack_amount ("amount",
    2483             :                                   &dci->cdd.amount),
    2484             :           GNUNET_JSON_pack_uint64 ("rtransaction_id",
    2485             :                                    0))));
    2486             :   }
    2487             :   return (MHD_YES ==
    2488           0 :           TALER_MHD_REPLY_JSON_PACK (
    2489             :             pc->connection,
    2490             :             MHD_HTTP_CONFLICT,
    2491             :             TALER_MHD_PACK_EC (
    2492             :               TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_ALREADY_PAID),
    2493             :             GNUNET_JSON_pack_array_steal ("refunds",
    2494             :                                           refunds)))
    2495             :        ? GNUNET_NO
    2496           0 :        : GNUNET_SYSERR;
    2497             : }
    2498             : 
    2499             : 
    2500             : /**
    2501             :  * Check the database state for the given order. * Schedules an error response in the connection on failure.
    2502             :  *
    2503             :  * @param pc context we use to handle the payment
    2504             :  * @return #GNUNET_OK on success,
    2505             :  *         #GNUNET_NO on failure (response was queued with MHD)
    2506             :  *         #GNUNET_SYSERR on hard error (MHD connection must be dropped)
    2507             :  */
    2508             : static enum GNUNET_GenericReturnValue
    2509           0 : check_contract (struct PayContext *pc)
    2510             : {
    2511             :   /* obtain contract terms */
    2512             :   enum GNUNET_DB_QueryStatus qs;
    2513           0 :   json_t *contract_terms = NULL;
    2514           0 :   bool paid = false;
    2515             : 
    2516           0 :   qs = TMH_db->lookup_contract_terms (TMH_db->cls,
    2517           0 :                                       pc->hc->instance->settings.id,
    2518             :                                       pc->order_id,
    2519             :                                       &contract_terms,
    2520             :                                       &pc->order_serial,
    2521             :                                       &paid,
    2522             :                                       NULL);
    2523           0 :   if (0 > qs)
    2524             :   {
    2525             :     /* single, read-only SQL statements should never cause
    2526             :        serialization problems */
    2527           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
    2528             :     /* Always report on hard error to enable diagnostics */
    2529           0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    2530             :     return (MHD_YES ==
    2531           0 :             TALER_MHD_reply_with_error (pc->connection,
    2532             :                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    2533             :                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
    2534             :                                         "contract terms"))
    2535             :        ? GNUNET_NO
    2536           0 :        : GNUNET_SYSERR;
    2537             :   }
    2538           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2539             :   {
    2540             :     return (MHD_YES ==
    2541           0 :             TALER_MHD_reply_with_error (pc->connection,
    2542             :                                         MHD_HTTP_NOT_FOUND,
    2543             :                                         TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
    2544             :                                         pc->order_id))
    2545             :        ? GNUNET_NO
    2546           0 :        : GNUNET_SYSERR;
    2547             :   }
    2548             :   /* hash contract (needed later) */
    2549           0 :   if (GNUNET_OK !=
    2550           0 :       TALER_JSON_contract_hash (contract_terms,
    2551             :                                 &pc->h_contract_terms))
    2552             :   {
    2553           0 :     GNUNET_break (0);
    2554           0 :     json_decref (contract_terms);
    2555             :     return (MHD_YES ==
    2556           0 :             TALER_MHD_reply_with_error (pc->connection,
    2557             :                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    2558             :                                         TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
    2559             :                                         NULL))
    2560             :        ? GNUNET_NO
    2561           0 :        : GNUNET_SYSERR;
    2562             :   }
    2563           0 :   if (paid)
    2564             :   {
    2565           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2566             :                 "Order `%s' paid, checking for double-payment\n",
    2567             :                 pc->order_id);
    2568           0 :     return handle_contract_paid (pc);
    2569             :   }
    2570           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2571             :               "Handling payment for order `%s' with contract hash `%s'\n",
    2572             :               pc->order_id,
    2573             :               GNUNET_h2s (&pc->h_contract_terms.hash));
    2574             : 
    2575             :   /* basic sanity check on the contract */
    2576           0 :   if (NULL == json_object_get (contract_terms,
    2577             :                                "merchant"))
    2578             :   {
    2579             :     /* invalid contract */
    2580           0 :     GNUNET_break (0);
    2581           0 :     json_decref (contract_terms);
    2582             :     return (MHD_YES ==
    2583           0 :             TALER_MHD_reply_with_error (pc->connection,
    2584             :                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    2585             :                                         TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_MERCHANT_FIELD_MISSING,
    2586             :                                         NULL))
    2587             :        ? GNUNET_NO
    2588           0 :        : GNUNET_SYSERR;
    2589             :   }
    2590             : 
    2591             :   /* Get details from contract and check fundamentals */
    2592             :   {
    2593           0 :     const char *fulfillment_url = NULL;
    2594             :     struct GNUNET_JSON_Specification espec[] = {
    2595           0 :       TALER_JSON_spec_amount ("amount",
    2596             :                               TMH_currency,
    2597             :                               &pc->amount),
    2598           0 :       GNUNET_JSON_spec_mark_optional (
    2599             :         GNUNET_JSON_spec_string ("fulfillment_url",
    2600             :                                  &fulfillment_url),
    2601             :         NULL),
    2602           0 :       TALER_JSON_spec_amount ("max_fee",
    2603             :                               TMH_currency,
    2604             :                               &pc->max_fee),
    2605           0 :       TALER_JSON_spec_amount ("max_wire_fee",
    2606             :                               TMH_currency,
    2607             :                               &pc->max_wire_fee),
    2608           0 :       GNUNET_JSON_spec_uint32 ("wire_fee_amortization",
    2609             :                                &pc->wire_fee_amortization),
    2610           0 :       GNUNET_JSON_spec_timestamp ("timestamp",
    2611             :                                   &pc->timestamp),
    2612           0 :       GNUNET_JSON_spec_timestamp ("refund_deadline",
    2613             :                                   &pc->refund_deadline),
    2614           0 :       GNUNET_JSON_spec_timestamp ("pay_deadline",
    2615             :                                   &pc->pay_deadline),
    2616           0 :       GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
    2617             :                                   &pc->wire_transfer_deadline),
    2618           0 :       GNUNET_JSON_spec_fixed_auto ("h_wire",
    2619             :                                    &pc->h_wire),
    2620           0 :       GNUNET_JSON_spec_mark_optional (
    2621             :         GNUNET_JSON_spec_uint32 ("minimum_age",
    2622           0 :                                  &pc->minimum_age),
    2623             :         NULL),
    2624           0 :       GNUNET_JSON_spec_end ()
    2625             :     };
    2626             :     enum GNUNET_GenericReturnValue res;
    2627             : 
    2628           0 :     pc->minimum_age = 0;
    2629           0 :     res = TALER_MHD_parse_internal_json_data (pc->connection,
    2630             :                                               contract_terms,
    2631             :                                               espec);
    2632           0 :     if (NULL != fulfillment_url)
    2633           0 :       pc->fulfillment_url = GNUNET_strdup (fulfillment_url);
    2634           0 :     json_decref (contract_terms);
    2635           0 :     if (GNUNET_YES != res)
    2636             :     {
    2637           0 :       GNUNET_break (0);
    2638           0 :       return res;
    2639             :     }
    2640             :   }
    2641             : 
    2642           0 :   if (GNUNET_TIME_timestamp_cmp (pc->wire_transfer_deadline,
    2643             :                                  <,
    2644             :                                  pc->refund_deadline))
    2645             :   {
    2646             :     /* This should already have been checked when creating the order! */
    2647           0 :     GNUNET_break (0);
    2648           0 :     return TALER_MHD_reply_with_error (pc->connection,
    2649             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    2650             :                                        TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE,
    2651             :                                        NULL);
    2652             :   }
    2653           0 :   if (GNUNET_TIME_absolute_is_past (pc->pay_deadline.abs_time))
    2654             :   {
    2655             :     /* too late */
    2656             :     return (MHD_YES ==
    2657           0 :             TALER_MHD_reply_with_error (pc->connection,
    2658             :                                         MHD_HTTP_GONE,
    2659             :                                         TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED,
    2660             :                                         NULL))
    2661             :        ? GNUNET_NO
    2662           0 :        : GNUNET_SYSERR;
    2663             :   }
    2664             : 
    2665             :   /* Make sure wire method (still) exists for this instance */
    2666             :   {
    2667             :     struct TMH_WireMethod *wm;
    2668             : 
    2669           0 :     wm = pc->hc->instance->wm_head;
    2670           0 :     while (0 != GNUNET_memcmp (&pc->h_wire,
    2671             :                                &wm->h_wire))
    2672           0 :       wm = wm->next;
    2673           0 :     if (NULL == wm)
    2674             :     {
    2675           0 :       GNUNET_break (0);
    2676           0 :       return TALER_MHD_reply_with_error (pc->connection,
    2677             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    2678             :                                          TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_HASH_UNKNOWN,
    2679             :                                          NULL);
    2680             :     }
    2681           0 :     pc->wm = wm;
    2682             :   }
    2683             : 
    2684           0 :   return GNUNET_OK;
    2685             : }
    2686             : 
    2687             : 
    2688             : /**
    2689             :  * Handle a timeout for the processing of the pay request.
    2690             :  *
    2691             :  * @param cls our `struct PayContext`
    2692             :  */
    2693             : static void
    2694           0 : handle_pay_timeout (void *cls)
    2695             : {
    2696           0 :   struct PayContext *pc = cls;
    2697             : 
    2698           0 :   pc->timeout_task = NULL;
    2699           0 :   GNUNET_assert (GNUNET_YES == pc->suspended);
    2700           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2701             :               "Resuming pay with error after timeout\n");
    2702           0 :   resume_pay_with_error (pc,
    2703             :                          MHD_HTTP_GATEWAY_TIMEOUT,
    2704             :                          TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
    2705             :                          NULL);
    2706           0 : }
    2707             : 
    2708             : 
    2709             : MHD_RESULT
    2710           0 : TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
    2711             :                         struct MHD_Connection *connection,
    2712             :                         struct TMH_HandlerContext *hc)
    2713             : {
    2714           0 :   struct PayContext *pc = hc->ctx;
    2715             :   enum GNUNET_GenericReturnValue ret;
    2716             : 
    2717           0 :   GNUNET_assert (NULL != hc->infix);
    2718           0 :   if (NULL == pc)
    2719             :   {
    2720           0 :     pc = GNUNET_new (struct PayContext);
    2721           0 :     GNUNET_CONTAINER_DLL_insert (pc_head,
    2722             :                                  pc_tail,
    2723             :                                  pc);
    2724           0 :     pc->connection = connection;
    2725           0 :     pc->hc = hc;
    2726           0 :     pc->order_id = hc->infix;
    2727           0 :     hc->ctx = pc;
    2728           0 :     hc->cc = &pay_context_cleanup;
    2729           0 :     ret = parse_pay (pc);
    2730           0 :     if (GNUNET_OK != ret)
    2731             :       return (GNUNET_NO == ret)
    2732             :        ? MHD_YES
    2733           0 :        : MHD_NO;
    2734             :   }
    2735           0 :   if (GNUNET_SYSERR == pc->suspended)
    2736           0 :     return MHD_NO; /* during shutdown, we don't generate any more replies */
    2737           0 :   GNUNET_assert (GNUNET_NO == pc->suspended);
    2738           0 :   if (0 != pc->response_code)
    2739             :   {
    2740             :     /* We are *done* processing the request, just queue the response (!) */
    2741           0 :     if (UINT_MAX == pc->response_code)
    2742             :     {
    2743           0 :       GNUNET_break (0);
    2744           0 :       return MHD_NO; /* hard error */
    2745             :     }
    2746           0 :     return MHD_queue_response (connection,
    2747             :                                pc->response_code,
    2748             :                                pc->response);
    2749             :   }
    2750           0 :   ret = check_contract (pc);
    2751           0 :   if (GNUNET_OK != ret)
    2752             :     return (GNUNET_NO == ret)
    2753             :       ? MHD_YES
    2754           0 :       : MHD_NO;
    2755             : 
    2756             :   /* Payment not finished, suspend while we interact with the exchange */
    2757           0 :   MHD_suspend_connection (connection);
    2758           0 :   pc->suspended = GNUNET_YES;
    2759           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2760             :               "Suspending pay handling while working with the exchange\n");
    2761           0 :   GNUNET_assert (NULL == pc->timeout_task);
    2762             :   pc->timeout_task
    2763           0 :     = GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->coins_cnt),
    2764             :                                     &handle_pay_timeout,
    2765             :                                     pc);
    2766           0 :   GNUNET_assert (NULL != pc->wm);
    2767           0 :   execute_pay_transaction (pc);
    2768           0 :   return MHD_YES;
    2769             : }
    2770             : 
    2771             : 
    2772             : /* end of taler-merchant-httpd_post-orders-ID-pay.c */

Generated by: LCOV version 1.14