/*
This file is part of TALER
Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2.1, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with
TALER; see the file COPYING.LGPL. If not, see
*/
/**
* @file merchant_api_get_orders.c
* @brief Implementation of the GET /orders request of the merchant's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include
#include
#include /* just for HTTP status codes */
#include
#include
#include "taler_merchant_service.h"
#include "merchant_api_curl_defaults.h"
#include
#include
/**
* Handle for a GET /orders operation.
*/
struct TALER_MERCHANT_OrdersGetHandle
{
/**
* The url for this request.
*/
char *url;
/**
* Handle for the request.
*/
struct GNUNET_CURL_Job *job;
/**
* Function to call with the result.
*/
TALER_MERCHANT_OrdersGetCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Reference to the execution context.
*/
struct GNUNET_CURL_Context *ctx;
};
/**
* Parse order information from @a ia.
*
* @param ia JSON array (or NULL!) with order data
* @param[in] ogr response to fill
* @param ogh operation handle
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
parse_orders (const json_t *ia,
struct TALER_MERCHANT_OrdersGetResponse *ogr,
struct TALER_MERCHANT_OrdersGetHandle *ogh)
{
unsigned int oes_len = json_array_size (ia);
struct TALER_MERCHANT_OrderEntry oes[GNUNET_NZL (oes_len)];
size_t index;
json_t *value;
json_array_foreach (ia, index, value) {
struct TALER_MERCHANT_OrderEntry *ie = &oes[index];
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("order_id",
&ie->order_id),
GNUNET_JSON_spec_timestamp ("timestamp",
&ie->timestamp),
GNUNET_JSON_spec_uint64 ("row_id",
&ie->order_serial),
TALER_JSON_spec_amount_any ("amount",
&ie->amount),
GNUNET_JSON_spec_string ("summary",
&ie->summary),
GNUNET_JSON_spec_bool ("refundable",
&ie->refundable),
GNUNET_JSON_spec_bool ("paid",
&ie->paid),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (value,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
ogr->details.ok.orders_length = oes_len;
ogr->details.ok.orders = oes;
ogh->cb (ogh->cb_cls,
ogr);
ogh->cb = NULL; /* just to be sure */
return GNUNET_OK;
}
/**
* Function called when we're done processing the
* HTTP /orders request.
*
* @param cls the `struct TALER_MERCHANT_OrdersGetHandle`
* @param response_code HTTP response code, 0 on error
* @param response response body, NULL if not in JSON
*/
static void
handle_get_orders_finished (void *cls,
long response_code,
const void *response)
{
struct TALER_MERCHANT_OrdersGetHandle *ogh = cls;
const json_t *json = response;
struct TALER_MERCHANT_OrdersGetResponse ogr = {
.hr.http_status = (unsigned int) response_code,
.hr.reply = json
};
ogh->job = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Got /orders response with status code %u\n",
(unsigned int) response_code);
switch (response_code)
{
case MHD_HTTP_OK:
{
json_t *orders;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("orders",
&orders),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
NULL, NULL))
{
ogr.hr.http_status = 0;
ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
}
else
{
if ( (! json_is_array (orders)) ||
(GNUNET_OK ==
parse_orders (orders,
&ogr,
ogh)) )
{
GNUNET_JSON_parse_free (spec);
TALER_MERCHANT_orders_get_cancel (ogh);
return;
}
ogr.hr.http_status = 0;
ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
}
GNUNET_JSON_parse_free (spec);
break;
}
case MHD_HTTP_UNAUTHORIZED:
ogr.hr.ec = TALER_JSON_get_error_code (json);
ogr.hr.hint = TALER_JSON_get_error_hint (json);
/* Nothing really to verify, merchant says we need to authenticate. */
break;
case MHD_HTTP_NOT_FOUND:
ogr.hr.ec = TALER_JSON_get_error_code (json);
ogr.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
ogr.hr.ec = TALER_JSON_get_error_code (json);
ogr.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d\n",
(unsigned int) response_code,
(int) ogr.hr.ec);
break;
}
ogh->cb (ogh->cb_cls,
&ogr);
TALER_MERCHANT_orders_get_cancel (ogh);
}
struct TALER_MERCHANT_OrdersGetHandle *
TALER_MERCHANT_orders_get (
struct GNUNET_CURL_Context *ctx,
const char *backend_url,
TALER_MERCHANT_OrdersGetCallback cb,
void *cb_cls)
{
return TALER_MERCHANT_orders_get2 (ctx,
backend_url,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
GNUNET_TIME_UNIT_FOREVER_TS,
UINT64_MAX,
-20, /* default is most recent 20 entries */
GNUNET_TIME_UNIT_ZERO,
cb,
cb_cls);
}
struct TALER_MERCHANT_OrdersGetHandle *
TALER_MERCHANT_orders_get2 (
struct GNUNET_CURL_Context *ctx,
const char *backend_url,
enum TALER_EXCHANGE_YesNoAll paid,
enum TALER_EXCHANGE_YesNoAll refunded,
enum TALER_EXCHANGE_YesNoAll wired,
struct GNUNET_TIME_Timestamp date,
uint64_t start_row,
int64_t delta,
struct GNUNET_TIME_Relative timeout,
TALER_MERCHANT_OrdersGetCallback cb,
void *cb_cls)
{
struct TALER_MERCHANT_OrdersGetHandle *ogh;
CURL *eh;
unsigned int timeout_ms = timeout.rel_value_us
/ GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
GNUNET_assert (NULL != backend_url);
if (0 == delta)
{
GNUNET_break (0);
return NULL;
}
ogh = GNUNET_new (struct TALER_MERCHANT_OrdersGetHandle);
ogh->ctx = ctx;
ogh->cb = cb;
ogh->cb_cls = cb_cls;
/* build ogh->url with the various optional arguments */
{
char *dstr;
bool have_date;
bool have_srow;
char cbuf[30];
char dbuf[30];
char tbuf[30];
GNUNET_snprintf (tbuf,
sizeof (tbuf),
"%llu",
(unsigned long long) timeout_ms);
GNUNET_snprintf (dbuf,
sizeof (dbuf),
"%lld",
(long long) delta);
GNUNET_snprintf (cbuf,
sizeof (cbuf),
"%llu",
(unsigned long long) start_row);
dstr = GNUNET_strdup (GNUNET_TIME_timestamp2s (date));
if (delta > 0)
{
have_date = ! GNUNET_TIME_absolute_is_zero (date.abs_time);
have_srow = (0 != start_row);
}
else
{
have_date = ! GNUNET_TIME_absolute_is_never (date.abs_time);
have_srow = (UINT64_MAX != start_row);
}
ogh->url = TALER_url_join (backend_url,
"private/orders",
"paid",
(TALER_EXCHANGE_YNA_ALL != paid)
? TALER_yna_to_string (paid)
: NULL,
"refunded",
(TALER_EXCHANGE_YNA_ALL != refunded)
? TALER_yna_to_string (refunded)
: NULL,
"wired",
(TALER_EXCHANGE_YNA_ALL != wired)
? TALER_yna_to_string (wired)
: NULL,
"date_s",
(have_date)
? dstr
: NULL,
"start",
(have_srow)
? cbuf
: NULL,
"delta",
(-20 != delta)
? dbuf
: NULL,
"timeout_ms",
(0 != timeout_ms)
? tbuf
: NULL,
NULL);
GNUNET_free (dstr);
}
if (NULL == ogh->url)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Could not construct request URL.\n");
GNUNET_free (ogh);
return NULL;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Requesting URL '%s'\n",
ogh->url);
eh = TALER_MERCHANT_curl_easy_get_ (ogh->url);
ogh->job = GNUNET_CURL_job_add (ctx,
eh,
&handle_get_orders_finished,
ogh);
return ogh;
}
void
TALER_MERCHANT_orders_get_cancel (
struct TALER_MERCHANT_OrdersGetHandle *ogh)
{
if (NULL != ogh->job)
GNUNET_CURL_job_cancel (ogh->job);
GNUNET_free (ogh->url);
GNUNET_free (ogh);
}