diff options
Diffstat (limited to 'src/backend/taler-merchant-httpd_get-orders-ID.c')
-rw-r--r-- | src/backend/taler-merchant-httpd_get-orders-ID.c | 723 |
1 files changed, 532 insertions, 191 deletions
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c index e86e4e4b..ba0d3544 100644 --- a/src/backend/taler-merchant-httpd_get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -14,9 +14,10 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** - * @file backend/taler-merchant-httpd_refund_lookup.c - * @brief refund handling logic + * @file backend/taler-merchant-httpd_get-orders-ID.c + * @brief implementation of GET /orders/$ID * @author Marcello Stanisci + * @author Christian Grothoff */ #include "platform.h" #include <jansson.h> @@ -59,9 +60,9 @@ struct CoinRefund struct TALER_EXCHANGE_RefundHandle *rh; /** - * PRD this operation is part of. + * Request this operation is part of. */ - struct ProcessRefundData *prd; + struct GetOrderData *god; /** * URL of the exchange for this @e coin_pub. @@ -119,15 +120,10 @@ struct CoinRefund /** - * Closure for #process_refunds_cb. + * Context for the operation. */ -struct ProcessRefundData +struct GetOrderData { - /** - * Must be first for #handle_mhd_completion_callback() cleanup - * logic to work. - */ - struct TM_HandlerContext hc; /** * Hashed version of contract terms. @@ -137,106 +133,186 @@ struct ProcessRefundData /** * DLL of (suspended) requests. */ - struct ProcessRefundData *next; + struct GetOrderData *next; /** * DLL of (suspended) requests. */ - struct ProcessRefundData *prev; + struct GetOrderData *prev; /** - * Head of DLL of coin refunds for this request. + * Context of the request. */ - struct CoinRefund *cr_head; + struct TMH_HandlerContext *hc; /** - * Tail of DLL of coin refunds for this request. + * Did we suspend @a connection? */ - struct CoinRefund *cr_tail; + int suspended; /** - * Both public and private key are needed by the callback + * Return code: #TALER_EC_NONE if successful. */ - const struct MerchantInstance *merchant; + enum TALER_ErrorCode ec; /** - * Connection we are handling. + * Entry in the #resume_timeout_heap for this check payment, if we are + * suspended. */ - struct MHD_Connection *connection; + struct TMH_SuspendedConnection sc; /** - * Did we suspend @a connection? + * Which merchant instance is this for? */ - int suspended; + struct MerchantInstance *mi; /** - * Return code: #TALER_EC_NONE if successful. + * URL where the final contract can be found for this payment. */ - enum TALER_ErrorCode ec; + char *final_contract_url; + + /** + * order ID for the payment + */ + const char *order_id; + + /** + * Where to get the contract + */ + const char *contract_url; + + /** + * fulfillment URL of the contract (valid as long as + * @e contract_terms is valid). + */ + const char *fulfillment_url; + + /** + * session of the client + */ + const char *session_id; + + /** + * Contract terms of the payment we are checking. NULL when they + * are not (yet) known. + */ + json_t *contract_terms; + + /** + * Hash of @e contract_terms, set only once @e contract_terms + * is available. + */ + struct GNUNET_HashCode h_contract_terms; + + /** + * Total refunds granted for this payment. Only initialized + * if @e refunded is set to true. + */ + struct TALER_Amount refund_amount; + + /** + * Set to true if this payment has been refunded and + * @e refund_amount is initialized. + */ + bool refunded; + + /** + * Initially #GNUNET_SYSERR. If we queued a response, set to the + * result code (i.e. #MHD_YES or #MHD_NO). FIXME: fix type! + */ + int ret; + + }; /** * HEad of DLL of (suspended) requests. */ -static struct ProcessRefundData *prd_head; +static struct GetOrderData *prd_head; /** * Tail of DLL of (suspended) requests. */ -static struct ProcessRefundData *prd_tail; +static struct GetOrderData *prd_tail; + + +/** + * Force resuming all suspended order lookups, needed during shutdown. + */ +void +TMH_force_wallet_get_order_resume (void) +{ + struct GetOrderData *god; + + while (NULL != (god = god_head)) + { + GNUNET_CONTAINER_DLL_remove (god_head, + god_tail, + god); + GNUNET_assert (god->suspended); + god->suspended = GNUNET_NO; + MHD_resume_connection (god->connection); + } +} /** - * Clean up memory in @a cls, the connection was closed. + * The client did not yet pay, send it the payment request. * - * @param cls a `struct ProcessRefundData` to clean up. + * @param god check pay request context + * @return #MHD_YES on success */ -static void -cleanup_prd (struct TM_HandlerContext *cls) +static MHD_RESULT +send_pay_request (struct GetOrderData *god, + const char *already_paid_order_id) { - struct ProcessRefundData *prd = (struct ProcessRefundData *) cls; - struct CoinRefund *cr; + MHD_RESULT ret; + char *taler_pay_uri; + struct GNUNET_TIME_Relative remaining; - while (NULL != (cr = prd->cr_head)) + remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout); + if (0 != remaining.rel_value_us) { - GNUNET_CONTAINER_DLL_remove (prd->cr_head, - prd->cr_tail, - cr); - if (NULL != cr->fo) - { - TMH_EXCHANGES_find_exchange_cancel (cr->fo); - cr->fo = NULL; - } - if (NULL != cr->rh) - { - TALER_EXCHANGE_refund_cancel (cr->rh); - cr->rh = NULL; - } - if (NULL != cr->exchange_reply) - { - json_decref (cr->exchange_reply); - cr->exchange_reply = NULL; - } - GNUNET_free (cr->exchange_url); - GNUNET_free (cr); + /* long polling: do not queue a response, suspend connection instead */ + suspend_god (god); + return MHD_YES; } - GNUNET_free (prd); + + /* Check if resource_id has been paid for in the same session + * with another order_id. + */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending payment request in /poll-payment\n"); + taler_pay_uri = TMH_make_taler_pay_uri (god->sc.con, + god->order_id, + god->session_id, + god->mi->id); + ret = TALER_MHD_reply_json_pack (god->sc.con, + MHD_HTTP_OK, + "{s:s, s:s, s:b, s:s?}", + "taler_pay_uri", taler_pay_uri, + "contract_url", god->final_contract_url, + "paid", 0, + "already_paid_order_id", + already_paid_order_id); + GNUNET_free (taler_pay_uri); + return ret; } /** - * Check if @a prd has sub-activities still pending. + * Check if @a god has sub-activities still pending. * - * @param prd request to check + * @param god request to check * @return #GNUNET_YES if activities are still pending */ static int -prd_pending (struct ProcessRefundData *prd) +god_pending (struct GetOrderData *god) { int pending = GNUNET_NO; - for (struct CoinRefund *cr = prd->cr_head; + for (struct CoinRefund *cr = god->cr_head; NULL != cr; cr = cr->next) { @@ -252,21 +328,21 @@ prd_pending (struct ProcessRefundData *prd) /** - * Check if @a prd is ready to be resumed, and if so, do it. + * Check if @a god is ready to be resumed, and if so, do it. * - * @param prd refund request to be possibly ready + * @param god refund request to be possibly ready */ static void -check_resume_prd (struct ProcessRefundData *prd) +check_resume_god (struct GetOrderData *god) { - if (prd_pending (prd)) + if (god_pending (god)) return; - GNUNET_CONTAINER_DLL_remove (prd_head, - prd_tail, - prd); - GNUNET_assert (prd->suspended); - prd->suspended = GNUNET_NO; - MHD_resume_connection (prd->connection); + GNUNET_CONTAINER_DLL_remove (god_head, + god_tail, + god); + GNUNET_assert (god->suspended); + god->suspended = GNUNET_NO; + MHD_resume_connection (god->connection); TMH_trigger_daemon (); } @@ -306,8 +382,8 @@ refund_cb (void *cls, cr->exchange_pub = *exchange_pub; cr->exchange_sig = *exchange_sig; qs = db->put_refund_proof (db->cls, - &cr->prd->merchant->pubkey, - &cr->prd->h_contract_terms, + &cr->god->merchant->pubkey, + &cr->god->h_contract_terms, &cr->coin_pub, cr->rtransaction_id, exchange_pub, @@ -321,7 +397,7 @@ refund_cb (void *cls, qs); } } - check_resume_prd (cr->prd); + check_resume_god (cr->god); } @@ -350,10 +426,10 @@ exchange_found_cb (void *cls, cr->rh = TALER_EXCHANGE_refund (eh, &cr->refund_amount, &cr->refund_fee, - &cr->prd->h_contract_terms, + &cr->god->h_contract_terms, &cr->coin_pub, cr->rtransaction_id, - &cr->prd->merchant->privkey, + &cr->god->merchant->privkey, &refund_cb, cr); return; @@ -361,7 +437,7 @@ exchange_found_cb (void *cls, cr->exchange_status = hr->http_status; cr->exchange_code = hr->ec; cr->exchange_reply = json_incref ((json_t*) hr->reply); - check_resume_prd (cr->prd); + check_resume_god (cr->god); } @@ -386,7 +462,7 @@ process_refunds_cb (void *cls, const struct TALER_Amount *refund_amount, const struct TALER_Amount *refund_fee) { - struct ProcessRefundData *prd = cls; + struct GetOrderData *god = cls; struct CoinRefund *cr; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -396,83 +472,230 @@ process_refunds_cb (void *cls, reason); cr = GNUNET_new (struct CoinRefund); cr->exchange_url = GNUNET_strdup (exchange_url); - cr->prd = prd; + cr->god = god; cr->coin_pub = *coin_pub; cr->rtransaction_id = rtransaction_id; cr->refund_amount = *refund_amount; cr->refund_fee = *refund_fee; - GNUNET_CONTAINER_DLL_insert (prd->cr_head, - prd->cr_tail, + GNUNET_CONTAINER_DLL_insert (god->cr_head, + god->cr_tail, cr); + if (god->refunded) + { + GNUNET_assert (0 <= + TALER_amount_add (&god->refund_amount, + &god->refund_amount, + refund_amount)); + return; + } + god->refund_amount = *refund_amount; + god->refunded = true; } /** - * Force resuming all suspended refund lookups, needed during shutdown. + * Suspend this @a god until the trigger is satisfied. + * + * @param ppr */ -void -MH_force_refund_resume (void) +static void +suspend_god (struct GetOrderData *god) { - struct ProcessRefundData *prd; + TMH_compute_pay_key (god->order_id, + &god->mi->pubkey, + &god->sc.key); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Suspending /poll-payment on key %s\n", + GNUNET_h2s (&god->sc.key)); + TMH_long_poll_suspend (&god->sc, + (god->sc.awaiting_refund) + ? &god->sc.refund_expected + : NULL); +} - while (NULL != (prd = prd_head)) + +/** + * Clean up the session state for a GET /orders/$ID request. + * + * @param cls must be a `struct GetOrderData *` + */ +static void +god_cleanup (void *cls) +{ + struct GetOrderData *god = cls; + + while (NULL != (cr = god->cr_head)) { - GNUNET_CONTAINER_DLL_remove (prd_head, - prd_tail, - prd); - GNUNET_assert (prd->suspended); - prd->suspended = GNUNET_NO; - MHD_resume_connection (prd->connection); + GNUNET_CONTAINER_DLL_remove (god->cr_head, + god->cr_tail, + cr); + if (NULL != cr->fo) + { + TMH_EXCHANGES_find_exchange_cancel (cr->fo); + cr->fo = NULL; + } + if (NULL != cr->rh) + { + TALER_EXCHANGE_refund_cancel (cr->rh); + cr->rh = NULL; + } + if (NULL != cr->exchange_reply) + { + json_decref (cr->exchange_reply); + cr->exchange_reply = NULL; + } + GNUNET_free (cr->exchange_url); + GNUNET_free (cr); } + + if (NULL != god->contract_terms) + json_decref (god->contract_terms); + GNUNET_free_non_null (god->final_contract_url); + GNUNET_free (god); } /** - * Return refund situation about a contract. + * Handle a GET "/orders/$ID" request. * * @param rh context of the handler * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL + * @param[in,out] hc context with further information about the request * @return MHD result code */ MHD_RESULT -MH_handler_refund_lookup (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size, - struct MerchantInstance *mi) +TMH_get_orders_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { - struct ProcessRefundData *prd; - const char *order_id; - json_t *contract_terms; + struct GetOrderData *god = hc->ctx; + const char *order_id = hc->infix; enum GNUNET_DB_QueryStatus qs; - prd = *connection_cls; - if (NULL == prd) + if (NULL == god) { - order_id = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "order_id"); - if (NULL == order_id) + god = GNUNET_new (struct GetOrderData); + hc->ctx = god; + god->hc.cc = &god_cleanup; + god->sc.con = connection; + god->ec = TALER_EC_NONE; + god->connection = connection; + god->hc = hc; + god->ret = GNUNET_SYSERR; + { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_PARAMETER_MISSING, - "order_id"); + const char *cts; + + cts = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "h_contract"); + if (NULL == cts) + { + /* h_contract required but missing */ + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_PARAMETER_MISSING, + "h_contract required"); + } + if (GNUNET_OK != + GNUNET_CRYPTO_hash_from_string (cts, + &god->h_contract_terms)) + { + /* cts has wrong encoding */ + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_PARAMETER_MALFORMED, + "h_contract malformed"); + } + } + + { + const char *long_poll_timeout_s; + + long_poll_timeout_s = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "timeout"); + if (NULL != long_poll_timeout_s) + { + unsigned int timeout; + + if (1 != sscanf (long_poll_timeout_s, + "%u", + &timeout)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_PARAMETER_MALFORMED, + "timeout must be non-negative number"); + } + god->sc.long_poll_timeout + = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + timeout)); + } + else + { + god->sc.long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS; + } } + { + const char *min_refund; + + min_refund = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "refund"); + if (NULL != min_refund) + { + if ( (GNUNET_OK != + TALER_string_to_amount (min_refund, + &god->sc.refund_expected)) || + (0 != strcasecmp (god->sc.refund_expected.currency, + TMH_currency) ) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_PARAMETER_MALFORMED, + "invalid amount given for refund argument"); + } + god->sc.awaiting_refund = true; + } + } + + // FIXME: what is this about again??? + god->contract_url = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "contract_url"); + if (NULL == god->contract_url) + { + // FIXME: this can't be right anymore... + god->final_contract_url = TALER_url_absolute_mhd (connection, + "/public/proposal", + "instance", mi->id, + "order_id", + god->order_id, + NULL); + GNUNET_assert (NULL != god->final_contract_url); + } + else + { + god->final_contract_url = GNUNET_strdup (god->contract_url); + } + + god->session_id = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "session_id"); + /* Convert order id to h_contract_terms */ - contract_terms = NULL; db->preflight (db->cls); qs = db->find_contract_terms (db->cls, - &contract_terms, + hc->instance->settings.id, order_id, - &mi->pubkey); + &god->contract_terms); if (0 > qs) { /* single, read-only SQL statements should never cause @@ -483,9 +706,8 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh, return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_REFUND_LOOKUP_DB_ERROR, - "database error looking up order_id from merchant_contract_terms table"); + "database error looking up contract"); } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -493,108 +715,210 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh, order_id); return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, - TALER_EC_REFUND_ORDER_ID_UNKNOWN, + TALER_EC_ORDER_ID_UNKNOWN, "order_id not found in database"); } - prd = GNUNET_new (struct ProcessRefundData); - if (GNUNET_OK != - TALER_JSON_hash (contract_terms, - &prd->h_contract_terms)) + /* Check client provided the right hash code of the contract terms */ + { + struct GNUNET_HashCode h; + + if (GNUNET_OK != + TALER_JSON_hash (god->contract_terms, + &h)) + { + GNUNET_break (0); + GNUNET_free (god); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_INTERNAL_LOGIC_ERROR, + "Could not hash contract terms"); + } + if (0 != + GNUNET_memcmp (&h, + &god->h_contract_terms)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_WRONG_CONTRACT, + "Contract hash does not match order"); + } + } + + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("fulfillment_url", + &god->fulfillment_url), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (god->contract_terms, + spec, + NULL, NULL)) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR, + "Merchant database error (contract terms corrupted)"); + } + } + } /* end of first-time initialization / sanity checks */ + + if ( (NULL != god->session_id) && + (NULL != god->fulfillment_url) ) + { + /* Check if paid within a session. */ + char *already_paid_order_id = NULL; + + qs = db->find_session_info (db->cls, + &already_paid_order_id, + god->session_id, + god->fulfillment_url, + &mi->pubkey); + if (qs < 0) { - GNUNET_break (0); - json_decref (contract_terms); - GNUNET_free (prd); + /* single, read-only SQL statements should never cause + serialization problems */ + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + /* Always report on hard error as well to enable diagnostics */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_INTERNAL_LOGIC_ERROR, - "Could not hash contract terms"); + TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR, + "db error fetching pay session info"); } - json_decref (contract_terms); - prd->hc.cc = &cleanup_prd; - prd->merchant = mi; - prd->ec = TALER_EC_NONE; - prd->connection = connection; - *connection_cls = prd; - - for (unsigned int i = 0; i<MAX_RETRIES; i++) + else if (0 == qs) { - qs = db->get_refunds_from_contract_terms_hash (db->cls, - &mi->pubkey, - &prd->h_contract_terms, - &process_refunds_cb, - prd); - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; + ret = send_pay_request (god, + already_paid_order_id); + GNUNET_free_non_null (already_paid_order_id); + return ret; } + GNUNET_break (1 == qs); + GNUNET_break (0 == strcmp (order_id, + already_paid_order_id)); + GNUNET_free_non_null (already_paid_order_id); + } + else + { + /* Check if paid regardless of session. */ + json_t *xcontract_terms = NULL; + + qs = db->find_paid_contract_terms_from_hash (db->cls, + &xcontract_terms, + &god->h_contract_terms, + &mi->pubkey); if (0 > qs) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Database hard error on refunds_from_contract_terms_hash lookup: %s\n", - GNUNET_h2s (&prd->h_contract_terms)); + /* Always report on hard error as well to enable diagnostics */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_REFUND_LOOKUP_DB_ERROR, - "Failed to lookup refunds for contract"); + TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR, + "Merchant database error"); + } + if (0 == qs) + { + return send_pay_request (god, + NULL); } + GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); + GNUNET_assert (NULL != xcontract_terms); + json_decref (xcontract_terms); + } - /* Now launch exchange interactions, unless we already have the - response in the database! */ - for (struct CoinRefund *cr = prd->cr_head; - NULL != cr; - cr = cr->next) + + for (unsigned int i = 0; i<MAX_RETRIES; i++) + { + qs = db->get_refunds_from_contract_terms_hash (db->cls, + hc->instance->settings.id, + &god->h_contract_terms, + &process_refunds_cb, + god); + if (GNUNET_DB_STATUS_SOFT_ERROR != qs) + break; + } + if (0 > qs) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_REFUND_LOOKUP_DB_ERROR, + "Failed to lookup refunds for contract"); + } + + /* Now launch exchange interactions, unless we already have the + response in the database! */ + for (struct CoinRefund *cr = god->cr_head; + NULL != cr; + cr = cr->next) + { + enum GNUNET_DB_QueryStatus qs; + + qs = db->get_refund_proof (db->cls, + &cr->god->merchant->pubkey, + &cr->god->h_contract_terms, + &cr->coin_pub, + cr->rtransaction_id, + &cr->exchange_pub, + &cr->exchange_sig); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { - enum GNUNET_DB_QueryStatus qs; - - qs = db->get_refund_proof (db->cls, - &cr->prd->merchant->pubkey, - &cr->prd->h_contract_terms, - &cr->coin_pub, - cr->rtransaction_id, - &cr->exchange_pub, - &cr->exchange_sig); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - /* We need to talk to the exchange */ - cr->fo = TMH_EXCHANGES_find_exchange (cr->exchange_url, - NULL, - GNUNET_NO, - &exchange_found_cb, - cr); - } + /* We need to talk to the exchange */ + cr->fo = TMH_EXCHANGES_find_exchange (cr->exchange_url, + NULL, + GNUNET_NO, + &exchange_found_cb, + cr); } } + if ( (god->awaiting_refund) && + ( (! god->refunded) || + (1 != TALER_amount_cmp (&god->refund_amount, + &god->min_refund)) ) ) + { + /* Client is waiting for a refund larger than what we have, suspend + until timeout */ + struct GNUNET_TIME_Relative remaining; + + remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout); + if (0 != remaining.rel_value_us) + { + /* yes, indeed suspend */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting refund exceeding %s\n", + TALER_amount2s (&god->min_refund)); + suspend_god (god); + return MHD_YES; + } + } + + /* Check if there are still exchange operations pending */ - if (GNUNET_YES == prd_pending (prd)) + if (GNUNET_YES == god_pending (god)) { - if (! prd->suspended) + if (! god->suspended) { - prd->suspended = GNUNET_YES; + god->suspended = GNUNET_YES; MHD_suspend_connection (connection); - GNUNET_CONTAINER_DLL_insert (prd_head, - prd_tail, - prd); + GNUNET_CONTAINER_DLL_insert (god_head, + god_tail, + god); } return MHD_YES; /* we're still talking to the exchange */ } /* All operations done, build final response */ - if (NULL == prd->cr_head) - { - /* There ARE no refunds scheduled, bitch */ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_REFUND_LOOKUP_NO_REFUND, - "This contract is not currently eligible for refunds"); - } - { json_t *ra; ra = json_array (); GNUNET_assert (NULL != ra); - for (struct CoinRefund *cr = prd->cr_head; + for (struct CoinRefund *cr = god->cr_head; NULL != cr; cr = cr->next) { @@ -637,6 +961,23 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh, GNUNET_JSON_from_data_auto (&cr->exchange_sig) ))); } + + if (god->refunded) + return TALER_MHD_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:b, s:b, s:o}", + "paid", 1, + "refunded", god->refunded, + "refund_amount", + TALER_JSON_from_amount ( + &god->refund_amount)); + return TALER_MHD_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:b, s:b }", + "paid", 1, + "refunded", 0); + + return TALER_MHD_reply_json_pack ( connection, MHD_HTTP_OK, @@ -646,9 +987,9 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh, "merchant_pub", GNUNET_JSON_from_data_auto (&mi->pubkey), "h_contract_terms", - GNUNET_JSON_from_data_auto (&prd->h_contract_terms)); + GNUNET_JSON_from_data_auto (&god->h_contract_terms)); } } -/* end of taler-merchant-httpd_refund_lookup.c */ +/* end of taler-merchant-httpd_get-orders-ID.c */ |