merchant

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

insert_transfer_details.c (7628B)


      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_error_codes.h>
     23 #include <taler/taler_dbevents.h>
     24 #include <taler/taler_pq_lib.h>
     25 #include "merchant-database/start.h"
     26 #include "merchant-database/insert_transfer_details.h"
     27 #include "helper.h"
     28 
     29 
     30 /**
     31  * How often do we re-try if we run into a DB serialization error?
     32  */
     33 #define MAX_RETRIES 3
     34 
     35 
     36 enum GNUNET_DB_QueryStatus
     37 TALER_MERCHANTDB_insert_transfer_details (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   for (unsigned int i = 0; i<len; i++)
     53   {
     54     const struct TALER_TrackTransferDetails *tdd = &td->details[i];
     55 
     56     coin_values[i] = tdd->coin_value;
     57     deposit_fees[i] = tdd->coin_fee;
     58     coin_pubs[i] = &tdd->coin_pub;
     59     contract_terms[i] = &tdd->h_contract_terms;
     60   }
     61 
     62   check_connection (pg);
     63   PREPARE (pg,
     64            "insert_transfer_details",
     65            "SELECT"
     66            " out_no_instance"
     67            ",out_no_account"
     68            ",out_no_exchange"
     69            ",out_duplicate"
     70            ",out_conflict"
     71            ",out_order_id"
     72            ",out_merchant_pub"
     73            " FROM merchant_do_insert_transfer_details"
     74            " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);");
     75 
     76   for (unsigned int retries = 0;
     77        retries < MAX_RETRIES;
     78        retries++)
     79   {
     80     if (GNUNET_OK !=
     81         TALER_MERCHANTDB_start (pg,
     82                                 "insert transfer details"))
     83     {
     84       GNUNET_break (0);
     85       return GNUNET_DB_STATUS_HARD_ERROR;
     86     }
     87 
     88     {
     89       struct GNUNET_PQ_QueryParam params[] = {
     90         GNUNET_PQ_query_param_string (instance_id),
     91         GNUNET_PQ_query_param_string (exchange_url),
     92         GNUNET_PQ_query_param_string (payto_uri.full_payto),
     93         GNUNET_PQ_query_param_auto_from_type (wtid),
     94         GNUNET_PQ_query_param_timestamp (&td->execution_time),
     95         GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
     96         GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
     97         TALER_PQ_query_param_amount_with_currency (pg->conn,
     98                                                    &td->total_amount),
     99         TALER_PQ_query_param_amount_with_currency (pg->conn,
    100                                                    &td->wire_fee),
    101         TALER_PQ_query_param_array_amount_with_currency (
    102           len,
    103           coin_values,
    104           pg->conn),
    105         TALER_PQ_query_param_array_amount_with_currency (
    106           len,
    107           deposit_fees,
    108           pg->conn),
    109         GNUNET_PQ_query_param_array_ptrs_auto_from_type (
    110           len,
    111           coin_pubs,
    112           pg->conn),
    113         GNUNET_PQ_query_param_array_ptrs_auto_from_type (
    114           len,
    115           contract_terms,
    116           pg->conn),
    117         GNUNET_PQ_query_param_end
    118       };
    119       bool no_instance;
    120       bool no_account;
    121       bool no_exchange;
    122       bool conflict;
    123       char *order_id = NULL;
    124       struct TALER_MerchantPublicKeyP merchant_pub;
    125       struct GNUNET_PQ_ResultSpec rs[] = {
    126         GNUNET_PQ_result_spec_bool ("out_no_instance",
    127                                     &no_instance),
    128         GNUNET_PQ_result_spec_bool ("out_no_account",
    129                                     &no_account),
    130         GNUNET_PQ_result_spec_bool ("out_no_exchange",
    131                                     &no_exchange),
    132         GNUNET_PQ_result_spec_bool ("out_duplicate",
    133                                     &duplicate),
    134         GNUNET_PQ_result_spec_bool ("out_conflict",
    135                                     &conflict),
    136         GNUNET_PQ_result_spec_allow_null (
    137           GNUNET_PQ_result_spec_string ("out_order_id",
    138                                         &order_id),
    139           NULL),
    140         GNUNET_PQ_result_spec_allow_null (
    141           GNUNET_PQ_result_spec_auto_from_type ("out_merchant_pub",
    142                                                 &merchant_pub),
    143           NULL),
    144         GNUNET_PQ_result_spec_end
    145       };
    146 
    147       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    148                                                      "insert_transfer_details",
    149                                                      params,
    150                                                      rs);
    151       GNUNET_PQ_cleanup_query_params_closures (params);
    152       if (0 >= qs)
    153       {
    154         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    155         TALER_MERCHANTDB_rollback (pg);
    156         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    157           continue;
    158         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    159                     "'insert_transfer_details' failed with status %d\n",
    160                     qs);
    161         return qs;
    162       }
    163       if (NULL != order_id)
    164       {
    165         struct TMH_OrderPayEventP pay_eh = {
    166           .header.size = htons (sizeof (pay_eh)),
    167           .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED),
    168           .merchant_pub = merchant_pub
    169         };
    170 
    171         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    172                     "Notifying clients about status change of order %s\n",
    173                     order_id);
    174         GNUNET_CRYPTO_hash (order_id,
    175                             strlen (order_id),
    176                             &pay_eh.h_order_id);
    177         GNUNET_PQ_event_notify (pg->conn,
    178                                 &pay_eh.header,
    179                                 NULL,
    180                                 0);
    181         GNUNET_free (order_id);
    182       }
    183       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    184                   "Transfer details inserted: %s%s%s%s%s\n",
    185                   no_instance ? "no instance " : "",
    186                   no_account ? "no account " : "",
    187                   no_exchange ? "no exchange ": "",
    188                   duplicate ? "duplicate ": "",
    189                   conflict ? "conflict" : "");
    190     }
    191 
    192     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    193                 "Committing transaction...\n");
    194     qs = TALER_MERCHANTDB_commit (pg);
    195     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    196       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    197     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    198     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    199       break;
    200   }
    201   if (duplicate)
    202     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    203   return qs;
    204 }