commit f25e67c2c329998dc0827a38834548b75a168ae4
parent 4f3756b826bedc1dfeca19bd2671af66630fb7f6
Author: Christian Grothoff <christian@grothoff.org>
Date: Mon, 29 Dec 2025 14:42:19 +0100
check money pots exist on order creation
Diffstat:
3 files changed, 101 insertions(+), 1 deletion(-)
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -3282,6 +3282,28 @@ phase_add_payment_details (struct OrderContext *oc)
/**
+ * Helper function to sort uint64_t array with qsort().
+ *
+ * @param a pointer to element to compare
+ * @param b pointer to element to compare
+ * @return 0 on equal, -1 on smaller, 1 on larger
+ */
+static int
+uint64_cmp (const void *a,
+ const void *b)
+{
+ uint64_t ua = *(const uint64_t *) a;
+ uint64_t ub = *(const uint64_t *) b;
+
+ if (ua < ub)
+ return -1;
+ if (ua > ub)
+ return 1;
+ return 0;
+}
+
+
+/**
* Merge the inventory products into products, querying the
* database about the details of those products. Upon success,
* continue processing by calling add_payment_details().
@@ -3291,6 +3313,9 @@ phase_add_payment_details (struct OrderContext *oc)
static void
phase_merge_inventory (struct OrderContext *oc)
{
+ uint64_t pots[GNUNET_NZL (oc->parse_order.products_len)];
+ size_t pots_off = 0;
+
/**
* parse_request.inventory_products => instructions to add products to contract terms
* parse_order.products => contains products that are not from the backend-managed inventory.
@@ -3303,7 +3328,73 @@ phase_merge_inventory (struct OrderContext *oc)
json_array_append_new (
oc->merge_inventory.products,
TALER_MERCHANT_product_serialize (&oc->parse_order.products[i])));
+ if (0 != oc->parse_order.products[i].product_money_pot)
+ pots[pots_off++] = oc->parse_order.products[i].product_money_pot;
+ }
+
+ /* make sure pots array only has distinct elements */
+ qsort (pots,
+ sizeof (uint64_t),
+ pots_off,
+ &uint64_cmp);
+ {
+ size_t e = 0;
+
+ for (size_t i = 1; i<pots_off; i++)
+ {
+ if (pots[e] != pots[i])
+ pots[++e] = pots[i];
+ }
+ if (pots_off > 0)
+ e++;
+ pots_off = e;
+ }
+
+ /* check if all money pots exist; note that we do NOT treat
+ the inventory products to this check, as (1) the foreign key
+ constraint should ensure this, and (2) if the money pot
+ were deleted (concurrently), the value is specified to be
+ considered 0 (aka none) and so we can proceed anyway. */
+ if (pots_off > 0)
+ {
+ enum GNUNET_DB_QueryStatus qs;
+ uint64_t pot_missing;
+
+ qs = TMH_db->check_money_pots (TMH_db->cls,
+ oc->hc->instance->settings.id,
+ pots_off,
+ pots,
+ &pot_missing);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ reply_with_error (oc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "check_money_pots");
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* great, good case! */
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ {
+ char mstr[32];
+
+ GNUNET_snprintf (mstr,
+ sizeof (mstr),
+ "%llu",
+ (unsigned long long) pot_missing);
+ reply_with_error (oc,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_MERCHANT_GENERIC_MONEY_POT_UNKNOWN,
+ mstr);
+ return;
+ }
+ }
}
+
/* Populate products from inventory product array and database */
{
GNUNET_assert (NULL != oc->merge_inventory.products);
@@ -3403,7 +3494,9 @@ phase_merge_inventory (struct OrderContext *oc)
ip->quantity_frac,
sizeof (unit_quantity_buf),
unit_quantity_buf);
-
+ if (0 != pd.money_pot_id)
+ pots[pots_off++] = pd.money_pot_id;
+ // FIXME: reuse TALER_MERCHANT_product_serialize() here!?
p = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("product_name",
pd.product_name),
@@ -3422,6 +3515,9 @@ phase_merge_inventory (struct OrderContext *oc)
GNUNET_JSON_pack_uint64 (
"quantity",
ip->quantity),
+ GNUNET_JSON_pack_uint64 (
+ "product_money_pot",
+ pd.money_pot_id),
GNUNET_JSON_pack_string ("unit_quantity",
unit_quantity_buf));
GNUNET_assert (NULL != p);
@@ -3432,6 +3528,7 @@ phase_merge_inventory (struct OrderContext *oc)
TALER_MERCHANTDB_product_details_free (&pd);
}
}
+
/* check if final product list is well-formed */
if (! TMH_products_array_valid (oc->merge_inventory.products))
{
diff --git a/src/include/taler_merchant_util.h b/src/include/taler_merchant_util.h
@@ -667,6 +667,7 @@ struct TALER_MERCHANT_Product
* once we add taxes, need is_net_price indicator!
*/
struct TALER_Amount price;
+ // FIXME: modern product has a price *array*!
/**
* An optional base64-encoded image of the product.
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
@@ -320,6 +320,8 @@ typedef void
/**
* Details about a product.
+ *
+ * FIXME: reuse TALER_MERCHANT_Product as a member in this structure!
*/
struct TALER_MERCHANTDB_ProductDetails
{