merchant

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

commit 63c171f49aa4b3d6d412bf50d07a0fae4beab211
parent 5bc607b1fe266598ad5b25202ec98e57e532b4d7
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 18 Mar 2024 22:14:48 +0100

move lookup order for idempotency check into transaction scope where it belongs

Diffstat:
Msrc/backend/taler-merchant-httpd_private-post-orders.c | 152++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 88 insertions(+), 64 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -423,6 +423,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 @@ -661,6 +679,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, @@ -757,70 +814,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, @@ -865,6 +858,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) {