diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-06-04 21:48:34 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-06-04 21:48:34 +0200 |
commit | ea5d4f680dd1394889e61adbb805d6d8decfe9e1 (patch) | |
tree | 857b85825def1de596fbb797c4729058b0535eab /src/backend/taler-merchant-httpd_private-get-orders-ID.c | |
parent | 96affdc638c66e04adccbf1cf3ff427cd37f0ada (diff) | |
download | merchant-ea5d4f680dd1394889e61adbb805d6d8decfe9e1.tar.gz merchant-ea5d4f680dd1394889e61adbb805d6d8decfe9e1.tar.bz2 merchant-ea5d4f680dd1394889e61adbb805d6d8decfe9e1.zip |
more work on private-get-orders-ID
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 | 399 |
1 files changed, 255 insertions, 144 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 327ccf7f..d4d26608 100644 --- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c @@ -158,11 +158,33 @@ struct GetOrderRequestContext json_t *wire_details; /** + * Details about refunds, NULL if there are no refunds. + */ + json_t *refund_details; + + /** * Hash over the @e contract_terms. */ struct GNUNET_HashCode h_contract_terms; /** + * Total amount the exchange deposited into our bank account + * (confirmed or unconfirmed), excluding fees. + */ + struct TALER_Amount deposits_total; + + /** + * Total value of the coins that the exchange deposited into our bank + * account (confirmed or unconfirmed), including deposit fees. + */ + struct TALER_Amount value_total; + + /** + * Total we were to be paid under the contract, excluding refunds. + */ + struct TALER_Amount contract_amount; + + /** * Serial ID of the order. */ uint64_t order_serial; @@ -309,7 +331,7 @@ deposit_get_cb (void *cls, GNUNET_free (tq); return; } - else + else if (MHD_HTTP_OK == hr->http_status) { enum GNUNET_DB_QueryStatus qs; @@ -324,6 +346,35 @@ deposit_get_cb (void *cls, GNUNET_free (tq); return; } + /* Compute total amount *wired* */ + if (0 > + TALER_amount_add (&gorc->deposits_total, + &gorc->deposits_total, + &dd->coin_contribution)) + { + gorc_resume (gorc, + 42, // FIXME: EC + NULL); + GNUNET_free (tq); + return; + } + /* Compute total amount including deposit fees (so how much + of the total deposit was properly paid by the exchange) */ + if (0 > + TALER_amount_add (&gorc->value_total, + &gorc->value_total, + &tq->coin_value)) + { + gorc_resume (gorc, + 42, // FIXME: EC + NULL); + GNUNET_free (tq); + return; + } + } + else + { + /* got a 'preliminary' reply from the exchange, simply skip */ } GNUNET_free (tq); if (NULL != gorc->tq_head) @@ -448,6 +499,10 @@ gorc_cleanup (void *cls) if (NULL != gorc->contract_terms) json_decref (gorc->contract_terms); + if (NULL != gorc->wire_details) + json_decref (gorc->wire_details); + if (NULL != gorc->refund_details) + json_decref (gorc->refund_details); GNUNET_assert (NULL == gorc->tt); GNUNET_assert (NULL == gorc->fo); GNUNET_free (gorc); @@ -477,9 +532,18 @@ process_refunds_cb (void *cls, { struct GetOrderRequestContext *gorc = cls; - // 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 (NULL == gorc->refund_details) + { + gorc->refund_details = json_array_new (); + GNUNET_assert (NULL != gorc->refund_details); + } + GNUNET_assert (0 == + json_array_append_new (json_pack ("{s:s}", + "reason", + reason))); + // FIXME: properly setup json_pack above + // with the details on the refund + // FIXME: update spec accordingly! if (gorc->refunded) { GNUNET_assert (0 <= @@ -498,22 +562,29 @@ process_refunds_cb (void *cls, * the response. * * @param cls a `struct GetOrderRequestContext` + * @param wtid wire transfer subject of the wire transfer for the coin + * @param deposit_value contribution of the coin to the total wire transfer value + * @param deposit_fee deposit fee charged by the exchange for the coin + * @param transfer_confirmed did the merchant confirm that a wire transfer with + * @a wtid over the total amount happened? */ static void -process_wire_details (void *cls, - ...) +process_transfer_details (void *cls, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *deposit_value, + const struct TALER_Amount *deposit_fee, + bool transfer_confirmed) { struct GetOrderRequestContext *gorc = cls; json_t *wire_details = gorc->wire_details; - GNUNET_assert (0 == json_array_append_new (wire_details, json_pack ("{s:s, s:b}", "blah", - "stuff", "FIXME", - false))); + "confirmed", + transfer_confirmed))); } @@ -533,6 +604,8 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, { struct GetOrderRequestContext *gorc = hc->ctx; enum GNUNET_DB_QueryStatus qs; + bool paid; + bool wired; if (NULL == gorc) { @@ -621,11 +694,13 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, "Did not find order in DB"); } - /* extract the fulfillment URL from the contract terms! */ + /* extract the fulfillment URL and total amount from the contract terms! */ { struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("fulfillment_url", &gorc->fulfillment_url), + TALER_JSON_spec_amount ("amount", + &gorc->contract_amount), GNUNET_JSON_spec_end () }; @@ -641,6 +716,7 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR, "Merchant database error (contract terms corrupted)"); } + // FIXME: sanity check on gorc->contract_amount.currency! } if (GNUNET_OK != TALER_JSON_hash (gorc->contract_terms, @@ -659,17 +735,32 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, GNUNET_assert (NULL != gorc->contract_terms); + TMH_db->preflight (TMH_db->cls); + qs = TMH_db->lookup_payment_status (TMH_db->cls, + gorc->order_serial, + gorc->session_id, + &paid, + &wired); + if (0 >= qs) + { + /* 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_INTERNAL_SERVER_ERROR, + TALER_EC_CHECK_PAYMENT_DB_FETCH_PAYMENT_STATUS, + "DB error fetching payment status"); + } + if (! paid) { - bool paid; - bool wired; + char *already_paid_order_id; - TMH_db->preflight (TMH_db->cls); - qs = TMH_db->lookup_payment_status (TMH_db->cls, - gorc->order_serial, - gorc->session_id, - &paid, - &wired); - if (0 >= qs) + qs = TMH_db->lookup_order_by_fulfillment (TMH_db->cls, + hc->instance->settings.id, + gorc->fulfillment_url, + gorc->session_id, + &already_paid_order_id); + if (0 > qs) { /* single, read-only SQL statements should never cause serialization problems, and the entry should exist as per above */ @@ -679,90 +770,10 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_CHECK_PAYMENT_DB_FETCH_PAYMENT_STATUS, "DB error fetching payment status"); } - if (! paid) - { - char *already_paid_order_id; - - qs = TMH_db->lookup_order_by_fulfillment (TMH_db->cls, - hc->instance->settings.id, - gorc->fulfillment_url, - gorc->session_id, - &already_paid_order_id); - if (0 > qs) - { - /* 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_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; - } - } - - if (paid && - (! wired) && - gorc->transfer_status_requested) + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) { - /* suspend connection, wait for exchange to check wire transfer status there */ - gorc->transfer_status_requested = false; /* only try ONCE */ - TMH_db->lookup_deposits (TMH_db->cls, - gorc->order_serial, - &deposit_cb, - gorc); - if (NULL != gorc->tq) - { - GNUNET_CONTAINER_DLL_insert (gorc_head, - gorc_tail, - gorc); - gorc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT, - &exchange_timeout_cb, - gorc); - MHD_suspend_connection (connection); - return MHD_YES; - } - } - - if ( (! paid) && - (0 != gorc.sc.long_poll_timeout.abs_value_us) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Suspending GET /private/orders/%s\n", - hc->infix); - TMH_long_poll_suspend (hc->infix, - hc->instance, - &gorc->sc, - NULL); - return MHD_YES; - } - - if (! paid) - { - /* User never paid for this order */ + /* User did pay for this order, but under a different session; ask wallet + to switch order ID */ char *taler_pay_uri; MHD_RESULT ret; @@ -772,46 +783,111 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, hc->instance.settings->id); ret = TALER_MHD_reply_json_pack (connection, MHD_HTTP_OK, - "{s:s, s:b}", + "{s:s, s:b, s:s}", "taler_pay_uri", taler_pay_uri, "paid", - false); + false, + "already_paid_order_id", + already_paid_order_id); GNUNET_free (taler_pay_uri); + GNUNET_free (already_paid_order_id); return ret; } + } - /* Here we know the user DID pay, compute refunds... */ - - /* Accumulate refunds, if any. */ + if (paid && + (! wired) && + gorc->transfer_status_requested) + { + /* suspend connection, wait for exchange to check wire transfer status there */ + gorc->transfer_status_requested = false; /* only try ONCE */ + TMH_db->lookup_deposits (TMH_db->cls, + gorc->order_serial, + &deposit_cb, + gorc); + if (NULL != gorc->tq) { - - qs = TMH_db->lookup_refunds (TMH_db->cls, - hc->instance.settings->id, - &h_contract_terms, - &process_refunds_cb, + GNUNET_CONTAINER_DLL_insert (gorc_head, + gorc_tail, gorc); + gorc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT, + &exchange_timeout_cb, + gorc); + MHD_suspend_connection (connection); + return MHD_YES; } - if (0 > qs) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_ORDERS_GET_DB_FETCH_TRANSACTION_ERROR, - "Merchant database error"); - } - } /* end of scope of 'paid/wired' */ + } + + if ( (! paid) && + (0 != gorc.sc.long_poll_timeout.abs_value_us) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Suspending GET /private/orders/%s\n", + hc->infix); + TMH_long_poll_suspend (hc->infix, + hc->instance, + &gorc->sc, + NULL); + return MHD_YES; + } + + if (! paid) + { + /* 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; + } + + /* Here we know the user DID pay, compute refunds... */ + + /* Accumulate refunds, if any. */ + { + + qs = TMH_db->lookup_refunds (TMH_db->cls, + hc->instance.settings->id, + &h_contract_terms, + &process_refunds_cb, + gorc); + } + if (0 > qs) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_ORDERS_GET_DB_FETCH_TRANSACTION_ERROR, + "Merchant database error"); + } /* Generate final reply, including wire details if we have them */ { + MHD_RESULT ret; + gorc->wire_details = json_array_new (); GNUNET_assert (NULL != gorc->wire_details); -#if FIXME - qs = TMH_db->select_wire_details_by_order (TMH_db->cls, - gorc->order_serial, - &process_wire_details, - gorc); -#endif + // FIXME: assert on return codes: + TALER_amount_get_zero (&gorc->value_total, + TMH_currency); + TALER_amount_get_zero (&gorc->deposits_total, + TMH_currency); + qs = TMH_db->lookup_transfer_details_by_order (TMH_db->cls, + gorc->order_serial, + &process_transfer_details, + gorc); if (0 > qs) { GNUNET_break (0); @@ -820,22 +896,57 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR, "Merchant database error"); } - // FIXME: also return gorc->ec/exchange_(hc,ec) codes! - return TALER_MHD_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:O, s:b, s:b, s:o?, s:o?}", - "contract_terms", - gorc->contract_terms, - "paid", - true, - "refunded", - gorc->refunded, - "refund_amount", - (gorc->refunded) - ? TALER_JSON_from_amount ( - &gorc->refund_amount) - : NULL, - "wire_details", - gorc->wire_details); + + if (! wired) + { + /* we believe(d) the wire transfer did not happen yet, check if maybe + in light of new evidence it did */ + struct TALER_Amount expect_total; + + if (0 > + TALER_amount_subtract (&expect_total, + &gorc->contract_amount, + &gorc->refund_amount)) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + 42, // FIXME: EC + "Inconsistent contract terms in DB"); + } + if (0 >= // FIXME: right cmp? + TALER_amount_cmp (&expect_total, + &gorc->value_total)) + { + wired = true; + qs = TMH_db->mark_order_wired (TMH_db->cls, + gorc->order_serial); + GNUNET_break (qs >= 0); /* just warn if transaction failed */ + } + } + + // FIXME: also return gorc->ec/exchange_(hc,ec) codes + // and the gorc->value_total/deposits_total/etc. + ret = TALER_MHD_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:O, s:b, s:b, s:o?, s:o?, s:o}", + "contract_terms", + gorc->contract_terms, + "paid", + true, + "refunded", + gorc->refunded, + "refund_amount", + (gorc->refunded) + ? TALER_JSON_from_amount ( + &gorc->refund_amount) + : NULL, + "wire_details", + gorc->wire_details, + "refund_details", + gorc->refund_details); + gorc->wire_details = NULL; + gorc->refund_details = NULL; + return ret; } } |