merchant

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

taler-merchant-httpd_get-sessions-SESSION_ID.c (8453B)


      1 /*
      2   This file is part of TALER
      3   (C) 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 Affero 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 taler-merchant-httpd_get-sessions-SESSION_ID.c
     18  * @brief implement GET /session/$ID
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/platform.h"
     22 #include "taler-merchant-httpd_get-sessions-SESSION_ID.h"
     23 #include <taler/taler_json_lib.h>
     24 #include <taler/taler_dbevents.h>
     25 
     26 
     27 /**
     28  * Context for a get sessions request.
     29  */
     30 struct GetSessionContext
     31 {
     32   /**
     33    * Kept in a DLL.
     34    */
     35   struct GetSessionContext *next;
     36 
     37   /**
     38    * Kept in a DLL.
     39    */
     40   struct GetSessionContext *prev;
     41 
     42   /**
     43    * Request context.
     44    */
     45   struct TMH_HandlerContext *hc;
     46 
     47   /**
     48    * Entry in the #resume_timeout_heap for this check payment, if we are
     49    * suspended.
     50    */
     51   struct TMH_SuspendedConnection sc;
     52 
     53   /**
     54    * Fulfillment URL from the HTTP request.
     55    */
     56   const char *fulfillment_url;
     57 
     58   /**
     59    * Database event we are waiting on to be resuming on payment.
     60    */
     61   struct GNUNET_DB_EventHandler *eh;
     62 
     63   /**
     64    * Did we suspend @a connection and are thus in
     65    * the #gsc_head DLL (#GNUNET_YES). Set to
     66    * #GNUNET_NO if we are not suspended, and to
     67    * #GNUNET_SYSERR if we should close the connection
     68    * without a response due to shutdown.
     69    */
     70   enum GNUNET_GenericReturnValue suspended;
     71 };
     72 
     73 
     74 /**
     75  * Kept in a DLL.
     76  */
     77 static struct GetSessionContext *gsc_head;
     78 
     79 /**
     80  * Kept in a DLL.
     81  */
     82 static struct GetSessionContext *gsc_tail;
     83 
     84 
     85 void
     86 TMH_force_get_sessions_ID_resume (void)
     87 {
     88   struct GetSessionContext *gsc;
     89 
     90   while (NULL != (gsc = gsc_head))
     91   {
     92     GNUNET_CONTAINER_DLL_remove (gsc_head,
     93                                  gsc_tail,
     94                                  gsc);
     95     gsc->suspended = GNUNET_SYSERR;
     96     MHD_resume_connection (gsc->sc.con);
     97     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     98   }
     99 }
    100 
    101 
    102 /**
    103  * Cleanup helper for TMH_get_session_ID().
    104  *
    105  * @param cls must be a `struct GetSessionContext`
    106  */
    107 static void
    108 gsc_cleanup (void *cls)
    109 {
    110   struct GetSessionContext *gsc = cls;
    111 
    112   if (NULL != gsc->eh)
    113   {
    114     TMH_db->event_listen_cancel (gsc->eh);
    115     gsc->eh = NULL;
    116   }
    117   GNUNET_free (gsc);
    118 }
    119 
    120 
    121 /**
    122  * We have received a trigger from the database
    123  * that we should (possibly) resume the request.
    124  *
    125  * @param cls a `struct GetOrderData` to resume
    126  * @param extra string encoding refund amount (or NULL)
    127  * @param extra_size number of bytes in @a extra
    128  */
    129 static void
    130 resume_by_event (void *cls,
    131                  const void *extra,
    132                  size_t extra_size)
    133 {
    134   struct GetSessionContext *gsc = cls;
    135 
    136   if (GNUNET_YES != gsc->suspended)
    137   {
    138     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    139                 "Not suspended, ignoring event\n");
    140     return; /* duplicate event is possible */
    141   }
    142   gsc->suspended = GNUNET_NO;
    143   GNUNET_CONTAINER_DLL_remove (gsc_head,
    144                                gsc_tail,
    145                                gsc);
    146   MHD_resume_connection (gsc->sc.con);
    147   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
    148 }
    149 
    150 
    151 MHD_RESULT
    152 TMH_get_sessions_ID (
    153   const struct TMH_RequestHandler *rh,
    154   struct MHD_Connection *connection,
    155   struct TMH_HandlerContext *hc)
    156 {
    157   struct GetSessionContext *gsc = hc->ctx;
    158   struct TMH_MerchantInstance *mi = hc->instance;
    159   char *order_id = NULL;
    160   bool paid = false;
    161   bool is_past;
    162 
    163   GNUNET_assert (NULL != mi);
    164   if (NULL == gsc)
    165   {
    166     gsc = GNUNET_new (struct GetSessionContext);
    167     gsc->hc = hc;
    168     hc->ctx = gsc;
    169     hc->cc = &gsc_cleanup;
    170     gsc->sc.con = connection;
    171     gsc->fulfillment_url = MHD_lookup_connection_value (
    172       connection,
    173       MHD_GET_ARGUMENT_KIND,
    174       "fulfillment_url");
    175     if (NULL == gsc->fulfillment_url)
    176     {
    177       GNUNET_break_op (0);
    178       return TALER_MHD_reply_with_error (connection,
    179                                          MHD_HTTP_BAD_REQUEST,
    180                                          TALER_EC_GENERIC_PARAMETER_MISSING,
    181                                          "fulfillment_url");
    182     }
    183     if (! TALER_is_web_url (gsc->fulfillment_url))
    184     {
    185       GNUNET_break_op (0);
    186       return TALER_MHD_reply_with_error (connection,
    187                                          MHD_HTTP_BAD_REQUEST,
    188                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    189                                          "fulfillment_url");
    190     }
    191 
    192     TALER_MHD_parse_request_timeout (connection,
    193                                      &gsc->sc.long_poll_timeout);
    194 
    195     if (! GNUNET_TIME_absolute_is_past (gsc->sc.long_poll_timeout) )
    196     {
    197       struct TMH_SessionEventP session_eh = {
    198         .header.size = htons (sizeof (session_eh)),
    199         .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
    200         .merchant_pub = gsc->hc->instance->merchant_pub
    201       };
    202 
    203       GNUNET_CRYPTO_hash (hc->infix,
    204                           strlen (hc->infix),
    205                           &session_eh.h_session_id);
    206       GNUNET_CRYPTO_hash (gsc->fulfillment_url,
    207                           strlen (gsc->fulfillment_url),
    208                           &session_eh.h_fulfillment_url);
    209       gsc->eh
    210         = TMH_db->event_listen (
    211             TMH_db->cls,
    212             &session_eh.header,
    213             GNUNET_TIME_absolute_get_remaining (gsc->sc.long_poll_timeout),
    214             &resume_by_event,
    215             gsc);
    216     }
    217   } /* end first-time initialization (NULL == gsc) */
    218 
    219   if (GNUNET_SYSERR == gsc->suspended)
    220     return MHD_NO; /* close connection on service shutdown */
    221 
    222   is_past = GNUNET_TIME_absolute_is_past (gsc->sc.long_poll_timeout);
    223   /* figure out order_id */
    224   {
    225     enum GNUNET_DB_QueryStatus qs;
    226 
    227     qs = TMH_db->lookup_order_by_fulfillment (TMH_db->cls,
    228                                               mi->settings.id,
    229                                               gsc->fulfillment_url,
    230                                               hc->infix,
    231                                               false,
    232                                               &order_id);
    233     if (0 > qs)
    234     {
    235       GNUNET_break (0);
    236       return TALER_MHD_reply_with_error (
    237         connection,
    238         MHD_HTTP_INTERNAL_SERVER_ERROR,
    239         TALER_EC_GENERIC_DB_FETCH_FAILED,
    240         "lookup_order_by_fulfillment");
    241     }
    242     if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) &&
    243          is_past)
    244     {
    245       return TALER_MHD_reply_with_error (
    246         connection,
    247         MHD_HTTP_NOT_FOUND,
    248         TALER_EC_MERCHANT_GENERIC_SESSION_UNKNOWN,
    249         hc->infix);
    250     }
    251   }
    252 
    253   /* Check if paid */
    254   if (NULL != order_id)
    255   {
    256     enum GNUNET_DB_QueryStatus qs;
    257     struct TALER_PrivateContractHashP h_contract_terms;
    258 
    259     qs = TMH_db->lookup_order_status (TMH_db->cls,
    260                                       mi->settings.id,
    261                                       order_id,
    262                                       &h_contract_terms,
    263                                       &paid);
    264     if (0 >= qs)
    265     {
    266       GNUNET_break (0);
    267       return TALER_MHD_reply_with_error (
    268         connection,
    269         MHD_HTTP_INTERNAL_SERVER_ERROR,
    270         TALER_EC_GENERIC_DB_FETCH_FAILED,
    271         "lookup_order_status");
    272     }
    273   }
    274 
    275   if (paid)
    276   {
    277     MHD_RESULT ret;
    278 
    279     ret = TALER_MHD_REPLY_JSON_PACK (
    280       connection,
    281       MHD_HTTP_OK,
    282       GNUNET_JSON_pack_string ("order_id",
    283                                order_id));
    284     GNUNET_free (order_id);
    285     return ret;
    286   }
    287 
    288   if (is_past)
    289   {
    290     MHD_RESULT ret;
    291 
    292     GNUNET_assert (NULL != order_id);
    293     ret = TALER_MHD_REPLY_JSON_PACK (
    294       connection,
    295       MHD_HTTP_ACCEPTED,
    296       GNUNET_JSON_pack_string ("order_id",
    297                                order_id));
    298     GNUNET_free (order_id);
    299     return ret;
    300   }
    301 
    302   GNUNET_free (order_id);
    303   GNUNET_CONTAINER_DLL_insert (gsc_head,
    304                                gsc_tail,
    305                                gsc);
    306   gsc->suspended = GNUNET_YES;
    307   MHD_suspend_connection (gsc->sc.con);
    308   return MHD_YES;
    309 }
    310 
    311 
    312 /* end of taler-merchant-httpd_get-templates-TEMPLATE_ID.c */