gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-exchange] branch master updated: largely fix #5077


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated: largely fix #5077
Date: Wed, 06 Dec 2017 19:24:03 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 0426168  largely fix #5077
0426168 is described below

commit 042616899f89d38167632e3ff24b16469a27fbef
Author: Christian Grothoff <address@hidden>
AuthorDate: Wed Dec 6 19:24:00 2017 +0100

    largely fix #5077
---
 src/auditor/taler-wire-auditor.c              |  18 ++
 src/bank-lib/Makefile.am                      |   3 +-
 src/bank-lib/bank_api_admin.c                 |  21 ++-
 src/bank-lib/bank_api_common.c                |  30 ++-
 src/bank-lib/bank_api_common.h                |  10 +
 src/bank-lib/bank_api_history.c               |  91 ++++++---
 src/bank-lib/bank_api_reject.c                | 245 ++++++++++++++++++++++++
 src/bank-lib/fakebank.c                       | 256 ++++++++++++++++++++++++--
 src/bank-lib/test_bank_api.c                  |  23 +++
 src/bank-lib/test_bank_api_with_fakebank.c    |  37 ++++
 src/bank-lib/test_bank_interpreter.c          | 214 +++++++++++++++++----
 src/bank-lib/test_bank_interpreter.h          |  29 ++-
 src/exchange/taler-exchange-wirewatch.c       |   8 +
 src/include/taler_bank_service.h              |  77 +++++++-
 src/include/taler_error_codes.h               |  28 +++
 src/include/taler_fakebank_lib.h              |  17 ++
 src/include/taler_wire_plugin.h               |   2 +
 src/wire/plugin_wire_test.c                   |  74 ++++++--
 src/wire/test_wire_plugin_transactions_test.c |   2 +
 19 files changed, 1073 insertions(+), 112 deletions(-)

diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c
index 6d5085e..8ba38d3 100644
--- a/src/auditor/taler-wire-auditor.c
+++ b/src/auditor/taler-wire-auditor.c
@@ -830,6 +830,7 @@ check_exchange_wire_out ()
  * transactions).
  *
  * @param cls closure
+ * @param ec error code in case something went wrong
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -838,6 +839,7 @@ check_exchange_wire_out ()
  */
 static int
 history_debit_cb (void *cls,
+                  enum TALER_ErrorCode ec,
                  enum TALER_BANK_Direction dir,
                  const void *row_off,
                  size_t row_off_size,
@@ -848,6 +850,13 @@ history_debit_cb (void *cls,
 
   if (TALER_BANK_DIRECTION_NONE == dir)
   {
+    if (TALER_EC_NONE != ec)
+    {
+      /* FIXME: log properly to audit report! */
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Error fetching history: %u!\n",
+                  (unsigned int) ec);
+    }
     /* end of iteration, now check wire_out to see
        if it matches #out_map */
     hh = NULL;
@@ -1069,6 +1078,7 @@ conclude_credit_history ()
  * transactions).
  *
  * @param cls closure
+ * @param ec error code in case something went wrong
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -1077,6 +1087,7 @@ conclude_credit_history ()
  */
 static int
 history_credit_cb (void *cls,
+                   enum TALER_ErrorCode ec,
                    enum TALER_BANK_Direction dir,
                    const void *row_off,
                    size_t row_off_size,
@@ -1087,6 +1098,13 @@ history_credit_cb (void *cls,
 
   if (TALER_BANK_DIRECTION_NONE == dir)
   {
+    if (TALER_EC_NONE != ec)
+    {
+      /* FIXME: log properly to audit report! */
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Error fetching history: %u!\n",
+                  (unsigned int) ec);
+    }
     /* end of operation */
     hh = NULL;
     conclude_credit_history ();
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index 7489fe7..da8dd9d 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -17,7 +17,8 @@ libtalerbank_la_LDFLAGS = \
 libtalerbank_la_SOURCES = \
   bank_api_admin.c \
   bank_api_common.c bank_api_common.h \
-  bank_api_history.c
+  bank_api_history.c \
+  bank_api_reject.c
 
 libtalerbank_la_LIBADD = \
   $(top_builddir)/src/json/libtalerjson.la \
diff --git a/src/bank-lib/bank_api_admin.c b/src/bank-lib/bank_api_admin.c
index cebd934..4a1732e 100644
--- a/src/bank-lib/bank_api_admin.c
+++ b/src/bank-lib/bank_api_admin.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2015, 2016, 2017 GNUnet e.V.
+  Copyright (C) 2015, 2016, 2017 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
@@ -79,11 +79,13 @@ handle_admin_add_incoming_finished (void *cls,
 {
   struct TALER_BANK_AdminAddIncomingHandle *aai = cls;
   uint64_t serial_id = UINT64_MAX;
+  enum TALER_ErrorCode ec;
 
   aai->job = NULL;
   switch (response_code)
   {
   case 0:
+    ec = TALER_EC_INVALID_RESPONSE;
     break;
   case MHD_HTTP_OK:
     {
@@ -100,29 +102,36 @@ handle_admin_add_incoming_finished (void *cls,
       {
         GNUNET_break_op (0);
         response_code = 0;
+        ec = TALER_EC_INVALID_RESPONSE;
         break;
       }
+      ec = TALER_EC_NONE;
     }
     break;
   case MHD_HTTP_BAD_REQUEST:
     /* This should never happen, either us or the bank is buggy
        (or API version conflict); just pass JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_FORBIDDEN:
     /* Access denied */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_UNAUTHORIZED:
     /* Nothing really to verify, bank says one of the signatures is
        invalid; as we checked them, this should never happen, we
        should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_NOT_FOUND:
     /* Nothing really to verify, this should never
        happen, we should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   default:
     /* unexpected response code */
@@ -130,11 +139,13 @@ handle_admin_add_incoming_finished (void *cls,
                 "Unexpected response code %u\n",
                 (unsigned int) response_code);
     GNUNET_break (0);
+    ec = TALER_BANK_parse_ec_ (json);
     response_code = 0;
     break;
   }
   aai->cb (aai->cb_cls,
            response_code,
+           ec,
            serial_id,
            json);
   TALER_BANK_admin_add_incoming_cancel (aai);
@@ -151,7 +162,7 @@ handle_admin_add_incoming_finished (void *cls,
  * @param bank_base_url URL of the bank (used to execute this request)
  * @param auth authentication data to send to the bank
  * @param exchange_base_url base URL of the exchange (for tracking)
- * @param wtid wire transfer identifier for the transfer
+ * @param subject wire transfer subject for the transfer
  * @param amount amount that was deposited
  * @param debit_account_no account number to withdraw from (53 bits at most)
  * @param credit_account_no account number to deposit into (53 bits at most)
@@ -166,7 +177,7 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context 
*ctx,
                                const char *bank_base_url,
                                const struct TALER_BANK_AuthenticationData 
*auth,
                                const char *exchange_base_url,
-                               const struct TALER_WireTransferIdentifierRawP 
*wtid,
+                               const char *subject,
                                const struct TALER_Amount *amount,
                                uint64_t debit_account_no,
                                uint64_t credit_account_no,
@@ -182,10 +193,10 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context 
*ctx,
     GNUNET_break (0);
     return NULL;
   }
-  admin_obj = json_pack ("{s:{s:s}, s:s, s:o, s:o, s:I, s:I}",
+  admin_obj = json_pack ("{s:{s:s}, s:s, s:s, s:o, s:I, s:I}",
                          "auth", "type", "basic",
                          "exchange_url", exchange_base_url,
-                         "wtid", GNUNET_JSON_from_data_auto (wtid),
+                         "subject", subject,
                          "amount", TALER_JSON_from_amount (amount),
                          "debit_account", (json_int_t) debit_account_no,
                          "credit_account", (json_int_t) credit_account_no);
diff --git a/src/bank-lib/bank_api_common.c b/src/bank-lib/bank_api_common.c
index 738d2a5..b4b1974 100644
--- a/src/bank-lib/bank_api_common.c
+++ b/src/bank-lib/bank_api_common.c
@@ -80,7 +80,7 @@ TALER_BANK_make_auth_header_ (const struct 
TALER_BANK_AuthenticationData *auth)
     authh = append (authh,
                     "X-Taler-Bank-Password",
                     auth->details.basic.password);
-    return authh;    
+    return authh;
   }
   return NULL;
 }
@@ -111,5 +111,33 @@ TALER_BANK_path_to_url_ (const char *u,
 }
 
 
+/**
+ * Parse error code given in @a json.
+ *
+ * @param json the json to parse
+ * @return error code, or #TALER_EC_INVALID if not found
+ */
+enum TALER_ErrorCode
+TALER_BANK_parse_ec_ (const json_t *json)
+{
+  uint32_t ec;
+
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_uint32 ("ec",
+                             &ec),
+    GNUNET_JSON_spec_end()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return TALER_EC_INVALID;
+  }
+  return (enum TALER_ErrorCode) ec;
+}
+
 
 /* end of bank_api_common.c */
diff --git a/src/bank-lib/bank_api_common.h b/src/bank-lib/bank_api_common.h
index 9d7a780..5d6578c 100644
--- a/src/bank-lib/bank_api_common.h
+++ b/src/bank-lib/bank_api_common.h
@@ -51,4 +51,14 @@ TALER_BANK_path_to_url_ (const char *u,
                          const char *path);
 
 
+/**
+ * Parse error code given in @a json.
+ *
+ * @param json the json to parse
+ * @return error code, or #TALER_EC_INVALID if not found
+ */
+enum TALER_ErrorCode
+TALER_BANK_parse_ec_ (const json_t *json);
+
+
 #endif
diff --git a/src/bank-lib/bank_api_history.c b/src/bank-lib/bank_api_history.c
index e134f20..a6c7dce 100644
--- a/src/bank-lib/bank_api_history.c
+++ b/src/bank-lib/bank_api_history.c
@@ -118,22 +118,37 @@ parse_account_history (struct TALER_BANK_HistoryHandle 
*hh,
       return GNUNET_SYSERR;
     }
 
+    if (0 == strcasecmp (sign,
+                         "+"))
+      direction = TALER_BANK_DIRECTION_CREDIT;
+    else if (0 == strcasecmp (sign,
+                              "-"))
+      direction = TALER_BANK_DIRECTION_DEBIT;
+    else if (0 == strcasecmp (sign,
+                              "cancel+"))
+      direction = TALER_BANK_DIRECTION_CREDIT | TALER_BANK_DIRECTION_CANCEL;
+    else if (0 == strcasecmp (sign,
+                              "cancel-"))
+      direction = TALER_BANK_DIRECTION_DEBIT | TALER_BANK_DIRECTION_CANCEL;
+    else
+    {
+      GNUNET_break_op (0);
+      GNUNET_JSON_parse_free (hist_spec);
+      return GNUNET_SYSERR;
+    }
     td.account_details = json_pack ("{s:s, s:s, s:I}",
                                     "type", "test",
                                     "bank_uri", hh->bank_base_url,
                                     "account_number", (json_int_t) 
other_account);
-    direction = (0 == strcasecmp (sign,
-                                  "+"))
-      ? TALER_BANK_DIRECTION_CREDIT
-      : TALER_BANK_DIRECTION_DEBIT;
     hh->hcb (hh->hcb_cls,
              MHD_HTTP_OK,
+             TALER_EC_NONE,
              direction,
              serial_id,
              &td,
              transaction);
-    GNUNET_JSON_parse_free (hist_spec);
     json_decref (td.account_details);
+    GNUNET_JSON_parse_free (hist_spec);
   }
   return GNUNET_OK;
 }
@@ -153,6 +168,7 @@ handle_history_finished (void *cls,
                          const json_t *json)
 {
   struct TALER_BANK_HistoryHandle *hh = cls;
+  enum TALER_ErrorCode ec;
 
   hh->job = NULL;
   switch (response_code)
@@ -166,31 +182,38 @@ handle_history_finished (void *cls,
     {
       GNUNET_break_op (0);
       response_code = 0;
+      ec = TALER_EC_INVALID_RESPONSE;
       break;
     }
     response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
     break;
   case MHD_HTTP_NO_CONTENT:
+    ec = TALER_EC_NONE;
     break;
   case MHD_HTTP_BAD_REQUEST:
     /* This should never happen, either us or the bank is buggy
        (or API version conflict); just pass JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_FORBIDDEN:
     /* Access denied */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_UNAUTHORIZED:
     /* Nothing really to verify, bank says one of the signatures is
        invalid; as we checked them, this should never happen, we
        should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_NOT_FOUND:
     /* Nothing really to verify, this should never
        happen, we should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   default:
     /* unexpected response code */
@@ -198,11 +221,13 @@ handle_history_finished (void *cls,
                 "Unexpected response code %u\n",
                 (unsigned int) response_code);
     GNUNET_break (0);
+    ec = TALER_BANK_parse_ec_ (json);
     response_code = 0;
     break;
   }
   hh->hcb (hh->hcb_cls,
            response_code,
+           ec,
            TALER_BANK_DIRECTION_NONE,
            0LLU,
            NULL,
@@ -243,6 +268,8 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
   struct TALER_BANK_HistoryHandle *hh;
   CURL *eh;
   char *url;
+  const char *dir;
+  const char *can;
 
   if (0 == num_results)
   {
@@ -254,36 +281,42 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
     GNUNET_break (0);
     return NULL;
   }
+
+  dir = NULL;
+  if (TALER_BANK_DIRECTION_BOTH == (TALER_BANK_DIRECTION_BOTH & direction))
+    dir = "both";
+  else if (TALER_BANK_DIRECTION_CREDIT == (TALER_BANK_DIRECTION_CREDIT & 
direction))
+    dir = "credit";
+  else if (TALER_BANK_DIRECTION_DEBIT == (TALER_BANK_DIRECTION_BOTH & 
direction))
+    dir = "debit";
+  if (NULL == dir)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  if (TALER_BANK_DIRECTION_CANCEL == (TALER_BANK_DIRECTION_CANCEL & direction))
+    can = "show";
+  else
+    can = "omit";
   if (UINT64_MAX == start_row)
   {
-    if (TALER_BANK_DIRECTION_BOTH == direction)
-      GNUNET_asprintf (&url,
-                       "/history?auth=basic&account_number=%llu&delta=%lld",
-                       (unsigned long long) account_number,
-                       (long long) num_results);
-    else
-      GNUNET_asprintf (&url,
-                       
"/history?auth=basic&account_number=%llu&delta=%lld&direction=%s",
-                       (unsigned long long) account_number,
-                       (long long) num_results,
-                       (TALER_BANK_DIRECTION_CREDIT == direction) ? "credit" : 
"debit");
+    GNUNET_asprintf (&url,
+                     
"/history?auth=basic&account_number=%llu&delta=%lld&direction=%s&cancelled=%s",
+                     (unsigned long long) account_number,
+                     (long long) num_results,
+                     dir,
+                     can);
 
   }
   else
   {
-    if (TALER_BANK_DIRECTION_BOTH == direction)
-      GNUNET_asprintf (&url,
-                       
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu",
-                       (unsigned long long) account_number,
-                       (long long) num_results,
-                       (unsigned long long) start_row);
-    else
-      GNUNET_asprintf (&url,
-                       
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu&direction=%s",
-                       (unsigned long long) account_number,
-                       (long long) num_results,
-                       (unsigned long long) start_row,
-                       (TALER_BANK_DIRECTION_CREDIT == direction) ? "credit" : 
"debit");
+    GNUNET_asprintf (&url,
+                     
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu&direction=%s&cancelled=%s",
+                     (unsigned long long) account_number,
+                     (long long) num_results,
+                     (unsigned long long) start_row,
+                     dir,
+                     can);
   }
 
   hh = GNUNET_new (struct TALER_BANK_HistoryHandle);
diff --git a/src/bank-lib/bank_api_reject.c b/src/bank-lib/bank_api_reject.c
new file mode 100644
index 0000000..c630ccd
--- /dev/null
+++ b/src/bank-lib/bank_api_reject.c
@@ -0,0 +1,245 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015, 2016, 2017 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 bank-lib/bank_api_reject.c
+ * @brief Implementation of the /reject request of the bank's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "bank_api_common.h"
+#include <microhttpd.h> /* just for HTTP status codes */
+#include "taler_signatures.h"
+
+
+/**
+ * @brief A /reject Handle
+ */
+struct TALER_BANK_RejectHandle
+{
+
+  /**
+   * The url for this request.
+   */
+  char *request_url;
+
+  /**
+   * JSON encoding of the request to POST.
+   */
+  char *json_enc;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * HTTP authentication-related headers for the request.
+   */
+  struct curl_slist *authh;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_BANK_RejectResultCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reject request.
+ *
+ * @param cls the `struct TALER_BANK_RejectHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param json parsed JSON result, NULL on error
+ */
+static void
+handle_reject_finished (void *cls,
+                        long response_code,
+                        const json_t *json)
+{
+  struct TALER_BANK_RejectHandle *rh = cls;
+  enum TALER_ErrorCode ec;
+
+  rh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    ec = TALER_EC_INVALID_RESPONSE;
+    break;
+  case MHD_HTTP_OK:
+    GNUNET_break_op (0);
+    response_code = 0;
+    ec = TALER_EC_INVALID_RESPONSE;
+    break;
+  case MHD_HTTP_NO_CONTENT:
+    ec = TALER_EC_NONE;
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the bank is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    /* Access denied */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  case MHD_HTTP_UNAUTHORIZED:
+    /* Nothing really to verify, bank says one of the signatures is
+       invalid; as we checked them, this should never happen, we
+       should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    ec = TALER_BANK_parse_ec_ (json);
+    response_code = 0;
+    break;
+  }
+  rh->cb (rh->cb_cls,
+          response_code,
+          ec);
+  TALER_BANK_reject_cancel (rh);
+}
+
+
+/**
+ * Request rejection of a wire transfer, marking it as cancelled and voiding
+ * its effects.
+ *
+ * @param ctx curl context for the event loop
+ * @param bank_base_url URL of the bank (used to execute this request)
+ * @param auth authentication data to use
+ * @param account_number which account number should we query
+ * @param rowid transfer to reject
+ * @param rcb the callback to call with the operation result
+ * @param rcb_cls closure for @a rcb
+ * @return NULL
+ *         if the inputs are invalid.
+ *         In this case, the callback is not called.
+ */
+struct TALER_BANK_RejectHandle *
+TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
+                   const char *bank_base_url,
+                   const struct TALER_BANK_AuthenticationData *auth,
+                   uint64_t account_number,
+                   uint64_t rowid,
+                   TALER_BANK_RejectResultCallback rcb,
+                   void *rcb_cls)
+{
+  struct TALER_BANK_RejectHandle *rh;
+  json_t *reject_obj;
+  CURL *eh;
+
+  reject_obj = json_pack ("{s:{s:s}, s:I, s:I}",
+                          "auth", "type", "basic",
+                          "row_id", (json_int_t) rowid,
+                          "credit_account", (json_int_t) account_number);
+  if (NULL == reject_obj)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  rh = GNUNET_new (struct TALER_BANK_RejectHandle);
+  rh->cb = rcb;
+  rh->cb_cls = rcb_cls;
+  rh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
+                                             "/reject");
+  rh->authh = TALER_BANK_make_auth_header_ (auth);
+  /* Append content type header here, can't do it in GNUNET_CURL_job_add
+     as that would override the CURLOPT_HTTPHEADER instead of appending. */
+  {
+    struct curl_slist *ext;
+
+    ext = curl_slist_append (rh->authh,
+                             "Content-Type: application/json");
+    if (NULL == ext)
+      GNUNET_break (0);
+    else
+      rh->authh = ext;
+  }
+  eh = curl_easy_init ();
+  GNUNET_assert (NULL != (rh->json_enc =
+                          json_dumps (reject_obj,
+                                      JSON_COMPACT)));
+  json_decref (reject_obj);
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_HTTPHEADER,
+                                   rh->authh));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_URL,
+                                   rh->request_url));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_POSTFIELDS,
+                                   rh->json_enc));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_POSTFIELDSIZE,
+                                   strlen (rh->json_enc)));
+  rh->job = GNUNET_CURL_job_add (ctx,
+                                 eh,
+                                 GNUNET_NO,
+                                 &handle_reject_finished,
+                                 rh);
+  return rh;
+}
+
+
+/**
+ * Cancel an reject request.  This function cannot be used on a request
+ * handle if the response was is already served for it.
+ *
+ * @param rh the reject request handle
+ */
+void
+TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh)
+{
+  if (NULL != rh->job)
+  {
+    GNUNET_CURL_job_cancel (rh->job);
+    rh->job = NULL;
+  }
+  curl_slist_free_all (rh->authh);
+  GNUNET_free (rh->request_url);
+  GNUNET_free (rh->json_enc);
+  GNUNET_free (rh);
+}
+
+
+/* end of bank_api_reject.c */
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index 2689241..6717572 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -81,6 +81,11 @@ struct Transaction
    * Number of this transaction.
    */
   uint64_t serial_id;
+
+  /**
+   * Flag set if the transfer was rejected.
+   */
+  int rejected;
 };
 
 
@@ -220,6 +225,31 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle 
*h,
 
 
 /**
+ * Reject incoming wire transfer to account @a credit_account
+ * as identified by @a rowid.
+ *
+ * @param h fake bank handle
+ * @param rowid identifies transfer to reject
+ * @param credit_account account number of owner of credited account
+ * @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not 
found
+ */
+int
+TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
+                                uint64_t rowid,
+                                uint64_t credit_account)
+{
+  for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
+    if ( (t->serial_id == rowid) &&
+         (t->credit_account == credit_account) )
+    {
+      t->rejected = GNUNET_YES;
+      return GNUNET_YES;
+    }
+  return GNUNET_NO;
+}
+
+
+/**
  * Check that no wire transfers were ordered (or at least none
  * that have not been taken care of via #TALER_FAKEBANK_check()).
  * If any transactions are onrecord, return #GNUNET_SYSERR.
@@ -288,6 +318,62 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
 
 
 /**
+ * Create and queue a bank error message with the HTTP response
+ * code @a response_code on connection @a connection.
+ *
+ * @param connection where to queue the reply
+ * @param response_code http status code to use
+ * @param ec taler error code to use
+ * @param message human readable error message
+ * @return MHD status code
+ */
+static int
+create_bank_error (struct MHD_Connection *connection,
+                   unsigned int response_code,
+                   enum TALER_ErrorCode ec,
+                   const char *message)
+{
+  json_t *json;
+  struct MHD_Response *resp;
+  void *json_str;
+  size_t json_len;
+  int ret;
+
+  json = json_pack ("{s:s, s:I}",
+                    "error",
+                    message,
+                    "ec",
+                    (json_int_t) ec);
+  json_str = json_dumps (json,
+                         JSON_INDENT(2));
+  json_decref (json);
+  if (NULL == json_str)
+  {
+    GNUNET_break (0);
+    return MHD_NO;
+  }
+  json_len = strlen (json_str);
+  resp = MHD_create_response_from_buffer (json_len,
+                                          json_str,
+                                          MHD_RESPMEM_MUST_FREE);
+  if (NULL == resp)
+  {
+    GNUNET_break (0);
+    free (json_str);
+    return MHD_NO;
+  }
+  (void) MHD_add_response_header (resp,
+                                  MHD_HTTP_HEADER_CONTENT_TYPE,
+                                  "application/json");
+  ret = MHD_queue_response (connection,
+                            response_code,
+                            resp);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+/**
  * Function called whenever MHD is done with a request.  If the
  * request was a POST, we may have stored a `struct Buffer *` in the
  * @a con_cls that might still need to be cleaned up.  Call the
@@ -359,13 +445,13 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle 
*h,
     break;
   }
   {
-    const char *wtid;
+    const char *subject;
     uint64_t debit_account;
     uint64_t credit_account;
     const char *base_url;
     struct TALER_Amount amount;
     struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_string ("wtid", &wtid),
+      GNUNET_JSON_spec_string ("subject", &subject),
       GNUNET_JSON_spec_uint64 ("debit_account", &debit_account),
       GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
       TALER_JSON_spec_amount ("amount", &amount),
@@ -385,7 +471,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
                                               debit_account,
                                               credit_account,
                                               &amount,
-                                              wtid,
+                                              subject,
                                               base_url);
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Receiving incoming wire transfer: %llu->%llu from %s\n",
@@ -434,6 +520,94 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
 
 
 /**
+ * Handle incoming HTTP request for /reject.
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct Buffer *`)
+ * @return MHD result code
+ */
+static int
+handle_reject (struct TALER_FAKEBANK_Handle *h,
+               struct MHD_Connection *connection,
+               const char *upload_data,
+               size_t *upload_data_size,
+               void **con_cls)
+{
+  enum GNUNET_JSON_PostResult pr;
+  json_t *json;
+  struct MHD_Response *resp;
+  int ret;
+  int found;
+
+  pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+                                con_cls,
+                                upload_data,
+                                upload_data_size,
+                                &json);
+  switch (pr)
+  {
+  case GNUNET_JSON_PR_OUT_OF_MEMORY:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_CONTINUE:
+    return MHD_YES;
+  case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_JSON_INVALID:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_SUCCESS:
+    break;
+  }
+  {
+    uint64_t serial_id;
+    uint64_t credit_account;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_uint64 ("row_id", &serial_id),
+      GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
+      GNUNET_JSON_spec_end ()
+    };
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (json,
+                           spec,
+                           NULL, NULL))
+    {
+      GNUNET_break (0);
+      json_decref (json);
+      return MHD_NO;
+    }
+    found = TALER_FAKEBANK_reject_transfer (h,
+                                            serial_id,
+                                            credit_account);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Rejected wire transfer #%llu (to %llu)\n",
+                (unsigned long long) serial_id,
+                (unsigned long long) credit_account);
+  }
+  json_decref (json);
+
+  if (GNUNET_OK != found)
+    return create_bank_error (connection,
+                              MHD_HTTP_NOT_FOUND,
+                              TALER_EC_BANK_REJECT_NOT_FOUND,
+                              "transaction unknown");
+  /* finally build regular response */
+  resp = MHD_create_response_from_buffer (0,
+                                          NULL,
+                                          MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection,
+                            MHD_HTTP_NO_CONTENT,
+                            resp);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+/**
  * Handle incoming HTTP request for /history
  *
  * @param h the fakebank handle
@@ -451,6 +625,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
   const char *start;
   const char *dir;
   const char *acc;
+  const char *cancelled;
   unsigned long long account_number;
   unsigned long long start_number;
   long long count;
@@ -469,6 +644,9 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
   dir = MHD_lookup_connection_value (connection,
                                      MHD_GET_ARGUMENT_KIND,
                                      "direction");
+  cancelled = MHD_lookup_connection_value (connection,
+                                           MHD_GET_ARGUMENT_KIND,
+                                           "cancelled");
   start = MHD_lookup_connection_value (connection,
                                        MHD_GET_ARGUMENT_KIND,
                                        "start");
@@ -496,7 +674,14 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
          (1 != sscanf (start,
                        "%llu",
                        &start_number)) ) ||
-       ( (NULL != dir) &&
+       (NULL == dir) ||
+       (NULL == cancelled) ||
+       ( (0 != strcasecmp (cancelled,
+                           "OMIT")) &&
+         (0 != strcasecmp (cancelled,
+                           "SHOW")) ) ||
+       ( (0 != strcasecmp (dir,
+                           "BOTH")) &&
          (0 != strcasecmp (dir,
                            "CREDIT")) &&
          (0 != strcasecmp (dir,
@@ -513,13 +698,40 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
               dir,
               (unsigned long long) account_number,
               start);
-  if (NULL == dir)
-    direction = TALER_BANK_DIRECTION_BOTH;
-  else if (0 == strcasecmp (dir,
-                            "CREDIT"))
+  if (0 == strcasecmp (dir,
+                       "CREDIT"))
+  {
     direction = TALER_BANK_DIRECTION_CREDIT;
-  else
+  }
+  else if (0 == strcasecmp (dir,
+                            "DEBIT"))
+  {
     direction = TALER_BANK_DIRECTION_DEBIT;
+  }
+  else if (0 == strcasecmp (dir,
+                            "BOTH"))
+  {
+    direction = TALER_BANK_DIRECTION_BOTH;
+  }
+  else
+  {
+    GNUNET_assert (0);
+    return MHD_NO;
+  }
+  if (0 == strcasecmp (cancelled,
+                       "OMIT"))
+  {
+    /* nothing */
+  } else if (0 == strcasecmp (cancelled,
+                              "SHOW"))
+  {
+    direction |= TALER_BANK_DIRECTION_CANCEL;
+  }
+  else
+  {
+    GNUNET_assert (0);
+    return MHD_NO;
+  }
   if (NULL == start)
   {
     if (count > 0)
@@ -557,6 +769,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
   {
     json_t *trans;
     char *subject;
+    const char *sign;
 
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Found transaction over %s from %llu to %llu\n",
@@ -564,10 +777,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
                 (unsigned long long) pos->debit_account,
                 (unsigned long long) pos->credit_account);
 
-    if (! ( ( (account_number == pos->debit_account) &&
-              (0 != (direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
-            ( (account_number == pos->credit_account) &&
-              (0 != (direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) )
+    if ( (! ( ( (account_number == pos->debit_account) &&
+                (0 != (direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
+              ( (account_number == pos->credit_account) &&
+                (0 != (direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) ) ||
+         ( (0 == (direction & TALER_BANK_DIRECTION_CANCEL)) &&
+           (GNUNET_YES == pos->rejected) ) )
     {
       if (count > 0)
         pos = pos->next;
@@ -580,11 +795,15 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
                      "%s %s",
                      pos->subject,
                      pos->exchange_base_url);
+    sign =
+      (account_number == pos->debit_account)
+      ? (pos->rejected ? "cancel-" : "-")
+      : (pos->rejected ? "cancel+" : "+");
     trans = json_pack ("{s:I, s:o, s:o, s:s, s:I, s:s}",
                        "row_id", (json_int_t) pos->serial_id,
                        "date", GNUNET_JSON_from_time_abs (pos->date),
                        "amount", TALER_JSON_from_amount (&pos->amount),
-                       "sign", (account_number == pos->debit_account) ? "-" : 
"+",
+                       "sign", sign,
                        "counterpart", (json_int_t) ( (account_number == 
pos->debit_account)
                                                      ? pos->credit_account
                                                      : pos->debit_account),
@@ -699,6 +918,15 @@ handle_mhd_request (void *cls,
                                       upload_data_size,
                                       con_cls);
   if ( (0 == strcasecmp (url,
+                         "/reject")) &&
+       (0 == strcasecmp (method,
+                         MHD_HTTP_METHOD_POST)) )
+    return handle_reject (h,
+                          connection,
+                          upload_data,
+                          upload_data_size,
+                          con_cls);
+  if ( (0 == strcasecmp (url,
                          "/history")) &&
        (0 == strcasecmp (method,
                          MHD_HTTP_METHOD_GET)) )
diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c
index 19d15ca..80c462d 100644
--- a/src/bank-lib/test_bank_api.c
+++ b/src/bank-lib/test_bank_api.c
@@ -50,6 +50,7 @@ run (void *cls)
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "deposit-1",
       .details.admin_add_incoming.exchange_base_url = "https://exchange.net/";, 
/* bogus */
+      .details.admin_add_incoming.subject = "subject 1",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
       .details.admin_add_incoming.credit_account_no = 1,
       .details.admin_add_incoming.debit_account_no = 2, /* Ignored */
@@ -58,6 +59,7 @@ run (void *cls)
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "deposit-2",
       .details.admin_add_incoming.exchange_base_url = "https://exchange.net/";, 
/* bogus */
+      .details.admin_add_incoming.subject = "subject 2",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
       .details.admin_add_incoming.credit_account_no = 1,
       .details.admin_add_incoming.debit_account_no = 2, /* Ignored */
@@ -89,6 +91,27 @@ run (void *cls)
       .details.history.direction = TALER_BANK_DIRECTION_DEBIT,
       .details.history.start_row_ref = "deposit-1",
       .details.history.num_results = 5 },
+    { .oc = TBI_OC_REJECT,
+      .label = "reject-1",
+      .details.reject.cmd_ref = "deposit-1" },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r1",
+      .details.history.account_number = 2,
+      .details.history.direction = TALER_BANK_DIRECTION_CREDIT,
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r2",
+      .details.history.account_number = 2,
+      .details.history.direction = TALER_BANK_DIRECTION_DEBIT,
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r3",
+      .details.history.account_number = 2,
+      .details.history.direction = TALER_BANK_DIRECTION_BOTH | 
TALER_BANK_DIRECTION_CANCEL,
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
     { .oc = TBI_OC_END }
   };
 
diff --git a/src/bank-lib/test_bank_api_with_fakebank.c 
b/src/bank-lib/test_bank_api_with_fakebank.c
index e16c3a9..440851b 100644
--- a/src/bank-lib/test_bank_api_with_fakebank.c
+++ b/src/bank-lib/test_bank_api_with_fakebank.c
@@ -48,6 +48,7 @@ run (void *cls)
     /* Add EUR:5.01 to account 1 */
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "debit-1",
+      .details.admin_add_incoming.subject = "subject 1",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
       .details.admin_add_incoming.credit_account_no = 1,
       .details.admin_add_incoming.debit_account_no = 2,
@@ -68,6 +69,7 @@ run (void *cls)
       .details.history.num_results = 5 },
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "debit-2",
+      .details.admin_add_incoming.subject = "subject 2",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
       .details.admin_add_incoming.credit_account_no = 3,
       .details.admin_add_incoming.debit_account_no = 2,
@@ -75,6 +77,7 @@ run (void *cls)
       .details.admin_add_incoming.amount = "KUDOS:3.21" },
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "credit-2",
+      .details.admin_add_incoming.subject = "credit 2",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
       .details.admin_add_incoming.credit_account_no = 2,
       .details.admin_add_incoming.debit_account_no = 3,
@@ -105,6 +108,40 @@ run (void *cls)
     /* check transfer list is now empty */
     { .oc = TBI_OC_EXPECT_TRANSFERS_EMPTY,
       .label = "expect-empty" },
+    /* Add EUR:5.01 to account 1 */
+    { .oc = TBI_OC_ADMIN_ADD_INCOMING,
+      .label = "credit-for-reject-1",
+      .details.admin_add_incoming.subject = "subject 3",
+      .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
+      .details.admin_add_incoming.credit_account_no = 1,
+      .details.admin_add_incoming.debit_account_no = 2,
+      .details.admin_add_incoming.exchange_base_url = "https://exchange.net/";,
+      .details.admin_add_incoming.amount = "KUDOS:5.01" },
+    { .oc = TBI_OC_REJECT,
+      .label = "reject-1",
+      .details.reject.cmd_ref = "credit-for-reject-1" },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r1",
+      .details.history.account_number = 1,
+      .details.history.direction = TALER_BANK_DIRECTION_BOTH,
+      /* range is exclusive, and everything up to and including "credit-2"
+         was already killed via TBI_OC_EXPECT_TRANSFER and thus won't show
+         in the history. So to see the rejected transfer, we need to start
+         looking after "credit-2" */
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r1c",
+      .details.history.account_number = 1,
+      .details.history.direction = TALER_BANK_DIRECTION_BOTH | 
TALER_BANK_DIRECTION_CANCEL,
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    { .oc = TBI_OC_EXPECT_TRANSFER,
+      .label = "expect-credit-reject-1",
+      .details.expect_transfer.cmd_ref = "credit-for-reject-1" },
+    /* check transfer list is now empty */
+    { .oc = TBI_OC_EXPECT_TRANSFERS_EMPTY,
+      .label = "expect-empty-2" },
     { .oc = TBI_OC_END }
   };
 
diff --git a/src/bank-lib/test_bank_interpreter.c 
b/src/bank-lib/test_bank_interpreter.c
index f5aee8e..fac0ab6 100644
--- a/src/bank-lib/test_bank_interpreter.c
+++ b/src/bank-lib/test_bank_interpreter.c
@@ -110,7 +110,6 @@ static const struct TBI_Command *
 find_command (const struct InterpreterState *is,
               const char *label)
 {
-  unsigned int i;
   const struct TBI_Command *cmd;
 
   if (NULL == label)
@@ -119,7 +118,7 @@ find_command (const struct InterpreterState *is,
                 "Attempt to lookup command for empty label\n");
     return NULL;
   }
-  for (i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
+  for (unsigned int i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
     if ( (NULL != cmd->label) &&
          (0 == strcmp (cmd->label,
                        label)) )
@@ -132,6 +131,63 @@ find_command (const struct InterpreterState *is,
 
 
 /**
+ * Test if the /admin/add/incoming transaction at offset @a off
+ * has been /rejected.
+ *
+ * @param is interpreter state (where we are right now)
+ * @param off offset of the command to test for rejection
+ * @return #GNUNET_YES if the command at @a off was cancelled
+ */
+static int
+test_cancelled (struct InterpreterState *is,
+                unsigned int off)
+{
+  const struct TBI_Command *cmd = &is->commands[off];
+
+  for (unsigned int i=0;i<is->ip;i++)
+  {
+    const struct TBI_Command *c = &is->commands[i];
+
+    if (TBI_OC_REJECT != c->oc)
+      continue;
+    if (0 == strcmp (c->details.reject.cmd_ref,
+                     cmd->label))
+      return GNUNET_YES;
+  }
+  return GNUNET_NO;
+}
+
+
+/**
+ * Test if the /admin/add/incoming transaction at offset @a off
+ * has been #TBI_OC_EXPECT_TRANSFER treated, and thus been
+ * forgotten by the fakebank.
+ *
+ * @param is interpreter state (where we are right now)
+ * @param off offset of the command to test for rejection
+ * @return #GNUNET_YES if the command at @a off was cancelled
+ */
+static int
+test_deleted_by_expected (struct InterpreterState *is,
+                          unsigned int off)
+{
+  const struct TBI_Command *cmd = &is->commands[off];
+
+  for (unsigned int i=0;i<is->ip;i++)
+  {
+    const struct TBI_Command *c = &is->commands[i];
+
+    if (TBI_OC_EXPECT_TRANSFER != c->oc)
+      continue;
+    if (0 == strcmp (c->details.expect_transfer.cmd_ref,
+                     cmd->label))
+      return GNUNET_YES;
+  }
+  return GNUNET_NO;
+}
+
+
+/**
  * Item in the transaction history, as reconstructed from the
  * command history.
  */
@@ -214,6 +270,7 @@ build_history (struct InterpreterState *is,
   for (unsigned int off = start;off != end + inc; off += inc)
   {
     const struct TBI_Command *pos = &is->commands[off];
+    int cancelled;
 
     if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc)
       continue;
@@ -229,6 +286,15 @@ build_history (struct InterpreterState *is,
       continue; /* skip until we find the marker */
     if (total >= cmd->details.history.num_results * inc)
       break; /* hit limit specified by command */
+    if (GNUNET_YES ==
+        test_deleted_by_expected (is,
+                                  off))
+      continue;
+    cancelled = test_cancelled (is,
+                                off);
+    if ( (GNUNET_YES == cancelled) &&
+         (0 == (cmd->details.history.direction & TALER_BANK_DIRECTION_CANCEL)) 
)
+      continue;
     if ( ( (0 != (cmd->details.history.direction & 
TALER_BANK_DIRECTION_CREDIT)) &&
            (cmd->details.history.account_number ==
             pos->details.admin_add_incoming.credit_account_no)) ||
@@ -253,6 +319,7 @@ build_history (struct InterpreterState *is,
   for (unsigned int off = start;off != end + inc; off += inc)
   {
     const struct TBI_Command *pos = &is->commands[off];
+    int cancelled;
 
     if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc)
       continue;
@@ -268,6 +335,10 @@ build_history (struct InterpreterState *is,
       continue; /* skip until we find the marker */
     if (total >= cmd->details.history.num_results * inc)
       break; /* hit limit specified by command */
+    if (GNUNET_YES ==
+        test_deleted_by_expected (is,
+                                  off))
+      continue;
 
     if ( ( (0 != (cmd->details.history.direction & 
TALER_BANK_DIRECTION_CREDIT)) &&
            (cmd->details.history.account_number ==
@@ -280,11 +351,19 @@ build_history (struct InterpreterState *is,
       continue;
     }
 
+    cancelled = test_cancelled (is,
+                                off);
+    if ( (GNUNET_YES == cancelled) &&
+         (0 == (cmd->details.history.direction & TALER_BANK_DIRECTION_CANCEL)) 
)
+      continue;
+
     if ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) 
&&
          (cmd->details.history.account_number ==
           pos->details.admin_add_incoming.credit_account_no))
     {
       h[total].direction = TALER_BANK_DIRECTION_CREDIT;
+      if (GNUNET_YES == cancelled)
+        h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
       h[total].details.account_details
         = json_pack ("{s:s, s:s, s:I}",
                      "type",
@@ -300,6 +379,8 @@ build_history (struct InterpreterState *is,
             pos->details.admin_add_incoming.debit_account_no))
     {
       h[total].direction = TALER_BANK_DIRECTION_DEBIT;
+      if (GNUNET_YES == cancelled)
+        h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
       h[total].details.account_details
         = json_pack ("{s:s, s:s, s:I}",
                      "type",
@@ -323,17 +404,10 @@ build_history (struct InterpreterState *is,
       /* h[total].execution_date; // unknown here */
       h[total].serial_id
         = pos->details.admin_add_incoming.serial_id;
-      {
-        char *ws;
-
-        ws = GNUNET_STRINGS_data_to_string_alloc 
(&pos->details.admin_add_incoming.wtid,
-                                                  sizeof (struct 
TALER_WireTransferIdentifierRawP));
-        GNUNET_asprintf (&h[total].details.wire_transfer_subject,
-                         "%s %s",
-                         ws,
-                         pos->details.admin_add_incoming.exchange_base_url);
-        GNUNET_free (ws);
-      }
+      GNUNET_asprintf (&h[total].details.wire_transfer_subject,
+                       "%s %s",
+                       pos->details.admin_add_incoming.subject,
+                       pos->details.admin_add_incoming.exchange_base_url);
       total++;
     }
   }
@@ -489,17 +563,33 @@ interpreter_run (void *cls);
 
 
 /**
+ * Run the next command.
+ *
+ * @param is interpreter to progress
+ */
+static void
+next (struct InterpreterState *is)
+{
+  is->ip++;
+  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+                                       is);
+}
+
+
+/**
  * Function called upon completion of our /admin/add/incoming request.
  *
  * @param cls closure with the interpreter state
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
  *                    0 if the bank's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler status code
  * @param serial_id unique ID of the wire transfer in the bank's records; 
UINT64_MAX on error
  * @param json detailed response from the HTTPD, or NULL if reply was not in 
JSON
  */
 static void
 add_incoming_cb (void *cls,
                  unsigned int http_status,
+                 enum TALER_ErrorCode ec,
                  uint64_t serial_id,
                  const json_t *json)
 {
@@ -522,9 +612,7 @@ add_incoming_cb (void *cls,
     fail (is);
     return;
   }
-  is->ip++;
-  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                       is);
+  next (is);
 }
 
 
@@ -538,6 +626,7 @@ add_incoming_cb (void *cls,
  *                    #MHD_HTTP_NO_CONTENT if there are no more results; on 
success the
  *                    last callback is always of this status (even if 
`abs(num_results)` were
  *                    already returned).
+ * @param ec taler status code
  * @param dir direction of the transfer
  * @param serial_id monotonically increasing counter corresponding to the 
transaction
  * @param details details about the wire transfer
@@ -546,6 +635,7 @@ add_incoming_cb (void *cls,
 static void
 history_cb (void *cls,
             unsigned int http_status,
+            enum TALER_ErrorCode ec,
             enum TALER_BANK_Direction dir,
             uint64_t serial_id,
             const struct TALER_BANK_TransferDetails *details,
@@ -580,9 +670,7 @@ history_cb (void *cls,
       fail (is);
       return;
     }
-    is->ip++;
-    is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                         is);
+    next (is);
     return;
   }
   if (GNUNET_OK !=
@@ -613,6 +701,38 @@ history_cb (void *cls,
 
 
 /**
+ * Callbacks of this type are used to serve the result of asking
+ * the bank to reject an incoming wire transfer.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for 
successful status request;
+ *                    #MHD_HTTP_NOT_FOUND if the rowid is unknown;
+ *                    0 if the bank's reply is bogus (fails to follow the 
protocol),
+ * @param ec detailed error code
+ */
+static void
+reject_cb (void *cls,
+           unsigned int http_status,
+           enum TALER_ErrorCode ec)
+{
+  struct InterpreterState *is = cls;
+  struct TBI_Command *cmd = &is->commands[is->ip];
+
+  cmd->details.reject.rh = NULL;
+  if (MHD_HTTP_NO_CONTENT != http_status)
+  {
+    GNUNET_break (0);
+    fprintf (stderr,
+             "Unexpected response code %u:\n",
+             http_status);
+    fail (is);
+    return;
+  }
+  next (is);
+}
+
+
+/**
  * Run the main interpreter loop that performs bank operations.
  *
  * @param cls contains the `struct InterpreterState`
@@ -658,15 +778,13 @@ interpreter_run (void *cls)
       fail (is);
       return;
     }
-    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
-                                &cmd->details.admin_add_incoming.wtid,
-                                sizeof (cmd->details.admin_add_incoming.wtid));
+    GNUNET_break (NULL != cmd->details.admin_add_incoming.subject);
     cmd->details.admin_add_incoming.aih
       = TALER_BANK_admin_add_incoming (is->ctx,
                                        "http://localhost:8080";,
                                        &auth,
                                        
cmd->details.admin_add_incoming.exchange_base_url,
-                                       &cmd->details.admin_add_incoming.wtid,
+                                       cmd->details.admin_add_incoming.subject,
                                        &amount,
                                        
cmd->details.admin_add_incoming.debit_account_no,
                                        
cmd->details.admin_add_incoming.credit_account_no,
@@ -722,7 +840,6 @@ interpreter_run (void *cls)
                                            &amount));
     {
       char *subject;
-      char *expect;
 
       if (GNUNET_OK !=
           TALER_FAKEBANK_check (is->fakebank,
@@ -736,22 +853,17 @@ interpreter_run (void *cls)
         fail (is);
         return;
       }
-      expect = GNUNET_STRINGS_data_to_string_alloc 
(&ref->details.admin_add_incoming.wtid,
-                                                    sizeof 
(ref->details.admin_add_incoming.wtid));
-      if (0 != strcmp (subject, expect))
+      if (0 != strcmp (ref->details.admin_add_incoming.subject,
+                       subject))
       {
-        GNUNET_free (expect);
         GNUNET_free (subject);
         GNUNET_break (0);
         fail (is);
         return;
       }
       GNUNET_free (subject);
-      GNUNET_free (expect);
     }
-    is->ip++;
-    is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                         is);
+    next (is);
    return;
   case TBI_OC_EXPECT_TRANSFERS_EMPTY:
     if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank))
@@ -760,9 +872,27 @@ interpreter_run (void *cls)
       fail (is);
       return;
     }
-    is->ip++;
-    is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                         is);
+    next (is);
+    return;
+  case TBI_OC_REJECT:
+    ref = find_command (is,
+                        cmd->details.reject.cmd_ref);
+    GNUNET_assert (NULL != ref);
+    GNUNET_assert (TBI_OC_ADMIN_ADD_INCOMING == ref->oc);
+    cmd->details.reject.rh
+      = TALER_BANK_reject (is->ctx,
+                           "http://localhost:8080";,
+                           &auth,
+                           ref->details.admin_add_incoming.credit_account_no,
+                           ref->details.admin_add_incoming.serial_id,
+                           &reject_cb,
+                           is);
+    if (NULL == cmd->details.reject.rh)
+    {
+      GNUNET_break (0);
+      fail (is);
+      return;
+    }
     return;
   default:
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -802,7 +932,6 @@ do_shutdown (void *cls)
 {
   struct InterpreterState *is = cls;
   struct TBI_Command *cmd;
-  unsigned int i;
 
   if (NULL != is->timeout_task)
   {
@@ -810,7 +939,7 @@ do_shutdown (void *cls)
     is->timeout_task = NULL;
   }
 
-  for (i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
+  for (unsigned int i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
   {
     switch (cmd->oc)
     {
@@ -843,6 +972,17 @@ do_shutdown (void *cls)
       break;
     case TBI_OC_EXPECT_TRANSFERS_EMPTY:
       break;
+    case TBI_OC_REJECT:
+      if (NULL != cmd->details.reject.rh)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+        TALER_BANK_reject_cancel (cmd->details.reject.rh);
+        cmd->details.reject.rh = NULL;
+      }
+      break;
     default:
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Unknown instruction %d at %u (%s)\n",
diff --git a/src/bank-lib/test_bank_interpreter.h 
b/src/bank-lib/test_bank_interpreter.h
index d4e9c1a..ed2a00d 100644
--- a/src/bank-lib/test_bank_interpreter.h
+++ b/src/bank-lib/test_bank_interpreter.h
@@ -57,7 +57,12 @@ enum TBI_OpCode
   /**
    * Expect that we have exhaustively gone over all transfers at fakebank.
    */
-  TBI_OC_EXPECT_TRANSFERS_EMPTY
+  TBI_OC_EXPECT_TRANSFERS_EMPTY,
+
+  /**
+   * Reject incoming transfer.
+   */
+  TBI_OC_REJECT
 
 };
 
@@ -110,10 +115,9 @@ struct TBI_Command
       const char *exchange_base_url;
 
       /**
-       * Wire transfer identifier to use.  Initialized to
-       * a random value.
+       * Wire transfer subject to use.
        */
-      struct TALER_WireTransferIdentifierRawP wtid;
+      const char *subject;
 
       /**
        * Which response code do we expect for this command?
@@ -186,6 +190,23 @@ struct TBI_Command
 
     } expect_transfer;
 
+    /**
+     * Execute /reject operation.
+     */
+    struct {
+
+      /**
+       * Reference to the matching transfer that is now to be rejected.
+       */
+      const char *cmd_ref;
+
+      /**
+       * Set to the API's handle during the operation.
+       */
+      struct TALER_BANK_RejectHandle *rh;
+
+    } reject;
+
   } details;
 
 };
diff --git a/src/exchange/taler-exchange-wirewatch.c 
b/src/exchange/taler-exchange-wirewatch.c
index ca7f3ba..7fbc0b8 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -282,6 +282,7 @@ reject_cb (void *cls,
  * the bank for the transaction history.
  *
  * @param cls closure with the `struct TALER_EXCHANGEDB_Session *`
+ * @param ec taler error code
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -290,6 +291,7 @@ reject_cb (void *cls,
  */
 static int
 history_cb (void *cls,
+            enum TALER_ErrorCode ec,
            enum TALER_BANK_Direction dir,
            const void *row_off,
            size_t row_off_size,
@@ -303,6 +305,12 @@ history_cb (void *cls,
   {
     hh = NULL;
 
+    if (TALER_EC_NONE != ec)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Error fetching history: %u!\n",
+                  (unsigned int) ec);
+    }
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "End of list. Committing progress!\n");
     qs = db_plugin->commit (db_plugin->cls,
diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h
index 246174d..6349326 100644
--- a/src/include/taler_bank_service.h
+++ b/src/include/taler_bank_service.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2015, 2016, 2017 GNUnet e.V. & Inria
+  Copyright (C) 2015, 2016, 2017 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -25,6 +25,7 @@
 #include <jansson.h>
 #include <gnunet/gnunet_curl_lib.h>
 #include "taler_util.h"
+#include "taler_error_codes.h"
 
 
 /**
@@ -98,12 +99,14 @@ struct TALER_BANK_AdminAddIncomingHandle;
  * @param cls closure
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
  *                    0 if the bank's reply is bogus (fails to follow the 
protocol)
+ * @param ec detailed error code
  * @param serial_id unique ID of the wire transfer in the bank's records; 
UINT64_MAX on error
  * @param json detailed response from the HTTPD, or NULL if reply was not in 
JSON
  */
 typedef void
 (*TALER_BANK_AdminAddIncomingResultCallback) (void *cls,
                                               unsigned int http_status,
+                                              enum TALER_ErrorCode ec,
                                               uint64_t serial_id,
                                               const json_t *json);
 
@@ -118,7 +121,7 @@ typedef void
  * @param bank_base_url URL of the bank (used to execute this request)
  * @param auth authentication data to use
  * @param exchange_base_url base URL of the exchange (for tracking)
- * @param wtid wire transfer identifier for the transfer
+ * @param subject wire transfer subject for the transfer
  * @param amount amount that was deposited
  * @param debit_account_no account number to withdraw from (53 bits at most)
  * @param credit_account_no account number to deposit into (53 bits at most)
@@ -133,7 +136,7 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context 
*ctx,
                                const char *bank_base_url,
                                const struct TALER_BANK_AuthenticationData 
*auth,
                                const char *exchange_base_url,
-                               const struct TALER_WireTransferIdentifierRawP 
*wtid,
+                               const char *subject,
                                const struct TALER_Amount *amount,
                                uint64_t debit_account_no,
                                uint64_t credit_account_no,
@@ -174,7 +177,15 @@ enum TALER_BANK_Direction {
   /**
    * Return both types of transactions.
    */
-  TALER_BANK_DIRECTION_BOTH = (TALER_BANK_DIRECTION_CREDIT | 
TALER_BANK_DIRECTION_DEBIT)
+  TALER_BANK_DIRECTION_BOTH = (TALER_BANK_DIRECTION_CREDIT | 
TALER_BANK_DIRECTION_DEBIT),
+
+  /**
+   * Bit mask that is applied to view transactions that have been
+   * cancelled. The bit is set for cancelled transactions that are
+   * returned from /history, and must also be set in order for
+   * cancelled transactions to show up in the /history.
+   */
+  TALER_BANK_DIRECTION_CANCEL = 4
 
 };
 
@@ -222,6 +233,7 @@ struct TALER_BANK_TransferDetails
  *                    #MHD_HTTP_NO_CONTENT if there are no more results; on 
success the
  *                    last callback is always of this status (even if 
`abs(num_results)` were
  *                    already returned).
+ * @param ec detailed error code
  * @param dir direction of the transfer
  * @param serial_id monotonically increasing counter corresponding to the 
transaction
  * @param details details about the wire transfer
@@ -230,6 +242,7 @@ struct TALER_BANK_TransferDetails
 typedef void
 (*TALER_BANK_HistoryResultCallback) (void *cls,
                                      unsigned int http_status,
+                                     enum TALER_ErrorCode ec,
                                      enum TALER_BANK_Direction dir,
                                      uint64_t serial_id,
                                      const struct TALER_BANK_TransferDetails 
*details,
@@ -277,5 +290,61 @@ void
 TALER_BANK_history_cancel (struct TALER_BANK_HistoryHandle *hh);
 
 
+/**
+ * Handle for #TALER_BANK_reject() operation.
+ */
+struct TALER_BANK_RejectHandle;
+
+
+/**
+ * Callbacks of this type are used to serve the result of asking
+ * the bank to reject an incoming wire transfer.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for 
successful status request;
+ *                    #MHD_HTTP_NOT_FOUND if the rowid is unknown;
+ *                    0 if the bank's reply is bogus (fails to follow the 
protocol),
+ * @param ec detailed error code
+ */
+typedef void
+(*TALER_BANK_RejectResultCallback) (void *cls,
+                                    unsigned int http_status,
+                                    enum TALER_ErrorCode ec);
+
+
+/**
+ * Request rejection of a wire transfer, marking it as cancelled and voiding
+ * its effects.
+ *
+ * @param ctx curl context for the event loop
+ * @param bank_base_url URL of the bank (used to execute this request)
+ * @param auth authentication data to use
+ * @param account_number which account number should we query
+ * @param rowid transfer to reject
+ * @param rcb the callback to call with the operation result
+ * @param rcb_cls closure for @a rcb
+ * @return NULL
+ *         if the inputs are invalid.
+ *         In this case, the callback is not called.
+ */
+struct TALER_BANK_RejectHandle *
+TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
+                   const char *bank_base_url,
+                   const struct TALER_BANK_AuthenticationData *auth,
+                   uint64_t account_number,
+                   uint64_t rowid,
+                   TALER_BANK_RejectResultCallback rcb,
+                   void *rcb_cls);
+
+
+/**
+ * Cancel an reject request.  This function cannot be used on a request
+ * handle if the response was is already served for it.
+ *
+ * @param rh the reject request handle
+ */
+void
+TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh);
+
 
 #endif  /* _TALER_BANK_SERVICE_H */
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index 6047e16..e8a3aae 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -1488,6 +1488,34 @@ enum TALER_ErrorCode
    */
   TALER_EC_TEST_RSA_SIGN_ERROR = 4005,
 
+  /* *************** Taler BANK/FAKEBANK error codes *************** */
+
+
+  /**
+   * Authentication failed for the /admin/add/incoming request.
+   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   */
+  TALER_EC_BANK_TRANSFER_NOT_AUHTORIZED = 4101,
+
+  /**
+   * Authentication failed for the /history request.
+   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   */
+  TALER_EC_BANK_HISTORY_NOT_AUHTORIZED = 4151,
+
+  /**
+   * The bank could not find the wire transfer that was supposed to
+   * be rejected.
+   * Returned with a status code of MHD_HTTP_NOT_FOUND.
+   */
+  TALER_EC_BANK_REJECT_NOT_FOUND = 4250,
+
+  /**
+   * Authentication failed for the /reject request.
+   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   */
+  TALER_EC_BANK_REJECT_NOT_AUHTORIZED = 4251,
+
 
   /**
    * End of error code range.
diff --git a/src/include/taler_fakebank_lib.h b/src/include/taler_fakebank_lib.h
index 3df1e60..5e0d7d1 100644
--- a/src/include/taler_fakebank_lib.h
+++ b/src/include/taler_fakebank_lib.h
@@ -88,6 +88,8 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
  * to the transfer identifier and remove the transaction from the
  * list.  If the transaction was not recorded, return #GNUNET_SYSERR.
  *
+ * Rejected transfers do NOT show with "check".
+ *
  * @param h bank instance
  * @param want_amount transfer amount desired
  * @param want_debit account that should have been debited
@@ -107,6 +109,21 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
 
 
 /**
+ * Reject incoming wire transfer to account @a credit_account
+ * as identified by @a rowid.
+ *
+ * @param h fake bank handle
+ * @param rowid identifies transfer to reject
+ * @param credit_account account number of owner of credited account
+ * @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not 
found
+ */
+int
+TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
+                                uint64_t rowid,
+                                uint64_t credit_account);
+
+
+/**
  * Stop running the fake bank.
  *
  * @param h bank to stop
diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h
index 6e355ba..c0e2960 100644
--- a/src/include/taler_wire_plugin.h
+++ b/src/include/taler_wire_plugin.h
@@ -83,6 +83,7 @@ struct TALER_WIRE_TransferDetails
  * the bank for the transaction history.
  *
  * @param cls closure
+ * @param ec taler error code
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -91,6 +92,7 @@ struct TALER_WIRE_TransferDetails
  */
 typedef int
 (*TALER_WIRE_HistoryResultCallback) (void *cls,
+                                     enum TALER_ErrorCode ec,
                                      enum TALER_BANK_Direction dir,
                                      const void *row_off,
                                      size_t row_off_size,
diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c
index 1a7443b..fa6ba3d 100644
--- a/src/wire/plugin_wire_test.c
+++ b/src/wire/plugin_wire_test.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2016 GNUnet e.V. & Inria
+  Copyright (C) 2017 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
@@ -553,12 +553,14 @@ test_prepare_wire_transfer (void *cls,
  * @param cls closure with the `struct TALER_WIRE_ExecuteHandle`
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
  *                    0 if the bank's reply is bogus (fails to follow the 
protocol)
+ * @param ec error code from the bank
  * @param serial_id unique ID of the wire transfer in the bank's records; 
UINT64_MAX on error
  * @param json detailed response from the HTTPD, or NULL if reply was not JSON
  */
 static void
 execute_cb (void *cls,
             unsigned int http_status,
+            enum TALER_ErrorCode ec,
             uint64_t serial_id,
             const json_t *json)
 {
@@ -578,13 +580,15 @@ execute_cb (void *cls,
   }
   if (NULL != emsg)
     GNUNET_asprintf (&s,
-                     "%u (%s)",
+                     "%u/%u (%s)",
                      http_status,
+                     (unsigned int) ec,
                      emsg);
   else
     GNUNET_asprintf (&s,
-                     "%u",
-                     http_status);
+                     "%u/%u",
+                     http_status,
+                     (unsigned int) ec);
   eh->cc (eh->cc_cls,
           (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR,
           serial_id,
@@ -676,6 +680,7 @@ test_execute_wire_transfer (void *cls,
   char *emsg;
   const char *json_s;
   const char *exchange_base_url;
+  char *wire_s;
 
   if (NULL == tc->ctx)
   {
@@ -728,16 +733,19 @@ test_execute_wire_transfer (void *cls,
   eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle);
   eh->cc = cc;
   eh->cc_cls = cc_cls;
+  wire_s = GNUNET_STRINGS_data_to_string_alloc (&bf.wtid,
+                                                sizeof (bf.wtid));
   eh->aaih = TALER_BANK_admin_add_incoming (tc->ctx,
                                             tc->bank_uri,
                                             &tc->auth,
                                             exchange_base_url,
-                                            &bf.wtid,
+                                            wire_s,
                                             &amount,
                                             (uint64_t) tc->exchange_account_no,
                                            (uint64_t) account_no,
                                             &execute_cb,
                                             eh);
+  GNUNET_free (wire_s);
   json_decref (wire);
   if (NULL == eh->aaih)
   {
@@ -803,6 +811,7 @@ struct TALER_WIRE_HistoryHandle
  *                    #MHD_HTTP_NO_CONTENT if there are no more results; on 
success the
  *                    last callback is always of this status (even if 
`abs(num_results)` were
  *                    already returned).
+ * @param ec taler error code
  * @param dir direction of the transfer
  * @param serial_id monotonically increasing counter corresponding to the 
transaction
  * @param details details about the wire transfer
@@ -811,6 +820,7 @@ struct TALER_WIRE_HistoryHandle
 static void
 bhist_cb (void *cls,
           unsigned int http_status,
+          enum TALER_ErrorCode ec,
           enum TALER_BANK_Direction dir,
           uint64_t serial_id,
           const struct TALER_BANK_TransferDetails *details,
@@ -852,12 +862,17 @@ bhist_cb (void *cls,
                 sizeof (wd.wtid));
         wd.wtid_s = details->wire_transfer_subject;
       }
+      else
+      {
+        wd.wtid_s = NULL;
+      }
       GNUNET_free (subject);
       wd.account_details = details->account_details;
 
       if ( (NULL != whh->hres_cb) &&
            (GNUNET_OK !=
             whh->hres_cb (whh->hres_cb_cls,
+                          TALER_EC_NONE,
                           dir,
                           &bserial_id,
                           sizeof (bserial_id),
@@ -868,6 +883,7 @@ bhist_cb (void *cls,
   case MHD_HTTP_NO_CONTENT:
     if (NULL != whh->hres_cb)
       (void) whh->hres_cb (whh->hres_cb_cls,
+                           ec,
                            TALER_BANK_DIRECTION_NONE,
                            NULL,
                            0,
@@ -880,6 +896,7 @@ bhist_cb (void *cls,
     GNUNET_break (0);
     if (NULL != whh->hres_cb)
       (void) whh->hres_cb (whh->hres_cb_cls,
+                           ec,
                            TALER_BANK_DIRECTION_NONE,
                            NULL,
                            0,
@@ -1004,26 +1021,32 @@ struct TALER_WIRE_RejectHandle
   void *rej_cb_cls;
 
   /**
-   * Handle to task for timeout of operation.
+   * Handle for the reject operation.
    */
-  struct GNUNET_SCHEDULER_Task *timeout_task;
+  struct TALER_BANK_RejectHandle *brh;
 };
 
 
 /**
- * Rejection operation failed with timeout, notify callback
- * and clean up.
+ * Callbacks of this type are used to serve the result of asking
+ * the bank to reject an incoming wire transfer.
  *
- * @param cls closure with `struct TALER_WIRE_RejectHandle`
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for 
successful status request;
+ *                    #MHD_HTTP_NOT_FOUND if the rowid is unknown;
+ *                    0 if the bank's reply is bogus (fails to follow the 
protocol),
+ * @param ec detailed error code
  */
 static void
-timeout_reject (void *cls)
+reject_cb (void *cls,
+           unsigned int http_status,
+           enum TALER_ErrorCode ec)
 {
   struct TALER_WIRE_RejectHandle *rh = cls;
 
-  rh->timeout_task = NULL;
+  rh->brh = NULL;
   rh->rej_cb (rh->rej_cb_cls,
-              TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
+              ec);
   GNUNET_free (rh);
 }
 
@@ -1052,14 +1075,30 @@ test_reject_transfer (void *cls,
                       TALER_WIRE_RejectTransferCallback rej_cb,
                       void *rej_cb_cls)
 {
+  struct TestClosure *tc = cls;
+  const uint64_t *rowid_b64 = start_off;
   struct TALER_WIRE_RejectHandle *rh;
 
-  GNUNET_break (0); /* not implemented, just a stub! */
+  if (sizeof (uint64_t) != start_off_len)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
   rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
   rh->rej_cb = rej_cb;
   rh->rej_cb_cls = rej_cb_cls;
-  rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
-                                               rh);
+  rh->brh = TALER_BANK_reject (tc->ctx,
+                               tc->bank_uri,
+                               &tc->auth,
+                               (uint64_t) tc->exchange_account_no,
+                               GNUNET_ntohll (*rowid_b64),
+                               &reject_cb,
+                               rh);
+  if (NULL == rh->brh)
+  {
+    GNUNET_free (rh);
+    return NULL;
+  }
   return rh;
 }
 
@@ -1082,7 +1121,8 @@ test_reject_transfer_cancel (void *cls,
 {
   void *ret = rh->rej_cb_cls;
 
-  GNUNET_SCHEDULER_cancel (rh->timeout_task);
+  if (NULL != rh->brh)
+    TALER_BANK_reject_cancel (rh->brh);
   GNUNET_free (rh);
   return ret;
 }
diff --git a/src/wire/test_wire_plugin_transactions_test.c 
b/src/wire/test_wire_plugin_transactions_test.c
index 26331b5..a020f13 100644
--- a/src/wire/test_wire_plugin_transactions_test.c
+++ b/src/wire/test_wire_plugin_transactions_test.c
@@ -159,6 +159,7 @@ timeout_cb (void *cls)
  * the bank for the transaction history.
  *
  * @param cls closure
+ * @param ec taler status code
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -167,6 +168,7 @@ timeout_cb (void *cls)
  */
 static int
 history_result_cb (void *cls,
+                   enum TALER_ErrorCode ec,
                    enum TALER_BANK_Direction dir,
                    const void *row_off,
                    size_t row_off_size,

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]