LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-private-orders-ORDER_ID-refund.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 68.0 % 175 119
Test Date: 2026-09-04 23:42:01 Functions: 100.0 % 4 4

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2014-2024 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Affero General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file src/backend/taler-merchant-httpd_post-private-orders-ORDER_ID-refund.c
      18              :  * @brief Handle request to increase the refund for an order
      19              :  * @author Marcello Stanisci
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"
      23              : #include <jansson.h>
      24              : #include <taler/taler_dbevents.h>
      25              : #include <taler/taler_signatures.h>
      26              : #include <taler/taler_json_lib.h>
      27              : #include "taler-merchant-httpd_exchanges.h"
      28              : #include "taler-merchant-httpd_post-private-orders-ORDER_ID-refund.h"
      29              : #include "taler-merchant-httpd_get-private-orders.h"
      30              : #include "taler-merchant-httpd_helper.h"
      31              : #include "taler-merchant-httpd_get-exchanges.h"
      32              : #include "merchant-database/do_increase_refund.h"
      33              : #include "merchant-database/get_contract_terms_status.h"
      34              : #include "merchant-database/get_external_refunds_total.h"
      35              : #include "merchant-database/get_order_summary.h"
      36              : #include "merchant-database/start.h"
      37              : #include "merchant-database/preflight.h"
      38              : #include "merchant-database/event_notify.h"
      39              : 
      40              : /**
      41              :  * How often do we retry the non-trivial refund INSERT database
      42              :  * transaction?
      43              :  */
      44              : #define MAX_RETRIES 5
      45              : 
      46              : 
      47              : /**
      48              :  * Use database to notify other clients about the
      49              :  * @a order_id being refunded
      50              :  *
      51              :  * @param hc handler context we operate in
      52              :  * @param amount the (total) refunded amount
      53              :  */
      54              : static void
      55           10 : trigger_refund_notification (
      56              :   struct TMH_HandlerContext *hc,
      57              :   const struct TALER_Amount *amount)
      58              : {
      59              :   {
      60              :     const char *as;
      61           10 :     struct TMH_OrderRefundEventP refund_eh = {
      62           10 :       .header.size = htons (sizeof (refund_eh)),
      63           10 :       .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_REFUND),
      64           10 :       .merchant_pub = hc->instance->merchant_pub
      65              :     };
      66              : 
      67              :     /* Resume clients that may wait for this refund */
      68           10 :     as = TALER_amount2s (amount);
      69           10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
      70              :                 "Awakening clients on %s waiting for refund of no more than %s\n",
      71              :                 hc->infix,
      72              :                 as);
      73           10 :     GNUNET_CRYPTO_hash (hc->infix,
      74           10 :                         strlen (hc->infix),
      75              :                         &refund_eh.h_order_id);
      76           10 :     TALER_MERCHANTDB_event_notify (TMH_db,
      77              :                                    &refund_eh.header,
      78              :                                    as,
      79              :                                    strlen (as));
      80              :   }
      81              :   {
      82           10 :     struct TMH_OrderPayEventP pay_eh = {
      83           10 :       .header.size = htons (sizeof (pay_eh)),
      84           10 :       .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED),
      85           10 :       .merchant_pub = hc->instance->merchant_pub
      86              :     };
      87              : 
      88           10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
      89              :                 "Notifying clients about status change of order %s\n",
      90              :                 hc->infix);
      91           10 :     GNUNET_CRYPTO_hash (hc->infix,
      92           10 :                         strlen (hc->infix),
      93              :                         &pay_eh.h_order_id);
      94           10 :     TALER_MERCHANTDB_event_notify (TMH_db,
      95              :                                    &pay_eh.header,
      96              :                                    NULL,
      97              :                                    0);
      98              :   }
      99           10 : }
     100              : 
     101              : 
     102              : /**
     103              :  * Make a taler://refund URI
     104              :  *
     105              :  * @param connection MHD connection to take host and path from
     106              :  * @param instance_id merchant's instance ID, must not be NULL
     107              :  * @param order_id order ID to show a refund for, must not be NULL
     108              :  * @returns the URI, must be freed with #GNUNET_free
     109              :  */
     110              : static char *
     111           10 : make_taler_refund_uri (struct MHD_Connection *connection,
     112              :                        const char *instance_id,
     113              :                        const char *order_id)
     114              : {
     115              :   struct GNUNET_Buffer buf;
     116              : 
     117           10 :   GNUNET_assert (NULL != instance_id);
     118           10 :   GNUNET_assert (NULL != order_id);
     119           10 :   if (GNUNET_OK !=
     120           10 :       TMH_taler_uri_by_connection (connection,
     121              :                                    "refund",
     122              :                                    instance_id,
     123              :                                    &buf))
     124              :   {
     125            0 :     GNUNET_break (0);
     126            0 :     return NULL;
     127              :   }
     128           10 :   GNUNET_buffer_write_path (&buf,
     129              :                             order_id);
     130           10 :   GNUNET_buffer_write_path (&buf,
     131              :                             ""); /* Trailing slash */
     132           10 :   return GNUNET_buffer_reap_str (&buf);
     133              : }
     134              : 
     135              : 
     136              : /**
     137              :  * Wrapper around #TMH_EXCHANGES_get_limit() that
     138              :  * determines the refund limit for a given @a exchange_url
     139              :  *
     140              :  * @param cls unused
     141              :  * @param exchange_url base URL of the exchange to get
     142              :  *   the refund limit for
     143              :  * @param[in,out] amount lowered to the maximum refund
     144              :  *   allowed at the exchange
     145              :  */
     146              : static void
     147           10 : get_refund_limit (void *cls,
     148              :                   const char *exchange_url,
     149              :                   struct TALER_Amount *amount)
     150              : {
     151              :   (void) cls;
     152           10 :   TMH_EXCHANGES_get_limit (exchange_url,
     153              :                            TALER_KYCLOGIC_KYC_TRIGGER_REFUND,
     154              :                            amount);
     155           10 : }
     156              : 
     157              : 
     158              : /**
     159              :  * Handle request for increasing the refund associated with
     160              :  * a contract.
     161              :  *
     162              :  * @param rh context of the handler
     163              :  * @param connection the MHD connection to handle
     164              :  * @param[in,out] hc context with further information about the request
     165              :  * @return MHD result code
     166              :  */
     167              : enum MHD_Result
     168           16 : TMH_private_post_orders_ID_refund (
     169              :   const struct TMH_RequestHandler *rh,
     170              :   struct MHD_Connection *connection,
     171              :   struct TMH_HandlerContext *hc)
     172              : {
     173              :   struct TALER_Amount refund;
     174              :   const char *reason;
     175              :   struct GNUNET_JSON_Specification spec[] = {
     176           16 :     TALER_JSON_spec_amount_any ("refund",
     177              :                                 &refund),
     178           16 :     GNUNET_JSON_spec_string ("reason",
     179              :                              &reason),
     180           16 :     GNUNET_JSON_spec_end ()
     181              :   };
     182              :   enum TALER_MERCHANTDB_RefundStatus rs;
     183              :   struct TALER_PrivateContractHashP h_contract;
     184              :   json_t *contract_terms;
     185              :   struct GNUNET_TIME_Timestamp timestamp;
     186              :   struct TALER_Amount order_total;
     187              :   int16_t choice_index;
     188              : 
     189              :   {
     190              :     enum GNUNET_GenericReturnValue res;
     191              : 
     192           16 :     res = TALER_MHD_parse_json_data (connection,
     193           16 :                                      hc->request_body,
     194              :                                      spec);
     195           16 :     if (GNUNET_OK != res)
     196              :     {
     197              :       return (GNUNET_NO == res)
     198              :              ? MHD_YES
     199            0 :              : MHD_NO;
     200              :     }
     201              :   }
     202              : 
     203              :   {
     204              :     enum GNUNET_DB_QueryStatus qs;
     205              :     uint64_t order_serial;
     206              :     bool paid;
     207              :     bool wired;
     208              :     bool session_matches;
     209              :     struct GNUNET_TIME_Timestamp refund_deadline;
     210              :     struct GNUNET_TIME_Timestamp wire_deadline;
     211              : 
     212           16 :     qs = TALER_MERCHANTDB_get_contract_terms_status (TMH_db,
     213           16 :                                                      hc->instance->settings.id,
     214           16 :                                                      hc->infix,
     215              :                                                      NULL,
     216              :                                                      &contract_terms,
     217              :                                                      &order_serial,
     218              :                                                      &paid,
     219              :                                                      &wired,
     220              :                                                      &session_matches,
     221              :                                                      NULL,
     222              :                                                      &choice_index);
     223           16 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     224              :     {
     225            2 :       if (qs < 0)
     226              :       {
     227            0 :         GNUNET_break (0);
     228            2 :         return TALER_MHD_reply_with_error (
     229              :           connection,
     230              :           MHD_HTTP_INTERNAL_SERVER_ERROR,
     231              :           TALER_EC_GENERIC_DB_FETCH_FAILED,
     232              :           "get_contract_terms");
     233              :       }
     234            2 :       return TALER_MHD_reply_with_error (
     235              :         connection,
     236              :         MHD_HTTP_NOT_FOUND,
     237              :         TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
     238            2 :         hc->infix);
     239              :     }
     240           14 :     if (GNUNET_OK !=
     241           14 :         TALER_JSON_contract_hash (contract_terms,
     242              :                                   &h_contract))
     243              :     {
     244            0 :       GNUNET_break (0);
     245            0 :       json_decref (contract_terms);
     246            0 :       return TALER_MHD_reply_with_error (
     247              :         connection,
     248              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     249              :         TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
     250              :         "Could not hash contract terms");
     251              :     }
     252              :     {
     253              :       struct GNUNET_JSON_Specification cspec[] = {
     254           14 :         GNUNET_JSON_spec_timestamp ("refund_deadline",
     255              :                                     &refund_deadline),
     256           14 :         GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
     257              :                                     &wire_deadline),
     258           14 :         GNUNET_JSON_spec_timestamp ("timestamp",
     259              :                                     &timestamp),
     260           14 :         GNUNET_JSON_spec_end ()
     261              :       };
     262              : 
     263           14 :       if (GNUNET_YES !=
     264           14 :           GNUNET_JSON_parse (contract_terms,
     265              :                              cspec,
     266              :                              NULL, NULL))
     267              :       {
     268            0 :         GNUNET_break (0);
     269            0 :         json_decref (contract_terms);
     270            0 :         return TALER_MHD_reply_with_error (
     271              :           connection,
     272              :           MHD_HTTP_INTERNAL_SERVER_ERROR,
     273              :           TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
     274              :           "mandatory fields missing");
     275              :       }
     276           14 :       if (GNUNET_TIME_timestamp_cmp (timestamp,
     277              :                                      ==,
     278              :                                      refund_deadline))
     279              :       {
     280              :         /* refund was never allowed, so we should refuse hard */
     281            0 :         json_decref (contract_terms);
     282            0 :         return TALER_MHD_reply_with_error (
     283              :           connection,
     284              :           MHD_HTTP_FORBIDDEN,
     285              :           TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_NOT_ALLOWED_BY_CONTRACT,
     286              :           NULL);
     287              :       }
     288           14 :       if (GNUNET_TIME_absolute_is_past (refund_deadline.abs_time))
     289              :       {
     290              :         /* it is too late for refunds */
     291              :         /* NOTE: We MAY still be lucky that the exchange did not yet
     292              :            wire the funds, so we will try to give the refund anyway */
     293              :       }
     294           14 :       if (GNUNET_TIME_absolute_is_past (wire_deadline.abs_time))
     295              :       {
     296              :         /* it is *really* too late for refunds */
     297            0 :         return TALER_MHD_reply_with_error (
     298              :           connection,
     299              :           MHD_HTTP_GONE,
     300              :           TALER_EC_MERCHANT_PRIVATE_POST_REFUND_AFTER_WIRE_DEADLINE,
     301              :           NULL);
     302              :       }
     303              :     }
     304              :   }
     305              : 
     306           14 :   TALER_MERCHANTDB_preflight (TMH_db);
     307           14 :   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     308              :   {
     309           14 :     struct TALER_Amount external_total = {0};
     310           14 :     bool external_mismatch = false;
     311              : 
     312           14 :     if (GNUNET_OK !=
     313           14 :         TALER_MERCHANTDB_start (TMH_db,
     314              :                                 "increase refund"))
     315              :     {
     316            0 :       GNUNET_break (0);
     317            0 :       json_decref (contract_terms);
     318            2 :       return TALER_MHD_reply_with_error (connection,
     319              :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     320              :                                          TALER_EC_GENERIC_DB_START_FAILED,
     321              :                                          NULL);
     322              :     }
     323              :     {
     324              :       enum GNUNET_DB_QueryStatus eqs;
     325              : 
     326           14 :       eqs = TALER_MERCHANTDB_get_external_refunds_total (
     327              :         TMH_db,
     328           14 :         hc->instance->settings.id,
     329           14 :         hc->infix,
     330              :         &external_total,
     331              :         &external_mismatch);
     332           14 :       if (0 > eqs)
     333              :       {
     334            0 :         TALER_MERCHANTDB_rollback (TMH_db);
     335            0 :         if (GNUNET_DB_STATUS_SOFT_ERROR == eqs)
     336            0 :           continue;
     337            0 :         json_decref (contract_terms);
     338            0 :         return TALER_MHD_reply_with_error (connection,
     339              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     340              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     341              :                                            "select external refunds");
     342              :       }
     343              :     }
     344           14 :     if (external_mismatch)
     345              :     {
     346            0 :       GNUNET_break (0);
     347            0 :       TALER_MERCHANTDB_rollback (TMH_db);
     348            0 :       json_decref (contract_terms);
     349            0 :       return TALER_MHD_reply_with_error (
     350              :         connection,
     351              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     352              :         TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     353              :         "external refund currencies do not match");
     354              :     }
     355           14 :     if (TALER_amount_is_valid (&external_total))
     356              :     {
     357              :       struct TALER_Amount remaining;
     358              : 
     359              :       /* External refunds only exist for paid orders, so at this point a
     360              :          choice was selected and the order total is well-defined. */
     361            2 :       if (GNUNET_OK !=
     362            2 :           TMH_compute_order_total (contract_terms,
     363              :                                    choice_index,
     364              :                                    &order_total))
     365              :       {
     366            0 :         TALER_MERCHANTDB_rollback (TMH_db);
     367            0 :         json_decref (contract_terms);
     368            2 :         return TALER_MHD_reply_with_error (
     369              :           connection,
     370              :           MHD_HTTP_INTERNAL_SERVER_ERROR,
     371              :           TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
     372              :           "amount");
     373              :       }
     374            2 :       if (0 >
     375            2 :           TALER_amount_subtract (&remaining,
     376              :                                  &order_total,
     377              :                                  &external_total))
     378              :       {
     379            0 :         GNUNET_break (0);
     380            0 :         TALER_MERCHANTDB_rollback (TMH_db);
     381            0 :         json_decref (contract_terms);
     382            0 :         return TALER_MHD_reply_with_error (
     383              :           connection,
     384              :           MHD_HTTP_INTERNAL_SERVER_ERROR,
     385              :           TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     386              :           "external refunds exceed order total");
     387              :       }
     388            2 :       if (1 ==
     389            2 :           TALER_amount_cmp (&refund,
     390              :                             &remaining))
     391              :       {
     392            2 :         TALER_MERCHANTDB_rollback (TMH_db);
     393            2 :         GNUNET_log (
     394              :           GNUNET_ERROR_TYPE_WARNING,
     395              :           "Refusing Taler refund of %s that would exceed remaining refundable amount of %s\n",
     396              :           TALER_amount2s (&refund),
     397              :           TALER_amount2s (&remaining));
     398            2 :         json_decref (contract_terms);
     399            2 :         return TALER_MHD_reply_with_error (
     400              :           connection,
     401              :           MHD_HTTP_CONFLICT,
     402              :           TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_INCONSISTENT_AMOUNT,
     403              :           "Taler and external refunds would exceed the order total");
     404              :       }
     405              :     }
     406           12 :     rs = TALER_MERCHANTDB_do_increase_refund (TMH_db,
     407           12 :                                               hc->instance->settings.id,
     408           12 :                                               hc->infix,
     409              :                                               &refund,
     410              :                                               &get_refund_limit,
     411              :                                               NULL,
     412              :                                               reason);
     413           12 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     414              :                 "increase refund returned %d\n",
     415              :                 rs);
     416           12 :     if (TALER_MERCHANTDB_RS_SUCCESS != rs)
     417            2 :       TALER_MERCHANTDB_rollback (TMH_db);
     418           12 :     if (TALER_MERCHANTDB_RS_SOFT_ERROR == rs)
     419            0 :       continue;
     420           12 :     if (TALER_MERCHANTDB_RS_SUCCESS == rs)
     421              :     {
     422              :       enum GNUNET_DB_QueryStatus qs;
     423              :       json_t *rargs;
     424              : 
     425           10 :       rargs = GNUNET_JSON_PACK (
     426              :         GNUNET_JSON_pack_timestamp ("timestamp",
     427              :                                     timestamp),
     428              :         GNUNET_JSON_pack_string ("order_id",
     429              :                                  hc->infix),
     430              :         GNUNET_JSON_pack_object_incref ("contract_terms",
     431              :                                         contract_terms),
     432              :         TALER_JSON_pack_amount ("refund_amount",
     433              :                                 &refund),
     434              :         GNUNET_JSON_pack_string ("reason",
     435              :                                  reason)
     436              :         );
     437           10 :       GNUNET_assert (NULL != rargs);
     438           10 :       qs = TMH_trigger_webhook (
     439           10 :         hc->instance->settings.id,
     440              :         "refund",
     441              :         rargs);
     442           10 :       json_decref (rargs);
     443           10 :       switch (qs)
     444              :       {
     445            0 :       case GNUNET_DB_STATUS_HARD_ERROR:
     446            0 :         GNUNET_break (0);
     447            0 :         TALER_MERCHANTDB_rollback (TMH_db);
     448            0 :         rs = TALER_MERCHANTDB_RS_HARD_ERROR;
     449            0 :         break;
     450            0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
     451            0 :         TALER_MERCHANTDB_rollback (TMH_db);
     452            0 :         continue;
     453           10 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     454              :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     455           10 :         qs = TALER_MERCHANTDB_commit (TMH_db);
     456           10 :         break;
     457              :       }
     458           10 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     459              :       {
     460            0 :         GNUNET_break (0);
     461            0 :         rs = TALER_MERCHANTDB_RS_HARD_ERROR;
     462            0 :         break;
     463              :       }
     464           10 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     465            0 :         continue;
     466           10 :       trigger_refund_notification (hc,
     467              :                                    &refund);
     468              :     }
     469           12 :     break;
     470              :   } /* retries loop */
     471           12 :   json_decref (contract_terms);
     472              : 
     473           12 :   switch (rs)
     474              :   {
     475            0 :   case TALER_MERCHANTDB_RS_LEGAL_FAILURE:
     476            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     477              :                 "Refund amount %s exceeded legal limits of the exchanges involved\n",
     478              :                 TALER_amount2s (&refund));
     479            0 :     return TALER_MHD_reply_with_error (
     480              :       connection,
     481              :       MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
     482              :       TALER_EC_MERCHANT_POST_ORDERS_ID_REFUND_EXCHANGE_TRANSACTION_LIMIT_VIOLATION,
     483              :       NULL);
     484            0 :   case TALER_MERCHANTDB_RS_BAD_CURRENCY:
     485            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     486              :                 "Refund amount %s is not in the currency of the original payment\n",
     487              :                 TALER_amount2s (&refund));
     488            0 :     return TALER_MHD_reply_with_error (
     489              :       connection,
     490              :       MHD_HTTP_CONFLICT,
     491              :       TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
     492              :       "Order was paid in a different currency");
     493            0 :   case TALER_MERCHANTDB_RS_TOO_HIGH:
     494            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     495              :                 "Refusing refund amount %s that is larger than original payment\n",
     496              :                 TALER_amount2s (&refund));
     497            0 :     return TALER_MHD_reply_with_error (
     498              :       connection,
     499              :       MHD_HTTP_CONFLICT,
     500              :       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_INCONSISTENT_AMOUNT,
     501              :       "Amount above payment");
     502            0 :   case TALER_MERCHANTDB_RS_SOFT_ERROR:
     503              :   case TALER_MERCHANTDB_RS_HARD_ERROR:
     504            0 :     return TALER_MHD_reply_with_error (
     505              :       connection,
     506              :       MHD_HTTP_INTERNAL_SERVER_ERROR,
     507              :       TALER_EC_GENERIC_DB_COMMIT_FAILED,
     508              :       NULL);
     509            2 :   case TALER_MERCHANTDB_RS_NO_SUCH_ORDER:
     510              :     /* We know the order exists from the
     511              :        "get_contract_terms" at the beginning;
     512              :        so if we get 'no such order' here, it
     513              :        must be read as "no PAID order" */
     514            2 :     return TALER_MHD_reply_with_error (
     515              :       connection,
     516              :       MHD_HTTP_CONFLICT,
     517              :       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_ORDER_UNPAID,
     518            2 :       hc->infix);
     519           10 :   case TALER_MERCHANTDB_RS_SUCCESS:
     520              :     /* continued below */
     521           10 :     break;
     522              :   } /* end switch */
     523              : 
     524              :   {
     525              :     uint64_t order_serial;
     526              :     enum GNUNET_DB_QueryStatus qs;
     527              : 
     528           10 :     qs = TALER_MERCHANTDB_get_order_summary (TMH_db,
     529           10 :                                              hc->instance->settings.id,
     530           10 :                                              hc->infix,
     531              :                                              &timestamp,
     532              :                                              &order_serial);
     533           10 :     if (0 >= qs)
     534              :     {
     535            0 :       GNUNET_break (0);
     536            0 :       return TALER_MHD_reply_with_error (
     537              :         connection,
     538              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     539              :         TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     540              :         NULL);
     541              :     }
     542           10 :     TMH_notify_order_change (hc->instance,
     543              :                              TMH_OSF_CLAIMED
     544              :                              | TMH_OSF_PAID
     545              :                              | TMH_OSF_REFUNDED,
     546              :                              timestamp,
     547              :                              order_serial);
     548              :   }
     549              :   {
     550              :     enum MHD_Result ret;
     551              :     char *taler_refund_uri;
     552              : 
     553           10 :     taler_refund_uri = make_taler_refund_uri (connection,
     554           10 :                                               hc->instance->settings.id,
     555           10 :                                               hc->infix);
     556           10 :     ret = TALER_MHD_REPLY_JSON_PACK (
     557              :       connection,
     558              :       MHD_HTTP_OK,
     559              :       GNUNET_JSON_pack_string ("taler_refund_uri",
     560              :                                taler_refund_uri),
     561              :       GNUNET_JSON_pack_data_auto ("h_contract",
     562              :                                   &h_contract));
     563           10 :     GNUNET_free (taler_refund_uri);
     564           10 :     return ret;
     565              :   }
     566              : }
     567              : 
     568              : 
     569              : /* end of taler-merchant-httpd_post-private-orders-ORDER_ID-refund.c */
        

Generated by: LCOV version 2.0-1