[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant] tag demo-2019-11-02-00 updated (b4f2c5e -> 3a22e60)
From: |
gnunet |
Subject: |
[taler-merchant] tag demo-2019-11-02-00 updated (b4f2c5e -> 3a22e60) |
Date: |
Sat, 02 Nov 2019 14:59:35 +0100 |
This is an automated email from the git hooks/post-receive script.
dold pushed a change to tag demo-2019-11-02-00
in repository merchant.
*** WARNING: tag demo-2019-11-02-00 was modified! ***
from b4f2c5e (commit)
to 3a22e60 (commit)
from b4f2c5e logging
add cadda47 fix newlines
add d19a45f separate /order and /proposal handlers into different files
add e8d2cde clean up check payment logic
add ae06bf1 simplify logic
add ea48d9f implement /public/poll-payment API, refactor to avoid code
duplication
add 3a22e60 implement TALER_MERCHANT_poll_payment()
No new revisions were added by this update.
Summary of changes:
contrib/uncrustify.cfg | 4 +-
src/backend/Makefile.am | 22 +-
src/backend/taler-merchant-httpd.c | 88 ++-
src/backend/taler-merchant-httpd.h | 31 +-
src/backend/taler-merchant-httpd_check-payment.c | 584 +++++++++---------
...tpd_proposal.c => taler-merchant-httpd_order.c} | 196 +-----
...-transaction.h => taler-merchant-httpd_order.h} | 29 +-
src/backend/taler-merchant-httpd_pay.c | 5 -
src/backend/taler-merchant-httpd_poll-payment.c | 557 +++++++++++++++++
...orize.h => taler-merchant-httpd_poll-payment.h} | 25 +-
src/backend/taler-merchant-httpd_proposal.c | 658 ---------------------
src/backend/taler-merchant-httpd_proposal.h | 22 -
src/backend/taler-merchant-httpd_tip-pickup.c | 4 +-
src/include/taler_merchant_service.h | 74 +++
src/lib/Makefile.am | 11 +-
src/lib/merchant_api_check_payment.c | 27 +-
...check_payment.c => merchant_api_poll_payment.c} | 132 +++--
17 files changed, 1168 insertions(+), 1301 deletions(-)
copy src/backend/{taler-merchant-httpd_proposal.c =>
taler-merchant-httpd_order.c} (74%)
copy src/backend/{taler-merchant-httpd_track-transaction.h =>
taler-merchant-httpd_order.h} (59%)
create mode 100644 src/backend/taler-merchant-httpd_poll-payment.c
copy src/backend/{taler-merchant-httpd_tip-authorize.h =>
taler-merchant-httpd_poll-payment.h} (65%)
copy src/lib/{merchant_api_check_payment.c => merchant_api_poll_payment.c}
(54%)
diff --git a/contrib/uncrustify.cfg b/contrib/uncrustify.cfg
index 12dd62c..00cf228 100644
--- a/contrib/uncrustify.cfg
+++ b/contrib/uncrustify.cfg
@@ -49,8 +49,8 @@ nl_assign_brace=remove
# No extra newlines that cause noisy diffs
nl_start_of_file=remove
-nl_before_func_body_proto = 2
-nl_before_func_body_def = 3
+nl_before_func_body_proto = 0
+nl_before_func_body_def = 0
nl_after_func_proto = 2
nl_after_func_body = 3
# If there's no new line, it's not a text file!
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index cd221ca..966d8f4 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -14,25 +14,25 @@ bin_PROGRAMS = \
taler_merchant_httpd_SOURCES = \
taler-merchant-httpd.c taler-merchant-httpd.h \
- taler-merchant-httpd_parsing.c taler-merchant-httpd_parsing.h \
- taler-merchant-httpd_responses.c taler-merchant-httpd_responses.h \
- taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
taler-merchant-httpd_auditors.c taler-merchant-httpd_auditors.h \
+ taler-merchant-httpd_config.c taler-merchant-httpd_config.h \
+ taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
- taler-merchant-httpd_proposal.c taler-merchant-httpd_proposal.h \
- taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
taler-merchant-httpd_history.c taler-merchant-httpd_history.h \
+ taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
+ taler-merchant-httpd_order.c taler-merchant-httpd_order.h \
+ taler-merchant-httpd_parsing.c taler-merchant-httpd_parsing.h \
+ taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
+ taler-merchant-httpd_poll-payment.c taler-merchant-httpd_poll-payment.h \
+ taler-merchant-httpd_proposal.c taler-merchant-httpd_proposal.h \
+ taler-merchant-httpd_refund.c taler-merchant-httpd_refund.h \
+ taler-merchant-httpd_responses.c taler-merchant-httpd_responses.h \
taler-merchant-httpd_tip-authorize.c taler-merchant-httpd_tip-authorize.h \
taler-merchant-httpd_tip-pickup.c taler-merchant-httpd_tip-pickup.h \
taler-merchant-httpd_tip-query.c taler-merchant-httpd_tip-query.h \
taler-merchant-httpd_tip-reserve-helper.c
taler-merchant-httpd_tip-reserve-helper.h \
taler-merchant-httpd_track-transaction.c
taler-merchant-httpd_track-transaction.h \
- taler-merchant-httpd_track-transfer.c taler-merchant-httpd_track-transfer.h \
- taler-merchant-httpd_refund.c taler-merchant-httpd_refund.h \
- taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
- taler-merchant-httpd_config.c taler-merchant-httpd_config.h
-
-
+ taler-merchant-httpd_track-transfer.c taler-merchant-httpd_track-transfer.h
taler_merchant_httpd_LDADD = \
$(top_builddir)/src/backenddb/libtalermerchantdb.la \
-ltalerexchange \
diff --git a/src/backend/taler-merchant-httpd.c
b/src/backend/taler-merchant-httpd.c
index ef7b650..8abb444 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -36,6 +36,7 @@
#include "taler-merchant-httpd_mhd.h"
#include "taler-merchant-httpd_auditors.h"
#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_order.h"
#include "taler-merchant-httpd_proposal.h"
#include "taler-merchant-httpd_pay.h"
#include "taler-merchant-httpd_track-transaction.h"
@@ -47,6 +48,7 @@
#include "taler-merchant-httpd_history.h"
#include "taler-merchant-httpd_refund.h"
#include "taler-merchant-httpd_check-payment.h"
+#include "taler-merchant-httpd_poll-payment.h"
#include "taler-merchant-httpd_config.h"
/**
@@ -167,6 +169,11 @@ struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
*/
struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
+/**
+ * Task responsible for timeouts in the #resume_timeout_heap.
+ */
+struct GNUNET_SCHEDULER_Task *resume_timeout_task;
+
/**
* Return #GNUNET_YES if given a valid correlation ID and
@@ -275,6 +282,73 @@ TMH_compute_pay_key (const char *order_id,
}
+/**
+ * Create a taler://pay/ URI for the given @a con and @a order_id
+ * and @a session_id and @a instance_id.
+ *
+ * @param con HTTP connection
+ * @param order_id the order id
+ * @param session_id session, may be NULL
+ * @param instance_id instance, may be "default"
+ * @return corresponding taler://pay/ URI, or NULL on missing "host"
+ */
+char *
+TMH_make_taler_pay_uri (struct MHD_Connection *con,
+ const char *order_id,
+ const char *session_id,
+ const char *instance_id)
+{
+ const char *host;
+ const char *forwarded_host;
+ const char *uri_path;
+ const char *uri_instance_id;
+ const char *query;
+ char *result;
+
+ host = MHD_lookup_connection_value (con,
+ MHD_HEADER_KIND,
+ "Host");
+ forwarded_host = MHD_lookup_connection_value (con,
+ MHD_HEADER_KIND,
+ "X-Forwarded-Host");
+
+ uri_path = MHD_lookup_connection_value (con,
+ MHD_HEADER_KIND,
+ "X-Forwarded-Prefix");
+ if (NULL == uri_path)
+ uri_path = "-";
+ if (NULL != forwarded_host)
+ host = forwarded_host;
+ if (0 == strcmp (instance_id,
+ "default"))
+ uri_instance_id = "-";
+ else
+ uri_instance_id = instance_id;
+ if (NULL == host)
+ {
+ /* Should never happen, at least the host header should be defined */
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ if (GNUNET_YES == TALER_mhd_is_https (con))
+ query = "";
+ else
+ query = "?insecure=1";
+ GNUNET_assert (NULL != order_id);
+ GNUNET_assert (0 < GNUNET_asprintf (&result,
+ "taler://pay/%s/%s/%s/%s%s%s%s",
+ host,
+ uri_path,
+ uri_instance_id,
+ order_id,
+ (NULL == session_id) ? "" : "/",
+ (NULL == session_id) ? "" : session_id,
+ query));
+ return result;
+}
+
+
/**
* Shutdown task (magically invoked when the application is being
* quit)
@@ -310,6 +384,11 @@ do_shutdown (void *cls)
GNUNET_CONTAINER_heap_destroy (resume_timeout_heap);
resume_timeout_heap = NULL;
}
+ if (NULL != resume_timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (resume_timeout_task);
+ resume_timeout_task = NULL;
+ }
if (NULL != mhd)
{
MHD_stop_daemon (mhd);
@@ -441,8 +520,6 @@ TMH_trigger_daemon ()
* @param daemon_handle HTTP server to prepare to run
*/
static struct GNUNET_SCHEDULER_Task *
-
-
prepare_daemon ()
{
struct GNUNET_SCHEDULER_Task *ret;
@@ -1034,8 +1111,6 @@ instances_iterator_cb (void *cls,
* @return NULL if that instance is unknown to us
*/
static struct MerchantInstance *
-
-
lookup_instance (const char *instance_id)
{
struct GNUNET_HashCode h_instance;
@@ -1186,7 +1261,7 @@ url_handler (void *cls,
&MH_handler_history, MHD_HTTP_OK},
{ "/order", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
- &MH_handler_proposal_put, MHD_HTTP_OK },
+ &MH_handler_order_post, MHD_HTTP_OK },
{ "/refund", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&MH_handler_refund_increase, MHD_HTTP_OK},
@@ -1199,6 +1274,9 @@ url_handler (void *cls,
{ "/check-payment", MHD_HTTP_METHOD_GET, "text/plain",
NULL, 0,
&MH_handler_check_payment, MHD_HTTP_OK},
+ { "/public/poll-payment", MHD_HTTP_METHOD_GET, "text/plain",
+ NULL, 0,
+ &MH_handler_poll_payment, MHD_HTTP_OK},
{ "/config", MHD_HTTP_METHOD_GET, "text/plain",
NULL, 0,
&MH_handler_config, MHD_HTTP_OK},
diff --git a/src/backend/taler-merchant-httpd.h
b/src/backend/taler-merchant-httpd.h
index bc53bbc..53dedcb 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -276,15 +276,21 @@ struct TMH_SuspendedConnection
*/
struct MHD_Connection *con;
+ /**
+ * Associated heap node.
+ */
+ struct GNUNET_CONTAINER_HeapNode *hn;
+
/**
* Key of this entry in the #payment_trigger_map
*/
struct GNUNET_HashCode key;
/**
- * Associated heap node.
+ * At what time does this request expire? If set in the future, we
+ * may wait this long for a payment to arrive before responding.
*/
- struct GNUNET_CONTAINER_HeapNode *hn;
+ struct GNUNET_TIME_Absolute long_poll_timeout;
};
@@ -319,6 +325,11 @@ extern unsigned long long default_wire_fee_amortization;
*/
extern struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
+/**
+ * Task responsible for timeouts in the #resume_timeout_heap.
+ */
+extern struct GNUNET_SCHEDULER_Task *resume_timeout_task;
+
/**
* Hash map from H(order_id,merchant_pub) to `struct TMH_SuspendedConnection`
* entries to resume when a payment is made for the given order.
@@ -397,4 +408,20 @@ TMH_compute_pay_key (const char *order_id,
struct GNUNET_HashCode *key);
+/**
+ * Create a taler://pay/ URI for the given @a con and @a order_id
+ * and @a session_id and @a instance_id.
+ *
+ * @param con HTTP connection
+ * @param order_id the order id
+ * @param session_id session, may be NULL
+ * @param instance_id instance, may be "default"
+ * @return corresponding taler://pay/ URI, or NULL on missing "host"
+ */
+char *
+TMH_make_taler_pay_uri (struct MHD_Connection *con,
+ const char *order_id,
+ const char *session_id,
+ const char *instance_id);
+
#endif
diff --git a/src/backend/taler-merchant-httpd_check-payment.c
b/src/backend/taler-merchant-httpd_check-payment.c
index 74b8c5c..fad3fdc 100644
--- a/src/backend/taler-merchant-httpd_check-payment.c
+++ b/src/backend/taler-merchant-httpd_check-payment.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2017 Taler Systems SA
+ (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
@@ -17,6 +17,7 @@
* @file backend/taler-merchant-httpd_check-payment.c
* @brief implementation of /check-payment handler
* @author Florian Dold
+ * @author Christian Grothoff
*/
#include "platform.h"
#include <string.h>
@@ -38,69 +39,96 @@
/**
- * Make a taler://pay URI
- *
- * @param connection MHD connection to take host and path from
- * @param instance_id merchant's instance ID
- * @param order_id order ID to request a payment for
- * @param session_id session ID for the payment or NULL
- * if not a session-bound payment
- * @returns the URI, must be freed with #GNUNET_free
+ * Data structure we keep for a check payment request.
*/
-static char *
-make_taler_pay_uri (struct MHD_Connection *connection,
- const char *instance_id,
- const char *order_id,
- const char *session_id)
+struct CheckPaymentRequestContext
{
- const char *host;
- const char *forwarded_host;
- const char *uri_path;
- const char *uri_instance_id;
- const char *query;
- char *result;
-
- host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host");
- forwarded_host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
- "X-Forwarded-Host");
-
- uri_path = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
- "X-Forwarded-Prefix");
- if (NULL == uri_path)
- uri_path = "-";
-
- if (NULL != forwarded_host)
- host = forwarded_host;
-
- if (0 == strcmp (instance_id, "default"))
- uri_instance_id = "-";
- else
- uri_instance_id = instance_id;
+ /**
+ * Must be first for #handle_mhd_completion_callback.
+ */
+ struct TM_HandlerContext hc;
- if (NULL == host)
- {
- /* Should never happen, at least the host header should be defined */
- GNUNET_break (0);
- return NULL;
- }
+ struct MHD_Connection *connection;
- if (GNUNET_YES == TALER_mhd_is_https (connection))
- query = "";
- else
- query = "?insecure=1";
-
- GNUNET_assert (NULL != order_id);
-
- GNUNET_assert (0 < GNUNET_asprintf (&result,
- "taler://pay/%s/%s/%s/%s%s%s%s",
- host,
- uri_path,
- uri_instance_id,
- order_id,
- (session_id == NULL) ? "" : "/",
- (session_id == NULL) ? "" : session_id,
- query));
- return result;
+ /**
+ * 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;
+
+ /**
+ * session of the client
+ */
+ const char *session_id;
+
+ /**
+ * fulfillment URL of the contract (valid as long as
+ * @e contract_terms is valid).
+ */
+ const char *fulfillment_url;
+
+ /**
+ * 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;
+
+ /**
+ * Set to #GNUNET_YES if this payment has been refunded and
+ * @e refund_amount is initialized.
+ */
+ int refunded;
+
+ /**
+ * Initially #GNUNET_SYSERR. If we queued a response, set to the
+ * result code (i.e. #MHD_YES or #MHD_NO).
+ */
+ int ret;
+
+};
+
+
+/**
+ * Clean up the session state for a check payment request.
+ *
+ * @param hc must be a `struct CheckPaymentRequestContext *`
+ */
+static void
+cprc_cleanup (struct TM_HandlerContext *hc)
+{
+ struct CheckPaymentRequestContext *cprc = (struct
+ CheckPaymentRequestContext *) hc;
+
+ if (NULL != cprc->contract_terms)
+ json_decref (cprc->contract_terms);
+ GNUNET_free_non_null (cprc->final_contract_url);
+ GNUNET_free (cprc);
}
@@ -123,51 +151,47 @@ process_refunds_cb (void *cls,
const struct TALER_Amount *refund_amount,
const struct TALER_Amount *refund_fee)
{
- struct TALER_Amount *acc_amount = cls;
+ struct CheckPaymentRequestContext *cprc = cls;
- GNUNET_assert (GNUNET_SYSERR !=
- TALER_amount_add (acc_amount,
- acc_amount,
- refund_amount));
+ if (cprc->refunded)
+ {
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_add (&cprc->refund_amount,
+ &cprc->refund_amount,
+ refund_amount));
+ return;
+ }
+ cprc->refund_amount = *refund_amount;
+ cprc->refunded = GNUNET_YES;
}
/**
* The client did not yet pay, send it the payment request.
*
- * @param connection connection to send on
- * @param order_id order ID for the payment
- * @param final_contract_url where to get the contract
- * @param session_id session of the client
- * @param fulfillment_url fulfillment URL of the contract
- * @param h_contract_terms_str hash of the contract terms, stringified
- * @param mi merchant instance
+ * @param cprc check pay request context
* @return #MHD_YES on success
*/
static int
-send_pay_request (struct MHD_Connection *connection,
- const char *order_id,
- const char *final_contract_url,
- const char *session_id,
- const char *fulfillment_url,
- const char *h_contract_terms_str,
- const struct MerchantInstance *mi)
+send_pay_request (const struct CheckPaymentRequestContext *cprc)
{
int ret;
- int qs;
char *already_paid_order_id = NULL;
char *taler_pay_uri;
/* Check if resource_id has been paid for in the same session
* with another order_id.
*/
- if ( (NULL != session_id) && (NULL != fulfillment_url) )
+ if ( (NULL != cprc->session_id) &&
+ (NULL != cprc->fulfillment_url) )
{
+ enum GNUNET_DB_QueryStatus qs;
+
qs = db->find_session_info (db->cls,
&already_paid_order_id,
- session_id,
- fulfillment_url,
- &mi->pubkey);
+ cprc->session_id,
+ cprc->fulfillment_url,
+ &cprc->mi->pubkey);
if (qs < 0)
{
/* single, read-only SQL statements should never cause
@@ -175,61 +199,99 @@ send_pay_request (struct MHD_Connection *connection,
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,
+ return TMH_RESPONSE_reply_internal_error (cprc->connection,
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
"db error fetching pay session
info");
}
}
-
- taler_pay_uri = make_taler_pay_uri (connection, mi->id, order_id,
session_id);
-
- ret = TMH_RESPONSE_reply_json_pack (connection,
+ taler_pay_uri = TMH_make_taler_pay_uri (cprc->connection,
+ cprc->order_id,
+ cprc->session_id,
+ cprc->mi->id);
+ ret = TMH_RESPONSE_reply_json_pack (cprc->connection,
MHD_HTTP_OK,
"{s:s, s:s, s:b, s:s?}",
- "taler_pay_uri",
- taler_pay_uri,
- "contract_url",
- final_contract_url,
- "paid",
- 0,
+ "taler_pay_uri", taler_pay_uri,
+ "contract_url", cprc->final_contract_url,
+ "paid", 0,
"already_paid_order_id",
- already_paid_order_id
- );
- GNUNET_free_non_null (already_paid_order_id);
+ already_paid_order_id);
GNUNET_free (taler_pay_uri);
+ GNUNET_free_non_null (already_paid_order_id);
return ret;
}
+/**
+ * Parse the "contract_terms" in @a cprc and set the
+ * "fulfillment_url" and the "h_contract_terms" in @a cprc
+ * accordingly.
+ *
+ * On errors, the response is being queued and the status
+ * code set in @cprc "ret".
+ *
+ * @param cprc[in,out] context to process
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ */
+static int
+parse_contract_terms (struct CheckPaymentRequestContext *cprc)
+{
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("fulfillment_url",
+ &cprc->fulfillment_url),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (cprc->contract_terms,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ cprc->ret
+ = TMH_RESPONSE_reply_internal_error (cprc->connection,
+
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
+ "Merchant database error (contract
terms corrupted)");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_JSON_hash (cprc->contract_terms,
+ &cprc->h_contract_terms))
+ {
+ GNUNET_break (0);
+ cprc->ret
+ = TMH_RESPONSE_reply_internal_error (cprc->connection,
+
TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH,
+ "Failed to hash proposal");
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
/**
* Check that we are aware of @a order_id and if so request the payment,
* otherwise generate an error response.
*
- * @param connection where to send the response
- * @param mi the merchant's instance
- * @param final_contract_url where to redirect for the contract
- * @param session_id the session_id
- * @param order_id the order to look up
- * @return #MHD_YES on success
+ * @param cprc session state
+ * @return status code to return to MHD for @a connection
*/
static int
-check_order_and_request_payment (struct MHD_Connection *connection,
- struct MerchantInstance *mi,
- const char *final_contract_url,
- const char *session_id,
- const char *order_id)
+check_order_and_request_payment (struct CheckPaymentRequestContext *cprc)
{
enum GNUNET_DB_QueryStatus qs;
- json_t *contract_terms;
- struct GNUNET_HashCode h_contract_terms;
- char *h_contract_terms_str;
- int ret;
- const char *fulfillment_url;
+ if (NULL != cprc->contract_terms)
+ {
+ /* This should never happen. */
+ GNUNET_break (0);
+ json_decref (cprc->contract_terms);
+ cprc->contract_terms = NULL;
+ }
qs = db->find_order (db->cls,
- &contract_terms,
- order_id,
- &mi->pubkey);
+ &cprc->contract_terms,
+ cprc->order_id,
+ &cprc->mi->pubkey);
if (0 > qs)
{
/* single, read-only SQL statements should never cause
@@ -237,55 +299,22 @@ check_order_and_request_payment (struct MHD_Connection
*connection,
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,
+ return TMH_RESPONSE_reply_internal_error (cprc->connection,
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
"db error fetching order");
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
- return TMH_RESPONSE_reply_not_found (connection,
+ return TMH_RESPONSE_reply_not_found (cprc->connection,
TALER_EC_CHECK_PAYMENT_ORDER_ID_UNKNOWN,
"unknown order_id");
}
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("fulfillment_url", &fulfillment_url),
- GNUNET_JSON_spec_end ()
- };
- if (GNUNET_OK != GNUNET_JSON_parse (contract_terms, spec, NULL, NULL))
- {
- GNUNET_break (0);
- json_decref (contract_terms);
- return TMH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
- "Merchant database error
(contract terms corrupted)");
- }
- }
if (GNUNET_OK !=
- TALER_JSON_hash (contract_terms,
- &h_contract_terms))
- {
- GNUNET_break (0);
- json_decref (contract_terms);
- return TMH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH,
- "Failed to hash proposal");
- }
+ parse_contract_terms (cprc))
+ return cprc->ret;
/* Offer was not picked up yet, but we ensured that it exists */
- h_contract_terms_str = GNUNET_STRINGS_data_to_string_alloc
(&h_contract_terms,
- sizeof (struct
-
GNUNET_HashCode));
- ret = send_pay_request (connection,
- order_id,
- final_contract_url,
- session_id,
- fulfillment_url,
- h_contract_terms_str,
- mi);
- GNUNET_free_non_null (h_contract_terms_str);
- json_decref (contract_terms);
- return ret;
+ return send_pay_request (cprc);
}
@@ -310,131 +339,92 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
size_t *upload_data_size,
struct MerchantInstance *mi)
{
- const char *order_id;
- const char *contract_url;
- const char *session_id;
- const char *fulfillment_url;
- char *final_contract_url;
- char *h_contract_terms_str;
+ struct CheckPaymentRequestContext *cprc = *connection_cls;
enum GNUNET_DB_QueryStatus qs;
- json_t *contract_terms;
- struct GNUNET_HashCode h_contract_terms;
- struct TALER_Amount refund_amount;
int ret;
- int refunded;
- order_id = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "order_id");
- if (NULL == order_id)
- {
- /* order_id is required but missing */
- GNUNET_break_op (0);
- return TMH_RESPONSE_reply_bad_request (connection,
- TALER_EC_PARAMETER_MISSING,
- "order_id required");
- }
- contract_url = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "contract_url");
- if (NULL == contract_url)
- {
- final_contract_url = TALER_url_absolute_mhd (connection,
- "/public/proposal",
- "instance", mi->id,
- "order_id", order_id,
- NULL);
- GNUNET_assert (NULL != final_contract_url);
- }
- else
+ if (NULL == cprc)
{
- final_contract_url = GNUNET_strdup (contract_url);
- }
- session_id = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "session_id");
-
- db->preflight (db->cls);
- qs = db->find_contract_terms (db->cls,
- &contract_terms,
- order_id,
- &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);
- GNUNET_free (final_contract_url);
- return TMH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
- "db error fetching contract
terms");
- }
-
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- /* Check that we're at least aware of the order */
- ret = check_order_and_request_payment (connection,
- mi,
- final_contract_url,
- session_id,
- order_id);
- GNUNET_free (final_contract_url);
- return ret;
- }
-
- GNUNET_assert (NULL != contract_terms);
-
- /* Get the amount and fulfillment_url from the contract. */
- {
- struct TALER_Amount amount;
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_amount ("amount", &amount),
- GNUNET_JSON_spec_string ("fulfillment_url", &fulfillment_url),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK != GNUNET_JSON_parse (contract_terms, spec, NULL, NULL))
+ /* First time here, parse request and check order is known */
+ cprc = GNUNET_new (struct CheckPaymentRequestContext);
+ cprc->hc.cc = &cprc_cleanup;
+ cprc->ret = GNUNET_SYSERR;
+ cprc->connection = connection;
+ cprc->mi = mi;
+ *connection_cls = cprc;
+
+ cprc->order_id = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "order_id");
+ if (NULL == cprc->order_id)
{
- GNUNET_break (0);
- GNUNET_free (final_contract_url);
- json_decref (contract_terms);
+ /* order_id is required but missing */
+ GNUNET_break_op (0);
+ return TMH_RESPONSE_reply_bad_request (connection,
+ TALER_EC_PARAMETER_MISSING,
+ "order_id required");
+ }
+ cprc->contract_url = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "contract_url");
+ if (NULL == cprc->contract_url)
+ {
+ cprc->final_contract_url = TALER_url_absolute_mhd (connection,
+ "/public/proposal",
+ "instance", mi->id,
+ "order_id",
+ cprc->order_id,
+ NULL);
+ GNUNET_assert (NULL != cprc->final_contract_url);
+ }
+ else
+ {
+ cprc->final_contract_url = GNUNET_strdup (cprc->contract_url);
+ }
+ cprc->session_id = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "session_id");
+ db->preflight (db->cls);
+ qs = db->find_contract_terms (db->cls,
+ &cprc->contract_terms,
+ cprc->order_id,
+ &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_CONTRACT_TERMS_ERROR,
- "Merchant database error
(contract terms corrupted)");
+ "db error fetching contract
terms");
}
- TALER_amount_get_zero (amount.currency, &refund_amount);
- }
- if (GNUNET_OK !=
- TALER_JSON_hash (contract_terms,
- &h_contract_terms))
- {
- GNUNET_break (0);
- json_decref (contract_terms);
- GNUNET_free (final_contract_url);
- return TMH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH,
- "Failed to hash proposal");
- }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ /* Check that we're at least aware of the order */
+ return check_order_and_request_payment (cprc);
+ }
+ GNUNET_assert (NULL != cprc->contract_terms);
- h_contract_terms_str = GNUNET_STRINGS_data_to_string_alloc
(&h_contract_terms,
- sizeof (struct
-
GNUNET_HashCode));
+ if (GNUNET_OK !=
+ parse_contract_terms (cprc))
+ return cprc->ret;
+ }
+ GNUNET_assert (NULL != cprc->contract_terms);
/* Check if the order has been paid for. */
- if (NULL != session_id)
+ if (NULL != cprc->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,
- session_id,
- fulfillment_url,
+ cprc->session_id,
+ cprc->fulfillment_url,
&mi->pubkey);
if (qs < 0)
{
@@ -449,56 +439,37 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
}
else if (0 == qs)
{
- ret = send_pay_request (connection,
- order_id,
- final_contract_url,
- session_id,
- fulfillment_url,
- h_contract_terms_str,
- mi);
+ ret = send_pay_request (cprc);
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_break (0 == strcmp (cprc->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,
- &h_contract_terms,
+ &cprc->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);
- GNUNET_free_non_null (h_contract_terms_str);
- GNUNET_free (final_contract_url);
- json_decref (contract_terms);
return TMH_RESPONSE_reply_internal_error (connection,
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
"Merchant database error");
}
if (0 == qs)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "not paid yet\n");
- ret = send_pay_request (connection,
- order_id,
- final_contract_url,
- session_id,
- fulfillment_url,
- h_contract_terms_str,
- mi);
- GNUNET_free_non_null (h_contract_terms_str);
- GNUNET_free (final_contract_url);
- json_decref (contract_terms);
- return ret;
-
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "not paid yet\n");
+ return send_pay_request (cprc);
}
GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
GNUNET_assert (NULL != xcontract_terms);
@@ -510,9 +481,9 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
{
qs = db->get_refunds_from_contract_terms_hash (db->cls,
&mi->pubkey,
- &h_contract_terms,
+ &cprc->h_contract_terms,
&process_refunds_cb,
- &refund_amount);
+ cprc);
if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
break;
}
@@ -520,26 +491,25 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Database hard error on refunds_from_contract_terms_hash
lookup: %s\n",
- GNUNET_h2s (&h_contract_terms));
- GNUNET_free_non_null (h_contract_terms_str);
- GNUNET_free (final_contract_url);
- json_decref (contract_terms);
+ GNUNET_h2s (&cprc->h_contract_terms));
return TMH_RESPONSE_reply_internal_error (connection,
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
"Merchant database error");
}
- GNUNET_free_non_null (h_contract_terms_str);
-
- refunded = (0 != refund_amount.value) || (0 != refund_amount.fraction);
-
- ret = TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:o, s:b, s:b, s:o}",
- "contract_terms", contract_terms,
- "paid", 1,
- "refunded", refunded,
- "refund_amount", TALER_JSON_from_amount (
- &refund_amount));
- GNUNET_free (final_contract_url);
- return ret;
+ if (cprc->refunded)
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:b, s:b, s:o}",
+ "contract_terms",
cprc->contract_terms,
+ "paid", 1,
+ "refunded", cprc->refunded,
+ "refund_amount",
+ TALER_JSON_from_amount (
+ &cprc->refund_amount));
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:b, s:b }",
+ "contract_terms", cprc->contract_terms,
+ "paid", 1,
+ "refunded", 0);
}
diff --git a/src/backend/taler-merchant-httpd_proposal.c
b/src/backend/taler-merchant-httpd_order.c
similarity index 74%
copy from src/backend/taler-merchant-httpd_proposal.c
copy to src/backend/taler-merchant-httpd_order.c
index 6afd275..250a2e5 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_order.c
@@ -18,7 +18,7 @@
*/
/**
- * @file backend/taler-merchant-httpd_proposal.c
+ * @file backend/taler-merchant-httpd_order.c
* @brief HTTP serving layer mainly intended to communicate
* with the frontend
* @author Marcello Stanisci
@@ -644,12 +644,12 @@ proposal_put (struct MHD_Connection *connection,
* @return MHD result code
*/
int
-MH_handler_proposal_put (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi)
+MH_handler_order_post (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ struct MerchantInstance *mi)
{
int res;
struct TMH_JsonParseContext *ctx;
@@ -697,184 +697,4 @@ MH_handler_proposal_put (struct TMH_RequestHandler *rh,
}
-/**
- * Manage a GET /proposal request. Query the db and returns the
- * proposal's data related to the transaction id given as the URL's
- * parameter.
- *
- * Binds the proposal to a nonce.
- *
- * @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
- */
-int
-MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi)
-{
- const char *order_id;
- const char *nonce;
- int res;
- enum GNUNET_DB_QueryStatus qs;
- json_t *contract_terms;
- struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
- const char *stored_nonce;
-
- order_id = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "order_id");
- if (NULL == order_id)
- return TMH_RESPONSE_reply_arg_missing (connection,
- TALER_EC_PARAMETER_MISSING,
- "order_id");
- nonce = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "nonce");
- if (NULL == nonce)
- return TMH_RESPONSE_reply_arg_missing (connection,
- TALER_EC_PARAMETER_MISSING,
- "nonce");
- db->preflight (db->cls);
- qs = db->find_contract_terms (db->cls,
- &contract_terms,
- order_id,
- &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_PROPOSAL_LOOKUP_DB_ERROR,
- "An error occurred while
retrieving proposal data from db");
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- struct GNUNET_TIME_Absolute timestamp;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_absolute_time ("timestamp", ×tamp),
- GNUNET_JSON_spec_end ()
- };
-
- db->preflight (db->cls);
- qs = db->find_order (db->cls,
- &contract_terms,
- order_id,
- &mi->pubkey);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- return TMH_RESPONSE_reply_not_found (connection,
- TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
- "unknown order id");
- }
- GNUNET_assert (NULL != contract_terms);
- json_object_set_new (contract_terms,
- "nonce",
- json_string (nonce));
-
- /* extract fields we need to sign separately */
- res = TMH_PARSE_json_data (connection,
- contract_terms,
- spec);
- if (GNUNET_NO == res)
- {
- return MHD_YES;
- }
- if (GNUNET_SYSERR == res)
- {
- return TMH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
- "Impossible to parse the
order");
- }
-
- for (unsigned int i = 0; i<MAX_RETRIES; i++)
- {
- db->preflight (db->cls);
- qs = db->insert_contract_terms (db->cls,
- order_id,
- &mi->pubkey,
- timestamp,
- contract_terms);
- if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
- break;
- }
- if (0 > qs)
- {
- /* Special report if retries insufficient */
- 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_PROPOSAL_STORE_DB_ERROR,
- "db error: could not store
this proposal's data into db");
- }
- // FIXME: now we can delete (merchant_pub, order_id) from the
merchant_orders table
- }
-
- GNUNET_assert (NULL != contract_terms);
-
- stored_nonce
- = json_string_value (json_object_get (contract_terms,
- "nonce"));
-
- if (NULL == stored_nonce)
- {
- GNUNET_break (0);
- return TMH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
- "existing proposal has no
nonce");
- }
-
- if (0 != strcmp (stored_nonce,
- nonce))
- {
- return TMH_RESPONSE_reply_bad_request (connection,
- TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
- "mismatched nonce");
- }
-
-
- /* create proposal signature */
- {
- struct TALER_ProposalDataPS pdps = {
- .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT),
- .purpose.size = htonl (sizeof (pdps))
- };
-
- if (GNUNET_OK !=
- TALER_JSON_hash (contract_terms,
- &pdps.hash))
- {
- GNUNET_break (0);
- return TMH_RESPONSE_reply_internal_error (connection,
- TALER_EC_INTERNAL_LOGIC_ERROR,
- "Could not hash order");
- }
-
- GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
- &pdps.purpose,
- &merchant_sig);
- }
- res = TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{ s:o, s:o }",
- "contract_terms",
- contract_terms,
- "sig",
- GNUNET_JSON_from_data_auto (
- &merchant_sig));
- return res;
-}
-
-
-/* end of taler-merchant-httpd_proposal.c */
+/* end of taler-merchant-httpd_order.c */
diff --git a/src/backend/taler-merchant-httpd_track-transaction.h
b/src/backend/taler-merchant-httpd_order.h
similarity index 59%
copy from src/backend/taler-merchant-httpd_track-transaction.h
copy to src/backend/taler-merchant-httpd_order.h
index 91b8c47..fd09880 100644
--- a/src/backend/taler-merchant-httpd_track-transaction.h
+++ b/src/backend/taler-merchant-httpd_order.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014, 2015 GNUnet e.V.
+ (C) 2014, 2015, 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
@@ -14,20 +14,21 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file backend/taler-merchant-httpd_track-transaction.h
- * @brief headers for /track/transaction handler
- * @author Christian Grothoff
+ * @file backend/taler-merchant-httpd_order.h
+ * @brief headers for /order handler
* @author Marcello Stanisci
*/
-#ifndef TALER_MERCHANT_HTTPD_TRACK_TRANSACTION_H
-#define TALER_MERCHANT_HTTPD_TRACK_TRANSACTION_H
+#ifndef TALER_MERCHANT_HTTPD_ORDER_H
+#define TALER_MERCHANT_HTTPD_ORDER_H
#include <microhttpd.h>
#include "taler-merchant-httpd.h"
/**
- * Handle a "/track/transaction" request.
+ * Generate a proposal, given its order. In practical terms, it adds the
+ * fields 'exchanges', 'merchant_pub', and 'H_wire' to the order gotten
+ * from the frontend. Finally, it signs this data, and returns it to the
+ * frontend.
*
- * @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
@@ -36,12 +37,12 @@
* @return MHD result code
*/
int
-MH_handler_track_transaction (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
+MH_handler_order_post (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_pay.c
b/src/backend/taler-merchant-httpd_pay.c
index 1bd68d3..e9c6e69 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -523,8 +523,6 @@ abort_deposit (struct PayContext *pc)
* @return the mhd response
*/
static struct MHD_Response *
-
-
sign_success_response (struct PayContext *pc)
{
json_t *refunds;
@@ -661,8 +659,6 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
* @return taler error code, #TALER_EC_NONE if amount is sufficient
*/
static enum TALER_ErrorCode
-
-
check_payment_sufficient (struct PayContext *pc)
{
struct TALER_Amount acc_fee;
@@ -2202,7 +2198,6 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"/pay: picked instance %s\n",
mi->id);
-
}
else
{
diff --git a/src/backend/taler-merchant-httpd_poll-payment.c
b/src/backend/taler-merchant-httpd_poll-payment.c
new file mode 100644
index 0000000..945a356
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_poll-payment.c
@@ -0,0 +1,557 @@
+/*
+ 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_parsing.h"
+#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_responses.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;
+
+ /**
+ * Set to #GNUNET_YES if this payment has been refunded and
+ * @e refund_amount is initialized.
+ */
+ int refunded;
+
+ /**
+ * Initially #GNUNET_SYSERR. If we queued a response, set to the
+ * result code (i.e. #MHD_YES or #MHD_NO).
+ */
+ 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 rtransaction_id identificator of the refund
+ * @param reason human-readable explaination of the refund
+ * @param refund_amount refund amount which is being taken from coin_pub
+ * @param refund_fee cost of this refund operation
+ */
+static void
+process_refunds_cb (void *cls,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ 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 (GNUNET_SYSERR !=
+ TALER_amount_add (&pprc->refund_amount,
+ &pprc->refund_amount,
+ refund_amount));
+ return;
+ }
+ pprc->refund_amount = *refund_amount;
+ pprc->refunded = GNUNET_YES;
+}
+
+
+/**
+ * Resume processing all suspended connections past timeout.
+ *
+ * @param cls unused
+ */
+static void
+do_resume (void *cls)
+{
+ struct TMH_SuspendedConnection *sc;
+
+ (void) cls;
+ resume_timeout_task = NULL;
+ while (1)
+ {
+ sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
+ if (NULL == sc)
+ return;
+ if (0 !=
+ GNUNET_TIME_absolute_get_remaining (
+ sc->long_poll_timeout).rel_value_us)
+ break;
+ GNUNET_assert (sc ==
+ GNUNET_CONTAINER_heap_remove_root (resume_timeout_heap));
+ sc->hn = NULL;
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
+ &sc->key,
+ sc));
+ MHD_resume_connection (sc->con);
+ }
+ resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
+ &do_resume,
+ NULL);
+}
+
+
+/**
+ * The client did not yet pay, send it the payment request.
+ *
+ * @param pprc check pay request context
+ * @return #MHD_YES on success
+ */
+static int
+send_pay_request (struct PollPaymentRequestContext *pprc)
+{
+ int ret;
+ char *already_paid_order_id = NULL;
+ char *taler_pay_uri;
+
+ if (0 !=
+ GNUNET_TIME_absolute_get_remaining (
+ pprc->sc.long_poll_timeout).rel_value_us)
+ {
+ struct TMH_SuspendedConnection *sc;
+
+ /* long polling: do not queue a response, suspend connection instead */
+ TMH_compute_pay_key (pprc->order_id,
+ &pprc->mi->pubkey,
+ &pprc->sc.key);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (payment_trigger_map,
+ &pprc->sc.key,
+ &pprc->sc,
+
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+ pprc->sc.hn = GNUNET_CONTAINER_heap_insert (resume_timeout_heap,
+ &pprc->sc,
+ pprc->sc.long_poll_timeout.
+ abs_value_us);
+ MHD_suspend_connection (pprc->sc.con);
+ if (NULL != resume_timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (resume_timeout_task);
+ resume_timeout_task = NULL;
+ }
+ sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
+ resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
+ &do_resume,
+ NULL);
+ 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 TMH_RESPONSE_reply_internal_error (pprc->sc.con,
+
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
+ "db error fetching pay session
info");
+ }
+ }
+ taler_pay_uri = TMH_make_taler_pay_uri (pprc->sc.con,
+ pprc->order_id,
+ pprc->session_id,
+ pprc->mi->id);
+ ret = TMH_RESPONSE_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
+ */
+int
+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;
+ int ret;
+
+ if (NULL == pprc)
+ {
+ /* First time here, parse request and check order is known */
+ const char *long_poll_timeout_s;
+ const char *cts;
+
+ 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 TMH_RESPONSE_reply_bad_request (connection,
+ 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 TMH_RESPONSE_reply_bad_request (connection,
+ 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 TMH_RESPONSE_reply_bad_request (connection,
+ 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 TMH_RESPONSE_reply_bad_request (connection,
+ 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;
+ }
+ 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 TMH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+ "Merchant database error");
+ }
+ if (0 == qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Contract unknown\n");
+ return TMH_RESPONSE_reply_not_found (connection,
+
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 TMH_RESPONSE_reply_internal_error (connection,
+
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 TMH_RESPONSE_reply_internal_error (connection,
+
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 TMH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+ "Merchant database error");
+ }
+ if (0 == qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "not paid yet\n");
+ 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++)
+ {
+ 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 TMH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+ "Merchant database error");
+ }
+ if (pprc->refunded)
+ return TMH_RESPONSE_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 TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:b, s:b }",
+ "paid", 1,
+ "refunded", 0);
+}
diff --git a/src/backend/taler-merchant-httpd_tip-authorize.h
b/src/backend/taler-merchant-httpd_poll-payment.h
similarity index 65%
copy from src/backend/taler-merchant-httpd_tip-authorize.h
copy to src/backend/taler-merchant-httpd_poll-payment.h
index 8706d64..e9f54c2 100644
--- a/src/backend/taler-merchant-httpd_tip-authorize.h
+++ b/src/backend/taler-merchant-httpd_poll-payment.h
@@ -14,18 +14,19 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file backend/taler-merchant-httpd_tip-authorize.h
- * @brief headers for /tip-authorize handler
+ * @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_TIP_AUTHORIZE_H
-#define TALER_MERCHANT_HTTPD_TIP_AUTHORIZE_H
+#ifndef TALER_MERCHANT_HTTPD_POLL_PAYMENT_H
+#define TALER_MERCHANT_HTTPD_POLL_PAYMENT_H
#include <microhttpd.h>
#include "taler-merchant-httpd.h"
/**
- * Manages a /tip-authorize call, creating a TIP ID and storing the
- * authorization in our DB.
+ * 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
@@ -36,11 +37,11 @@
* @return MHD result code
*/
int
-MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
+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_proposal.c
b/src/backend/taler-merchant-httpd_proposal.c
index 6afd275..7afd0f7 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_proposal.c
@@ -39,664 +39,6 @@
*/
#define MAX_RETRIES 3
-/**
- * What is the label under which we find/place the merchant's
- * jurisdiction in the locations list by default?
- */
-#define STANDARD_LABEL_MERCHANT_JURISDICTION "_mj"
-
-/**
- * What is the label under which we find/place the merchant's
- * address in the locations list by default?
- */
-#define STANDARD_LABEL_MERCHANT_ADDRESS "_ma"
-
-
-/**
- * Check that the given JSON array of products is well-formed.
- *
- * @param products JSON array to check
- * @return #GNUNET_OK if all is fine
- */
-static int
-check_products (json_t *products)
-{
- size_t index;
- json_t *value;
-
- if (! json_is_array (products))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- json_array_foreach (products, index, value) {
- const char *description;
- const char *error_name;
- unsigned int error_line;
- int res;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("description", &description),
- /* FIXME: there are other fields in the product specification
- that are currently not labeled as optional. Maybe check
- those as well, or make them truly optional. */
- GNUNET_JSON_spec_end ()
- };
-
- /* extract fields we need to sign separately */
- res = GNUNET_JSON_parse (value,
- spec,
- &error_name,
- &error_line);
- if (GNUNET_OK != res)
- {
- GNUNET_break (0);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Product description parsing failed at #%u: %s:%u\n",
- (unsigned int) index,
- error_name,
- error_line);
- return GNUNET_SYSERR;
- }
- GNUNET_JSON_parse_free (spec);
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Information we keep for individual calls
- * to requests that parse JSON, but keep no other state.
- */
-struct TMH_JsonParseContext
-{
-
- /**
- * This field MUST be first.
- * FIXME: Explain why!
- */
- struct TM_HandlerContext hc;
-
- /**
- * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
- */
- void *json_parse_context;
-};
-
-
-/**
- * Custom cleanup routine for a `struct TMH_JsonParseContext`.
- *
- * @param hc the `struct TMH_JsonParseContext` to clean up.
- */
-static void
-json_parse_cleanup (struct TM_HandlerContext *hc)
-{
- struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc;
-
- TMH_PARSE_post_cleanup_callback (jpc->json_parse_context);
- GNUNET_free (jpc);
-}
-
-
-/**
- * Generate the base URL for the given merchant instance.
- *
- * @param connection the MHD connection
- * @param instance_id the merchant instance ID
- * @returns the merchant instance's base URL
- */
-static char *
-make_merchant_base_url (struct MHD_Connection *connection, const
- char *instance_id)
-{
- const char *host;
- const char *forwarded_host;
- const char *uri_path;
- struct TALER_Buffer buf = { 0 };
-
- if (GNUNET_YES == TALER_mhd_is_https (connection))
- TALER_buffer_write_str (&buf, "https://");
- else
- TALER_buffer_write_str (&buf, "http://");
-
-
- host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host");
- forwarded_host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
- "X-Forwarded-Host");
-
- if (NULL != forwarded_host)
- {
- TALER_buffer_write_str (&buf, forwarded_host);
- }
- else
- {
- GNUNET_assert (NULL != host);
- TALER_buffer_write_str (&buf, host);
- }
-
- uri_path = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
- "X-Forwarded-Prefix");
- if (NULL != uri_path)
- {
- /* Currently the merchant backend is only supported at the root of the
path,
- this might change in the future. */
- GNUNET_assert (0);
- }
-
- TALER_buffer_write_path (&buf, "public");
-
- if (0 != strcmp (instance_id, "default"))
- {
- TALER_buffer_write_path (&buf, "/instances/");
- TALER_buffer_write_str (&buf, instance_id);
- }
- TALER_buffer_write_path (&buf, "");
-
- return TALER_buffer_reap_str (&buf);
-}
-
-
-/**
- * Transform an order into a proposal and store it in the
- * database. Write the resulting proposal or an error message
- * of a MHD connection.
- *
- * @param connection connection to write the result or error to
- * @param root root of the request
- * @param order[in] order to process (can be modified)
- * @return MHD result code
- */
-static int
-proposal_put (struct MHD_Connection *connection,
- json_t *root,
- json_t *order,
- const struct MerchantInstance *mi)
-{
- int res;
- struct TALER_Amount total;
- const char *order_id;
- const char *summary;
- const char *fulfillment_url;
- json_t *products;
- json_t *merchant;
- struct GNUNET_TIME_Absolute timestamp;
- struct GNUNET_TIME_Absolute refund_deadline;
- struct GNUNET_TIME_Absolute wire_transfer_deadline;
- struct GNUNET_TIME_Absolute pay_deadline;
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_amount ("amount", &total),
- GNUNET_JSON_spec_string ("order_id", &order_id),
- GNUNET_JSON_spec_string ("summary", &summary),
- GNUNET_JSON_spec_string ("fulfillment_url",
- &fulfillment_url),
- /**
- * The following entries we don't actually need,
- * except to check that the order is well-formed */
- GNUNET_JSON_spec_json ("products", &products),
- GNUNET_JSON_spec_json ("merchant", &merchant),
- GNUNET_JSON_spec_absolute_time ("timestamp",
- ×tamp),
- GNUNET_JSON_spec_absolute_time ("refund_deadline",
- &refund_deadline),
- GNUNET_JSON_spec_absolute_time ("pay_deadline",
- &pay_deadline),
- GNUNET_JSON_spec_absolute_time ("wire_transfer_deadline",
- &wire_transfer_deadline),
- GNUNET_JSON_spec_end ()
- };
- enum GNUNET_DB_QueryStatus qs;
- struct WireMethod *wm;
-
- /* Add order_id if it doesn't exist. */
- if (NULL ==
- json_string_value (json_object_get (order,
- "order_id")))
- {
- char buf[256];
- time_t timer;
- struct tm*tm_info;
- size_t off;
- uint64_t rand;
- char *last;
-
- time (&timer);
- tm_info = localtime (&timer);
- if (NULL == tm_info)
- {
- return TMH_RESPONSE_reply_internal_error
- (connection,
- TALER_EC_PROPOSAL_NO_LOCALTIME,
- "failed to determine local time");
- }
- off = strftime (buf,
- sizeof (buf),
- "%Y.%j",
- tm_info);
- buf[off++] = '-';
- rand = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
- UINT64_MAX);
- last = GNUNET_STRINGS_data_to_string (&rand,
- sizeof (uint64_t),
- &buf[off],
- sizeof (buf) - off);
- *last = '\0';
- json_object_set_new (order,
- "order_id",
- json_string (buf));
- }
-
- /* Add timestamp if it doesn't exist */
- if (NULL == json_object_get (order,
- "timestamp"))
- {
- struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
-
- (void) GNUNET_TIME_round_abs (&now);
- json_object_set_new (order,
- "timestamp",
- GNUNET_JSON_from_time_abs (now));
- }
-
- /* If no refund_deadline given, set one as zero. */
- if (NULL == json_object_get (order,
- "refund_deadline"))
- {
- struct GNUNET_TIME_Absolute zero = { 0 };
-
- json_object_set_new (order,
- "refund_deadline",
- GNUNET_JSON_from_time_abs (zero));
- }
-
- if (NULL == json_object_get (order,
- "pay_deadline"))
- {
- struct GNUNET_TIME_Absolute t;
-
- t = GNUNET_TIME_relative_to_absolute (default_pay_deadline);
- (void) GNUNET_TIME_round_abs (&t);
- json_object_set_new (order,
- "pay_deadline",
- GNUNET_JSON_from_time_abs (t));
- }
-
- if (NULL == json_object_get (order,
- "wire_transfer_deadline"))
- {
- struct GNUNET_TIME_Absolute t;
-
- t = GNUNET_TIME_relative_to_absolute (default_wire_transfer_delay);
- (void) GNUNET_TIME_round_abs (&t);
- json_object_set_new (order,
- "wire_transfer_deadline",
- GNUNET_JSON_from_time_abs (t));
- }
-
- if (NULL == json_object_get (order,
- "max_wire_fee"))
- {
- json_object_set_new (order,
- "max_wire_fee",
- TALER_JSON_from_amount
- (&default_max_wire_fee));
- }
-
- if (NULL == json_object_get (order,
- "max_fee"))
- {
- json_object_set_new (order,
- "max_fee",
- TALER_JSON_from_amount
- (&default_max_deposit_fee));
- }
-
- if (NULL == json_object_get (order,
- "wire_fee_amortization"))
- {
- json_object_set_new
- (order,
- "wire_fee_amortization",
- json_integer
- ((json_int_t) default_wire_fee_amortization));
- }
-
- if (NULL == json_object_get (order,
- "merchant_base_url"))
- {
- char *url;
-
- url = make_merchant_base_url (connection, mi->id);
- json_object_set_new (order,
- "merchant_base_url",
- json_string (url));
- GNUNET_free (url);
- }
-
- if (NULL == json_object_get (order,
- "products"))
- {
- json_object_set_new (order,
- "products",
- json_array ());
- }
-
- /* Fill in merchant information if necessary */
- if (NULL == json_object_get (order, "merchant"))
- {
- const char *mj = NULL;
- const char *ma = NULL;
- json_t *locations;
- char *label;
- json_t *jmerchant;
-
- jmerchant = json_object ();
- json_object_set_new (jmerchant,
- "name",
- json_string (mi->name));
- json_object_set_new (jmerchant,
- "instance",
- json_string (mi->id));
- locations = json_object_get (order,
- "locations");
- if (NULL != locations)
- {
- json_t *loca;
- json_t *locj;
-
- /* Handle merchant address */
- GNUNET_assert (0 < GNUNET_asprintf (&label,
- "%s-address",
- mi->id));
- loca = json_object_get (default_locations,
- label);
- if (NULL != loca)
- {
- loca = json_deep_copy (loca);
- ma = STANDARD_LABEL_MERCHANT_ADDRESS;
- json_object_set_new (locations,
- ma,
- loca);
- json_object_set_new (jmerchant,
- "address",
- json_string (ma));
- }
- GNUNET_free (label);
-
- /* Handle merchant jurisdiction */
- GNUNET_assert (0 < GNUNET_asprintf (&label,
- "%s-jurisdiction",
- mi->id));
- locj = json_object_get (default_locations,
- label);
- if (NULL != locj)
- {
- if ( (NULL != loca) &&
- (1 == json_equal (locj,
- loca)) )
- {
- /* addresses equal, re-use */
- mj = ma;
- }
- else
- {
- locj = json_deep_copy (locj);
- mj = STANDARD_LABEL_MERCHANT_JURISDICTION;
- json_object_set_new (locations,
- mj,
- locj);
- }
- json_object_set_new (merchant,
- "jurisdiction",
- json_string (mj));
- }
- GNUNET_free (label);
- } /* have locations */
- json_object_set_new (order,
- "merchant",
- jmerchant);
- } /* needed to synthesize merchant info */
-
- /* extract fields we need to sign separately */
- res = TMH_PARSE_json_data (connection,
- order,
- spec);
- /* json is malformed */
- if (GNUNET_NO == res)
- {
- return MHD_YES;
- }
-
- /* other internal errors might have occurred */
- if (GNUNET_SYSERR == res)
- {
- return TMH_RESPONSE_reply_internal_error
- (connection,
- TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
- "Impossible to parse the order");
- }
-
- if (wire_transfer_deadline.abs_value_us <
- refund_deadline.abs_value_us)
- {
- GNUNET_JSON_parse_free (spec);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "invariant failed: wire_transfer_deadline >=
refund_deadline\n");
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "wire_transfer_deadline: %s\n",
- GNUNET_STRINGS_absolute_time_to_string (
- wire_transfer_deadline));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "refund_deadline: %s\n",
- GNUNET_STRINGS_absolute_time_to_string (refund_deadline));
- return TMH_RESPONSE_reply_arg_invalid
- (connection,
- TALER_EC_PARAMETER_MALFORMED,
- "order:wire_transfer_deadline;order:refund_deadline");
- }
-
-
- /* check contract is well-formed */
- if (GNUNET_OK != check_products (products))
- {
- GNUNET_JSON_parse_free (spec);
- return TMH_RESPONSE_reply_arg_invalid
- (connection,
- TALER_EC_PARAMETER_MALFORMED,
- "order:products");
- }
-
- /* add fields to the contract that the backend should provide */
- json_object_set (order,
- "exchanges",
- trusted_exchanges);
-
- json_object_set (order,
- "auditors",
- j_auditors);
- /* TODO (#4939-12806): add proper mechanism for
- selection of wire method(s) by merchant! */
- wm = mi->wm_head;
-
- if (NULL == wm)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "No wire method available for instance '%s'\n", mi->id);
- GNUNET_JSON_parse_free (spec);
- return TMH_RESPONSE_reply_not_found (connection,
-
TALER_EC_PROPOSAL_INSTANCE_CONFIGURATION_LACKS_WIRE,
- "No wire method configured for
instance");
- }
- json_object_set_new (order,
- "H_wire",
- GNUNET_JSON_from_data_auto (&wm->h_wire));
- json_object_set_new (order,
- "wire_method",
- json_string (wm->wire_method));
- json_object_set_new (order,
- "merchant_pub",
- GNUNET_JSON_from_data_auto (&mi->pubkey));
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Inserting order '%s' for instance '%s'\n",
- order_id,
- mi->id);
-
- for (unsigned int i = 0; i<MAX_RETRIES; i++)
- {
- db->preflight (db->cls);
- qs = db->insert_order (db->cls,
- order_id,
- &mi->pubkey,
- timestamp,
- order);
- if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
- break;
- }
- if (0 > qs)
- {
- /* Special report if retries insufficient */
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- GNUNET_break (0);
- return TMH_RESPONSE_reply_internal_error
- (connection,
- TALER_EC_PROPOSAL_STORE_DB_ERROR_SOFT,
- "db error: could not check for existing order"
- " due to repeated soft transaction failure");
- }
-
- {
- /* Hard error could be constraint violation,
- check if order already exists */
- json_t *contract_terms = NULL;
-
- db->preflight (db->cls);
- qs = db->find_order (db->cls,
- &contract_terms,
- order_id,
- &mi->pubkey);
- if (0 < qs)
- {
- /* Yep, indeed uniqueness constraint violation */
- int rv;
- char *msg;
-
- GNUNET_JSON_parse_free (spec);
- GNUNET_asprintf (&msg,
- "order ID `%s' already exists",
- order_id);
- {
- /* Log plenty of details for the admin */
- char *js;
-
- js = json_dumps (contract_terms,
- JSON_COMPACT);
- GNUNET_log
- (GNUNET_ERROR_TYPE_ERROR,
- _ ("Order ID `%s' already exists with proposal `%s'\n"),
- order_id,
- js);
- free (js);
- }
- json_decref (contract_terms);
-
- /* contract_terms may be private, only expose
- * duplicate order_id to the network */
- rv = TMH_RESPONSE_reply_external_error
- (connection,
- TALER_EC_PROPOSAL_STORE_DB_ERROR_ALREADY_EXISTS,
- msg);
- GNUNET_free (msg);
- return rv;
- }
- }
-
- /* Other hard transaction error (disk full, etc.) */
- GNUNET_JSON_parse_free (spec);
- return TMH_RESPONSE_reply_internal_error
- (connection,
- TALER_EC_PROPOSAL_STORE_DB_ERROR_HARD,
- "db error: could not store this proposal's data into db");
- }
-
- /* DB transaction succeeded, generate positive response */
- res = TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:s}",
- "order_id",
- order_id);
- GNUNET_JSON_parse_free (spec);
- return res;
-}
-
-
-/**
- * Generate a proposal, given its order. In practical terms,
- * it adds the fields 'exchanges', 'merchant_pub', and 'H_wire'
- * to the order gotten from the frontend. Finally, it signs this
- * data, and returns it to the frontend.
- *
- * @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
- */
-int
-MH_handler_proposal_put (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi)
-{
- int res;
- struct TMH_JsonParseContext *ctx;
- json_t *root;
- json_t *order;
-
- if (NULL == *connection_cls)
- {
- ctx = GNUNET_new (struct TMH_JsonParseContext);
- ctx->hc.cc = &json_parse_cleanup;
- *connection_cls = ctx;
- }
- else
- {
- ctx = *connection_cls;
- }
-
- res = TMH_PARSE_post_json (connection,
- &ctx->json_parse_context,
- upload_data,
- upload_data_size,
- &root);
-
- if (GNUNET_SYSERR == res)
- return MHD_NO;
-
- /* A error response was already generated */
- if ( (GNUNET_NO == res) ||
- /* or, need more data to accomplish parsing */
- (NULL == root) )
- return MHD_YES;
- order = json_object_get (root,
- "order");
- if (NULL == order)
- {
- res = TMH_RESPONSE_reply_arg_missing
- (connection,
- TALER_EC_PARAMETER_MISSING,
- "order");
- }
- else
- res = proposal_put (connection, root, order, mi);
- json_decref (root);
- return res;
-}
-
-
/**
* Manage a GET /proposal request. Query the db and returns the
* proposal's data related to the transaction id given as the URL's
diff --git a/src/backend/taler-merchant-httpd_proposal.h
b/src/backend/taler-merchant-httpd_proposal.h
index b118523..85db1ff 100644
--- a/src/backend/taler-merchant-httpd_proposal.h
+++ b/src/backend/taler-merchant-httpd_proposal.h
@@ -23,28 +23,6 @@
#include <microhttpd.h>
#include "taler-merchant-httpd.h"
-/**
- * Generate a proposal, given its order. In practical terms, it adds the
- * fields 'exchanges', 'merchant_pub', and 'H_wire' to the order gotten
- * from the frontend. Finally, it signs this data, and returns it to the
- * frontend.
- *
- * @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
- */
-int
-MH_handler_proposal_put (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
-
-
/**
* Manage a GET /proposal request. Query the db and returns the
* proposal's data related to the transaction id given as the URL's
diff --git a/src/backend/taler-merchant-httpd_tip-pickup.c
b/src/backend/taler-merchant-httpd_tip-pickup.c
index bead341..d53596c 100644
--- a/src/backend/taler-merchant-httpd_tip-pickup.c
+++ b/src/backend/taler-merchant-httpd_tip-pickup.c
@@ -649,7 +649,9 @@ MH_handler_tip_pickup_get (struct TMH_RequestHandler *rh,
"tip_id required");
}
- if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (tip_id_str, &tip_id))
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_hash_from_string (tip_id_str,
+ &tip_id))
{
/* tip_id has wrong encoding */
GNUNET_break_op (0);
diff --git a/src/include/taler_merchant_service.h
b/src/include/taler_merchant_service.h
index fff74ea..1d424f2 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -1077,4 +1077,78 @@ void
TALER_MERCHANT_tip_query_cancel (struct TALER_MERCHANT_TipQueryOperation *tqh);
+/* ********************** /public/poll-payment ************************* */
+
+
+/**
+ * Handle for a /public/poll-payment operation.
+ */
+struct TALER_MERCHANT_PollPaymentOperation;
+
+
+/**
+ * Callback to process a GET /poll-payment request
+ *
+ * @param cls closure
+ * @param http_status HTTP status code for this request
+ * @param obj raw response body
+ * @param paid #GNUNET_YES if the payment is settled, #GNUNET_NO if not
+ * settled, $GNUNET_SYSERR on error
+ * (note that refunded payments are returned as paid!)
+ * @param refunded #GNUNET_YES if there is at least on refund on this payment,
+ * #GNUNET_NO if refunded, #GNUNET_SYSERR or error
+ * @param refunded_amount amount that was refunded, NULL if there
+ * was no refund
+ * @param taler_pay_uri the URI that instructs the wallets to process
+ * the payment
+ */
+typedef void
+(*TALER_MERCHANT_PollPaymentCallback) (void *cls,
+ unsigned int http_status,
+ const json_t *obj,
+ int paid,
+ int refunded,
+ struct TALER_Amount *refund_amount,
+ const char *taler_pay_uri);
+
+
+/**
+ * Issue a /poll-payment request to the backend. Polls the status
+ * of a payment.
+ *
+ * @param ctx execution context
+ * @param backend_url base URL of the merchant backend
+ * @param order_id order id to identify the payment
+ * @param h_contract hash of the contract for @a order_id
+ * @parem session_id sesion id for the payment (or NULL if the payment is not
bound to a session)
+ * @param timeout timeout to use in long polling (how long may the server wait
to reply
+ * before generating an unpaid response). Note that this is just
provided to
+ * the server, we as client will block until the response comes back or
until
+ * #TALER_MERCHANT_poll_payment_cancel() is called.
+ * @param poll_payment_cb callback which will work the response gotten from
the backend
+ * @param poll_payment_cb_cls closure to pass to @a poll_payment_cb
+ * @return handle for this operation, NULL upon errors
+ */
+struct TALER_MERCHANT_PollPaymentOperation *
+TALER_MERCHANT_poll_payment (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *order_id,
+ const struct GNUNET_HashCode *h_contract,
+ const char *session_id,
+ struct GNUNET_TIME_Relative timeout,
+ TALER_MERCHANT_PollPaymentCallback
+ poll_payment_cb,
+ void *poll_payment_cls);
+
+
+/**
+ * Cancel a GET /public/poll-payment request.
+ *
+ * @param cpo handle to the request to be canceled
+ */
+void
+TALER_MERCHANT_poll_payment_cancel (struct
+ TALER_MERCHANT_PollPaymentOperation *cpo);
+
+
#endif /* _TALER_MERCHANT_SERVICE_H */
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 6a5e9bf..3d54d68 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -19,16 +19,17 @@ libtalermerchanttesting_la_LDFLAGS = \
-no-undefined
libtalermerchant_la_SOURCES = \
+ merchant_api_check_payment.c \
+ merchant_api_history.c \
merchant_api_proposal.c \
merchant_api_pay.c \
+ merchant_api_poll_payment.c \
+ merchant_api_refund.c \
merchant_api_tip_authorize.c \
merchant_api_tip_pickup.c \
+ merchant_api_tip_query.c \
merchant_api_track_transaction.c \
- merchant_api_track_transfer.c \
- merchant_api_history.c \
- merchant_api_refund.c \
- merchant_api_check_payment.c \
- merchant_api_tip_query.c
+ merchant_api_track_transfer.c
libtalermerchant_la_LIBADD = \
-ltalerexchange \
-ltalercurl \
diff --git a/src/lib/merchant_api_check_payment.c
b/src/lib/merchant_api_check_payment.c
index 8a85d26..35cd44c 100644
--- a/src/lib/merchant_api_check_payment.c
+++ b/src/lib/merchant_api_check_payment.c
@@ -79,12 +79,10 @@ handle_check_payment_finished (void *cls,
{
struct TALER_MERCHANT_CheckPaymentOperation *cpo = cls;
struct TALER_Amount refund_amount = { 0 };
- int refunded;
const json_t *json = response;
+ const json_t *refunded;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_boolean ("refunded",
- &refunded),
TALER_JSON_spec_amount ("refund_amount",
&refund_amount),
GNUNET_JSON_spec_end ()
@@ -103,7 +101,7 @@ handle_check_payment_finished (void *cls,
json,
GNUNET_SYSERR,
GNUNET_SYSERR,
- &refund_amount,
+ NULL,
NULL);
TALER_MERCHANT_check_payment_cancel (cpo);
return;
@@ -123,7 +121,7 @@ handle_check_payment_finished (void *cls,
json,
GNUNET_SYSERR,
GNUNET_SYSERR,
- &refund_amount,
+ NULL,
NULL);
}
else
@@ -133,17 +131,19 @@ handle_check_payment_finished (void *cls,
json,
GNUNET_NO,
GNUNET_NO,
- &refund_amount,
+ NULL,
taler_pay_uri);
}
TALER_MERCHANT_check_payment_cancel (cpo);
return;
}
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
+ if ( (NULL == (refunded = json_object_get (json, "refunded"))) ||
+ ( (json_true () == refunded) &&
+ (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL)) ) )
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"check payment failed to parse JSON\n");
@@ -153,7 +153,7 @@ handle_check_payment_finished (void *cls,
json,
GNUNET_SYSERR,
GNUNET_SYSERR,
- &refund_amount,
+ NULL,
NULL);
TALER_MERCHANT_check_payment_cancel (cpo);
return;
@@ -163,10 +163,9 @@ handle_check_payment_finished (void *cls,
MHD_HTTP_OK,
json,
GNUNET_YES,
- refunded,
- &refund_amount,
+ (json_true () == refunded),
+ (json_true () == refunded) ? &refund_amount : NULL,
NULL);
- GNUNET_JSON_parse_free (spec);
TALER_MERCHANT_check_payment_cancel (cpo);
}
diff --git a/src/lib/merchant_api_check_payment.c
b/src/lib/merchant_api_poll_payment.c
similarity index 54%
copy from src/lib/merchant_api_check_payment.c
copy to src/lib/merchant_api_poll_payment.c
index 8a85d26..84da443 100644
--- a/src/lib/merchant_api_check_payment.c
+++ b/src/lib/merchant_api_poll_payment.c
@@ -15,8 +15,8 @@
<http://www.gnu.org/licenses/>
*/
/**
- * @file lib/merchant_api_check_payment.c
- * @brief Implementation of the /check-payment GET request
+ * @file lib/merchant_api_poll_payment.c
+ * @brief Implementation of the /poll-payment GET request
* @author Christian Grothoff
* @author Marcello Stanisci
* @author Florian Dold
@@ -33,9 +33,9 @@
/**
- * @brief A check payment operation handle
+ * @brief A poll payment operation handle
*/
-struct TALER_MERCHANT_CheckPaymentOperation
+struct TALER_MERCHANT_PollPaymentOperation
{
/**
@@ -51,7 +51,7 @@ struct TALER_MERCHANT_CheckPaymentOperation
/**
* Function to call with the result.
*/
- TALER_MERCHANT_CheckPaymentCallback cb;
+ TALER_MERCHANT_PollPaymentCallback cb;
/**
* Closure for @a cb.
@@ -66,25 +66,23 @@ struct TALER_MERCHANT_CheckPaymentOperation
/**
- * Function called when we're done processing the GET /check-payment request.
+ * Function called when we're done processing the GET /poll-payment request.
*
- * @param cls the `struct TALER_MERCHANT_CheckPaymentOperation`
+ * @param cls the `struct TALER_MERCHANT_PollPaymentOperation`
* @param response_code HTTP response code, 0 on error
* @param json response body, should be NULL
*/
static void
-handle_check_payment_finished (void *cls,
- long response_code,
- const void *response)
+handle_poll_payment_finished (void *cls,
+ long response_code,
+ const void *response)
{
- struct TALER_MERCHANT_CheckPaymentOperation *cpo = cls;
+ struct TALER_MERCHANT_PollPaymentOperation *cpo = cls;
struct TALER_Amount refund_amount = { 0 };
- int refunded;
const json_t *json = response;
+ const json_t *refunded;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_boolean ("refunded",
- &refunded),
TALER_JSON_spec_amount ("refund_amount",
&refund_amount),
GNUNET_JSON_spec_end ()
@@ -95,7 +93,7 @@ handle_check_payment_finished (void *cls,
if (MHD_HTTP_OK != response_code)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Checking payment failed with HTTP status code %u\n",
+ "Polling payment failed with HTTP status code %u\n",
(unsigned int) response_code);
GNUNET_break_op (0);
cpo->cb (cpo->cb_cls,
@@ -103,9 +101,9 @@ handle_check_payment_finished (void *cls,
json,
GNUNET_SYSERR,
GNUNET_SYSERR,
- &refund_amount,
+ NULL,
NULL);
- TALER_MERCHANT_check_payment_cancel (cpo);
+ TALER_MERCHANT_poll_payment_cancel (cpo);
return;
}
@@ -116,14 +114,14 @@ handle_check_payment_finished (void *cls,
if (NULL == taler_pay_uri)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "no taler_pay_uri in unpaid check-payment response\n");
+ "no taler_pay_uri in unpaid poll-payment response\n");
GNUNET_break_op (0);
cpo->cb (cpo->cb_cls,
0,
json,
GNUNET_SYSERR,
GNUNET_SYSERR,
- &refund_amount,
+ NULL,
NULL);
}
else
@@ -133,29 +131,32 @@ handle_check_payment_finished (void *cls,
json,
GNUNET_NO,
GNUNET_NO,
- &refund_amount,
+ NULL,
taler_pay_uri);
}
- TALER_MERCHANT_check_payment_cancel (cpo);
+ TALER_MERCHANT_poll_payment_cancel (cpo);
return;
}
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
+ if ( (NULL == (refunded = json_object_get (json,
+ "refunded"))) ||
+ ( (json_true () == refunded) &&
+ (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL)) ) )
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "check payment failed to parse JSON\n");
+ "poll payment failed to parse JSON\n");
GNUNET_break_op (0);
cpo->cb (cpo->cb_cls,
0,
json,
GNUNET_SYSERR,
GNUNET_SYSERR,
- &refund_amount,
+ NULL,
NULL);
- TALER_MERCHANT_check_payment_cancel (cpo);
+ TALER_MERCHANT_poll_payment_cancel (cpo);
return;
}
@@ -163,49 +164,70 @@ handle_check_payment_finished (void *cls,
MHD_HTTP_OK,
json,
GNUNET_YES,
- refunded,
- &refund_amount,
+ (json_true () == refunded),
+ (json_true () == refunded) ? &refund_amount : NULL,
NULL);
- GNUNET_JSON_parse_free (spec);
- TALER_MERCHANT_check_payment_cancel (cpo);
+ TALER_MERCHANT_poll_payment_cancel (cpo);
}
/**
- * Issue a /check-payment request to the backend. Checks the status
+ * Issue a /poll-payment request to the backend. Polls the status
* of a payment.
*
* @param ctx execution context
* @param backend_url base URL of the merchant backend
* @param order_id order id to identify the payment
+ * @param h_contract hash of the contract for @a order_id
* @parem session_id sesion id for the payment (or NULL if the payment is not
bound to a session)
- * @param check_payment_cb callback which will work the response gotten from
the backend
- * @param check_payment_cb_cls closure to pass to @a check_payment_cb
+ * @param timeout timeout to use in long polling (how long may the server wait
to reply
+ * before generating an unpaid response). Note that this is just
provided to
+ * the server, we as client will block until the response comes back or
until
+ * #TALER_MERCHANT_poll_payment_cancel() is called.
+ * @param poll_payment_cb callback which will work the response gotten from
the backend
+ * @param poll_payment_cb_cls closure to pass to @a poll_payment_cb
* @return handle for this operation, NULL upon errors
*/
-struct TALER_MERCHANT_CheckPaymentOperation *
-TALER_MERCHANT_check_payment (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *order_id,
- const char *session_id,
- TALER_MERCHANT_CheckPaymentCallback
- check_payment_cb,
- void *check_payment_cb_cls)
+struct TALER_MERCHANT_PollPaymentOperation *
+TALER_MERCHANT_poll_payment (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *order_id,
+ const struct GNUNET_HashCode *h_contract,
+ const char *session_id,
+ struct GNUNET_TIME_Relative timeout,
+ TALER_MERCHANT_PollPaymentCallback
+ poll_payment_cb,
+ void *poll_payment_cb_cls)
{
- struct TALER_MERCHANT_CheckPaymentOperation *cpo;
+ struct TALER_MERCHANT_PollPaymentOperation *cpo;
CURL *eh;
+ char *h_contract_s;
+ char *timeout_s;
+ unsigned int ts;
GNUNET_assert (NULL != backend_url);
GNUNET_assert (NULL != order_id);
-
- cpo = GNUNET_new (struct TALER_MERCHANT_CheckPaymentOperation);
+ h_contract_s = GNUNET_STRINGS_data_to_string_alloc (h_contract,
+ sizeof (*h_contract));
+ ts = (unsigned int) (timeout.rel_value_us
+ / GNUNET_TIME_UNIT_SECONDS.rel_value_us);
+ GNUNET_asprintf (&timeout_s,
+ "%u",
+ ts);
+ cpo = GNUNET_new (struct TALER_MERCHANT_PollPaymentOperation);
cpo->ctx = ctx;
- cpo->cb = check_payment_cb;
- cpo->cb_cls = check_payment_cb_cls;
- cpo->url = TALER_url_join (backend_url, "check-payment",
+ cpo->cb = poll_payment_cb;
+ cpo->cb_cls = poll_payment_cb_cls;
+ cpo->url = TALER_url_join (backend_url,
+ "public/poll-payment",
"order_id", order_id,
"session_id", session_id,
+ "h_contract", h_contract_s,
+ (0 != ts) ? "timeout" : NULL,
+ timeout_s,
NULL);
+ GNUNET_free (h_contract_s);
+ GNUNET_free (timeout_s);
eh = curl_easy_init ();
if (CURLE_OK != curl_easy_setopt (eh,
CURLOPT_URL,
@@ -216,13 +238,13 @@ TALER_MERCHANT_check_payment (struct GNUNET_CURL_Context
*ctx,
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "checking payment from %s\n",
+ "polling payment from %s\n",
cpo->url);
if (NULL == (cpo->job = GNUNET_CURL_job_add (ctx,
eh,
GNUNET_YES,
- &handle_check_payment_finished,
+ &handle_poll_payment_finished,
cpo)))
{
GNUNET_break (0);
@@ -233,13 +255,13 @@ TALER_MERCHANT_check_payment (struct GNUNET_CURL_Context
*ctx,
/**
- * Cancel a GET /check-payment request.
+ * Cancel a GET /poll-payment request.
*
* @param cph handle to the request to be canceled
*/
void
-TALER_MERCHANT_check_payment_cancel (struct
- TALER_MERCHANT_CheckPaymentOperation *cph)
+TALER_MERCHANT_poll_payment_cancel (struct
+ TALER_MERCHANT_PollPaymentOperation *cph)
{
if (NULL != cph->job)
{
@@ -251,4 +273,4 @@ TALER_MERCHANT_check_payment_cancel (struct
}
-/* end of merchant_api_check_payment.c */
+/* end of merchant_api_poll_payment.c */
--
To stop receiving notification emails like this one, please contact
address@hidden.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-merchant] tag demo-2019-11-02-00 updated (b4f2c5e -> 3a22e60),
gnunet <=