/* This file is part of TALER Copyright (C) 2020 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 Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file testing_api_cmd_wallet_post_orders_refund.c * @brief command to test refunds. * @author Marcello Stanisci * @author Christian Grothoff */ #include "platform.h" #include #include #include "taler_merchant_service.h" #include "taler_merchant_testing_lib.h" /** * State for an "obtain refunds" CMD. */ struct WalletRefundState { /** * Operation handle for a (public) POST /orders/$ID/refund request. */ struct TALER_MERCHANT_WalletOrderRefundHandle *orh; /** * Base URL of the merchant serving the request. */ const char *merchant_url; /** * Interpreter state. */ struct TALER_TESTING_Interpreter *is; /** * Expected HTTP response code. */ unsigned int http_code; /** * Label of the command that created the order we want to obtain refunds for. */ const char *proposal_reference; /** * A list of refunds associated with this order. */ const char **refunds; /** * The length of @e refunds. */ unsigned int refunds_length; }; /** * Process POST /refund (increase) response; just checking * if the HTTP response code is the one expected. * * @param cls closure * @param hr HTTP response * @param refund_amount refund amount * @param merchant_pub public key of the merchant giving the refund * @param refunds the given refunds * @param refunds_length how many refunds were given */ static void refund_cb ( void *cls, const struct TALER_MERCHANT_HttpResponse *hr, const struct TALER_Amount *refund_amount, const struct TALER_MerchantPublicKeyP *merchant_pub, struct TALER_MERCHANT_RefundDetail refunds[], unsigned int refunds_length) { struct WalletRefundState *wrs = cls; wrs->orh = NULL; if (wrs->http_code != hr->http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Expected status %u, got %u(%d) for refund increase\n", wrs->http_code, hr->http_status, (int) hr->ec); TALER_TESTING_FAIL (wrs->is); } switch (hr->http_status) { case MHD_HTTP_OK: { struct TALER_Amount refunded_total; if (refunds_length > 0) GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (refunds[0].refund_amount.currency, &refunded_total)); for (unsigned int i = 0; i < refunds_length; ++i) { const struct TALER_TESTING_Command *refund_cmd; const char *expected_amount_str; struct TALER_Amount expected_amount; refund_cmd = TALER_TESTING_interpreter_lookup_command ( wrs->is, wrs->refunds[i]); if (GNUNET_OK != TALER_TESTING_get_trait_string (refund_cmd, 0, &expected_amount_str)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not fetch refund amount\n"); TALER_TESTING_interpreter_fail (wrs->is); return; } GNUNET_assert (GNUNET_OK == TALER_string_to_amount (expected_amount_str, &expected_amount)); /* The most recent refunds are returned first */ GNUNET_assert (0 <= TALER_amount_add (&refunded_total, &refunded_total, &refunds[refunds_length - 1 - i].refund_amount)); if ((GNUNET_OK != TALER_amount_cmp_currency (&expected_amount, &refunded_total)) || (0 != TALER_amount_cmp (&expected_amount, &refunded_total))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Refund amounts do not match\n"); TALER_TESTING_interpreter_fail (wrs->is); return; } } } break; default: break; } TALER_TESTING_interpreter_next (wrs->is); } /** * Run the "refund increase" CMD. * * @param cls closure. * @param cmd command currently being run. * @param is the interpreter state. */ static void obtain_refunds_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) { struct WalletRefundState *wrs = cls; const struct TALER_TESTING_Command *proposal_cmd = TALER_TESTING_interpreter_lookup_command (is, wrs->proposal_reference); const struct GNUNET_HashCode *h_contract_terms; const char *order_id; if (NULL == proposal_cmd) TALER_TESTING_FAIL (is); if (GNUNET_OK != TALER_TESTING_get_trait_h_contract_terms (proposal_cmd, 0, &h_contract_terms)) TALER_TESTING_FAIL (is); { const json_t *contract_terms; const char *error_name; unsigned int error_line; if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms (proposal_cmd, 0, &contract_terms)) TALER_TESTING_FAIL (is); { /* Get information that needs to be put verbatim in the * deposit permission */ struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("order_id", &order_id), GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (contract_terms, spec, &error_name, &error_line)) { char *js; js = json_dumps (contract_terms, JSON_INDENT (1)); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Parser failed on %s:%u for input `%s'\n", error_name, error_line, js); free (js); TALER_TESTING_FAIL (is); } } } wrs->is = is; wrs->orh = TALER_MERCHANT_wallet_post_order_refund ( is->ctx, wrs->merchant_url, order_id, h_contract_terms, &refund_cb, wrs); if (NULL == wrs->orh) TALER_TESTING_FAIL (is); } /** * Free the state of a "refund increase" CMD, and * possibly cancel a pending "refund increase" operation. * * @param cls closure * @param cmd command currently being freed. */ static void obtain_refunds_cleanup (void *cls, const struct TALER_TESTING_Command *cmd) { struct WalletRefundState *wrs = cls; if (NULL != wrs->orh) { TALER_LOG_WARNING ("Refund operation did not complete\n"); TALER_MERCHANT_wallet_post_order_refund_cancel (wrs->orh); } GNUNET_array_grow (wrs->refunds, wrs->refunds_length, 0); GNUNET_free (wrs); } struct TALER_TESTING_Command TALER_TESTING_cmd_wallet_order_refund (const char *label, const char *merchant_url, const char *order_ref, unsigned int http_code, ...) { struct WalletRefundState *wrs; wrs = GNUNET_new (struct WalletRefundState); wrs->merchant_url = merchant_url; wrs->proposal_reference = order_ref; wrs->http_code = http_code; wrs->refunds_length = 0; { const char *clabel; va_list ap; va_start (ap, http_code); while (NULL != (clabel = va_arg (ap, const char *))) { GNUNET_array_append (wrs->refunds, wrs->refunds_length, clabel); } va_end (ap); } { struct TALER_TESTING_Command cmd = { .cls = wrs, .label = label, .run = &obtain_refunds_run, .cleanup = &obtain_refunds_cleanup }; return cmd; } }