summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-04-01 23:22:23 +0200
committerChristian Grothoff <christian@grothoff.org>2020-04-01 23:22:23 +0200
commit1782ab3c6dcfa854c303bdf751e82b5a600e2087 (patch)
tree979eae4c498e3d479e3fff76a03a01d90aa21ac5
parenteb494d2cafb6dff07f86dacf6859928c6d3398b3 (diff)
downloadmerchant-1782ab3c6dcfa854c303bdf751e82b5a600e2087.tar.gz
merchant-1782ab3c6dcfa854c303bdf751e82b5a600e2087.tar.bz2
merchant-1782ab3c6dcfa854c303bdf751e82b5a600e2087.zip
work on #6014, incomplete
-rw-r--r--src/backend/taler-merchant-httpd.c6
-rw-r--r--src/backend/taler-merchant-httpd_auditors.c24
-rw-r--r--src/backend/taler-merchant-httpd_auditors.h8
-rw-r--r--src/backend/taler-merchant-httpd_pay.c971
-rw-r--r--src/backend/taler-merchant-httpd_tip-reserve-helper.c2
-rw-r--r--src/lib/merchant_api_pay.c51
-rw-r--r--src/lib/test_merchant_api.c2
7 files changed, 561 insertions, 503 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index d9584346..46b60fd1 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -561,7 +561,7 @@ TMH_trigger_daemon ()
* @param daemon_handle HTTP server to prepare to run
*/
static struct GNUNET_SCHEDULER_Task *
-prepare_daemon ()
+prepare_daemon (void)
{
struct GNUNET_SCHEDULER_Task *ret;
fd_set rs;
@@ -1670,8 +1670,8 @@ run (void *cls,
MHD_OPTION_LISTEN_SOCKET, fh,
MHD_OPTION_NOTIFY_COMPLETED,
&handle_mhd_completion_callback, NULL,
- MHD_OPTION_CONNECTION_TIMEOUT, (unsigned
- int) 10 /* 10s */,
+ MHD_OPTION_CONNECTION_TIMEOUT,
+ (unsigned int) 10 /* 10s */,
MHD_OPTION_END);
if (NULL == mhd)
{
diff --git a/src/backend/taler-merchant-httpd_auditors.c b/src/backend/taler-merchant-httpd_auditors.c
index 69e613bf..852654f6 100644
--- a/src/backend/taler-merchant-httpd_auditors.c
+++ b/src/backend/taler-merchant-httpd_auditors.c
@@ -70,12 +70,16 @@ json_t *j_auditors;
* @param mh exchange issuing @a dk
* @param dk a denomination issued by @a mh
* @param exchange_trusted #GNUNET_YES if the exchange of @a dk is trusted by config
- * @return #GNUNET_OK if we accept this denomination // FIXME: should return TALER_EC instead!
+ * @param[out] hc HTTP status code to return (on error)
+ * @param[out] ec Taler error code to return (on error)
+ * @return #GNUNET_OK
*/
int
TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh,
const struct TALER_EXCHANGE_DenomPublicKey *dk,
- int exchange_trusted)
+ int exchange_trusted,
+ unsigned int *hc,
+ enum TALER_ErrorCode *ec)
{
const struct TALER_EXCHANGE_Keys *keys;
const struct TALER_EXCHANGE_AuditorInformation *ai;
@@ -84,16 +88,23 @@ TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh,
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Denomination key offered by client has expired for deposits\n");
+ *hc = MHD_HTTP_GONE;
+ *ec = TALER_EC_PAY_DENOMINATION_DEPOSIT_EXPIRED;
return GNUNET_SYSERR; /* expired */
}
if (GNUNET_YES == exchange_trusted)
+ {
+ *ec = TALER_EC_NONE;
+ *hc = MHD_HTTP_OK;
return GNUNET_OK;
+ }
keys = TALER_EXCHANGE_get_keys (mh);
if (NULL == keys)
{
/* this should never happen, keys should have been successfully
obtained before we even got into this function */
- GNUNET_break (0);
+ *ec = TALER_EC_PAY_EXCHANGE_HAS_NO_KEYS;
+ *hc = MHD_HTTP_FAILED_DEPENDENCY;
return GNUNET_SYSERR;
}
for (unsigned int i = 0; i<keys->num_auditors; i++)
@@ -108,16 +119,21 @@ TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh,
"Found supported auditor `%s' (%s)\n",
auditors[j].name,
TALER_B2S (&auditors[j].public_key));
-
}
for (unsigned int k = 0; k<ai->num_denom_keys; k++)
if (&keys->denom_keys[k] == dk)
+ {
+ *ec = TALER_EC_NONE;
+ *hc = MHD_HTTP_OK;
return GNUNET_OK;
+ }
}
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Denomination key %s offered by client not audited by any accepted auditor\n",
GNUNET_h2s (&dk->h_key));
+ *hc = MHD_HTTP_BAD_REQUEST;
+ *ec = TALER_EC_PAY_DENOMINATION_KEY_AUDITOR_FAILURE;
return GNUNET_NO;
}
diff --git a/src/backend/taler-merchant-httpd_auditors.h b/src/backend/taler-merchant-httpd_auditors.h
index e3ec292e..4a1a0b61 100644
--- a/src/backend/taler-merchant-httpd_auditors.h
+++ b/src/backend/taler-merchant-httpd_auditors.h
@@ -54,12 +54,16 @@ TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
* @param mh exchange issuing @a dk
* @param dk a denomination issued by @a mh
* @param exchange_trusted #GNUNET_YES if the exchange of @a dk is trusted by config
- * @return #GNUNET_OK if we accept this denomination
+ * @param[out] hc set to the HTTP status code to return
+ * @param[out] ec set to the Taler error code to return
+ * @return #GNUNET_OK on success
*/
int
TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh,
const struct TALER_EXCHANGE_DenomPublicKey *dk,
- int exchange_trusted);
+ int exchange_trusted,
+ unsigned int *hc,
+ enum TALER_ErrorCode *ec);
/**
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
index d2b7abf8..6719c9cd 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014-2017 GNUnet e.V. and INRIA
+ (C) 2014-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -141,8 +141,8 @@ struct PayContext
{
/**
- * This field MUST be first.
- * FIXME: Explain why!
+ * This field MUST be first for handle_mhd_completion_callback() to work
+ * when it treats this struct as a `struct TM_HandlerContext`.
*/
struct TM_HandlerContext hc;
@@ -219,6 +219,22 @@ struct PayContext
void *json_parse_context;
/**
+ * Optional session id given in @e root.
+ * NULL if not given.
+ */
+ char *session_id;
+
+ /**
+ * Transaction ID given in @e root.
+ */
+ char *order_id;
+
+ /**
+ * Fulfillment URL from @e contract_terms.
+ */
+ char *fulfillment_url;
+
+ /**
* Hashed proposal.
*/
struct GNUNET_HashCode h_contract_terms;
@@ -359,22 +375,6 @@ struct PayContext
*/
enum { PC_MODE_PAY, PC_MODE_ABORT_REFUND } mode;
- /**
- * Optional session id given in @e root.
- * NULL if not given.
- */
- char *session_id;
-
- /**
- * Transaction ID given in @e root.
- */
- char *order_id;
-
- /**
- * Fulfillment URL from @e contract_terms.
- */
- char *fulfillment_url;
-
};
@@ -390,6 +390,29 @@ static struct PayContext *pc_tail;
/**
+ * Abort all pending /deposit operations.
+ *
+ * @param pc pay context to abort
+ */
+static void
+abort_deposit (struct PayContext *pc)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Aborting pending /deposit operations\n");
+ for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ {
+ struct DepositConfirmation *dci = &pc->dc[i];
+
+ if (NULL != dci->dh)
+ {
+ TALER_EXCHANGE_deposit_cancel (dci->dh);
+ dci->dh = NULL;
+ }
+ }
+}
+
+
+/**
* Force all pay contexts to be resumed as we are about
* to shut down MHD.
*/
@@ -400,6 +423,12 @@ MH_force_pc_resume ()
NULL != pc;
pc = pc->next)
{
+ abort_deposit (pc);
+ if (NULL != pc->timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (pc->timeout_task);
+ pc->timeout_task = NULL;
+ }
if (GNUNET_YES == pc->suspended)
{
pc->suspended = GNUNET_SYSERR;
@@ -501,25 +530,23 @@ resume_pay_with_response (struct PayContext *pc,
/**
- * Abort all pending /deposit operations.
+ * Resume payment processing with an error.
*
- * @param pc pay context to abort
+ * @param pc operation to resume
+ * @param http_status http status code to return
+ * @param ec taler error code to return
+ * @param msg human readable error message
*/
static void
-abort_deposit (struct PayContext *pc)
+resume_pay_with_error (struct PayContext *pc,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const char *msg)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Aborting pending /deposit operations\n");
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
- {
- struct DepositConfirmation *dci = &pc->dc[i];
-
- if (NULL != dci->dh)
- {
- TALER_EXCHANGE_deposit_cancel (dci->dh);
- dci->dh = NULL;
- }
- }
+ resume_pay_with_response (pc,
+ http_status,
+ TALER_MHD_make_error (ec,
+ msg));
}
@@ -527,25 +554,36 @@ abort_deposit (struct PayContext *pc)
* Generate a response that indicates payment success.
*
* @param pc payment context
- * @return the mhd response
*/
-static struct MHD_Response *
-sign_success_response (struct PayContext *pc)
+static void
+generate_success_response (struct PayContext *pc)
{
json_t *refunds;
- enum TALER_ErrorCode ec;
- const char *errmsg;
struct GNUNET_CRYPTO_EddsaSignature sig;
- json_t *resp;
- struct MHD_Response *mret;
-
- refunds = TM_get_refund_json (pc->mi,
- &pc->h_contract_terms,
- &ec,
- &errmsg);
- if (NULL == refunds)
- return TALER_MHD_make_error (ec,
- errmsg);
+
+ /* Check for applicable refunds */
+ {
+ enum TALER_ErrorCode ec;
+ const char *errmsg;
+
+ refunds = TM_get_refund_json (pc->mi,
+ &pc->h_contract_terms,
+ &ec,
+ &errmsg);
+ /* We would get an EMPTY array back on success if there
+ are no refunds, but not NULL. So NULL is always an error. */
+ if (NULL == refunds)
+ {
+ resume_pay_with_error (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ ec,
+ errmsg);
+ return;
+ }
+ }
+
+ /* Sign on our end (as the payment did go through, even if it may
+ have been refunded already) */
{
struct PaymentResponsePS mr = {
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK),
@@ -558,41 +596,35 @@ sign_success_response (struct PayContext *pc)
&mr.purpose,
&sig));
}
- resp = json_pack ("{s:O, s:o, s:o, s:o}",
- "contract_terms",
- pc->contract_terms,
- "sig",
- GNUNET_JSON_from_data_auto (&sig),
- "h_contract_terms",
- GNUNET_JSON_from_data (&pc->h_contract_terms,
- sizeof (struct GNUNET_HashCode)),
- "refund_permissions",
- refunds);
-
- mret = TALER_MHD_make_json (resp);
- json_decref (resp);
- return mret;
-}
-
-/**
- * Resume payment processing with an error.
- *
- * @param pc operation to resume
- * @param http_status http status code to return
- * @param ec taler error code to return
- * @param msg human readable error message
- */
-static void
-resume_pay_with_error (struct PayContext *pc,
- unsigned int http_status,
- enum TALER_ErrorCode ec,
- const char *msg)
-{
- resume_pay_with_response (pc,
- http_status,
- TALER_MHD_make_error (ec,
- msg));
+ /* Build the response */
+ {
+ json_t *resp;
+
+ resp = json_pack ("{s:O, s:o, s:o, s:o}",
+ "contract_terms",
+ pc->contract_terms,
+ "sig",
+ GNUNET_JSON_from_data_auto (&sig),
+ "h_contract_terms",
+ GNUNET_JSON_from_data (&pc->h_contract_terms,
+ sizeof (struct GNUNET_HashCode)),
+ "refund_permissions",
+ refunds);
+ if (NULL == resp)
+ {
+ GNUNET_break (0);
+ resume_pay_with_error (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_JSON_ALLOCATION_FAILURE,
+ "could not build final response");
+ return;
+ }
+ resume_pay_with_response (pc,
+ MHD_HTTP_OK,
+ TALER_MHD_make_json (resp));
+ json_decref (resp);
+ }
}
@@ -612,15 +644,11 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
pc->timeout_task = NULL;
}
TALER_MHD_parse_post_cleanup_callback (pc->json_parse_context);
+ abort_deposit (pc);
for (unsigned int i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
- if (NULL != dc->dh)
- {
- TALER_EXCHANGE_deposit_cancel (dc->dh);
- dc->dh = NULL;
- }
if (NULL != dc->denom.rsa_public_key)
{
GNUNET_CRYPTO_rsa_public_key_free (dc->denom.rsa_public_key);
@@ -664,9 +692,10 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
* the contract.
*
* @param pc payment context to check
- * @return taler error code, #TALER_EC_NONE if amount is sufficient
+ * @return #GNUNET_OK if the payment is sufficient, #GNUNET_SYSERR if it is
+ * insufficient
*/
-static enum TALER_ErrorCode
+static int
check_payment_sufficient (struct PayContext *pc)
{
struct TALER_Amount acc_fee;
@@ -676,7 +705,14 @@ check_payment_sufficient (struct PayContext *pc)
struct TALER_Amount total_wire_fee;
if (0 == pc->coins_cnt)
- return TALER_EC_PAY_PAYMENT_INSUFFICIENT;
+ {
+ GNUNET_break_op (0);
+ resume_pay_with_error (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PAY_PAYMENT_INSUFFICIENT,
+ "insufficient funds (no coins!)");
+ return GNUNET_SYSERR;
+ }
acc_fee = pc->dc[0].deposit_fee;
total_wire_fee = pc->dc[0].wire_fee;
@@ -700,17 +736,23 @@ check_payment_sufficient (struct PayContext *pc)
&dc->amount_with_fee,
&acc_amount)) )
{
- GNUNET_break_op (0);
+ GNUNET_break (0);
/* Overflow in these amounts? Very strange. */
- return TALER_EC_PAY_AMOUNT_OVERFLOW;
+ resume_pay_with_error (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_PAY_AMOUNT_OVERFLOW,
+ "Overflow adding up amounts");
}
if (1 ==
TALER_amount_cmp (&dc->deposit_fee,
&dc->amount_with_fee))
{
GNUNET_break_op (0);
- /* fee higher than residual coin value, makes no sense. */
- return TALER_EC_PAY_FEES_EXCEED_PAYMENT;
+ resume_pay_with_error (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PAY_FEES_EXCEED_PAYMENT,
+ "Deposit fees exceed coin's contribution");
+ return GNUNET_SYSERR;
}
/* If exchange differs, add wire fee */
@@ -727,38 +769,48 @@ check_payment_sufficient (struct PayContext *pc)
if (GNUNET_YES == new_exchange)
{
if (GNUNET_OK !=
+ TALER_amount_cmp_currency (&total_wire_fee,
+ &dc->wire_fee))
+ {
+ GNUNET_break_op (0);
+ resume_pay_with_error (pc,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TALER_EC_PAY_WIRE_FEE_CURRENCY_MISMATCH,
+ "exchange wire in different currency");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
TALER_amount_add (&total_wire_fee,
&total_wire_fee,
&dc->wire_fee))
{
GNUNET_break_op (0);
- return TALER_EC_PAY_EXCHANGE_REJECTED;
+ resume_pay_with_error (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED,
+ "could not add exchange wire fee to total");
+ return GNUNET_SYSERR;
}
}
}
}
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Amount received from wallet: %s\n",
TALER_amount_to_string (&acc_amount));
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Deposit fee for all coins: %s\n",
TALER_amount_to_string (&acc_fee));
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Total wire fee: %s\n",
TALER_amount_to_string (&total_wire_fee));
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Max wire fee: %s\n",
TALER_amount_to_string (&pc->max_wire_fee));
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Deposit fee limit for merchant: %s\n",
TALER_amount_to_string (&pc->max_fee));
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Total refunded amount: %s\n",
TALER_amount_to_string (&pc->total_refunded));
@@ -769,12 +821,17 @@ check_payment_sufficient (struct PayContext *pc)
&pc->max_wire_fee))
{
GNUNET_break (0);
- return TALER_EC_PAY_WIRE_FEE_CURRENCY_MISSMATCH;
+ resume_pay_with_error (pc,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TALER_EC_PAY_WIRE_FEE_CURRENCY_MISMATCH,
+ "exchange wire does not match our currency");
+ return GNUNET_SYSERR;
}
- if (GNUNET_OK == TALER_amount_subtract (&wire_fee_delta,
- &total_wire_fee,
- &pc->max_wire_fee))
+ if (GNUNET_OK ==
+ TALER_amount_subtract (&wire_fee_delta,
+ &total_wire_fee,
+ &pc->max_wire_fee))
{
/* Actual wire fee is indeed higher than our maximum,
compute how much the customer is expected to cover! */
@@ -786,97 +843,102 @@ check_payment_sufficient (struct PayContext *pc)
{
/* Wire fee threshold is still above the wire fee amount.
Customer is not going to contribute on this. */
- GNUNET_assert
- (GNUNET_OK == TALER_amount_get_zero
- (total_wire_fee.currency,
- &wire_fee_customer_contribution));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (total_wire_fee.currency,
+ &wire_fee_customer_contribution));
}
- /**
- * Deposit fees of *all* the coins are higher than
- * the fixed limit that the merchant is willing to
- * pay. The fees difference must be paid by the customer.
- */if (-1 == TALER_amount_cmp (&pc->max_fee,
+ if (-1 == TALER_amount_cmp (&pc->max_fee,
&acc_fee))
{
+ /**
+ * Deposit fees of *all* the different exchanges of all the coins are
+ * higher than the fixed limit that the merchant is willing to pay. The
+ * fees difference must be paid by the customer.
+ *///
struct TALER_Amount excess_fee;
struct TALER_Amount total_needed;
/* compute fee amount to be covered by customer */
- GNUNET_assert
- (GNUNET_OK == TALER_amount_subtract (&excess_fee,
- &acc_fee,
- &pc->max_fee));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_subtract (&excess_fee,
+ &acc_fee,
+ &pc->max_fee));
/* add that to the total */
- if (GNUNET_OK != TALER_amount_add (&total_needed,
- &excess_fee,
- &pc->amount))
+ if (GNUNET_OK !=
+ TALER_amount_add (&total_needed,
+ &excess_fee,
+ &pc->amount))
{
GNUNET_break (0);
- return TALER_EC_PAY_AMOUNT_OVERFLOW;
+ resume_pay_with_error (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PAY_AMOUNT_OVERFLOW,
+ "Overflow adding up amounts");
+ return GNUNET_SYSERR;
}
/* add wire fee contribution to the total */
- GNUNET_assert
- (GNUNET_OK == TALER_amount_add
- (&total_needed,
- &total_needed,
- &wire_fee_customer_contribution));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_add (&total_needed,
+ &total_needed,
+ &wire_fee_customer_contribution));
/* check if total payment sufficies */
if (-1 == TALER_amount_cmp (&acc_amount,
&total_needed))
{
GNUNET_break_op (0);
- return TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES;
+ resume_pay_with_error (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES,
+ "insufficient funds (after including wire fee share to be covered by customer)");
+ return GNUNET_SYSERR;
}
+ /* We're actually OK, the customer did pay their share of the wire fees */
}
-
- /**
- * The deposit fees of all the coins are below the
- * limit that the merchant is willing to pay, for some X
- * delta. Since a fraction of the wire fee must be paid
- * by the customer, this block will take from X such a
- * fraction, and check that the remaining paid amount is
- * enough to pay both the remaining wire fee customer's
- * fraction AND the price of the contract itself.
- */else
+ else
{
+ /**
+ * The deposit fees of all the exchanges of all the coins are below the
+ * limit that the merchant is willing to pay, for some X delta. Since a
+ * fraction of the wire fee must be paid by the customer, this block will
+ * take from X such a fraction, and check that the remaining paid amount
+ * is enough to pay both the remaining wire fee customer's fraction AND
+ * the price of the contract itself.
+ *///
+ // FIXME: I'm confused even reading the above comment. -CG
struct TALER_Amount deposit_fee_savings;
- GNUNET_assert
- (GNUNET_SYSERR != TALER_amount_subtract
- (&deposit_fee_savings,
- &pc->max_fee,
- &acc_fee));
-
- /* See how much of wire fee
- contribution is covered by
- fee_savings */
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_subtract (&deposit_fee_savings,
+ &pc->max_fee,
+ &acc_fee));
+ /* See how much of wire fee contribution is covered by fee_savings */
if (-1 == TALER_amount_cmp (&deposit_fee_savings,
&wire_fee_customer_contribution))
{
/* wire_fee_customer_contribution > deposit_fee_savings */
- GNUNET_assert
- (GNUNET_SYSERR != TALER_amount_subtract
- (&wire_fee_customer_contribution,
- &wire_fee_customer_contribution,
- &deposit_fee_savings));
-
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_subtract (&wire_fee_customer_contribution,
+ &wire_fee_customer_contribution,
+ &deposit_fee_savings));
/* subtract remaining wire fees from total contribution */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Subtract remaining wire fees"
- " from total contribution: %s\n",
- TALER_amount_to_string
- (&wire_fee_customer_contribution));
-
- if (GNUNET_SYSERR == TALER_amount_subtract
- (&acc_amount,
- &acc_amount,
- &wire_fee_customer_contribution))
+ "Subtract remaining wire fees from total contribution: %s\n",
+ TALER_amount_to_string (&wire_fee_customer_contribution));
+
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&acc_amount,
+ &acc_amount,
+ &wire_fee_customer_contribution))
{
GNUNET_break_op (0);
- return TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES;
+ resume_pay_with_error (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES,
+ "insufficient funds (FIXME: why)");
+ return GNUNET_SYSERR;
}
}
@@ -894,79 +956,25 @@ check_payment_sufficient (struct PayContext *pc)
TALER_amount2s (&pc->amount),
str);
GNUNET_free (str);
- return TALER_EC_PAY_PAYMENT_INSUFFICIENT;
+ resume_pay_with_error (pc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PAY_PAYMENT_INSUFFICIENT,
+ "FIXME: explain nicely");
+ return GNUNET_SYSERR;
}
}
/* Do not count any refunds towards the payment */
- GNUNET_assert
- (GNUNET_SYSERR != TALER_amount_subtract (&acc_amount,
- &acc_amount,
- &pc->total_refunded));
+ // FIXME: check why this assertion holds:
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_subtract (&acc_amount,
+ &acc_amount,
+ &pc->total_refunded));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Subtracting total refunds from paid amount: %s\n",
TALER_amount_to_string (&pc->total_refunded));
- return TALER_EC_NONE;
-}
-
-
-/**
- * Generate full error response based on the @a ec
- *
- * @param pc context for which to generate the error
- * @param ec error code identifying the issue
- */
-static void
-generate_error_response (struct PayContext *pc,
- enum TALER_ErrorCode ec)
-{
- switch (ec)
- {
- case TALER_EC_PAY_AMOUNT_OVERFLOW:
- resume_pay_with_error (pc,
- MHD_HTTP_BAD_REQUEST,
- ec,
- "Overflow adding up amounts");
- break;
- case TALER_EC_PAY_FEES_EXCEED_PAYMENT:
- resume_pay_with_error (pc,
- MHD_HTTP_BAD_REQUEST,
- ec,
- "Deposit fees exceed coin's contribution");
- break;
- case TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES:
- resume_pay_with_error (pc,
- MHD_HTTP_METHOD_NOT_ACCEPTABLE,
- ec,
- "insufficient funds (including excessive exchange fees to be covered by customer)");
- break;
- case TALER_EC_PAY_PAYMENT_INSUFFICIENT:
- resume_pay_with_error (pc,
- MHD_HTTP_METHOD_NOT_ACCEPTABLE,
- ec,
- "insufficient funds");
- break;
- case TALER_EC_PAY_WIRE_FEE_CURRENCY_MISSMATCH:
- resume_pay_with_error (pc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- ec,
- "wire_fee currency does not match");
- break;
- case TALER_EC_PAY_EXCHANGE_REJECTED:
- resume_pay_with_error (pc,
- MHD_HTTP_PRECONDITION_FAILED,
- ec,
- "exchange charges incompatible wire fee");
- break;
- default:
- resume_pay_with_error (pc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- ec,
- "unexpected error code");
- GNUNET_break (0);
- break;
- }
+ return GNUNET_OK;
}
@@ -1036,34 +1044,54 @@ deposit_cb (void *cls,
{
/* We can't do anything meaningful here, the exchange did something wrong */
resume_pay_with_response (pc,
- MHD_HTTP_SERVICE_UNAVAILABLE,
+ MHD_HTTP_FAILED_DEPENDENCY,
TALER_MHD_make_json_pack (
- "{s:s, s:I, s:I, s:I, s:s}",
- "error",
- "exchange failed",
+ "{s:s, s:I, s:I}",
+ "hint",
+ "exchange failed, response body not even in JSON",
"code",
(json_int_t) TALER_EC_PAY_EXCHANGE_FAILED,
- "exchange-code",
- (json_int_t) ec,
"exchange-http-status",
- (json_int_t) http_status,
- "hint",
- "The exchange provided an unexpected response"));
+ (json_int_t) http_status));
}
else
{
/* Forward error, adding the "coin_pub" for which the
error was being generated */
- json_t *eproof;
-
- eproof = json_copy ((json_t *) proof);
- json_object_set_new (eproof,
- "coin_pub",
- GNUNET_JSON_from_data_auto (&dc->coin_pub));
- resume_pay_with_response (pc,
- http_status,
- TALER_MHD_make_json (eproof));
- json_decref (eproof);
+ if (TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS == ec)
+ resume_pay_with_response (
+ pc,
+ MHD_HTTP_CONFLICT,
+ TALER_MHD_make_json_pack ("{s:s, s:I, s:I, s:I, s:o, s:O}",
+ "hint",
+ "exchange failed on deposit of a coin",
+ "code",
+ (json_int_t) TALER_EC_PAY_EXCHANGE_FAILED,
+ "exchange-code",
+ (json_int_t) ec,
+ "exchange-http-status",
+ (json_int_t) http_status,
+ "coin_pub",
+ GNUNET_JSON_from_data_auto (&dc->coin_pub),
+ "exchange-reply",
+ proof));
+ else
+ resume_pay_with_response (
+ pc,
+ MHD_HTTP_FAILED_DEPENDENCY,
+ TALER_MHD_make_json_pack ("{s:s, s:I, s:I, s:I, s:o, s:O}",
+ "hint",
+ "exchange failed on deposit of a coin",
+ "code",
+ (json_int_t) TALER_EC_PAY_EXCHANGE_FAILED,
+ "exchange-code",
+ (json_int_t) ec,
+ "exchange-http-status",
+ (json_int_t) http_status,
+ "coin_pub",
+ GNUNET_JSON_from_data_auto (&dc->coin_pub),
+ "exchange-reply",
+ proof));
}
return;
}
@@ -1151,17 +1179,17 @@ process_pay_with_exchange (void *cls,
{
GNUNET_break (0);
resume_pay_with_error (pc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
+ MHD_HTTP_FAILED_DEPENDENCY,
TALER_EC_PAY_EXCHANGE_KEYS_FAILURE,
"no keys");
return;
}
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Found transaction data for proposal `%s'"
- " of merchant `%s', initiating deposits\n",
- GNUNET_h2s (&pc->h_contract_terms),
- TALER_B2S (&pc->mi->pubkey));
+ GNUNET_log (
+ GNUNET_ERROR_TYPE_DEBUG,
+ "Found transaction data for proposal `%s' of merchant `%s', initiating deposits\n",
+ GNUNET_h2s (&pc->h_contract_terms),
+ TALER_B2S (&pc->mi->pubkey));
/* Initiate /deposit operation for all coins of
the current exchange (!) */
@@ -1170,49 +1198,48 @@ process_pay_with_exchange (void *cls,
{
struct DepositConfirmation *dc = &pc->dc[i];
const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
+ enum TALER_ErrorCode ec;
+ unsigned int hc;
if (GNUNET_YES == dc->found_in_db)
continue;
if (0 != strcmp (dc->exchange_url,
pc->current_exchange))
continue;
- denom_details = TALER_EXCHANGE_get_denomination_key
- (keys,
- &dc->denom);
-
+ denom_details = TALER_EXCHANGE_get_denomination_key (keys,
+ &dc->denom);
if (NULL == denom_details)
{
- GNUNET_break_op (0);
- resume_pay_with_response
- (pc,
- MHD_HTTP_BAD_REQUEST,
- TALER_MHD_make_json_pack
- ("{s:s, s:I, s:o, s:o}",
- "error", "denomination not found",
+ struct GNUNET_HashCode h_denom;
+
+ GNUNET_CRYPTO_rsa_public_key_hash (dc->denom.rsa_public_key,
+ &h_denom);
+ resume_pay_with_response (
+ pc,
+ MHD_HTTP_FAILED_DEPENDENCY,
+ TALER_MHD_make_json_pack (
+ "{s:s, s:I, s:o, s:o}",
+ "hint", "coin's denomination not found",
"code", TALER_EC_PAY_DENOMINATION_KEY_NOT_FOUND,
- "denom_pub", GNUNET_JSON_from_rsa_public_key
- (dc->denom.rsa_public_key),
+ "h_denom_pub", GNUNET_JSON_from_data_auto (&h_denom),
"exchange_keys", TALER_EXCHANGE_get_keys_raw (mh)));
return;
}
- // FIXME: AUDITORS_check_dk should return TALER_EC instead!
if (GNUNET_OK !=
TMH_AUDITORS_check_dk (mh,
denom_details,
- exchange_trusted))
+ exchange_trusted,
+ &hc,
+ &ec))
{
- // FIXME: and we should use THAT EC in our error code generation here!
- GNUNET_break_op (0);
- resume_pay_with_response
- (pc,
- MHD_HTTP_FAILED_DEPENDENCY,
- TALER_MHD_make_json_pack
- ("{s:s, s:I, s:o}",
- "error", "invalid denomination",
- "code", (json_int_t)
- TALER_EC_PAY_DENOMINATION_KEY_AUDITOR_FAILURE,
- "denom_pub", GNUNET_JSON_from_rsa_public_key
- (dc->denom.rsa_public_key)));
+ resume_pay_with_response (
+ pc,
+ hc,
+ TALER_MHD_make_json_pack ("{s:s, s:I, s:o}",
+ "hint", "invalid denomination",
+ "code", (json_int_t) ec,
+ "h_denom_pub", GNUNET_JSON_from_data_auto (
+ &denom_details->h_key)));
return;
}
@@ -1222,15 +1249,11 @@ process_pay_with_exchange (void *cls,
GNUNET_assert (NULL != pc->wm);
GNUNET_assert (NULL != pc->wm->j_wire);
- GNUNET_log
- (GNUNET_ERROR_TYPE_DEBUG,
- "Timing for this payment, wire_deadline:"
- " %llu, refund_deadline: %llu\n",
- (unsigned long long)
- pc->wire_transfer_deadline.abs_value_us,
- (unsigned long long) pc->refund_deadline.abs_value_us);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Timing for this payment, wire_deadline: %llu, refund_deadline: %llu\n",
+ (unsigned long long) pc->wire_transfer_deadline.abs_value_us,
+ (unsigned long long) pc->refund_deadline.abs_value_us);
db->preflight (db->cls);
-
dc->dh = TALER_EXCHANGE_deposit (mh,
&dc->amount_with_fee,
pc->wire_transfer_deadline,
@@ -1247,17 +1270,17 @@ process_pay_with_exchange (void *cls,
dc);
if (NULL == dc->dh)
{
- /* Signature was invalid. If the exchange was
- unavailable, we'd get that information in the callback. */
+ /* Signature was invalid or some other constraint was not satisfied. If
+ the exchange was unavailable, we'd get that information in the
+ callback. */
GNUNET_break_op (0);
- resume_pay_with_response
- (pc,
+ resume_pay_with_response (
+ pc,
MHD_HTTP_UNAUTHORIZED,
- TALER_MHD_make_json_pack
- ("{s:s, s:I, s:i}",
- "hint", "Coin signature invalid.",
- "code", (json_int_t)
- TALER_EC_PAY_COIN_SIGNATURE_INVALID,
+ TALER_MHD_make_json_pack (
+ "{s:s, s:I, s:i}",
+ "hint", "deposit signature invalid",
+ "code", (json_int_t) TALER_EC_PAY_COIN_SIGNATURE_INVALID,
"coin_idx", i));
return;
}
@@ -1329,9 +1352,9 @@ handle_pay_timeout (void *cls)
pc->fo = NULL;
}
resume_pay_with_error (pc,
- MHD_HTTP_SERVICE_UNAVAILABLE,
+ MHD_HTTP_REQUEST_TIMEOUT,
TALER_EC_PAY_EXCHANGE_TIMEOUT,
- "exchange not reachable");
+ "likely the exchange did not reply quickly enough");
}
@@ -1431,9 +1454,6 @@ parse_pay (struct MHD_Connection *connection,
struct PayContext *pc)
{
json_t *coins;
- json_t *coin;
- json_t *merchant;
- unsigned int coins_index;
const char *order_id;
const char *mode;
struct TALER_MerchantPublicKeyP merchant_pub;
@@ -1450,36 +1470,41 @@ parse_pay (struct MHD_Connection *connection,
GNUNET_JSON_spec_end ()
};
enum GNUNET_DB_QueryStatus qs;
- const char *session_id;
res = TALER_MHD_parse_json_data (connection,
root,
spec);
if (GNUNET_YES != res)
{
- GNUNET_break (0);
+ GNUNET_break_op (0);
return res;
}
- session_id = json_string_value (json_object_get (root,
- "session_id"));
-
if (0 != GNUNET_memcmp (&merchant_pub,
&pc->mi->pubkey))
{
+ GNUNET_JSON_parse_free (spec);
TALER_LOG_INFO (
"Unknown merchant public key included in payment (usually wrong instance chosen)\n");
- if (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_PAY_WRONG_INSTANCE,
- "Payment sent to wrong instance (merchant_pub unknown to the merchant)"))
- return GNUNET_NO;
- return GNUNET_SYSERR;
+ return
+ (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PAY_WRONG_INSTANCE,
+ "merchant_pub in contract does not match this instance"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
- if (NULL != session_id)
- pc->session_id = GNUNET_strdup (session_id);
+ {
+ const char *session_id;
+
+ session_id = json_string_value (json_object_get (root,
+ "session_id"));
+ if (NULL != session_id)
+ pc->session_id = GNUNET_strdup (session_id);
+ }
+ GNUNET_assert (NULL == pc->order_id);
pc->order_id = GNUNET_strdup (order_id);
GNUNET_assert (NULL == pc->contract_terms);
qs = db->find_contract_terms (db->cls,
@@ -1488,30 +1513,32 @@ parse_pay (struct MHD_Connection *connection,
&merchant_pub);
if (0 > qs)
{
+ GNUNET_JSON_parse_free (spec);
/* 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 */
+ /* Always report on hard error 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_PAY_DB_FETCH_PAY_ERROR,
- "db error to previous /pay data");
-
+ return
+ (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_PAY_DB_FETCH_PAY_ERROR,
+ "db error to previous /pay data"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
GNUNET_JSON_parse_free (spec);
- if (MHD_YES !=
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_PAY_DB_STORE_PAY_ERROR,
- "Proposal not found"))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_NO;
+ return
+ (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_PAY_PROPOSAL_NOT_FOUND,
+ "Proposal not found"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -1520,39 +1547,33 @@ parse_pay (struct MHD_Connection *connection,
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
- if (MHD_YES !=
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_PAY_FAILED_COMPUTE_PROPOSAL_HASH,
- "Failed to hash proposal"))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_NO;
+ return
+ (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_PAY_FAILED_COMPUTE_PROPOSAL_HASH,
+ "Failed to hash proposal"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Handling /pay for order `%s' with contract hash `%s'\n",
order_id,
GNUNET_h2s (&pc->h_contract_terms));
- merchant = json_object_get (pc->contract_terms,
- "merchant");
- if (NULL == merchant)
+ if (NULL == json_object_get (pc->contract_terms,
+ "merchant"))
{
/* invalid contract */
- GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
- if (MHD_YES !=
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_PAY_MERCHANT_FIELD_MISSING,
- "No merchant field in proposal"))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_NO;
+ return
+ (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_PAY_MERCHANT_FIELD_MISSING,
+ "No merchant field in proposal"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
if (0 != strcasecmp ("abort-refund",
mode))
@@ -1586,17 +1607,17 @@ parse_pay (struct MHD_Connection *connection,
espec);
if (GNUNET_YES != res)
{
- GNUNET_JSON_parse_free (spec);
GNUNET_break (0);
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ GNUNET_JSON_parse_free (spec);
+ return res;
}
pc->fulfillment_url = GNUNET_strdup (fulfillment_url);
-
if (pc->wire_transfer_deadline.abs_value_us <
pc->refund_deadline.abs_value_us)
{
- /* This should already have been checked when creating the order! */
+ /* This should already have been checked when creating the
+ order! */
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
@@ -1641,11 +1662,9 @@ parse_pay (struct MHD_Connection *connection,
espec);
if (GNUNET_YES != res)
{
- GNUNET_break_op (0); /* invalid input, use default */
- /* default is we cover no fee */
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (pc->max_fee.currency,
- &pc->max_wire_fee));
+ GNUNET_break_op (0); /* invalid input, fail */
+ GNUNET_JSON_parse_free (spec);
+ return res;
}
}
else
@@ -1655,6 +1674,7 @@ parse_pay (struct MHD_Connection *connection,
TALER_amount_get_zero (pc->max_fee.currency,
&pc->max_wire_fee));
}
+
if (NULL != json_object_get (pc->contract_terms,
"wire_fee_amortization"))
{
@@ -1685,7 +1705,7 @@ parse_pay (struct MHD_Connection *connection,
{
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
+ MHD_HTTP_BAD_REQUEST,
TALER_EC_PAY_COINS_ARRAY_EMPTY,
"coins");
}
@@ -1694,38 +1714,42 @@ parse_pay (struct MHD_Connection *connection,
struct DepositConfirmation);
/* This loop populates the array 'dc' in 'pc' */
- json_array_foreach (coins, coins_index, coin)
{
- struct DepositConfirmation *dc = &pc->dc[coins_index];
- const char *exchange_url;
- struct GNUNET_JSON_Specification ispec[] = {
- TALER_JSON_spec_denomination_public_key ("denom_pub",
- &dc->denom),
- TALER_JSON_spec_amount ("contribution",
- &dc->amount_with_fee),
- GNUNET_JSON_spec_string ("exchange_url",
- &exchange_url),
- GNUNET_JSON_spec_fixed_auto ("coin_pub",
- &dc->coin_pub),
- TALER_JSON_spec_denomination_signature ("ub_sig",
- &dc->ub_sig),
- GNUNET_JSON_spec_fixed_auto ("coin_sig",
- &dc->coin_sig),
- GNUNET_JSON_spec_end ()
- };
-
- res = TALER_MHD_parse_json_data (connection,
- coin,
- ispec);
- if (GNUNET_YES != res)
+ unsigned int coins_index;
+ json_t *coin;
+ json_array_foreach (coins, coins_index, coin)
{
- GNUNET_JSON_parse_free (spec);
- GNUNET_break_op (0);
- return res;
+ struct DepositConfirmation *dc = &pc->dc[coins_index];
+ const char *exchange_url;
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_denomination_public_key ("denom_pub",
+ &dc->denom),
+ TALER_JSON_spec_amount ("contribution",
+ &dc->amount_with_fee),
+ GNUNET_JSON_spec_string ("exchange_url",
+ &exchange_url),
+ GNUNET_JSON_spec_fixed_auto ("coin_pub",
+ &dc->coin_pub),
+ TALER_JSON_spec_denomination_signature ("ub_sig",
+ &dc->ub_sig),
+ GNUNET_JSON_spec_fixed_auto ("coin_sig",
+ &dc->coin_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TALER_MHD_parse_json_data (connection,
+ coin,
+ ispec);
+ if (GNUNET_YES != res)
+ {
+ GNUNET_JSON_parse_free (spec);
+ GNUNET_break_op (0);
+ return res;
+ }
+ dc->exchange_url = GNUNET_strdup (exchange_url);
+ dc->index = coins_index;
+ dc->pc = pc;
}
- dc->exchange_url = GNUNET_strdup (exchange_url);
- dc->index = coins_index;
- dc->pc = pc;
}
pc->pending = pc->coins_cnt;
GNUNET_JSON_parse_free (spec);
@@ -1790,17 +1814,12 @@ begin_transaction (struct PayContext *pc)
if (pc->retry_counter++ > MAX_RETRIES)
{
GNUNET_break (0);
- resume_pay_with_response (pc,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_MHD_make_json_pack ("{s:I, s:s}",
- "code",
- (json_int_t)
- TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR,
- "hint",
- "Soft merchant database error: retry counter exceeded"));
+ resume_pay_with_error (pc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR,
+ "Soft merchant database error: retry counter exceeded");
return;
}
-
GNUNET_assert (GNUNET_YES == pc->suspended);
/* Init. some price accumulators. */
@@ -1824,7 +1843,7 @@ begin_transaction (struct PayContext *pc)
resume_pay_with_error (pc,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
- "Merchant database error (could not start transaction)");
+ "Merchant database error (could not begin transaction)");
return;
}
@@ -1870,7 +1889,7 @@ begin_transaction (struct PayContext *pc)
resume_pay_with_error (pc,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
- "Merchant database error");
+ "Merchant database error checking for refunds");
return;
}
@@ -1938,7 +1957,7 @@ begin_transaction (struct PayContext *pc)
resume_pay_with_error (pc,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_PAY_DB_STORE_PAY_ERROR,
- "Merchant database error");
+ "Merchant database error storing abort-refund");
return;
}
qs = db->commit (db->cls);
@@ -1973,21 +1992,18 @@ begin_transaction (struct PayContext *pc)
}
for (unsigned int i = 0; i<pc->coins_cnt; i++)
{
- struct TALER_RefundRequestPS rr;
struct TALER_MerchantSignatureP msig;
- uint64_t rtransactionid;
+ struct TALER_RefundRequestPS rr = {
+ .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
+ .purpose.size = htonl (sizeof (rr)),
+ .h_contract_terms = pc->h_contract_terms,
+ .coin_pub = pc->dc[i].coin_pub,
+ .merchant = pc->mi->pubkey,
+ .rtransaction_id = GNUNET_htonll (0)
+ };
- /* Will only work with coins found in DB. */
if (GNUNET_YES != pc->dc[i].found_in_db)
- continue;
-
- rtransactionid = 0;
- rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
- rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
- rr.h_contract_terms = pc->h_contract_terms;
- rr.coin_pub = pc->dc[i].coin_pub;
- rr.merchant = pc->mi->pubkey;
- rr.rtransaction_id = GNUNET_htonll (rtransactionid);
+ continue; /* Skip coins not found in DB. */
TALER_amount_hton (&rr.refund_amount,
&pc->dc[i].amount_with_fee);
TALER_amount_hton (&rr.refund_fee,
@@ -2010,22 +2026,19 @@ begin_transaction (struct PayContext *pc)
/* Pack refund for i-th coin. */
if (0 !=
- json_array_append_new (refunds,
- json_pack ("{s:I, s:o, s:o s:o s:o}",
- "rtransaction_id",
- (json_int_t) rtransactionid,
- "coin_pub",
- GNUNET_JSON_from_data_auto (
- &rr.coin_pub),
- "merchant_sig",
- GNUNET_JSON_from_data_auto (
- &msig),
- "refund_amount",
- TALER_JSON_from_amount_nbo (
- &rr.refund_amount),
- "refund_fee",
- TALER_JSON_from_amount_nbo (
- &rr.refund_fee))))
+ json_array_append_new (
+ refunds,
+ json_pack ("{s:I, s:o, s:o s:o s:o}",
+ "rtransaction_id",
+ (json_int_t) 0,
+ "coin_pub",
+ GNUNET_JSON_from_data_auto (&rr.coin_pub),
+ "merchant_sig",
+ GNUNET_JSON_from_data_auto (&msig),
+ "refund_amount",
+ TALER_JSON_from_amount_nbo (&rr.refund_amount),
+ "refund_fee",
+ TALER_JSON_from_amount_nbo (&rr.refund_fee))))
{
json_decref (refunds);
GNUNET_break (0);
@@ -2038,11 +2051,11 @@ begin_transaction (struct PayContext *pc)
}
/* Resume and send back the response. */
- resume_pay_with_response
- (pc,
+ resume_pay_with_response (
+ pc,
MHD_HTTP_OK,
- TALER_MHD_make_json_pack
- ("{s:o, s:o, s:o}",
+ TALER_MHD_make_json_pack (
+ "{s:o, s:o, s:o}",
/* Refunds pack. */
"refund_permissions", refunds,
"merchant_pub",
@@ -2051,24 +2064,17 @@ begin_transaction (struct PayContext *pc)
GNUNET_JSON_from_data_auto (&pc->h_contract_terms)));
}
return;
- }
+ } /* End of PC_MODE_ABORT_REFUND */
+
/* Default PC_MODE_PAY mode */
/* Final termination case: all coins already known, just
generate ultimate outcome. */
if (0 == pc->pending)
{
- enum TALER_ErrorCode ec;
-
- ec = check_payment_sufficient (pc);
- if (TALER_EC_NONE != ec)
+ if (GNUNET_OK != check_payment_sufficient (pc))
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Payment is not sufficient (ec=%u).\n",
- (unsigned int) ec);
db->rollback (db->cls);
- generate_error_response (pc,
- ec);
return;
}
/* Payment succeeded, save in database */
@@ -2078,7 +2084,6 @@ begin_transaction (struct PayContext *pc)
qs = db->mark_proposal_paid (db->cls,
&pc->h_contract_terms,
&pc->mi->pubkey);
-
if (qs < 0)
{
db->rollback (db->cls);
@@ -2087,16 +2092,16 @@ begin_transaction (struct PayContext *pc)
begin_transaction (pc);
return;
}
- resume_pay_with_error
- (pc,
+ resume_pay_with_error (
+ pc,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR,
- "Merchant database error: could not "
- "mark proposal as 'paid'");
+ "Merchant database error: could not mark proposal as 'paid'");
return;
}
- if ( (NULL != pc->session_id) && (NULL != pc->fulfillment_url) )
+ if ( (NULL != pc->session_id) &&
+ (NULL != pc->fulfillment_url) )
{
qs = db->insert_session_info (db->cls,
pc->session_id,
@@ -2117,20 +2122,16 @@ begin_transaction (struct PayContext *pc)
begin_transaction (pc);
return;
}
- resume_pay_with_error
- (pc,
+ resume_pay_with_error (
+ pc,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR,
- "Merchant database error: could not "
- "mark proposal as 'paid'");
+ "Merchant database error: could not commit to mark proposal as 'paid'");
return;
}
-
resume_suspended_payment_checks (pc->order_id,
&pc->mi->pubkey);
- resume_pay_with_response (pc,
- MHD_HTTP_OK,
- sign_success_response (pc));
+ generate_success_response (pc);
return;
}
@@ -2159,26 +2160,24 @@ handler_pay_json (struct MHD_Connection *connection,
const json_t *root,
struct PayContext *pc)
{
- int ret;
-
- ret = parse_pay (connection,
- root,
- pc);
+ {
+ int ret;
- if (GNUNET_OK != ret)
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ ret = parse_pay (connection,
+ root,
+ pc);
+ if (GNUNET_OK != ret)
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ /* Payment not finished, suspend while we interact with the exchange */
MHD_suspend_connection (connection);
pc->suspended = GNUNET_YES;
- GNUNET_log
- (GNUNET_ERROR_TYPE_DEBUG,
- "Suspending /pay handling while working with the exchange\n");
-
- pc->timeout_task = GNUNET_SCHEDULER_add_delayed
- (PAY_TIMEOUT,
- &handle_pay_timeout,
- pc);
-
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Suspending /pay handling while working with the exchange\n");
+ pc->timeout_task = GNUNET_SCHEDULER_add_delayed (PAY_TIMEOUT,
+ &handle_pay_timeout,
+ pc);
begin_transaction (pc);
return MHD_YES;
}
@@ -2231,6 +2230,8 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
/* not the first call, recover state */
pc = *connection_cls;
}
+ if (GNUNET_SYSERR == pc->suspended)
+ return MHD_NO; /* during shutdown, we don't generate any more replies */
if (0 != pc->response_code)
{
/* We are *done* processing the request,
diff --git a/src/backend/taler-merchant-httpd_tip-reserve-helper.c b/src/backend/taler-merchant-httpd_tip-reserve-helper.c
index 5566148d..0ded8ace 100644
--- a/src/backend/taler-merchant-httpd_tip-reserve-helper.c
+++ b/src/backend/taler-merchant-httpd_tip-reserve-helper.c
@@ -158,7 +158,7 @@ handle_status (void *cls,
resume_with_response (ctr,
MHD_HTTP_SERVICE_UNAVAILABLE,
TALER_MHD_make_error (
- TALER_EC_TIP_QUERY_RESERVE_CURRENCY_MISSMATCH,
+ TALER_EC_TIP_QUERY_RESERVE_CURRENCY_MISMATCH,
"Exchange currency unexpected"));
return;
}
diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c
index 1d5abde7..893efd17 100644
--- a/src/lib/merchant_api_pay.c
+++ b/src/lib/merchant_api_pay.c
@@ -288,12 +288,17 @@ check_conflict (struct TALER_MERCHANT_Pay *ph,
const json_t *json)
{
json_t *history;
+ json_t *ereply;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("history", &history),
+ GNUNET_JSON_spec_json ("exchange-reply", &ereply),
GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
GNUNET_JSON_spec_end ()
};
+ struct GNUNET_JSON_Specification hspec[] = {
+ GNUNET_JSON_spec_json ("history", &history),
+ GNUNET_JSON_spec_end ()
+ };
int ret;
if (GNUNET_OK !=
@@ -304,6 +309,17 @@ check_conflict (struct TALER_MERCHANT_Pay *ph,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (ereply,
+ hspec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_JSON_parse_free (spec);
+
for (unsigned int i = 0; i<ph->num_coins; i++)
{
if (0 == memcmp (&ph->coins[i].coin_pub,
@@ -312,13 +328,13 @@ check_conflict (struct TALER_MERCHANT_Pay *ph,
{
ret = check_coin_history (&ph->coins[i],
history);
- GNUNET_JSON_parse_free (spec);
+ GNUNET_JSON_parse_free (hspec);
return ret;
}
}
GNUNET_break_op (0); /* complaint is not about any of the coins
that we actually paid with... */
- GNUNET_JSON_parse_free (spec);
+ GNUNET_JSON_parse_free (hspec);
return GNUNET_SYSERR;
}
@@ -378,10 +394,9 @@ handle_pay_finished (void *cls,
break;
case MHD_HTTP_FORBIDDEN:
ec = TALER_JSON_get_error_code (json);
- /* Nothing really to verify, merchant says one of the
- * signatures is invalid; as we checked them, this
- * should never happen, we should pass the JSON reply
- * to the application */
+ /* Nothing really to verify, merchant says one of the signatures is
+ * invalid OR we tried to abort the payment after it was successful. We
+ * should pass the JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
ec = TALER_JSON_get_error_code (json);
@@ -389,6 +404,28 @@ handle_pay_finished (void *cls,
happen, we should pass the JSON reply to the
application */
break;
+ case MHD_HTTP_PRECONDITION_FAILED:
+ ec = TALER_JSON_get_error_code (json);
+ /* Nothing really to verify, the merchant is blaming us for failing to
+ satisfy some constraint. We should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_REQUEST_TIMEOUT:
+ ec = TALER_JSON_get_error_code (json);
+ /* The merchant couldn't generate a timely response, likely because
+ it itself waited too long on the exchange.
+ Pass on to application. */
+ break;
+ case MHD_HTTP_GONE:
+ ec = TALER_JSON_get_error_code (json);
+ /* The merchant says our denomination key has expired for deposits,
+ might be a disagreement in timestamps? Still, pass on to application. */
+ break;
+ case MHD_HTTP_FAILED_DEPENDENCY:
+ ec = TALER_JSON_get_error_code (json);
+ /* Nothing really to verify, the merchant is blaming the exchange.
+ We should pass the JSON reply to the application */
+ break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
ec = TALER_JSON_get_error_code (json);
/* Server had an internal issue; we should retry,
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
index b214814d..be7dec7e 100644
--- a/src/lib/test_merchant_api.c
+++ b/src/lib/test_merchant_api.c
@@ -802,7 +802,7 @@ run (void *cls,
\"value\":\"{EUR:10}\"} ] }"),
TALER_TESTING_cmd_pay ("pay-fail-partial-double-11-good",
merchant_url,
- MHD_HTTP_NOT_ACCEPTABLE,
+ MHD_HTTP_BAD_REQUEST,
"create-proposal-11",
"withdraw-coin-11a",
"EUR:5",