aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/merchant.conf5
-rw-r--r--src/backend/taler-merchant-httpd.c22
-rw-r--r--src/backend/taler-merchant-httpd.h20
-rw-r--r--src/backend/taler-merchant-httpd_contract.c57
-rw-r--r--src/backend/taler-merchant-httpd_mints.c10
-rw-r--r--src/backend/taler-merchant-httpd_pay.c198
6 files changed, 246 insertions, 66 deletions
diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf
index 99d30b22..93d9fbba 100644
--- a/src/backend/merchant.conf
+++ b/src/backend/merchant.conf
@@ -25,7 +25,7 @@ EDATE = 3 week
DB = postgres
[mint-taler]
-URI = mint.demo.taler.net
+URI = http://mint.demo.taler.net/
MASTER_KEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0
# Auditors must be in sections "auditor-", the rest of the section
@@ -46,9 +46,10 @@ PUBLIC_KEY = 9QXF7XY7E9VPV47B5Z806NDFSX2VJ79SVHHD29QEQ3BG31ANHZ60
[merchantdb-postgres]
CONFIG = postgres:///talerdemo
-
# "wire-" sections include wire details, here for SEPA.
[wire-sepa]
IBAN = DE67830654080004822650
NAME = GNUNET E.V
BIC = GENODEF1SRL
+ADDRESS = "Garching"
+SALT = 12345
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 01ae471f..01a00fdb 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -334,6 +334,7 @@ parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
unsigned long long salt;
char *iban;
char *name;
+ char *address;
char *bic;
if (GNUNET_OK !=
@@ -366,21 +367,35 @@ parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"wire-sepa",
+ "ADDRESS",
+ &address))
+ {
+ GNUNET_free (iban);
+ GNUNET_free (name);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
"BIC",
&bic))
{
GNUNET_free (iban);
GNUNET_free (name);
GNUNET_free (bic);
+ GNUNET_free (address);
+ return GNUNET_SYSERR;
}
- j_wire = json_pack ("{s:s, s:s, s:s, s:s, s:o}",
+ j_wire = json_pack ("{s:s, s:s, s:s, s:s, s:o, s:s}",
"type", "SEPA",
"IBAN", iban,
"name", name,
"bic", bic,
- "r", json_integer (salt));
+ "r", json_integer (salt),
+ "address", address);
GNUNET_free (iban);
GNUNET_free (name);
+ GNUNET_free (address);
GNUNET_free (bic);
if (NULL == j_wire)
return GNUNET_SYSERR;
@@ -454,6 +469,7 @@ prepare_daemon ()
tv = GNUNET_TIME_UNIT_FOREVER_REL;
GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding run_daemon select task\n");
ret =
GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
tv, wrs, wws,
@@ -491,7 +507,7 @@ run (void *cls,
TMH_AUDITORS_init (config));
/* FIXME: for now, we just support SEPA here: */
EXITIF (GNUNET_OK !=
- parse_wireformat_sepa (config));
+ parse_wireformat_sepa (config));
EXITIF (GNUNET_OK !=
validate_and_hash_wireformat ("SEPA"));
EXITIF (GNUNET_OK !=
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index c2147dcc..c5a2c55e 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -136,19 +136,29 @@ extern json_t *j_wire;
*/
extern struct GNUNET_HashCode h_wire;
-
+/**
+ * Our private key (for the merchant to sign contracts).
+ */
extern struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
+/**
+ * Our public key, corresponds to #privkey.
+ */
extern struct TALER_MerchantPublicKeyP pubkey;
-
+/**
+ * Handle to the database backend.
+ */
extern struct TALER_MERCHANTDB_Plugin *db;
-
-
+/**
+ * If the frontend does NOT specify an execution date, how long should
+ * we tell the mint to wait to aggregate transactions before
+ * executing? This delay is added to the current time when we
+ * generate the advisory execution time for the mint.
+ */
extern struct GNUNET_TIME_Relative edate_delay;
-
/**
* Kick MHD to run now, to be called after MHD_resume_connection().
* Basically, we need to explicitly resume MHD's event loop whenever
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
index cd35b482..c6cbcb70 100644
--- a/src/backend/taler-merchant-httpd_contract.c
+++ b/src/backend/taler-merchant-httpd_contract.c
@@ -52,8 +52,10 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
size_t *upload_data_size)
{
json_t *root;
+ json_t *jcontract;
+ json_t *pay_url;
+ json_t *exec_url;
int res;
- struct GNUNET_HashCode h_wire;
struct TALER_ContractPS contract;
struct GNUNET_CRYPTO_EddsaSignature contract_sig;
@@ -68,39 +70,64 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
if ((GNUNET_NO == res) || (NULL == root))
return MHD_YES;
- /* add fields to the "root" that the backend should provide */
- json_object_set (root,
+ jcontract = json_object_get (root, "contract");
+
+ if (NULL == jcontract)
+ {
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "contract request malformed");
+ }
+
+ /* add fields to the contract that the backend should provide */
+ json_object_set (jcontract,
"mints",
trusted_mints);
- json_object_set (root,
+ json_object_set (jcontract,
"auditors",
j_auditors);
- json_object_set_new (root,
+ json_object_set_new (jcontract,
"H_wire",
TALER_json_from_data (&h_wire,
sizeof (h_wire)));
- json_object_set_new (root,
+ json_object_set_new (jcontract,
"merchant_pub",
TALER_json_from_data (&pubkey,
sizeof (pubkey)));
/* create contract signature */
GNUNET_assert (GNUNET_OK ==
- TALER_hash_json (root,
+ TALER_hash_json (jcontract,
&contract.h_contract));
contract.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
contract.purpose.size = htonl (sizeof (contract));
GNUNET_CRYPTO_eddsa_sign (privkey,
&contract.purpose,
&contract_sig);
+
+ pay_url = json_object_get (root, "pay_url");
+ if (NULL == pay_url)
+ {
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "pay url missing");
+ }
+ exec_url = json_object_get (root, "exec_url");
+ if (NULL == exec_url)
+ {
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "exec url missing");
+ }
/* return final response */
- return TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:o, s:o, s:o}",
- "contract", root,
- "sig", TALER_json_from_data (&contract_sig,
- sizeof (contract_sig)),
- "H_contract", TALER_json_from_data (&contract.h_contract,
- sizeof (contract.h_contract)));
+ res = TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:O, s:O, s:O, s:o, s:o}",
+ "contract", jcontract,
+ "exec_url", exec_url,
+ "pay_url", pay_url,
+ "sig", TALER_json_from_data (&contract_sig,
+ sizeof (contract_sig)),
+ "H_contract", TALER_json_from_data (&contract.h_contract,
+ sizeof (contract.h_contract)));
+ json_decref (root);
+ return res;
}
/* end of taler-merchant-httpd_contract.c */
diff --git a/src/backend/taler-merchant-httpd_mints.c b/src/backend/taler-merchant-httpd_mints.c
index b2accf2b..56aee9a9 100644
--- a/src/backend/taler-merchant-httpd_mints.c
+++ b/src/backend/taler-merchant-httpd_mints.c
@@ -230,6 +230,8 @@ context_task (void *cls,
struct GNUNET_NETWORK_FDSet *ws;
struct GNUNET_TIME_Relative delay;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "In mint context polling task\n");
+
poller_task = NULL;
TALER_MINT_perform (ctx);
max_fd = -1;
@@ -243,6 +245,9 @@ context_task (void *cls,
&except_fd_set,
&max_fd,
&timeout);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "In mint context polling task, max_fd=%d, timeout=%ld\n",
+ max_fd, timeout);
if (timeout >= 0)
delay =
GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
@@ -319,6 +324,11 @@ TMH_MINTS_find_mint (const char *chosen_mint,
GNUNET_break (0);
return NULL;
}
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to find chosen mint `%s'\n",
+ chosen_mint);
+
/* Check if the mint is known */
for (mint = mint_head; NULL != mint; mint = mint->next)
/* test it by checking public key --- FIXME: hostname or public key!?
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
index f4a1a2b4..ea59bd7d 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -15,8 +15,9 @@
*/
/**
* @file backend/taler-merchant-httpd_pay.c
- * @brief HTTP serving layer mainly intended to communicate with the frontend
+ * @brief handling of /pay requests
* @author Marcello Stanisci
+ * @author Christian Grothoff
*/
#include "platform.h"
#include <jansson.h>
@@ -67,6 +68,11 @@ struct MERCHANT_DepositConfirmation
struct TALER_Amount percoin_amount;
/**
+ * Amount this coin contributes to the total purchase price.
+ */
+ struct TALER_Amount amount_without_fee;
+
+ /**
* Public key of the coin.
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
@@ -112,6 +118,12 @@ struct PayContext
struct MHD_Connection *connection;
/**
+ * Handle to the mint that we are doing the payment with.
+ * (initially NULL while @e fo is trying to find a mint).
+ */
+ struct TALER_MINT_Handle *mh;
+
+ /**
* Handle for operation to lookup /keys (and auditors) from
* the mint used for this transaction; NULL if no operation is
* pending.
@@ -219,6 +231,29 @@ resume_pay_with_response (struct PayContext *pc,
/**
+ * Abort all pending /deposit operations.
+ *
+ * @param pc pay context to abort
+ */
+static void
+abort_deposit (struct PayContext *pc)
+{
+ unsigned int i;
+
+ for (i=0;i<pc->coins_cnt;i++)
+ {
+ struct MERCHANT_DepositConfirmation *dci = &pc->dc[i];
+
+ if (NULL != dci->dh)
+ {
+ TALER_MINT_deposit_cancel (dci->dh);
+ dci->dh = NULL;
+ }
+ }
+}
+
+
+/**
* Callback to handle a deposit permission's response.
*
* @param cls a `struct MERCHANT_DepositConfirmation` (i.e. a pointer
@@ -245,26 +280,38 @@ deposit_cb (void *cls,
pc->pending--;
if (MHD_HTTP_OK != http_status)
{
- unsigned int i;
-
/* Transaction failed; stop all other ongoing deposits */
- for (i=0;i<pc->coins_cnt;i++)
- {
- struct MERCHANT_DepositConfirmation *dci = &pc->dc[i];
-
- if (NULL != dci->dh)
- {
- TALER_MINT_deposit_cancel (dci->dh);
- dci->dh = NULL;
- }
- }
+ 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));
return;
}
- /* FIXME: store result to DB here somewhere! */
+ /* store result to DB */
+ if (GNUNET_OK !=
+ db->store_payment (db->cls,
+ &pc->h_contract,
+ &h_wire,
+ pc->transaction_id,
+ pc->timestamp,
+ pc->refund_deadline,
+ &dc->amount_without_fee,
+ &dc->coin_pub,
+ proof))
+ {
+ GNUNET_break (0);
+ /* internal error */
+ abort_deposit (pc);
+ /* Forward error including 'proof' for the body */
+ resume_pay_with_response (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TMH_RESPONSE_make_internal_error ("Merchant database error"));
+ return;
+ }
if (0 != pc->pending)
return; /* still more to do */
resume_pay_with_response (pc,
@@ -351,6 +398,7 @@ process_pay_with_mint (void *cls,
TMH_RESPONSE_make_external_error ("mint not supported"));
return;
}
+ pc->mh = mh;
keys = TALER_MINT_get_keys (mh);
if (NULL == keys)
@@ -372,6 +420,7 @@ process_pay_with_mint (void *cls,
&dc->denom);
if (NULL == denom_details)
{
+ GNUNET_break_op (0);
resume_pay_with_response (pc,
MHD_HTTP_BAD_REQUEST,
TMH_RESPONSE_make_json_pack ("{s:s, s:o}",
@@ -384,6 +433,7 @@ process_pay_with_mint (void *cls,
denom_details,
mint_trusted))
{
+ GNUNET_break_op (0);
resume_pay_with_response (pc,
MHD_HTTP_BAD_REQUEST,
TMH_RESPONSE_make_json_pack ("{s:s, s:o}",
@@ -398,12 +448,37 @@ process_pay_with_mint (void *cls,
}
else
{
- TALER_amount_add (&acc_fee,
- &denom_details->fee_deposit,
- &acc_fee);
- TALER_amount_add (&acc_amount,
- &dc->percoin_amount,
- &acc_amount);
+ if ( (GNUNET_OK !=
+ TALER_amount_add (&acc_fee,
+ &denom_details->fee_deposit,
+ &acc_fee)) ||
+ (GNUNET_OK !=
+ TALER_amount_add (&acc_amount,
+ &dc->percoin_amount,
+ &acc_amount)) )
+ {
+ GNUNET_break_op (0);
+ /* Overflow in these amounts? Very strange. */
+ resume_pay_with_response (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TMH_RESPONSE_make_internal_error ("Overflow adding up amounts"));
+ return;
+ }
+ }
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&dc->amount_without_fee,
+ &dc->percoin_amount,
+ &denom_details->fee_deposit))
+ {
+ GNUNET_break_op (0);
+ /* fee higher than residual coin value, makes no sense. */
+ resume_pay_with_response (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TMH_RESPONSE_make_json_pack ("{s:s, s:o, s:o}",
+ "hint", "fee higher than coin value",
+ "f", TALER_json_from_amount (&dc->percoin_amount),
+ "fee_deposit", TALER_json_from_amount (&denom_details->fee_deposit)));
+ return;
}
}
@@ -436,6 +511,7 @@ process_pay_with_mint (void *cls,
if (-1 == TALER_amount_cmp (&acc_amount,
&total_needed))
{
+ GNUNET_break_op (0);
resume_pay_with_response (pc,
MHD_HTTP_NOT_ACCEPTABLE,
TMH_RESPONSE_make_external_error ("insufficient funds (including excessive mint fees to be covered by customer)"));
@@ -448,6 +524,7 @@ process_pay_with_mint (void *cls,
if (-1 == TALER_amount_cmp (&acc_amount,
&pc->amount))
{
+ GNUNET_break_op (0);
resume_pay_with_response (pc,
MHD_HTTP_NOT_ACCEPTABLE,
TMH_RESPONSE_make_external_error ("insufficient funds"));
@@ -477,11 +554,14 @@ process_pay_with_mint (void *cls,
dc);
if (NULL == dc->dh)
{
+ /* Signature was invalid. If the mint was unavailable,
+ * we'd get that information in the callback. */
+ GNUNET_break_op (0);
resume_pay_with_response (pc,
- MHD_HTTP_SERVICE_UNAVAILABLE,
+ MHD_HTTP_UNAUTHORIZED,
TMH_RESPONSE_make_json_pack ("{s:s, s:i}",
- "mint", pc->chosen_mint,
- "transaction_id", pc->transaction_id));
+ "hint", "Coin signature invalid.",
+ "coin_idx", i));
return;
}
}
@@ -511,6 +591,8 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
int res;
json_t *root;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "In handler for /pay.\n");
+
if (NULL == *connection_cls)
{
pc = GNUNET_new (struct PayContext);
@@ -528,6 +610,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
/* We are *done* processing the request, just queue the response (!) */
if (UINT_MAX == pc->response_code)
return MHD_NO; /* hard error */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Queueing response for /pay.\n");
res = MHD_queue_response (connection,
pc->response_code,
pc->response);
@@ -549,23 +632,6 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
if ((GNUNET_NO == res) || (NULL == root))
return MHD_YES; /* the POST's body has to be further fetched */
- /* We got no 'edate' from frontend. Generate it here; it will be
- timestamp plus the edate_delay supplied in config file */
- if (NULL == json_object_get (root, "edate"))
- {
- pc->edate = GNUNET_TIME_absolute_add (pc->timestamp, // FIXME: uninit!
- edate_delay);
- if (-1 ==
- json_object_set (root,
- "edate",
- TALER_json_from_abs (pc->edate)))
- {
- GNUNET_break (0);
- json_decref (root);
- return MHD_NO;
- }
- }
-
/* Got the JSON upload, parse it */
{
json_t *coins;
@@ -580,19 +646,47 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
TMH_PARSE_member_time_abs ("timestamp", &pc->timestamp),
TMH_PARSE_member_time_abs ("refund_deadline", &pc->refund_deadline),
TMH_PARSE_member_fixed ("H_contract", &pc->h_contract),
- TMH_PARSE_member_time_abs ("edate", &pc->edate),
TMH_PARSE_MEMBER_END
};
res = TMH_PARSE_json_data (connection,
root,
spec);
+
if (GNUNET_YES != res)
{
json_decref (root);
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsed JSON for /pay.\n");
+
+ /* 'edate' is optional, if it is not present, generate it here; it
+ will be timestamp plus the edate_delay supplied in config
+ file */
+ if (NULL == json_object_get (root, "edate"))
+ {
+ pc->edate = GNUNET_TIME_absolute_add (pc->timestamp,
+ edate_delay);
+ }
+ else
+ {
+ struct TMH_PARSE_FieldSpecification espec[] = {
+ TMH_PARSE_member_time_abs ("edate", &pc->edate),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_json_data (connection,
+ root,
+ espec);
+ if (GNUNET_YES != res)
+ {
+ json_decref (root);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ }
+
+
pc->coins_cnt = json_array_size (coins);
if (0 == pc->coins_cnt)
{
@@ -600,9 +694,15 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
return TMH_RESPONSE_reply_external_error (connection,
"no coins given");
}
+ /* note: 1 coin = 1 deposit confirmation expected */
pc->dc = GNUNET_new_array (pc->coins_cnt,
struct MERCHANT_DepositConfirmation);
+ {
+ char *s = json_dumps (coins, JSON_INDENT(2));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Coins json is: %s\n", s);
+ }
+
json_array_foreach (coins, coins_index, coin)
{
struct MERCHANT_DepositConfirmation *dc = &pc->dc[coins_index];
@@ -623,10 +723,26 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
json_decref (root);
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
+
+ {
+ char *s;
+ s = TALER_amount_to_string (&dc->percoin_amount);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Coin #%i has f %s\n", coins_index, s);
+ GNUNET_free (s);
+ }
+
dc->index = coins_index;
+ dc->pc = pc;
}
}
+ /* Check if this payment attempt has already taken place */
+ if (GNUNET_OK == db->check_payment (db->cls, pc->transaction_id))
+ return TMH_RESPONSE_reply_external_error (connection, "payment already attempted");
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking up chosen mint '%s'\n", pc->chosen_mint);
+
/* Find the responsible mint, this may take a while... */
pc->pending = pc->coins_cnt;
pc->fo = TMH_MINTS_find_mint (pc->chosen_mint,