commit fb7e96e9cc1fcf2a78cec4fa216954e7d83670a9
parent 6f5288a2457054e31f1710865c759895acbead81
Author: Christian Grothoff <christian@grothoff.org>
Date: Sat, 30 Apr 2016 10:58:54 +0200
Merge branch 'master' of git+ssh://taler.net/var/git/merchant
Diffstat:
24 files changed, 708 insertions(+), 316 deletions(-)
diff --git a/bootstrap b/bootstrap
@@ -1,2 +1,12 @@
#!/bin/sh
+
+if ! git --version >/dev/null; then
+ echo "git not installed"
+ exit 1
+fi
+
+echo "$0: Updating submodules"
+echo | git submodule update --init
+
+echo "$0: Running autoreconf"
autoreconf -if
diff --git a/configure.ac b/configure.ac
@@ -162,6 +162,16 @@ else
AC_DEFINE([HAVE_LIBCURL],[1],[Have CURL])
fi
+
+# Check for curl/curl.h and gnurl/curl.h so we can use #ifdef
+# HAVE_CURL_CURL_H later (the above LIBCURL_CHECK_CONFIG accepted
+# *either* header set).
+AC_CHECK_HEADERS([curl/curl.h],,
+ curl=false
+ AC_CHECK_HEADERS([gnurl/curl.h],,
+ gnurl=false))
+
+
# libgnurl
if test "x$gnurl" = "x0"
then
diff --git a/copylib/util.php b/copylib/util.php
@@ -42,7 +42,7 @@ function article_state_to_str($article_state){
}
function log_string($str){
- file_put_contents("/tmp/blog.dbg", $str . "\n", FILE_APPEND);
+ file_put_contents("/tmp/frontend.dbg", $str . "\n", FILE_APPEND);
}
function get_full_uri(){
diff --git a/examples/blog/README b/examples/blog/README
@@ -0,0 +1,21 @@
+
+This guide is for running the 'blog' example, which require PHP and is
+served via nginx.
+
+Assuming that your system has a working PHP, the following
+pecl packages are needed (the preferred way to install them
+is with the pecl package manager, and not via your distribution's):
+
+pecl_http
+propro
+raphf
+
+As for the versioning, our working setup is using:
+
+PHP 5.6
+pecl_http 2.4.3 stable
+propro 1.0.0 stable
+raphf 1.1.0 stable
+
+You can now refer to the nginx configuration examples (nginx_example.conf)
+in this directory
diff --git a/examples/blog/essay_fulfillment.php b/examples/blog/essay_fulfillment.php
@@ -26,7 +26,7 @@
return;
}
session_start();
-
+ //syslog($LOG_ERR, "merchant: official log system");
$payments = &pull($_SESSION, 'payments', array());
$my_payment = &pull($payments, $article, false);
@@ -43,11 +43,9 @@
$js_code = "get_contract(\"$article\");";
$cc_page = template("./essay_cc-form.html", array('article' => $article, 'jscode' => $js_code));
echo $cc_page;
- log_string("cnt blog");
return;
}
- log_string("restoring blog");
- //log_string("state: " . print_r($_SESSION, true));
+ // using deeplink (whether 1st time or not)
// restore contract
$now = new DateTime();
$now->setTimestamp(intval($timestamp));
@@ -78,14 +76,14 @@
}
$hc = json_decode($resp->body->toString(), true)['H_contract'];
$my_payment['hc'] = $hc;
- log_string("sending payment event");
+ syslog($LOG_INFO, "sending payment event");
$js_code = "taler.executePayment(\"$hc\", \"$pay_url\", \"$offering_url\");";
$cc_page = template("./essay_cc-form.html", array('article' => $article, 'jscode' => $js_code));
echo $cc_page;
return;
}
// control here == article payed
- log_string("arti blog");
+ syslog($LOG_INFO, "showing article");
$article = get_article($article);
echo $article;
?>
diff --git a/examples/blog/essay_pay.php b/examples/blog/essay_pay.php
@@ -19,7 +19,7 @@
include("../../copylib/util.php");
include("./blog_lib.php");
- log_string("paying");
+ syslog($LOG_INFO, "paying");
$article = get($_GET["article"]);
if (empty($article)){
@@ -56,7 +56,6 @@
echo $json;
die();
}
- // FIXME put some control below
// with the article that's going to be payed
$resp = give_to_backend("backend/pay",
diff --git a/examples/blog/nginx_example.conf b/examples/blog/nginx_example.conf
@@ -0,0 +1,40 @@
+server {
+ listen 80; ## listen for ipv4; this line is default and implied
+ # listen [::]:80 default_server ipv6only=on; ## listen for ipv6
+ server_name example.com;
+
+ # WARNING the 'blog' directory can't be freely moved in your filesystem
+ # because it includes some PHP files via relative path (../../copylib)
+ root /path/to/merchant/examples/blog;
+ index index.php;
+
+ # Make site accessible from http://localhost/
+
+ location / {
+ try_files $uri $uri/ =404;
+ rewrite /taler/pay /pay.php;
+ rewrite /taler/contract /generate_taler_contract.php;
+
+ }
+
+ location /fullfillment {
+ rewrite /(.*) /$1.php;
+
+ }
+
+ location ~ \.php$ {
+
+ fastcgi_pass unix:/var/run/php5-fpm.sock;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ include fastcgi_params;
+
+ }
+
+ location /backend {
+ rewrite /backend/(.*) /$1 break;
+ proxy_pass http://127.0.0.1:19966;
+ proxy_redirect off;
+ proxy_set_header Host $host;
+ }
+
+}
diff --git a/examples/shop/README b/examples/shop/README
@@ -1,3 +1,30 @@
+INSTALL
+=======
+
+This guide is for running the 'shop' example, which require PHP and is
+served via nginx.
+
+Assuming that your system has a working PHP, the following
+pecl packages are needed (the preferred way to install them
+is with the pecl package manager, and not via your distribution's):
+
+pecl_http
+propro
+raphf
+
+As for the versioning, our working setup is using:
+
+PHP 5.6
+pecl_http 2.4.3 stable
+propro 1.0.0 stable
+raphf 1.1.0 stable
+
+You can now refer to the nginx configuration examples (nginx_example.conf)
+in this directory
+
+FILES
+=====
+
This directory contains the files implementing the frontend of the new merchant architecture.
Only tested on nginx. To run the website, it suffices to have all the .php and .html files
diff --git a/examples/shop/fulfillment.php b/examples/shop/fulfillment.php
@@ -28,8 +28,7 @@
}
</script>
</head>
-<body style="display:none;">
-
+<body style="display:none;">
<header>
<div id="logo">
<svg height="100" width="100">
@@ -49,10 +48,13 @@
<?php
include '../../copylib/util.php';
+include "../../copylib/merchants.php";
-$hc = get($_GET["uuid"]);
+$receiver = get($_GET["receiver"]);
+$now = new DateTime();
+$now->setTimestamp(intval(get($_GET["timestamp"])));
-if (empty($hc)) {
+if (empty($receiver)) {
http_response_code(400);
echo "<p>Bad request (UUID missing)</p>";
return;
@@ -60,32 +62,50 @@ if (empty($hc)) {
session_start();
-$payments = get($_SESSION['payments'], array());
-$my_payment = get($payments[$hc]);
+$payments = &pull($_SESSION, 'payments', array());
+$my_payment = &pull($payments, $receiver, array());
// This will keep the query parameters.
$pay_url = url_rel("pay.php");
+$offering_url = url_rel("index.php", true);
+
+if (array() === $my_payment || true !== get($my_payment["is_payed"], false)) {
+ // restore contract
+
+ $contract = generate_contract(array(
+ "amount_value" => intval($_GET['aval']),
+ "amount_fraction" => intval($_GET['afrac']),
+ "currency" => $_GET['acurr'],
+ "refund_delta" => 'P3M',
+ "transaction_id" => intval($_GET['tid']),
+ "description" => "Donation to " . $receiver,
+ "product_id" => "unused",
+ "correlation_id" => "",
+ "merchant_name" => "Kudos Inc.",
+ "taxes" => array(),
+ "now" => $now,
+ "fulfillment_url" => get_full_uri())
+ );
+
+ $resp = give_to_backend("backend/contract", $contract);
+ if ($resp->getResponseCode() != 200){
+ echo json_encode(array(
+ 'error' => "internal error",
+ 'hint' => "failed to regenerate contract",
+ 'detail' => $resp->body->toString()
+ ), JSON_PRETTY_PRINT);
+ return;
+ }
-$offering_url = url_rel("checkout.php", true);
-
-if (null === $my_payment) {
- // TODO: show spinner after timeout
- echo "<p>you do not have the session state for this contract: " . $hc . "</p>";
- echo "<p>Asking the wallet to re-execute it ... </p>";
- echo "<script>taler.executePayment('$hc', '$pay_url', '$offering_url');</script>";
- return;
-}
-
-if (true !== get($my_payment["is_payed"], false)) {
- // TODO: show spinner after timeout
+ $hc = json_decode($resp->body->toString(), true)['H_contract'];
+ $my_payment['is_payed'] = false;
+ $my_payment['hc'] = $hc;
echo "<p>you have not payed for this contract: " . $hc . "</p>";
echo "<p>Asking the wallet to re-execute it ... </p>";
- echo "<script>taler.executePayment('$hc', '$pay_url');</script>";
+ echo "<script>taler.executePayment('$hc', '$pay_url', '$offering_url');</script>";
return;
}
-$receiver = $my_payment["receiver"];
-
$news = false;
switch ($receiver) {
case "Taler":
diff --git a/examples/shop/generate_taler_contract.php b/examples/shop/generate_taler_contract.php
@@ -47,14 +47,13 @@ $now = new DateTime('now');
// Include all information so we can
// restore the contract without storing it
$fulfillment_url = url_rel("fulfillment.php")
- . '?uuid=${H_contract}'
+ . '?timestamp=' . $now->getTimestamp()
. '&receiver=' . urlencode($receiver)
. '&aval=' . urlencode($amount_value)
. '&afrac=' . urlencode($amount_fraction)
. '&acurr=' . urlencode($currency)
. '&tid=' . $transaction_id;
-
$contract = generate_contract(array(
"amount_value" => $amount_value,
"amount_fraction" => $amount_fraction,
@@ -62,7 +61,7 @@ $contract = generate_contract(array(
"refund_delta" => 'P3M',
"transaction_id" => $transaction_id,
"description" => $desc,
- "product_id" => $p_id,
+ "product_id" => "unused",
"correlation_id" => "",
"merchant_name" => "Kudos Inc.",
"taxes" => array(),
@@ -70,69 +69,15 @@ $contract = generate_contract(array(
"fulfillment_url" => $fulfillment_url)
);
+file_put_contents("/tmp/ff.link", $fulfillment_url . "\r\n");
-// pack the JSON for the contract
-
-/*
-$contract = array(
- 'fulfillment_url' => $fulfillment_url,
- 'amount' => array(
- 'value' => $amount_value,
- 'fraction' => $amount_fraction,
- 'currency' => $currency
- ),
- 'max_fee' => array(
- 'value' => 3,
- 'fraction' => 01010,
- 'currency' => $currency
- ),
- 'transaction_id' => $transaction_id,
- 'products' => array(
- array(
- 'description' => $desc,
- 'quantity' => 1,
- 'price' => array (
- 'value' => $amount_value,
- 'fraction' => $amount_fraction,
- 'currency' => $currency
- ),
- 'product_id' => $p_id,
- )
- ),
- 'timestamp' => "/Date(" . $now->getTimestamp() . ")/",
- 'expiry' => "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/",
- 'refund_deadline' => "/Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")/",
- 'merchant' => array(
- 'name' => 'Kudos Inc.'
- )
-);
-*/
-
-$json = json_encode(array(
- 'contract' => $contract
-), JSON_PRETTY_PRINT);
-
-$url = url_join("http://".$_SERVER["HTTP_HOST"], "backend/contract");
-
-$req = new http\Client\Request("POST",
- $url,
- array ("Content-Type" => "application/json"));
-
-$req->getBody()->append($contract);
-
-// Execute the HTTP request
-$client = new http\Client;
-$client->enqueue($req)->send();
-
-// Fetch the response
-$resp = $client->getResponse();
-$status_code = $resp->getResponseCode();
+$resp = give_to_backend("backend/contract", $contract);
// Our response code is the same we got from the backend:
-http_response_code($status_code);
+http_response_code($resp->getResponseCode());
// Now generate our body
-if ($status_code != 200)
+if ($resp->getResponseCode() != 200)
{
echo json_encode(array(
'error' => "internal error",
@@ -142,14 +87,8 @@ if ($status_code != 200)
}
else
{
+ # no state here
$got_json = json_decode($resp->body->toString(), true);
- $hc = $got_json["H_contract"];
-
- $payments = &pull($_SESSION, "payments", array());
- $payments[$hc] = array(
- 'receiver' => $receiver,
- );
-
echo json_encode ($got_json, JSON_PRETTY_PRINT);
}
?>
diff --git a/examples/shop/index.php b/examples/shop/index.php
@@ -16,6 +16,7 @@
-->
<?php
require_once "../../copylib/config.php";
+session_destroy();
?>
<html lang="en">
<head>
diff --git a/examples/shop/pay.php b/examples/shop/pay.php
@@ -18,33 +18,52 @@
include '../../copylib/util.php';
-$hc = get($_GET["uuid"]);
-if (empty($hc))
+// so we won't generate a response for the wrong receiver.
+$receiver = get($_GET["receiver"]);
+if (empty($receiver))
{
http_response_code(400);
echo json_encode(array(
"error" => "missing parameter",
- "parameter" => "uuid"
+ "parameter" => "receiver"
));
return;
}
-// TODO: check if contract body matches URL parameters,
-// so we won't generate a response for the wrong receiver.
-$receiver = get($_GET["receiver"]);
-if (empty($receiver))
+session_start();
+$payments = &pull($_SESSION, "payments", array());
+
+if (!isset($payments[$receiver]))
{
http_response_code(400);
echo json_encode(array(
- "error" => "missing parameter",
- "parameter" => "receiver"
+ "error" => "no payment session active"
));
return;
}
+echo 'recognized session';
+echo 'with hash ' . $payments[$receiver]['hc'];
+die();
+
$post_body = file_get_contents('php://input');
$deposit_permission = json_decode ($post_body, true);
+// Check if the receiver is actually *mentioned* in the contract
+if ($payments[$receiver]['hc'] != $deposit_permission['H_contract']) {
+
+ $json = json_encode(
+ array(
+ "error" => "ill behaved wallet",
+ "status" => 400,
+ "detail" => "deposit permission mismatches with reconstructed contract"
+ )
+ );
+ echo $json;
+ die();
+}
+
+
/* Craft the HTTP request, note that the backend
could be on an entirely different machine if
desired. */
@@ -57,7 +76,7 @@ $req = new http\Client\Request("POST",
array("Content-Type" => "application/json"));
$req->getBody()->append (json_encode ($deposit_permission));
-// Execute the HTTP request
+// Execute the HTTP request to the backend
$client = new http\Client;
$client->enqueue($req)->send();
@@ -80,12 +99,7 @@ if ($status_code != 200)
die();
}
-session_start();
-
$payments = &pull($_SESSION, "payments", array());
-$payments[$hc] = array(
- 'receiver' => $receiver,
- 'is_payed' => true
-);
+$payments[$receiver]['is_payed'] = true;
?>
diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf
@@ -5,11 +5,16 @@
# General settings for the backend.
[merchant]
+SERVE = tcp
+
# Which HTTP port does the backend listen on?
PORT = 9966
+UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http
+UNIXPATH_MODE = 660
+
# Where does the backend store the merchant's private key?
-KEYFILE = merchant.priv
+KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
# Which database backend do we use?
DB = postgres
@@ -19,14 +24,6 @@ DB = postgres
# Must match the specification given in [merchant-wireformat]
-[exchange-taler]
-# FIXME: should use URI, need https! Avoid PORT (other than as part of URI)!
-# FIXME: is this used?
-HOSTNAME = exchange.demo.taler.net
-PORT = 80
-PUBKEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0
-
-
# Configuration for postgres database.
[merchantdb-postgres]
CONFIG = postgres:///talermerchant
@@ -34,6 +31,7 @@ CONFIG = postgres:///talermerchant
# Configuration of our bank account details
[merchant-wireformat]
+TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json
# The values in this section must match the "WIREFORMAT" given in [merchant]:
# * for SEPA:
# IBAN = DE67830654080004822650
diff --git a/src/backend/myconf.sh b/src/backend/myconf.sh
@@ -1,3 +0,0 @@
-#!/bin/sh
-# this file is in the public domain
-./configure CFLAGS='-I/usr/include/postgresql'
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
@@ -19,6 +19,7 @@
* communication with the exchange
* @author Marcello Stanisci
* @author Christian Grothoff
+ * @author Florian Dold
*/
#include "platform.h"
#include <microhttpd.h>
@@ -40,6 +41,12 @@
/**
+ * Backlog for listen operation on unix-domain sockets.
+ */
+#define UNIX_BACKLOG 500
+
+
+/**
* Our wire format details in JSON format (with salt).
*/
struct json_t *j_wire;
@@ -105,6 +112,17 @@ struct TALER_MERCHANTDB_Plugin *db;
*/
static struct MHD_Daemon *mhd;
+/**
+ * Path for the unix domain-socket
+ * to run the daemon on.
+ */
+static char *serve_unixpath;
+
+/**
+ * File mode for unix-domain socket.
+ */
+static mode_t unixpath_mode;
+
/**
* A client has requested the given url using the given method
@@ -483,26 +501,14 @@ run (void *cls,
return;
}
if (GNUNET_SYSERR ==
- GNUNET_CONFIGURATION_get_value_number (config,
- "merchant",
- "PORT",
- &port))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "merchant",
- "PORT");
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
- if (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_string (config,
- "merchant",
- "CURRENCY",
+ "taler",
+ "currency",
&TMH_merchant_currency_string))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "merchant",
- "CURRENCY");
+ "taler",
+ "currency");
GNUNET_SCHEDULER_shutdown ();
return;
}
@@ -576,15 +582,175 @@ run (void *cls,
return;
}
- mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME,
- port,
- NULL, NULL,
- &url_handler, NULL,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback, NULL,
- MHD_OPTION_CONNECTION_TIMEOUT,
- (unsigned int) 10 /* 10s */,
- MHD_OPTION_END);
+
+ {
+ const char *choices[] = {"tcp", "unix"};
+ const char *serve_type;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_choice (config,
+ "merchant",
+ "serve",
+ choices,
+ &serve_type))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "merchant",
+ "serve",
+ "serve type required");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ // FIXME: refactor two calls to MHD_start_daemon
+ // into one, using an options array instead of varags
+
+ if (0 == strcmp (serve_type, "tcp"))
+ {
+
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_number (config,
+ "merchant",
+ "PORT",
+ &port))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "merchant",
+ "PORT");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME,
+ port,
+ NULL, NULL,
+ &url_handler, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT,
+ (unsigned int) 10 /* 10s */,
+ MHD_OPTION_END);
+ }
+ else if (0 == strcmp (serve_type, "unix"))
+ {
+ struct sockaddr_un *un;
+ char *mode;
+ struct GNUNET_NETWORK_Handle *nh;
+ int fh;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (config,
+ "merchant",
+ "unixpath",
+ &serve_unixpath))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "merchant",
+ "unixpath",
+ "unixpath required");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ if (strlen (serve_unixpath) >= sizeof (un->sun_path))
+ {
+ fprintf (stderr,
+ "Invalid configuration: unix path too long\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "merchant",
+ "unixpath_mode",
+ &mode))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "merchant",
+ "unixpath_mode",
+ "unixpath_mode required");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ errno = 0;
+ unixpath_mode = (mode_t) strtoul (mode, NULL, 8);
+ if (0 != errno)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "merchant",
+ "unixpath_mode",
+ "unixpath_mode must be octal number");
+ GNUNET_free (mode);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_free (mode);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Creating listen socket '%s' with mode %o\n",
+ serve_unixpath, unixpath_mode);
+
+ if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (serve_unixpath))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "mkdir",
+ serve_unixpath);
+ }
+
+ un = GNUNET_new (struct sockaddr_un);
+ un->sun_family = AF_UNIX;
+ strncpy (un->sun_path, serve_unixpath, sizeof (un->sun_path) - 1);
+
+ GNUNET_NETWORK_unix_precheck (un);
+
+ if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0)))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "create(for AF_UNIX)");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh, (void *) un, sizeof (struct sockaddr_un)))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind(for AF_UNIX)");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK != GNUNET_NETWORK_socket_listen (nh, UNIX_BACKLOG))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen(for AF_UNIX)");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ fh = GNUNET_NETWORK_get_fd (nh);
+
+ if (0 != chmod (serve_unixpath, unixpath_mode))
+ {
+ fprintf (stderr, "chmod failed: %s\n", strerror (errno));
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME,
+ 0,
+ NULL, NULL,
+ &url_handler, NULL,
+ MHD_OPTION_LISTEN_SOCKET, fh,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT,
+ (unsigned int) 10 /* 10s */,
+ MHD_OPTION_END);
+ GNUNET_NETWORK_socket_free_memory_only_ (nh);
+ }
+ else
+ {
+ // not reached
+ GNUNET_assert (0);
+ }
+ }
+
if (NULL == mhd)
{
GNUNET_break (0);
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
@@ -104,12 +104,14 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
res = TMH_PARSE_json_data (connection,
jcontract,
spec);
+ printf ("parsed\n");
if (GNUNET_NO == res)
return MHD_YES;
if (GNUNET_SYSERR == res)
return TMH_RESPONSE_reply_external_error (connection,
"contract request malformed");
+ printf ("beyond\n");
/* add fields to the contract that the backend should provide */
json_object_set (jcontract,
"exchanges",
diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c
@@ -27,7 +27,7 @@
/**
* How often do we retry fetching /keys?
*/
-#define KEYS_RETRY_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
+#define KEYS_RETRY_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
/**
@@ -141,12 +141,13 @@ struct Exchange
/**
* Context for all exchange operations (useful to the event loop)
*/
-static struct GNUNET_CURL_Context *ctx;
+static struct GNUNET_CURL_Context *merchant_curl_ctx;
/**
- * Task we use to drive the interaction with this exchange.
+ * Task that pumps events into curl as soon as any
+ * curl-related events are available.
*/
-static struct GNUNET_SCHEDULER_Task *poller_task;
+static struct GNUNET_SCHEDULER_Task *merchant_curl_task;
/**
* Head of exchanges we know about.
@@ -164,6 +165,15 @@ static struct Exchange *exchange_tail;
json_t *trusted_exchanges;
+/* forward declarations */
+
+static void
+merchant_curl_cb (void *cls);
+
+static void
+retry_exchange (void *cls);
+
+
/**
* Function called with information about who is auditing
* a particular exchange and what key the exchange is using.
@@ -196,8 +206,17 @@ keys_mgmt_cb (void *cls,
exchange->uri);
TALER_EXCHANGE_disconnect (exchange->conn);
exchange->conn = NULL;
- exchange->pending = GNUNET_SYSERR; /* failed hard */
exchange->retry_time = GNUNET_TIME_relative_to_absolute (KEYS_RETRY_FREQ);
+ /* Always retry trusted exchanges in the background, so that we don't have
+ * to wait for a customer to trigger it and thus delay his response */
+ if (GNUNET_YES == exchange->trusted)
+ {
+ GNUNET_SCHEDULER_add_delayed (KEYS_RETRY_FREQ, retry_exchange, exchange);
+ }
+ else
+ {
+ exchange->pending = GNUNET_SYSERR; /* failed hard */
+ }
}
while (NULL != (fo = exchange->fo_head))
{
@@ -213,12 +232,30 @@ keys_mgmt_cb (void *cls,
/**
+ * Restart the task that pumps events into curl
+ * with updated file descriptors.
+ */
+static void
+merchant_curl_refresh ()
+{
+ if (NULL != merchant_curl_task)
+ {
+ GNUNET_SCHEDULER_cancel (merchant_curl_task);
+ merchant_curl_task = NULL;
+ }
+
+ merchant_curl_task = GNUNET_SCHEDULER_add_now (&merchant_curl_cb,
+ NULL);
+}
+
+
+/**
* Task that runs the exchange's event loop using the GNUnet scheduler.
*
* @param cls a `struct Exchange *`
*/
static void
-context_task (void *cls)
+merchant_curl_cb (void *cls)
{
long timeout;
int max_fd;
@@ -231,14 +268,14 @@ context_task (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "In exchange context polling task\n");
- poller_task = NULL;
- GNUNET_CURL_perform (ctx);
+ merchant_curl_task = NULL;
+ GNUNET_CURL_perform (merchant_curl_ctx);
max_fd = -1;
timeout = -1;
FD_ZERO (&read_fd_set);
FD_ZERO (&write_fd_set);
FD_ZERO (&except_fd_set);
- GNUNET_CURL_get_select_info (ctx,
+ GNUNET_CURL_get_select_info (merchant_curl_ctx,
&read_fd_set,
&write_fd_set,
&except_fd_set,
@@ -249,8 +286,8 @@ context_task (void *cls)
max_fd, timeout);
if (timeout >= 0)
delay =
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- timeout);
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ timeout);
else
delay = GNUNET_TIME_UNIT_FOREVER_REL;
rs = GNUNET_NETWORK_fdset_create ();
@@ -261,12 +298,12 @@ context_task (void *cls)
GNUNET_NETWORK_fdset_copy_native (ws,
&write_fd_set,
max_fd + 1);
- poller_task =
+ merchant_curl_task =
GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
delay,
rs,
ws,
- &context_task,
+ &merchant_curl_cb,
NULL);
GNUNET_NETWORK_fdset_destroy (rs);
GNUNET_NETWORK_fdset_destroy (ws);
@@ -288,13 +325,40 @@ return_result (void *cls)
GNUNET_CONTAINER_DLL_remove (exchange->fo_head,
exchange->fo_tail,
fo);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Returning result for exchange %s, trusted=%d\n",
+ exchange->uri, exchange->trusted);
fo->fc (fo->fc_cls,
(GNUNET_SYSERR == exchange->pending) ? NULL : exchange->conn,
exchange->trusted);
GNUNET_free (fo);
- GNUNET_SCHEDULER_cancel (poller_task);
- GNUNET_SCHEDULER_add_now (&context_task,
- NULL);
+ merchant_curl_refresh ();
+}
+
+
+/**
+ * Retry getting information from the given exchange in
+ * the closure.
+ *
+ * @param cls the exchange
+ *
+ */
+static void
+retry_exchange (void *cls)
+{
+ struct Exchange *exchange = (struct Exchange *) cls;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Connecting to exchange exchange %s in retry_exchange\n",
+ exchange->uri);
+
+ exchange->pending = GNUNET_SYSERR; /* failed hard */
+ exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx,
+ exchange->uri,
+ &keys_mgmt_cb,
+ exchange,
+ TALER_EXCHANGE_OPTION_END);
+ GNUNET_break (NULL != exchange->conn);
+ merchant_curl_refresh ();
}
@@ -316,7 +380,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
struct Exchange *exchange;
struct TMH_EXCHANGES_FindOperation *fo;
- if (NULL == ctx)
+ if (NULL == merchant_curl_ctx)
{
GNUNET_break (0);
return NULL;
@@ -330,9 +394,19 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
for (exchange = exchange_head; NULL != exchange; exchange = exchange->next)
/* test it by checking public key --- FIXME: hostname or public key!?
Should probably be URI, not hostname anyway! */
+ {
if (0 == strcmp (exchange->uri,
chosen_exchange))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "The exchange `%s' is already known\n",
+ chosen_exchange);
break;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Comparing chosen exchange url '%s' with known url '%s'.\n",
+ chosen_exchange, exchange->uri);
+ }
if (NULL == exchange)
{
/* This is a new exchange */
@@ -342,12 +416,32 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
GNUNET_CONTAINER_DLL_insert (exchange_head,
exchange_tail,
exchange);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "The exchange `%s' is new\n",
+ chosen_exchange);
}
- /* check if we should resume this exchange */
- if ( (GNUNET_SYSERR == exchange->pending) &&
- (0 == GNUNET_TIME_absolute_get_remaining (exchange->retry_time).rel_value_us) )
- exchange->pending = GNUNET_YES;
+ if (GNUNET_SYSERR == exchange->pending)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Maybe retrying previously contacted exchange `%s'\n",
+ chosen_exchange);
+ /* check if we should resume this exchange */
+ if (0 == GNUNET_TIME_absolute_get_remaining (exchange->retry_time).rel_value_us)
+ {
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Retrying exchange `%s'\n",
+ chosen_exchange);
+ exchange->pending = GNUNET_YES;
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Not retrying exchange `%s', too early\n",
+ chosen_exchange);
+ }
+ }
fo = GNUNET_new (struct TMH_EXCHANGES_FindOperation);
@@ -358,7 +452,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
exchange->fo_tail,
fo);
- if (GNUNET_NO == exchange->pending)
+ if (GNUNET_YES != exchange->pending)
{
/* We are not currently waiting for a reply, immediately
return result */
@@ -371,12 +465,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
if ( (NULL == exchange->conn) &&
(GNUNET_YES == exchange->pending) )
{
- exchange->conn = TALER_EXCHANGE_connect (ctx,
- exchange->uri,
- &keys_mgmt_cb,
- exchange,
- TALER_EXCHANGE_OPTION_END);
- GNUNET_break (NULL != exchange->conn);
+ retry_exchange (exchange);
}
return fo;
}
@@ -413,7 +502,7 @@ TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo)
*/
static void
parse_exchanges (void *cls,
- const char *section)
+ const char *section)
{
const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
char *uri;
@@ -421,8 +510,8 @@ parse_exchanges (void *cls,
struct Exchange *exchange;
if (0 != strncasecmp (section,
- "exchange-",
- strlen ("exchange-")))
+ "merchant-exchange-",
+ strlen ("merchant-exchange-")))
return;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
@@ -459,16 +548,18 @@ parse_exchanges (void *cls,
}
GNUNET_free (mks);
}
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "MASTER_KEY not given in section '%s', not trusting exchange\n",
+ section);
+
+ }
GNUNET_CONTAINER_DLL_insert (exchange_head,
exchange_tail,
exchange);
exchange->pending = GNUNET_YES;
- exchange->conn = TALER_EXCHANGE_connect (ctx,
- exchange->uri,
- &keys_mgmt_cb,
- exchange,
- TALER_EXCHANGE_OPTION_END);
- GNUNET_break (NULL != exchange->conn);
+ retry_exchange (exchange);
}
@@ -485,8 +576,8 @@ TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
struct Exchange *exchange;
json_t *j_exchange;
- ctx = GNUNET_CURL_init ();
- if (NULL == ctx)
+ merchant_curl_ctx = GNUNET_CURL_init ();
+ if (NULL == merchant_curl_ctx)
{
GNUNET_break (0);
return GNUNET_SYSERR;
@@ -507,8 +598,7 @@ TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
json_array_append_new (trusted_exchanges,
j_exchange);
}
- poller_task = GNUNET_SCHEDULER_add_now (&context_task,
- NULL);
+ merchant_curl_refresh ();
return GNUNET_OK;
}
@@ -531,11 +621,11 @@ TMH_EXCHANGES_done ()
GNUNET_free (exchange->uri);
GNUNET_free (exchange);
}
- if (NULL != poller_task)
+ if (NULL != merchant_curl_task)
{
- GNUNET_SCHEDULER_cancel (poller_task);
- poller_task = NULL;
+ GNUNET_SCHEDULER_cancel (merchant_curl_task);
+ merchant_curl_task = NULL;
}
- GNUNET_CURL_fini (ctx);
- ctx = NULL;
+ GNUNET_CURL_fini (merchant_curl_ctx);
+ merchant_curl_ctx = NULL;
}
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
@@ -30,7 +30,6 @@
#include "taler-merchant-httpd_responses.h"
#include "taler-merchant-httpd_auditors.h"
#include "taler-merchant-httpd_exchanges.h"
-#include "taler_merchantdb_lib.h"
/**
@@ -277,7 +276,7 @@ abort_deposit (struct PayContext *pc)
static void
deposit_cb (void *cls,
unsigned int http_status,
- json_t *proof)
+ const json_t *proof)
{
struct MERCHANT_DepositConfirmation *dc = cls;
struct PayContext *pc = dc->pc;
@@ -286,15 +285,26 @@ deposit_cb (void *cls,
pc->pending--;
if (MHD_HTTP_OK != http_status)
{
- /* Transaction failed; stop all other ongoing deposits */
- abort_deposit (pc);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Deposit operation failed with HTTP code %u\n",
http_status);
- /* Forward error including 'proof' for the body */
- resume_pay_with_response (pc,
- http_status,
- TMH_RESPONSE_make_json (proof));
+ /* Transaction failed; stop all other ongoing deposits */
+ abort_deposit (pc);
+
+ if (NULL == proof)
+ {
+ /* FIXME: is this the right code for when the exchange fails? */
+ resume_pay_with_response (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_internal_error ("Exchange failed, no proof available"));
+ }
+ else
+ {
+ /* Forward error including 'proof' for the body */
+ resume_pay_with_response (pc,
+ http_status,
+ TMH_RESPONSE_make_json (proof));
+ }
return;
}
/* store result to DB */
@@ -423,7 +433,7 @@ process_pay_with_exchange (void *cls,
const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
denom_details = TALER_EXCHANGE_get_denomination_key (keys,
- &dc->denom);
+ &dc->denom);
if (NULL == denom_details)
{
GNUNET_break_op (0);
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
@@ -39,3 +39,24 @@ libtaler_plugin_merchantdb_postgres_la_LDFLAGS = \
-lpq \
-lgnunetpostgres \
-lgnunetutil $(XLIB)
+
+if HAVE_POSTGRESQL
+if HAVE_GNUNETPQ
+check_PROGRAMS = \
+ test-merchantdb-postgres
+endif
+endif
+
+test_merchantdb_postgres_SOURCES = \
+ test_merchantdb.c
+
+test_merchantdb_postgres_LDFLAGS = \
+ -lgnunetutil \
+ -ltalerutil \
+ -ljansson
+
+test_merchantdb_postgres_LDADD = \
+ $(top_srcdir)/src/backenddb/libtalermerchantdb.la
+
+TESTS = \
+ test-merchantdb-postgres
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
@@ -152,7 +152,7 @@ postgres_store_payment (void *cls,
struct GNUNET_TIME_Absolute refund,
const struct TALER_Amount *amount_without_fee,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
- json_t *exchange_proof)
+ const json_t *exchange_proof)
{
struct PostgresClosure *pg = cls;
PGresult *res;
diff --git a/src/backenddb/test-merchantdb-postgres.conf b/src/backenddb/test-merchantdb-postgres.conf
@@ -0,0 +1,5 @@
+[merchant]
+DB = postgres
+
+[merchantdb-postgres]
+CONFIG = postgres:///talertest
diff --git a/src/backenddb/test_backenddb.c b/src/backenddb/test_backenddb.c
@@ -1,108 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2015, 2016 GNUnet e.V. and INRIA
-
- 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, If not, see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file merchantdb/test_merchantdb.c
- * @brief Testcases for backenddb
- * @author Marcello Stanisci
- * @author Sree Harsha Totakura
- */
-
-/**
- * Connection handle to the our database
- */
-struct TALER_MERCHANTDB_Plugin *db;
-
-/* FIXME define 'drop' label */
-#define FAILIF(cond) \
- do { \
- if (!(cond)){ break;} \
- GNUNET_break (0); \
- goto drop; \
- } while (0)
-
-
-/**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param cfg configuration
- */
-static void
-run (void *cls,
- char *const *args,
- const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-
- EXITIF (NULL == (db = TALER_MERCHANTDB_plugin_load (cfg)));
- EXITIF (GNUNET_SYSERR == db->initialize())
- // crea sample data
-
- // call plugin's functions
-
-
- TALER_MERCHANTDB_plugin_unload (db);
-
- // define FAILIF
- // define EXITIF
-}
-
-int
-main (int argc,
- char *const argv[])
-{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_END
- };
- char *argv2[] = {
- "test-merchant-db-<plugin_name>", /* will be replaced later */
- "-c", "test-merchant-db-<plugin_name>.conf", /* will be replaced later */
- NULL,
- };
- const char *plugin_name;
- char *config_filename;
- char *testname;
-
- result = -1;
- if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
- {
- GNUNET_break (0);
- return -1;
- }
- plugin_name++;
- (void) GNUNET_asprintf (&testname,
- "test-merchant-db-%s", plugin_name);
- (void) GNUNET_asprintf (&config_filename,
- "%s.conf", testname);
- argv2[0] = argv[0];
- argv2[2] = config_filename;
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run ((sizeof (argv2)/sizeof (char *)) - 1, argv2,
- testname,
- "Test cases for merchant database helper functions.",
- options, &run, NULL))
- {
- GNUNET_free (config_filename);
- GNUNET_free (testname);
- return 3;
- }
- GNUNET_free (config_filename);
- GNUNET_free (testname);
- return result;
-}
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
@@ -0,0 +1,132 @@
+/*
+ This file is part of TALER
+ (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser 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, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant/test_merchantdb_postgres.c
+ * @brief testcase for merchant's postgres db plugin
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include <taler/taler_util.h>
+#include "taler_merchantdb_lib.h"
+#include <jansson.h>
+
+#define FAILIF(cond) \
+ do { \
+ if (!(cond)){ break;} \
+ GNUNET_break (0); \
+ goto drop; \
+ } while (0)
+
+#define RND_BLK(ptr) \
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr))
+
+#define CURRENCY "EUR"
+
+static int result;
+static struct TALER_MERCHANTDB_Plugin *plugin;
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure with config
+ */
+static void
+run (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ /* Data for 'store_payment()' */
+ struct GNUNET_HashCode h_contract;
+ struct GNUNET_HashCode h_wire;
+ uint64_t transaction_id;
+ struct GNUNET_TIME_Absolute timestamp;
+ struct GNUNET_TIME_Absolute refund;
+ struct TALER_Amount amount_without_fee;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ json_t *exchange_proof = NULL;
+
+ FAILIF (NULL == (plugin = TALER_MERCHANTDB_plugin_load (cfg)));
+ FAILIF (GNUNET_OK != plugin->initialize (plugin->cls, GNUNET_YES));
+
+ /* Prepare data for 'store_payment()' */
+ RND_BLK (&h_contract);
+ RND_BLK (&h_wire);
+ RND_BLK (&transaction_id);
+ timestamp = GNUNET_TIME_absolute_get();
+ refund = GNUNET_TIME_absolute_get();
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":1.000010",
+ &amount_without_fee));
+ RND_BLK (&coin_pub);
+ exchange_proof = json_object ();
+ GNUNET_assert (0 == json_object_set (exchange_proof, "test", json_string ("backenddb test")));
+ FAILIF (GNUNET_OK != plugin->store_payment (plugin->cls,
+ &h_contract,
+ &h_wire,
+ transaction_id,
+ timestamp,
+ refund,
+ &amount_without_fee,
+ &coin_pub,
+ exchange_proof));
+ FAILIF (GNUNET_OK != plugin->check_payment (plugin->cls, transaction_id));
+ result = 0;
+
+ drop:
+ TALER_MERCHANTDB_plugin_unload (plugin);
+ plugin = NULL;
+ if (NULL != exchange_proof)
+ json_decref(exchange_proof);
+}
+
+int
+main (int argc,
+ char *const argv[])
+{
+
+ const char *plugin_name;
+ char *config_filename;
+ char *testname;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ result = -1;
+ if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
+ {
+ GNUNET_break (0);
+ return -1;
+ }
+ GNUNET_log_setup (argv[0], "WARNING", NULL);
+ plugin_name++;
+ (void) GNUNET_asprintf (&testname,
+ "test-merchantdb-%s", plugin_name);
+ (void) GNUNET_asprintf (&config_filename,
+ "%s.conf", testname);
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse (cfg,
+ config_filename))
+ {
+ GNUNET_break (0);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return 2;
+ }
+ GNUNET_SCHEDULER_run (&run, cfg);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return result;
+}
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
@@ -82,7 +82,7 @@ struct TALER_MERCHANTDB_Plugin
struct GNUNET_TIME_Absolute refund,
const struct TALER_Amount *amount_without_fee,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
- json_t *exchange_proof);
+ const json_t *exchange_proof);
/**
* Check whether a payment has already been stored