merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 8a7f3119157b8d6c2be3425353b8420051ee617e
parent 2cf860f09c28cbbdfd0b66176f4f0bd93a184048
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 24 Apr 2016 23:33:18 +0200

Merge branch 'master' of ssh://taler.net:/var/git/merchant

Diffstat:
Mbootstrap | 10++++++++++
Mconfigure.ac | 10++++++++++
Mcopylib/util.php | 2+-
Aexamples/blog/README | 21+++++++++++++++++++++
Mexamples/blog/essay_fulfillment.php | 10++++------
Mexamples/blog/essay_pay.php | 3+--
Aexamples/blog/nginx_example.conf | 40++++++++++++++++++++++++++++++++++++++++
Mexamples/shop/README | 27+++++++++++++++++++++++++++
Mexamples/shop/fulfillment.php | 62+++++++++++++++++++++++++++++++++++++++++---------------------
Mexamples/shop/generate_taler_contract.php | 75+++++++--------------------------------------------------------------------
Mexamples/shop/index.php | 1+
Mexamples/shop/pay.php | 46++++++++++++++++++++++++++++++----------------
Msrc/backend/merchant.conf | 11++---------
Dsrc/backend/myconf.sh | 3---
Msrc/backend/taler-merchant-httpd.c | 8++++----
Msrc/backend/taler-merchant-httpd_contract.c | 2++
Msrc/backend/taler-merchant-httpd_exchanges.c | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/backend/taler-merchant-httpd_pay.c | 23+++++++++++++++++------
18 files changed, 354 insertions(+), 182 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 @@ -9,7 +9,7 @@ PORT = 9966 # 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 +19,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 +26,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 @@ -503,13 +503,13 @@ run (void *cls, } 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; } 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 @@ -286,15 +286,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 */