diff options
author | Christian Grothoff <grothoff@gnunet.org> | 2023-10-13 20:51:04 +0200 |
---|---|---|
committer | Christian Grothoff <grothoff@gnunet.org> | 2023-10-13 21:16:50 +0200 |
commit | 0f3490dc022620c7e42b86426e14198a04c2e17f (patch) | |
tree | 0b16661ae6aa9273bf81652fe6a8806060b31fb0 | |
parent | a5f50083e65a3e9a0945b150701349afa81a0e9e (diff) | |
download | merchant-0f3490dc022620c7e42b86426e14198a04c2e17f.tar.gz merchant-0f3490dc022620c7e42b86426e14198a04c2e17f.tar.bz2 merchant-0f3490dc022620c7e42b86426e14198a04c2e17f.zip |
more work on multicurrency support: use checks everywhere...
-rw-r--r-- | src/backend/taler-merchant-httpd_get-orders-ID.c | 135 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_helper.c | 8 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_post-orders-ID-pay.c | 159 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_post-using-templates.c | 23 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-get-orders-ID.c | 96 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-get-orders.c | 11 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders.c | 18 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-templates.c | 2 | ||||
-rw-r--r-- | src/backenddb/pg_increase_refund.c | 9 |
9 files changed, 363 insertions, 98 deletions
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c index 9c60f7a9..af5513d9 100644 --- a/src/backend/taler-merchant-httpd_get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -120,6 +120,22 @@ struct GetOrderData json_t *contract_terms; /** + * Merchant base URL from @e contract_terms. + */ + const char *merchant_base_url; + + /** + * Public reorder URL from @e contract_terms. + * Could be NULL if contract does not have one. + */ + const char *public_reorder_url; + + /** + * Total amount in contract. + */ + struct TALER_Amount contract_total; + + /** * Total refunds granted for this payment. Only initialized * if @e refunded is set to true. */ @@ -180,6 +196,16 @@ struct GetOrderData */ bool generate_html; + /** + * Did we parse the contract terms? + */ + bool contract_parsed; + + /** + * Set to true if the refunds found in the DB have + * a different currency then the main contract. + */ + bool bad_refund_currency_in_db; }; @@ -316,6 +342,9 @@ suspend_god (struct GetOrderData *god) json_decref (god->contract_terms); god->fulfillment_url = NULL; god->contract_terms = NULL; + god->contract_parsed = false; + god->merchant_base_url = NULL; + god->public_reorder_url = NULL; } GNUNET_assert (! god->suspended); god->suspended = GNUNET_YES; @@ -712,6 +741,16 @@ process_refunds_cb (void *cls, TALER_B2S (coin_pub), reason); god->refund_pending |= pending; + if ( (GNUNET_OK != + TALER_amount_cmp_currency (&god->refund_taken, + refund_amount)) || + (GNUNET_OK != + TALER_amount_cmp_currency (&god->refund_amount, + refund_amount)) ) + { + god->bad_refund_currency_in_db = true; + return; + } if (! pending) { GNUNET_assert (0 <= @@ -767,7 +806,6 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, bool contract_match = false; bool token_match = false; bool contract_available = false; - const char *merchant_base_url; (void) rh; if (NULL == god) @@ -945,10 +983,9 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, if (NULL != god->contract_terms) { contract_available = true; - - if (GNUNET_YES == GNUNET_is_zero (&god->h_contract_terms)) + if (GNUNET_YES == + GNUNET_is_zero (&god->h_contract_terms)) { - if (GNUNET_OK != TALER_JSON_contract_hash (god->contract_terms, &god->h_contract_terms)) @@ -959,11 +996,9 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, "contract terms"); } - } else { - struct TALER_PrivateContractHashP h; if (GNUNET_OK != @@ -988,9 +1023,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER, NULL); } - } - } if (contract_available) @@ -1040,21 +1073,46 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, } /* end unclaimed order logic */ GNUNET_assert (NULL != god->contract_terms); - merchant_base_url = json_string_value (json_object_get (god->contract_terms, - "merchant_base_url")); - if (NULL == merchant_base_url) + if (! god->contract_parsed) { - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, - order_id); + struct GNUNET_JSON_Specification espec[] = { + TALER_JSON_spec_amount_any ("amount", + &god->contract_total), + GNUNET_JSON_spec_string ("merchant_base_url", + &god->merchant_base_url), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("fulfillment_url", + &god->fulfillment_url), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("public_reorder_url", + &god->public_reorder_url), + NULL), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + const char *ename; + unsigned int eline; + + res = GNUNET_JSON_parse (god->contract_terms, + espec, + &ename, + &eline); + if (GNUNET_OK != res) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse contract %s in DB at field %s\n", + order_id, + ename); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, + order_id); + } + god->contract_parsed = true; } - if (NULL == god->fulfillment_url) - god->fulfillment_url = json_string_value (json_object_get ( - god->contract_terms, - "fulfillment_url")); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Token match: %d, contract_available: %d, contract match: %d, claimed: %d\n", token_match, @@ -1085,15 +1143,11 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, if (! (token_match || contract_match) ) { - const char *public_reorder_url; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Neither claim token nor contract matched\n"); - public_reorder_url = json_string_value (json_object_get ( - god->contract_terms, - "public_reorder_url")); /* Client has no rights to this order */ - if (NULL == public_reorder_url) + if (NULL == god->public_reorder_url) { /* We cannot give the client a new order, just fail */ if (! GNUNET_is_zero (&god->h_contract_terms)) @@ -1135,7 +1189,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, GNUNET_break (MHD_YES == MHD_add_response_header (reply, MHD_HTTP_HEADER_LOCATION, - public_reorder_url)); + god->public_reorder_url)); ret = MHD_queue_response (connection, MHD_HTTP_FOUND, reply); @@ -1147,7 +1201,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, connection, MHD_HTTP_ACCEPTED, GNUNET_JSON_pack_string ("public_reorder_url", - public_reorder_url)); + god->public_reorder_url)); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -1247,13 +1301,25 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, } } + if ( (god->sc.awaiting_refund) && + (GNUNET_OK != + TALER_amount_cmp_currency (&god->contract_total, + &god->sc.refund_expected)) ) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + god->contract_total.currency); + } + /* At this point, we know the contract was paid. Let's check for refunds. First, clear away refunds found from previous invocations. */ GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TMH_currency, + TALER_amount_set_zero (god->contract_total.currency, &god->refund_amount)); GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TMH_currency, + TALER_amount_set_zero (god->contract_total.currency, &god->refund_taken)); qs = TMH_db->lookup_refunds_detailed (TMH_db->cls, hc->instance->settings.id, @@ -1268,7 +1334,14 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_DB_FETCH_FAILED, "lookup_refunds_detailed"); } - + if (god->bad_refund_currency_in_db) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "currency mix-up between contract price and refunds in database"); + } if ( ((god->sc.awaiting_refund) && ( (! god->refunded) || (1 != TALER_amount_cmp (&god->refund_amount, @@ -1308,7 +1381,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, char *uri; GNUNET_assert (NULL != god->contract_terms); - uri = make_taler_refund_uri (merchant_base_url, + uri = make_taler_refund_uri (god->merchant_base_url, order_id); if (NULL == uri) { diff --git a/src/backend/taler-merchant-httpd_helper.c b/src/backend/taler-merchant-httpd_helper.c index 53b4fe89..3e525245 100644 --- a/src/backend/taler-merchant-httpd_helper.c +++ b/src/backend/taler-merchant-httpd_helper.c @@ -413,6 +413,7 @@ bool TMH_template_contract_valid (const json_t *template_contract) { const char *summary; + const char *currency; struct TALER_Amount amount = { .value = 0}; uint32_t minimum_age = 0; struct GNUNET_TIME_Relative pay_duration = { 0 }; @@ -421,9 +422,10 @@ TMH_template_contract_valid (const json_t *template_contract) GNUNET_JSON_spec_string ("summary", &summary), NULL), - /* FIXME: #7951: may want to allow the template to only - fix the currency but not the amount; current approach - does not allow this! */ + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("currency", + ¤cy), + NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("amount", &amount), 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 d17e98c8..9edc553c 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -399,6 +399,18 @@ struct PayContext */ enum GNUNET_GenericReturnValue suspended; + /** + * Set to true if the deposit currency of a coin + * does not match the contract currency. + */ + bool deposit_currency_mismatch; + + /** + * Set to true if the database contains a (bogus) + * refund for a different currency. + */ + bool refund_currency_mismatch; + }; @@ -909,11 +921,13 @@ batch_deposit_transaction (const struct ExchangeGroup *eg, { const struct PayContext *pc = eg->pc; enum GNUNET_DB_QueryStatus qs; - struct TALER_Amount total_without_fees = { 0 }; + struct TALER_Amount total_without_fees; uint64_t b_dep_serial; uint32_t off = 0; - bool found = false; + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (pc->amount.currency, + &total_without_fees)); for (unsigned int i = 0; i<pc->coins_cnt; i++) { struct DepositConfirmation *dc = &pc->dc[i]; @@ -925,25 +939,14 @@ batch_deposit_transaction (const struct ExchangeGroup *eg, continue; if (dc->found_in_db) continue; - /* FIXME: #7951 */ GNUNET_assert (0 <= TALER_amount_subtract (&amount_without_fees, &dc->cdd.amount, &dc->deposit_fee)); - if (! found) - { - found = true; - total_without_fees = amount_without_fees; - } - else - { - /* FIXME: #7951 */ - GNUNET_assert ( - 0 <= - TALER_amount_add (&total_without_fees, - &total_without_fees, - &amount_without_fees)); - } + GNUNET_assert (0 <= + TALER_amount_add (&total_without_fees, + &total_without_fees, + &amount_without_fees)); } qs = TMH_db->insert_deposit_confirmation ( TMH_db->cls, @@ -960,13 +963,6 @@ batch_deposit_transaction (const struct ExchangeGroup *eg, if (qs <= 0) return qs; /* Entire batch already known or failure, we're done */ - if (! found) - { - /* All coins already done, but the batch was not? Invariant violation! */ - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - for (unsigned int i = 0; i<pc->coins_cnt; i++) { struct DepositConfirmation *dc = &pc->dc[i]; @@ -1596,14 +1592,26 @@ check_coin_paid (void *cls, (0 != strcmp (exchange_url, dc->exchange_url)) || - /* FIXME: #7951 */ + (GNUNET_OK != + TALER_amount_cmp_currency (amount_with_fee, + &dc->cdd.amount)) || (0 != TALER_amount_cmp (amount_with_fee, &dc->cdd.amount)) ) continue; /* does not match, skip */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deposit of coin `%s' already in our DB.\n", TALER_B2S (coin_pub)); - /* FIXME: #7951 */ + if ( (GNUNET_OK != + TALER_amount_cmp_currency (&pc->total_paid, + amount_with_fee)) || + (GNUNET_OK != + TALER_amount_cmp_currency (&pc->total_fees_paid, + deposit_fee)) ) + { + GNUNET_break_op (0); + pc->deposit_currency_mismatch = true; + break; + } GNUNET_assert (0 <= TALER_amount_add (&pc->total_paid, &pc->total_paid, @@ -1654,7 +1662,14 @@ check_coin_refunded (void *cls, if (0 != GNUNET_memcmp (coin_pub, &dc->cdd.coin_pub)) continue; - /* FIXME: #7951 */ + if (GNUNET_OK != + TALER_amount_cmp_currency (&pc->total_refunded, + refund_amount)) + { + GNUNET_break (0); + pc->refund_currency_mismatch = true; + break; + } GNUNET_assert (0 <= TALER_amount_add (&pc->total_refunded, &pc->total_refunded, @@ -1681,15 +1696,12 @@ check_payment_sufficient (struct PayContext *pc) struct TALER_Amount total_needed; if (0 == pc->coins_cnt) + return TALER_amount_is_zero (&pc->amount); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (pc->amount.currency, + &total_wire_fee)); + for (unsigned int i = 0; i < pc->num_exchanges; i++) { - return ((0 == pc->amount.value) && - (0 == pc->amount.fraction)); - } - - total_wire_fee = pc->egs[0]->wire_fee; - for (unsigned int i = 1; i < pc->num_exchanges; i++) - { - /* FIXME: #7951 */ if (GNUNET_OK != TALER_amount_cmp_currency (&total_wire_fee, &pc->egs[i]->wire_fee)) @@ -1700,7 +1712,6 @@ check_payment_sufficient (struct PayContext *pc) total_wire_fee.currency); return false; } - /* FIXME: #7951 */ if (0 > TALER_amount_add (&total_wire_fee, &total_wire_fee, @@ -1714,19 +1725,34 @@ check_payment_sufficient (struct PayContext *pc) } } - acc_fee = pc->dc[0].deposit_fee; - acc_amount = pc->dc[0].cdd.amount; - /** * This loops calculates what are the deposit fee / total * amount with fee / and wire fee, for all the coins. */ - for (unsigned int i = 1; i<pc->coins_cnt; i++) + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (pc->amount.currency, + &acc_fee)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (pc->amount.currency, + &acc_amount)); + for (unsigned int i = 0; i<pc->coins_cnt; i++) { struct DepositConfirmation *dc = &pc->dc[i]; GNUNET_assert (dc->found_in_db); - /* FIXME: #7951 */ + if ( (GNUNET_OK != + TALER_amount_cmp_currency (&acc_fee, + &dc->deposit_fee)) || + (GNUNET_OK != + TALER_amount_cmp_currency (&acc_amount, + &dc->cdd.amount)) ) + { + GNUNET_break_op (0); + resume_pay_with_error (pc, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + dc->deposit_fee.currency); + return false; + } if ( (0 > TALER_amount_add (&acc_fee, &dc->deposit_fee, @@ -1743,7 +1769,6 @@ check_payment_sufficient (struct PayContext *pc) "Overflow adding up amounts"); return false; } - /* FIXME: #7951 */ if (1 == TALER_amount_cmp (&dc->deposit_fee, &dc->cdd.amount)) @@ -1786,9 +1811,7 @@ check_payment_sufficient (struct PayContext *pc) return false; } - /* add wire fee to the total fees */ - /* FIXME: #7951 */ if (0 > TALER_amount_add (&acc_fee, &acc_fee, @@ -1800,7 +1823,6 @@ check_payment_sufficient (struct PayContext *pc) "Overflow adding up amounts"); return false; } - /* FIXME: #7951 */ if (-1 == TALER_amount_cmp (&pc->max_fee, &acc_fee)) { @@ -2045,6 +2067,14 @@ execute_pay_transaction (struct PayContext *pc) "lookup deposits"); return; } + if (pc->deposit_currency_mismatch) + { + GNUNET_break_op (0); + resume_pay_with_error (pc, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + pc->amount.currency); + return; + } } @@ -2072,6 +2102,14 @@ execute_pay_transaction (struct PayContext *pc) "lookup refunds"); return; } + if (pc->refund_currency_mismatch) + { + TMH_db->rollback (TMH_db->cls); + resume_pay_with_error (pc, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "refund currency in database does not match order currency"); + return; + } } /* Check if there are coins that still need to be processed */ @@ -2299,7 +2337,6 @@ parse_pay (struct PayContext *pc) GNUNET_break_op (0); return res; } - for (unsigned int j = 0; j<coins_index; j++) { if (0 == @@ -2657,6 +2694,36 @@ check_contract (struct PayContext *pc) } } + if (GNUNET_OK != + TALER_amount_cmp_currency (&pc->max_fee, + &pc->amount)) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "'max_fee' in database does not match currency of contract price"); + } + + for (unsigned int i=0;i<pc->coins_cnt;i++) + { + struct DepositConfirmation *dc = &pc->dc[i]; + + if (GNUNET_OK != + TALER_amount_cmp_currency (&dc->cdd.amount, + &pc->amount)) + { + GNUNET_break_op (0); + return (MHD_YES == + TALER_MHD_reply_with_error (pc->connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + pc->amount.currency)) + ? GNUNET_NO + : GNUNET_SYSERR; + } + } + if (GNUNET_TIME_timestamp_cmp (pc->wire_transfer_deadline, <, pc->refund_deadline)) diff --git a/src/backend/taler-merchant-httpd_post-using-templates.c b/src/backend/taler-merchant-httpd_post-using-templates.c index 9be6e46b..cdaf917e 100644 --- a/src/backend/taler-merchant-httpd_post-using-templates.c +++ b/src/backend/taler-merchant-httpd_post-using-templates.c @@ -160,7 +160,8 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh, { /* template */ - const char *tsummary; + const char *tsummary = NULL; + const char *tcurrency = NULL; uint32_t min_age; struct GNUNET_TIME_Relative pay_duration; struct TALER_Amount tamount; @@ -170,9 +171,10 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh, GNUNET_JSON_spec_string ("summary", &tsummary), NULL), - /* FIXME: #7951: may want to allow the template to only - fix the currency but not the amount; current approach - does not allow this! */ + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("currency", + &tcurrency), + NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("amount", &tamount), @@ -219,6 +221,19 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh, NULL); } + if ( (! no_amount) && + (NULL != tcurrency) && + (0 != strcmp (tcurrency, + amount.currency)) ) + { + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + tcurrency); + } + if (no_amount && no_tamount) { GNUNET_JSON_parse_free (spec); 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 14e71762..98bf2ab8 100644 --- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2017-2022 Taler Systems SA + (C) 2017-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -285,6 +285,19 @@ struct GetOrderRequestContext */ bool transfer_status_requested; + /** + * Set to true if our database (incorrectly) has refunds + * in a different currency than the currency of the + * original payment for the order. + */ + bool refund_currency_mismatch; + + /** + * Set to true if our database (incorrectly) has deposits + * in a different currency than the currency of the + * original payment for the order. + */ + bool deposit_currency_mismatch; }; @@ -484,6 +497,29 @@ deposit_get_cb (void *cls, return; } /* Compute total amount *wired* */ + if ( (GNUNET_OK != + TALER_amount_cmp_currency ( + &gorc->deposits_total, + &dr->details.ok.coin_contribution)) || + (GNUNET_OK != + TALER_amount_cmp_currency ( + &gorc->deposit_fees_total, + &tq->deposit_fee)) ) + { + /* something very wrong in our database ... */ + GNUNET_break (0); + gorc_report (gorc, + TALER_EC_GENERIC_DB_FETCH_FAILED, + &tq->coin_pub, + NULL); + GNUNET_free (tq->exchange_url); + GNUNET_free (tq); + if (NULL == gorc->tq_head) + gorc_resume (gorc, + 0, + TALER_EC_NONE); + return; + } if (0 > TALER_amount_add (&gorc->deposits_total, &gorc->deposits_total, @@ -775,12 +811,29 @@ process_refunds_cb (void *cls, GNUNET_memcmp (&tq->coin_pub, coin_pub)) { + if (GNUNET_OK != + TALER_amount_cmp_currency ( + &gorc->deposit_fees_total, + &tq->deposit_fee)) + { + gorc->refund_currency_mismatch = true; + return; + } + GNUNET_assert (0 <= TALER_amount_subtract (&gorc->deposit_fees_total, &gorc->deposit_fees_total, &tq->deposit_fee)); } } + if (GNUNET_OK != + TALER_amount_cmp_currency ( + &gorc->refund_amount, + refund_amount)) + { + gorc->refund_currency_mismatch = true; + return; + } GNUNET_assert (0 <= TALER_amount_add (&gorc->refund_amount, &gorc->refund_amount, @@ -817,6 +870,18 @@ process_transfer_details ( json_t *wire_details = gorc->wire_details; struct TALER_Amount wired; + if ( (GNUNET_OK != + TALER_amount_cmp_currency (&gorc->deposits_total, + deposit_value)) || + (GNUNET_OK != + TALER_amount_cmp_currency (&gorc->deposit_fees_total, + deposit_fee)) ) + { + GNUNET_break (0); + gorc->deposit_currency_mismatch = true; + return; + } + /* Compute total amount *wired* */ GNUNET_assert (0 < TALER_amount_add (&gorc->deposits_total, @@ -1233,12 +1298,11 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, { /* suspend connection, wait for exchange to check wire transfer status there */ gorc->transfer_status_requested = false; /* only try ONCE */ - /* FIXME: #7951 */ GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TMH_currency, + TALER_amount_set_zero (gorc->contract_amount.currency, &gorc->deposits_total)); GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TMH_currency, + TALER_amount_set_zero (gorc->contract_amount.currency, &gorc->deposit_fees_total)); TMH_db->lookup_deposits_by_order (TMH_db->cls, gorc->order_serial, @@ -1316,9 +1380,8 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, GNUNET_assert (paid); /* Accumulate refunds, if any. */ { - /* FIXME: #7951 */ GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TMH_currency, + TALER_amount_set_zero (gorc->contract_amount.currency, &gorc->refund_amount)); qs = TMH_db->lookup_refunds_detailed (TMH_db->cls, hc->instance->settings.id, @@ -1334,18 +1397,25 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_DB_FETCH_FAILED, "detailed refunds"); } + if (gorc->refund_currency_mismatch) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "refunds in different currency than original order price"); + } /* Generate final reply, including wire details if we have them */ { MHD_RESULT ret; char *order_status_url; - /* FIXME: #7951 */ GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TMH_currency, + TALER_amount_set_zero (gorc->contract_amount.currency, &gorc->deposits_total)); GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TMH_currency, + 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, @@ -1359,6 +1429,14 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_DB_FETCH_FAILED, "transfer details"); } + if (gorc->deposit_currency_mismatch) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "deposits in different currency than original order price"); + } if (! wired) { diff --git a/src/backend/taler-merchant-httpd_private-get-orders.c b/src/backend/taler-merchant-httpd_private-get-orders.c index 3b8f8c34..0e0511cc 100644 --- a/src/backend/taler-merchant-httpd_private-get-orders.c +++ b/src/backend/taler-merchant-httpd_private-get-orders.c @@ -251,6 +251,16 @@ process_refunds_cb (void *cls, { struct TALER_Amount *total_refund_amount = cls; + if (GNUNET_OK != + TALER_amount_cmp_currency (total_refund_amount, + refund_amount)) + { + /* Database error, refunds in mixed currency in DB. Not OK! */ + /* FIXME: we may want to return DB error to the client instead of just + ignoring the refund. */ + GNUNET_break (0); + return; + } GNUNET_assert (0 <= TALER_amount_add (total_refund_amount, total_refund_amount, @@ -387,7 +397,6 @@ add_order (void *cls, { struct TALER_Amount refund_amount; - /* FIXME: #7951 */ GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (order_amount.currency, &refund_amount)); diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index 09c709e9..7d9bc4f0 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -1238,6 +1238,7 @@ patch_order (struct OrderContext *oc) * mostly because in GNUnet relative times can't * be negative. */ struct GNUNET_TIME_Relative auto_refund; + bool no_fee; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("merchant_base_url", @@ -1276,7 +1277,7 @@ patch_order (struct OrderContext *oc) GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("max_fee", &oc->max_fee), - NULL), + &no_fee), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("delivery_date", &delivery_date), @@ -1310,7 +1311,20 @@ patch_order (struct OrderContext *oc) reply_with_error (oc, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_CURRENCY_MISMATCH, - NULL); + "no trusted exchange for this currency"); + return; + } + if ( (! no_fee) && + (GNUNET_OK != + TALER_amount_cmp_currency (&oc->brutto, + &oc->max_fee)) ) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + "different currencies used for 'max_fee' and 'amount' currency"); return; } diff --git a/src/backend/taler-merchant-httpd_private-post-templates.c b/src/backend/taler-merchant-httpd_private-post-templates.c index 4a5d8133..a064769c 100644 --- a/src/backend/taler-merchant-httpd_private-post-templates.c +++ b/src/backend/taler-merchant-httpd_private-post-templates.c @@ -189,7 +189,7 @@ TMH_private_post_templates (const struct TMH_RequestHandler *rh, /* idempotency check: is etp == tp? */ { bool eq; - + eq = templates_equal (&tp, &etp); TALER_MERCHANTDB_template_details_free (&etp); diff --git a/src/backenddb/pg_increase_refund.c b/src/backenddb/pg_increase_refund.c index 832a84eb..eef7adc6 100644 --- a/src/backenddb/pg_increase_refund.c +++ b/src/backenddb/pg_increase_refund.c @@ -91,7 +91,14 @@ process_refund_cb (void *cls, ictx->err = true; return; } - /* FIXME: #7951 */ + if (GNUNET_OK != + TALER_amount_cmp_currency (&ictx->refunded_amount, + &acc)) + { + GNUNET_break (0); + ictx->err = true; + return; + } if (0 > TALER_amount_add (&ictx->refunded_amount, &ictx->refunded_amount, |