summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-04-10 23:21:11 +0200
committerChristian Grothoff <christian@grothoff.org>2020-04-10 23:21:11 +0200
commit6d978efe14aa90564ce9257a23bcb2854ba07036 (patch)
tree40d7e0fbf22d9364c5809f46debfe9a977146cb4 /src/backend
parentc22eb34d925c55e1a07710c6f0e8df5b954dece7 (diff)
downloadmerchant-6d978efe14aa90564ce9257a23bcb2854ba07036.tar.gz
merchant-6d978efe14aa90564ce9257a23bcb2854ba07036.tar.bz2
merchant-6d978efe14aa90564ce9257a23bcb2854ba07036.zip
implementing long-polling for refunds (#5985)
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/taler-merchant-httpd.c77
-rw-r--r--src/backend/taler-merchant-httpd.h29
-rw-r--r--src/backend/taler-merchant-httpd_check-payment.c3
-rw-r--r--src/backend/taler-merchant-httpd_pay.c65
-rw-r--r--src/backend/taler-merchant-httpd_poll-payment.c86
-rw-r--r--src/backend/taler-merchant-httpd_refund_increase.c5
6 files changed, 191 insertions, 74 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index f577c470..1f9d439e 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -300,15 +300,23 @@ do_resume (void *cls)
* Suspend connection from @a sc until payment has been received.
*
* @param sc connection to suspend
+ * @param min_refund refund amount we are waiting on to be exceeded before resuming,
+ * NULL if we are not waiting for refunds
*/
void
-TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc)
+TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
+ const struct TALER_Amount *min_refund)
{
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (payment_trigger_map,
&sc->key,
sc,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+ if (NULL != min_refund)
+ {
+ sc->awaiting_refund = GNUNET_YES;
+ sc->refund_expected = *min_refund;
+ }
sc->hn = GNUNET_CONTAINER_heap_insert (resume_timeout_heap,
sc,
sc->long_poll_timeout.abs_value_us);
@@ -326,6 +334,73 @@ TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc)
/**
+ * Function called to resume suspended connections.
+ *
+ * @param cls pointer to a `struct TALER_Amount` indicating the refund amount, or NULL
+ * @param key key in the #payment_trigger_map
+ * @param value a `struct TMH_SuspendedConnection` to resume
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+resume_operation (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ const struct TALER_Amount *have_refund = cls;
+ struct TMH_SuspendedConnection *sc = value;
+
+ if ( (GNUNET_YES == sc->awaiting_refund) &&
+ ( (NULL == have_refund) ||
+ (1 != TALER_amount_cmp (have_refund,
+ &sc->refund_expected)) ) )
+ return GNUNET_OK; /* skip */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Resuming operation suspended pending payment on key %s\n",
+ GNUNET_h2s (key));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
+ key,
+ sc));
+ GNUNET_assert (sc ==
+ GNUNET_CONTAINER_heap_remove_node (sc->hn));
+ sc->hn = NULL;
+ MHD_resume_connection (sc->con);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Find out if we have any clients long-polling for @a order_id to be
+ * confirmed at merchant @a mpub, and if so, tell them to resume.
+ *
+ * @param order_id the order that was paid
+ * @param mpub the merchant's public key of the instance where the payment happened
+ * @param have_refund refunded amount, NULL if there was no refund
+ */
+void
+TMH_long_poll_resume (const char *order_id,
+ const struct TALER_MerchantPublicKeyP *mpub,
+ const struct TALER_Amount *have_refund)
+{
+ struct GNUNET_HashCode key;
+
+ TMH_compute_pay_key (order_id,
+ mpub,
+ &key);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Resuming operations suspended pending payment on key %s\n",
+ GNUNET_h2s (&key));
+ GNUNET_CONTAINER_multihashmap_get_multiple (payment_trigger_map,
+ &key,
+ &resume_operation,
+ (void *) have_refund);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%u operations remain suspended pending payment\n",
+ GNUNET_CONTAINER_multihashmap_size (payment_trigger_map));
+}
+
+
+/**
* Create a taler://pay/ URI for the given @a con and @a order_id
* and @a session_id and @a instance_id.
*
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index b24b321b..ad83a669 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -289,6 +289,16 @@ struct TMH_SuspendedConnection
*/
struct GNUNET_TIME_Absolute long_poll_timeout;
+ /**
+ * Minimum refund amount to be exceeded (exclusive this value!) for resume.
+ */
+ struct TALER_Amount refund_expected;
+
+ /**
+ * #GNUNET_YES if we are waiting for a refund.
+ */
+ int awaiting_refund;
+
};
@@ -412,9 +422,26 @@ TMH_compute_pay_key (const char *order_id,
* Suspend connection from @a sc until payment has been received.
*
* @param sc connection to suspend
+ * @param min_refund refund amount we are waiting on to be exceeded before resuming,
+ * NULL if we are not waiting for refunds
+ */
+void
+TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
+ const struct TALER_Amount *min_refund);
+
+
+/**
+ * Find out if we have any clients long-polling for @a order_id to be
+ * confirmed at merchant @a mpub, and if so, tell them to resume.
+ *
+ * @param order_id the order that was paid
+ * @param mpub the merchant's public key of the instance where the payment happened
+ * @param refund_amount refunded amount, if the trigger was a refund, otherwise NULL
*/
void
-TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc);
+TMH_long_poll_resume (const char *order_id,
+ const struct TALER_MerchantPublicKeyP *mpub,
+ const struct TALER_Amount *refund_amount);
/**
diff --git a/src/backend/taler-merchant-httpd_check-payment.c b/src/backend/taler-merchant-httpd_check-payment.c
index 66b4941e..bb5384d1 100644
--- a/src/backend/taler-merchant-httpd_check-payment.c
+++ b/src/backend/taler-merchant-httpd_check-payment.c
@@ -194,7 +194,8 @@ send_pay_request (struct CheckPaymentRequestContext *cprc)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Suspending /check-payment on key %s\n",
GNUNET_h2s (&cprc->sc.key));
- TMH_long_poll_suspend (&cprc->sc);
+ TMH_long_poll_suspend (&cprc->sc,
+ NULL);
return MHD_YES;
}
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
index 4c1e2367..446320b3 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -444,66 +444,6 @@ MH_force_pc_resume ()
/**
- * Function called to resume suspended connections.
- *
- * @param cls NULL
- * @param key key in the #payment_trigger_map
- * @param value a `struct TMH_SuspendedConnection` to resume
- * @return #GNUNET_OK (continue to iterate)
- */
-static int
-resume_operation (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
-{
- struct TMH_SuspendedConnection *sc = value;
-
- (void) cls;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Resuming operation suspended pending payment on key %s\n",
- GNUNET_h2s (key));
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
- key,
- sc));
- GNUNET_assert (sc ==
- GNUNET_CONTAINER_heap_remove_node (sc->hn));
- sc->hn = NULL;
- MHD_resume_connection (sc->con);
- return GNUNET_OK;
-}
-
-
-/**
- * Find out if we have any clients long-polling for @a order_id to be
- * confirmed at merchant @a mpub, and if so, tell them to resume.
- *
- * @param order_id the order that was paid
- * @param mpub the merchant's public key of the instance where the payment happened
- */
-static void
-resume_suspended_payment_checks (const char *order_id,
- const struct TALER_MerchantPublicKeyP *mpub)
-{
- struct GNUNET_HashCode key;
-
- TMH_compute_pay_key (order_id,
- mpub,
- &key);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Resuming operations suspended pending payment on key %s\n",
- GNUNET_h2s (&key));
- GNUNET_CONTAINER_multihashmap_get_multiple (payment_trigger_map,
- &key,
- &resume_operation,
- NULL);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "%u operations remain suspended pending payment\n",
- GNUNET_CONTAINER_multihashmap_size (payment_trigger_map));
-}
-
-
-/**
* Resume the given pay context and send the given response.
* Stores the response in the @a pc and signals MHD to resume
* the connection. Also ensures MHD runs immediately.
@@ -2166,8 +2106,9 @@ begin_transaction (struct PayContext *pc)
"Merchant database error: could not commit to mark proposal as 'paid'");
return;
}
- resume_suspended_payment_checks (pc->order_id,
- &pc->mi->pubkey);
+ TMH_long_poll_resume (pc->order_id,
+ &pc->mi->pubkey,
+ NULL);
generate_success_response (pc);
return;
}
diff --git a/src/backend/taler-merchant-httpd_poll-payment.c b/src/backend/taler-merchant-httpd_poll-payment.c
index 8db72896..49f94896 100644
--- a/src/backend/taler-merchant-httpd_poll-payment.c
+++ b/src/backend/taler-merchant-httpd_poll-payment.c
@@ -102,12 +102,24 @@ struct PollPaymentRequestContext
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!
*/
@@ -124,8 +136,8 @@ struct PollPaymentRequestContext
static void
pprc_cleanup (struct TM_HandlerContext *hc)
{
- struct PollPaymentRequestContext *pprc = (struct
- PollPaymentRequestContext *) hc;
+ struct PollPaymentRequestContext *pprc
+ = (struct PollPaymentRequestContext *) hc;
if (NULL != pprc->contract_terms)
json_decref (pprc->contract_terms);
@@ -171,6 +183,27 @@ process_refunds_cb (void *cls,
/**
+ * 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
@@ -188,13 +221,7 @@ send_pay_request (struct PollPaymentRequestContext *pprc)
if (0 != remaining.rel_value_us)
{
/* long polling: do not queue a response, suspend connection instead */
- 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);
+ suspend_pprc (pprc);
return MHD_YES;
}
@@ -274,6 +301,7 @@ MH_handler_poll_payment (struct TMH_RequestHandler *rh,
/* 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;
@@ -343,6 +371,27 @@ MH_handler_poll_payment (struct TMH_RequestHandler *rh,
{
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");
@@ -495,6 +544,25 @@ MH_handler_poll_payment (struct TMH_RequestHandler *rh,
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 */
+ pprc->refunded = GNUNET_NO;
+ suspend_pprc (pprc);
+ return MHD_YES;
+ }
+ }
+
if (pprc->refunded)
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
diff --git a/src/backend/taler-merchant-httpd_refund_increase.c b/src/backend/taler-merchant-httpd_refund_increase.c
index 1832deec..6d49dfc4 100644
--- a/src/backend/taler-merchant-httpd_refund_increase.c
+++ b/src/backend/taler-merchant-httpd_refund_increase.c
@@ -257,6 +257,11 @@ process_refund (struct MHD_Connection *connection,
"Amount above payment");
}
+ /* Resume /public/poll-payments clients that may wait for this refund */
+ TMH_long_poll_resume (order_id,
+ &mi->pubkey,
+ refund);
+
{
MHD_RESULT ret;
char *taler_refund_uri;