merchant

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

iterate_orders.c (11589B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2022, 2023, 2025 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/iterate_orders.c
     18  * @brief Implementation of the iterate_orders function for Postgres
     19  * @author Iván Ávalos
     20  * @author Christian Grothoff
     21  */
     22 #include "platform.h"
     23 #include <taler/taler_pq_lib.h>
     24 #include "merchant-database/iterate_orders.h"
     25 #include "helper.h"
     26 
     27 /**
     28  * Context used for TALER_MERCHANTDB_iterate_orders().
     29  */
     30 struct LookupOrdersContext
     31 {
     32   /**
     33    * Function to call with the results.
     34    */
     35   TALER_MERCHANTDB_OrdersCallback cb;
     36 
     37   /**
     38    * Closure for @a cb.
     39    */
     40   void *cb_cls;
     41 
     42   /**
     43    * Did database result extraction fail?
     44    */
     45   bool extract_failed;
     46 };
     47 
     48 
     49 /**
     50  * Function to be called with the results of a SELECT statement
     51  * that has returned @a num_results results about orders.
     52  *
     53  * @param[in,out] cls of type `struct LookupOrdersContext *`
     54  * @param result the postgres result
     55  * @param num_results the number of results in @a result
     56  */
     57 static void
     58 lookup_orders_cb (void *cls,
     59                   PGresult *result,
     60                   unsigned int num_results)
     61 {
     62   struct LookupOrdersContext *plc = cls;
     63 
     64   for (unsigned int i = 0; i < num_results; i++)
     65   {
     66     char *order_id;
     67     uint64_t order_serial;
     68     struct GNUNET_TIME_Timestamp ts;
     69     struct GNUNET_PQ_ResultSpec rs[] = {
     70       GNUNET_PQ_result_spec_string ("order_id",
     71                                     &order_id),
     72       GNUNET_PQ_result_spec_uint64 ("order_serial",
     73                                     &order_serial),
     74       GNUNET_PQ_result_spec_timestamp ("creation_time",
     75                                        &ts),
     76       GNUNET_PQ_result_spec_end
     77     };
     78 
     79     if (GNUNET_OK !=
     80         GNUNET_PQ_extract_result (result,
     81                                   rs,
     82                                   i))
     83     {
     84       GNUNET_break (0);
     85       plc->extract_failed = true;
     86       return;
     87     }
     88     plc->cb (plc->cb_cls,
     89              order_id,
     90              order_serial,
     91              ts);
     92     GNUNET_PQ_cleanup_result (rs);
     93   }
     94 }
     95 
     96 
     97 enum GNUNET_DB_QueryStatus
     98 TALER_MERCHANTDB_iterate_orders (
     99   struct TALER_MERCHANTDB_PostgresContext *pg,
    100   const char *instance_id,
    101   const struct TALER_MERCHANTDB_OrderFilter *of,
    102   TALER_MERCHANTDB_OrdersCallback cb,
    103   void *cb_cls)
    104 {
    105   struct LookupOrdersContext plc = {
    106     .cb = cb,
    107     .cb_cls = cb_cls
    108   };
    109   uint64_t limit = (of->delta > 0) ? of->delta : -of->delta;
    110   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
    111   struct GNUNET_PQ_QueryParam params[] = {
    112     /* $1 */
    113     GNUNET_PQ_query_param_uint64 (&limit),
    114     GNUNET_PQ_query_param_uint64 (&of->start_row),
    115     GNUNET_PQ_query_param_timestamp (&of->date),
    116     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->paid)),
    117     /* $5 */
    118     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->paid)),
    119     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->refunded)),
    120     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->refunded)),
    121     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->wired)),
    122     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->wired)),
    123     /* $10 */
    124     GNUNET_PQ_query_param_bool (NULL == of->session_id),
    125     NULL == of->session_id
    126     ? GNUNET_PQ_query_param_null ()
    127     : GNUNET_PQ_query_param_string (of->session_id),
    128     GNUNET_PQ_query_param_bool (NULL == of->fulfillment_url),
    129     NULL == of->fulfillment_url
    130     ? GNUNET_PQ_query_param_null ()
    131     : GNUNET_PQ_query_param_string (of->fulfillment_url),
    132     NULL == of->summary_filter
    133     ? GNUNET_PQ_query_param_null ()
    134     : GNUNET_PQ_query_param_string (of->summary_filter),
    135     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->expired)),
    136     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->expired)),
    137     GNUNET_PQ_query_param_timestamp (&now),
    138     GNUNET_PQ_query_param_end
    139   };
    140   enum GNUNET_DB_QueryStatus qs;
    141 
    142   GNUNET_assert (NULL != pg->current_merchant_id);
    143   GNUNET_assert (0 == strcmp (instance_id,
    144                               pg->current_merchant_id));
    145   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    146               "Looking up orders, using filter paid: %d, refunded: %d, wired: %d, expired: %d\n",
    147               of->paid,
    148               of->refunded,
    149               of->wired,
    150               of->expired);
    151 
    152   if (of->delta > 0)
    153   {
    154     TMH_PQ_prepare_anon (pg,
    155                          "(SELECT"
    156                          " order_id"
    157                          ",order_serial"
    158                          ",creation_time"
    159                          " FROM merchant_orders"
    160                          " WHERE order_serial > $2"
    161                          "   AND"
    162                          "    creation_time > $3"
    163                          "   AND ($4 OR "
    164                          "    NOT CAST($5 AS BOOL))" /* unclaimed orders are never paid */
    165                          "   AND ($6 OR "
    166                          "    NOT CAST($7 AS BOOL))"/* unclaimed orders are never refunded */
    167                          "   AND ($8 OR "
    168                          "    NOT CAST($9 AS BOOL))" /* unclaimed orders are never wired */
    169                          "   AND"
    170                          "    (order_serial NOT IN"
    171                          "     (SELECT order_serial"
    172                          "      FROM merchant_contract_terms))" /* only select unclaimed orders */
    173                          "   AND ($10 OR "
    174                          "     ($11 = session_id))"
    175                          "   AND ($12 OR "
    176                          "     ($13 = fulfillment_url))"
    177                          "   AND ( ($14::TEXT IS NULL) OR "
    178                          "         (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )"
    179                          "   AND ($15 OR"
    180                          "        (CAST($16 AS BOOL) = (pay_deadline < $17)))"
    181                          " ORDER BY order_serial ASC"
    182                          " LIMIT $1)"
    183                          "UNION " /* union ensures elements are distinct! */
    184                          "(SELECT"
    185                          " order_id"
    186                          ",order_serial"
    187                          ",creation_time"
    188                          " FROM merchant_contract_terms"
    189                          " WHERE order_serial > $2"
    190                          "   AND"
    191                          "    creation_time > $3"
    192                          "   AND ($4 OR "
    193                          "    (CAST($5 AS BOOL) = paid))"
    194                          "   AND ($6 OR "
    195                          "    (CAST($7 AS BOOL) = (order_serial IN"
    196                          "     (SELECT order_serial "
    197                          "      FROM merchant_refunds))))"
    198                          "   AND ($8 OR"
    199                          "    (CAST($9 AS BOOL) = wired))"
    200                          "   AND ($10 OR "
    201                          "     ($11 = session_id))"
    202                          "   AND ($12 OR "
    203                          "     ($13 = fulfillment_url))"
    204                          "   AND ( ($14::TEXT IS NULL) OR "
    205                          "         (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )"
    206                          "   AND ($15 OR"
    207                          "        (CAST($16 AS BOOL) = ((NOT paid) AND"
    208                          "                              (pay_deadline < $17))))"
    209                          " ORDER BY order_serial ASC"
    210                          " LIMIT $1)"
    211                          " ORDER BY order_serial ASC"
    212                          " LIMIT $1");
    213   }
    214   else
    215   {
    216     TMH_PQ_prepare_anon (pg,
    217                          "(SELECT"
    218                          " order_id"
    219                          ",order_serial"
    220                          ",creation_time"
    221                          " FROM merchant_orders"
    222                          " WHERE order_serial < $2"
    223                          "   AND"
    224                          "    creation_time < $3"
    225                          "   AND ($4 OR "
    226                          "    NOT CAST($5 AS BOOL))" /* unclaimed orders are never paid */
    227                          "   AND ($6 OR "
    228                          "    NOT CAST($7 AS BOOL))"/* unclaimed orders are never refunded */
    229                          "   AND ($8 OR "
    230                          "    NOT CAST($9 AS BOOL))" /* unclaimed orders are never wired */
    231                          "   AND"
    232                          "    order_serial NOT IN"
    233                          "     (SELECT order_serial"
    234                          "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    235                          "   AND ($10 OR "
    236                          "     ($11 = session_id))"
    237                          "   AND ($12 OR "
    238                          "     ($13 = fulfillment_url))"
    239                          "   AND ( ($14::TEXT IS NULL) OR "
    240                          "         (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )"
    241                          "   AND ($15 OR"
    242                          "        (CAST($16 AS BOOL) = (pay_deadline < $17)))"
    243                          " ORDER BY order_serial DESC"
    244                          " LIMIT $1)"
    245                          "UNION " /* union ensures elements are distinct! */
    246                          "(SELECT"
    247                          " order_id"
    248                          ",order_serial"
    249                          ",creation_time"
    250                          " FROM merchant_contract_terms"
    251                          " WHERE order_serial < $2"
    252                          "   AND"
    253                          "    creation_time < $3"
    254                          "   AND ($4 OR "
    255                          "    (CAST($5 AS BOOL) = paid))"
    256                          "   AND ($6 OR "
    257                          "    (CAST($7 AS BOOL) = (order_serial IN"
    258                          "     (SELECT order_serial "
    259                          "      FROM merchant_refunds))))"
    260                          "   AND ($8 OR"
    261                          "     (CAST($9 AS BOOL) = wired))"
    262                          "   AND ($10 OR "
    263                          "     ($11 = session_id))"
    264                          "   AND ($12 OR "
    265                          "     ($13 = fulfillment_url))"
    266                          "   AND ( ($14::TEXT IS NULL) OR "
    267                          "         (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )"
    268                          "   AND ($15 OR"
    269                          "        (CAST($16 AS BOOL) = ((NOT paid) AND"
    270                          "                              (pay_deadline < $17))))"
    271                          " ORDER BY order_serial DESC"
    272                          " LIMIT $1)"
    273                          " ORDER BY order_serial DESC"
    274                          " LIMIT $1");
    275   }
    276   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    277                                              "",
    278                                              params,
    279                                              &lookup_orders_cb,
    280                                              &plc);
    281   if (plc.extract_failed)
    282     return GNUNET_DB_STATUS_HARD_ERROR;
    283   return qs;
    284 }