diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-08-25 23:57:39 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-08-25 23:57:39 +0200 |
commit | deac44203154413eb11af52aa5fab6743a9f5724 (patch) | |
tree | 9a9d12afaad72dbb43aa534b8001fb8b61359731 | |
parent | 1720a924491f278580bc31182bee8f8190bc1081 (diff) | |
download | merchant-deac44203154413eb11af52aa5fab6743a9f5724.tar.gz merchant-deac44203154413eb11af52aa5fab6743a9f5724.tar.bz2 merchant-deac44203154413eb11af52aa5fab6743a9f5724.zip |
implement re-pointing to an already paid order
This is mostly useful if a resource should be displayed on
one device, but the payment happens on another device.
-rw-r--r-- | src/backend/taler-merchant-httpd_check-payment.c | 56 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_pay.c | 45 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 108 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 37 |
4 files changed, 233 insertions, 13 deletions
diff --git a/src/backend/taler-merchant-httpd_check-payment.c b/src/backend/taler-merchant-httpd_check-payment.c index 2e6cea91..9ca445b4 100644 --- a/src/backend/taler-merchant-httpd_check-payment.c +++ b/src/backend/taler-merchant-httpd_check-payment.c @@ -73,6 +73,7 @@ process_refunds_cb (void *cls, * @param session_id session of the client * @param resource_url where the resource will be (?), can be NULL! * @param h_contract_terms_str hash of the contract terms, stringified + * @param mi merchant instance * @return #MHD_YES on success */ static int @@ -80,26 +81,55 @@ send_pay_request (struct MHD_Connection *connection, const char *final_contract_url, const char *session_id, const char *resource_url, - const char *h_contract_terms_str) + const char *h_contract_terms_str, + const struct MerchantInstance *mi) { int ret; - char *url = TALER_url_absolute_mhd (connection, - "public/trigger-pay", - "contract_url", final_contract_url, - "session_id", session_id, - "resource_url", resource_url, - "h_contract_terms", h_contract_terms_str, - NULL); + int qs; + char *url; + char *already_paid_order_id = NULL; + + if ( (NULL != session_id) && (NULL != resource_url) ) + { + qs = db->find_session_info (db->cls, + &already_paid_order_id, + session_id, + resource_url, + &mi->pubkey); + if (0 > qs) + { + /* single, read-only SQL statements should never cause + serialization problems */ + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + /* Always report on hard error as well to enable diagnostics */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); + return TMH_RESPONSE_reply_internal_error (connection, + TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR, + "db error fetching pay session info"); + } + } + + url = TALER_url_absolute_mhd (connection, + "public/trigger-pay", + "contract_url", final_contract_url, + "session_id", session_id, + "resource_url", resource_url, + "h_contract_terms", h_contract_terms_str, + NULL); GNUNET_assert (NULL != url); ret = TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, - "{s:s, s:s, s:b}", + "{s:s, s:s, s:b, s:s?}", "payment_redirect_url", url, "contract_url", final_contract_url, "paid", - 0); + 0, + "already_paid_order_id", + already_paid_order_id + ); + GNUNET_free_non_null (already_paid_order_id); GNUNET_free (url); return ret; } @@ -169,7 +199,8 @@ check_order_and_request_payment (struct MHD_Connection *connection, final_contract_url, session_id, resource_url, - h_contract_terms_str); + h_contract_terms_str, + mi); GNUNET_free_non_null (h_contract_terms_str); json_decref (contract_terms); return ret; @@ -370,7 +401,8 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh, final_contract_url, session_id, resource_url, - h_contract_terms_str); + h_contract_terms_str, + mi); GNUNET_free_non_null (h_contract_terms_str); GNUNET_free (final_contract_url); json_decref (contract_terms); diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index bf15106b..bf9b5440 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -371,6 +371,12 @@ struct PayContext * Transaction ID given in @e root. */ char *order_id; + + /** + * Fulfillment URL from @e contract_terms. + */ + char *fulfillment_url; + }; @@ -610,6 +616,7 @@ pay_context_cleanup (struct TM_HandlerContext *hc) } GNUNET_free_non_null (pc->order_id); GNUNET_free_non_null (pc->session_id); + GNUNET_free_non_null (pc->fulfillment_url); GNUNET_CONTAINER_DLL_remove (pc_head, pc_tail, pc); @@ -1504,6 +1511,7 @@ parse_pay (struct MHD_Connection *connection, pc->mi->id); { + const char *fulfillment_url; struct GNUNET_JSON_Specification espec[] = { GNUNET_JSON_spec_absolute_time ("refund_deadline", &pc->refund_deadline), @@ -1515,6 +1523,8 @@ parse_pay (struct MHD_Connection *connection, &pc->max_fee), TALER_JSON_spec_amount ("amount", &pc->amount), + GNUNET_JSON_spec_string ("fulfillment_url", + &fulfillment_url), GNUNET_JSON_spec_fixed_auto ("H_wire", &pc->h_wire), GNUNET_JSON_spec_end() @@ -1530,6 +1540,8 @@ parse_pay (struct MHD_Connection *connection, return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } + pc->fulfillment_url = GNUNET_strdup (fulfillment_url); + /* Use the value from config as default. */ used_wire_transfer_delay = wire_transfer_delay; @@ -2021,11 +2033,40 @@ begin_transaction (struct PayContext *pc) ec); return; } - /* Payment succeeded, commit! */ + /* Payment succeeded, save in database */ qs = db->mark_proposal_paid (db->cls, &pc->h_contract_terms, &pc->mi->pubkey, pc->session_id); + + if (qs < 0) + { + db->rollback (db->cls); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + { + begin_transaction (pc); + return; + } + resume_pay_with_error + (pc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR, + "Merchant database error: could not " + "mark proposal as 'paid'"); + return; + } + + if ( (NULL != pc->session_id) && (NULL != pc->fulfillment_url) ) + { + qs = db->insert_session_info (db->cls, + pc->session_id, + pc->fulfillment_url, + pc->order_id, + &pc->mi->pubkey); + } + + /* Now commit! */ + if (0 <= qs) qs = db->commit (db->cls); else @@ -2045,6 +2086,8 @@ begin_transaction (struct PayContext *pc) "mark proposal as 'paid'"); return; } + + resume_pay_with_response (pc, MHD_HTTP_OK, sign_success_response (pc)); diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index 0d0f337d..1b4828e6 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -114,6 +114,7 @@ postgres_drop_tables (void *cls) GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserve_credits CASCADE;"), GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserves CASCADE;"), GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_orders CASCADE;"), + GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_session_info CASCADE;"), GNUNET_PQ_EXECUTE_STATEMENT_END }; @@ -257,6 +258,16 @@ postgres_initialize (void *cls) ",amount_frac INT4 NOT NULL" ",PRIMARY KEY (pickup_id)" ");"), + /* sessions and their order_id/resource_url mapping */ + GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_session_info (" + " session_id VARCHAR NOT NULL" + ",resource_url VARCHAR NOT NULL" + ",order_id VARCHAR NOT NULL" + ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)" + ",timestamp INT8 NOT NULL" + ",PRIMARY KEY (session_id, resource_url, merchant_pub)" + ",UNIQUE (session_id, resource_url, order_id, merchant_pub)" + ");"), GNUNET_PQ_EXECUTE_STATEMENT_END }; struct GNUNET_PQ_PreparedStatement ps[] = { @@ -326,6 +337,16 @@ postgres_initialize (void *cls) " VALUES " "($1, $2, $3, $4)", 4), + GNUNET_PQ_make_prepare ("insert_session_info", + "INSERT INTO merchant_session_info" + "(session_id" + ",resource_url" + ",order_id" + ",merchant_pub" + ",timestamp)" + " VALUES " + "($1, $2, $3, $4, $5)", + 4), GNUNET_PQ_make_prepare ("mark_proposal_paid", "UPDATE merchant_contract_terms SET" " paid=TRUE, last_session_id=$3" @@ -413,6 +434,15 @@ postgres_initialize (void *cls) " order_id=$1" " AND merchant_pub=$2", 2), + GNUNET_PQ_make_prepare ("find_session_info", + "SELECT" + " order_id" + " FROM merchant_session_info" + " WHERE" + " resource_url=$1" + " AND session_id=$2" + " AND merchant_pub=$3", + 2), GNUNET_PQ_make_prepare ("find_contract_terms_by_date", "SELECT" " contract_terms" @@ -1111,6 +1141,82 @@ postgres_mark_proposal_paid (void *cls, /** + * Store the order ID that was used to pay for a resource within a session. + * + * @param cls closure + * @param session_id session id + * @param resource_url URL that canonically identifies the resource + * being paid for + * @param order_id the order ID that was used when paying for the resource URL + * @param merchant_pub public key of the merchant, identifying the instance + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +postgres_insert_session_info (void *cls, + const char *session_id, + const char *resource_url, + const char *order_id, + const struct TALER_MerchantPublicKeyP *merchant_pub) +{ + struct PostgresClosure *pg = cls; + + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (session_id), + GNUNET_PQ_query_param_auto_from_type (resource_url), + GNUNET_PQ_query_param_auto_from_type (order_id), + GNUNET_PQ_query_param_auto_from_type (merchant_pub), + GNUNET_PQ_query_param_absolute_time (&now), + GNUNET_PQ_query_param_end + }; + + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_session_info", + params); +} + +/** + * Retrieve the order ID that was used to pay for a resource within a session. + * + * @param cls closure + * @param[out] order_id location to store the order ID that was used when + * paying for the resource URL + * @param session_id session id + * @param resource_url URL that canonically identifies the resource + * being paid for + * @param merchant_pub public key of the merchant, identifying the instance + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +postgres_find_session_info (void *cls, + char **order_id, + const char *session_id, + const char *resource_url, + const struct TALER_MerchantPublicKeyP *merchant_pub) +{ + struct PostgresClosure *pg = cls; + + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (session_id), + GNUNET_PQ_query_param_auto_from_type (resource_url), + GNUNET_PQ_query_param_auto_from_type (merchant_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("order_id", + order_id), + GNUNET_PQ_result_spec_end + }; + // We don't clean up the result spec since we want + // to keep around the memory for order_id. + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "find_contract_terms_history", + params, + rs); +} + + +/** * Insert payment confirmation from the exchange into the database. * * @param cls closure @@ -3559,6 +3665,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->lookup_wire_fee = &postgres_lookup_wire_fee; plugin->increase_refund_for_contract_NT = &postgres_increase_refund_for_contract_NT; plugin->mark_proposal_paid = &postgres_mark_proposal_paid; + plugin->insert_session_info = &postgres_insert_session_info; + plugin->find_session_info = &postgres_find_session_info; plugin->enable_tip_reserve_TR = &postgres_enable_tip_reserve_TR; plugin->authorize_tip_TR = &postgres_authorize_tip_TR; plugin->lookup_tip_by_id = &postgres_lookup_tip_by_id; diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index f2dad94a..7d4948e1 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -250,6 +250,43 @@ struct TALER_MERCHANTDB_Plugin const char *last_session_id); /** + * Store the order ID that was used to pay for a resource within a session. + * + * @param cls closure + * @param session_id session id + * @param resource_url URL that canonically identifies the resource + * being paid for + * @param order_id the order ID that was used when paying for the resource URL + * @param merchant_pub public key of the merchant, identifying the instance + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*insert_session_info) (void *cls, + const char *session_id, + const char *resource_url, + const char *order_id, + const struct TALER_MerchantPublicKeyP *merchant_pub); + + /** + * Retrieve the order ID that was used to pay for a resource within a session. + * + * @param cls closure + * @param[out] order_id location to store the order ID that was used when + * paying for the resource URL + * @param session_id session id + * @param resource_url URL that canonically identifies the resource + * being paid for + * @param merchant_pub public key of the merchant, identifying the instance + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*find_session_info) (void *cls, + char **order_id, + const char *session_id, + const char *resource_url, + const struct TALER_MerchantPublicKeyP *merchant_pub); + + /** * Retrieve proposal data given its order ID. * * @param cls closure |