merchant

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

iterate_orders.c (11881B)


      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                          "    NOT EXISTS"
    171                          "     (SELECT 1"
    172                          "        FROM merchant_contract_terms mct"
    173                          "       WHERE mct.order_serial=merchant_orders.order_serial)" /* only select unclaimed orders */
    174                          "   AND ($10 OR "
    175                          "     ($11 = session_id))"
    176                          "   AND ($12 OR "
    177                          "     ($13 = fulfillment_url))"
    178                          "   AND ( ($14::TEXT IS NULL) OR "
    179                          "         (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )"
    180                          "   AND ($15 OR"
    181                          "        (CAST($16 AS BOOL) = (pay_deadline < $17)))"
    182                          " ORDER BY order_serial ASC"
    183                          " LIMIT $1)"
    184                          "UNION " /* union ensures elements are distinct! */
    185                          "(SELECT"
    186                          " order_id"
    187                          ",order_serial"
    188                          ",creation_time"
    189                          " FROM merchant_contract_terms"
    190                          " WHERE order_serial > $2"
    191                          "   AND"
    192                          "    creation_time > $3"
    193                          "   AND ($4 OR "
    194                          "    (CAST($5 AS BOOL) = paid))"
    195                          "   AND ($6 OR "
    196                          "    (CAST($7 AS BOOL) = EXISTS"
    197                          "     (SELECT 1"
    198                          "        FROM merchant_refunds mr"
    199                          "       WHERE mr.order_serial=merchant_contract_terms.order_serial)))"
    200                          "   AND ($8 OR"
    201                          "    (CAST($9 AS BOOL) = wired))"
    202                          "   AND ($10 OR "
    203                          "     ($11 = session_id))"
    204                          "   AND ($12 OR "
    205                          "     ($13 = fulfillment_url))"
    206                          "   AND ( ($14::TEXT IS NULL) OR "
    207                          "         (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )"
    208                          "   AND ($15 OR"
    209                          "        (CAST($16 AS BOOL) = ((NOT paid) AND"
    210                          "                              (pay_deadline < $17))))"
    211                          " ORDER BY order_serial ASC"
    212                          " LIMIT $1)"
    213                          " ORDER BY order_serial ASC"
    214                          " LIMIT $1");
    215   }
    216   else
    217   {
    218     TMH_PQ_prepare_anon (pg,
    219                          "(SELECT"
    220                          " order_id"
    221                          ",order_serial"
    222                          ",creation_time"
    223                          " FROM merchant_orders"
    224                          " WHERE order_serial < $2"
    225                          "   AND"
    226                          "    creation_time < $3"
    227                          "   AND ($4 OR "
    228                          "    NOT CAST($5 AS BOOL))" /* unclaimed orders are never paid */
    229                          "   AND ($6 OR "
    230                          "    NOT CAST($7 AS BOOL))"/* unclaimed orders are never refunded */
    231                          "   AND ($8 OR "
    232                          "    NOT CAST($9 AS BOOL))" /* unclaimed orders are never wired */
    233                          "   AND"
    234                          "    NOT EXISTS"
    235                          "     (SELECT 1"
    236                          "        FROM merchant_contract_terms mct"
    237                          "       WHERE mct.order_serial=merchant_orders.order_serial)" /* only select unclaimed orders */
    238                          "   AND ($10 OR "
    239                          "     ($11 = session_id))"
    240                          "   AND ($12 OR "
    241                          "     ($13 = fulfillment_url))"
    242                          "   AND ( ($14::TEXT IS NULL) OR "
    243                          "         (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )"
    244                          "   AND ($15 OR"
    245                          "        (CAST($16 AS BOOL) = (pay_deadline < $17)))"
    246                          " ORDER BY order_serial DESC"
    247                          " LIMIT $1)"
    248                          "UNION " /* union ensures elements are distinct! */
    249                          "(SELECT"
    250                          " order_id"
    251                          ",order_serial"
    252                          ",creation_time"
    253                          " FROM merchant_contract_terms"
    254                          " WHERE order_serial < $2"
    255                          "   AND"
    256                          "    creation_time < $3"
    257                          "   AND ($4 OR "
    258                          "    (CAST($5 AS BOOL) = paid))"
    259                          "   AND ($6 OR "
    260                          "    (CAST($7 AS BOOL) = EXISTS"
    261                          "     (SELECT 1"
    262                          "        FROM merchant_refunds mr"
    263                          "       WHERE mr.order_serial=merchant_contract_terms.order_serial)))"
    264                          "   AND ($8 OR"
    265                          "     (CAST($9 AS BOOL) = wired))"
    266                          "   AND ($10 OR "
    267                          "     ($11 = session_id))"
    268                          "   AND ($12 OR "
    269                          "     ($13 = fulfillment_url))"
    270                          "   AND ( ($14::TEXT IS NULL) OR "
    271                          "         (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )"
    272                          "   AND ($15 OR"
    273                          "        (CAST($16 AS BOOL) = ((NOT paid) AND"
    274                          "                              (pay_deadline < $17))))"
    275                          " ORDER BY order_serial DESC"
    276                          " LIMIT $1)"
    277                          " ORDER BY order_serial DESC"
    278                          " LIMIT $1");
    279   }
    280   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    281                                              "",
    282                                              params,
    283                                              &lookup_orders_cb,
    284                                              &plc);
    285   if (plc.extract_failed)
    286     return GNUNET_DB_STATUS_HARD_ERROR;
    287   return qs;
    288 }