diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-06-03 19:15:54 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-06-03 19:15:54 +0200 |
commit | 5e5fa7449c0343d644505d486cda2a2b10ed0b51 (patch) | |
tree | eb0aa5dae44436e5a949a760c76421c7c07552b6 /src/backend/taler-merchant-httpd_private-get-orders-ID.c | |
parent | 2edb7ff575ec61fc570e80b6c492878c3bb97f70 (diff) | |
download | merchant-5e5fa7449c0343d644505d486cda2a2b10ed0b51.tar.gz merchant-5e5fa7449c0343d644505d486cda2a2b10ed0b51.tar.bz2 merchant-5e5fa7449c0343d644505d486cda2a2b10ed0b51.zip |
first high-level hack job at GET /orders/ID -- certainly FTBFS still
Diffstat (limited to 'src/backend/taler-merchant-httpd_private-get-orders-ID.c')
-rw-r--r-- | src/backend/taler-merchant-httpd_private-get-orders-ID.c | 691 |
1 files changed, 261 insertions, 430 deletions
diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c index bb5384d1..e6d1825b 100644 --- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2017, 2019 Taler Systems SA + (C) 2017, 2019, 2020 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,37 +14,22 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** - * @file backend/taler-merchant-httpd_check-payment.c - * @brief implementation of /check-payment handler + * @file backend/taler-merchant-httpd_private-get-orders-ID.c + * @brief implementation of GET /private/orders/ID handler * @author Florian Dold * @author Christian Grothoff */ #include "platform.h" -#include <string.h> -#include <microhttpd.h> -#include <jansson.h> +#include "taler-merchant-httpd_private-get-orders-ID.h" #include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include "taler-merchant-httpd.h" #include "taler-merchant-httpd_mhd.h" -#include "taler-merchant-httpd_exchanges.h" -#include "taler-merchant-httpd_check-payment.h" - -/** - * Maximum number of retries for database operations. - */ -#define MAX_RETRIES 5 /** * Data structure we keep for a check payment request. */ -struct CheckPaymentRequestContext +struct GetOrderRequestContext { - /** - * 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 @@ -55,22 +40,7 @@ struct CheckPaymentRequestContext /** * 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; + struct TMH_HandlerContext *hc; /** * session of the client @@ -78,8 +48,8 @@ struct CheckPaymentRequestContext const char *session_id; /** - * fulfillment URL of the contract (valid as long as - * @e contract_terms is valid). + * Fulfillment URL extracted from the contract. For repurchase detection. + * Only valid as long as @e contract_terms is valid! */ const char *fulfillment_url; @@ -90,47 +60,44 @@ struct CheckPaymentRequestContext json_t *contract_terms; /** - * Hash of @e contract_terms, set only once @e contract_terms - * is available. + * Serial ID of the order. */ - struct GNUNET_HashCode h_contract_terms; + uint64_t order_serial; /** * Total refunds granted for this payment. Only initialized - * if @e refunded is set to #GNUNET_YES. + * if @e refunded is set to true. */ struct TALER_Amount refund_amount; /** - * Set to #GNUNET_YES if this payment has been refunded and + * Set to true if this payment has been refunded and * @e refund_amount is initialized. */ - int refunded; + bool refunded; /** - * Initially #GNUNET_SYSERR. If we queued a response, set to the - * result code (i.e. #MHD_YES or #MHD_NO). + * Did the client request us to fetch the wire transfer status? + * If false, we may still return it if it is available. */ - int ret; + bool transfer_status_requested; }; /** - * Clean up the session state for a check payment request. + * Clean up the session state for a GET /private/order/ID request. * - * @param hc must be a `struct CheckPaymentRequestContext *` + * @param cls closure, must be a `struct GetOrderRequestContext *` */ static void -cprc_cleanup (struct TM_HandlerContext *hc) +gorc_cleanup (void *cls) { - struct CheckPaymentRequestContext *cprc = (struct - CheckPaymentRequestContext *) hc; + struct GetOrderRequestContext *gorc = cls; - if (NULL != cprc->contract_terms) - json_decref (cprc->contract_terms); - GNUNET_free_non_null (cprc->final_contract_url); - GNUNET_free (cprc); + if (NULL != gorc->contract_terms) + json_decref (gorc->contract_terms); + GNUNET_free (gorc); } @@ -155,437 +122,301 @@ process_refunds_cb (void *cls, const struct TALER_Amount *refund_amount, const struct TALER_Amount *refund_fee) { - struct CheckPaymentRequestContext *cprc = cls; + struct GetOrderRequestContext *gorc = cls; - if (cprc->refunded) + // FIXME: probably should do more here and compute + // an array with the details on the refunds with the reasons; + // FIXME: spec does NOT mandate that yet! + if (gorc->refunded) { GNUNET_assert (0 <= - TALER_amount_add (&cprc->refund_amount, - &cprc->refund_amount, + TALER_amount_add (&gorc->refund_amount, + &gorc->refund_amount, refund_amount)); return; } - cprc->refund_amount = *refund_amount; - cprc->refunded = GNUNET_YES; + gorc->refund_amount = *refund_amount; + gorc->refunded = true; } /** - * The client did not yet pay, send it the payment request. + * Manages a GET /private/orders/ID call, checking the status of a payment and + * refunds and, if necessary, constructing the URL for a payment redirect URL. * - * @param cprc check pay request context - * @return #MHD_YES on success + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code */ -static int -send_pay_request (struct CheckPaymentRequestContext *cprc) +MHD_RESULT +TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { - int ret; - char *already_paid_order_id = NULL; - char *taler_pay_uri; - struct GNUNET_TIME_Relative remaining; + struct GetOrderRequestContext *gorc = hc->ctx; + enum GNUNET_DB_QueryStatus qs; - remaining = GNUNET_TIME_absolute_get_remaining (cprc->sc.long_poll_timeout); - if (0 != remaining.rel_value_us) + if (NULL == gorc) { - /* long polling: do not queue a response, suspend connection instead */ - TMH_compute_pay_key (cprc->order_id, - &cprc->mi->pubkey, - &cprc->sc.key); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Suspending /check-payment on key %s\n", - GNUNET_h2s (&cprc->sc.key)); - TMH_long_poll_suspend (&cprc->sc, - NULL); - return MHD_YES; - } + /* First time here, parse request and check order is known */ + gorc = GNUNET_new (struct GetOrderRequestContext); + hc->cc = &gorc_cleanup; + hc->ctx = gorc; + gorc->sc.con = connection; + gorc->hc = hc; + GNUNET_assert (NULL != hc->infix); + gorc->session_id = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "session_id"); + { + const char *long_poll_timeout_s; + + long_poll_timeout_s = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "timeout_ms"); + if (NULL != long_poll_timeout_s) + { + unsigned long long timeout; + + if (1 != sscanf (long_poll_timeout_s, + "%llu", + &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"); + } + gorc->sc.long_poll_timeout + = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_MILLISECONDS, + timeout)); + } + else + { + gorc->sc.long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS; + } + } - /* Check if resource_id has been paid for in the same session - * with another order_id. - */ - 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, - cprc->session_id, - cprc->fulfillment_url, - &cprc->mi->pubkey); - if (qs < 0) + { + const char *transfer_s; + + transfer_s = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "transfer"); + if ( (NULL != transfer_s) && + (0 == strcasecmp (transfer_s, + "yes")) ) + gorc->transfer_status_requested = true; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Starting GET /private/orders/%s processing with timeout %s\n", + hc->infix, + GNUNET_STRINGS_absolute_time_to_string ( + gorc->sc.long_poll_timeout)); + + db->preflight (db->cls); + qs = db->lookup_contract_terms (db->cls, + hc->instance.settings->id, + hc->infix, + &gorc->contract_terms, + &gorc->order_serial); + 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 TALER_MHD_reply_with_error (cprc->sc.con, + return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR, - "db error fetching pay session info"); + TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR, + "db error fetching contract terms"); } - } - taler_pay_uri = TMH_make_taler_pay_uri (cprc->sc.con, - cprc->order_id, - cprc->session_id, - cprc->mi->id); - ret = TALER_MHD_reply_json_pack (cprc->sc.con, - MHD_HTTP_OK, - "{s:s, s:s, s:b, s:s?}", - "taler_pay_uri", taler_pay_uri, - "contract_url", cprc->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; -} + // FIXME: handle qs! + // => mainly: if 0 return 404! -/** - * 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 - = TALER_MHD_reply_with_error (cprc->sc.con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - 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 - = TALER_MHD_reply_with_error (cprc->sc.con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - 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 cprc session state - * @return status code to return to MHD for @a connection - */ -static int -check_order_and_request_payment (struct CheckPaymentRequestContext *cprc) -{ - enum GNUNET_DB_QueryStatus qs; - - 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, - &cprc->contract_terms, - cprc->order_id, - &cprc->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 TALER_MHD_reply_with_error (cprc->sc.con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR, - "db error fetching order"); - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - return TALER_MHD_reply_with_error (cprc->sc.con, - MHD_HTTP_NOT_FOUND, - TALER_EC_CHECK_PAYMENT_ORDER_ID_UNKNOWN, - "unknown order_id"); + /* extract the fulfillment URL from the contract terms! */ + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("fulfillment_url", + &gorc->fulfillment_url), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (gorc->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)"); + } + } } - if (GNUNET_OK != - parse_contract_terms (cprc)) - return cprc->ret; - /* Offer was not picked up yet, but we ensured that it exists */ - return send_pay_request (cprc); -} - + GNUNET_assert (NULL != gorc->contract_terms); -/** - * Manages a /check-payment call, checking the status - * of a payment and, if necessary, constructing the URL - * for a payment redirect URL. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL - * @return MHD result code - */ -MHD_RESULT -MH_handler_check_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 CheckPaymentRequestContext *cprc = *connection_cls; - enum GNUNET_DB_QueryStatus qs; - MHD_RESULT ret; - - if (NULL == cprc) { - const char *long_poll_timeout_s; - - /* 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->sc.con = 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) + bool paid; + bool wired; + + db->preflight (db->cls); + qs = db->lookup_payment_status (db->cls, + gorc->order_serial, + gorc->session_id, + &paid, + &wired); + if (0 >= qs) { - /* order_id is required but missing */ - GNUNET_break_op (0); + /* single, read-only SQL statements should never cause + serialization problems, and the entry should exist as per above */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - 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); + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_CHECK_PAYMENT_DB_FETCH_PAYMENT_STATUS, + "DB error fetching payment status"); } - cprc->session_id = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "session_id"); - long_poll_timeout_s = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "timeout"); - if (NULL != long_poll_timeout_s) + if (! paid) { - unsigned int timeout; - - if (1 != sscanf (long_poll_timeout_s, - "%u", - &timeout)) + char *already_paid_order_id; + + qs = db->lookup_repayment_status (db->cls, + hc->instance->settings.id, + hc->infix, + gorc->session_id, + gorc->fulfillment_url, + &already_paid_order_id); + if (0 > qs) { - GNUNET_break_op (0); + /* single, read-only SQL statements should never cause + serialization problems, and the entry should exist as per above */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_PARAMETER_MALFORMED, - "timeout must be non-negative number"); + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_CHECK_PAYMENT_DB_FETCH_PAYMENT_STATUS, + "DB error fetching payment status"); + } + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + { + /* User did pay for this order, but under a different session; ask wallet + to switch order ID */ + char *taler_pay_uri; + MHD_RESULT ret; + + taler_pay_uri = TMH_make_taler_pay_uri (connection, + hc->infix, + gorc->session_id, + hc->instance.settings->id); + ret = TALER_MHD_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:s, s:b, s:s}", + "taler_pay_uri", + taler_pay_uri, + "paid", + false, + "already_paid_order_id", + already_paid_order_id); + GNUNET_free (taler_pay_uri); + GNUNET_free (already_paid_order_id); + return ret; } - cprc->sc.long_poll_timeout - = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_SECONDS, - timeout)); } - else + + if (paid && + (! wired) && + gorc->transfer_status_requested) { - cprc->sc.long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS; + // FIXME: suspend connection, go to exhange to ask for the + // wire transfer status, and resume once we have it! + // (make sure to set gorc->transfer_status_requested to FALSE + // while we are at it!) + return MHD_YES; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting /check-payment processing with timeout %s\n", - GNUNET_STRINGS_absolute_time_to_string ( - cprc->sc.long_poll_timeout)); - } - if (NULL != cprc->contract_terms) - { - json_decref (cprc->contract_terms); - cprc->contract_terms = NULL; - } - 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 TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - 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 */ - return check_order_and_request_payment (cprc); - } - GNUNET_assert (NULL != cprc->contract_terms); - - if (GNUNET_OK != - parse_contract_terms (cprc)) - return cprc->ret; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Checkig payment status for order `%s' with contract %s\n", - cprc->order_id, - GNUNET_h2s (&cprc->h_contract_terms)); - GNUNET_assert (NULL != cprc->contract_terms); - - /* Check if the order has been paid for. */ - 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, - cprc->session_id, - cprc->fulfillment_url, - &mi->pubkey); - if (qs < 0) + if ( (! paid) && + (0 != gorc.sc.long_poll_timeout.abs_value_us) ) { - /* single, read-only SQL statements should never cause - serialization problems */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - /* Always report on hard error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR, - "db error fetching pay session info"); + // FIXME: suspend connection, wait for payment! + return MHD_YES; } - else if (0 == qs) + + if (! paid) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Order `%s' was not yet paid for session `%s'\n", - cprc->order_id, - cprc->session_id); - ret = send_pay_request (cprc); - GNUNET_free_non_null (already_paid_order_id); + /* User never paid for this order */ + char *taler_pay_uri; + MHD_RESULT ret; + + taler_pay_uri = TMH_make_taler_pay_uri (connection, + hc->infix, + gorc->session_id, + hc->instance.settings->id); + ret = TALER_MHD_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:s, s:b}", + "taler_pay_uri", + taler_pay_uri, + "paid", + false); + GNUNET_free (taler_pay_uri); return ret; } - GNUNET_break (1 == qs); - 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, - &cprc->h_contract_terms, - &mi->pubkey); + /* Here we know the user DID pay, compute refunds... */ + + /* Accumulate refunds, if any. */ + qs = db->select_refunds_by_order (db->cls, + gorc->order_serial, + &process_refunds_cb, + gorc); if (0 > qs) { - /* Always report on hard error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); + GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR, "Merchant database error"); } - if (0 == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Order `%s' (contract `%s') was not yet paid\n", - cprc->order_id, - GNUNET_h2s (&cprc->h_contract_terms)); - return send_pay_request (cprc); - } - GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); - GNUNET_assert (NULL != xcontract_terms); - json_decref (xcontract_terms); - } + } /* end of scope of 'paid/wired' */ - /* Accumulate refunds, if any. */ - for (unsigned int i = 0; i<MAX_RETRIES; i++) + /* Generate final reply, including wire details if we have them */ { - qs = db->get_refunds_from_contract_terms_hash (db->cls, - &mi->pubkey, - &cprc->h_contract_terms, - &process_refunds_cb, - cprc); - 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 (&cprc->h_contract_terms)); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR, - "Merchant database error"); - } - if (cprc->refunded) + json_t *wire_details = NULL; + + qs = db->select_wire_details_by_order (db->cls, + gorc->order_serial, + &process_wire_details, + &wire_details); + if (0 > qs) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR, + "Merchant database error"); + } return TALER_MHD_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, + "{s:O, s:b, s:b, s:o?, s:o?}", + "contract_terms", + gorc->contract_terms, + "paid", + true, + "refunded", + gorc->refunded, "refund_amount", - TALER_JSON_from_amount ( - &cprc->refund_amount)); - return TALER_MHD_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:O, s:b, s:b }", - "contract_terms", cprc->contract_terms, - "paid", 1, - "refunded", 0); + (gorc->refunded) + ? TALER_JSON_from_amount ( + &gorc->refund_amount) + : NULL, + "wire_details", + wire_details); + } } |