/*
  This file is part of TALER
  Copyright (C) 2014-2023 Taler Systems SA

  TALER is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as
  published by the Free Software 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 test_merchant_api.c
 * @brief testcase to test exchange's HTTP API interface
 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
 * @author Christian Grothoff
 * @author Marcello Stanisci
 */
#include "platform.h"
#include <gnunet/gnunet_time_lib.h>
#include <taler/taler_util.h>
#include <taler/taler_signatures.h>
#include <taler/taler_exchange_service.h>
#include <taler/taler_json_lib.h>
#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_testing_lib.h>
#include <microhttpd.h>
#include <taler/taler_bank_service.h>
#include <taler/taler_fakebank_lib.h>
#include <taler/taler_testing_lib.h>
#include <taler/taler_error_codes.h>
#include "taler_merchant_util.h"
#include "taler_merchant_testing_lib.h"

#ifdef HAVE_DONAU_DONAU_SERVICE_H
#include <donau/donau_testing_lib.h>
#endif


/**
 * The 'poll-orders-conclude-1' and other 'conclude'
 * commands should NOT wait for this timeout!
 */
#define POLL_ORDER_TIMEOUT \
  GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)

/**
 * The 'poll-orders-conclude-1x' and other 'conclude'
 * commands that should (!) wait for this timeout! Hence,
 * here we use a short value!
 */
#define POLL_ORDER_SHORT_TIMEOUT \
  GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)

/**
 * Configuration file we use.  One (big) configuration is used
 * for the various components for this test.
 */
static char *config_file;

/**
 * Exchange base URL.  Could also be taken from config.
 */
#define EXCHANGE_URL "http://localhost:8081/"

/**
 * Payto URI of the customer (payer).
 */
static struct TALER_FullPayto payer_payto;

/**
 * Payto URI of the exchange (escrow account).
 */
static struct TALER_FullPayto exchange_payto;

/**
 * Payto URI of the merchant (receiver).
 */
static struct TALER_FullPayto merchant_payto;

/**
 * Credentials for the test.
 */
static struct TALER_TESTING_Credentials cred;

/**
 * Merchant base URL.
 */
static const char *merchant_url;

/**
 * Merchant instance "i1a" base URL.
 */
static char *merchant_url_i1a;

/**
 * Account number of the exchange at the bank.
 */
#define EXCHANGE_ACCOUNT_NAME "2"

/**
 * Account number of some user.
 */
#define USER_ACCOUNT_NAME "62"

/**
 * Account number of some other user.
 */
#define USER_ACCOUNT_NAME2 "63"

/**
 * Account number used by the merchant
 */
#define MERCHANT_ACCOUNT_NAME "3"

static const char *order_1_transfers[] = {
  "post-transfer-1",
  NULL
};

static const char *order_1_forgets_1[] = {
  "forget-1",
  NULL
};

static const char *order_1_forgets_2[] = {
  "forget-1",
  "forget-order-array-elem",
  NULL
};

static const char *order_1_forgets_3[] = {
  "forget-1",
  "forget-order-array-elem",
  "forget-order-array-wc",
  NULL
};

static const struct TALER_TESTING_ProductUnitExpectations
  expect_unicorn_defaults = {
  .have_unit_allow_fraction = true,
  .unit_allow_fraction = true,
  .have_unit_precision_level = true,
  .unit_precision_level = 1
};

static const struct TALER_TESTING_ProductUnitExpectations
  expect_unicorn_patched = {
  .have_unit_allow_fraction = true,
  .unit_allow_fraction = false,
  .have_unit_precision_level = true,
  .unit_precision_level = 0
};

/**
 * Execute the taler-merchant-webhook command with
 * our configuration file.
 *
 * @param label label to use for the command.
 */
static struct TALER_TESTING_Command
cmd_webhook (const char *label)
{
  return TALER_TESTING_cmd_webhook (label, config_file);
}


/**
 * Execute the taler-exchange-wirewatch command with
 * our configuration file.
 *
 * @param label label to use for the command.
 */
static struct TALER_TESTING_Command
cmd_exec_wirewatch (const char *label)
{
  return TALER_TESTING_cmd_exec_wirewatch (label,
                                           config_file);
}


/**
 * Execute the taler-exchange-aggregator, closer and transfer commands with
 * our configuration file.
 *
 * @param label label to use for the command.
 */
#define CMD_EXEC_AGGREGATOR(label) \
  TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \
  TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file)


/**
 * Run wire transfer of funds from some user's account to the
 * exchange.
 *
 * @param label label to use for the command.
 * @param amount amount to transfer, i.e. "EUR:1"
 * @param url exchange_url
 */
static struct TALER_TESTING_Command
cmd_transfer_to_exchange (const char *label,
                          const char *amount)
{
  return TALER_TESTING_cmd_admin_add_incoming (label,
                                               amount,
                                               &cred.ba,
                                               payer_payto);
}


/**
 * Main function that will tell the interpreter what commands to
 * run.
 *
 * @param cls closure
 */
static void
run (void *cls,
     struct TALER_TESTING_Interpreter *is)
{
  struct TALER_TESTING_Command get_private_order_id[] = {
    TALER_TESTING_cmd_merchant_post_instances (
      "instance-create-admin",
      merchant_url,
      "admin",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_account (
      "instance-create-default-account",
      merchant_url,
      merchant_payto,
      NULL, NULL,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_kyc_get (
      "instance-create-kyc-0",
      merchant_url,
      NULL,
      NULL,
      EXCHANGE_URL,
      TALER_EXCHANGE_KLPT_NONE,
      MHD_HTTP_NO_CONTENT,
      false),
    TALER_TESTING_cmd_merchant_post_orders_no_claim (
      "create-proposal-bad-currency",
      merchant_url,
      MHD_HTTP_CONFLICT,
      "4",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      "XXX:5.0"),
    TALER_TESTING_cmd_merchant_post_orders_no_claim (
      "create-proposal-4",
      merchant_url,
      MHD_HTTP_OK,
      "4",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      "EUR:5.0"),
    TALER_TESTING_cmd_merchant_get_order (
      "get-order-4",
      merchant_url,
      "create-proposal-4",
      TALER_MERCHANT_OSC_UNPAID,
      false,
      MHD_HTTP_OK,
      NULL),
    TALER_TESTING_cmd_merchant_delete_order (
      "delete-order-4",
      merchant_url,
      "4",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_purge_instance (
      "purge-admin",
      merchant_url,
      "admin",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_end ()
  };

  struct TALER_TESTING_Command pay[] = {
    /**
     * Move money to the exchange's bank account.
     */
    cmd_transfer_to_exchange ("create-reserve-1",
                              "EUR:10.02"),
    /**
     * Make a reserve exist, according to the previous transfer.
     */
    cmd_exec_wirewatch ("wirewatch-1"),
    TALER_TESTING_cmd_check_bank_admin_transfer ("check_bank_transfer-2",
                                                 "EUR:10.02",
                                                 payer_payto,
                                                 exchange_payto,
                                                 "create-reserve-1"),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
                                       "create-reserve-1",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
                                       "create-reserve-1",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_orders ("get-orders-empty",
                                           merchant_url,
                                           MHD_HTTP_OK,
                                           NULL),
    /**
     * Check the reserve is depleted.
     */
    TALER_TESTING_cmd_status ("withdraw-status-1",
                              "create-reserve-1",
                              "EUR:0",
                              MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_delete_order ("delete-order-nx",
                                             merchant_url,
                                             "1",
                                             MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_poll_orders_start ("poll-orders-1-start",
                                         merchant_url,
                                         POLL_ORDER_TIMEOUT),
    TALER_TESTING_cmd_merchant_claim_order ("claim-order-nx",
                                            merchant_url,
                                            MHD_HTTP_NOT_FOUND,
                                            NULL,
                                            "1"),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-1",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_OK,
                                             "1",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             true,
                                             "EUR:5.0",
                                             "x-taler-bank",
                                             "",
                                             "",
                                             NULL),
    TALER_TESTING_cmd_merchant_post_webhooks ("post-webhooks-pay-w1",
                                              merchant_url,
                                              "webhook-pay-1",
                                              "pay",
                                              MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_testserver ("launch-http-server-for-webhooks",
                                  12345),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-1-idem",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_OK,
                                             "1",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             true,
                                             "EUR:5.0",
                                             "x-taler-bank",
                                             "",
                                             "",
                                             "create-proposal-1"),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-1x",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_OK,
                                             "1x",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             true,
                                             "EUR:5.0",
                                             "x-taler-bank",
                                             "",
                                             "",
                                             NULL),
    TALER_TESTING_cmd_merchant_claim_order ("reclaim-1",
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "create-proposal-1",
                                            NULL),
    TALER_TESTING_cmd_merchant_claim_order ("reclaim-1-bad-nonce",
                                            merchant_url,
                                            MHD_HTTP_CONFLICT,
                                            NULL,
                                            "1"),
    TALER_TESTING_cmd_merchant_claim_order ("reclaim-1x",
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "create-proposal-1x",
                                            NULL),
    TALER_TESTING_cmd_merchant_post_orders ("create-proposal-1-pre-exists",
                                            cred.cfg,
                                            merchant_url,
                                            MHD_HTTP_CONFLICT,
                                            "1",
                                            GNUNET_TIME_UNIT_ZERO_TS,
                                            GNUNET_TIME_UNIT_FOREVER_TS,
                                            "EUR:5.0"),
    TALER_TESTING_cmd_poll_orders_conclude ("poll-orders-1-conclude",
                                            MHD_HTTP_OK,
                                            "poll-orders-1-start"),
    TALER_TESTING_cmd_merchant_get_orders ("get-orders-1",
                                           merchant_url,
                                           MHD_HTTP_OK,
                                           "create-proposal-1x",
                                           "create-proposal-1",
                                           NULL),
    TALER_TESTING_cmd_wallet_get_order ("get-order-wallet-1",
                                        merchant_url,
                                        "create-proposal-1",
                                        false,
                                        false,
                                        false,
                                        MHD_HTTP_PAYMENT_REQUIRED),
    TALER_TESTING_cmd_merchant_get_order ("get-order-merchant-1",
                                          merchant_url,
                                          "create-proposal-1",
                                          TALER_MERCHANT_OSC_CLAIMED,
                                          false,
                                          MHD_HTTP_OK,
                                          NULL),
    TALER_TESTING_cmd_wallet_poll_order_start ("poll-order-wallet-start-1",
                                               merchant_url,
                                               "create-proposal-1",
                                               POLL_ORDER_TIMEOUT,
                                               NULL),
    TALER_TESTING_cmd_wallet_poll_order_start2 ("poll-order-wallet-start-1x",
                                                merchant_url,
                                                "create-proposal-1x",
                                                POLL_ORDER_SHORT_TIMEOUT,
                                                NULL, /* no refund */
                                                "session-0"),
    TALER_TESTING_cmd_poll_order_start ("poll-order-merchant-1-start",
                                        merchant_url,
                                        "1",
                                        POLL_ORDER_TIMEOUT),
    TALER_TESTING_cmd_merchant_pay_order ("deposit-simple",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "create-proposal-1",
                                          "withdraw-coin-1",
                                          "EUR:5",
                                          "EUR:4.99",
                                          "session-0"),
    TALER_TESTING_cmd_poll_order_conclude ("poll-order-merchant-1-conclude",
                                           MHD_HTTP_OK,
                                           "poll-order-merchant-1-start"),
    TALER_TESTING_cmd_wallet_poll_order_conclude ("poll-order-1-conclude",
                                                  MHD_HTTP_OK,
                                                  NULL,
                                                  "poll-order-wallet-start-1"),
    /* Check for webhook */
    cmd_webhook ("pending-webhooks-pay-w1"),
    /* Check webhook did anything: have a command that inspects traits of the testserver
       and check if the traits have the right values set! */
    TALER_TESTING_cmd_checkserver ("check-http-server-for-webhooks",
                                   "launch-http-server-for-webhooks",
                                   0),
    /* Here we expect to run into a timeout, as we do not pay this one */
    TALER_TESTING_cmd_wallet_poll_order_conclude2 ("poll-order-1x-conclude",
                                                   MHD_HTTP_PAYMENT_REQUIRED,
                                                   NULL,
                                                   "poll-order-wallet-start-1x",
                                                   "1"),
    TALER_TESTING_cmd_merchant_post_orders_paid ("verify-order-1-paid",
                                                 merchant_url,
                                                 "deposit-simple",
                                                 "session-1",
                                                 MHD_HTTP_OK),
    TALER_TESTING_cmd_wallet_get_order ("get-order-wallet-1-2",
                                        merchant_url,
                                        "create-proposal-1",
                                        true,
                                        false,
                                        false,
                                        MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_order ("get-order-merchant-1-2a",
                                          merchant_url,
                                          "create-proposal-1",
                                          TALER_MERCHANT_OSC_PAID,
                                          false,
                                          MHD_HTTP_OK,
                                          NULL),
    TALER_TESTING_cmd_merchant_get_orders ("get-orders-1-paid",
                                           merchant_url,
                                           MHD_HTTP_OK,
                                           "create-proposal-1x",
                                           "create-proposal-1",
                                           NULL),
    TALER_TESTING_cmd_merchant_pay_order ("replay-simple",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "create-proposal-1",
                                          "withdraw-coin-1",
                                          "EUR:5",
                                          "EUR:4.99",
                                          "session-0"),
    TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-1"),
    TALER_TESTING_cmd_sleep (
      "Wait for wire transfer deadline",
      3),
    CMD_EXEC_AGGREGATOR ("run-aggregator"),
    TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-498c",
                                           EXCHANGE_URL,
                                           "EUR:4.98",
                                           exchange_payto,
                                           merchant_payto),
    TALER_TESTING_cmd_merchant_post_transfer ("post-transfer-1",
                                              &cred.ba,
                                              merchant_payto,
                                              merchant_url,
                                              "EUR:4.98",
                                              MHD_HTTP_NO_CONTENT,
                                              "deposit-simple",
                                              NULL),
    TALER_TESTING_cmd_depositcheck ("run taler-merchant-depositcheck-1",
                                    config_file),
    TALER_TESTING_cmd_run_tme ("run taler-merchant-reconciliation-1",
                               config_file),
    TALER_TESTING_cmd_merchant_post_transfer2 ("post-transfer-bad",
                                               merchant_url,
                                               merchant_payto,
                                               "EUR:4.98",
                                               NULL,
                                               /*non-routable IP address
                                               so we are sure to not get
                                               any reply*/
                                               "http://192.0.2.1/404/",
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_transfers ("get-transfers-1",
                                              merchant_url,
                                              merchant_payto,
                                              MHD_HTTP_OK,
                                              "post-transfer-1",
                                              "post-transfer-bad",
                                              NULL),
    TALER_TESTING_cmd_merchant_delete_transfer ("delete-transfer-1",
                                                merchant_url,
                                                "post-transfer-bad",
                                                MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_order2 ("get-order-merchant-1-2b",
                                           merchant_url,
                                           "create-proposal-1",
                                           TALER_MERCHANT_OSC_PAID,
                                           true,
                                           order_1_transfers, /* "post-transfer-1" */
                                           false,
                                           NULL,
                                           NULL,
                                           MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_forget_order ("forget-1",
                                             merchant_url,
                                             MHD_HTTP_NO_CONTENT,
                                             "create-proposal-1",
                                             NULL,
                                             "$.dummy_obj",
                                             NULL),
    TALER_TESTING_cmd_merchant_get_order2 ("get-order-merchant-1-forget-1",
                                           merchant_url,
                                           "create-proposal-1",
                                           TALER_MERCHANT_OSC_PAID,
                                           true,
                                           order_1_transfers,
                                           false,
                                           NULL,
                                           order_1_forgets_1,
                                           MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_forget_order ("forget-unforgettable",
                                             merchant_url,
                                             MHD_HTTP_CONFLICT,
                                             "create-proposal-1",
                                             NULL,
                                             "$.amount",
                                             NULL),
    TALER_TESTING_cmd_merchant_forget_order ("forget-order-nx",
                                             merchant_url,
                                             MHD_HTTP_NOT_FOUND,
                                             NULL,
                                             "nx-order",
                                             "$.dummy_obj",
                                             NULL),
    TALER_TESTING_cmd_merchant_forget_order ("forget-order-array-elem",
                                             merchant_url,
                                             MHD_HTTP_NO_CONTENT,
                                             "create-proposal-1",
                                             NULL,
                                             "$.dummy_array[0].item",
                                             NULL),
    TALER_TESTING_cmd_merchant_get_order2 ("get-order-merchant-1-forget-2",
                                           merchant_url,
                                           "create-proposal-1",
                                           TALER_MERCHANT_OSC_PAID,
                                           true,
                                           order_1_transfers,
                                           false,
                                           NULL,
                                           order_1_forgets_2,
                                           MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_forget_order ("forget-order-array-wc",
                                             merchant_url,
                                             MHD_HTTP_NO_CONTENT,
                                             "create-proposal-1",
                                             NULL,
                                             "$.dummy_array[*].item",
                                             NULL),
    TALER_TESTING_cmd_merchant_get_order2 ("get-order-merchant-1-forget-3",
                                           merchant_url,
                                           "create-proposal-1",
                                           TALER_MERCHANT_OSC_PAID,
                                           true,
                                           order_1_transfers,
                                           false,
                                           NULL,
                                           order_1_forgets_3,
                                           MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_forget_order ("forget-order-malformed",
                                             merchant_url,
                                             MHD_HTTP_BAD_REQUEST,
                                             "create-proposal-1",
                                             NULL,
                                             "$.dummy_array[abc].item",
                                             NULL),
    TALER_TESTING_cmd_merchant_post_products ("post-products-p3",
                                              merchant_url,
                                              "product-3",
                                              "a product",
                                              "EUR:1",
                                              MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_products2 ("post-products-p4",
                                               merchant_url,
                                               "product-4age",
                                               "an age-restricted product",
                                               NULL,
                                               "unit",
                                               "EUR:1",
                                               "", /* image */
                                               NULL,
                                               4,
                                               16 /* minimum age */,
                                               NULL,
                                               GNUNET_TIME_UNIT_FOREVER_TS,
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_products3 ("post-products-frac",
                                               merchant_url,
                                               "product-frac",
                                               "fractional product",
                                               json_pack ("{s:s}",
                                                          "en",
                                                          "fractional product"),
                                               "kg",
                                               "EUR:1.5",
                                               "",
                                               json_array (),
                                               1,
                                               (MERCHANT_UNIT_FRAC_BASE * 3)
                                               / 4,
                                               true,
                                               0,
                                               NULL,
                                               GNUNET_TIME_UNIT_ZERO_TS,
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_product ("get-product-frac-post",
                                            merchant_url,
                                            "product-frac",
                                            MHD_HTTP_OK,
                                            "post-products-frac"),
    TALER_TESTING_cmd_merchant_patch_product ("patch-products-p3",
                                              merchant_url,
                                              "product-3",
                                              "a product",
                                              json_object (),
                                              "can",
                                              "EUR:1",
                                              "data:image/jpeg;base64,RAWDATA",
                                              json_array (),
                                              5,
                                              0,
                                              json_object (),
                                              GNUNET_TIME_relative_to_timestamp
                                              (
                                                GNUNET_TIME_UNIT_MINUTES),
                                              MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_patch_product2 ("patch-product-frac",
                                               merchant_url,
                                               "product-frac",
                                               "fractional product patched",
                                               json_pack ("{s:s}",
                                                          "en",
                                                          "fractional product patched"),
                                               "kg",
                                               "EUR:1.6",
                                               "",
                                               json_array (),
                                               2,
                                               MERCHANT_UNIT_FRAC_BASE / 4,
                                               true,
                                               0,
                                               json_object (),
                                               GNUNET_TIME_relative_to_timestamp (
                                                 GNUNET_TIME_UNIT_MINUTES),
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_product ("get-product-frac-patched",
                                            merchant_url,
                                            "product-frac",
                                            MHD_HTTP_OK,
                                            "patch-product-frac"),
    TALER_TESTING_cmd_merchant_delete_unit ("delete-unit-piece-denied",
                                            merchant_url,
                                            "Piece",
                                            MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_post_units ("post-unit-piece-conflict",
                                           merchant_url,
                                           "Piece",
                                           "piece",
                                           "pc",
                                           false,
                                           0,
                                           true,
                                           NULL,
                                           NULL,
                                           MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_post_units ("post-unit-bucket",
                                           merchant_url,
                                           "BucketCustomUnit",
                                           "bucket",
                                           "bkt",
                                           false,
                                           0,
                                           true,
                                           json_pack ("{s:s}", "en", "bucket"),
                                           json_pack ("{s:s}", "en", "bkt"),
                                           MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_unit ("get-unit-bucket",
                                         merchant_url,
                                         "BucketCustomUnit",
                                         MHD_HTTP_OK,
                                         "post-unit-bucket"),
    TALER_TESTING_cmd_merchant_get_units ("get-units-bucket-list",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "post-unit-bucket",
                                          NULL),
    TALER_TESTING_cmd_merchant_patch_unit ("patch-unit-bucket",
                                           merchant_url,
                                           "BucketCustomUnit",
                                           "bucket-updated",
                                           "bkt-upd",
                                           json_pack ("{s:s}", "en",
                                                      "bucket-updated"),
                                           json_pack ("{s:s}", "en", "bkt-upd"),
                                           true,
                                           0,
                                           false,
                                           MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_unit ("get-unit-bucket-patched",
                                         merchant_url,
                                         "BucketCustomUnit",
                                         MHD_HTTP_OK,
                                         "patch-unit-bucket"),
    TALER_TESTING_cmd_merchant_get_units ("get-units-bucket-updated",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "patch-unit-bucket",
                                          NULL),
    TALER_TESTING_cmd_merchant_delete_unit ("delete-unit-bucket",
                                            merchant_url,
                                            "BucketCustomUnit",
                                            MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_unit ("get-unit-bucket-deleted",
                                         merchant_url,
                                         "BucketCustomUnit",
                                         MHD_HTTP_NOT_FOUND,
                                         NULL),
    TALER_TESTING_cmd_merchant_post_units ("post-unit-unicorn",
                                           merchant_url,
                                           "UnitUnicorn",
                                           "unicorn",
                                           "uni",
                                           true,
                                           1,
                                           true,
                                           json_pack ("{s:s}", "en", "unicorn"),
                                           json_pack ("{s:s}", "en", "uni"),
                                           MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_products2 ("post-product-unicorn",
                                               merchant_url,
                                               "product-unicorn",
                                               "a unicorn snack",
                                               json_pack ("{s:s}",
                                                          "en",
                                                          "a unicorn snack"),
                                               "UnitUnicorn",
                                               "EUR:3.5",
                                               "",
                                               json_array (),
                                               7,
                                               0,
                                               json_object (),
                                               GNUNET_TIME_UNIT_ZERO_TS,
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_product2 ("get-product-unicorn-default",
                                             merchant_url,
                                             "product-unicorn",
                                             MHD_HTTP_OK,
                                             "post-product-unicorn",
                                             &expect_unicorn_defaults),
    TALER_TESTING_cmd_merchant_patch_unit ("patch-unit-unicorn",
                                           merchant_url,
                                           "UnitUnicorn",
                                           NULL,
                                           NULL,
                                           NULL,
                                           NULL,
                                           false,
                                           0,
                                           true,
                                           MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_product2 ("get-product-unicorn-patched",
                                             merchant_url,
                                             "product-unicorn",
                                             MHD_HTTP_OK,
                                             "post-product-unicorn",
                                             &expect_unicorn_patched),
    TALER_TESTING_cmd_merchant_delete_product ("delete-product-unicorn",
                                               merchant_url,
                                               "product-unicorn",
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_delete_unit ("delete-unit-unicorn",
                                            merchant_url,
                                            "UnitUnicorn",
                                            MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_lock_product ("lock-product-p3",
                                             merchant_url,
                                             "product-3",
                                             GNUNET_TIME_UNIT_MINUTES,
                                             2,
                                             MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_lock_product2 (
      "lock-product-p3-float-denied",
      merchant_url,
      "product-3",
      GNUNET_TIME_UNIT_MINUTES,
      1,
      MERCHANT_UNIT_FRAC_BASE / 2,
      true,
      MHD_HTTP_BAD_REQUEST),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-p3-float-denied",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_BAD_REQUEST,
                                             "order-p3-float-denied",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             true,
                                             "EUR:5.0",
                                             "x-taler-bank",
                                             "product-3/1.5",
                                             "",
                                             NULL),
    TALER_TESTING_cmd_merchant_patch_product2 (
      "patch-product-3-allow-float",
      merchant_url,
      "product-3",
      "a product allow fractional",
      json_pack ("{s:s}",
                 "en",
                 "a product allow fractional"),
      "can",
      "EUR:1",
      "data:image/jpeg;base64,RAWDATA",
      json_array (),
      5,
      0,
      true,
      0,
      json_object (),
      GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MINUTES),
      MHD_HTTP_NO_CONTENT),
    cmd_transfer_to_exchange ("create-reserve-p3-float",
                              "EUR:5.01"),
    cmd_exec_wirewatch ("wirewatch-p3-float"),
    TALER_TESTING_cmd_check_bank_admin_transfer (
      "check_bank_transfer-p3-float",
      "EUR:5.01",
      payer_payto,
      exchange_payto,
      "create-reserve-p3-float"),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-p3-float",
                                       "create-reserve-p3-float",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_lock_product2 (
      "lock-product-p3-float",
      merchant_url,
      "product-3",
      GNUNET_TIME_UNIT_MINUTES,
      1,
      MERCHANT_UNIT_FRAC_BASE / 2,
      true,
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-p3-float",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_OK,
                                             "order-p3-float",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             true,
                                             "EUR:5.0",
                                             "x-taler-bank",
                                             "product-3/1.5",
                                             "",
                                             NULL),
    TALER_TESTING_cmd_merchant_claim_order ("claim-order-p3-float",
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "create-proposal-p3-float",
                                            NULL),
    TALER_TESTING_cmd_merchant_pay_order ("pay-order-p3-float",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "create-proposal-p3-float",
                                          "withdraw-coin-p3-float",
                                          "EUR:5",
                                          "EUR:4.99",
                                          "session-p3-float"),
    TALER_TESTING_cmd_sleep (
      "Wait for wire transfer deadline-p3-float",
      3),
    CMD_EXEC_AGGREGATOR ("run-aggregator-p3-float"),
    TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-p3-float",
                                           EXCHANGE_URL,
                                           "EUR:4.98",
                                           exchange_payto,
                                           merchant_payto),
    TALER_TESTING_cmd_merchant_get_order ("get-order-merchant-p3-float-paid",
                                          merchant_url,
                                          "create-proposal-p3-float",
                                          TALER_MERCHANT_OSC_PAID,
                                          false,
                                          MHD_HTTP_OK,
                                          NULL),
    TALER_TESTING_cmd_merchant_patch_product2 (
      "patch-product-3-restore",
      merchant_url,
      "product-3",
      "a product",
      json_pack ("{s:s}",
                 "en",
                 "a product"),
      "can",
      "EUR:1",
      "data:image/jpeg;base64,RAWDATA",
      json_array (),
      5,
      0,
      false,
      0,
      json_object (),
      GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MINUTES),
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-p3-wm-nx",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_NOT_FOUND,
                                             "order-p3",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             true,
                                             "EUR:5.0",
                                             "unsupported-wire-method",
                                             "product-3/2",
                                             "", /* locks */
                                             NULL),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-p3-pd-nx",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_NOT_FOUND,
                                             "order-p3",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             true,
                                             "EUR:5.0",
                                             "x-taler-bank",
                                             "unknown-product/2",
                                             "",
                                             NULL),
    TALER_TESTING_cmd_merchant_post_orders2 (
      "create-proposal-p3-not-enough-stock",
      cred.cfg,
      merchant_url,
      MHD_HTTP_GONE,
      "order-p3",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      true,
      "EUR:5.0",
      "x-taler-bank",
      "product-3/24",
      "",
      NULL),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-p3",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_OK,
                                             "order-p3",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             false,
                                             "EUR:5.0",
                                             "x-taler-bank",
                                             "product-3/3",
                                             "lock-product-p3",
                                             NULL),
    TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-p4-age",
                                             cred.cfg,
                                             merchant_url,
                                             MHD_HTTP_OK,
                                             "order-p4-age",
                                             GNUNET_TIME_UNIT_ZERO_TS,
                                             GNUNET_TIME_UNIT_FOREVER_TS,
                                             false,
                                             "EUR:5.0",
                                             "x-taler-bank",
                                             "product-4age",
                                             "", /* locks */
                                             NULL),
    TALER_TESTING_cmd_merchant_get_order4 ("get-order-merchant-p4-age",
                                           merchant_url,
                                           "create-proposal-p4-age",
                                           TALER_MERCHANT_OSC_CLAIMED,
                                           16,
                                           MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_delete_order ("delete-order-paid",
                                             merchant_url,
                                             "1",
                                             MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_post_orders ("create-proposal-no-id",
                                            cred.cfg,
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            NULL,
                                            GNUNET_TIME_UNIT_ZERO_TS,
                                            GNUNET_TIME_UNIT_FOREVER_TS,
                                            "EUR:5.0"),
    TALER_TESTING_cmd_merchant_delete_webhook ("post-webhooks-pay-w1",
                                               merchant_url,
                                               "webhook-pay-1",
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-2"),
    TALER_TESTING_cmd_end ()
  };
  struct TALER_TESTING_Command double_spending[] = {
    TALER_TESTING_cmd_merchant_post_orders ("create-proposal-2",
                                            cred.cfg,
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "2",
                                            GNUNET_TIME_UNIT_ZERO_TS,
                                            GNUNET_TIME_UNIT_FOREVER_TS,
                                            "EUR:5.0"),
    TALER_TESTING_cmd_merchant_claim_order ("fetch-proposal-2",
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "create-proposal-2",
                                            NULL),
    TALER_TESTING_cmd_merchant_pay_order ("deposit-double-2",
                                          merchant_url,
                                          MHD_HTTP_CONFLICT,
                                          "create-proposal-2",
                                          "withdraw-coin-1",
                                          "EUR:5",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_end ()
  };

  const char *order_1r_refunds[] = {
    "refund-increase-1r",
    "refund-increase-1r-2",
    NULL
  };
  struct TALER_TESTING_Command refund[] = {
    cmd_transfer_to_exchange ("create-reserve-1r",
                              "EUR:10.02"),
    /**
     * Make a reserve exist, according to the previous transfer.
     */
    cmd_exec_wirewatch ("wirewatch-1r"),
    TALER_TESTING_cmd_check_bank_admin_transfer ("check_bank_transfer-2r",
                                                 "EUR:10.02",
                                                 payer_payto,
                                                 exchange_payto,
                                                 "create-reserve-1r"),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1r",
                                       "create-reserve-1r",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2r",
                                       "create-reserve-1r",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    /**
     * Check the reserve is depleted.
     */
    TALER_TESTING_cmd_status ("withdraw-status-1r",
                              "create-reserve-1r",
                              "EUR:0",
                              MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_orders ("create-proposal-1r",
                                            cred.cfg,
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "1r",
                                            GNUNET_TIME_UNIT_ZERO_TS,
                                            GNUNET_TIME_UNIT_FOREVER_TS,
                                            "EUR:5.0"),
    TALER_TESTING_cmd_wallet_poll_order_start ("poll-order-wallet-refund-1-low",
                                               merchant_url,
                                               "create-proposal-1r",
                                               POLL_ORDER_TIMEOUT,
                                               "EUR:0.01"),
    TALER_TESTING_cmd_wallet_poll_order_start (
      "poll-order-wallet-refund-1-high",
      merchant_url,
      "create-proposal-1r",
      POLL_ORDER_TIMEOUT,
      "EUR:0.2"),
    TALER_TESTING_cmd_merchant_pay_order ("pay-for-refund-1r",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "create-proposal-1r",
                                          "withdraw-coin-1r",
                                          "EUR:5",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_poll_order_start ("poll-payment-refund-1",
                                        merchant_url,
                                        "1r", /* proposal name, not cmd ref! */
                                        POLL_ORDER_TIMEOUT),
    TALER_TESTING_cmd_merchant_order_refund ("refund-increase-1r",
                                             merchant_url,
                                             "refund test",
                                             "1r", /* order ID */
                                             "EUR:0.1",
                                             MHD_HTTP_OK),
    TALER_TESTING_cmd_wallet_poll_order_conclude ("poll-order-1-conclude-low",
                                                  MHD_HTTP_OK,
                                                  "EUR:0.1",
                                                  "poll-order-wallet-refund-1-low"),
    TALER_TESTING_cmd_wallet_get_order ("get-order-wallet-1r",
                                        merchant_url,
                                        "create-proposal-1r",
                                        true,
                                        true,
                                        true,
                                        MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_order_refund ("refund-increase-1r-2",
                                             merchant_url,
                                             "refund test 2",
                                             "1r", /* order ID */
                                             "EUR:1.0",
                                             MHD_HTTP_OK),
    TALER_TESTING_cmd_wallet_poll_order_conclude ("poll-order-1-conclude-high",
                                                  MHD_HTTP_OK,
                                                  "EUR:1.0",
                                                  "poll-order-wallet-refund-1-high"),
    TALER_TESTING_cmd_wallet_get_order ("get-order-wallet-1r-2",
                                        merchant_url,
                                        "create-proposal-1r",
                                        true,
                                        true,
                                        true,
                                        MHD_HTTP_OK),
    TALER_TESTING_cmd_wallet_order_refund ("obtain-refund-1r",
                                           merchant_url,
                                           "create-proposal-1r",
                                           MHD_HTTP_OK,
                                           "refund-increase-1r",
                                           "refund-increase-1r-2",
                                           NULL),
    TALER_TESTING_cmd_wallet_get_order ("get-order-wallet-1r-3",
                                        merchant_url,
                                        "create-proposal-1r",
                                        true,
                                        true,
                                        false,
                                        MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_order ("get-order-merchant-1r",
                                          merchant_url,
                                          "create-proposal-1r",
                                          TALER_MERCHANT_OSC_PAID,
                                          true,
                                          MHD_HTTP_OK,
                                          "refund-increase-1r",
                                          "refund-increase-1r-2",
                                          NULL),
    TALER_TESTING_cmd_merchant_get_order2 ("get-order-merchant-1r-2",
                                           merchant_url,
                                           "create-proposal-1r",
                                           TALER_MERCHANT_OSC_PAID,
                                           false,
                                           NULL,
                                           true,
                                           order_1r_refunds,
                                           NULL,
                                           MHD_HTTP_OK),
    TALER_TESTING_cmd_poll_order_conclude ("poll-payment-refund-conclude-1",
                                           MHD_HTTP_OK,
                                           "poll-payment-refund-1"),

    /* Test /refund on a contract that was never paid.  */
    TALER_TESTING_cmd_merchant_post_orders ("create-proposal-not-to-be-paid",
                                            cred.cfg,
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "1-unpaid",
                                            GNUNET_TIME_UNIT_ZERO_TS,
                                            GNUNET_TIME_UNIT_FOREVER_TS,
                                            "EUR:5.0"),
    /* Try to increase an unpaid proposal.  */
    TALER_TESTING_cmd_merchant_order_refund ("refund-increase-unpaid-proposal",
                                             merchant_url,
                                             "refund test",
                                             "1-unpaid",
                                             "EUR:0.1",
                                             MHD_HTTP_CONFLICT),
    /* Try to increase a non existent proposal.  */
    TALER_TESTING_cmd_merchant_order_refund (
      "refund-increase-nonexistent-proposal",
      merchant_url,
      "refund test",
      "non-existent-id",
      "EUR:0.1",
      MHD_HTTP_NOT_FOUND),
    /*
       The following block will (1) create a new
       reserve, then (2) a proposal, then (3) pay for
       it, and finally (4) attempt to pick up a refund
       from it without any increasing taking place
       in the first place.
    */
    cmd_transfer_to_exchange ("create-reserve-unincreased-refund",
                              "EUR:5.01"),
    cmd_exec_wirewatch ("wirewatch-unincreased-refund"),
    TALER_TESTING_cmd_check_bank_admin_transfer (
      "check_bank_transfer-unincreased-refund",
      "EUR:5.01",
      payer_payto,
      exchange_payto,
      "create-reserve-unincreased-refund"),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-unincreased-refund",
                                       "create-reserve-unincreased-refund",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_orders (
      "create-proposal-unincreased-refund",
      cred.cfg,
      merchant_url,
      MHD_HTTP_OK,
      "unincreased-proposal",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      "EUR:5.0"),
    TALER_TESTING_cmd_merchant_pay_order ("pay-unincreased-proposal",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "create-proposal-unincreased-refund",
                                          "withdraw-coin-unincreased-refund",
                                          "EUR:5",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_sleep (
      "Wait for wire transfer deadline",
      3),
    CMD_EXEC_AGGREGATOR ("run-aggregator-unincreased-refund"),
    TALER_TESTING_cmd_check_bank_transfer (
      "check_bank_transfer-paid-unincreased-refund",
      EXCHANGE_URL,
      "EUR:8.97", /* '4.98 from above', plus 4.99 from 'pay-for-refund-1r'
                     and MINUS 0.1 MINUS 0.9 from
                     'refund-increase-1r' and 'refund-increase-1r-2' */
      exchange_payto,
      merchant_payto),
    TALER_TESTING_cmd_end ()
  };

  struct TALER_TESTING_Command auth[] = {
    TALER_TESTING_cmd_merchant_post_instances ("instance-create-i1a",
                                               merchant_url,
                                               "i1a",
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_account (
      "instance-create-i1a-account",
      merchant_url_i1a,
      merchant_payto,
      NULL, NULL,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_product ("get-nx-product-i1a-1",
                                            merchant_url_i1a,
                                            "nx-product",
                                            MHD_HTTP_NOT_FOUND,
                                            NULL),
    TALER_TESTING_cmd_merchant_get_products ("get-i1a-products-empty-1",
                                             merchant_url_i1a,
                                             MHD_HTTP_OK,
                                             NULL),
    TALER_TESTING_cmd_merchant_get_product ("get-nx-product-i1a-2",
                                            merchant_url_i1a,
                                            "nx-product",
                                            MHD_HTTP_NOT_FOUND,
                                            NULL),
    TALER_TESTING_cmd_merchant_get_products ("get-i1a-products-empty-2",
                                             merchant_url_i1a,
                                             MHD_HTTP_OK,
                                             NULL),
    TALER_TESTING_cmd_merchant_post_instance_auth (
      "instance-create-i1a-auth-ok",
      merchant_url,
      "i1a",
      "my-secret",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_product ("get-nx-product-i1a-3",
                                            merchant_url_i1a,
                                            "nx-product",
                                            MHD_HTTP_UNAUTHORIZED,
                                            NULL),
    TALER_TESTING_cmd_merchant_get_products ("get-i1a-products-empty-3",
                                             merchant_url_i1a,
                                             MHD_HTTP_UNAUTHORIZED,
                                             NULL),
    TALER_TESTING_cmd_set_authorization ("set-auth-valid",
                                         "Basic aTFhOm15LXNlY3JldA=="),
    TALER_TESTING_cmd_merchant_post_instance_token (
      "instance-create-i1a-token-ok",
      merchant_url,
      "i1a",
      "write", /* scope */
      GNUNET_TIME_UNIT_DAYS, /* duration */
      GNUNET_YES, /* refreshable */
      MHD_HTTP_OK),
    TALER_TESTING_cmd_set_authorization ("unset-auth-valid",
                                         NULL), // Unset header
    TALER_TESTING_cmd_merchant_set_instance_token (
      "instance-create-i1a-token-set",
      "instance-create-i1a-token-ok"),
    TALER_TESTING_cmd_merchant_get_product ("get-nx-product-i1a-4",
                                            merchant_url_i1a,
                                            "nx-product",
                                            MHD_HTTP_NOT_FOUND,
                                            NULL),
    TALER_TESTING_cmd_merchant_get_products ("get-i1a-products-empty-4",
                                             merchant_url_i1a,
                                             MHD_HTTP_OK,
                                             NULL),
    TALER_TESTING_cmd_merchant_delete_instance_token (
      "instance-create-i1a-token-delete",
      merchant_url,
      "i1a",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_product ("get-nx-product-i1a-5",
                                            merchant_url_i1a,
                                            "nx-product",
                                            MHD_HTTP_UNAUTHORIZED,
                                            NULL),
    TALER_TESTING_cmd_merchant_get_products ("get-i1a-products-empty-5",
                                             merchant_url_i1a,
                                             MHD_HTTP_UNAUTHORIZED,
                                             NULL),
    TALER_TESTING_cmd_merchant_purge_instance ("instance-delete-i1a-fail",
                                               merchant_url_i1a,
                                               NULL,
                                               MHD_HTTP_UNAUTHORIZED),
    TALER_TESTING_cmd_set_authorization ("set-token-auth-valid-again",
                                         "Basic aTFhOm15LXNlY3JldA=="),
    TALER_TESTING_cmd_merchant_post_instance_token (
      "set-auth-valid-again",
      merchant_url,
      "i1a",
      "write", /* scope */
      GNUNET_TIME_UNIT_DAYS, /* duration */
      GNUNET_YES, /* refreshable */
      MHD_HTTP_OK),
    TALER_TESTING_cmd_set_authorization ("unset-auth-valid2",
                                         NULL), // Unset header
    TALER_TESTING_cmd_merchant_set_instance_token (
      "instance-create-i1a-token-set-again",
      "set-auth-valid-again"),
    TALER_TESTING_cmd_merchant_post_instance_auth (
      "instance-create-i1a-auth-ok-idempotent",
      merchant_url_i1a,
      NULL,
      RFC_8959_PREFIX "my-other-secret",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_instance_auth (
      "instance-create-i1a-clear-auth",
      merchant_url_i1a,
      NULL,
      NULL,
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_set_authorization ("set-auth-none",
                                         NULL),
    TALER_TESTING_cmd_merchant_purge_instance ("instance-delete-i1a",
                                               merchant_url_i1a,
                                               NULL,
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_end ()
  };

  struct TALER_TESTING_Command pay_again[] = {
    cmd_transfer_to_exchange ("create-reserve-20",
                              "EUR:20.04"),
    cmd_exec_wirewatch ("wirewatch-20"),
    TALER_TESTING_cmd_check_bank_admin_transfer ("check_bank_transfer-10",
                                                 "EUR:20.04",
                                                 payer_payto,
                                                 exchange_payto,
                                                 "create-reserve-20"),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-10a",
                                       "create-reserve-20",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-10b",
                                       "create-reserve-20",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-10c",
                                       "create-reserve-20",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-10d",
                                       "create-reserve-20",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_status ("withdraw-status-20",
                              "create-reserve-20",
                              "EUR:0",
                              MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_orders ("create-proposal-10",
                                            cred.cfg,
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "10",
                                            GNUNET_TIME_UNIT_ZERO_TS,
                                            GNUNET_TIME_UNIT_FOREVER_TS,
                                            "EUR:10.0"),
    TALER_TESTING_cmd_merchant_pay_order ("pay-fail-partial-double-10",
                                          merchant_url,
                                          MHD_HTTP_CONFLICT,
                                          "create-proposal-10",
                                          "withdraw-coin-10a;withdraw-coin-1",
                                          "EUR:5",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_merchant_pay_order ("pay-again-10",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "create-proposal-10",
                                          "withdraw-coin-10a;withdraw-coin-10b",
                                          "EUR:5",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_merchant_pay_order ("pay-too-much-10",
                                          merchant_url,
                                          MHD_HTTP_CONFLICT,
                                          "create-proposal-10",
                                          "withdraw-coin-10c;withdraw-coin-10d",
                                          "EUR:5",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_sleep (
      "Wait for wire transfer deadline",
      3),
    CMD_EXEC_AGGREGATOR ("run-aggregator-10"),
    TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-9.97-10",
                                           EXCHANGE_URL,
                                           "EUR:9.97",
                                           exchange_payto,
                                           merchant_payto),
    TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-10"),
    TALER_TESTING_cmd_end ()
  };

  struct TALER_TESTING_Command pay_abort[] = {
    cmd_transfer_to_exchange ("create-reserve-11",
                              "EUR:10.02"),
    cmd_exec_wirewatch ("wirewatch-11"),
    TALER_TESTING_cmd_check_bank_admin_transfer ("check_bank_transfer-11",
                                                 "EUR:10.02",
                                                 payer_payto,
                                                 exchange_payto,
                                                 "create-reserve-11"),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-11a",
                                       "create-reserve-11",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-11b",
                                       "create-reserve-11",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_status ("withdraw-status-11",
                              "create-reserve-11",
                              "EUR:0",
                              MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_orders ("create-proposal-11",
                                            cred.cfg,
                                            merchant_url,
                                            MHD_HTTP_OK,
                                            "11",
                                            GNUNET_TIME_UNIT_ZERO_TS,
                                            GNUNET_TIME_UNIT_FOREVER_TS,
                                            "EUR:10.0"),
    TALER_TESTING_cmd_merchant_pay_order ("pay-fail-partial-double-11-good",
                                          merchant_url,
                                          MHD_HTTP_BAD_REQUEST,
                                          "create-proposal-11",
                                          "withdraw-coin-11a",
                                          "EUR:5",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_merchant_pay_order ("pay-fail-partial-double-11-bad",
                                          merchant_url,
                                          MHD_HTTP_CONFLICT,
                                          "create-proposal-11",
                                          "withdraw-coin-1",
                                          "EUR:5",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_merchant_order_abort ("pay-abort-11",
                                            merchant_url,
                                            "pay-fail-partial-double-11-good",
                                            MHD_HTTP_OK),
    TALER_TESTING_cmd_sleep (
      "Wait for wire transfer deadline",
      3),
    CMD_EXEC_AGGREGATOR ("run-aggregator-11"),
    TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-11"),
    TALER_TESTING_cmd_end ()
  };

  struct TALER_TESTING_Command templates[] = {
    cmd_transfer_to_exchange ("create-reserve-20x",
                              "EUR:20.04"),
    cmd_exec_wirewatch ("wirewatch-20x"),
    TALER_TESTING_cmd_check_bank_admin_transfer ("check_bank_transfer-20x",
                                                 "EUR:20.04",
                                                 payer_payto,
                                                 exchange_payto,
                                                 "create-reserve-20x"),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-xa",
                                       "create-reserve-20x",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-xb",
                                       "create-reserve-20x",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-xc",
                                       "create-reserve-20x",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-xd",
                                       "create-reserve-20x",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_status ("withdraw-status-20x",
                              "create-reserve-20x",
                              "EUR:0",
                              MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_templates ("get-templates-empty",
                                              merchant_url,
                                              MHD_HTTP_OK,
                                              NULL),
    TALER_TESTING_cmd_merchant_post_templates ("post-templates-t1",
                                               merchant_url,
                                               "template-1",
                                               "a template",
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_templates ("post-templates-t1-idem",
                                               merchant_url,
                                               "template-1",
                                               "a template",
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_templates ("post-templates-t1-non-idem",
                                               merchant_url,
                                               "template-1",
                                               "a different template",
                                               MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_get_templates ("get-templates-t1",
                                              merchant_url,
                                              MHD_HTTP_OK,
                                              "post-templates-t1",
                                              NULL),
    TALER_TESTING_cmd_merchant_get_template ("get-template-t1",
                                             merchant_url,
                                             "template-1",
                                             MHD_HTTP_OK,
                                             "post-templates-t1"),
    TALER_TESTING_cmd_merchant_post_templates ("post-templates-t2",
                                               merchant_url,
                                               "template-2",
                                               "a template",
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_patch_template (
      "patch-templates-t2",
      merchant_url,
      "template-2",
      "another template",
      NULL,
      GNUNET_JSON_PACK (
        GNUNET_JSON_pack_uint64 ("minimum_age", 0),
        GNUNET_JSON_pack_time_rel ("pay_duration",
                                   GNUNET_TIME_UNIT_MINUTES)),
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_template ("get-template-t2",
                                             merchant_url,
                                             "template-2",
                                             MHD_HTTP_OK,
                                             "patch-templates-t2"),
    TALER_TESTING_cmd_merchant_get_template ("get-template-nx",
                                             merchant_url,
                                             "template-nx",
                                             MHD_HTTP_NOT_FOUND,
                                             NULL),
    TALER_TESTING_cmd_merchant_post_otp_devices (
      "post-otp-device",
      merchant_url,
      "otp-dev",
      "my OTP device",
      "FEE4P2J",
      TALER_MCA_WITH_PRICE,
      0,
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_patch_template (
      "patch-templates-t3-nx",
      merchant_url,
      "template-3",
      "updated template",
      "otp-dev",
      GNUNET_JSON_PACK (
        GNUNET_JSON_pack_uint64 ("minimum_age", 0),
        GNUNET_JSON_pack_time_rel ("pay_duration",
                                   GNUNET_TIME_UNIT_MINUTES)),
      MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_post_templates2 (
      "post-templates-t3-amount",
      merchant_url,
      "template-amount",
      "a different template with an amount",
      NULL,
      GNUNET_JSON_PACK (
        GNUNET_JSON_pack_uint64 ("minimum_age", 0),
        GNUNET_JSON_pack_time_rel ("pay_duration",
                                   GNUNET_TIME_UNIT_MINUTES),
        GNUNET_JSON_pack_string ("amount",
                                 "EUR:4")),
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_using_templates (
      "using-templates-t1",
      "post-templates-t1",
      NULL,
      merchant_url,
      "1",
      "summary-1",
      "EUR:9.98",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_using_templates (
      "using-templates-t1-amount-missing",
      "post-templates-t1",
      NULL,
      merchant_url,
      "2",
      "summary-1",
      NULL,
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_post_using_templates (
      "using-templates-t1-summary-missing",
      "post-templates-t1",
      NULL,
      merchant_url,
      "3",
      NULL,
      "EUR:10",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_post_using_templates (
      "using-templates-t1-amount-conflict",
      "post-templates-t3-amount",
      NULL,
      merchant_url,
      "4",
      "summary-1",
      "EUR:10",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_post_using_templates (
      "using-templates-t1-amount-duplicate",
      "post-templates-t3-amount",
      NULL,
      merchant_url,
      "4",
      "summary-1",
      "EUR:4",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_pay_order ("pay-100",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "using-templates-t1",
                                          "withdraw-coin-xa;withdraw-coin-xb",
                                          "EUR:4.99",
                                          "EUR:4.99",
                                          NULL),
    TALER_TESTING_cmd_merchant_delete_template ("get-templates-empty",
                                                merchant_url,
                                                "t1",
                                                MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_delete_template ("get-templates-empty",
                                                merchant_url,
                                                "template-1",
                                                MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_using_templates (
      "post-templates-t1-deleted",
      "post-templates-t1",
      NULL,
      merchant_url,
      "0",
      "summary-1",
      "EUR:5",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_post_otp_devices (
      "post-otp-device",
      merchant_url,
      "otp-dev-2",
      "my OTP device",
      "secret",
      TALER_MCA_WITH_PRICE,
      0,
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_templates2 (
      "post-templates-with-pos-key",
      merchant_url,
      "template-key",
      "a different template with POS KEY",
      "otp-dev-2",
      GNUNET_JSON_PACK (
        GNUNET_JSON_pack_uint64 ("minimum_age", 0),
        GNUNET_JSON_pack_time_rel ("pay_duration",
                                   GNUNET_TIME_UNIT_MINUTES)),
      MHD_HTTP_NO_CONTENT),

    TALER_TESTING_cmd_merchant_post_using_templates (
      "using-templates-pos-key",
      "post-templates-with-pos-key",
      "post-otp-device",
      merchant_url,
      "1",
      "summary-1-pos",
      "EUR:9.98",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_pay_order ("pay-with-pos",
                                          merchant_url,
                                          MHD_HTTP_OK,
                                          "using-templates-pos-key",
                                          "withdraw-coin-xc;withdraw-coin-xd",
                                          "EUR:4.99",
                                          "EUR:4.99",
                                          NULL),


    TALER_TESTING_cmd_end ()
  };

  struct TALER_TESTING_Command webhooks[] = {
    TALER_TESTING_cmd_merchant_get_webhooks ("get-webhooks-empty",
                                             merchant_url,
                                             MHD_HTTP_OK,
                                             NULL),
    TALER_TESTING_cmd_merchant_post_webhooks ("post-webhooks-w1",
                                              merchant_url,
                                              "webhook-1",
                                              "Paid",
                                              MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_webhooks ("post-webhooks-w1-idem",
                                              merchant_url,
                                              "webhook-1",
                                              "Paid",
                                              MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_webhooks ("post-webhooks-w1-non-idem",
                                              merchant_url,
                                              "webhook-1",
                                              "Refund",
                                              MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_get_webhooks ("get-webhooks-w1",
                                             merchant_url,
                                             MHD_HTTP_OK,
                                             "post-webhooks-w1",
                                             NULL),
    TALER_TESTING_cmd_merchant_get_webhook ("get-webhook-w1",
                                            merchant_url,
                                            "webhook-1",
                                            MHD_HTTP_OK,
                                            "post-webhooks-w1"),
    TALER_TESTING_cmd_merchant_post_webhooks ("post-webhooks-w2",
                                              merchant_url,
                                              "webhook-2",
                                              "Paid",
                                              MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_patch_webhook ("patch-webhooks-w2",
                                              merchant_url,
                                              "webhook-2",
                                              "Refund2",
                                              "http://localhost:38188/",
                                              "POST",
                                              "Authorization:WHWOXZXPLL",
                                              "Amount",
                                              MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_webhook ("get-webhook-w2",
                                            merchant_url,
                                            "webhook-2",
                                            MHD_HTTP_OK,
                                            "patch-webhooks-w2"),
    TALER_TESTING_cmd_merchant_get_webhook ("get-webhook-nx",
                                            merchant_url,
                                            "webhook-nx",
                                            MHD_HTTP_NOT_FOUND,
                                            NULL),
    TALER_TESTING_cmd_merchant_patch_webhook ("patch-webhooks-w3-nx",
                                              merchant_url,
                                              "webhook-3",
                                              "Paid2",
                                              "https://example.com",
                                              "POST",
                                              "Authorization:WHWOXZXPLL",
                                              "Amount",
                                              MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_delete_webhook ("get-webhooks-empty",
                                               merchant_url,
                                               "w1",
                                               MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_delete_webhook ("get-webhooks-empty",
                                               merchant_url,
                                               "webhook-1",
                                               MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_end ()
  };
  struct TALER_TESTING_Command repurchase[] = {
    cmd_transfer_to_exchange (
      "create-reserve-30x",
      "EUR:30.06"),
    cmd_exec_wirewatch (
      "wirewatch-30x"),
    TALER_TESTING_cmd_check_bank_admin_transfer (
      "check_bank_transfer-30x",
      "EUR:30.06",
      payer_payto,
      exchange_payto,
      "create-reserve-30x"),
    TALER_TESTING_cmd_withdraw_amount (
      "withdraw-coin-rep",
      "create-reserve-30x",
      "EUR:5",
      0,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_orders3 (
      "post-order-repurchase-original",
      cred.cfg,
      merchant_url,
      MHD_HTTP_OK,
      "repurchase-original",
      GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MINUTES),
      GNUNET_TIME_UNIT_FOREVER_TS,
      "https://fulfillment.example.com/",
      "EUR:1.0"),
    TALER_TESTING_cmd_merchant_pay_order (
      "repurchase-pay-first",
      merchant_url,
      MHD_HTTP_OK,
      "post-order-repurchase-original",
      "withdraw-coin-rep",
      "EUR:1.00",
      "EUR:0.99",
      "repurchase-session"),
    TALER_TESTING_cmd_wallet_get_order (
      "repurchase-wallet-check-primary-order",
      merchant_url,
      "post-order-repurchase-original",
      true,
      false,
      false,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_order3 (
      "repurchase-check-primary-payment",
      merchant_url,
      "post-order-repurchase-original",
      TALER_MERCHANT_OSC_PAID,
      "repurchase-session",
      NULL,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_order3 (
      "repurchase-check-primary-payment-bad-binding",
      merchant_url,
      "post-order-repurchase-original",
      TALER_MERCHANT_OSC_CLAIMED, /* someone else has it! */
      "wrong-session",
      NULL,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_orders3 (
      "post-order-repurchase-secondary",
      cred.cfg,
      merchant_url,
      MHD_HTTP_OK,
      "repurchase-secondary",
      GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MINUTES),
      GNUNET_TIME_UNIT_FOREVER_TS,
      "https://fulfillment.example.com/",
      "EUR:1.0"),
    TALER_TESTING_cmd_merchant_get_order3 (
      "repurchase-check-secondary-payment",
      merchant_url,
      "post-order-repurchase-secondary",
      TALER_MERCHANT_OSC_UNPAID,
      "repurchase-session",
      NULL,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_order3 (
      "repurchase-check-secondary-payment",
      merchant_url,
      "post-order-repurchase-secondary",
      TALER_MERCHANT_OSC_UNPAID,
      "repurchase-session",
      "post-order-repurchase-original",
      MHD_HTTP_OK),
    TALER_TESTING_cmd_wallet_get_order2 (
      "repurchase-wallet-check-order-secondary",
      merchant_url,
      "post-order-repurchase-secondary",
      "repurchase-session",
      false,
      false,
      false,
      "post-order-repurchase-original",
      MHD_HTTP_PAYMENT_REQUIRED),
    TALER_TESTING_cmd_wallet_get_order2 (
      "repurchase-wallet-check-order-secondary-bad-session",
      merchant_url,
      "post-order-repurchase-secondary",
      "wrong-session",
      false,
      false,
      false,
      NULL,
      MHD_HTTP_PAYMENT_REQUIRED),
    TALER_TESTING_cmd_merchant_order_refund (
      "refund-repurchased",
      merchant_url,
      "refund repurchase",
      "repurchase-original",
      "EUR:1.0",
      MHD_HTTP_OK),
    TALER_TESTING_cmd_wallet_get_order2 (
      "repurchase-wallet-check-primary-order-refunded-no-session",
      merchant_url,
      "post-order-repurchase-original",
      NULL,
      true,
      true,
      true,
      "post-order-repurchase-original",
      MHD_HTTP_OK),
    TALER_TESTING_cmd_wallet_get_order2 (
      "repurchase-wallet-check-primary-order-refunded",
      merchant_url,
      "post-order-repurchase-original",
      "repurchase-session",
      true,
      true,
      true,
      "post-order-repurchase-original",
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_order3 (
      "repurchase-check-refunded",
      merchant_url,
      "post-order-repurchase-secondary",
      TALER_MERCHANT_OSC_CLAIMED,
      "repurchase-session",
      NULL,
      MHD_HTTP_OK),

    TALER_TESTING_cmd_end ()
  };

  struct TALER_TESTING_Command tokens[] = {
    /**
     * Move money to the exchange's bank account.
     */
    cmd_transfer_to_exchange ("create-reserve-tokens",
                              "EUR:20.03"),
    /**
     * Make a reserve exist, according to the previous transfer.
     */
    cmd_exec_wirewatch ("wirewatch-1"),
    TALER_TESTING_cmd_check_bank_admin_transfer ("check_bank_transfer-tokens",
                                                 "EUR:20.03",
                                                 payer_payto,
                                                 exchange_payto,
                                                 "create-reserve-tokens"),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
                                       "create-reserve-tokens",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
                                       "create-reserve-tokens",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-3",
                                       "create-reserve-tokens",
                                       "EUR:5",
                                       0,
                                       MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_tokenfamilies (
      "create-upcoming-tokenfamily",
      merchant_url,
      MHD_HTTP_NO_CONTENT,
      "subscription-upcoming",
      "Upcoming Subscription",
      "An upcoming subscription that is not valid yet.",
      NULL,
      /* In one day */
      GNUNET_TIME_absolute_to_timestamp (
        GNUNET_TIME_absolute_add (
          GNUNET_TIME_timestamp_get ().abs_time,
          GNUNET_TIME_UNIT_DAYS)),
      /* In a year */
      GNUNET_TIME_absolute_to_timestamp (
        GNUNET_TIME_absolute_add (
          GNUNET_TIME_timestamp_get ().abs_time,
          GNUNET_TIME_UNIT_YEARS)),
      GNUNET_TIME_UNIT_MONTHS,
      GNUNET_TIME_UNIT_MONTHS,
      "subscription"),
    TALER_TESTING_cmd_merchant_post_orders_choices (
      "create-order-with-upcoming-output",
      cred.cfg,
      merchant_url,
      MHD_HTTP_OK,
      "subscription-upcoming",
      "A choice in the contract",
      NULL,
      0,
      1,
      "5-upcoming-output",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      "EUR:5.0"),
    TALER_TESTING_cmd_merchant_post_tokenfamilies (
      "create-tokenfamily",
      merchant_url,
      MHD_HTTP_NO_CONTENT,
      "subscription-1",
      "Subscription",
      "A subscription.",
      NULL,
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_relative_to_timestamp
        (GNUNET_TIME_UNIT_YEARS),
      GNUNET_TIME_UNIT_MONTHS,
      GNUNET_TIME_UNIT_MONTHS,
      "subscription"),
    TALER_TESTING_cmd_merchant_post_orders_choices (
      "create-order-with-output",
      cred.cfg,
      merchant_url,
      MHD_HTTP_OK,
      "subscription-1",
      "A choice in the contract",
      NULL,
      0,
      1,
      "5-output",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      "EUR:5.0"),
    TALER_TESTING_cmd_merchant_pay_order_choices (
      "pay-order-with-output",
      merchant_url,
      MHD_HTTP_OK,
      "create-order-with-output",
      "withdraw-coin-1",
      "EUR:5",
      "EUR:4.99",
      NULL,
      0,
      NULL),
    TALER_TESTING_cmd_merchant_post_orders_choices (
      "create-order-with-input-and-output",
      cred.cfg,
      merchant_url,
      MHD_HTTP_OK,
      "subscription-1",
      "A choice in the contract",
      NULL,
      1,
      1,
      "5-input-output",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      "EUR:0.0"),
    TALER_TESTING_cmd_merchant_pay_order_choices (
      "pay-order-with-input-and-output",
      merchant_url,
      MHD_HTTP_OK,
      "create-order-with-input-and-output",
      "",
      "EUR:0",
      "EUR:0",
      NULL,
      0,
      "pay-order-with-output"),
    // TALER_TESTING_cmd_merchant_pay_order_choices ("idempotent-pay-order-with-input-and-output",
    //                                               merchant_url,
    //                                               MHD_HTTP_OK,
    //                                               "create-order-with-input-and-output",
    //                                               "",
    //                                               "EUR:0",
    //                                               "EUR:0",
    //                                               NULL,
    //                                               0,
    //                                               "pay-order-with-output"),
    TALER_TESTING_cmd_merchant_post_orders_choices (
      "create-another-order-with-input-and-output",
      cred.cfg,
      merchant_url,
      MHD_HTTP_OK,
      "subscription-1",
      "A choice in the contract",
      NULL,
      1,
      1,
      "5-input-output-2",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      "EUR:0.0"),
    TALER_TESTING_cmd_merchant_pay_order_choices ("double-spend-token",
                                                  merchant_url,
                                                  MHD_HTTP_CONFLICT,
                                                  "create-another-order-with-input-and-output",
                                                  "",
                                                  "EUR:0",
                                                  "EUR:0",
                                                  NULL,
                                                  0,
                                                  "pay-order-with-output"),
    TALER_TESTING_cmd_end ()
  };

  #ifdef HAVE_DONAU_DONAU_SERVICE_H
  const struct DONAU_BearerToken bearer = {
    .token = NULL
  };

  struct TALER_TESTING_Command donau[] = {
    TALER_TESTING_cmd_set_var (
      "donau",
      TALER_TESTING_cmd_get_donau ("get-donau",
                                   cred.cfg,
                                   true)),
    TALER_TESTING_cmd_charity_post_merchant ("post-charity",
                                             "example",
                                             "example.com",
                                             "EUR:50", // max_per_year
                                             &bearer,
                                             "create-another-order-with-input-and-output",
                                             // reusing the merchant_reference for merchant_pub
                                             MHD_HTTP_CREATED),
    TALER_TESTING_cmd_charity_post_merchant ("post-charity-idempotent",
                                             "example",
                                             "example.com",
                                             "EUR:50", // max_per_year
                                             &bearer,
                                             "create-another-order-with-input-and-output",
                                             // reusing the merchant_reference for merchant_pub
                                             MHD_HTTP_CREATED),
    TALER_TESTING_cmd_merchant_post_donau_instance (
      "post-donau-instance",
      merchant_url,
      "create-another-order-with-input-and-output",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_donau_instance (
      "post-donau-instance-idempotent",
      merchant_url,
      "create-another-order-with-input-and-output",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_donau_instances (
      "get-donau-instances-after-insert",
      merchant_url,
      1,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_sleep (
      "In this time donaukeyupdate must fetch the keys from the donau",
      1),
    TALER_TESTING_cmd_merchant_get_donau_instances (
      "get-donau-instance",
      merchant_url,
      1,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_orders_donau (
      "create-donau-order",
      cred.cfg,
      merchant_url,
      MHD_HTTP_OK,
      "donau",
      GNUNET_TIME_UNIT_ZERO_TS,
      GNUNET_TIME_UNIT_FOREVER_TS,
      "EUR:1"),
    TALER_TESTING_cmd_merchant_pay_order_donau (
      "pay-donau-order",
      merchant_url,
      MHD_HTTP_OK,
      "create-donau-order",
      "withdraw-coin-3",
      "EUR:1", /* full amount */
      "EUR:0.99", /* amount without fees */
      "EUR:1", /* donation amount */
      NULL,
      0,
      "post-charity",
      2025,
      "7560001010000",
      "1234"
      ),
    TALER_TESTING_cmd_merchant_delete_donau_instance (
      "delete-donau-instance",
      merchant_url,
      1,
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_donau_instances (
      "get-donau-instances-after-delete",
      merchant_url,
      0,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_end ()
  };
  #endif

  struct TALER_TESTING_Command commands[] = {
    /* general setup */
    TALER_TESTING_cmd_run_fakebank (
      "run-fakebank",
      cred.cfg,
      "exchange-account-exchange"),
    #ifdef HAVE_DONAU_DONAU_SERVICE_H
    TALER_TESTING_cmd_system_start (
      "start-taler",
      config_file,
      "-emaDZ",
      "-u", "exchange-account-exchange",
      "-r", "merchant-exchange-test",
      NULL),
    #else
    TALER_TESTING_cmd_system_start (
      "start-taler",
      config_file,
      "-ema",
      "-u", "exchange-account-exchange",
      "-r", "merchant-exchange-test",
      NULL),
    #endif
    TALER_TESTING_cmd_get_exchange (
      "get-exchange",
      cred.cfg,
      NULL,
      true,
      true),
    TALER_TESTING_cmd_batch (
      "orders-id",
      get_private_order_id),
    TALER_TESTING_cmd_config (
      "config",
      merchant_url,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_instances (
      "instances-empty",
      merchant_url,
      MHD_HTTP_OK,
      NULL),
    TALER_TESTING_cmd_merchant_post_instances (
      "instance-create-default-setup",
      merchant_url,
      "admin",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_account (
      "instance-create-default-account",
      merchant_url,
      merchant_payto,
      NULL, NULL,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_post_instances (
      "instance-create-i1",
      merchant_url,
      "i1",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_instances (
      "instances-get-i1",
      merchant_url,
      MHD_HTTP_OK,
      "instance-create-i1",
      "instance-create-default-setup",
      NULL),
    TALER_TESTING_cmd_merchant_get_instance (
      "instances-get-i1",
      merchant_url,
      "i1",
      MHD_HTTP_OK,
      "instance-create-i1"),
    TALER_TESTING_cmd_merchant_patch_instance (
      "instance-patch-i1",
      merchant_url,
      "i1",
      "bob-the-merchant",
      json_pack ("{s:s}",
                 "street",
                 "bobstreet"),
      json_pack ("{s:s}",
                 "street",
                 "bobjuryst"),
      true,
      GNUNET_TIME_UNIT_ZERO, /* wire transfer */
      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
                                     2), /* small pay delay */
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_instance (
      "instances-get-i1-2",
      merchant_url,
      "i1",
      MHD_HTTP_OK,
      "instance-patch-i1"),
    TALER_TESTING_cmd_merchant_get_instance (
      "instances-get-i2-nx",
      merchant_url,
      "i2",
      MHD_HTTP_NOT_FOUND,
      NULL),
    TALER_TESTING_cmd_merchant_post_instances2 (
      "instance-create-ACL",
      merchant_url,
      "i-acl",
      "controlled instance",
      json_pack ("{s:s}", "city",
                 "shopcity"),
      json_pack ("{s:s}", "city",
                 "lawyercity"),
      true,
      GNUNET_TIME_UNIT_ZERO, /* wire transfer */
      GNUNET_TIME_UNIT_SECONDS, /* pay delay */
      // FIXME: change this back once
      // we have a update auth test CMD
      // RFC_8959_PREFIX "EXAMPLE",
      NULL,
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_patch_instance (
      "instance-patch-ACL",
      merchant_url,
      "i-acl",
      "controlled instance",
      json_pack ("{s:s}",
                 "street",
                 "bobstreet"),
      json_pack ("{s:s}",
                 "street",
                 "bobjuryst"),
      true,
      GNUNET_TIME_UNIT_ZERO, /* wire transfer */
      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
                                     2), /* small pay delay */
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_instances (
      "instance-create-i2",
      merchant_url,
      "i2",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_instances (
      "instance-create-i2-idem",
      merchant_url,
      "i2",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_delete_instance (
      "instance-delete-i2",
      merchant_url,
      "i2",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_instance (
      "instances-get-i2-post-deletion",
      merchant_url,
      "i2",
      MHD_HTTP_NOT_FOUND,
      NULL),
    TALER_TESTING_cmd_merchant_purge_instance (
      "instance-delete-then-purge-i2",
      merchant_url,
      "i2",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_purge_instance (
      "instance-purge-i1",
      merchant_url,
      "i1",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_delete_instance (
      "instance-purge-then-delete-i1",
      merchant_url,
      "i1",
      MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_purge_instance (
      "instance-purge-i-acl-middle",
      merchant_url,
      "i-acl",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_purge_instance (
      "instance-purge-default-middle",
      merchant_url,
      "admin",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_instances (
      "instance-create-default-after-purge",
      merchant_url,
      "admin",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_account (
      "instance-create-default-account-after-purge",
      merchant_url,
      merchant_payto,
      NULL, NULL,
      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_products (
      "get-products-empty",
      merchant_url,
      MHD_HTTP_OK,
      NULL),
    TALER_TESTING_cmd_merchant_post_products (
      "post-products-p1",
      merchant_url,
      "product-1",
      "a product",
      "EUR:1",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_products (
      "post-products-p1-idem",
      merchant_url,
      "product-1",
      "a product",
      "EUR:1",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_post_products (
      "post-products-p1-non-idem",
      merchant_url,
      "product-1",
      "a different product",
      "EUR:1",
      MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_merchant_get_products (
      "get-products-p1",
      merchant_url,
      MHD_HTTP_OK,
      "post-products-p1",
      NULL),
    TALER_TESTING_cmd_merchant_get_product (
      "get-product-p1",
      merchant_url,
      "product-1",
      MHD_HTTP_OK,
      "post-products-p1"),
    TALER_TESTING_cmd_merchant_post_products2 (
      "post-products-img",
      merchant_url,
      "product-img",
      "product with image",
      json_pack ("{s:s}", "en", "product with image"),
      "test-unit",
      "EUR:1",
      "data:image/jpeg;base64,RAWDATA",
      json_array (),
      4,
      0,
      json_pack ("{s:s}", "street", "my street"),
      GNUNET_TIME_UNIT_ZERO_TS,
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_get_product_image (
      "get-product-image-img",
      merchant_url,
      "post-products-img",
      "5831ca012639a29df949c3b1e5cd436bac0a5b26a5340ccd95df394b7a8742d6",
      MHD_HTTP_OK),
    TALER_TESTING_cmd_get_product_image (
      "get-product-image-invalid",
      merchant_url,
      NULL,
      "not-a-valid-hash",
      MHD_HTTP_BAD_REQUEST),
    TALER_TESTING_cmd_get_product_image (
      "get-product-image-empty",
      merchant_url,
      NULL,
      "",
      MHD_HTTP_BAD_REQUEST),
    TALER_TESTING_cmd_get_product_image (
      "get-product-image-not-found",
      merchant_url,
      NULL,
      "5831ca012639a29df949c3b1e5cd436bac0a5b26a5340ccd95df394b7a8742d7",
      MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_post_products (
      "post-products-p2",
      merchant_url,
      "product-2",
      "a product",
      "EUR:1",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_patch_product (
      "patch-products-p2",
      merchant_url,
      "product-2",
      "another product",
      json_pack ("{s:s}", "en", "text"),
      "kg",
      "EUR:1",
      "data:image/jpeg;base64,RAWDATA",
      json_array (),
      40,
      0,
      json_pack ("{s:s}",
                 "street",
                 "pstreet"),
      GNUNET_TIME_relative_to_timestamp (
        GNUNET_TIME_UNIT_MINUTES),
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_get_product (
      "get-product-p2",
      merchant_url,
      "product-2",
      MHD_HTTP_OK,
      "patch-products-p2"),
    TALER_TESTING_cmd_merchant_get_product (
      "get-product-nx",
      merchant_url,
      "product-nx",
      MHD_HTTP_NOT_FOUND,
      NULL),
    TALER_TESTING_cmd_merchant_patch_product (
      "patch-products-p3-nx",
      merchant_url,
      "product-3",
      "nx updated product",
      json_pack ("{s:s}", "en", "text"),
      "kg",
      "EUR:1",
      "data:image/jpeg;base64,RAWDATA",
      json_array (),
      40,
      0,
      json_pack ("{s:s}",
                 "street",
                 "pstreet"),
      GNUNET_TIME_relative_to_timestamp (
        GNUNET_TIME_UNIT_MINUTES),
      MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_delete_product (
      "get-products-empty",
      merchant_url,
      "p1",
      MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_delete_product (
      "get-products-empty",
      merchant_url,
      "product-1",
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_lock_product (
      "lock-product-p2",
      merchant_url,
      "product-2",
      GNUNET_TIME_UNIT_MINUTES,
      2,
      MHD_HTTP_NO_CONTENT),
    TALER_TESTING_cmd_merchant_lock_product (
      "lock-product-nx",
      merchant_url,
      "product-nx",
      GNUNET_TIME_UNIT_MINUTES,
      2,
      MHD_HTTP_NOT_FOUND),
    TALER_TESTING_cmd_merchant_lock_product (
      "lock-product-too-much",
      merchant_url,
      "product-2",
      GNUNET_TIME_UNIT_MINUTES,
      39,
      MHD_HTTP_GONE),
    TALER_TESTING_cmd_merchant_delete_product (
      "delete-product-locked",
      merchant_url,
      "product-2",
      MHD_HTTP_CONFLICT),
    TALER_TESTING_cmd_batch ("pay",
                             pay),
    TALER_TESTING_cmd_batch ("double-spending",
                             double_spending),
    TALER_TESTING_cmd_batch ("pay-again",
                             pay_again),
    TALER_TESTING_cmd_batch ("pay-abort",
                             pay_abort),
    TALER_TESTING_cmd_batch ("refund",
                             refund),
    TALER_TESTING_cmd_batch ("templates",
                             templates),
    TALER_TESTING_cmd_batch ("webhooks",
                             webhooks),
    TALER_TESTING_cmd_batch ("auth",
                             auth),
    TALER_TESTING_cmd_batch ("repurchase",
                             repurchase),
    TALER_TESTING_cmd_batch ("tokens",
                             tokens),

    #ifdef HAVE_DONAU_DONAU_SERVICE_H
    TALER_TESTING_cmd_batch ("donau",
                             donau),
    #endif

    TALER_TESTING_cmd_merchant_get_statisticsamount ("stats-refund",
                                                     merchant_url,
                                                     "refunds-granted", 6, 0,
                                                     MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_statisticscounter ("stats-tokens-issued",
                                                      merchant_url,
                                                      "tokens-issued", 6, 0,
                                                      MHD_HTTP_OK),
    TALER_TESTING_cmd_merchant_get_statisticscounter ("stats-tokens-used",
                                                      merchant_url,
                                                      "tokens-used", 6, 0,
                                                      MHD_HTTP_OK),

    /**
     * End the suite.
     */
    TALER_TESTING_cmd_end ()
  };

  TALER_TESTING_run (is,
                     commands);
}


int
main (int argc,
      char *const *argv)
{
  {
    char *cipher;

    cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
    GNUNET_assert (NULL != cipher);
    GNUNET_asprintf (&config_file,
                     "test_merchant_api-%s.conf",
                     cipher);
    GNUNET_free (cipher);
  }
  payer_payto.full_payto =
    (char *) "payto://x-taler-bank/localhost/" USER_ACCOUNT_NAME
    "?receiver-name=" USER_ACCOUNT_NAME;
  exchange_payto.full_payto =
    (char *) "payto://x-taler-bank/localhost/" EXCHANGE_ACCOUNT_NAME
    "?receiver-name="
    EXCHANGE_ACCOUNT_NAME;
  merchant_payto.full_payto =
    (char *) "payto://x-taler-bank/localhost/" MERCHANT_ACCOUNT_NAME
    "?receiver-name=" MERCHANT_ACCOUNT_NAME;
  merchant_url = "http://localhost:8080/";
  GNUNET_asprintf (&merchant_url_i1a,
                   "%sinstances/i1a/",
                   merchant_url);
  return TALER_TESTING_main (argv,
                             "INFO",
                             config_file,
                             "exchange-account-exchange",
                             TALER_TESTING_BS_FAKEBANK,
                             &cred,
                             &run,
                             NULL);
}


/* end of test_merchant_api.c */
