diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-04-10 23:21:11 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-04-10 23:21:11 +0200 |
commit | 6d978efe14aa90564ce9257a23bcb2854ba07036 (patch) | |
tree | 40d7e0fbf22d9364c5809f46debfe9a977146cb4 /src/backend | |
parent | c22eb34d925c55e1a07710c6f0e8df5b954dece7 (diff) | |
download | merchant-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.c | 77 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.h | 29 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_check-payment.c | 3 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_pay.c | 65 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_poll-payment.c | 86 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_refund_increase.c | 5 |
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; |