/*
  This file is part of TALER
  Copyright (C) 2020 Taler Systems SA

  TALER is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as
  published by the Free Software Foundation; either version 3, or
  (at your option) any later version.

  TALER is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public
  License along with TALER; see the file COPYING.  If not, see
  <http://www.gnu.org/licenses/>
*/
/**
 * @file testing_api_cmd_wallet_get_order.c
 * @brief command to test GET /order/$ORDER_ID
 * @author Jonathan Buchanan
 */
#include "platform.h"
#include <taler/taler_exchange_service.h>
#include <taler/taler_testing_lib.h>
#include "taler_merchant_service.h"
#include "taler_merchant_testing_lib.h"


/**
 * State for a GET /orders/$ORDER_ID CMD.
 */
struct WalletGetOrderState
{
  /**
   * The merchant base URL.
   */
  const char *merchant_url;

  /**
   * Expected HTTP response code for this CMD.
   */
  unsigned int http_status;

  /**
   * The handle to the current GET /orders/$ORDER_ID request.
   */
  struct TALER_MERCHANT_OrderWalletGetHandle *ogh;

  /**
   * The interpreter state.
   */
  struct TALER_TESTING_Interpreter *is;

  /**
   * Reference to a command that created an order.
   */
  const char *order_reference;

  /**
   * Reference to a command that created a paid
   * equivalent order that we expect to be referred
   * to during repurchase detection, or NULL.
   */
  const char *repurchase_order_ref;

  /**
   * Session Id the order needs to be bound to.
   */
  const char *session_id;

  /**
   * Whether the order was paid or not.
   */
  bool paid;

  /**
   * Whether the order was refunded or not.
   */
  bool refunded;

  /**
   * Whether the order has refunds pending.
   */
  bool refund_pending;
};


/**
 * Callback to process a GET /orders/$ID request
 *
 * @param cls closure
 * @param owgr response details
 */
static void
wallet_get_order_cb (
  void *cls,
  const struct TALER_MERCHANT_OrderWalletGetResponse *owgr)
{
  struct WalletGetOrderState *gos = cls;
  const struct TALER_MERCHANT_HttpResponse *hr = &owgr->hr;

  gos->ogh = NULL;
  if (gos->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 (gos->is));
    TALER_TESTING_interpreter_fail (gos->is);
    return;
  }
  switch (hr->http_status)
  {
  case MHD_HTTP_OK:
    if (gos->refunded != owgr->details.ok.refunded)
    {
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Order refunded does not match\n");
      TALER_TESTING_interpreter_fail (gos->is);
      return;
    }
    if (gos->refund_pending != owgr->details.ok.refund_pending)
    {
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Order refund pending does not match\n");
      TALER_TESTING_interpreter_fail (gos->is);
      return;
    }
    break;
  case MHD_HTTP_PAYMENT_REQUIRED:
    {
      struct TALER_MERCHANT_PayUriData pud;
      const struct TALER_TESTING_Command *order_cmd;
      const char *order_id;
      const struct TALER_ClaimTokenP *claim_token;

      if (NULL != gos->repurchase_order_ref)
      {
        const struct TALER_TESTING_Command *rep_cmd;
        const char *rep_id;
        const char *ri;

        rep_cmd = TALER_TESTING_interpreter_lookup_command (
          gos->is,
          gos->repurchase_order_ref);
        if (GNUNET_OK !=
            TALER_TESTING_get_trait_order_id (rep_cmd,
                                              &rep_id))
        {
          TALER_TESTING_FAIL (gos->is);
        }
        ri = owgr->details.payment_required.already_paid_order_id;
        if ( (NULL == ri) ||
             (0 !=
              strcmp (ri,
                      rep_id)) )
        {
          TALER_TESTING_FAIL (gos->is);
        }
      }

      if (GNUNET_OK !=
          TALER_MERCHANT_parse_pay_uri (
            owgr->details.payment_required.taler_pay_uri,
            &pud))
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Taler pay uri `%s' is malformed\n",
                    owgr->details.payment_required.taler_pay_uri);
        TALER_TESTING_interpreter_fail (gos->is);
        return;
      }

      order_cmd = TALER_TESTING_interpreter_lookup_command (
        gos->is,
        gos->order_reference);

      if (GNUNET_OK !=
          TALER_TESTING_get_trait_order_id (order_cmd,
                                            &order_id))
      {
        TALER_MERCHANT_parse_pay_uri_free (&pud);
        TALER_TESTING_FAIL (gos->is);
      }

      if (GNUNET_OK !=
          TALER_TESTING_get_trait_claim_token (order_cmd,
                                               &claim_token))
      {
        TALER_MERCHANT_parse_pay_uri_free (&pud);
        TALER_TESTING_FAIL (gos->is);
      }

      {
        char *host;

        host = TALER_MERCHANT_TESTING_extract_host (gos->merchant_url);
        if ((0 != strcmp (host,
                          pud.merchant_host)) ||
            (NULL != pud.merchant_prefix_path) ||
            (0 != strcmp (order_id,
                          pud.order_id)) ||
            (NULL != pud.ssid))
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Order pay uri `%s' does not match `%s'\n",
                      owgr->details.payment_required.taler_pay_uri,
                      pud.order_id);
          TALER_TESTING_interpreter_fail (gos->is);
          TALER_MERCHANT_parse_pay_uri_free (&pud);
          GNUNET_free (host);
          return;
        }
        GNUNET_free (host);
      }
      /* The claim token is not given in the pay uri if the order
         has been claimed already. */
      if ((NULL != pud.claim_token) &&
          ((NULL == claim_token) ||
           (0 != GNUNET_memcmp (claim_token,
                                pud.claim_token))))
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Order pay uri claim token does not match (%d/%d)\n",
                    NULL == pud.claim_token,
                    NULL == claim_token);
        TALER_TESTING_interpreter_fail (gos->is);
        TALER_MERCHANT_parse_pay_uri_free (&pud);
        return;
      }
      TALER_MERCHANT_parse_pay_uri_free (&pud);
    }
    break;
  default:
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Unhandled HTTP status.\n");
  }
  TALER_TESTING_interpreter_next (gos->is);
}


/**
 * Run the "GET order" CMD.
 *
 * @param cls closure.
 * @param cmd command being run now.
 * @param is interpreter state.
 */
static void
wallet_get_order_run (void *cls,
                      const struct TALER_TESTING_Command *cmd,
                      struct TALER_TESTING_Interpreter *is)
{
  struct WalletGetOrderState *gos = cls;
  const struct TALER_TESTING_Command *order_cmd;
  const char *order_id;
  const struct TALER_PrivateContractHashP *h_contract;

  order_cmd = TALER_TESTING_interpreter_lookup_command (
    is,
    gos->order_reference);

  if (GNUNET_OK !=
      TALER_TESTING_get_trait_order_id (order_cmd,
                                        &order_id))
    TALER_TESTING_FAIL (is);

  if (GNUNET_OK !=
      TALER_TESTING_get_trait_h_contract_terms (order_cmd,
                                                &h_contract))
    TALER_TESTING_FAIL (is);

  gos->is = is;
  gos->ogh = TALER_MERCHANT_wallet_order_get (
    TALER_TESTING_interpreter_get_context (is),
    gos->merchant_url,
    order_id,
    h_contract,
    GNUNET_TIME_UNIT_ZERO,
    gos->session_id,
    NULL,
    false,
    &wallet_get_order_cb,
    gos);
}


/**
 * Free the state of a "GET order" CMD, and possibly
 * cancel a pending operation thereof.
 *
 * @param cls closure.
 * @param cmd command being run.
 */
static void
wallet_get_order_cleanup (void *cls,
                          const struct TALER_TESTING_Command *cmd)
{
  struct WalletGetOrderState *gos = cls;

  if (NULL != gos->ogh)
  {
    TALER_LOG_WARNING ("Get order operation did not complete\n");
    TALER_MERCHANT_wallet_order_get_cancel (gos->ogh);
  }
  GNUNET_free (gos);
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_wallet_get_order (
  const char *label,
  const char *merchant_url,
  const char *order_reference,
  bool paid,
  bool refunded,
  bool refund_pending,
  unsigned int http_status)
{
  struct WalletGetOrderState *gos;

  gos = GNUNET_new (struct WalletGetOrderState);
  gos->merchant_url = merchant_url;
  gos->order_reference = order_reference;
  gos->http_status = http_status;
  gos->paid = paid;
  gos->refunded = refunded;
  gos->refund_pending = refund_pending;
  {
    struct TALER_TESTING_Command cmd = {
      .cls = gos,
      .label = label,
      .run = &wallet_get_order_run,
      .cleanup = &wallet_get_order_cleanup
    };

    return cmd;
  }
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_wallet_get_order2 (
  const char *label,
  const char *merchant_url,
  const char *order_reference,
  const char *session_id,
  bool paid,
  bool refunded,
  bool refund_pending,
  const char *repurchase_order_ref,
  unsigned int http_status)
{
  struct WalletGetOrderState *gos;

  gos = GNUNET_new (struct WalletGetOrderState);
  gos->merchant_url = merchant_url;
  gos->order_reference = order_reference;
  gos->http_status = http_status;
  gos->paid = paid;
  gos->session_id = session_id;
  gos->refunded = refunded;
  gos->refund_pending = refund_pending;
  gos->repurchase_order_ref = repurchase_order_ref;
  {
    struct TALER_TESTING_Command cmd = {
      .cls = gos,
      .label = label,
      .run = &wallet_get_order_run,
      .cleanup = &wallet_get_order_cleanup
    };

    return cmd;
  }
}


struct WalletPollOrderConcludeState
{
  /**
   * The interpreter state.
   */
  struct TALER_TESTING_Interpreter *is;

  /**
   * Reference to a command that can provide a poll order start command.
   */
  const char *start_reference;

  /**
   * Already paid order ID expected, or NULL for none.
   */
  const char *already_paid_order_id;

  /**
   * Task to wait for the deadline.
   */
  struct GNUNET_SCHEDULER_Task *task;

  /**
   * Amount of a refund expected.
   */
  struct TALER_Amount expected_refund_amount;

  /**
   * Expected HTTP response status code.
   */
  unsigned int expected_http_status;

  /**
   * Are we expecting a refund?
   */
  bool expected_refund;
};


struct WalletPollOrderStartState
{
  /**
   * The merchant base URL.
   */
  const char *merchant_url;

  /**
   * The handle to the current GET /orders/$ORDER_ID request.
   */
  struct TALER_MERCHANT_OrderWalletGetHandle *ogh;

  /**
   * The interpreter state.
   */
  struct TALER_TESTING_Interpreter *is;

  /**
   * Reference to a command that created an order.
   */
  const char *order_ref;

  /**
   * Which session ID to poll for.
   */
  const char *session_id;

  /**
   * How long to wait for server to return a response.
   */
  struct GNUNET_TIME_Relative timeout;

  /**
   * Conclude state waiting for completion (if any).
   */
  struct WalletPollOrderConcludeState *cs;

  /**
   * The HTTP status code returned by the backend.
   */
  unsigned int http_status;

  /**
   * When the request should be completed by.
   */
  struct GNUNET_TIME_Absolute deadline;

  /**
   * Minimum refund to wait for.
   */
  struct TALER_Amount refund_threshold;

  /**
   * Available refund as returned by the merchant.
   */
  struct TALER_Amount refund_available;

  /**
   * Already paid order ID returned, or NULL for none.
   */
  char *already_paid_order_id;

  /**
   * Should we poll for a refund?
   */
  bool wait_for_refund;

  /**
   * Did we receive a refund according to response from the merchant?
   */
  bool refunded;

  /**
   * Was the order paid according to response from the merchant?
   */
  bool paid;

  /**
   * Has the order a pending refund according to response from the merchant?
   */
  bool refund_pending;
};


/**
 * Task called when either the timeout for the GET /private/order/$ID command
 * expired or we got a response.  Checks if the result is what we expected.
 *
 * @param cls a `struct WalletPollOrderConcludeState`
 */
static void
conclude_task (void *cls)
{
  struct WalletPollOrderConcludeState *ppc = cls;
  const struct TALER_TESTING_Command *poll_cmd;
  struct WalletPollOrderStartState *cps;
  struct GNUNET_TIME_Absolute now;

  ppc->task = NULL;
  poll_cmd =
    TALER_TESTING_interpreter_lookup_command (ppc->is,
                                              ppc->start_reference);
  if (NULL == poll_cmd)
    TALER_TESTING_FAIL (ppc->is);
  cps = poll_cmd->cls;
  if (NULL != cps->ogh)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Expected poll GET /orders/$ORDER_ID to have completed, but it did not!\n");
    TALER_TESTING_FAIL (ppc->is);
  }
  if (cps->http_status != ppc->expected_http_status)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Expected HTTP status %u, got %u\n",
                ppc->expected_http_status,
                cps->http_status);
    TALER_TESTING_FAIL (ppc->is);
  }
  if (ppc->expected_refund != cps->refunded)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Order was %srefunded, contrary to our expectations\n",
                cps->refunded ? "" : "NOT ");
    TALER_TESTING_FAIL (ppc->is);
  }
  if ( (NULL == ppc->already_paid_order_id)
       ^ (NULL == cps->already_paid_order_id) )
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Mismatch in already paid order IDs: %s vs %s\n",
                ppc->already_paid_order_id,
                cps->already_paid_order_id);
    TALER_TESTING_FAIL (ppc->is);
  }
  if ( (NULL != ppc->already_paid_order_id) &&
       (0 != strcmp (ppc->already_paid_order_id,
                     cps->already_paid_order_id) ) )
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Mismatch in already paid order IDs: %s vs %s\n",
                ppc->already_paid_order_id,
                cps->already_paid_order_id);
    TALER_TESTING_FAIL (ppc->is);
  }

  if (cps->refunded)
  {
    if (0 != TALER_amount_cmp (&ppc->expected_refund_amount,
                               &cps->refund_available))
    {
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Refund amount %s does not match our expectation!\n",
                  TALER_amount2s (&cps->refund_available));
      TALER_TESTING_FAIL (ppc->is);
    }
  }
  // FIXME: add checks for cps->paid/refund_available status flags?
  now = GNUNET_TIME_absolute_get ();
  if ((GNUNET_TIME_absolute_add (cps->deadline,
                                 GNUNET_TIME_UNIT_SECONDS).abs_value_us <
       now.abs_value_us) )
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Expected answer to be delayed until %llu, but got response at %llu\n",
                (unsigned long long) cps->deadline.abs_value_us,
                (unsigned long long) now.abs_value_us);
    TALER_TESTING_FAIL (ppc->is);
  }
  TALER_TESTING_interpreter_next (ppc->is);
}


/**
 * Process response from a GET /orders/$ID request
 *
 * @param cls a `struct WalletPollOrderStartState *`
 * @param owgr response details
 */
static void
wallet_poll_order_cb (
  void *cls,
  const struct TALER_MERCHANT_OrderWalletGetResponse *owgr)
{
  struct WalletPollOrderStartState *pos = cls;
  const struct TALER_MERCHANT_HttpResponse *hr = &owgr->hr;

  pos->ogh = NULL;
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "GET /orders/$ID finished with status %u.\n",
              hr->http_status);
  pos->http_status = hr->http_status;
  switch (hr->http_status)
  {
  case MHD_HTTP_OK:
    pos->paid = true;
    pos->refunded = owgr->details.ok.refunded;
    pos->refund_pending = owgr->details.ok.refund_pending;
    if (owgr->details.ok.refunded)
      pos->refund_available = owgr->details.ok.refund_amount;
    break;
  case MHD_HTTP_PAYMENT_REQUIRED:
    if (NULL != owgr->details.payment_required.already_paid_order_id)
      pos->already_paid_order_id = GNUNET_strdup (
        owgr->details.payment_required.already_paid_order_id);
    break;
  default:
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Unhandled HTTP status.\n");
    break;
  }
  if ( (NULL != pos->cs) &&
       (NULL != pos->cs->task) )
  {
    GNUNET_SCHEDULER_cancel (pos->cs->task);
    pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
                                              pos->cs);
  }
}


/**
 * Run the "GET order" CMD.
 *
 * @param cls closure.
 * @param cmd command being run now.
 * @param is interpreter state.
 */
static void
wallet_poll_order_start_run (void *cls,
                             const struct TALER_TESTING_Command *cmd,
                             struct TALER_TESTING_Interpreter *is)
{
  struct WalletPollOrderStartState *pos = cls;
  const struct TALER_TESTING_Command *order_cmd;
  const char *order_id;
  const struct TALER_PrivateContractHashP *h_contract;

  order_cmd = TALER_TESTING_interpreter_lookup_command (
    is,
    pos->order_ref);

  if (GNUNET_OK !=
      TALER_TESTING_get_trait_order_id (order_cmd,
                                        &order_id))
    TALER_TESTING_FAIL (is);

  if (GNUNET_OK !=
      TALER_TESTING_get_trait_h_contract_terms (order_cmd,
                                                &h_contract))
    TALER_TESTING_FAIL (is);

  /* add 1s grace time to timeout */
  pos->deadline
    = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout),
                                GNUNET_TIME_UNIT_SECONDS);
  pos->is = is;
  pos->ogh = TALER_MERCHANT_wallet_order_get (
    TALER_TESTING_interpreter_get_context (is),
    pos->merchant_url,
    order_id,
    h_contract,
    pos->timeout,
    pos->session_id,
    pos->wait_for_refund
    ? &pos->refund_threshold
    : NULL,
    false,                                           /* await_refund_obtained */
    &wallet_poll_order_cb,
    pos);
  GNUNET_assert (NULL != pos->ogh);
  /* We CONTINUE to run the interpreter while the long-polled command
     completes asynchronously! */
  TALER_TESTING_interpreter_next (pos->is);
}


/**
 * Free the state of a "GET order" CMD, and possibly
 * cancel a pending operation thereof.
 *
 * @param cls closure.
 * @param cmd command being run.
 */
static void
wallet_poll_order_start_cleanup (void *cls,
                                 const struct TALER_TESTING_Command *cmd)
{
  struct WalletPollOrderStartState *pos = cls;

  if (NULL != pos->ogh)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                "Command `%s' was not terminated\n",
                TALER_TESTING_interpreter_get_current_label (
                  pos->is));
    TALER_MERCHANT_wallet_order_get_cancel (pos->ogh);
  }
  GNUNET_free (pos->already_paid_order_id);
  GNUNET_free (pos);
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_wallet_poll_order_start (
  const char *label,
  const char *merchant_url,
  const char *order_ref,
  struct GNUNET_TIME_Relative timeout,
  const char *await_refund)
{
  struct WalletPollOrderStartState *pos;

  pos = GNUNET_new (struct WalletPollOrderStartState);
  pos->order_ref = order_ref;
  pos->merchant_url = merchant_url;
  pos->timeout = timeout;
  if (NULL != await_refund)
  {
    pos->wait_for_refund = true;
    GNUNET_assert (GNUNET_OK ==
                   TALER_string_to_amount (await_refund,
                                           &pos->refund_threshold));
  }
  {
    struct TALER_TESTING_Command cmd = {
      .cls = pos,
      .label = label,
      .run = &wallet_poll_order_start_run,
      .cleanup = &wallet_poll_order_start_cleanup
    };

    return cmd;
  }
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_wallet_poll_order_start2 (
  const char *label,
  const char *merchant_url,
  const char *order_ref,
  struct GNUNET_TIME_Relative timeout,
  const char *await_refund,
  const char *session_id)
{
  struct WalletPollOrderStartState *pos;
  struct TALER_TESTING_Command cmd;

  cmd = TALER_TESTING_cmd_wallet_poll_order_start (label,
                                                   merchant_url,
                                                   order_ref,
                                                   timeout,
                                                   await_refund);
  pos = cmd.cls;
  pos->session_id = session_id;
  return cmd;
}


/**
 * Run the "GET order conclude" CMD.
 *
 * @param cls closure.
 * @param cmd command being run now.
 * @param is interpreter state.
 */
static void
wallet_poll_order_conclude_run (void *cls,
                                const struct TALER_TESTING_Command *cmd,
                                struct TALER_TESTING_Interpreter *is)
{
  struct WalletPollOrderConcludeState *poc = cls;
  const struct TALER_TESTING_Command *poll_cmd;
  struct WalletPollOrderStartState *pos;

  poc->is = is;
  poll_cmd =
    TALER_TESTING_interpreter_lookup_command (is,
                                              poc->start_reference);
  if (NULL == poll_cmd)
    TALER_TESTING_FAIL (poc->is);
  GNUNET_assert (poll_cmd->run == &wallet_poll_order_start_run);
  pos = poll_cmd->cls;
  pos->cs = poc;
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Waiting on GET /orders/$ID of %s (%s)\n",
              poc->start_reference,
              (NULL == pos->ogh)
              ? "finished"
              : "active");
  if (NULL == pos->ogh)
    poc->task = GNUNET_SCHEDULER_add_now (&conclude_task,
                                          poc);
  else
    poc->task = GNUNET_SCHEDULER_add_at (pos->deadline,
                                         &conclude_task,
                                         poc);
}


/**
 * Free the state of a "GET order" CMD, and possibly
 * cancel a pending operation thereof.
 *
 * @param cls closure.
 * @param cmd command being run.
 */
static void
wallet_poll_order_conclude_cleanup (void *cls,
                                    const struct TALER_TESTING_Command *cmd)
{
  struct WalletPollOrderConcludeState *poc = cls;

  if (NULL != poc->task)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                "Command `%s' was not terminated\n",
                TALER_TESTING_interpreter_get_current_label (
                  poc->is));
    GNUNET_SCHEDULER_cancel (poc->task);
    poc->task = NULL;
  }
  GNUNET_free (poc);
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_wallet_poll_order_conclude (
  const char *label,
  unsigned int expected_http_status,
  const char *expected_refund_amount,
  const char *poll_start_reference)
{
  struct WalletPollOrderConcludeState *cps;

  cps = GNUNET_new (struct WalletPollOrderConcludeState);
  cps->start_reference = poll_start_reference;
  cps->expected_http_status = expected_http_status;
  if (NULL != expected_refund_amount)
  {
    cps->expected_refund = true;
    GNUNET_assert (GNUNET_OK ==
                   TALER_string_to_amount (expected_refund_amount,
                                           &cps->expected_refund_amount));
  }
  {
    struct TALER_TESTING_Command cmd = {
      .cls = cps,
      .label = label,
      .run = &wallet_poll_order_conclude_run,
      .cleanup = &wallet_poll_order_conclude_cleanup
    };

    return cmd;
  }
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_wallet_poll_order_conclude2 (
  const char *label,
  unsigned int expected_http_status,
  const char *expected_refund_amount,
  const char *poll_start_reference,
  const char *already_paid_order_id)
{
  struct WalletPollOrderConcludeState *cps;
  struct TALER_TESTING_Command cmd;

  cmd = TALER_TESTING_cmd_wallet_poll_order_conclude (
    label,
    expected_http_status,
    expected_refund_amount,
    poll_start_reference);
  cps = cmd.cls;
  cps->already_paid_order_id = already_paid_order_id;
  return cmd;
}


/* end of testing_api_cmd_wallet_get_order.c */
