aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/include/taler_merchant_service.h55
-rw-r--r--src/lib/Makefile.am1
-rw-r--r--src/lib/merchant_api_post_order_abort.c186
-rw-r--r--src/testing/Makefile.am2
-rw-r--r--src/testing/testing_api_cmd_abort_order.c460
-rw-r--r--src/testing/testing_api_cmd_pay_abort.c595
-rw-r--r--src/testing/testing_api_cmd_pay_order.c (renamed from src/testing/testing_api_cmd_pay.c)0
7 files changed, 534 insertions, 765 deletions
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h
index 5037201d..d32d507b 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -1657,6 +1657,11 @@ struct TALER_MERCHANT_AbortedCoin
*/
struct TALER_ExchangePublicKeyP exchange_pub;
+ /**
+ * Refund fee charged by the exchange. The API will have checked the
+ * signature, but NOT that this is the expected fee.
+ */
+ struct TALER_Amount refund_fee;
};
@@ -1680,6 +1685,29 @@ typedef void
/**
+ * Information we need from the wallet for each coin when aborting.
+ */
+struct TALER_MERCHANT_AbortCoin
+{
+
+ /**
+ * Coin's public key.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Amount this coin contributes to (including fee).
+ */
+ struct TALER_Amount amount_with_fee;
+
+ /**
+ * URL of the exchange that issued @e coin_priv.
+ */
+ const char *exchange_url;
+
+};
+
+/**
* Run a payment abort operation, asking for the payment to be aborted,
* yieldingrefunds for coins that were previously spend on a payment that
* failed to go through.
@@ -1688,38 +1716,23 @@ typedef void
*
* @param ctx execution context
* @param merchant_url base URL of the merchant
- * @param h_wire hash of the merchant’s account details
+ * @param order_id order to abort
* @param h_contract hash of the contact of the merchant with the customer
- * @param transaction_id transaction id for the transaction between merchant and customer
- * @param amount total value of the contract to be paid to the merchant
- * @param max_fee maximum fee covered by the merchant (according to the contract)
* @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
- * @param merchant_sig signature from the merchant over the original contract
- * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
- * @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed)
- * @param pay_deadline maximum time limit to pay for this contract
* @param num_coins number of coins used to pay
* @param coins array of coins we use to pay
- * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key.
- * @param payref_cb the callback to call when a reply for this request is available
- * @param payref_cb_cls closure for @a pay_cb
+ * @param cb the callback to call when a reply for this request is available
+ * @param cb_cls closure for @a cb
* @return a handle for this request
*/
struct TALER_MERCHANT_OrderAbortHandle *
TALER_MERCHANT_order_abort (struct GNUNET_CURL_Context *ctx,
const char *merchant_url,
- const struct GNUNET_HashCode *h_contract,
- const struct TALER_Amount *amount,
- const struct TALER_Amount *max_fee,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct TALER_MerchantSignatureP *merchant_sig,
- struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute refund_deadline,
- struct GNUNET_TIME_Absolute pay_deadline,
- const struct GNUNET_HashCode *h_wire,
const char *order_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_contract,
unsigned int num_coins,
- const struct TALER_MERCHANT_PayCoin coins[],
+ const struct TALER_MERCHANT_AbortCoin coins[],
TALER_MERCHANT_AbortCallback cb,
void *cb_cls);
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index ddf42aff..c9520112 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -29,6 +29,7 @@ libtalermerchant_la_SOURCES = \
merchant_api_post_instances.c \
merchant_api_post_products.c \
merchant_api_post_orders.c \
+ merchant_api_post_order_abort.c \
merchant_api_post_order_claim.c \
merchant_api_post_order_pay.c \
merchant_api_post_order_refund.c \
diff --git a/src/lib/merchant_api_post_order_abort.c b/src/lib/merchant_api_post_order_abort.c
index 6ee20f93..3f1a7c0e 100644
--- a/src/lib/merchant_api_post_order_abort.c
+++ b/src/lib/merchant_api_post_order_abort.c
@@ -95,16 +95,16 @@ struct TALER_MERCHANT_OrderAbortHandle
/**
- * Check that the response for a /pay refund is well-formed,
+ * Check that the response for an abort is well-formed,
* and call the application callback with the result if it is
* OK. Otherwise returns #GNUNET_SYSERR.
*
- * @param ph handle to operation that created the reply
+ * @param oah handle to operation that created the reply
* @param json the reply to parse
* @return #GNUNET_OK on success
*/
static int
-check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *ph,
+check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah,
const json_t *json)
{
json_t *refunds;
@@ -161,9 +161,10 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *ph,
&res[i].exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&res[i].exchange_pub),
+ TALER_JSON_spec_amount ("refund_fee",
+ &res[i].refund_fee),
GNUNET_JSON_spec_end ()
};
- int found;
if (GNUNET_OK !=
GNUNET_JSON_parse (exchange_reply,
@@ -177,7 +178,7 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *ph,
}
{
- struct TALER_RefundRequestPS rr = {
+ struct TALER_RefundConfirmationPS rr = {
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.purpose.size = htonl (sizeof (rr)),
.h_contract_terms = oah->h_contract_terms,
@@ -189,12 +190,12 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *ph,
TALER_amount_hton (&rr.refund_amount,
&oah->coins[i].amount_with_fee);
TALER_amount_hton (&rr.refund_fee,
- &oah->coins[i].refund_fee);
+ &res[i].refund_fee);
if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND,
&rr,
- &sig->eddsa_sig,
- &merchant_pub.eddsa_pub))
+ &res[i].exchange_sig.eddsa_signature,
+ &res[i].exchange_pub.eddsa_pub))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
@@ -210,6 +211,7 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *ph,
oah->abort_cb (oah->abort_cb_cls,
&hr,
+ &oah->merchant_pub,
num_refunds,
res);
}
@@ -221,124 +223,6 @@ check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *ph,
/**
- * We got a 403 response back from the exchange (or the merchant).
- * Now we need to check the provided cryptographic proof that the
- * coin was actually already spent!
- *
- * @param pc handle of the original coin we paid with
- * @param json cryptographic proof of coin's transaction
- * history as was returned by the exchange/merchant
- * @return #GNUNET_OK if proof checks out
- */
-static int
-check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
- json_t *json)
-{
- struct TALER_Amount spent;
- struct TALER_Amount spent_plus_contrib;
-
- if (GNUNET_OK !=
- TALER_EXCHANGE_verify_coin_history (NULL, /* do not verify fees */
- pc->amount_with_fee.currency,
- &pc->coin_pub,
- json,
- &spent))
- {
- /* Exchange's history fails to verify */
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (0 >
- TALER_amount_add (&spent_plus_contrib,
- &spent,
- &pc->amount_with_fee))
- {
- /* We got an integer overflow? Bad application! */
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (-1 != TALER_amount_cmp (&pc->denom_value,
- &spent_plus_contrib))
- {
- /* according to our calculations, the transaction should
- have still worked, exchange error! */
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Accepting proof of double-spending\n");
- return GNUNET_OK;
-}
-
-
-/**
- * We got a 409 response back from the exchange (or the merchant).
- * Now we need to check the provided cryptographic proof that the
- * coin was actually already spent!
- *
- * @param ph handle of the original pay operation
- * @param json cryptographic proof returned by the
- * exchange/merchant
- * @return #GNUNET_OK if proof checks out
- */
-static int
-check_conflict (struct TALER_MERCHANT_Pay *ph,
- const json_t *json)
-{
- json_t *history;
- json_t *ereply;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("exchange_reply", &ereply),
- GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
- GNUNET_JSON_spec_end ()
- };
- struct GNUNET_JSON_Specification hspec[] = {
- GNUNET_JSON_spec_json ("history", &history),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- GNUNET_JSON_parse (ereply,
- hspec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return GNUNET_SYSERR;
- }
- GNUNET_JSON_parse_free (spec);
-
- for (unsigned int i = 0; i<oah->num_coins; i++)
- {
- if (0 == memcmp (&oah->coins[i].coin_pub,
- &coin_pub,
- sizeof (struct TALER_CoinSpendPublicKeyP)))
- {
- int ret;
-
- ret = check_coin_history (&oah->coins[i],
- history);
- GNUNET_JSON_parse_free (hspec);
- return ret;
- }
- }
- GNUNET_break_op (0); /* complaint is not about any of the coins
- that we actually paid with... */
- GNUNET_JSON_parse_free (hspec);
- return GNUNET_SYSERR;
-}
-
-
-/**
* Function called when we're done processing the
* abort request.
*
@@ -436,9 +320,10 @@ handle_abort_finished (void *cls,
}
oah->abort_cb (oah->abort_cb_cls,
&hr,
+ NULL,
0,
NULL);
- TALER_MERCHANT_abort_cancel (oah);
+ TALER_MERCHANT_order_abort_cancel (oah);
}
@@ -470,6 +355,7 @@ TALER_MERCHANT_order_abort (struct GNUNET_CURL_Context *ctx,
{
struct TALER_MERCHANT_OrderAbortHandle *oah;
json_t *abort_obj;
+ json_t *j_coins;
j_coins = json_array ();
if (NULL == j_coins)
@@ -542,30 +428,32 @@ TALER_MERCHANT_order_abort (struct GNUNET_CURL_Context *ctx,
memcpy (oah->coins,
coins,
num_coins * sizeof (struct TALER_MERCHANT_AbortCoin));
-
- eh = curl_easy_init ();
- GNUNET_assert (NULL != eh);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&oah->post_ctx,
- eh,
- abort_obj))
{
- GNUNET_break (0);
+ CURL *eh;
+
+ eh = curl_easy_init ();
+ GNUNET_assert (NULL != eh);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&oah->post_ctx,
+ eh,
+ abort_obj))
+ {
+ GNUNET_break (0);
+ json_decref (abort_obj);
+ GNUNET_free (oah);
+ return NULL;
+ }
json_decref (abort_obj);
- GNUNET_free (oah);
- return NULL;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ oah->url));
+ oah->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ oah->post_ctx.headers,
+ &handle_abort_finished,
+ oah);
}
- json_decref (abort_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_URL,
- oah->url));
- oah->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- oah->post_ctx.headers,
- &handle_abort_finished,
- oah);
-
return oah;
}
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index 24374ee3..e9395135 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -15,6 +15,7 @@ libtalermerchanttesting_la_LDFLAGS = \
libtalermerchanttesting_la_SOURCES = \
testing_api_cmd_config.c \
+ testing_api_cmd_abort_order.c \
testing_api_cmd_claim_order.c \
testing_api_cmd_get_instance.c \
testing_api_cmd_get_instances.c \
@@ -23,6 +24,7 @@ libtalermerchanttesting_la_SOURCES = \
testing_api_cmd_delete_instance.c \
testing_api_cmd_delete_product.c \
testing_api_cmd_lock_product.c \
+ testing_api_cmd_pay_order.c \
testing_api_cmd_post_instances.c \
testing_api_cmd_post_products.c \
testing_api_cmd_post_orders.c \
diff --git a/src/testing/testing_api_cmd_abort_order.c b/src/testing/testing_api_cmd_abort_order.c
new file mode 100644
index 00000000..4444c349
--- /dev/null
+++ b/src/testing/testing_api_cmd_abort_order.c
@@ -0,0 +1,460 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2018, 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/testing_api_cmd_abort_order.c
+ * @brief command to test the abort feature.
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_testing_lib.h>
+#include <taler/taler_signatures.h>
+#include "taler_merchant_service.h"
+#include "taler_merchant_testing_lib.h"
+
+#define AMOUNT_WITH_FEE 0
+
+/**
+ * State for a " abort" CMD.
+ */
+struct AbortState
+{
+
+ /**
+ * Merchant public key.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Reference to the "pay" command to abort.
+ */
+ const char *pay_reference;
+
+ /**
+ * Merchant URL.
+ */
+ const char *merchant_url;
+
+ /**
+ * Handle to a "abort" operation.
+ */
+ struct TALER_MERCHANT_OrderAbortHandle *oah;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * The actual abort/refund data.
+ */
+ struct TALER_MERCHANT_AbortedCoin *acs;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int http_status;
+
+ /**
+ * How many refund permissions this CMD got
+ * the right for. Roughly, there is one refund
+ * permission for one coin.
+ */
+ unsigned int acs_length;
+
+};
+
+
+/**
+ * Parse the @a coins specification and grow the @a ac
+ * array with the coins found, updating @a nac.
+ *
+ * @param[in,out] ac pointer to array of coins found
+ * @param[in,out] nac length of array at @a pc
+ * @param[in] coins string specifying coins to add to @a pc,
+ * clobbered in the process
+ * @param is interpreter state
+ * @param amount_with_fee total amount to be paid for a contract.
+ * @param amount_without_fee to be removed, there is no
+ * per-contract fee, only per-coin exists.
+ * @param refund_fee per-contract? per-coin?
+ * @return #GNUNET_OK on success
+ */
+static int
+build_coins (struct TALER_MERCHANT_AbortCoin **ac,
+ unsigned int *nac,
+ char *coins,
+ struct TALER_TESTING_Interpreter *is,
+ const char *amount_with_fee)
+{
+ char *token;
+
+ for (token = strtok (coins, ";");
+ NULL != token;
+ token = strtok (NULL, ";"))
+ {
+ char *ctok;
+ unsigned int ci;
+ struct TALER_MERCHANT_AbortCoin *icoin;
+
+ /* Token syntax is "LABEL[/NUMBER]" */
+ ctok = strchr (token, '/');
+ ci = 0;
+ if (NULL != ctok)
+ {
+ *ctok = '\0';
+ ctok++;
+ if (1 != sscanf (ctok,
+ "%u",
+ &ci))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ // FIXME: ci not used!?
+ {
+ const struct TALER_TESTING_Command *coin_cmd;
+ coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
+ token);
+ if (NULL == coin_cmd)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_array_grow (*pc,
+ *npc,
+ (*npc) + 1);
+ icoin = &((*pc)[(*npc) - 1]);
+
+ {
+ const struct TALER_CoinSpendPublicKeyP *coin_pub;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_coin_pub (coin_cmd,
+ 0,
+ &coin_pub));
+ icoin->coin_pub = *coin_pub;
+ }
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_url (coin_cmd,
+ TALER_TESTING_UT_EXCHANGE_BASE_URL,
+ &icoin->exchange_url));
+ // FIXME: initialize icon->amount_with_fee!
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Callback for a "pay abort" operation. Mainly, check HTTP
+ * response code was as expected and stores refund permissions
+ * in the state.
+ *
+ * @param cls closure.
+ * @param hr HTTP response
+ * @param merchant_pub public key of the merchant refunding the
+ * contract.
+ * @param h_contract the contract involved in the refund.
+ * @param num_refunds length of the @a res array
+ * @param res array containing the abort confirmations
+ */
+static void
+abort_cb (void *cls,
+ const struct TALER_MERCHANT_HttpResponse *hr,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ unsigned int num_aborts,
+ const struct TALER_MERCHANT_AbortedCoin res[])
+{
+ struct AbortState *as = cls;
+
+ as->pao = NULL;
+ if (as->http_status != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u (%d) to command %s\n",
+ hr->http_status,
+ (int) hr->ec,
+ TALER_TESTING_interpreter_get_current_label (as->is));
+ TALER_TESTING_FAIL (as->is);
+ }
+ if ( (MHD_HTTP_OK == hr->http_status) &&
+ (TALER_EC_NONE == hr->ec) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received %u refunds\n",
+ num_refunds);
+ as->acs_length = num_aborts;
+ as->acs = GNUNET_new_array (num_aborts,
+ struct TALER_MERCHANT_AbortedCoin);
+ memcpy (as->res,
+ res,
+ num_refunds * sizeof (struct TALER_MERCHANT_AbortedCoin));
+ as->merchant_pub = *merchant_pub;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Successful pay-abort (HTTP status: %u)\n",
+ hr->http_status);
+ TALER_TESTING_interpreter_next (as->is);
+}
+
+
+/**
+ * Run an "abort" CMD.
+ *
+ * @param cls closure
+ * @param cmd command being run.
+ * @param is interpreter state
+ */
+static void
+abort_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct AbortState *as = cls;
+ const struct TALER_TESTING_Command *pay_cmd;
+ const char *proposal_reference;
+ const char *coin_reference;
+ const char *amount_with_fee;
+ const struct TALER_TESTING_Command *proposal_cmd;
+ const char *order_id;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ const struct GNUNET_HashCode *h_proposal;
+ struct TALER_Amount total_amount;
+ const char *error_name;
+ unsigned int error_line;
+ struct TALER_MERCHANT_AbortCoin *abort_coins;
+ unsigned int nabort_coins;
+ char *cr;
+ struct TALER_MERCHANT_OrderAbortHandle *oah;
+
+ as->is = is;
+ pay_cmd = TALER_TESTING_interpreter_lookup_command (is,
+ as->pay_reference);
+ if (NULL == pay_cmd)
+ TALER_TESTING_FAIL (is);
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_proposal_reference (pay_cmd,
+ 0,
+ &proposal_reference))
+ TALER_TESTING_FAIL (is);
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_coin_reference (pay_cmd,
+ 0,
+ &coin_reference))
+ TALER_TESTING_FAIL (is);
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_string (pay_cmd,
+ AMOUNT_WITH_FEE,
+ &amount_with_fee))
+ TALER_TESTING_FAIL (is);
+ proposal_cmd = TALER_TESTING_interpreter_lookup_command (is,
+ proposal_reference);
+
+ if (NULL == proposal_cmd)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ {
+ const json_t *contract_terms;
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_contract_terms (proposal_cmd,
+ 0,
+ &contract_terms))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ {
+ /* 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_fixed_auto ("merchant_pub",
+ &as->merchant_pub),
+ TALER_JSON_spec_amount ("amount",
+ &total_amount),
+ 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);
+ GNUNET_break_op (0);
+ return NULL;
+ }
+ }
+ }
+
+ cr = GNUNET_strdup (coin_reference);
+ pay_coins = NULL;
+ npay_coins = 0;
+ if (GNUNET_OK !=
+ build_coins (&abort_coins,
+ &nabort_coins,
+ cr,
+ is,
+ amount_with_fee))
+ {
+ GNUNET_array_grow (pay_coins,
+ npay_coins,
+ 0);
+ GNUNET_free (cr);
+ GNUNET_break (0);
+ return NULL;
+ }
+ GNUNET_free (cr);
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
+ 0,
+ &h_proposal))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ as->oah = TALER_MERCHANT_order_abort (is->ctx,
+ merchant_url,
+ order_id,
+ &as->merchant_pub,
+ h_proposal,
+ nabort_coins,
+ abort_coins,
+ api_cb,
+ api_cb_cls);
+ GNUNET_array_grow (pay_coins,
+ npay_coins,
+ 0);
+ if (NULL == as->pao)
+ TALER_TESTING_FAIL (is);
+}
+
+
+/**
+ * Free a "pay abort" CMD, and cancel it if need be.
+ *
+ * @param cls closure.
+ * @param cmd command currently being freed.
+ */
+static void
+abort_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct AbortState *as = cls;
+
+ if (NULL != as->pao)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command `%s' did not complete.\n",
+ TALER_TESTING_interpreter_get_current_label (
+ as->is));
+ TALER_MERCHANT_pay_cancel (as->pao);
+ }
+ GNUNET_free_non_null (as->res);
+ GNUNET_free (as);
+}
+
+
+/**
+ * Offer internal data useful to other commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param index index number of the object to extract.
+ * @return #GNUNET_OK on success
+ */
+static int
+abort_traits (void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct AbortState *as = cls;
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_merchant_pub (0,
+ &as->merchant_pub),
+ TALER_TESTING_make_trait_refund_entry (0,
+ as->acs),
+ TALER_TESTING_make_trait_uint (0,
+ &as->num_refunds),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+/**
+ * Make an "abort" test command.
+ *
+ * @param label command label
+ * @param merchant_url merchant base URL
+ * @param pay_reference reference to the payment to abort
+ * @param http_status expected HTTP response code
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_abort (const char *label,
+ const char *merchant_url,
+ const char *pay_reference,
+ unsigned int http_status)
+{
+ struct AbortState *as;
+
+ as = GNUNET_new (struct AbortState);
+ as->http_status = http_status;
+ as->pay_reference = pay_reference;
+ as->merchant_url = merchant_url;
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = as,
+ .label = label,
+ .run = &abort_run,
+ .cleanup = &abort_cleanup,
+ .traits = &abort_traits
+ };
+
+ return cmd;
+ }
+}
+
+
+/* end of testing_api_cmd_abort_order.c */
diff --git a/src/testing/testing_api_cmd_pay_abort.c b/src/testing/testing_api_cmd_pay_abort.c
deleted file mode 100644
index 5911dd1d..00000000
--- a/src/testing/testing_api_cmd_pay_abort.c
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2018, 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
- <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file lib/testing_api_cmd_pay_abort.c
- * @brief command to test the /pay abort feature.
- * @author Marcello Stanisci
- */
-
-#include "platform.h"
-#include <taler/taler_exchange_service.h>
-#include <taler/taler_testing_lib.h>
-#include <taler/taler_signatures.h>
-#include "taler_merchant_service.h"
-#include "taler_merchant_testing_lib.h"
-
-#define AMOUNT_WITH_FEE 0
-#define AMOUNT_WITHOUT_FEE 1
-#define REFUND_FEE 2
-
-/**
- * State for a "pay abort" CMD.
- */
-struct PayAbortState
-{
-
- /**
- * Expected HTTP response code.
- */
- unsigned int http_status;
-
- /**
- * Reference to the "pay" command to abort.
- */
- const char *pay_reference;
-
- /**
- * Merchant URL.
- */
- const char *merchant_url;
-
- /**
- * Handle to a "pay abort" operation.
- */
- struct TALER_MERCHANT_Pay *pao;
-
- /**
- * Interpreter state.
- */
- struct TALER_TESTING_Interpreter *is;
-
- /**
- * How many refund permissions this CMD got
- * the right for. Roughly, there is one refund
- * permission for one coin.
- */
- unsigned int num_refunds;
-
- /**
- * The actual refund data.
- */
- struct TALER_MERCHANT_RefundEntry *res;
-
- /**
- * The contract whose payment is to be aborted.
- */
- struct GNUNET_HashCode h_contract;
-
- /**
- * Merchant public key.
- */
- struct TALER_MerchantPublicKeyP merchant_pub;
-};
-
-
-/**
- * Parse the @a coins specification and grow the @a pc
- * array with the coins found, updating @a npc.
- *
- * @param[in,out] pc pointer to array of coins found
- * @param[in,out] npc length of array at @a pc
- * @param[in] coins string specifying coins to add to @a pc,
- * clobbered in the process
- * @param is interpreter state
- * @param amount_with_fee total amount to be paid for a contract.
- * @param amount_without_fee to be removed, there is no
- * per-contract fee, only per-coin exists.
- * @param refund_fee per-contract? per-coin?
- * @return #GNUNET_OK on success
- */
-static int
-build_coins (struct TALER_MERCHANT_PayCoin **pc,
- unsigned int *npc,
- char *coins,
- struct TALER_TESTING_Interpreter *is,
- const char *amount_with_fee,
- const char *amount_without_fee,
- const char *refund_fee)
-{
- char *token;
-
- for (token = strtok (coins, ";");
- NULL != token;
- token = strtok (NULL, ";"))
- {
- const struct TALER_TESTING_Command *coin_cmd;
- char *ctok;
- unsigned int ci;
- struct TALER_MERCHANT_PayCoin *icoin;
- const struct TALER_EXCHANGE_DenomPublicKey *dpk;
-
- /* Token syntax is "LABEL[/NUMBER]" */
- ctok = strchr (token, '/');
- ci = 0;
- if (NULL != ctok)
- {
- *ctok = '\0';
- ctok++;
- if (1 != sscanf (ctok,
- "%u",
- &ci))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- }
-
- coin_cmd = TALER_TESTING_interpreter_lookup_command
- (is, token);
-
- if (NULL == coin_cmd)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
- GNUNET_array_grow (*pc,
- *npc,
- (*npc) + 1);
-
- icoin = &((*pc)[(*npc) - 1]);
-
- {
- const struct TALER_CoinSpendPrivateKeyP *coin_priv;
- const struct TALER_DenominationSignature *denom_sig;
- const struct TALER_Amount *denom_value;
- const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
-
- GNUNET_assert
- (GNUNET_OK == TALER_TESTING_get_trait_coin_priv
- (coin_cmd, 0, &coin_priv));
-
- GNUNET_assert
- (GNUNET_OK == TALER_TESTING_get_trait_denom_pub
- (coin_cmd, 0, &denom_pub));
-
- GNUNET_assert
- (GNUNET_OK == TALER_TESTING_get_trait_denom_sig
- (coin_cmd, 0, &denom_sig));
-
- GNUNET_assert
- (GNUNET_OK == TALER_TESTING_get_trait_amount_obj
- (coin_cmd, 0, &denom_value));
-
- icoin->coin_priv = *coin_priv;
- icoin->denom_pub = denom_pub->key;
- icoin->denom_sig = *denom_sig;
- icoin->denom_value = *denom_value;
- icoin->amount_with_fee = *denom_value;
- }
- GNUNET_assert (NULL != (dpk =
- TALER_TESTING_find_pk (is->keys,
- &icoin->denom_value)));
-
- GNUNET_assert (0 <=
- TALER_amount_subtract (&icoin->amount_without_fee,
- &icoin->denom_value,
- &dpk->fee_deposit));
- GNUNET_assert (GNUNET_OK ==
- TALER_TESTING_get_trait_url (coin_cmd,
- TALER_TESTING_UT_EXCHANGE_BASE_URL,
- &icoin->exchange_url));
- GNUNET_assert (GNUNET_OK ==
- TALER_string_to_amount (refund_fee,
- &icoin->refund_fee));
- }
-
- return GNUNET_OK;
-}
-
-
-/**
- * Callback for a "pay abort" operation. Mainly, check HTTP
- * response code was as expected and stores refund permissions
- * in the state.
- *
- * @param cls closure.
- * @param hr HTTP response
- * @param merchant_pub public key of the merchant refunding the
- * contract.
- * @param h_contract the contract involved in the refund.
- * @param num_refunds how many refund permissions have been
- * issued.
- * @param res array containing the refund permissions.
- */
-static void
-pay_abort_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct GNUNET_HashCode *h_contract,
- unsigned int num_refunds,
- const struct TALER_MERCHANT_RefundEntry *res)
-{
- struct PayAbortState *pas = cls;
-
- pas->pao = NULL;
- if (pas->http_status != hr->http_status)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
- TALER_TESTING_interpreter_get_current_label (pas->is));
- TALER_TESTING_FAIL (pas->is);
- }
- if ( (MHD_HTTP_OK == hr->http_status) &&
- (TALER_EC_NONE == hr->ec) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Received %u refunds\n",
- num_refunds);
- pas->num_refunds = num_refunds;
- pas->res = GNUNET_new_array (num_refunds,
- struct TALER_MERCHANT_RefundEntry);
- memcpy (pas->res,
- res,
- num_refunds * sizeof (struct TALER_MERCHANT_RefundEntry));
- pas->h_contract = *h_contract;
- pas->merchant_pub = *merchant_pub;
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Successful pay-abort (HTTP status: %u)\n",
- hr->http_status);
- TALER_TESTING_interpreter_next (pas->is);
-}
-
-
-/**
- * Function used by both the "abort" operation.
- * It prepares data and sends the "pay-abort" request to the
- * backend.
- *
- * @param merchant_url base URL of the merchant serving the
- * request.
- * @param coin_reference reference to the CMD(s) that offer
- * "coins" traits. It is possible to give multiple
- * references by using semicolons to separate them.
- * @param proposal_refere reference to a "proposal" CMD.
- * @param is interpreter state.
- * @param amount_with_fee amount to be paid, including deposit
- * fee.
- * @param amount_without_fee amount to be paid, without deposit
- * fee.
- * @param refund_fee refund fee.
- * @param api_func "lib" function that will be called to either
- * issue a "pay" or "abort" request.
- * @param api_cb callback for @a api_func.
- * @param api_cb_cls closure for @a api_cb
- * @return handle to the operation, NULL if errors occur.
- */
-static struct TALER_MERCHANT_Pay *
-_pay_abort_run (const char *merchant_url,
- const char *coin_reference,
- const char *proposal_reference,
- struct TALER_TESTING_Interpreter *is,
- const char *amount_with_fee,
- const char *amount_without_fee,
- const char *refund_fee,
- TALER_MERCHANT_PayRefundCallback api_cb,
- void *api_cb_cls)
-{
- const struct TALER_TESTING_Command *proposal_cmd;
- const json_t *contract_terms;
- const char *order_id;
- struct GNUNET_TIME_Absolute refund_deadline;
- struct GNUNET_TIME_Absolute pay_deadline;
- struct GNUNET_TIME_Absolute timestamp;
- struct TALER_MerchantPublicKeyP merchant_pub;
- struct GNUNET_HashCode h_wire;
- const struct GNUNET_HashCode *h_proposal;
- struct TALER_Amount total_amount;
- struct TALER_Amount max_fee;
- const char *error_name;
- unsigned int error_line;
- struct TALER_MERCHANT_PayCoin *pay_coins;
- unsigned int npay_coins;
- char *cr;
- struct TALER_MerchantSignatureP *merchant_sig;
- struct TALER_MERCHANT_Pay *ret;
-
- proposal_cmd = TALER_TESTING_interpreter_lookup_command (is,
- proposal_reference);
-
- if (NULL == proposal_cmd)
- {
- GNUNET_break (0);
- return NULL;
- }
-
- if (GNUNET_OK !=
- TALER_TESTING_get_trait_contract_terms (proposal_cmd,
- 0,
- &contract_terms))
- {
- GNUNET_break (0);
- return NULL;
- }
- {
- /* 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_absolute_time ("refund_deadline",
- &refund_deadline),
- GNUNET_JSON_spec_absolute_time ("pay_deadline",
- &pay_deadline),
- GNUNET_JSON_spec_absolute_time ("timestamp",
- &timestamp),
- GNUNET_JSON_spec_fixed_auto ("merchant_pub",
- &merchant_pub),
- GNUNET_JSON_spec_fixed_auto ("h_wire",
- &h_wire),
- TALER_JSON_spec_amount ("amount",
- &total_amount),
- TALER_JSON_spec_amount ("max_fee",
- &max_fee),
- 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);
- GNUNET_break_op (0);
- return NULL;
- }
- }
-
- cr = GNUNET_strdup (coin_reference);
- pay_coins = NULL;
- npay_coins = 0;
- if (GNUNET_OK !=
- build_coins (&pay_coins,
- &npay_coins,
- cr,
- is,
- amount_with_fee,
- amount_without_fee,
- refund_fee))
- {
- GNUNET_array_grow (pay_coins,
- npay_coins,
- 0);
- GNUNET_free (cr);
- GNUNET_break (0);
- return NULL;
- }
-
- GNUNET_free (cr);
- if (GNUNET_OK !=
- TALER_TESTING_get_trait_merchant_sig (proposal_cmd,
- 0,
- &merchant_sig))
- {
- GNUNET_break (0);
- return NULL;
- }
- if (GNUNET_OK !=
- TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
- 0,
- &h_proposal))
- {
- GNUNET_break (0);
- return NULL;
- }
- ret = TALER_MERCHANT_pay_abort (is->ctx,
- merchant_url,
- h_proposal,
- &total_amount,
- &max_fee,
- &merchant_pub,
- merchant_sig,
- timestamp,
- refund_deadline,
- pay_deadline,
- &h_wire,
- order_id,
- npay_coins,
- pay_coins,
- api_cb,
- api_cb_cls);
- GNUNET_array_grow (pay_coins,
- npay_coins,
- 0);
- return ret;
-}
-
-
-/**
- * Free a "pay abort" CMD, and cancel it if need be.
- *
- * @param cls closure.
- * @param cmd command currently being freed.
- */
-static void
-pay_abort_cleanup (void *cls,
- const struct TALER_TESTING_Command *cmd)
-{
- struct PayAbortState *pas = cls;
-
- if (NULL != pas->pao)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Command `%s' did not complete.\n",
- TALER_TESTING_interpreter_get_current_label (
- pas->is));
- TALER_MERCHANT_pay_cancel (pas->pao);
- }
- GNUNET_free_non_null (pas->res);
- GNUNET_free (pas);
-}
-
-
-/**
- * Run a "pay abort" CMD.
- *
- * @param cls closure
- * @param cmd command being run.
- * @param is interpreter state
- */
-static void
-pay_abort_run (void *cls,
- const struct TALER_TESTING_Command *cmd,
- struct TALER_TESTING_Interpreter *is)
-{
-
- struct PayAbortState *pas = cls;
- const struct TALER_TESTING_Command *pay_cmd;
-
- const char *proposal_reference;
- const char *coin_reference;
- const char *amount_with_fee;
- const char *amount_without_fee;
- const char *refund_fee;
-
- pas->is = is;
- pay_cmd = TALER_TESTING_interpreter_lookup_command
- (is, pas->pay_reference);
- if (NULL == pay_cmd)
- TALER_TESTING_FAIL (is);
-
- if (GNUNET_OK != TALER_TESTING_get_trait_proposal_reference
- (pay_cmd, 0, &proposal_reference))
- TALER_TESTING_FAIL (is);
-
- if (GNUNET_OK != TALER_TESTING_get_trait_coin_reference
- (pay_cmd, 0, &coin_reference))
- TALER_TESTING_FAIL (is);
-
- if (GNUNET_OK != TALER_TESTING_get_trait_string
- (pay_cmd, AMOUNT_WITH_FEE, &amount_with_fee))
- TALER_TESTING_FAIL (is);
-
- if (GNUNET_OK != TALER_TESTING_get_trait_string
- (pay_cmd, AMOUNT_WITHOUT_FEE, &amount_without_fee))
- TALER_TESTING_FAIL (is);
-
- if (GNUNET_OK != TALER_TESTING_get_trait_string
- (pay_cmd, REFUND_FEE, &refund_fee))
- TALER_TESTING_FAIL (is);
-
- if (NULL == (pas->pao = _pay_abort_run (pas->merchant_url,
- coin_reference,
- proposal_reference,
- is,
- amount_with_fee,
- amount_without_fee,
- refund_fee,
- &pay_abort_cb,
- pas)))
- TALER_TESTING_FAIL (is);
-}
-
-
-/**
- * Offer internal data useful to other commands.
- *
- * @param cls closure
- * @param ret[out] result (could be anything)
- * @param trait name of the trait
- * @param index index number of the object to extract.
- * @return #GNUNET_OK on success
- */
-static int
-pay_abort_traits (void *cls,
- const void **ret,
- const char *trait,
- unsigned int index)
-{
- struct PayAbortState *pas = cls;
- struct TALER_TESTING_Trait traits[] = {
- TALER_TESTING_make_trait_merchant_pub
- (0, &pas->merchant_pub),
- TALER_TESTING_make_trait_h_contract_terms
- (0, &pas->h_contract),
- TALER_TESTING_make_trait_refund_entry
- (0, pas->res),
- TALER_TESTING_make_trait_uint (0, &pas->num_refunds),
- TALER_TESTING_trait_end ()
- };
-
- return TALER_TESTING_get_trait (traits,
- ret,
- trait,
- index);
-}
-
-
-/**
- * Make a "pay abort" test command.
- *
- * @param label command label
- * @param merchant_url merchant base URL
- * @param pay_reference reference to the payment to abort
- * @param http_status expected HTTP response code
- *
- * @return the command
- */
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_pay_abort (const char *label,
- const char *merchant_url,
- const char *pay_reference,
- unsigned int http_status)
-{
- struct PayAbortState *pas;
-
- pas = GNUNET_new (struct PayAbortState);
- pas->http_status = http_status;
- pas->pay_reference = pay_reference;
- pas->merchant_url = merchant_url;
- {
- struct TALER_TESTING_Command cmd = {
- .cls = pas,
- .label = label,
- .run = &pay_abort_run,
- .cleanup = &pay_abort_cleanup,
- .traits = &pay_abort_traits
- };
-
- return cmd;
- }
-}
-
-
-/* end of testing_api_cmd_pay_abort.c */
diff --git a/src/testing/testing_api_cmd_pay.c b/src/testing/testing_api_cmd_pay_order.c
index 02bdf408..02bdf408 100644
--- a/src/testing/testing_api_cmd_pay.c
+++ b/src/testing/testing_api_cmd_pay_order.c