merchant

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

insert_transfer_details.c (7307B)


      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   // FIXME: why do we retry here, but not most other SQL statements?
     67   // We need a slightly more consistent strategy...
     68   for (unsigned int retries = 0;
     69        retries < MAX_RETRIES;
     70        retries++)
     71   {
     72     if (GNUNET_OK !=
     73         TALER_MERCHANTDB_start (pg,
     74                                 "insert transfer details"))
     75     {
     76       GNUNET_break (0);
     77       return GNUNET_DB_STATUS_HARD_ERROR;
     78     }
     79 
     80     {
     81       struct GNUNET_PQ_QueryParam params[] = {
     82         GNUNET_PQ_query_param_uint64 (&pg->current_merchant_serial),
     83         GNUNET_PQ_query_param_string (exchange_url),
     84         GNUNET_PQ_query_param_string (payto_uri.full_payto),
     85         GNUNET_PQ_query_param_auto_from_type (wtid),
     86         GNUNET_PQ_query_param_timestamp (&td->execution_time),
     87         GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
     88         GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
     89         TALER_PQ_query_param_amount_with_currency (pg->conn,
     90                                                    &td->total_amount),
     91         TALER_PQ_query_param_amount_with_currency (pg->conn,
     92                                                    &td->wire_fee),
     93         TALER_PQ_query_param_array_amount_with_currency (
     94           len,
     95           coin_values,
     96           "merchant",
     97           pg->conn),
     98         TALER_PQ_query_param_array_amount_with_currency (
     99           len,
    100           deposit_fees,
    101           "merchant",
    102           pg->conn),
    103         GNUNET_PQ_query_param_array_ptrs_auto_from_type (
    104           len,
    105           coin_pubs,
    106           pg->conn),
    107         GNUNET_PQ_query_param_array_ptrs_auto_from_type (
    108           len,
    109           contract_terms,
    110           pg->conn),
    111         GNUNET_PQ_query_param_end
    112       };
    113       bool no_account;
    114       bool no_exchange;
    115       bool conflict;
    116       char *order_id = NULL;
    117       struct GNUNET_PQ_ResultSpec rs[] = {
    118         GNUNET_PQ_result_spec_bool ("out_no_account",
    119                                     &no_account),
    120         GNUNET_PQ_result_spec_bool ("out_no_exchange",
    121                                     &no_exchange),
    122         GNUNET_PQ_result_spec_bool ("out_duplicate",
    123                                     &duplicate),
    124         GNUNET_PQ_result_spec_bool ("out_conflict",
    125                                     &conflict),
    126         GNUNET_PQ_result_spec_allow_null (
    127           GNUNET_PQ_result_spec_string ("out_order_id",
    128                                         &order_id),
    129           NULL),
    130         GNUNET_PQ_result_spec_end
    131       };
    132 
    133       TMH_PQ_prepare_anon (pg,
    134                            "SELECT"
    135                            " out_no_account"
    136                            ",out_no_exchange"
    137                            ",out_duplicate"
    138                            ",out_conflict"
    139                            ",out_order_id"
    140                            " FROM merchant_do_insert_transfer_details"
    141                            " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);");
    142       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    143                                                      "",
    144                                                      params,
    145                                                      rs);
    146       GNUNET_PQ_cleanup_query_params_closures (params);
    147       if (0 >= qs)
    148       {
    149         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    150         TALER_MERCHANTDB_rollback (pg);
    151         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    152           continue;
    153         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    154                     "'insert_transfer_details' failed with status %d\n",
    155                     qs);
    156         return qs;
    157       }
    158       if (NULL != order_id)
    159       {
    160         struct TMH_OrderPayEventP pay_eh = {
    161           .header.size = htons (sizeof (pay_eh)),
    162           .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED),
    163           .merchant_pub = pg->current_merchant_pub
    164         };
    165 
    166         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    167                     "Notifying clients about status change of order %s\n",
    168                     order_id);
    169         GNUNET_CRYPTO_hash (order_id,
    170                             strlen (order_id),
    171                             &pay_eh.h_order_id);
    172         GNUNET_PQ_event_notify (pg->conn,
    173                                 &pay_eh.header,
    174                                 NULL,
    175                                 0);
    176         GNUNET_free (order_id);
    177       }
    178       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    179                   "Transfer details inserted: %s%s%s%s\n",
    180                   no_account ? "no account " : "",
    181                   no_exchange ? "no exchange ": "",
    182                   duplicate ? "duplicate ": "",
    183                   conflict ? "conflict" : "");
    184     }
    185 
    186     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    187                 "Committing transaction...\n");
    188     qs = TALER_MERCHANTDB_commit (pg);
    189     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    190       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    191     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    192     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    193       break;
    194   }
    195   if (duplicate)
    196     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    197   return qs;
    198 }