summaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd_private-get-orders-ID.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-06-04 21:48:34 +0200
committerChristian Grothoff <christian@grothoff.org>2020-06-04 21:48:34 +0200
commitea5d4f680dd1394889e61adbb805d6d8decfe9e1 (patch)
tree857b85825def1de596fbb797c4729058b0535eab /src/backend/taler-merchant-httpd_private-get-orders-ID.c
parent96affdc638c66e04adccbf1cf3ff427cd37f0ada (diff)
downloadmerchant-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.c399
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;
}
}