diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/merchant.conf | 5 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 22 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.h | 20 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_contract.c | 57 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_mints.c | 10 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_pay.c | 198 |
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, |