summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-05-04 00:25:27 +0200
committerChristian Grothoff <christian@grothoff.org>2020-05-04 00:25:27 +0200
commite138cd0e5adff973cf1f065e3fd5588f4bd33ddc (patch)
tree8621e100caa867fcf64708ead7b11f7b1d892485
parent2a5388a263a2edb63dee7865d9da2d9b6db65f6e (diff)
downloadmerchant-e138cd0e5adff973cf1f065e3fd5588f4bd33ddc.tar.gz
merchant-e138cd0e5adff973cf1f065e3fd5588f4bd33ddc.tar.bz2
merchant-e138cd0e5adff973cf1f065e3fd5588f4bd33ddc.zip
starting with get-orders-ID logic
-rw-r--r--src/backend/taler-merchant-httpd_get-orders-ID-2.c582
-rw-r--r--src/backend/taler-merchant-httpd_get-orders-ID-2.h47
-rw-r--r--src/backend/taler-merchant-httpd_get-orders-ID.c723
-rw-r--r--src/backend/taler-merchant-httpd_get-orders-ID.h28
4 files changed, 542 insertions, 838 deletions
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID-2.c b/src/backend/taler-merchant-httpd_get-orders-ID-2.c
deleted file mode 100644
index 6ca4fcce..00000000
--- a/src/backend/taler-merchant-httpd_get-orders-ID-2.c
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017, 2019 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file backend/taler-merchant-httpd_poll-payment.c
- * @brief implementation of /public/poll-payment handler
- * @author Florian Dold
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <string.h>
-#include <microhttpd.h>
-#include <jansson.h>
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include "taler-merchant-httpd.h"
-#include "taler-merchant-httpd_mhd.h"
-#include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_poll-payment.h"
-
-/**
- * Maximum number of retries for database operations.
- */
-#define MAX_RETRIES 5
-
-
-/**
- * Data structure we keep for a check payment request.
- */
-struct PollPaymentRequestContext
-{
- /**
- * Must be first for #handle_mhd_completion_callback.
- */
- struct TM_HandlerContext hc;
-
- /**
- * Entry in the #resume_timeout_heap for this check payment, if we are
- * suspended.
- */
- struct TMH_SuspendedConnection sc;
-
- /**
- * Which merchant instance is this for?
- */
- struct MerchantInstance *mi;
-
- /**
- * URL where the final contract can be found for this payment.
- */
- char *final_contract_url;
-
- /**
- * order ID for the payment
- */
- const char *order_id;
-
- /**
- * Where to get the contract
- */
- const char *contract_url;
-
- /**
- * fulfillment URL of the contract (valid as long as
- * @e contract_terms is valid).
- */
- const char *fulfillment_url;
-
- /**
- * session of the client
- */
- const char *session_id;
-
- /**
- * Contract terms of the payment we are checking. NULL when they
- * are not (yet) known.
- */
- json_t *contract_terms;
-
- /**
- * Hash of @e contract_terms, set only once @e contract_terms
- * is available.
- */
- struct GNUNET_HashCode h_contract_terms;
-
- /**
- * Total refunds granted for this payment. Only initialized
- * if @e refunded is set to #GNUNET_YES.
- */
- struct TALER_Amount refund_amount;
-
- /**
- * Minimum refund amount the client would like to poll for.
- * Only initialized if
- * @e awaiting_refund is set to #GNUNET_YES.
- */
- struct TALER_Amount min_refund;
-
- /**
- * Set to #GNUNET_YES if this payment has been refunded and
- * @e refund_amount is initialized.
- */
- int refunded;
-
- /**
- * Set to #GNUNET_YES if this client is waiting for a refund.
- */
- int awaiting_refund;
-
- /**
- * Initially #GNUNET_SYSERR. If we queued a response, set to the
- * result code (i.e. #MHD_YES or #MHD_NO). FIXME: fix type!
- */
- int ret;
-
-};
-
-
-/**
- * Clean up the session state for a check payment request.
- *
- * @param hc must be a `struct PollPaymentRequestContext *`
- */
-static void
-pprc_cleanup (struct TM_HandlerContext *hc)
-{
- struct PollPaymentRequestContext *pprc
- = (struct PollPaymentRequestContext *) hc;
-
- if (NULL != pprc->contract_terms)
- json_decref (pprc->contract_terms);
- GNUNET_free_non_null (pprc->final_contract_url);
- GNUNET_free (pprc);
-}
-
-
-/**
- * Function called with information about a refund.
- * It is responsible for summing up the refund amount.
- *
- * @param cls closure
- * @param coin_pub public coin from which the refund comes from
- * @param exchange_url URL of the exchange that issued @a coin_pub
- * @param rtransaction_id identificator of the refund
- * @param reason human-readable explanation of the refund
- * @param refund_amount refund amount which is being taken from @a coin_pub
- * @param refund_fee cost of this refund operation
- */
-static void
-process_refunds_cb (void *cls,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const char *exchange_url,
- uint64_t rtransaction_id,
- const char *reason,
- const struct TALER_Amount *refund_amount,
- const struct TALER_Amount *refund_fee)
-{
- struct PollPaymentRequestContext *pprc = cls;
-
- if (pprc->refunded)
- {
- GNUNET_assert (0 <=
- TALER_amount_add (&pprc->refund_amount,
- &pprc->refund_amount,
- refund_amount));
- return;
- }
- pprc->refund_amount = *refund_amount;
- pprc->refunded = GNUNET_YES;
-}
-
-
-/**
- * Suspend this @a pprc until the trigger is satisfied.
- *
- * @param ppr
- */
-static void
-suspend_pprc (struct PollPaymentRequestContext *pprc)
-{
- TMH_compute_pay_key (pprc->order_id,
- &pprc->mi->pubkey,
- &pprc->sc.key);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Suspending /poll-payment on key %s\n",
- GNUNET_h2s (&pprc->sc.key));
- TMH_long_poll_suspend (&pprc->sc,
- (pprc->awaiting_refund)
- ? &pprc->min_refund
- : NULL);
-}
-
-
-/**
- * The client did not yet pay, send it the payment request.
- *
- * @param pprc check pay request context
- * @return #MHD_YES on success
- */
-static MHD_RESULT
-send_pay_request (struct PollPaymentRequestContext *pprc)
-{
- MHD_RESULT ret;
- char *already_paid_order_id = NULL;
- char *taler_pay_uri;
- struct GNUNET_TIME_Relative remaining;
-
- remaining = GNUNET_TIME_absolute_get_remaining (pprc->sc.long_poll_timeout);
- if (0 != remaining.rel_value_us)
- {
- /* long polling: do not queue a response, suspend connection instead */
- suspend_pprc (pprc);
- return MHD_YES;
- }
-
- /* Check if resource_id has been paid for in the same session
- * with another order_id.
- */
- if ( (NULL != pprc->session_id) &&
- (NULL != pprc->fulfillment_url) )
- {
- enum GNUNET_DB_QueryStatus qs;
-
- qs = db->find_session_info (db->cls,
- &already_paid_order_id,
- pprc->session_id,
- pprc->fulfillment_url,
- &pprc->mi->pubkey);
- if (qs < 0)
- {
- /* 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 TALER_MHD_reply_with_error (pprc->sc.con,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
- "db error fetching pay session info");
- }
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Sending payment request in /poll-payment\n");
- taler_pay_uri = TMH_make_taler_pay_uri (pprc->sc.con,
- pprc->order_id,
- pprc->session_id,
- pprc->mi->id);
- ret = TALER_MHD_reply_json_pack (pprc->sc.con,
- MHD_HTTP_OK,
- "{s:s, s:s, s:b, s:s?}",
- "taler_pay_uri", taler_pay_uri,
- "contract_url", pprc->final_contract_url,
- "paid", 0,
- "already_paid_order_id",
- already_paid_order_id);
- GNUNET_free (taler_pay_uri);
- GNUNET_free_non_null (already_paid_order_id);
- return ret;
-}
-
-
-/**
- * Manages a /public/poll-payment call, checking the status
- * of a payment and, if necessary, constructing the URL
- * for a payment redirect URL.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
- * @return MHD result code
- */
-MHD_RESULT
-MH_handler_poll_payment (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi)
-{
- struct PollPaymentRequestContext *pprc = *connection_cls;
- enum GNUNET_DB_QueryStatus qs;
- MHD_RESULT ret;
-
- if (NULL == pprc)
- {
- /* First time here, parse request and check order is known */
- const char *long_poll_timeout_s;
- const char *cts;
- const char *min_refund;
-
- pprc = GNUNET_new (struct PollPaymentRequestContext);
- pprc->hc.cc = &pprc_cleanup;
- pprc->ret = GNUNET_SYSERR;
- pprc->sc.con = connection;
- pprc->mi = mi;
- *connection_cls = pprc;
-
- pprc->order_id = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "order_id");
- if (NULL == pprc->order_id)
- {
- /* order_id is required but missing */
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_PARAMETER_MISSING,
- "order_id required");
- }
- cts = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "h_contract");
- if (NULL == cts)
- {
- /* h_contract required but missing */
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_PARAMETER_MISSING,
- "h_contract required");
- }
- if (GNUNET_OK !=
- GNUNET_CRYPTO_hash_from_string (cts,
- &pprc->h_contract_terms))
- {
- /* cts has wrong encoding */
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_PARAMETER_MALFORMED,
- "h_contract malformed");
- }
- long_poll_timeout_s = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "timeout");
- if (NULL != long_poll_timeout_s)
- {
- unsigned int timeout;
-
- if (1 != sscanf (long_poll_timeout_s,
- "%u",
- &timeout))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_PARAMETER_MALFORMED,
- "timeout must be non-negative number");
- }
- pprc->sc.long_poll_timeout
- = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (
- GNUNET_TIME_UNIT_SECONDS,
- timeout));
- }
- else
- {
- pprc->sc.long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS;
- }
-
- min_refund = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "refund");
- if (NULL != min_refund)
- {
- if ( (GNUNET_OK !=
- TALER_string_to_amount (min_refund,
- &pprc->min_refund)) ||
- (0 != strcasecmp (pprc->min_refund.currency,
- TMH_currency) ) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_PARAMETER_MALFORMED,
- "invalid amount given for refund argument");
- }
- pprc->awaiting_refund = GNUNET_YES;
- }
-
- pprc->contract_url = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "contract_url");
- if (NULL == pprc->contract_url)
- {
- pprc->final_contract_url = TALER_url_absolute_mhd (connection,
- "/public/proposal",
- "instance", mi->id,
- "order_id",
- pprc->order_id,
- NULL);
- GNUNET_assert (NULL != pprc->final_contract_url);
- }
- else
- {
- pprc->final_contract_url = GNUNET_strdup (pprc->contract_url);
- }
- pprc->session_id = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "session_id");
-
- /* obtain contract terms, indirectly checking that the client's contract
- terms hash is actually valid and known. */
- db->preflight (db->cls);
- qs = db->find_contract_terms_from_hash (db->cls,
- &pprc->contract_terms,
- &pprc->h_contract_terms,
- &mi->pubkey);
- if (0 > qs)
- {
- /* Always report on hard error as well to enable diagnostics */
- GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
- "Merchant database error");
- }
- if (0 == qs)
- {
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_POLL_PAYMENT_CONTRACT_NOT_FOUND,
- "Given order_id doesn't map to any proposal");
- }
- GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
-
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("fulfillment_url",
- &pprc->fulfillment_url),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (pprc->contract_terms,
- spec,
- NULL, NULL))
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
- "Merchant database error (contract terms corrupted)");
- }
- }
- } /* end of first-time initialization / sanity checks */
-
- db->preflight (db->cls);
-
- /* Check if the order has been paid for. */
- if (NULL != pprc->session_id)
- {
- /* Check if paid within a session. */
- char *already_paid_order_id = NULL;
-
- qs = db->find_session_info (db->cls,
- &already_paid_order_id,
- pprc->session_id,
- pprc->fulfillment_url,
- &mi->pubkey);
- if (qs < 0)
- {
- /* 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 TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
- "db error fetching pay session info");
- }
- else if (0 == qs)
- {
- ret = send_pay_request (pprc);
- GNUNET_free_non_null (already_paid_order_id);
- return ret;
- }
- GNUNET_break (1 == qs);
- GNUNET_break (0 == strcmp (pprc->order_id,
- already_paid_order_id));
- GNUNET_free_non_null (already_paid_order_id);
- }
- else
- {
- /* Check if paid regardless of session. */
- json_t *xcontract_terms = NULL;
-
- qs = db->find_paid_contract_terms_from_hash (db->cls,
- &xcontract_terms,
- &pprc->h_contract_terms,
- &mi->pubkey);
- if (0 > qs)
- {
- /* Always report on hard error as well to enable diagnostics */
- GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
- "Merchant database error");
- }
- if (0 == qs)
- {
- return send_pay_request (pprc);
- }
- GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
- GNUNET_assert (NULL != xcontract_terms);
- json_decref (xcontract_terms);
- }
-
- /* Accumulate refunds, if any. */
- for (unsigned int i = 0; i<MAX_RETRIES; i++)
- {
- pprc->refunded = GNUNET_NO;
- qs = db->get_refunds_from_contract_terms_hash (db->cls,
- &mi->pubkey,
- &pprc->h_contract_terms,
- &process_refunds_cb,
- pprc);
- if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
- break;
- }
- if (0 > qs)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Database hard error on refunds_from_contract_terms_hash lookup: %s\n",
- GNUNET_h2s (&pprc->h_contract_terms));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
- "Merchant database error");
- }
- if ( (pprc->awaiting_refund) &&
- ( (! pprc->refunded) ||
- (1 != TALER_amount_cmp (&pprc->refund_amount,
- &pprc->min_refund)) ) )
- {
- /* Client is waiting for a refund larger than what we have, suspend
- until timeout */
- struct GNUNET_TIME_Relative remaining;
-
- remaining = GNUNET_TIME_absolute_get_remaining (pprc->sc.long_poll_timeout);
- if (0 != remaining.rel_value_us)
- {
- /* yes, indeed suspend */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Awaiting refund exceeding %s\n",
- TALER_amount2s (&pprc->min_refund));
- suspend_pprc (pprc);
- return MHD_YES;
- }
- }
-
- if (pprc->refunded)
- return TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:b, s:b, s:o}",
- "paid", 1,
- "refunded", pprc->refunded,
- "refund_amount",
- TALER_JSON_from_amount (
- &pprc->refund_amount));
- return TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:b, s:b }",
- "paid", 1,
- "refunded", 0);
-}
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID-2.h b/src/backend/taler-merchant-httpd_get-orders-ID-2.h
deleted file mode 100644
index ac13c4a3..00000000
--- a/src/backend/taler-merchant-httpd_get-orders-ID-2.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file backend/taler-merchant-httpd_poll-payment.h
- * @brief headers for /public/poll-payment handler
- * @author Christian Grothoff
- * @author Florian Dold
- */
-#ifndef TALER_MERCHANT_HTTPD_POLL_PAYMENT_H
-#define TALER_MERCHANT_HTTPD_POLL_PAYMENT_H
-#include <microhttpd.h>
-#include "taler-merchant-httpd.h"
-
-/**
- * Manages a /public/poll-payment call, checking the status
- * of a payment.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
- * @return MHD result code
- */
-MHD_RESULT
-MH_handler_poll_payment (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
-
-#endif
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c
index e86e4e4b..ba0d3544 100644
--- a/src/backend/taler-merchant-httpd_get-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_get-orders-ID.c
@@ -14,9 +14,10 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file backend/taler-merchant-httpd_refund_lookup.c
- * @brief refund handling logic
+ * @file backend/taler-merchant-httpd_get-orders-ID.c
+ * @brief implementation of GET /orders/$ID
* @author Marcello Stanisci
+ * @author Christian Grothoff
*/
#include "platform.h"
#include <jansson.h>
@@ -59,9 +60,9 @@ struct CoinRefund
struct TALER_EXCHANGE_RefundHandle *rh;
/**
- * PRD this operation is part of.
+ * Request this operation is part of.
*/
- struct ProcessRefundData *prd;
+ struct GetOrderData *god;
/**
* URL of the exchange for this @e coin_pub.
@@ -119,15 +120,10 @@ struct CoinRefund
/**
- * Closure for #process_refunds_cb.
+ * Context for the operation.
*/
-struct ProcessRefundData
+struct GetOrderData
{
- /**
- * Must be first for #handle_mhd_completion_callback() cleanup
- * logic to work.
- */
- struct TM_HandlerContext hc;
/**
* Hashed version of contract terms.
@@ -137,106 +133,186 @@ struct ProcessRefundData
/**
* DLL of (suspended) requests.
*/
- struct ProcessRefundData *next;
+ struct GetOrderData *next;
/**
* DLL of (suspended) requests.
*/
- struct ProcessRefundData *prev;
+ struct GetOrderData *prev;
/**
- * Head of DLL of coin refunds for this request.
+ * Context of the request.
*/
- struct CoinRefund *cr_head;
+ struct TMH_HandlerContext *hc;
/**
- * Tail of DLL of coin refunds for this request.
+ * Did we suspend @a connection?
*/
- struct CoinRefund *cr_tail;
+ int suspended;
/**
- * Both public and private key are needed by the callback
+ * Return code: #TALER_EC_NONE if successful.
*/
- const struct MerchantInstance *merchant;
+ enum TALER_ErrorCode ec;
/**
- * Connection we are handling.
+ * Entry in the #resume_timeout_heap for this check payment, if we are
+ * suspended.
*/
- struct MHD_Connection *connection;
+ struct TMH_SuspendedConnection sc;
/**
- * Did we suspend @a connection?
+ * Which merchant instance is this for?
*/
- int suspended;
+ struct MerchantInstance *mi;
/**
- * Return code: #TALER_EC_NONE if successful.
+ * URL where the final contract can be found for this payment.
*/
- enum TALER_ErrorCode ec;
+ char *final_contract_url;
+
+ /**
+ * order ID for the payment
+ */
+ const char *order_id;
+
+ /**
+ * Where to get the contract
+ */
+ const char *contract_url;
+
+ /**
+ * fulfillment URL of the contract (valid as long as
+ * @e contract_terms is valid).
+ */
+ const char *fulfillment_url;
+
+ /**
+ * session of the client
+ */
+ const char *session_id;
+
+ /**
+ * Contract terms of the payment we are checking. NULL when they
+ * are not (yet) known.
+ */
+ json_t *contract_terms;
+
+ /**
+ * Hash of @e contract_terms, set only once @e contract_terms
+ * is available.
+ */
+ struct GNUNET_HashCode h_contract_terms;
+
+ /**
+ * Total refunds granted for this payment. Only initialized
+ * if @e refunded is set to true.
+ */
+ struct TALER_Amount refund_amount;
+
+ /**
+ * Set to true if this payment has been refunded and
+ * @e refund_amount is initialized.
+ */
+ bool refunded;
+
+ /**
+ * Initially #GNUNET_SYSERR. If we queued a response, set to the
+ * result code (i.e. #MHD_YES or #MHD_NO). FIXME: fix type!
+ */
+ int ret;
+
+
};
/**
* HEad of DLL of (suspended) requests.
*/
-static struct ProcessRefundData *prd_head;
+static struct GetOrderData *prd_head;
/**
* Tail of DLL of (suspended) requests.
*/
-static struct ProcessRefundData *prd_tail;
+static struct GetOrderData *prd_tail;
+
+
+/**
+ * Force resuming all suspended order lookups, needed during shutdown.
+ */
+void
+TMH_force_wallet_get_order_resume (void)
+{
+ struct GetOrderData *god;
+
+ while (NULL != (god = god_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (god_head,
+ god_tail,
+ god);
+ GNUNET_assert (god->suspended);
+ god->suspended = GNUNET_NO;
+ MHD_resume_connection (god->connection);
+ }
+}
/**
- * Clean up memory in @a cls, the connection was closed.
+ * The client did not yet pay, send it the payment request.
*
- * @param cls a `struct ProcessRefundData` to clean up.
+ * @param god check pay request context
+ * @return #MHD_YES on success
*/
-static void
-cleanup_prd (struct TM_HandlerContext *cls)
+static MHD_RESULT
+send_pay_request (struct GetOrderData *god,
+ const char *already_paid_order_id)
{
- struct ProcessRefundData *prd = (struct ProcessRefundData *) cls;
- struct CoinRefund *cr;
+ MHD_RESULT ret;
+ char *taler_pay_uri;
+ struct GNUNET_TIME_Relative remaining;
- while (NULL != (cr = prd->cr_head))
+ remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
+ if (0 != remaining.rel_value_us)
{
- GNUNET_CONTAINER_DLL_remove (prd->cr_head,
- prd->cr_tail,
- cr);
- if (NULL != cr->fo)
- {
- TMH_EXCHANGES_find_exchange_cancel (cr->fo);
- cr->fo = NULL;
- }
- if (NULL != cr->rh)
- {
- TALER_EXCHANGE_refund_cancel (cr->rh);
- cr->rh = NULL;
- }
- if (NULL != cr->exchange_reply)
- {
- json_decref (cr->exchange_reply);
- cr->exchange_reply = NULL;
- }
- GNUNET_free (cr->exchange_url);
- GNUNET_free (cr);
+ /* long polling: do not queue a response, suspend connection instead */
+ suspend_god (god);
+ return MHD_YES;
}
- GNUNET_free (prd);
+
+ /* Check if resource_id has been paid for in the same session
+ * with another order_id.
+ */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Sending payment request in /poll-payment\n");
+ taler_pay_uri = TMH_make_taler_pay_uri (god->sc.con,
+ god->order_id,
+ god->session_id,
+ god->mi->id);
+ ret = TALER_MHD_reply_json_pack (god->sc.con,
+ MHD_HTTP_OK,
+ "{s:s, s:s, s:b, s:s?}",
+ "taler_pay_uri", taler_pay_uri,
+ "contract_url", god->final_contract_url,
+ "paid", 0,
+ "already_paid_order_id",
+ already_paid_order_id);
+ GNUNET_free (taler_pay_uri);
+ return ret;
}
/**
- * Check if @a prd has sub-activities still pending.
+ * Check if @a god has sub-activities still pending.
*
- * @param prd request to check
+ * @param god request to check
* @return #GNUNET_YES if activities are still pending
*/
static int
-prd_pending (struct ProcessRefundData *prd)
+god_pending (struct GetOrderData *god)
{
int pending = GNUNET_NO;
- for (struct CoinRefund *cr = prd->cr_head;
+ for (struct CoinRefund *cr = god->cr_head;
NULL != cr;
cr = cr->next)
{
@@ -252,21 +328,21 @@ prd_pending (struct ProcessRefundData *prd)
/**
- * Check if @a prd is ready to be resumed, and if so, do it.
+ * Check if @a god is ready to be resumed, and if so, do it.
*
- * @param prd refund request to be possibly ready
+ * @param god refund request to be possibly ready
*/
static void
-check_resume_prd (struct ProcessRefundData *prd)
+check_resume_god (struct GetOrderData *god)
{
- if (prd_pending (prd))
+ if (god_pending (god))
return;
- GNUNET_CONTAINER_DLL_remove (prd_head,
- prd_tail,
- prd);
- GNUNET_assert (prd->suspended);
- prd->suspended = GNUNET_NO;
- MHD_resume_connection (prd->connection);
+ GNUNET_CONTAINER_DLL_remove (god_head,
+ god_tail,
+ god);
+ GNUNET_assert (god->suspended);
+ god->suspended = GNUNET_NO;
+ MHD_resume_connection (god->connection);
TMH_trigger_daemon ();
}
@@ -306,8 +382,8 @@ refund_cb (void *cls,
cr->exchange_pub = *exchange_pub;
cr->exchange_sig = *exchange_sig;
qs = db->put_refund_proof (db->cls,
- &cr->prd->merchant->pubkey,
- &cr->prd->h_contract_terms,
+ &cr->god->merchant->pubkey,
+ &cr->god->h_contract_terms,
&cr->coin_pub,
cr->rtransaction_id,
exchange_pub,
@@ -321,7 +397,7 @@ refund_cb (void *cls,
qs);
}
}
- check_resume_prd (cr->prd);
+ check_resume_god (cr->god);
}
@@ -350,10 +426,10 @@ exchange_found_cb (void *cls,
cr->rh = TALER_EXCHANGE_refund (eh,
&cr->refund_amount,
&cr->refund_fee,
- &cr->prd->h_contract_terms,
+ &cr->god->h_contract_terms,
&cr->coin_pub,
cr->rtransaction_id,
- &cr->prd->merchant->privkey,
+ &cr->god->merchant->privkey,
&refund_cb,
cr);
return;
@@ -361,7 +437,7 @@ exchange_found_cb (void *cls,
cr->exchange_status = hr->http_status;
cr->exchange_code = hr->ec;
cr->exchange_reply = json_incref ((json_t*) hr->reply);
- check_resume_prd (cr->prd);
+ check_resume_god (cr->god);
}
@@ -386,7 +462,7 @@ process_refunds_cb (void *cls,
const struct TALER_Amount *refund_amount,
const struct TALER_Amount *refund_fee)
{
- struct ProcessRefundData *prd = cls;
+ struct GetOrderData *god = cls;
struct CoinRefund *cr;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -396,83 +472,230 @@ process_refunds_cb (void *cls,
reason);
cr = GNUNET_new (struct CoinRefund);
cr->exchange_url = GNUNET_strdup (exchange_url);
- cr->prd = prd;
+ cr->god = god;
cr->coin_pub = *coin_pub;
cr->rtransaction_id = rtransaction_id;
cr->refund_amount = *refund_amount;
cr->refund_fee = *refund_fee;
- GNUNET_CONTAINER_DLL_insert (prd->cr_head,
- prd->cr_tail,
+ GNUNET_CONTAINER_DLL_insert (god->cr_head,
+ god->cr_tail,
cr);
+ if (god->refunded)
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_add (&god->refund_amount,
+ &god->refund_amount,
+ refund_amount));
+ return;
+ }
+ god->refund_amount = *refund_amount;
+ god->refunded = true;
}
/**
- * Force resuming all suspended refund lookups, needed during shutdown.
+ * Suspend this @a god until the trigger is satisfied.
+ *
+ * @param ppr
*/
-void
-MH_force_refund_resume (void)
+static void
+suspend_god (struct GetOrderData *god)
{
- struct ProcessRefundData *prd;
+ TMH_compute_pay_key (god->order_id,
+ &god->mi->pubkey,
+ &god->sc.key);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Suspending /poll-payment on key %s\n",
+ GNUNET_h2s (&god->sc.key));
+ TMH_long_poll_suspend (&god->sc,
+ (god->sc.awaiting_refund)
+ ? &god->sc.refund_expected
+ : NULL);
+}
- while (NULL != (prd = prd_head))
+
+/**
+ * Clean up the session state for a GET /orders/$ID request.
+ *
+ * @param cls must be a `struct GetOrderData *`
+ */
+static void
+god_cleanup (void *cls)
+{
+ struct GetOrderData *god = cls;
+
+ while (NULL != (cr = god->cr_head))
{
- GNUNET_CONTAINER_DLL_remove (prd_head,
- prd_tail,
- prd);
- GNUNET_assert (prd->suspended);
- prd->suspended = GNUNET_NO;
- MHD_resume_connection (prd->connection);
+ GNUNET_CONTAINER_DLL_remove (god->cr_head,
+ god->cr_tail,
+ cr);
+ if (NULL != cr->fo)
+ {
+ TMH_EXCHANGES_find_exchange_cancel (cr->fo);
+ cr->fo = NULL;
+ }
+ if (NULL != cr->rh)
+ {
+ TALER_EXCHANGE_refund_cancel (cr->rh);
+ cr->rh = NULL;
+ }
+ if (NULL != cr->exchange_reply)
+ {
+ json_decref (cr->exchange_reply);
+ cr->exchange_reply = NULL;
+ }
+ GNUNET_free (cr->exchange_url);
+ GNUNET_free (cr);
}
+
+ if (NULL != god->contract_terms)
+ json_decref (god->contract_terms);
+ GNUNET_free_non_null (god->final_contract_url);
+ GNUNET_free (god);
}
/**
- * Return refund situation about a contract.
+ * Handle a GET "/orders/$ID" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc context with further information about the request
* @return MHD result code
*/
MHD_RESULT
-MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi)
+TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
{
- struct ProcessRefundData *prd;
- const char *order_id;
- json_t *contract_terms;
+ struct GetOrderData *god = hc->ctx;
+ const char *order_id = hc->infix;
enum GNUNET_DB_QueryStatus qs;
- prd = *connection_cls;
- if (NULL == prd)
+ if (NULL == god)
{
- order_id = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "order_id");
- if (NULL == order_id)
+ god = GNUNET_new (struct GetOrderData);
+ hc->ctx = god;
+ god->hc.cc = &god_cleanup;
+ god->sc.con = connection;
+ god->ec = TALER_EC_NONE;
+ god->connection = connection;
+ god->hc = hc;
+ god->ret = GNUNET_SYSERR;
+
{
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_PARAMETER_MISSING,
- "order_id");
+ const char *cts;
+
+ cts = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "h_contract");
+ if (NULL == cts)
+ {
+ /* h_contract required but missing */
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PARAMETER_MISSING,
+ "h_contract required");
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_hash_from_string (cts,
+ &god->h_contract_terms))
+ {
+ /* cts has wrong encoding */
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PARAMETER_MALFORMED,
+ "h_contract malformed");
+ }
+ }
+
+ {
+ const char *long_poll_timeout_s;
+
+ long_poll_timeout_s = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "timeout");
+ if (NULL != long_poll_timeout_s)
+ {
+ unsigned int timeout;
+
+ if (1 != sscanf (long_poll_timeout_s,
+ "%u",
+ &timeout))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PARAMETER_MALFORMED,
+ "timeout must be non-negative number");
+ }
+ god->sc.long_poll_timeout
+ = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_SECONDS,
+ timeout));
+ }
+ else
+ {
+ god->sc.long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS;
+ }
}
+ {
+ const char *min_refund;
+
+ min_refund = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "refund");
+ if (NULL != min_refund)
+ {
+ if ( (GNUNET_OK !=
+ TALER_string_to_amount (min_refund,
+ &god->sc.refund_expected)) ||
+ (0 != strcasecmp (god->sc.refund_expected.currency,
+ TMH_currency) ) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PARAMETER_MALFORMED,
+ "invalid amount given for refund argument");
+ }
+ god->sc.awaiting_refund = true;
+ }
+ }
+
+ // FIXME: what is this about again???
+ god->contract_url = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "contract_url");
+ if (NULL == god->contract_url)
+ {
+ // FIXME: this can't be right anymore...
+ god->final_contract_url = TALER_url_absolute_mhd (connection,
+ "/public/proposal",
+ "instance", mi->id,
+ "order_id",
+ god->order_id,
+ NULL);
+ GNUNET_assert (NULL != god->final_contract_url);
+ }
+ else
+ {
+ god->final_contract_url = GNUNET_strdup (god->contract_url);
+ }
+
+ god->session_id = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "session_id");
+
/* Convert order id to h_contract_terms */
- contract_terms = NULL;
db->preflight (db->cls);
qs = db->find_contract_terms (db->cls,
- &contract_terms,
+ hc->instance->settings.id,
order_id,
- &mi->pubkey);
+ &god->contract_terms);
if (0 > qs)
{
/* single, read-only SQL statements should never cause
@@ -483,9 +706,8 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_REFUND_LOOKUP_DB_ERROR,
- "database error looking up order_id from merchant_contract_terms table");
+ "database error looking up contract");
}
-
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -493,108 +715,210 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
order_id);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_ORDER_ID_UNKNOWN,
+ TALER_EC_ORDER_ID_UNKNOWN,
"order_id not found in database");
}
- prd = GNUNET_new (struct ProcessRefundData);
- if (GNUNET_OK !=
- TALER_JSON_hash (contract_terms,
- &prd->h_contract_terms))
+ /* Check client provided the right hash code of the contract terms */
+ {
+ struct GNUNET_HashCode h;
+
+ if (GNUNET_OK !=
+ TALER_JSON_hash (god->contract_terms,
+ &h))
+ {
+ GNUNET_break (0);
+ GNUNET_free (god);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_INTERNAL_LOGIC_ERROR,
+ "Could not hash contract terms");
+ }
+ if (0 !=
+ GNUNET_memcmp (&h,
+ &god->h_contract_terms))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_WRONG_CONTRACT,
+ "Contract hash does not match order");
+ }
+ }
+
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("fulfillment_url",
+ &god->fulfillment_url),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (god->contract_terms,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
+ "Merchant database error (contract terms corrupted)");
+ }
+ }
+ } /* end of first-time initialization / sanity checks */
+
+ if ( (NULL != god->session_id) &&
+ (NULL != god->fulfillment_url) )
+ {
+ /* Check if paid within a session. */
+ char *already_paid_order_id = NULL;
+
+ qs = db->find_session_info (db->cls,
+ &already_paid_order_id,
+ god->session_id,
+ god->fulfillment_url,
+ &mi->pubkey);
+ if (qs < 0)
{
- GNUNET_break (0);
- json_decref (contract_terms);
- GNUNET_free (prd);
+ /* 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 TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_INTERNAL_LOGIC_ERROR,
- "Could not hash contract terms");
+ TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
+ "db error fetching pay session info");
}
- json_decref (contract_terms);
- prd->hc.cc = &cleanup_prd;
- prd->merchant = mi;
- prd->ec = TALER_EC_NONE;
- prd->connection = connection;
- *connection_cls = prd;
-
- for (unsigned int i = 0; i<MAX_RETRIES; i++)
+ else if (0 == qs)
{
- qs = db->get_refunds_from_contract_terms_hash (db->cls,
- &mi->pubkey,
- &prd->h_contract_terms,
- &process_refunds_cb,
- prd);
- if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
- break;
+ ret = send_pay_request (god,
+ already_paid_order_id);
+ GNUNET_free_non_null (already_paid_order_id);
+ return ret;
}
+ GNUNET_break (1 == qs);
+ GNUNET_break (0 == strcmp (order_id,
+ already_paid_order_id));
+ GNUNET_free_non_null (already_paid_order_id);
+ }
+ else
+ {
+ /* Check if paid regardless of session. */
+ json_t *xcontract_terms = NULL;
+
+ qs = db->find_paid_contract_terms_from_hash (db->cls,
+ &xcontract_terms,
+ &god->h_contract_terms,
+ &mi->pubkey);
if (0 > qs)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Database hard error on refunds_from_contract_terms_hash lookup: %s\n",
- GNUNET_h2s (&prd->h_contract_terms));
+ /* Always report on hard error as well to enable diagnostics */
+ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_REFUND_LOOKUP_DB_ERROR,
- "Failed to lookup refunds for contract");
+ TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+ "Merchant database error");
+ }
+ if (0 == qs)
+ {
+ return send_pay_request (god,
+ NULL);
}
+ GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+ GNUNET_assert (NULL != xcontract_terms);
+ json_decref (xcontract_terms);
+ }
- /* Now launch exchange interactions, unless we already have the
- response in the database! */
- for (struct CoinRefund *cr = prd->cr_head;
- NULL != cr;
- cr = cr->next)
+
+ for (unsigned int i = 0; i<MAX_RETRIES; i++)
+ {
+ qs = db->get_refunds_from_contract_terms_hash (db->cls,
+ hc->instance->settings.id,
+ &god->h_contract_terms,
+ &process_refunds_cb,
+ god);
+ if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+ break;
+ }
+ if (0 > qs)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_REFUND_LOOKUP_DB_ERROR,
+ "Failed to lookup refunds for contract");
+ }
+
+ /* Now launch exchange interactions, unless we already have the
+ response in the database! */
+ for (struct CoinRefund *cr = god->cr_head;
+ NULL != cr;
+ cr = cr->next)
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = db->get_refund_proof (db->cls,
+ &cr->god->merchant->pubkey,
+ &cr->god->h_contract_terms,
+ &cr->coin_pub,
+ cr->rtransaction_id,
+ &cr->exchange_pub,
+ &cr->exchange_sig);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- enum GNUNET_DB_QueryStatus qs;
-
- qs = db->get_refund_proof (db->cls,
- &cr->prd->merchant->pubkey,
- &cr->prd->h_contract_terms,
- &cr->coin_pub,
- cr->rtransaction_id,
- &cr->exchange_pub,
- &cr->exchange_sig);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- /* We need to talk to the exchange */
- cr->fo = TMH_EXCHANGES_find_exchange (cr->exchange_url,
- NULL,
- GNUNET_NO,
- &exchange_found_cb,
- cr);
- }
+ /* We need to talk to the exchange */
+ cr->fo = TMH_EXCHANGES_find_exchange (cr->exchange_url,
+ NULL,
+ GNUNET_NO,
+ &exchange_found_cb,
+ cr);
}
}
+ if ( (god->awaiting_refund) &&
+ ( (! god->refunded) ||
+ (1 != TALER_amount_cmp (&god->refund_amount,
+ &god->min_refund)) ) )
+ {
+ /* Client is waiting for a refund larger than what we have, suspend
+ until timeout */
+ struct GNUNET_TIME_Relative remaining;
+
+ remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
+ if (0 != remaining.rel_value_us)
+ {
+ /* yes, indeed suspend */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Awaiting refund exceeding %s\n",
+ TALER_amount2s (&god->min_refund));
+ suspend_god (god);
+ return MHD_YES;
+ }
+ }
+
+
/* Check if there are still exchange operations pending */
- if (GNUNET_YES == prd_pending (prd))
+ if (GNUNET_YES == god_pending (god))
{
- if (! prd->suspended)
+ if (! god->suspended)
{
- prd->suspended = GNUNET_YES;
+ god->suspended = GNUNET_YES;
MHD_suspend_connection (connection);
- GNUNET_CONTAINER_DLL_insert (prd_head,
- prd_tail,
- prd);
+ GNUNET_CONTAINER_DLL_insert (god_head,
+ god_tail,
+ god);
}
return MHD_YES; /* we're still talking to the exchange */
}
/* All operations done, build final response */
- if (NULL == prd->cr_head)
- {
- /* There ARE no refunds scheduled, bitch */
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_LOOKUP_NO_REFUND,
- "This contract is not currently eligible for refunds");
- }
-
{
json_t *ra;
ra = json_array ();
GNUNET_assert (NULL != ra);
- for (struct CoinRefund *cr = prd->cr_head;
+ for (struct CoinRefund *cr = god->cr_head;
NULL != cr;
cr = cr->next)
{
@@ -637,6 +961,23 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
GNUNET_JSON_from_data_auto (&cr->exchange_sig)
)));
}
+
+ if (god->refunded)
+ return TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:b, s:b, s:o}",
+ "paid", 1,
+ "refunded", god->refunded,
+ "refund_amount",
+ TALER_JSON_from_amount (
+ &god->refund_amount));
+ return TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:b, s:b }",
+ "paid", 1,
+ "refunded", 0);
+
+
return TALER_MHD_reply_json_pack (
connection,
MHD_HTTP_OK,
@@ -646,9 +987,9 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
"merchant_pub",
GNUNET_JSON_from_data_auto (&mi->pubkey),
"h_contract_terms",
- GNUNET_JSON_from_data_auto (&prd->h_contract_terms));
+ GNUNET_JSON_from_data_auto (&god->h_contract_terms));
}
}
-/* end of taler-merchant-httpd_refund_lookup.c */
+/* end of taler-merchant-httpd_get-orders-ID.c */
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.h b/src/backend/taler-merchant-httpd_get-orders-ID.h
index 24495daf..3aa157da 100644
--- a/src/backend/taler-merchant-httpd_get-orders-ID.h
+++ b/src/backend/taler-merchant-httpd_get-orders-ID.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014, 2015, 2016, 2017 Taler Systems SA
+ (C) 2014, 2015, 2016, 2017, 2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -13,36 +13,28 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-
/**
- * @file backend/taler-merchant-httpd_refund_lookup.h
- * @brief
+ * @file backend/taler-merchant-httpd_get-orders-ID.h
+ * @brief implementation of GET /orders/$ID
* @author Marcello Stanisci
*/
-
-#ifndef TALER_MERCHANT_HTTPD_REFUND_LOOKUP_H
-#define TALER_MERCHANT_HTTPD_REFUND_LOOKUP_H
+#ifndef TALER_MERCHANT_HTTPD_GET_ORDERS_ID_H
+#define TALER_MERCHANT_HTTPD_GET_ORDERS_ID_H
#include <microhttpd.h>
#include "taler-merchant-httpd.h"
/**
- * Return refund situation about a contract.
+ * Handle a GET "/orders/$ID" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc context with further information about the request
* @return MHD result code
*/
MHD_RESULT
-MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
+TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
#endif