commit 1a5f7b296e1c7440a1bba164de503196e479c320
parent f21080a5091d94773556c3e2e7eca506dc40f69c
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Sat, 8 Mar 2025 09:59:52 +0100
Merge branch 'master' into dev/bohdan-potuzhnyi/donau-integration
Diffstat:
3 files changed, 143 insertions(+), 95 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
@@ -18,20 +18,20 @@
* @brief implementation of GET /private/orders/ID handler
* @author Florian Dold
* @author Christian Grothoff
+ * @author Bohdan Potuzhnyi
+ * @author Iván Ávalos
*/
#include "platform.h"
-#include "taler-merchant-httpd_private-get-orders-ID.h"
-#include "taler-merchant-httpd_get-orders-ID.h"
-#include <gnunet/gnunet_json_lib.h>
-#include <jansson.h>
-#include <stdint.h>
#include <taler/taler_json_lib.h>
#include <taler/taler_dbevents.h>
-#include "taler-merchant-httpd_mhd.h"
-#include "taler-merchant-httpd_exchanges.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_util.h>
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_json_lib.h>
+#include "taler_merchant_util.h"
#include "taler-merchant-httpd_helper.h"
#include "taler-merchant-httpd_private-get-orders.h"
-
+#include "taler-merchant-httpd_private-get-orders-ID.h"
/**
* Data structure we keep for a check payment request.
@@ -206,13 +206,6 @@ struct GetOrderRequestContext
const char *session_id;
/**
- * Fulfillment URL extracted from the contract. For repurchase detection.
- * Only valid as long as @e contract_terms is valid! NULL if there is
- * no fulfillment URL in the contract.
- */
- const char *fulfillment_url;
-
- /**
* Kept in a DLL while suspended on exchange.
*/
struct GetOrderRequestContext *next;
@@ -253,17 +246,17 @@ struct GetOrderRequestContext
* Contract terms of the payment we are checking. NULL when they
* are not (yet) known.
*/
- json_t *contract_terms;
+ json_t *contract_terms_json;
/**
- * Claim token of the order.
+ * Parsed contract terms, NULL when parsing failed
*/
- struct TALER_ClaimTokenP claim_token;
+ struct TALER_MERCHANT_Contract *contract_terms;
/**
- * Timestamp from the @e contract_terms.
+ * Claim token of the order.
*/
- struct GNUNET_TIME_Timestamp timestamp;
+ struct TALER_ClaimTokenP claim_token;
/**
* Timestamp of the last payment.
@@ -271,11 +264,6 @@ struct GetOrderRequestContext
struct GNUNET_TIME_Timestamp last_payment;
/**
- * Order summary. Pointer into @e contract_terms.
- */
- const char *summary;
-
- /**
* Wire details for the payment, to be returned in the reply. NULL
* if not available.
*/
@@ -287,6 +275,11 @@ struct GetOrderRequestContext
json_t *refund_details;
/**
+ * Amount of the order, unset for unpaid v1 orders.
+ */
+ struct TALER_Amount contract_amount;
+
+ /**
* Hash over the @e contract_terms.
*/
struct TALER_PrivateContractHashP h_contract_terms;
@@ -309,11 +302,6 @@ struct GetOrderRequestContext
struct TALER_Amount value_total;
/**
- * Total we were to be paid under the contract, excluding refunds.
- */
- struct TALER_Amount contract_amount;
-
- /**
* Serial ID of the order.
*/
uint64_t order_serial;
@@ -487,8 +475,13 @@ gorc_cleanup (void *cls)
{
struct GetOrderRequestContext *gorc = cls;
+ if (NULL != gorc->contract_terms_json)
+ json_decref (gorc->contract_terms_json);
if (NULL != gorc->contract_terms)
- json_decref (gorc->contract_terms);
+ {
+ TALER_MERCHANT_contract_free (gorc->contract_terms);
+ gorc->contract_terms = NULL;
+ }
if (NULL != gorc->wire_details)
json_decref (gorc->wire_details);
if (NULL != gorc->refund_details)
@@ -563,7 +556,7 @@ phase_init (struct GetOrderRequestContext *gorc)
&resume_by_event,
gorc);
if ( (NULL != gorc->session_id) &&
- (NULL != gorc->fulfillment_url) )
+ (NULL != gorc->contract_terms->fulfillment_url) )
{
struct TMH_SessionEventP session_eh = {
.header.size = htons (sizeof (session_eh)),
@@ -577,8 +570,8 @@ phase_init (struct GetOrderRequestContext *gorc)
GNUNET_CRYPTO_hash (gorc->session_id,
strlen (gorc->session_id),
&session_eh.h_session_id);
- GNUNET_CRYPTO_hash (gorc->fulfillment_url,
- strlen (gorc->fulfillment_url),
+ GNUNET_CRYPTO_hash (gorc->contract_terms->fulfillment_url,
+ strlen (gorc->contract_terms->fulfillment_url),
&session_eh.h_fulfillment_url);
gorc->session_eh
= TMH_db->event_listen (
@@ -603,7 +596,7 @@ phase_fetch_contract (struct GetOrderRequestContext *gorc)
struct TMH_HandlerContext *hc = gorc->hc;
enum GNUNET_DB_QueryStatus qs;
- if (NULL != gorc->contract_terms)
+ if (NULL != gorc->contract_terms_json)
{
/* Free memory filled with old contract terms before fetching the latest
ones from the DB. Note that we cannot simply skip the database
@@ -612,10 +605,8 @@ phase_fetch_contract (struct GetOrderRequestContext *gorc)
invocation of this function and we are back here due to long polling)
and thus the contract terms could have changed during claiming. Thus,
we need to fetch the latest contract terms from the DB again. */
- json_decref (gorc->contract_terms);
- gorc->contract_terms = NULL;
- gorc->fulfillment_url = NULL;
- gorc->summary = NULL;
+ json_decref (gorc->contract_terms_json);
+ gorc->contract_terms_json = NULL;
gorc->order_only = false;
}
TMH_db->preflight (TMH_db->cls);
@@ -623,7 +614,7 @@ phase_fetch_contract (struct GetOrderRequestContext *gorc)
hc->instance->settings.id,
hc->infix,
gorc->session_id,
- &gorc->contract_terms,
+ &gorc->contract_terms_json,
&gorc->order_serial,
&gorc->paid,
&gorc->wired,
@@ -672,7 +663,7 @@ phase_fetch_contract (struct GetOrderRequestContext *gorc)
hc->infix,
&gorc->claim_token,
&unused,
- &gorc->contract_terms);
+ &gorc->contract_terms_json);
}
if (0 > qs)
{
@@ -711,39 +702,67 @@ static void
phase_parse_contract (struct GetOrderRequestContext *gorc)
{
struct TMH_HandlerContext *hc = gorc->hc;
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_amount_any ("amount",
- &gorc->contract_amount),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("fulfillment_url",
- &gorc->fulfillment_url),
- NULL),
- GNUNET_JSON_spec_string ("summary",
- &gorc->summary),
- GNUNET_JSON_spec_timestamp ("timestamp",
- &gorc->timestamp),
- GNUNET_JSON_spec_end ()
- };
- if (GNUNET_OK !=
- GNUNET_JSON_parse (gorc->contract_terms,
- spec,
- NULL, NULL))
+ if (NULL == gorc->contract_terms)
{
- GNUNET_break (0);
- phase_end (gorc,
- TALER_MHD_reply_with_error (
- gorc->sc.con,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
- hc->infix));
- return;
+ gorc->contract_terms = TALER_MERCHANT_contract_parse (
+ gorc->contract_terms_json,
+ true);
+
+ if (NULL == gorc->contract_terms)
+ {
+ GNUNET_break (0);
+ phase_end (gorc,
+ TALER_MHD_reply_with_error (
+ gorc->sc.con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
+ hc->infix));
+ return;
+ }
}
- if (! gorc->order_only)
+
+ switch (gorc->contract_terms->version)
+ {
+ case TALER_MERCHANT_CONTRACT_VERSION_0:
+ gorc->contract_amount = gorc->contract_terms->details.v0.brutto;
+ break;
+ case TALER_MERCHANT_CONTRACT_VERSION_1:
+ if (gorc->choice_index >= 0)
+ {
+ if (gorc->choice_index >=
+ gorc->contract_terms->details.v1.choices_len)
+ {
+ GNUNET_break (0);
+ phase_end (gorc,
+ TALER_MHD_reply_with_error (
+ gorc->sc.con, MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ NULL));
+ return;
+ }
+
+ gorc->contract_amount =
+ gorc->contract_terms->details.v1.choices[gorc->choice_index].amount;
+ }
+ break;
+ default:
+ {
+ GNUNET_break (0);
+ phase_end (gorc,
+ TALER_MHD_reply_with_error (
+ gorc->sc.con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION,
+ NULL));
+ return;
+ }
+ }
+
+ if (! gorc->order_only && GNUNET_OK !=
+ TALER_JSON_contract_hash (gorc->contract_terms_json,
+ &gorc->h_contract_terms))
{
- if (GNUNET_OK !=
- TALER_JSON_contract_hash (gorc->contract_terms,
- &gorc->h_contract_terms))
{
GNUNET_break (0);
phase_end (gorc,
@@ -754,6 +773,7 @@ phase_parse_contract (struct GetOrderRequestContext *gorc)
return;
}
}
+ GNUNET_assert (NULL != gorc->contract_terms_json);
GNUNET_assert (NULL != gorc->contract_terms);
gorc->phase++;
}
@@ -819,7 +839,7 @@ phase_check_repurchase (struct GetOrderRequestContext *gorc)
MHD_RESULT ret;
if ( (gorc->paid) ||
- (NULL == gorc->fulfillment_url) ||
+ (NULL == gorc->contract_terms->fulfillment_url) ||
(NULL == gorc->session_id) )
{
/* Repurchase cannot apply */
@@ -829,10 +849,11 @@ phase_check_repurchase (struct GetOrderRequestContext *gorc)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Running re-purchase detection for %s/%s\n",
gorc->session_id,
- gorc->fulfillment_url);
+ gorc->contract_terms->fulfillment_url);
qs = TMH_db->lookup_order_by_fulfillment (TMH_db->cls,
hc->instance->settings.id,
- gorc->fulfillment_url,
+ gorc->contract_terms->
+ fulfillment_url,
gorc->session_id,
TALER_EXCHANGE_YNA_NO !=
gorc->allow_refunded_for_repurchase,
@@ -854,7 +875,7 @@ phase_check_repurchase (struct GetOrderRequestContext *gorc)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"No already paid order for %s/%s\n",
gorc->session_id,
- gorc->fulfillment_url);
+ gorc->contract_terms->fulfillment_url);
gorc->phase++;
return;
}
@@ -900,13 +921,15 @@ phase_check_repurchase (struct GetOrderRequestContext *gorc)
GNUNET_JSON_pack_string ("already_paid_order_id",
already_paid_order_id),
GNUNET_JSON_pack_string ("already_paid_fulfillment_url",
- gorc->fulfillment_url),
- TALER_JSON_pack_amount ("total_amount",
- &gorc->contract_amount),
+ gorc->contract_terms->fulfillment_url),
+ /* undefined for unpaid v1 contracts */
+ GNUNET_JSON_pack_allow_null (
+ TALER_JSON_pack_amount ("total_amount",
+ &gorc->contract_amount)),
GNUNET_JSON_pack_string ("summary",
- gorc->summary),
+ gorc->contract_terms->summary),
GNUNET_JSON_pack_timestamp ("creation_time",
- gorc->timestamp));
+ gorc->contract_terms->timestamp));
GNUNET_free (taler_pay_uri);
GNUNET_free (already_paid_order_id);
phase_end (gorc,
@@ -965,7 +988,7 @@ phase_unpaid_finish (struct GetOrderRequestContext *gorc)
GNUNET_JSON_pack_string ("order_status_url",
order_status_url),
GNUNET_JSON_pack_object_incref ("contract_terms",
- gorc->contract_terms),
+ gorc->contract_terms_json),
GNUNET_JSON_pack_string ("order_status",
"claimed")));
return;
@@ -984,12 +1007,14 @@ phase_unpaid_finish (struct GetOrderRequestContext *gorc)
order_status_url),
GNUNET_JSON_pack_string ("order_status",
"unpaid"),
- TALER_JSON_pack_amount ("total_amount",
- &gorc->contract_amount),
+ /* undefined for unpaid v1 contracts */
+ GNUNET_JSON_pack_allow_null (
+ TALER_JSON_pack_amount ("total_amount",
+ &gorc->contract_amount)),
GNUNET_JSON_pack_string ("summary",
- gorc->summary),
+ gorc->contract_terms->summary),
GNUNET_JSON_pack_timestamp ("creation_time",
- gorc->timestamp));
+ gorc->contract_terms->timestamp));
GNUNET_free (taler_pay_uri);
GNUNET_free (order_status_url);
phase_end (gorc,
@@ -1100,10 +1125,12 @@ phase_check_refunds (struct GetOrderRequestContext *gorc)
GNUNET_assert (! gorc->order_only);
GNUNET_assert (gorc->paid);
+
/* Accumulate refunds, if any. */
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,
@@ -1199,6 +1226,13 @@ deposit_cb (
static void
phase_check_deposits (struct GetOrderRequestContext *gorc)
{
+ GNUNET_assert (! gorc->order_only);
+ GNUNET_assert (gorc->paid);
+
+ /* amount must be always valid for paid orders */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_is_valid (&gorc->contract_amount));
+
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (gorc->contract_amount.currency,
&gorc->deposits_total));
@@ -1292,12 +1326,16 @@ phase_check_local_transfers (struct GetOrderRequestContext *gorc)
struct TMH_HandlerContext *hc = gorc->hc;
enum GNUNET_DB_QueryStatus qs;
+ GNUNET_assert (gorc->paid);
+ GNUNET_assert (! gorc->order_only);
+
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (gorc->contract_amount.currency,
&gorc->deposits_total));
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (gorc->contract_amount.currency,
&gorc->deposit_fees_total));
+
qs = TMH_db->lookup_transfer_details_by_order (TMH_db->cls,
gorc->order_serial,
&process_transfer_details,
@@ -1369,7 +1407,7 @@ phase_check_local_transfers (struct GetOrderRequestContext *gorc)
TMH_notify_order_change (hc->instance,
TMH_OSF_PAID
| TMH_OSF_WIRED,
- gorc->timestamp,
+ gorc->contract_terms->timestamp,
gorc->order_serial);
}
}
@@ -1390,6 +1428,9 @@ phase_reply_result (struct GetOrderRequestContext *gorc)
char *order_status_url;
json_t *choice_index;
+ GNUNET_assert (gorc->paid);
+ GNUNET_assert (! gorc->order_only);
+
{
struct TALER_PrivateContractHashP *h_contract = NULL;
@@ -1414,7 +1455,7 @@ phase_reply_result (struct GetOrderRequestContext *gorc)
{
GNUNET_break (GNUNET_YES ==
TALER_amount_is_zero (&gorc->contract_amount));
- gorc->last_payment = gorc->timestamp;
+ gorc->last_payment = gorc->contract_terms->timestamp;
}
if (-1 != gorc->choice_index)
{
@@ -1446,7 +1487,7 @@ phase_reply_result (struct GetOrderRequestContext *gorc)
TALER_JSON_pack_amount ("deposit_total",
&gorc->deposits_total),
GNUNET_JSON_pack_object_incref ("contract_terms",
- gorc->contract_terms),
+ gorc->contract_terms_json),
GNUNET_JSON_pack_string ("order_status",
"paid"),
GNUNET_JSON_pack_timestamp ("last_payment",
@@ -1457,8 +1498,9 @@ phase_reply_result (struct GetOrderRequestContext *gorc)
gorc->wired),
GNUNET_JSON_pack_bool ("refund_pending",
gorc->refund_pending),
- TALER_JSON_pack_amount ("refund_amount",
- &gorc->refund_amount),
+ GNUNET_JSON_pack_allow_null (
+ TALER_JSON_pack_amount ("refund_amount",
+ &gorc->refund_amount)),
GNUNET_JSON_pack_array_steal ("wire_details",
gorc->wire_details),
GNUNET_JSON_pack_array_steal ("refund_details",
diff --git a/src/backenddb/pg_insert_contract_terms.c b/src/backenddb/pg_insert_contract_terms.c
@@ -56,10 +56,13 @@ TMH_PG_insert_contract_terms (
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
+ const char *error_json_name;
+ unsigned int error_line;
- res = TALER_MHD_parse_json_data (NULL,
- contract_terms,
- spec);
+ res = GNUNET_JSON_parse (contract_terms,
+ spec,
+ &error_json_name,
+ &error_line);
if (GNUNET_OK != res)
{
GNUNET_break (0);
diff --git a/src/backenddb/pg_update_contract_terms.c b/src/backenddb/pg_update_contract_terms.c
@@ -58,10 +58,13 @@ TMH_PG_update_contract_terms (void *cls,
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
+ const char *error_json_name;
+ unsigned int error_line;
- res = TALER_MHD_parse_json_data (NULL,
- contract_terms,
- spec);
+ res = GNUNET_JSON_parse (contract_terms,
+ spec,
+ &error_json_name,
+ &error_line);
if (GNUNET_OK != res)
{
GNUNET_break (0);