merchant

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

commit ca4d9eed5190b6d79dd2f7eac03a21e0cf69c3c8
parent d8b70e33e6300a168fa35606c5de9723fcd979c4
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date:   Thu, 24 Sep 2015 12:11:18 +0200

adding "refund_deadline" mgmt (DB,testcase,httpd,library)

Diffstat:
Msrc/backend-lib/merchant_db.c | 10+++++++---
Msrc/backend-lib/merchant_db.h | 2++
Msrc/backend-lib/taler-merchant-httpd_contract.c | 19+++++++++++++++----
Msrc/backend-lib/taler_merchant_contract_lib.h | 2++
Msrc/backend/taler-merchant-httpd.c | 113+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/frontend/generate_taler_contract.php | 8+++-----
Msrc/tests/merchant-contract-test.c | 7++++---
7 files changed, 100 insertions(+), 61 deletions(-)

diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c @@ -97,6 +97,7 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) "timestamp INT8 NOT NULL," "expiry INT8 NOT NULL," "edate INT8 NOT NULL," + "refund_deadline INT8 NOT NULL," "product INT8 NOT NULL);" "CREATE %1$s TABLE IF NOT EXISTS checkouts (" "coin_pub BYTEA PRIMARY KEY," @@ -122,10 +123,10 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) (conn, "contract_create", "INSERT INTO contracts" - "(contract_id, hash, timestamp, expiry, edate, amount, amount_fraction, amount_currency," + "(contract_id, hash, timestamp, expiry, edate, refund_deadline, amount, amount_fraction, amount_currency," "description, nounce, product) VALUES" - "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", - 11, NULL))); + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + 12, NULL))); EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); PQclear (res); @@ -214,6 +215,7 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) * @param expiry the time when the contract will expire * @param edate when the merchant wants to receive the wire transfer corresponding * to this deal (this value is also a field inside the 'wire' JSON format) +* @param refund deadline until which the merchant can return the paid amount * @param amount the taler amount corresponding to the contract * @param hash of the stringified JSON corresponding to this contract * @param c_id contract's id @@ -228,6 +230,7 @@ MERCHANT_DB_contract_create (PGconn *conn, const struct GNUNET_TIME_Absolute timestamp, const struct GNUNET_TIME_Absolute expiry, struct GNUNET_TIME_Absolute edate, + struct GNUNET_TIME_Absolute refund, const struct TALER_Amount *amount, const struct GNUNET_HashCode *h_contract, uint64_t c_id, @@ -250,6 +253,7 @@ MERCHANT_DB_contract_create (PGconn *conn, TALER_PQ_query_param_absolute_time (&timestamp), TALER_PQ_query_param_absolute_time (&expiry), TALER_PQ_query_param_absolute_time (&edate), + TALER_PQ_query_param_absolute_time (&refund), TALER_PQ_query_param_amount (amount), /* A *string* is being put in the following statement, though the column is declared as *blob*. Will this be diff --git a/src/backend-lib/merchant_db.h b/src/backend-lib/merchant_db.h @@ -66,6 +66,7 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp); * @param expiry the time when the contract will expire * @param edate when the merchant wants to receive the wire transfer corresponding * to this deal (this value is also a field inside the 'wire' JSON format) +* @param refund deadline until which the merchant can return the paid amount * @param amount the taler amount corresponding to the contract * @param hash of the stringified JSON corresponding to this contract * @param c_id contract's id @@ -80,6 +81,7 @@ MERCHANT_DB_contract_create (PGconn *conn, const struct GNUNET_TIME_Absolute timestamp, const struct GNUNET_TIME_Absolute expiry, struct GNUNET_TIME_Absolute edate, + struct GNUNET_TIME_Absolute refund, const struct TALER_Amount *amount, const struct GNUNET_HashCode *h_contract, uint64_t c_id, diff --git a/src/backend-lib/taler-merchant-httpd_contract.c b/src/backend-lib/taler-merchant-httpd_contract.c @@ -98,6 +98,7 @@ MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, * @param expiry the time when the contract will expire * @param edate when the merchant wants to receive the wire transfer corresponding * to this deal (this value is also a field inside the 'wire' JSON format) +* @param refund deadline until which the merchant can return the paid amount * @param nounce the nounce used to hash the wire details * @param contract_str where to store * @return pointer to the (stringified) contract; NULL upon errors @@ -114,6 +115,7 @@ MERCHANT_handle_contract (const json_t *j_contract, struct GNUNET_TIME_Absolute timestamp, struct GNUNET_TIME_Absolute expiry, struct GNUNET_TIME_Absolute edate, + struct GNUNET_TIME_Absolute refund, uint64_t nounce) { json_t *j_amount; @@ -127,17 +129,25 @@ MERCHANT_handle_contract (const json_t *j_contract, /* Extracting values useful for DB work. Only gettable from the JSON since they are generated by the frontend */ - if (-1 == json_unpack (j_contract, "{s:o, s:I, s:{s:{s:I}}}", + if (-1 == json_unpack (j_contract, "{s:o, s:I, s:{s:I}}", "amount", &j_amount, "trans_id", &j_trans_id, - "details", "items", - "product_id", &j_product_id)) + "details", "product_id", + &j_product_id)) { printf ("no unpacking\n"); return NULL; } - /* DB will store the amount */ + /* DB will store the amount -- WARNING: this call produces a + 'protocol violation' in json.c */ + + #if 0 + char *str = json_dumps (j_amount, JSON_INDENT(2) | JSON_PRESERVE_ORDER); + printf ("extracted amount : %s\n", str); + #endif + + TALER_json_to_amount (j_amount, &amount); contract_str = json_dumps (j_contract, JSON_COMPACT | JSON_PRESERVE_ORDER); GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, @@ -150,6 +160,7 @@ MERCHANT_handle_contract (const json_t *j_contract, timestamp, expiry, edate, + refund, &amount, &contract->h_contract_details, (uint64_t) j_trans_id, // safe? diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h @@ -84,6 +84,7 @@ MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, * @param expiry the time when the contract will expire * @param edate when the merchant wants to receive the wire transfer corresponding * to this deal (this value is also a field inside the 'wire' JSON format) +* @param refund deadline until which the merchant can return the paid amount * @param nounce the nounce used to hash the wire details * @param contract_str where to store * @return pointer to the (stringified) contract; NULL upon errors @@ -100,4 +101,5 @@ MERCHANT_handle_contract (const json_t *j_contract, struct GNUNET_TIME_Absolute timestamp, struct GNUNET_TIME_Absolute expiry, struct GNUNET_TIME_Absolute edate, + struct GNUNET_TIME_Absolute refund, uint64_t nounce); diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -342,35 +342,26 @@ url_handler (void *cls, struct MHD_Response *resp; json_t *root; json_t *j_sig_enc; + json_t *j_h_contract; + json_t *j_tmp; json_t *eddsa_pub_enc; json_t *response; json_t *j_mints; json_t *j_mint; json_t *j_wire; int cnt; /* loop counter */ - char *str; /* to debug JSONs */ char *deposit_body; - json_t *root_tmp; json_t *j_contract_add; - json_t *j_details_tmp; - json_t *j_max_fee_tmp; - json_int_t j_int_trans_id_tmp; - json_t *j_trans_id_tmp; - int64_t trans_id_tmp; - char *ub_sig; - char *coin_pub; - char *denom_key; - char *contract_sig; - char *h_contract; struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute expiry; struct GNUNET_TIME_Absolute edate; + struct GNUNET_TIME_Absolute refund; struct GNUNET_HashCode h_json_wire; - struct TALER_Amount amount_tmp; json_t *j_h_json_wire; struct curl_slist *slist; char *contract_str; struct GNUNET_HashCode h_contract_str; + uint64_t nounce; CURL *curl; CURLcode curl_res; @@ -428,19 +419,45 @@ url_handler (void *cls, TODO: Check if there is a row in 'contracts' table corresponding to this contract ('s hash). If so, the corresponding 'nounce' and 'edate' have to be retrieved */ - - j_wire = - + /* Get this dep. perm.'s H_contract */ + + if (NULL == (j_h_contract = json_object_get (root, "H_contract"))) + { + printf ("H_contract field missing\n"); + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + TALER_json_to_data (j_h_contract, &h_contract_str, sizeof (struct GNUNET_HashCode)); + if (GNUNET_SYSERR == + MERCHANT_DB_get_contract_values (db_conn, + &h_contract_str, + &nounce, + &edate)) + { + printf ("not existing contract\n"); + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + /* Reproducing the wire object */ + j_wire = MERCHANT_get_wire_json (wire, + nounce, + edate); /* Encoding merchant's key */ GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); - /* Crafting the wire JSON */ if (NULL == (j_tmp = json_pack ("{s:o, s:I}", "merchant_pub", eddsa_pub_enc, "wire", j_wire))) + if (-1 == json_object_update (root, j_tmp)) + { + printf ("depperm not augmented\n"); + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + /* POST to mint's "/deposit" */ curl = curl_easy_init (); @@ -457,7 +474,7 @@ url_handler (void *cls, /* NOTE: hopefully, this string won't need any URL-encoding, since as for the Jansson specs, any space and-or newline are not in place using JSON_COMPACT flag */ - deposit_body = json_dumps (j_depperm, JSON_COMPACT | JSON_PRESERVE_ORDER); + deposit_body = json_dumps (root, JSON_COMPACT | JSON_PRESERVE_ORDER); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, deposit_body); curl_res = curl_easy_perform (curl); @@ -520,31 +537,25 @@ url_handler (void *cls, GNUNET_CRYPTO_eddsa_public_key_to_string (&mint_infos[cnt].pubkey)); json_array_append_new (j_mints, j_mint); - #define DEBUGG - #ifdef DEBUG + #if 0 printf ("mint(s): url %s, key %s\n", mint_infos[cnt].hostname, GNUNET_CRYPTO_eddsa_public_key_to_string (&mint_infos[cnt].pubkey)); - str = json_dumps (j_mint, JSON_INDENT(2) | JSON_PRESERVE_ORDER); + char *str = json_dumps (j_mint, JSON_INDENT(2) | JSON_PRESERVE_ORDER); printf ("%s\n", str); #endif } - if (-1 == (json_object_update (root, j_mints))) - { - printf ("no mints specified in contract\n"); - goto end; - - - } - - /* timestamp */ now = GNUNET_TIME_absolute_get (); /* expiry */ expiry = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); - /* edate */ + /* edate, note: this value must be generated now (and not when the + wallet sends back a deposit permission because the hashed 'wire' object, + which carries this values in it, has to be included in the signed bundle + by the wallet) */ edate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); + refund = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); /* getting the SEPA-aware JSON */ /* nounce for hashing the wire object */ @@ -559,33 +570,41 @@ url_handler (void *cls, if (GNUNET_SYSERR == TALER_hash_json (j_wire, &h_json_wire)) goto end; j_h_json_wire = TALER_json_from_data (&h_json_wire, sizeof (struct GNUNET_HashCode)); - /* get key JSON entry */ + /* JSONify public key */ eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); - if (NULL == (j_contract_add = json_pack ("{s:o, s:o, s:o}", + if (NULL == (j_contract_add = json_pack ("{s:o, s:o, s:o, s:o, s:o}", "merchant_pub", eddsa_pub_enc, "H_wire", j_h_json_wire, - "timestamp", TALER_json_from_abs (now)))) + "timestamp", TALER_json_from_abs (now), + "refund", TALER_json_from_abs (refund), + "mints", j_mints))) { printf ("BAD contract enhancement\n"); goto end; } /* melt to what received from the wallet */ - if (-1 == json_object_update (j_contract_add, root)) + if (-1 == json_object_update (root, j_contract_add)) { printf ("depperm response not built\n"); goto end; } - if (GNUNET_SYSERR == MERCHANT_handle_contract (root, - db_conn, - &contract, - now, - expiry, - edate, - nounce, - contract_str)) + #if 0 + char *str = json_dumps (root, JSON_INDENT(2) | JSON_PRESERVE_ORDER); + printf ("augmented root : %s\n", str); + #endif + + + if (NULL == (contract_str = MERCHANT_handle_contract (root, + db_conn, + &contract, + now, + expiry, + edate, + refund, + nounce))) { status = MHD_HTTP_INTERNAL_SERVER_ERROR; goto end; @@ -600,7 +619,10 @@ url_handler (void *cls, * * {"contract" : {the contract in "plain" JSON}, * "sig" : base32 encoding of the signed 'struct ContractNBO', - * "eddsa_pub" : base32 encoding of merchant's public key} + * "eddsa_pub" : base32 encoding of merchant's public key, + * "h_contract" : the wallet will use this in the signature (to + * avoid neverending bugs due to how JavaScript stringifies the + * JSON respect to how libjansson does. To be soon removed.)} * */ @@ -613,7 +635,7 @@ url_handler (void *cls, "sig", j_sig_enc, "eddsa_pub", eddsa_pub_enc, "h_contract", - TALER_json_from_data (h_contract_str, sizeof (struct GNUNET_HashCode))); + TALER_json_from_data ((void *) &h_contract_str, sizeof (struct GNUNET_HashCode))); GNUNET_free (contract_str); @@ -769,7 +791,6 @@ run (void *cls, char *const *args, const char *cfgfile, /* WARNING: a 'poll_mhd ()' call is here in the original merchant. Is that mandatory ? */ - GNUNET_CRYPTO_hash (wire, sizeof (*wire), &h_wire); result = GNUNET_OK; EXITIF_exit: diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php @@ -25,7 +25,7 @@ to the wallet */ -$cli_debug = !TRUE; +$cli_debug = TRUE; // 1) recover the session information session_start(); @@ -47,12 +47,10 @@ if (!$cli_debug) else { $receiver = "Test Receiver"; - $amount = "5"; + $amount = 5; } - - /* Fill in variables for simple JSON contract */ // fake product id // --- FIXME: base on receiver for more realism! @@ -136,7 +134,7 @@ http_response_code ($status_code); // Now generate our body if ($status_code != 200) { - echo "Error while generating the contract, response code: " . $status_code; + echo "Error while generating the contract, response code: " . $status_code . "\n"; } else { diff --git a/src/tests/merchant-contract-test.c b/src/tests/merchant-contract-test.c @@ -203,11 +203,10 @@ run (void *cls, char *const *args, const char *cfgfile, j_teatax = json_pack ("{s:o}", "teatax", j_tax_amount); - if (NULL == (j_item = json_pack ("{s:s, s:I, s:o, s:I, s:[o]}", + if (NULL == (j_item = json_pack ("{s:s, s:I, s:o, s:[o]}", "description", desc, "quantity", json_integer_value (j_quantity), "itemprice", j_item_price, - "product_id", json_integer_value (j_pid), "taxes", j_teatax))) { printf ("error in packing [j_item: %p]\n", j_item); @@ -252,7 +251,8 @@ run (void *cls, char *const *args, const char *cfgfile, - j_details = json_pack ("{s:o, s:o, s:o, s:o, s:o}", + j_details = json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o}", + "product_id", json_integer_value (j_pid), "items", j_item, "delivery date", j_deldate, "delivery location", j_delloc, @@ -287,6 +287,7 @@ run (void *cls, char *const *args, const char *cfgfile, now, now, now, + now, nounce))) printf ("errors in contract handling\n");