merchant

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

insert_transfer_details.c (7288B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2022, 2026 Taler Systems SA
      4 
      5    TALER is free software; you can redistribute it and/or modify it under the
      6    terms of the GNU 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/backenddb/insert_transfer_details.c
     18  * @brief Implementation of the insert_transfer_details function for Postgres
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include <taler/taler_pq_lib.h>
     23 #include <taler/taler_dbevents.h>
     24 #include "merchant-database/start.h"
     25 #include "merchant-database/insert_transfer_details.h"
     26 #include "helper.h"
     27 
     28 
     29 /**
     30  * How often do we re-try if we run into a DB serialization error?
     31  */
     32 #define MAX_RETRIES 3
     33 
     34 
     35 enum GNUNET_DB_QueryStatus
     36 TALER_MERCHANTDB_insert_transfer_details (
     37   struct TALER_MERCHANTDB_PostgresContext *pg,
     38   const char *instance_id,
     39   const char *exchange_url,
     40   struct TALER_FullPayto payto_uri,
     41   const struct TALER_WireTransferIdentifierRawP *wtid,
     42   const struct TALER_EXCHANGE_TransferData *td)
     43 {
     44   unsigned int len = td->details_length;
     45   struct TALER_Amount coin_values[GNUNET_NZL (len)];
     46   struct TALER_Amount deposit_fees[GNUNET_NZL (len)];
     47   const struct TALER_CoinSpendPublicKeyP *coin_pubs[GNUNET_NZL (len)];
     48   const struct TALER_PrivateContractHashP *contract_terms[GNUNET_NZL (len)];
     49   enum GNUNET_DB_QueryStatus qs;
     50   bool duplicate;
     51 
     52   GNUNET_assert (NULL != pg->current_merchant_id);
     53   GNUNET_assert (0 == strcmp (instance_id,
     54                               pg->current_merchant_id));
     55 
     56   for (unsigned int i = 0; i<len; i++)
     57   {
     58     const struct TALER_TrackTransferDetails *tdd = &td->details[i];
     59 
     60     coin_values[i] = tdd->coin_value;
     61     deposit_fees[i] = tdd->coin_fee;
     62     coin_pubs[i] = &tdd->coin_pub;
     63     contract_terms[i] = &tdd->h_contract_terms;
     64   }
     65 
     66   check_connection (pg);
     67   // FIXME: why do we retry here, but not most other SQL statements?
     68   // We need a slightly more consistent strategy...
     69   for (unsigned int retries = 0;
     70        retries < MAX_RETRIES;
     71        retries++)
     72   {
     73     if (GNUNET_OK !=
     74         TALER_MERCHANTDB_start (pg,
     75                                 "insert transfer details"))
     76     {
     77       GNUNET_break (0);
     78       return GNUNET_DB_STATUS_HARD_ERROR;
     79     }
     80 
     81     {
     82       struct GNUNET_PQ_QueryParam params[] = {
     83         GNUNET_PQ_query_param_uint64 (&pg->current_merchant_serial),
     84         GNUNET_PQ_query_param_string (exchange_url),
     85         GNUNET_PQ_query_param_string (payto_uri.full_payto),
     86         GNUNET_PQ_query_param_auto_from_type (wtid),
     87         GNUNET_PQ_query_param_timestamp (&td->execution_time),
     88         GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
     89         GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
     90         TALER_PQ_query_param_amount_with_currency (pg->conn,
     91                                                    &td->total_amount),
     92         TALER_PQ_query_param_amount_with_currency (pg->conn,
     93                                                    &td->wire_fee),
     94         TALER_PQ_query_param_array_amount_with_currency (
     95           len,
     96           coin_values,
     97           pg->conn),
     98         TALER_PQ_query_param_array_amount_with_currency (
     99           len,
    100           deposit_fees,
    101           pg->conn),
    102         GNUNET_PQ_query_param_array_ptrs_auto_from_type (
    103           len,
    104           coin_pubs,
    105           pg->conn),
    106         GNUNET_PQ_query_param_array_ptrs_auto_from_type (
    107           len,
    108           contract_terms,
    109           pg->conn),
    110         GNUNET_PQ_query_param_end
    111       };
    112       bool no_account;
    113       bool no_exchange;
    114       bool conflict;
    115       char *order_id = NULL;
    116       struct GNUNET_PQ_ResultSpec rs[] = {
    117         GNUNET_PQ_result_spec_bool ("out_no_account",
    118                                     &no_account),
    119         GNUNET_PQ_result_spec_bool ("out_no_exchange",
    120                                     &no_exchange),
    121         GNUNET_PQ_result_spec_bool ("out_duplicate",
    122                                     &duplicate),
    123         GNUNET_PQ_result_spec_bool ("out_conflict",
    124                                     &conflict),
    125         GNUNET_PQ_result_spec_allow_null (
    126           GNUNET_PQ_result_spec_string ("out_order_id",
    127                                         &order_id),
    128           NULL),
    129         GNUNET_PQ_result_spec_end
    130       };
    131 
    132       TMH_PQ_prepare_anon (pg,
    133                            "SELECT"
    134                            " out_no_account"
    135                            ",out_no_exchange"
    136                            ",out_duplicate"
    137                            ",out_conflict"
    138                            ",out_order_id"
    139                            " FROM merchant_do_insert_transfer_details"
    140                            " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);");
    141       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    142                                                      "",
    143                                                      params,
    144                                                      rs);
    145       GNUNET_PQ_cleanup_query_params_closures (params);
    146       if (0 >= qs)
    147       {
    148         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    149         TALER_MERCHANTDB_rollback (pg);
    150         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    151           continue;
    152         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    153                     "'insert_transfer_details' failed with status %d\n",
    154                     qs);
    155         return qs;
    156       }
    157       if (NULL != order_id)
    158       {
    159         struct TMH_OrderPayEventP pay_eh = {
    160           .header.size = htons (sizeof (pay_eh)),
    161           .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED),
    162           .merchant_pub = pg->current_merchant_pub
    163         };
    164 
    165         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    166                     "Notifying clients about status change of order %s\n",
    167                     order_id);
    168         GNUNET_CRYPTO_hash (order_id,
    169                             strlen (order_id),
    170                             &pay_eh.h_order_id);
    171         GNUNET_PQ_event_notify (pg->conn,
    172                                 &pay_eh.header,
    173                                 NULL,
    174                                 0);
    175         GNUNET_free (order_id);
    176       }
    177       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    178                   "Transfer details inserted: %s%s%s%s\n",
    179                   no_account ? "no account " : "",
    180                   no_exchange ? "no exchange ": "",
    181                   duplicate ? "duplicate ": "",
    182                   conflict ? "conflict" : "");
    183     }
    184 
    185     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    186                 "Committing transaction...\n");
    187     qs = TALER_MERCHANTDB_commit (pg);
    188     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    189       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    190     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    191     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    192       break;
    193   }
    194   if (duplicate)
    195     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    196   return qs;
    197 }