aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorChristian Blättler <blatc2@bfh.ch>2024-04-10 08:01:28 +0200
committerChristian Blättler <blatc2@bfh.ch>2024-04-10 08:01:28 +0200
commitd42cb68e0ce55310aac0ed0fb83e57ede8e63836 (patch)
treec26cd5c4704eed3e87a45b52a5f5fac7026d8049 /src/backend
parent983ad0943b4f0b9ddf7d1b7a14693e4b69395ea6 (diff)
parentd4fd038d116381d76d1fdf8384101f8fa901ffe5 (diff)
Merge branch 'master' into tokens
# Conflicts: # src/backend/taler-merchant-httpd_private-post-orders.c
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/taler-merchant-depositcheck.c10
-rw-r--r--src/backend/taler-merchant-exchange.c55
-rw-r--r--src/backend/taler-merchant-httpd_config.c32
-rw-r--r--src/backend/taler-merchant-httpd_get-templates-ID.c15
-rw-r--r--src/backend/taler-merchant-httpd_helper.c4
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-abort.c18
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-pay.c371
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-refund.c8
-rw-r--r--src/backend/taler-merchant-httpd_private-get-orders-ID.c125
-rw-r--r--src/backend/taler-merchant-httpd_private-get-orders.c119
-rw-r--r--src/backend/taler-merchant-httpd_private-get-products.c20
-rw-r--r--src/backend/taler-merchant-httpd_private-get-templates-ID.c13
-rw-r--r--src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c7
-rw-r--r--src/backend/taler-merchant-httpd_private-get-transfers.c63
-rw-r--r--src/backend/taler-merchant-httpd_private-patch-templates-ID.c60
-rw-r--r--src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c3
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders.c440
-rw-r--r--src/backend/taler-merchant-httpd_private-post-templates.c73
-rw-r--r--src/backend/taler-merchant-httpd_private-post-token-families.c3
-rw-r--r--src/backend/taler-merchant-wirewatch.c5
20 files changed, 679 insertions, 765 deletions
diff --git a/src/backend/taler-merchant-depositcheck.c b/src/backend/taler-merchant-depositcheck.c
index 6bb3b05b..9245e1fb 100644
--- a/src/backend/taler-merchant-depositcheck.c
+++ b/src/backend/taler-merchant-depositcheck.c
@@ -391,9 +391,9 @@ run_at (struct GNUNET_TIME_Absolute deadline)
GNUNET_TIME_absolute2s (deadline));
return; /* too early */
}
+ next_deadline = deadline;
if (NULL != task)
GNUNET_SCHEDULER_cancel (task);
- next_deadline = deadline;
task = GNUNET_SCHEDULER_add_at (deadline,
&select_work,
NULL);
@@ -513,7 +513,7 @@ deposit_get_cb (void *cls,
qs = db_plugin->update_deposit_confirmation_status (
db_plugin->cls,
w->deposit_serial,
- true, /* FIXME: should we set this to 'false' as we are awaiting KYC? */
+ true,
GNUNET_TIME_absolute_to_timestamp (future_retry),
w->retry_backoff,
"Exchange reported 202 Accepted due to KYC/AML block");
@@ -567,8 +567,12 @@ deposit_get_cb (void *cls,
GNUNET_assert (NULL != keys);
if ( (w_count < CONCURRENCY_LIMIT / 2) ||
(0 == w_count) )
+ {
+ if (NULL != task)
+ GNUNET_SCHEDULER_cancel (task);
task = GNUNET_SCHEDULER_add_now (&select_work,
NULL);
+ }
}
@@ -774,6 +778,8 @@ keys_cb (
return;
}
keys = TALER_EXCHANGE_keys_incref (in_keys);
+ if (NULL != task)
+ GNUNET_SCHEDULER_cancel (task);
task = GNUNET_SCHEDULER_add_now (&select_work,
NULL);
}
diff --git a/src/backend/taler-merchant-exchange.c b/src/backend/taler-merchant-exchange.c
index f77ca3a5..7945cb50 100644
--- a/src/backend/taler-merchant-exchange.c
+++ b/src/backend/taler-merchant-exchange.c
@@ -238,6 +238,11 @@ static struct GNUNET_DB_EventHandler *eh;
static unsigned int active_inquiries;
/**
+ * Set to true if we ever encountered any problem.
+ */
+static bool found_problem;
+
+/**
* Value to return from main(). 0 on success, non-zero on errors.
*/
static int global_ret;
@@ -394,6 +399,8 @@ update_transaction_status (const struct Inquiry *w,
{
enum GNUNET_DB_QueryStatus qs;
+ if (failed)
+ found_problem = true;
qs = db_plugin->update_transfer_status (db_plugin->cls,
w->exchange->exchange_url,
&w->wtid,
@@ -476,6 +483,7 @@ end_inquiry (struct Inquiry *w)
(at_limit) )
{
at_limit = false;
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&find_work,
NULL);
}
@@ -539,6 +547,11 @@ shutdown_task (void *cls)
db_plugin->event_listen_cancel (eh);
eh = NULL;
}
+ if (NULL != task)
+ {
+ GNUNET_SCHEDULER_cancel (task);
+ task = NULL;
+ }
TALER_MERCHANTDB_plugin_unload (db_plugin);
db_plugin = NULL;
cfg = NULL;
@@ -707,12 +720,32 @@ check_transfer (void *cls,
GNUNET_break (0);
return; /* already had a serious issue; odd that we're called more than once as well... */
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Checking coin with value %s\n",
+ TALER_amount2s (amount_with_fee));
if ( (GNUNET_OK !=
TALER_amount_cmp_currency (amount_with_fee,
&ttd->coin_value)) ||
(0 != TALER_amount_cmp (amount_with_fee,
- &ttd->coin_value)) ||
- (GNUNET_OK !=
+ &ttd->coin_value)) )
+ {
+ /* Disagreement between the exchange and us about how much this
+ coin is worth! */
+ GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Disagreement about coin value %s\n",
+ TALER_amount2s (amount_with_fee));
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Exchange gave it a value of %s\n",
+ TALER_amount2s (&ttd->coin_value));
+ ctc->check_transfer_result = GNUNET_SYSERR;
+ /* Build the `TrackTransferConflictDetails` */
+ ctc->ec = TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS;
+ ctc->failure = true;
+ /* FIXME: this should be reported to the auditor (once the auditor has an API for this) */
+ return;
+ }
+ if ( (GNUNET_OK !=
TALER_amount_cmp_currency (deposit_fee,
&ttd->coin_fee)) ||
(0 != TALER_amount_cmp (deposit_fee,
@@ -721,11 +754,17 @@ check_transfer (void *cls,
/* Disagreement between the exchange and us about how much this
coin is worth! */
GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Expected fee is %s\n",
+ TALER_amount2s (&ttd->coin_fee));
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Fee claimed by exchange is %s\n",
+ TALER_amount2s (deposit_fee));
ctc->check_transfer_result = GNUNET_SYSERR;
/* Build the `TrackTransferConflictDetails` */
ctc->ec = TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS;
ctc->failure = true;
- /* TODO: this should be reported to the auditor! */
+ /* FIXME: this should be reported to the auditor (once the auditor has an API for this) */
return;
}
ctc->check_transfer_result = GNUNET_OK;
@@ -928,6 +967,12 @@ wire_transfer_cb (void *cls,
&w->total)) )
{
GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Wire transfer total value was %s\n",
+ TALER_amount2s (&w->total));
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Exchange claimed total value to be %s\n",
+ TALER_amount2s (&td->total_amount));
update_transaction_status (w,
GNUNET_TIME_UNIT_FOREVER_ABS,
TALER_EC_MERCHANT_EXCHANGE_TRANSFERS_CONFLICTING_TRANSFERS,
@@ -1203,6 +1248,7 @@ run (void *cls,
&transfer_added,
NULL);
}
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&find_work,
NULL);
}
@@ -1248,6 +1294,9 @@ main (int argc,
return EXIT_INVALIDARGUMENT;
if (GNUNET_NO == ret)
return EXIT_SUCCESS;
+ if ( (found_problem) &&
+ (0 == global_ret) )
+ global_ret = 7;
return global_ret;
}
diff --git a/src/backend/taler-merchant-httpd_config.c b/src/backend/taler-merchant-httpd_config.c
index d020985b..3777904f 100644
--- a/src/backend/taler-merchant-httpd_config.c
+++ b/src/backend/taler-merchant-httpd_config.c
@@ -42,7 +42,7 @@
* #MERCHANT_PROTOCOL_CURRENT and #MERCHANT_PROTOCOL_AGE in
* merchant_api_config.c!
*/
-#define MERCHANT_PROTOCOL_VERSION "11:0:7"
+#define MERCHANT_PROTOCOL_VERSION "14:0:10"
/**
@@ -89,9 +89,23 @@ MH_handler_config (struct TMH_RequestHandler *rh,
{
json_t *specs = json_object ();
json_t *exchanges = json_array ();
+ struct GNUNET_TIME_Absolute a;
+ struct GNUNET_TIME_Timestamp km;
+ char dat[128];
GNUNET_assert (NULL != specs);
GNUNET_assert (NULL != exchanges);
+ a = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
+ /* Round up to next full day to ensure the expiration
+ time does not become a fingerprint! */
+ a = GNUNET_TIME_absolute_round_down (a,
+ GNUNET_TIME_UNIT_DAYS);
+ a = GNUNET_TIME_absolute_add (a,
+ GNUNET_TIME_UNIT_DAYS);
+ /* => /config response stays at most 48h in caches! */
+ km = GNUNET_TIME_absolute_to_timestamp (a);
+ TALER_MHD_get_date_string (km.abs_time,
+ dat);
TMH_exchange_get_trusted (&add_exchange,
exchanges);
for (unsigned int i = 0; i<TMH_num_cspecs; i++)
@@ -102,7 +116,8 @@ MH_handler_config (struct TMH_RequestHandler *rh,
GNUNET_assert (0 ==
json_object_set_new (specs,
cspec->currency,
- TALER_CONFIG_currency_specs_to_json (
+ TALER_CONFIG_currency_specs_to_json
+ (
cspec)));
}
response = TALER_MHD_MAKE_JSON_PACK (
@@ -112,12 +127,21 @@ MH_handler_config (struct TMH_RequestHandler *rh,
specs),
GNUNET_JSON_pack_array_steal ("exchanges",
exchanges),
- GNUNET_JSON_pack_string ("implementation",
- "urn:net:taler:specs:merchant:c-reference"),
+ GNUNET_JSON_pack_string (
+ "implementation",
+ "urn:net:taler:specs:taler-merchant:c-reference"),
GNUNET_JSON_pack_string ("name",
"taler-merchant"),
GNUNET_JSON_pack_string ("version",
MERCHANT_PROTOCOL_VERSION));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_EXPIRES,
+ dat));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ "public,max-age=21600")); /* 6h */
}
return MHD_queue_response (connection,
MHD_HTTP_OK,
diff --git a/src/backend/taler-merchant-httpd_get-templates-ID.c b/src/backend/taler-merchant-httpd_get-templates-ID.c
index fee5ec20..add67b4d 100644
--- a/src/backend/taler-merchant-httpd_get-templates-ID.c
+++ b/src/backend/taler-merchant-httpd_get-templates-ID.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2022 Taler Systems SA
+ (C) 2022-2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -61,10 +61,15 @@ TMH_get_templates_ID (
ret = TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_OK,
- GNUNET_JSON_pack_object_steal ("template_contract",
- tp.template_contract));
- GNUNET_free (tp.template_description);
- GNUNET_free (tp.otp_id);
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("required_currency",
+ tp.required_currency)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("editable_defaults",
+ tp.editable_defaults)),
+ GNUNET_JSON_pack_object_incref ("template_contract",
+ tp.template_contract));
+ TALER_MERCHANTDB_template_details_free (&tp);
return ret;
}
}
diff --git a/src/backend/taler-merchant-httpd_helper.c b/src/backend/taler-merchant-httpd_helper.c
index f21b2e48..8fb5823e 100644
--- a/src/backend/taler-merchant-httpd_helper.c
+++ b/src/backend/taler-merchant-httpd_helper.c
@@ -97,7 +97,7 @@ TMH_cmp_wire_account (
bool
TMH_accounts_array_valid (const json_t *accounts)
{
- unsigned int len;
+ size_t len;
if (! json_is_array (accounts))
{
@@ -105,7 +105,7 @@ TMH_accounts_array_valid (const json_t *accounts)
return false;
}
len = json_array_size (accounts);
- for (unsigned int i = 0; i<len; i++)
+ for (size_t i = 0; i<len; i++)
{
json_t *payto_uri = json_array_get (accounts,
i);
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
index e7baf540..50a793a3 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
@@ -186,7 +186,7 @@ struct AbortContext
/**
* Number of coins this abort is for. Length of the @e rd array.
*/
- unsigned int coins_cnt;
+ size_t coins_cnt;
/**
* How often have we retried the 'main' transaction?
@@ -198,7 +198,7 @@ struct AbortContext
* @e coins_cnt, decremented on each transaction that
* successfully finished.
*/
- unsigned int pending;
+ size_t pending;
/**
* Number of transactions still pending for the currently selected
@@ -206,7 +206,7 @@ struct AbortContext
* exchange, decremented on each transaction that successfully
* finished. Once it hits zero, we pick the next exchange.
*/
- unsigned int pending_at_ce;
+ size_t pending_at_ce;
/**
* HTTP status code to use for the reply, i.e 200 for "OK".
@@ -247,7 +247,7 @@ abort_refunds (struct AbortContext *ac)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Aborting pending /deposit operations\n");
- for (unsigned int i = 0; i<ac->coins_cnt; i++)
+ for (size_t i = 0; i<ac->coins_cnt; i++)
{
struct RefundDetails *rdi = &ac->rd[i];
@@ -356,7 +356,7 @@ generate_success_response (struct AbortContext *ac)
"could not create JSON array");
return;
}
- for (unsigned int i = 0; i<ac->coins_cnt; i++)
+ for (size_t i = 0; i<ac->coins_cnt; i++)
{
struct RefundDetails *rdi = &ac->rd[i];
json_t *detail;
@@ -424,7 +424,7 @@ abort_context_cleanup (void *cls)
ac->timeout_task = NULL;
}
abort_refunds (ac);
- for (unsigned int i = 0; i<ac->coins_cnt; i++)
+ for (size_t i = 0; i<ac->coins_cnt; i++)
{
struct RefundDetails *rdi = &ac->rd[i];
@@ -522,7 +522,7 @@ process_abort_with_exchange (void *cls,
/* Initiate refund operation for all coins of
the current exchange (!) */
GNUNET_assert (0 == ac->pending_at_ce);
- for (unsigned int i = 0; i<ac->coins_cnt; i++)
+ for (size_t i = 0; i<ac->coins_cnt; i++)
{
struct RefundDetails *rdi = &ac->rd[i];
@@ -579,7 +579,7 @@ begin_transaction (struct AbortContext *ac);
static void
find_next_exchange (struct AbortContext *ac)
{
- for (unsigned int i = 0; i<ac->coins_cnt; i++)
+ for (size_t i = 0; i<ac->coins_cnt; i++)
{
struct RefundDetails *rdi = &ac->rd[i];
@@ -635,7 +635,7 @@ refund_coins (void *cls,
(void) deposit_fee;
(void) refund_fee;
now = GNUNET_TIME_timestamp_get ();
- for (unsigned int i = 0; i<ac->coins_cnt; i++)
+ for (size_t i = 0; i<ac->coins_cnt; i++)
{
struct RefundDetails *rdi = &ac->rd[i];
enum GNUNET_DB_QueryStatus qs;
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
index a1fdabec..14edfd55 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -418,7 +418,7 @@ struct PayContext
* Number of coins this payment is made of. Length
* of the @e dc array.
*/
- unsigned int coins_cnt;
+ size_t coins_cnt;
/**
* Number of exchanges involved in the payment. Length
@@ -477,69 +477,6 @@ struct PayContext
/**
- * Active KYC operation with an exchange.
- */
-struct KycContext
-{
- /**
- * Kept in a DLL.
- */
- struct KycContext *next;
-
- /**
- * Kept in a DLL.
- */
- struct KycContext *prev;
-
- /**
- * Looking for the exchange.
- */
- struct TMH_EXCHANGES_KeysOperation *fo;
-
- /**
- * Exchange this is about.
- */
- char *exchange_url;
-
- /**
- * Merchant instance this is for.
- */
- struct TMH_MerchantInstance *mi;
-
- /**
- * Wire method we are checking the status of.
- */
- struct TMH_WireMethod *wm;
-
- /**
- * Handle for the GET /deposits operation.
- */
- struct TALER_EXCHANGE_DepositGetHandle *dg;
-
- /**
- * Contract we are looking up.
- */
- struct TALER_PrivateContractHashP h_contract_terms;
-
- /**
- * Coin we are looking up.
- */
- struct TALER_CoinSpendPublicKeyP coin_pub;
-
- /**
- * Initial DB timestamp.
- */
- struct GNUNET_TIME_Timestamp kyc_timestamp;
-
- /**
- * Initial KYC status.
- */
- bool kyc_ok;
-
-};
-
-
-/**
* Head of active pay context DLL.
*/
static struct PayContext *pc_head;
@@ -549,57 +486,10 @@ static struct PayContext *pc_head;
*/
static struct PayContext *pc_tail;
-/**
- * Head of active KYC context DLL.
- */
-static struct KycContext *kc_head;
-
-/**
- * Tail of active KYC context DLL.
- */
-static struct KycContext *kc_tail;
-
-
-/**
- * Free resources used by @a kc.
- *
- * @param[in] kc object to free
- */
-static void
-destroy_kc (struct KycContext *kc)
-{
- if (NULL != kc->fo)
- {
- TMH_EXCHANGES_keys4exchange_cancel (kc->fo);
- kc->fo = NULL;
- }
- if (NULL != kc->dg)
- {
- TALER_EXCHANGE_deposits_get_cancel (kc->dg);
- kc->dg = NULL;
- }
- TMH_instance_decref (kc->mi);
- kc->mi = NULL;
- GNUNET_free (kc->exchange_url);
- GNUNET_CONTAINER_DLL_remove (kc_head,
- kc_tail,
- kc);
- GNUNET_free (kc);
-}
-
void
TMH_force_pc_resume ()
{
- struct KycContext *kc;
-
- while (NULL != (kc = kc_head))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Aborting KYC check at %s\n",
- kc->exchange_url);
- destroy_kc (kc);
- }
for (struct PayContext *pc = pc_head;
NULL != pc;
pc = pc->next)
@@ -652,7 +542,6 @@ resume_pay_with_response (struct PayContext *pc,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Resuming /pay handling. HTTP status for our reply is %u.\n",
response_code);
-#if 1
for (unsigned int i = 0; i<pc->num_exchanges; i++)
{
struct ExchangeGroup *eg = pc->egs[i];
@@ -671,7 +560,6 @@ resume_pay_with_response (struct PayContext *pc,
}
}
GNUNET_assert (0 == pc->pending_at_eg);
-#endif
if (NULL != pc->timeout_task)
{
GNUNET_SCHEDULER_cancel (pc->timeout_task);
@@ -744,221 +632,6 @@ phase_return_response (struct PayContext *pc)
/**
- * Function called with detailed wire transfer data.
- *
- * @param cls a `struct KycContext *`
- * @param dr HTTP response data
- */
-static void
-deposit_get_callback (
- void *cls,
- const struct TALER_EXCHANGE_GetDepositResponse *dr)
-{
- struct KycContext *kc = cls;
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_TIME_Timestamp now;
-
- kc->dg = NULL;
- now = GNUNET_TIME_timestamp_get ();
- switch (dr->hr.http_status)
- {
- case MHD_HTTP_OK:
- qs = TMH_db->account_kyc_set_status (
- TMH_db->cls,
- kc->mi->settings.id,
- &kc->wm->h_wire,
- kc->exchange_url,
- 0LL,
- NULL, /* no signature */
- NULL, /* no signature */
- now,
- true,
- TALER_AML_NORMAL);
- GNUNET_break (qs > 0);
- break;
- case MHD_HTTP_ACCEPTED:
- qs = TMH_db->account_kyc_set_status (
- TMH_db->cls,
- kc->mi->settings.id,
- &kc->wm->h_wire,
- kc->exchange_url,
- dr->details.accepted.requirement_row,
- NULL, /* no signature */
- NULL, /* no signature */
- now,
- dr->details.accepted.kyc_ok,
- dr->details.accepted.aml_decision);
- GNUNET_break (qs > 0);
- break;
- default:
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "KYC check failed at %s with unexpected status %u\n",
- kc->exchange_url,
- dr->hr.http_status);
- }
- destroy_kc (kc);
-}
-
-
-/**
- * Function called with the result of our exchange lookup.
- *
- * @param cls the `struct KycContext`
- * @param keys NULL if exchange was not found to be acceptable
- * @param exchange representation of the exchange
- */
-static void
-process_kyc_with_exchange (
- void *cls,
- struct TALER_EXCHANGE_Keys *keys,
- struct TMH_Exchange *exchange)
-{
- struct KycContext *kc = cls;
-
- (void) exchange;
- kc->fo = NULL;
- if (NULL == keys)
- {
- destroy_kc (kc);
- return;
- }
- kc->dg = TALER_EXCHANGE_deposits_get (
- TMH_curl_ctx,
- kc->exchange_url,
- keys,
- &kc->mi->merchant_priv,
- &kc->wm->h_wire,
- &kc->h_contract_terms,
- &kc->coin_pub,
- GNUNET_TIME_UNIT_ZERO,
- &deposit_get_callback,
- kc);
- if (NULL == kc->dg)
- {
- GNUNET_break (0);
- destroy_kc (kc);
- }
-}
-
-
-/**
- * Function called from ``account_kyc_get_status``
- * with KYC status information for this merchant.
- *
- * @param cls a `struct KycContext *`
- * @param h_wire hash of the wire account
- * @param exchange_kyc_serial serial number for the KYC process at the exchange, 0 if unknown
- * @param payto_uri payto:// URI of the merchant's bank account
- * @param exchange_url base URL of the exchange for which this is a status
- * @param last_check when did we last get an update on our KYC status from the exchange
- * @param kyc_ok true if we satisfied the KYC requirements
- * @param aml_decision latest AML decision by the exchange
- */
-static void
-kyc_cb (
- void *cls,
- const struct TALER_MerchantWireHashP *h_wire,
- uint64_t exchange_kyc_serial,
- const char *payto_uri,
- const char *exchange_url,
- struct GNUNET_TIME_Timestamp last_check,
- bool kyc_ok,
- enum TALER_AmlDecisionState aml_decision)
-{
- struct KycContext *kc = cls;
-
- (void) h_wire;
- (void) exchange_kyc_serial;
- (void) payto_uri;
- (void) exchange_url;
- kc->kyc_timestamp = last_check;
- kc->kyc_ok = kyc_ok;
- /* FIXME: act on aml_decision? */
-}
-
-
-/**
- * Check for our KYC status at @a exchange_url for the
- * payment of @a pc. First checks if we already have a
- * positive result from the exchange, and if not checks
- * with the exchange.
- *
- * @param pc payment context to use as starting point
- * @param eg exchange group of the exchange we are triggering on
- */
-static void
-check_kyc (struct PayContext *pc,
- const struct ExchangeGroup *eg)
-{
- enum GNUNET_DB_QueryStatus qs;
- struct KycContext *kc;
-
- kc = GNUNET_new (struct KycContext);
- qs = TMH_db->account_kyc_get_status (TMH_db->cls,
- pc->hc->instance->settings.id,
- &pc->wm->h_wire,
- eg->exchange_url,
- &kyc_cb,
- kc);
- if (qs < 0)
- {
- GNUNET_break (0);
- GNUNET_free (kc);
- return;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- if (kc->kyc_ok)
- {
- GNUNET_free (kc);
- return; /* we are done */
- }
- if (GNUNET_TIME_relative_cmp (
- GNUNET_TIME_absolute_get_duration (
- kc->kyc_timestamp.abs_time),
- <,
- KYC_RETRY_FREQUENCY))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Not re-checking KYC status at `%s', as we already recently asked\n",
- eg->exchange_url);
- GNUNET_free (kc);
- return;
- }
- }
- kc->mi = pc->hc->instance;
- kc->mi->rc++;
- kc->wm = pc->wm;
- kc->exchange_url = GNUNET_strdup (eg->exchange_url);
- kc->h_contract_terms = pc->h_contract_terms;
- /* find one of the coins of the batch */
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
- {
- struct DepositConfirmation *dc = &pc->dc[i];
-
- if (0 != strcmp (eg->exchange_url,
- pc->dc[i].exchange_url))
- continue;
- kc->coin_pub = dc->cdd.coin_pub;
- break;
- }
- GNUNET_CONTAINER_DLL_insert (kc_head,
- kc_tail,
- kc);
- kc->fo = TMH_EXCHANGES_keys4exchange (
- kc->exchange_url,
- false,
- &process_kyc_with_exchange,
- kc);
- if (NULL == kc->fo)
- {
- GNUNET_break (0);
- destroy_kc (kc);
- }
-}
-
-
-/**
* Do database transaction for a completed batch deposit.
*
* @param eg group that completed
@@ -978,7 +651,7 @@ batch_deposit_transaction (const struct ExchangeGroup *eg,
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (pc->amount.currency,
&total_without_fees));
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
struct TALER_Amount amount_without_fees;
@@ -1014,7 +687,7 @@ batch_deposit_transaction (const struct ExchangeGroup *eg,
if (qs <= 0)
return qs; /* Entire batch already known or failure, we're done */
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
@@ -1121,7 +794,7 @@ handle_batch_deposit_ok (struct ExchangeGroup *eg,
}
/* Transaction is done, mark affected coins as complete as well. */
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
@@ -1133,8 +806,6 @@ handle_batch_deposit_ok (struct ExchangeGroup *eg,
dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */
pc->pending--;
}
- check_kyc (pc,
- eg);
}
@@ -1313,7 +984,7 @@ process_pay_with_keys (
/* Initiate /batch-deposit operation for all coins of
the current exchange (!) */
group_size = 0;
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
@@ -1471,9 +1142,9 @@ AGE_FAIL:
.refund_deadline = pc->refund_deadline
};
enum TALER_ErrorCode ec;
- unsigned int off = 0;
+ size_t off = 0;
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
@@ -1577,9 +1248,9 @@ get_pay_timeout (unsigned int num_coins)
{
struct GNUNET_TIME_Relative t;
- /* FIXME: Do some benchmarking to come up with a better timeout.
- * We've increased this value so the wallet integration test passes again
- * on my (Florian) machine.
+ /* FIXME-Performance-Optimization: Do some benchmarking to come up with a
+ * better timeout. We've increased this value so the wallet integration
+ * test passes again on my (Florian) machine.
*/
t = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
15 * (1 + (num_coins / 5)));
@@ -1602,7 +1273,7 @@ phase_batch_deposits (struct PayContext *pc)
struct ExchangeGroup *eg = pc->egs[i];
bool have_coins = false;
- for (unsigned int j = 0; j<pc->coins_cnt; j++)
+ for (size_t j = 0; j<pc->coins_cnt; j++)
{
struct DepositConfirmation *dc = &pc->dc[j];
@@ -1764,7 +1435,7 @@ check_coin_paid (void *cls,
{
struct PayContext *pc = cls;
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
@@ -1838,7 +1509,7 @@ check_coin_refunded (void *cls,
an abort-pay refund (an unusual but possible case), we need
to make sure that existing refunds are accounted for. */
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
@@ -1924,7 +1595,7 @@ check_payment_sufficient (struct PayContext *pc)
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (pc->amount.currency,
&acc_amount));
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
@@ -2161,7 +1832,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
GNUNET_break (GNUNET_OK ==
TALER_amount_set_zero (pc->amount.currency,
&pc->total_refunded));
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
pc->dc[i].found_in_db = false;
pc->pending = pc->coins_cnt;
@@ -2361,9 +2032,10 @@ phase_execute_pay_transaction (struct PayContext *pc)
* @param cls closure with our `struct PayContext *`
* @param deposit_serial which deposit operation is this about
* @param exchange_url URL of the exchange that issued the coin
+ * @param h_wire hash of merchant's wire details
+ * @param deposit_timestamp when was the deposit made
* @param amount_with_fee amount the exchange will deposit for this coin
* @param deposit_fee fee the exchange will charge for this coin
- * @param h_wire hash of merchant's wire details
* @param coin_pub public key of the coin
*/
static void
@@ -2372,13 +2044,14 @@ deposit_paid_check (
uint64_t deposit_serial,
const char *exchange_url,
const struct TALER_MerchantWireHashP *h_wire,
+ struct GNUNET_TIME_Timestamp deposit_timestamp,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
const struct TALER_CoinSpendPublicKeyP *coin_pub)
{
struct PayContext *pc = cls;
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dci = &pc->dc[i];
@@ -2429,7 +2102,7 @@ phase_contract_paid (struct PayContext *pc)
"lookup_deposits_by_order"));
return;
}
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dci = &pc->dc[i];
@@ -2461,7 +2134,7 @@ phase_contract_paid (struct PayContext *pc)
pc->order_id);
refunds = json_array ();
GNUNET_assert (NULL != refunds);
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dci = &pc->dc[i];
struct TALER_MerchantSignatureP merchant_sig;
@@ -2655,7 +2328,7 @@ phase_check_contract (struct PayContext *pc)
return;
}
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
index e5595296..134cd2ee 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
@@ -565,7 +565,8 @@ TMH_post_orders_ID_refund (const struct TMH_RequestHandler *rh,
enum GNUNET_GenericReturnValue res;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("h_contract", &prd->h_contract_terms),
+ GNUNET_JSON_spec_fixed_auto ("h_contract",
+ &prd->h_contract_terms),
GNUNET_JSON_spec_end ()
};
res = TALER_MHD_parse_json_data (connection,
@@ -666,8 +667,9 @@ TMH_post_orders_ID_refund (const struct TMH_RequestHandler *rh,
}
{
- GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TMH_currency,
- &prd->refund_amount));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (TMH_currency,
+ &prd->refund_amount));
qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
hc->instance->settings.id,
&prd->h_contract_terms,
diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
index 1c850990..98653997 100644
--- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
@@ -263,6 +263,11 @@ struct GetOrderRequestContext
struct GNUNET_TIME_Timestamp timestamp;
/**
+ * Timestamp of the last payment.
+ */
+ struct GNUNET_TIME_Timestamp last_payment;
+
+ /**
* Order summary. Pointer into @e contract_terms.
*/
const char *summary;
@@ -996,15 +1001,16 @@ phase_unpaid_finish (struct GetOrderRequestContext *gorc)
* @param pending true if the this refund was not yet processed by the wallet/exchange
*/
static void
-process_refunds_cb (void *cls,
- uint64_t refund_serial,
- struct GNUNET_TIME_Timestamp timestamp,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const char *exchange_url,
- uint64_t rtransaction_id,
- const char *reason,
- const struct TALER_Amount *refund_amount,
- bool pending)
+process_refunds_cb (
+ void *cls,
+ uint64_t refund_serial,
+ struct GNUNET_TIME_Timestamp timestamp,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const char *exchange_url,
+ uint64_t rtransaction_id,
+ const char *reason,
+ const struct TALER_Amount *refund_amount,
+ bool pending)
{
struct GetOrderRequestContext *gorc = cls;
@@ -1013,18 +1019,19 @@ process_refunds_cb (void *cls,
(unsigned long long) rtransaction_id,
TALER_amount2s (refund_amount),
reason);
- GNUNET_assert (0 ==
- json_array_append_new (
- gorc->refund_details,
- GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("amount",
- refund_amount),
- GNUNET_JSON_pack_bool ("pending",
- pending),
- GNUNET_JSON_pack_timestamp ("timestamp",
- timestamp),
- GNUNET_JSON_pack_string ("reason",
- reason))));
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ gorc->refund_details,
+ GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("amount",
+ refund_amount),
+ GNUNET_JSON_pack_bool ("pending",
+ pending),
+ GNUNET_JSON_pack_timestamp ("timestamp",
+ timestamp),
+ GNUNET_JSON_pack_string ("reason",
+ reason))));
/* For refunded coins, we are not charged deposit fees, so subtract those
again */
for (struct TransferQuery *tq = gorc->tq_head;
@@ -1044,10 +1051,11 @@ process_refunds_cb (void *cls,
return;
}
- GNUNET_assert (0 <=
- TALER_amount_subtract (&gorc->deposit_fees_total,
- &gorc->deposit_fees_total,
- &tq->deposit_fee));
+ GNUNET_assert (
+ 0 <=
+ TALER_amount_subtract (&gorc->deposit_fees_total,
+ &gorc->deposit_fees_total,
+ &tq->deposit_fee));
}
}
if (GNUNET_OK !=
@@ -1084,29 +1092,32 @@ phase_check_refunds (struct GetOrderRequestContext *gorc)
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (gorc->contract_amount.currency,
&gorc->refund_amount));
- qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
- hc->instance->settings.id,
- &gorc->h_contract_terms,
- &process_refunds_cb,
- gorc);
+ qs = TMH_db->lookup_refunds_detailed (
+ TMH_db->cls,
+ hc->instance->settings.id,
+ &gorc->h_contract_terms,
+ &process_refunds_cb,
+ gorc);
if (0 > qs)
{
GNUNET_break (0);
phase_end (gorc,
- TALER_MHD_reply_with_error (gorc->sc.con,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "detailed refunds"));
+ TALER_MHD_reply_with_error (
+ gorc->sc.con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "detailed refunds"));
return;
}
if (gorc->refund_currency_mismatch)
{
GNUNET_break (0);
phase_end (gorc,
- TALER_MHD_reply_with_error (gorc->sc.con,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "refunds in different currency than original order price"));
+ TALER_MHD_reply_with_error (
+ gorc->sc.con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "refunds in different currency than original order price"));
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -1127,19 +1138,22 @@ phase_check_refunds (struct GetOrderRequestContext *gorc)
* @param cls a `struct GetOrderRequestContext`
* @param deposit_serial identifies the deposit operation
* @param exchange_url URL of the exchange that issued @a coin_pub
+ * @param h_wire hash of the merchant's wire account into which the deposit was made
+ * @param deposit_timestamp when was the deposit made
* @param amount_with_fee amount the exchange will deposit for this coin
* @param deposit_fee fee the exchange will charge for this coin
- * @param h_wire hash of the merchant's wire account into which the deposit was made
* @param coin_pub public key of the deposited coin
*/
static void
-deposit_cb (void *cls,
- uint64_t deposit_serial,
- const char *exchange_url,
- const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_Amount *amount_with_fee,
- const struct TALER_Amount *deposit_fee,
- const struct TALER_CoinSpendPublicKeyP *coin_pub)
+deposit_cb (
+ void *cls,
+ uint64_t deposit_serial,
+ const char *exchange_url,
+ const struct TALER_MerchantWireHashP *h_wire,
+ struct GNUNET_TIME_Timestamp deposit_timestamp,
+ const struct TALER_Amount *amount_with_fee,
+ const struct TALER_Amount *deposit_fee,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub)
{
struct GetOrderRequestContext *gorc = cls;
struct TransferQuery *tq;
@@ -1148,6 +1162,9 @@ deposit_cb (void *cls,
"Checking deposit status for coin %s (over %s)\n",
TALER_B2S (coin_pub),
TALER_amount2s (amount_with_fee));
+ gorc->last_payment
+ = GNUNET_TIME_timestamp_max (gorc->last_payment,
+ deposit_timestamp);
tq = GNUNET_new (struct TransferQuery);
tq->gorc = gorc;
tq->exchange_url = GNUNET_strdup (exchange_url);
@@ -1380,7 +1397,12 @@ phase_reply_result (struct GetOrderRequestContext *gorc)
&gorc->claim_token,
h_contract);
}
-
+ if (GNUNET_TIME_absolute_is_zero (gorc->last_payment.abs_time))
+ {
+ GNUNET_break (GNUNET_YES ==
+ TALER_amount_is_zero (&gorc->contract_amount));
+ gorc->last_payment = gorc->timestamp;
+ }
ret = TALER_MHD_REPLY_JSON_PACK (
gorc->sc.con,
MHD_HTTP_OK,
@@ -1403,6 +1425,8 @@ phase_reply_result (struct GetOrderRequestContext *gorc)
gorc->contract_terms),
GNUNET_JSON_pack_string ("order_status",
"paid"),
+ GNUNET_JSON_pack_timestamp ("last_payment",
+ gorc->last_payment),
GNUNET_JSON_pack_bool ("refunded",
gorc->refunded),
GNUNET_JSON_pack_bool ("wired",
@@ -1443,9 +1467,10 @@ phase_error (struct GetOrderRequestContext *gorc)
MHD_RESULT
-TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc)
+TMH_private_get_orders_ID (
+ const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
{
struct GetOrderRequestContext *gorc = hc->ctx;
diff --git a/src/backend/taler-merchant-httpd_private-get-orders.c b/src/backend/taler-merchant-httpd_private-get-orders.c
index 92a1f389..4c6a104e 100644
--- a/src/backend/taler-merchant-httpd_private-get-orders.c
+++ b/src/backend/taler-merchant-httpd_private-get-orders.c
@@ -406,6 +406,18 @@ add_order (void *cls,
return;
}
+ if (TALER_amount_is_zero (&order_amount) &&
+ (po->of.wired != TALER_EXCHANGE_YNA_ALL) )
+ {
+ /* If we are actually filtering by wire status,
+ and the order was over an amount of zero,
+ do not return it as wire status is not
+ exactly meaningful for orders over zero. */
+ json_decref (contract_terms);
+ GNUNET_free (order_id);
+ return;
+ }
+
if (GNUNET_TIME_absolute_is_future (rd.abs_time) &&
paid)
{
@@ -687,44 +699,23 @@ TMH_private_get_orders (const struct TMH_RequestHandler *rh,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"wired");
+ po->of.delta = -20;
+ /* deprecated in protocol v12 */
+ TALER_MHD_parse_request_snumber (connection,
+ "delta",
+ &po->of.delta);
+ /* since protocol v12 */
+ TALER_MHD_parse_request_snumber (connection,
+ "limit",
+ &po->of.delta);
+ if ( (-MAX_DELTA > po->of.delta) ||
+ (po->of.delta > MAX_DELTA) )
{
- const char *delta_str;
-
- delta_str = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "delta");
- if (NULL == delta_str)
- {
- po->of.delta = -20;
- }
- else
- {
- char dummy;
- long long ll;
-
- if (1 !=
- sscanf (delta_str,
- "%lld%c",
- &ll,
- &dummy))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "delta");
- }
- po->of.delta = (int64_t) ll;
- if ( (-MAX_DELTA > po->of.delta) ||
- (po->of.delta > MAX_DELTA) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "delta");
- }
- }
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "delta");
}
{
const char *date_s_str;
@@ -769,43 +760,25 @@ TMH_private_get_orders (const struct TMH_RequestHandler *rh,
}
}
}
+ if (po->of.delta > 0)
+ po->of.start_row = 0;
+ else
+ po->of.start_row = INT64_MAX;
+ /* deprecated in protocol v12 */
+ TALER_MHD_parse_request_number (connection,
+ "start",
+ &po->of.start_row);
+ /* since protocol v12 */
+ TALER_MHD_parse_request_number (connection,
+ "offset",
+ &po->of.start_row);
+ if (INT64_MAX < po->of.start_row)
{
- const char *start_row_str;
-
- start_row_str = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "start");
- if (NULL == start_row_str)
- {
- if (po->of.delta > 0)
- po->of.start_row = 0;
- else
- po->of.start_row = INT64_MAX;
- }
- else
- {
- char dummy;
- unsigned long long ull;
-
- if (1 !=
- sscanf (start_row_str,
- "%llu%c",
- &ull,
- &dummy))
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "start");
- po->of.start_row = (uint64_t) ull;
- if (INT64_MAX < po->of.start_row)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "start");
- }
- }
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "start");
}
po->of.session_id
= MHD_lookup_connection_value (connection,
diff --git a/src/backend/taler-merchant-httpd_private-get-products.c b/src/backend/taler-merchant-httpd_private-get-products.c
index bc90c94d..d9fa4e49 100644
--- a/src/backend/taler-merchant-httpd_private-get-products.c
+++ b/src/backend/taler-merchant-httpd_private-get-products.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2019, 2020, 2021 Taler Systems SA
+ (C) 2019, 2020, 2021, 2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -30,6 +30,7 @@
*/
static void
add_product (void *cls,
+ uint64_t product_serial,
const char *product_id)
{
json_t *pa = cls;
@@ -38,6 +39,8 @@ add_product (void *cls,
json_array_append_new (
pa,
GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("product_serial",
+ product_serial),
GNUNET_JSON_pack_string ("product_id",
product_id))));
}
@@ -50,11 +53,26 @@ TMH_private_get_products (const struct TMH_RequestHandler *rh,
{
json_t *pa;
enum GNUNET_DB_QueryStatus qs;
+ int64_t limit;
+ uint64_t offset;
+ limit = 20; /* default */
+ TALER_MHD_parse_request_snumber (connection,
+ "limit",
+ &limit);
+ if (limit < 0)
+ offset = INT64_MAX;
+ else
+ offset = 0;
+ TALER_MHD_parse_request_number (connection,
+ "offset",
+ &offset);
pa = json_array ();
GNUNET_assert (NULL != pa);
qs = TMH_db->lookup_products (TMH_db->cls,
hc->instance->settings.id,
+ offset,
+ limit,
&add_product,
pa);
if (0 > qs)
diff --git a/src/backend/taler-merchant-httpd_private-get-templates-ID.c b/src/backend/taler-merchant-httpd_private-get-templates-ID.c
index a5b05f0c..35fdd1d0 100644
--- a/src/backend/taler-merchant-httpd_private-get-templates-ID.c
+++ b/src/backend/taler-merchant-httpd_private-get-templates-ID.c
@@ -61,15 +61,20 @@ TMH_private_get_templates_ID (
ret = TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_OK,
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("required_currency",
+ tp.required_currency)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("editable_defaults",
+ tp.editable_defaults)),
GNUNET_JSON_pack_string ("template_description",
tp.template_description),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("otp_id",
tp.otp_id)),
- GNUNET_JSON_pack_object_steal ("template_contract",
- tp.template_contract));
- GNUNET_free (tp.template_description);
- GNUNET_free (tp.otp_id);
+ GNUNET_JSON_pack_object_incref ("template_contract",
+ tp.template_contract));
+ TALER_MERCHANTDB_template_details_free (&tp);
return ret;
}
}
diff --git a/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c b/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c
index b7c8ab4d..06dbbdf9 100644
--- a/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c
+++ b/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c
@@ -72,9 +72,9 @@ TMH_private_get_tokenfamilies_SLUG (const struct TMH_RequestHandler *rh,
}
else
{
+ GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
- // TODO: What error code to use here?
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
"invalid_token_family_kind");
}
@@ -86,12 +86,13 @@ TMH_private_get_tokenfamilies_SLUG (const struct TMH_RequestHandler *rh,
MHD_HTTP_OK,
GNUNET_JSON_pack_string ("name", details.name),
GNUNET_JSON_pack_string ("description", details.description),
- GNUNET_JSON_pack_object_steal ("description_i18n", details.description_i18n),
+ GNUNET_JSON_pack_object_steal ("description_i18n",
+ details.description_i18n),
GNUNET_JSON_pack_timestamp ("valid_after", details.valid_after),
GNUNET_JSON_pack_timestamp ("valid_before", details.valid_before),
GNUNET_JSON_pack_time_rel ("duration", details.duration),
GNUNET_JSON_pack_string ("kind", kind)
- );
+ );
GNUNET_free (details.name);
GNUNET_free (details.description);
diff --git a/src/backend/taler-merchant-httpd_private-get-transfers.c b/src/backend/taler-merchant-httpd_private-get-transfers.c
index c43781dd..3e540297 100644
--- a/src/backend/taler-merchant-httpd_private-get-transfers.c
+++ b/src/backend/taler-merchant-httpd_private-get-transfers.c
@@ -137,59 +137,16 @@ TMH_private_get_transfers (const struct TMH_RequestHandler *rh,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"after");
}
- {
- const char *limit_s;
-
- limit_s = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "limit");
- if (NULL != limit_s)
- {
- char dummy[2];
- long long l;
-
- if (1 !=
- sscanf (limit_s,
- "%lld%1s",
- &l,
- dummy))
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "limit");
- limit = (int64_t) l;
- }
- }
- {
- const char *offset_s;
-
- offset_s = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "offset");
- if (NULL != offset_s)
- {
- char dummy[2];
- unsigned long long o;
-
- if (1 !=
- sscanf (offset_s,
- "%llu%1s",
- &o,
- dummy))
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "offset");
- offset = (uint64_t) o;
- }
- else
- {
- if (limit < 0)
- offset = INT64_MAX;
- else
- offset = 0;
- }
- }
+ TALER_MHD_parse_request_snumber (connection,
+ "limit",
+ &limit);
+ if (limit < 0)
+ offset = INT64_MAX;
+ else
+ offset = 0;
+ TALER_MHD_parse_request_number (connection,
+ "offset",
+ &offset);
if (! (TALER_arg_to_yna (connection,
"verified",
TALER_EXCHANGE_YNA_ALL,
diff --git a/src/backend/taler-merchant-httpd_private-patch-templates-ID.c b/src/backend/taler-merchant-httpd_private-patch-templates-ID.c
index 68e0a478..e8a6c531 100644
--- a/src/backend/taler-merchant-httpd_private-patch-templates-ID.c
+++ b/src/backend/taler-merchant-httpd_private-patch-templates-ID.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2022 Taler Systems SA
+ (C) 2022, 2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -112,6 +112,14 @@ TMH_private_patch_templates_ID (const struct TMH_RequestHandler *rh,
NULL),
GNUNET_JSON_spec_json ("template_contract",
&tp.template_contract),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("required_currency",
+ (const char **) &tp.required_currency),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("editable_defaults",
+ &tp.editable_defaults),
+ NULL),
GNUNET_JSON_spec_end ()
};
@@ -138,6 +146,56 @@ TMH_private_patch_templates_ID (const struct TMH_RequestHandler *rh,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"template_contract");
}
+ if ( (NULL != tp.required_currency) &&
+ (GNUNET_OK !=
+ TALER_check_currency (tp.required_currency)) )
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "required_currency");
+ }
+ if ( (NULL != tp.required_currency) &&
+ (NULL != json_object_get (tp.template_contract,
+ "amount")) )
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "required_currency and contract::amount specified");
+ }
+ if (NULL != tp.editable_defaults)
+ {
+ const char *key;
+ json_t *val;
+
+ json_object_foreach (tp.editable_defaults, key, val)
+ {
+ if (NULL !=
+ json_object_get (tp.template_contract,
+ key))
+ {
+ char *msg;
+ MHD_RESULT ret;
+
+ GNUNET_break_op (0);
+ GNUNET_asprintf (&msg,
+ "editable_defaults::%s conflicts with template_contract",
+ key);
+ GNUNET_JSON_parse_free (spec);
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ msg);
+ GNUNET_free (msg);
+ return ret;
+ }
+ }
+ }
qs = TMH_db->update_template (TMH_db->cls,
mi->settings.id,
diff --git a/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c b/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c
index c49d80d7..755ed4c9 100644
--- a/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c
+++ b/src/backend/taler-merchant-httpd_private-patch-token-families-SLUG.c
@@ -136,10 +136,9 @@ TMH_private_patch_token_family_SLUG (const struct TMH_RequestHandler *rh,
"unexpected serialization problem");
break;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- // TODO: Add error code for token family not found
ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
- 0,
+ TALER_EC_MERCHANT_PATCH_TOKEN_FAMILY_NOT_FOUND,
slug);
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c
index 8a093813..b2070bcc 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -49,6 +49,11 @@
#define MAX_RETRIES 3
/**
+ * Maximum number of inventory products per order.
+ */
+#define MAX_PRODUCTS 1024
+
+/**
* What is the label under which we find/place the merchant's
* jurisdiction in the locations list by default?
*/
@@ -346,6 +351,11 @@ struct OrderContext
*/
const json_t *extra;
+ /**
+ * Minimum age required by the order.
+ */
+ uint32_t minimum_age;
+
} parse_order;
/**
@@ -451,6 +461,24 @@ struct OrderContext
* Which product (by offset) is out of stock, UINT_MAX if all were in-stock.
*/
unsigned int out_of_stock_index;
+
+ /**
+ * Set to a previous claim token *if* @e idempotent
+ * is also true.
+ */
+ struct TALER_ClaimTokenP token;
+
+ /**
+ * Set to true if the order was idempotent and there
+ * was an equivalent one before.
+ */
+ bool idempotent;
+
+ /**
+ * Set to true if the order is in conflict with a
+ * previous order with the same order ID.
+ */
+ bool conflict;
} execute_order;
struct
@@ -668,7 +696,6 @@ clean_order (void *cls)
oc->parse_request.uuids_length,
0);
json_decref (oc->parse_request.order);
- /* TODO: Check that all other fields are cleaned up! */
json_decref (oc->serialize_order.contract);
GNUNET_free (oc->parse_order.merchant_base_url);
GNUNET_free (oc);
@@ -695,6 +722,45 @@ execute_transaction (struct OrderContext *oc)
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
+
+ /* Test if we already have an order with this id */
+ {
+ json_t *contract_terms;
+ struct TALER_MerchantPostDataHashP orig_post;
+
+ qs = TMH_db->lookup_order (TMH_db->cls,
+ oc->hc->instance->settings.id,
+ oc->parse_order.order_id,
+ &oc->execute_order.token,
+ &orig_post,
+ &contract_terms);
+ /* If yes, check for idempotency */
+ if (0 > qs)
+ {
+ GNUNET_break (0);
+ TMH_db->rollback (TMH_db->cls);
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ {
+ TMH_db->rollback (TMH_db->cls);
+ json_decref (contract_terms);
+ /* Comparing the contract terms is sufficient because all the other
+ params get added to it at some point. */
+ if (0 == GNUNET_memcmp (&orig_post,
+ &oc->parse_request.h_post_data))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Order creation idempotent\n");
+ oc->execute_order.idempotent = true;
+ return qs;
+ }
+ GNUNET_break_op (0);
+ oc->execute_order.conflict = true;
+ return qs;
+ }
+ }
+
/* Setup order */
qs = TMH_db->insert_order (TMH_db->cls,
oc->hc->instance->settings.id,
@@ -791,70 +857,6 @@ execute_order (struct OrderContext *oc)
&oc->hc->instance->settings;
enum GNUNET_DB_QueryStatus qs;
- /* Test if we already have an order with this id */
- /* FIXME: this should be done within the main
- transaction! */
- {
- struct TALER_ClaimTokenP token;
- json_t *contract_terms;
- struct TALER_MerchantPostDataHashP orig_post;
-
- TMH_db->preflight (TMH_db->cls);
- qs = TMH_db->lookup_order (TMH_db->cls,
- oc->hc->instance->settings.id,
- oc->parse_order.order_id,
- &token,
- &orig_post,
- &contract_terms);
- /* If yes, check for idempotency */
- if (0 > qs)
- {
- GNUNET_break (0);
- TMH_db->rollback (TMH_db->cls);
- reply_with_error (oc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "lookup_order");
- return;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- MHD_RESULT ret;
-
- json_decref (contract_terms);
- /* Comparing the contract terms is sufficient because all the other
- params get added to it at some point. */
- if (0 == GNUNET_memcmp (&orig_post,
- &oc->parse_request.h_post_data))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Order creation idempotent\n");
- ret = TALER_MHD_REPLY_JSON_PACK (
- oc->connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_string ("order_id",
- oc->parse_order.order_id),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_data_varsize (
- "token",
- GNUNET_is_zero (&token)
- ? NULL
- : &token,
- sizeof (token))));
- finalize_order (oc,
- ret);
- return;
- }
- /* This request is not idempotent */
- GNUNET_break_op (0);
- reply_with_error (
- oc,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
- oc->parse_order.order_id);
- return;
- }
- }
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Executing database transaction to create order '%s' for instance '%s'\n",
oc->parse_order.order_id,
@@ -899,6 +901,37 @@ execute_order (struct OrderContext *oc)
return;
}
+ /* DB transaction succeeded, check for idempotent */
+ if (oc->execute_order.idempotent)
+ {
+ MHD_RESULT ret;
+
+ ret = TALER_MHD_REPLY_JSON_PACK (
+ oc->connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("order_id",
+ oc->parse_order.order_id),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_data_varsize (
+ "token",
+ GNUNET_is_zero (&oc->execute_order.token)
+ ? NULL
+ : &oc->execute_order.token,
+ sizeof (oc->execute_order.token))));
+ finalize_order (oc,
+ ret);
+ return;
+ }
+ if (oc->execute_order.conflict)
+ {
+ reply_with_error (
+ oc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
+ oc->parse_order.order_id);
+ return;
+ }
+
/* DB transaction succeeded, check for out-of-stock */
if (oc->execute_order.out_of_stock_index < UINT_MAX)
{
@@ -1436,161 +1469,155 @@ serialize_order (struct OrderContext *oc)
{
const struct TALER_MERCHANTDB_InstanceSettings *settings =
&oc->hc->instance->settings;
- json_t *merchant = NULL;
+ json_t *merchant;
json_t *token_types = json_object ();
json_t *choices = json_array ();
+ merchant = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("name",
+ settings->name),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("website",
+ settings->website)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("email",
+ settings->email)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("logo",
+ settings->logo)));
+ GNUNET_assert (NULL != merchant);
{
- merchant = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("name",
- settings->name),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("website",
- settings->website)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("email",
- settings->email)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("logo",
- settings->logo)));
- GNUNET_assert (NULL != merchant);
- {
- json_t *loca;
+ json_t *loca;
- /* Handle merchant address */
- loca = settings->address;
- if (NULL != loca)
- {
- loca = json_deep_copy (loca);
- GNUNET_assert (NULL != loca);
- GNUNET_assert (0 ==
- json_object_set_new (merchant,
- "address",
- loca));
- }
- }
+ /* Handle merchant address */
+ loca = settings->address;
+ if (NULL != loca)
{
- json_t *juri;
-
- /* Handle merchant jurisdiction */
- juri = settings->jurisdiction;
- if (NULL != juri)
- {
- juri = json_deep_copy (juri);
- GNUNET_assert (NULL != juri);
- GNUNET_assert (0 ==
- json_object_set_new (merchant,
- "jurisdiction",
- juri));
- }
+ loca = json_deep_copy (loca);
+ GNUNET_assert (NULL != loca);
+ GNUNET_assert (0 ==
+ json_object_set_new (merchant,
+ "address",
+ loca));
}
}
-
{
- for (unsigned int i = 0; i<oc->parse_choices.authorities_len; i++)
- {
- struct TALER_MerchantContractTokenAuthority *authority = &oc->parse_choices.authorities[i];
-
- // TODO: Finish spec to clearly define how token families are stored in
- // ContractTerms.
- // Here are some thoughts:
- // - Multiple keys of the same token family can be referrenced in
- // one contract. E.g. exchanging old subscription for new.
- // - TokenAuthority should be renamed to TokenFamily for consistency.
- // - TokenFamilySlug can be used instead of TokenAuthorityLabel, but
- // every token-based in- or ouput needs to have a valid_after date,
- // so it's clear with key is referenced.
- json_t *jauthority = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("description",
- authority->description),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("description_i18n",
- authority->description_i18n)),
- GNUNET_JSON_pack_data_auto ("h_pub",
- &authority->pub->public_key.pub_key_hash),
- GNUNET_JSON_pack_data_auto ("pub",
- &authority->pub->public_key.pub_key_hash),
- GNUNET_JSON_pack_timestamp ("token_expiration",
- authority->token_expiration)
- );
+ json_t *juri;
+ /* Handle merchant jurisdiction */
+ juri = settings->jurisdiction;
+ if (NULL != juri)
+ {
+ juri = json_deep_copy (juri);
+ GNUNET_assert (NULL != juri);
GNUNET_assert (0 ==
- json_object_set_new (token_types,
- authority->label,
- jauthority));
+ json_object_set_new (merchant,
+ "jurisdiction",
+ juri));
}
}
+ for (unsigned int i = 0; i<oc->parse_choices.authorities_len; i++)
{
- for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
- {
- struct TALER_MerchantContractChoice *choice = &oc->parse_choices.choices[i];
+ struct TALER_MerchantContractTokenAuthority *authority = &oc->parse_choices.authorities[i];
- json_t *inputs = json_array ();
- json_t *outputs = json_array ();
+ // TODO: Finish spec to clearly define how token families are stored in
+ // ContractTerms.
+ // Here are some thoughts:
+ // - Multiple keys of the same token family can be referrenced in
+ // one contract. E.g. exchanging old subscription for new.
+ // - TokenAuthority should be renamed to TokenFamily for consistency.
+ // - TokenFamilySlug can be used instead of TokenAuthorityLabel, but
+ // every token-based in- or ouput needs to have a valid_after date,
+ // so it's clear with key is referenced.
+ json_t *jauthority = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("description",
+ authority->description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("description_i18n",
+ authority->description_i18n)),
+ GNUNET_JSON_pack_data_auto ("h_pub",
+ &authority->pub->public_key.pub_key_hash),
+ GNUNET_JSON_pack_data_auto ("pub",
+ &authority->pub->public_key.pub_key_hash),
+ GNUNET_JSON_pack_timestamp ("token_expiration",
+ authority->token_expiration)
+ );
- for (unsigned int j = 0; j<choice->inputs_len; j++)
- {
- struct TALER_MerchantContractInput *input = &choice->inputs[j];
+ GNUNET_assert (0 ==
+ json_object_set_new (token_types,
+ authority->label,
+ jauthority));
+ }
- json_t *jinput = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_int64 ("type",
- input->type)
- );
+ for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
+ {
+ struct TALER_MerchantContractChoice *choice = &oc->parse_choices.choices[i];
- if (TALER_MCIT_TOKEN == input->type)
- {
- GNUNET_assert(0 ==
- json_object_set_new(jinput,
- "number",
- json_integer (
- input->details.token.count)));
- GNUNET_assert(0 ==
- json_object_set_new(jinput,
- "token_family_slug",
- json_string (
- input->details.token.token_family_slug)));
- }
+ json_t *inputs = json_array ();
+ json_t *outputs = json_array ();
- GNUNET_assert (0 == json_array_append_new (inputs, jinput));
- }
+ for (unsigned int j = 0; j<choice->inputs_len; j++)
+ {
+ struct TALER_MerchantContractInput *input = &choice->inputs[j];
- for (unsigned int j = 0; j<choice->outputs_len; j++)
- {
- struct TALER_MerchantContractOutput *output = &choice->outputs[j];
+ json_t *jinput = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("type",
+ input->type)
+ );
- json_t *joutput = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_int64 ("type",
- output->type)
- );
+ if (TALER_MCIT_TOKEN == input->type)
+ {
+ GNUNET_assert(0 ==
+ json_object_set_new(jinput,
+ "number",
+ json_integer (
+ input->details.token.count)));
+ GNUNET_assert(0 ==
+ json_object_set_new(jinput,
+ "token_family_slug",
+ json_string (
+ input->details.token.token_family_slug)));
+ }
- if (TALER_MCOT_TOKEN == output->type)
- {
- GNUNET_assert(0 ==
- json_object_set_new(joutput,
- "number",
- json_integer (
- output->details.token.count)));
-
- GNUNET_assert(0 ==
- json_object_set_new(joutput,
- "token_family_slug",
- json_string (
- output->details.token.token_family_slug)));
- }
+ GNUNET_assert (0 == json_array_append_new (inputs, jinput));
+ }
- GNUNET_assert (0 == json_array_append (outputs, joutput));
- }
+ for (unsigned int j = 0; j<choice->outputs_len; j++)
+ {
+ struct TALER_MerchantContractOutput *output = &choice->outputs[j];
- json_t *jchoice = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_array_incref ("inputs",
- inputs),
- GNUNET_JSON_pack_array_incref ("outputs",
- outputs)
+ json_t *joutput = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("type",
+ output->type)
);
- GNUNET_assert (0 == json_array_append (choices, jchoice));
+ if (TALER_MCOT_TOKEN == output->type)
+ {
+ GNUNET_assert(0 ==
+ json_object_set_new(joutput,
+ "number",
+ json_integer (
+ output->details.token.count)));
+
+ GNUNET_assert(0 ==
+ json_object_set_new(joutput,
+ "token_family_slug",
+ json_string (
+ output->details.token.token_family_slug)));
+ }
+
+ GNUNET_assert (0 == json_array_append (outputs, joutput));
}
+
+ json_t *jchoice = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_incref ("inputs",
+ inputs),
+ GNUNET_JSON_pack_array_incref ("outputs",
+ outputs)
+ );
+
+ GNUNET_assert (0 == json_array_append (choices, jchoice));
}
oc->serialize_order.contract = GNUNET_JSON_PACK (
@@ -1613,6 +1640,9 @@ serialize_order (struct OrderContext *oc)
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("fulfillment_url",
oc->parse_order.fulfillment_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_uint64 ("minimum_age",
+ oc->parse_order.minimum_age)),
GNUNET_JSON_pack_array_incref ("products",
oc->merge_inventory.products),
GNUNET_JSON_pack_data_auto ("h_wire",
@@ -1635,8 +1665,8 @@ serialize_order (struct OrderContext *oc)
oc->parse_order.delivery_location)),
GNUNET_JSON_pack_string ("merchant_base_url",
oc->parse_order.merchant_base_url),
- GNUNET_JSON_pack_object_incref ("merchant",
- merchant),
+ GNUNET_JSON_pack_object_steal ("merchant",
+ merchant),
GNUNET_JSON_pack_data_auto ("merchant_pub",
&oc->hc->instance->merchant_pub),
GNUNET_JSON_pack_array_incref ("exchanges",
@@ -1883,6 +1913,10 @@ parse_order (struct OrderContext *oc)
&oc->parse_order.delivery_date),
NULL),
GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("minimum_age",
+ &oc->parse_order.minimum_age),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_relative_time ("auto_refund",
&oc->parse_order.auto_refund),
NULL),
@@ -2600,6 +2634,9 @@ merge_inventory (struct OrderContext *oc)
ip->product_id);
return;
}
+ oc->parse_order.minimum_age
+ = GNUNET_MAX (oc->parse_order.minimum_age,
+ pd.minimum_age);
{
json_t *p;
@@ -2778,9 +2815,22 @@ parse_request (struct OrderContext *oc)
/* parse the inventory_products (optionally given) */
if (NULL != ip)
{
+ unsigned int ipl = (unsigned int) json_array_size (ip);
+
+ if ( (json_array_size (ip) != (size_t) ipl) ||
+ (ipl > MAX_PRODUCTS) )
+ {
+ GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
+ reply_with_error (oc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_ALLOCATION_FAILURE,
+ "inventory products too long");
+ return;
+ }
GNUNET_array_grow (oc->parse_request.inventory_products,
oc->parse_request.inventory_products_length,
- json_array_size (ip));
+ (unsigned int) json_array_size (ip));
for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++)
{
struct InventoryProduct *ipr = &oc->parse_request.inventory_products[i];
diff --git a/src/backend/taler-merchant-httpd_private-post-templates.c b/src/backend/taler-merchant-httpd_private-post-templates.c
index a064769c..7aa72992 100644
--- a/src/backend/taler-merchant-httpd_private-post-templates.c
+++ b/src/backend/taler-merchant-httpd_private-post-templates.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2022 Taler Systems SA
+ (C) 2022-2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -47,6 +47,18 @@ templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1,
(NULL != t2->otp_id) &&
(0 == strcmp (t1->otp_id,
t2->otp_id))) ) &&
+ ( ( (NULL == t1->required_currency) &&
+ (NULL == t2->required_currency) ) ||
+ ( (NULL != t1->required_currency) &&
+ (NULL != t2->required_currency) &&
+ (0 == strcmp (t1->required_currency,
+ t2->required_currency))) ) &&
+ ( ( (NULL == t1->editable_defaults) &&
+ (NULL == t2->editable_defaults) ) ||
+ ( (NULL != t1->editable_defaults) &&
+ (NULL != t2->editable_defaults) &&
+ (1 == json_equal (t1->editable_defaults,
+ t2->editable_defaults))) ) &&
(1 == json_equal (t1->template_contract,
t2->template_contract)) );
}
@@ -72,6 +84,14 @@ TMH_private_post_templates (const struct TMH_RequestHandler *rh,
NULL),
GNUNET_JSON_spec_json ("template_contract",
&tp.template_contract),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("required_currency",
+ (const char **) &tp.required_currency),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("editable_defaults",
+ &tp.editable_defaults),
+ NULL),
GNUNET_JSON_spec_end ()
};
uint64_t otp_serial = 0;
@@ -104,6 +124,57 @@ TMH_private_post_templates (const struct TMH_RequestHandler *rh,
"template_contract");
}
+ if ( (NULL != tp.required_currency) &&
+ (GNUNET_OK !=
+ TALER_check_currency (tp.required_currency)) )
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "required_currency");
+ }
+ if ( (NULL != tp.required_currency) &&
+ (NULL != json_object_get (tp.template_contract,
+ "amount")) )
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "required_currency and contract::amount specified");
+ }
+ if (NULL != tp.editable_defaults)
+ {
+ const char *key;
+ json_t *val;
+
+ json_object_foreach (tp.editable_defaults, key, val)
+ {
+ if (NULL !=
+ json_object_get (tp.template_contract,
+ key))
+ {
+ char *msg;
+ MHD_RESULT ret;
+
+ GNUNET_break_op (0);
+ GNUNET_asprintf (&msg,
+ "editable_defaults::%s conflicts with template_contract",
+ key);
+ GNUNET_JSON_parse_free (spec);
+ ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ msg);
+ GNUNET_free (msg);
+ return ret;
+ }
+ }
+ }
+
if (NULL != tp.otp_id)
{
qs = TMH_db->select_otp_serial (TMH_db->cls,
diff --git a/src/backend/taler-merchant-httpd_private-post-token-families.c b/src/backend/taler-merchant-httpd_private-post-token-families.c
index b9be59d8..f4472c39 100644
--- a/src/backend/taler-merchant-httpd_private-post-token-families.c
+++ b/src/backend/taler-merchant-httpd_private-post-token-families.c
@@ -195,10 +195,9 @@ TMH_private_post_token_families (const struct TMH_RequestHandler *rh,
NULL,
NULL,
0)
- // TODO: Use proper error code
: TALER_MHD_reply_with_error (connection,
MHD_HTTP_CONFLICT,
- 0,
+ TALER_EC_MERCHANT_POST_TOKEN_FAMILY_CONFLICT,
details.slug);
}
} /* end switch (qs) */
diff --git a/src/backend/taler-merchant-wirewatch.c b/src/backend/taler-merchant-wirewatch.c
index 6242ddbc..17eb7a0a 100644
--- a/src/backend/taler-merchant-wirewatch.c
+++ b/src/backend/taler-merchant-wirewatch.c
@@ -370,9 +370,8 @@ credit_cb (
w->start_row = serial_id;
return GNUNET_OK;
}
- /* FIXME: consider grouping multiple inserts
- into one bigger transaction with just one
- notify! */
+ /* FIXME-Performance-Optimization: consider grouping multiple inserts
+ into one bigger transaction with just one notify. */
credit_payto = TALER_payto_normalize (details->credit_account_uri);
qs = db_plugin->insert_transfer (db_plugin->cls,
w->instance_id,