merchant

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

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:
Msrc/backend/taler-merchant-httpd_private-post-orders.c | 99++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/include/taler_merchant_util.h | 1+
Msrc/include/taler_merchantdb_plugin.h | 2++
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 {