summaryrefslogtreecommitdiff
path: root/src/lib/merchant_api_post_order_pay.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/merchant_api_post_order_pay.c')
-rw-r--r--src/lib/merchant_api_post_order_pay.c314
1 files changed, 31 insertions, 283 deletions
diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c
index 765ca1a6..57c85565 100644
--- a/src/lib/merchant_api_post_order_pay.c
+++ b/src/lib/merchant_api_post_order_pay.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -109,12 +109,6 @@ struct TALER_MERCHANT_OrderPayHandle
json_t *error_history;
/**
- * Handle to the exchange that issued a problematic
- * coin (if any).
- */
- struct TALER_EXCHANGE_Handle *exchange;
-
- /**
* Number of @e coins we are paying with.
*/
unsigned int num_coins;
@@ -129,248 +123,6 @@ struct TALER_MERCHANT_OrderPayHandle
/**
- * We got a 409 response back from the exchange (or the merchant).
- * Now we need to check the provided cryptographic proof that the
- * coin was actually already spent!
- *
- * @param oph operation handle
- * @param keys key data from the exchange
- * @return #GNUNET_OK if conflict is valid
- */
-static enum GNUNET_GenericReturnValue
-check_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
- const struct TALER_EXCHANGE_Keys *keys)
-{
- struct TALER_Amount spent;
- struct TALER_Amount spent_plus_contrib;
- struct TALER_DenominationHashP h_denom_pub_pc;
- const struct TALER_EXCHANGE_DenomPublicKey *dpk;
-
- TALER_denom_pub_hash (&oph->error_pc->denom_pub,
- &h_denom_pub_pc);
- dpk = TALER_EXCHANGE_get_denomination_key_by_hash (
- keys,
- &h_denom_pub_pc);
- if (GNUNET_OK !=
- TALER_EXCHANGE_verify_coin_history (dpk,
- &oph->error_pc->coin_pub,
- oph->error_history,
- &spent))
- {
- /* Exchange's history fails to verify */
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (0 >
- TALER_amount_add (&spent_plus_contrib,
- &spent,
- &oph->error_pc->amount_with_fee))
- {
- /* We got an integer overflow? Bad application! */
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (-1 != TALER_amount_cmp (&oph->error_pc->denom_value,
- &spent_plus_contrib))
- {
- /* according to our calculations, the transaction should
- have still worked, AND we did not get any proof of
- coin public key re-use; hence: exchange error! */
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Accepting proof of double-spending (or coin public key re-use)\n");
- return GNUNET_OK;
-}
-
-
-/**
- * We got the fee structure from the exchange. Now
- * validate the conflict error.
- *
- * @param cls a `struct TALER_MERCHANT_OrderPayHandle`
- * @param ehr reply from the exchange
- * @param keys the key structure
- * @param compat protocol compatibility indication
- */
-static void
-cert_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *ehr,
- const struct TALER_EXCHANGE_Keys *keys,
- enum TALER_EXCHANGE_VersionCompatibility compat)
-{
- struct TALER_MERCHANT_OrderPayHandle *oph = cls;
-
- if (TALER_EXCHANGE_VC_INCOMPATIBLE & compat)
- {
- struct TALER_MERCHANT_PayResponse pr = {
- .hr.http_status = MHD_HTTP_CONFLICT,
- .hr.exchange_http_status = 0,
- .hr.ec = TALER_EC_WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE,
- .hr.reply = oph->full_reply,
- .hr.exchange_reply = ehr->reply,
- .hr.hint = "could not check error: incompatible exchange version"
- };
-
- oph->pay_cb (oph->pay_cb_cls,
- &pr);
- TALER_MERCHANT_order_pay_cancel (oph);
- return;
- }
- if ( (MHD_HTTP_OK != ehr->http_status) ||
- (NULL == keys) )
- {
- struct TALER_MERCHANT_PayResponse pr = {
- .hr.http_status = MHD_HTTP_CONFLICT,
- .hr.exchange_http_status = ehr->http_status,
- .hr.ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
- .hr.reply = oph->full_reply,
- .hr.exchange_reply = ehr->reply,
- .hr.hint = "failed to download /keys from the exchange"
- };
-
- oph->pay_cb (oph->pay_cb_cls,
- &pr);
- TALER_MERCHANT_order_pay_cancel (oph);
- return;
- }
-
- if (GNUNET_OK !=
- check_conflict (oph,
- keys))
- {
- struct TALER_MERCHANT_PayResponse pr = {
- .hr.http_status = 0,
- .hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE,
- .hr.reply = oph->full_reply
- };
-
- oph->pay_cb (oph->pay_cb_cls,
- &pr);
- TALER_MERCHANT_order_pay_cancel (oph);
- return;
- }
-
- {
- struct TALER_MERCHANT_PayResponse pr = {
- .hr.http_status = MHD_HTTP_CONFLICT,
- .hr.ec = TALER_JSON_get_error_code (oph->full_reply),
- .hr.reply = oph->full_reply
- };
-
- oph->pay_cb (oph->pay_cb_cls,
- &pr);
- TALER_MERCHANT_order_pay_cancel (oph);
- }
-}
-
-
-/**
- * We got a 409 response back from the exchange (or the merchant).
- * Now we need to check the provided cryptograophic proof that the
- * coin was actually already spent!
- *
- * @param[in,out] oph handle of the original pay operation
- * @param[in,out] pr response to modify if #GNUNET_OK is returned
- * @param json cryptograophic proof returned by the
- * exchange/merchant
- * @return #GNUNET_OK if proof checks out,
- * #GNUNET_SYSERR if it is wrong,
- * #GNUNET_NO if checking continues asynchronously
- */
-static enum GNUNET_GenericReturnValue
-parse_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
- struct TALER_MERCHANT_PayResponse *pr,
- const json_t *json)
-{
- json_t *ereply;
- const char *exchange_url;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("exchange_reply",
- &ereply),
- GNUNET_JSON_spec_string ("exchange_url",
- &exchange_url),
- GNUNET_JSON_spec_end ()
- };
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct GNUNET_JSON_Specification hspec[] = {
- GNUNET_JSON_spec_json ("history",
- &oph->error_history),
- GNUNET_JSON_spec_fixed_auto ("coin_pub",
- &coin_pub),
- GNUNET_JSON_spec_end ()
- };
- enum TALER_ErrorCode ec = TALER_JSON_get_error_code (json);
-
- switch (ec)
- {
- case TALER_EC_GENERIC_CURRENCY_MISMATCH:
- /* no proof to check, still very strange, as we
- should have checked that the currency matches */
- GNUNET_break_op (0);
- TALER_MERCHANT_parse_error_details_ (json,
- MHD_HTTP_CONFLICT,
- &pr->hr);
- return GNUNET_OK;
- case TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_ALREADY_PAID:
- /* We can only be happy and accept the result;
- FIXME: parse the refunds... */
- TALER_MERCHANT_parse_error_details_ (json,
- MHD_HTTP_CONFLICT,
- &pr->hr);
- return GNUNET_OK;
- case TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS:
- /* main case, handled below */
- break;
- default:
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- 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<oph->num_coins; i++)
- {
- if (0 ==
- GNUNET_memcmp (&oph->coins[i].coin_pub,
- &coin_pub))
- {
- oph->error_pc = &oph->coins[i];
- oph->full_reply = json_incref ((json_t *) json);
- oph->exchange = TALER_EXCHANGE_connect (oph->ctx,
- oph->error_pc->exchange_url,
- &cert_cb,
- oph,
- TALER_EXCHANGE_OPTION_END);
- return GNUNET_NO;
- }
- }
- GNUNET_break_op (0); /* complaint is not about any of the coins
- that we actually paid with... */
- GNUNET_JSON_parse_free (hspec);
- return GNUNET_SYSERR;
-}
-
-
-/**
* Function called when we're done processing the
* HTTP /pay request.
*
@@ -406,7 +158,12 @@ handle_pay_finished (void *cls,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto (
"sig",
- &pr.details.success.merchant_sig),
+ &pr.details.ok.merchant_sig),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string (
+ "pos_confirmation",
+ &pr.details.ok.pos_confirmation),
+ NULL),
GNUNET_JSON_spec_end ()
};
@@ -425,7 +182,7 @@ handle_pay_finished (void *cls,
if (GNUNET_OK !=
TALER_merchant_pay_verify (&oph->h_contract_terms,
&oph->merchant_pub,
- &pr.details.success.merchant_sig))
+ &pr.details.ok.merchant_sig))
{
GNUNET_break_op (0);
pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
@@ -475,27 +232,10 @@ handle_pay_finished (void *cls,
Pass on to application. */
break;
case MHD_HTTP_CONFLICT:
- {
- enum GNUNET_GenericReturnValue ret;
-
- ret = parse_conflict (oph,
- &pr,
- json);
- switch (ret)
- {
- case GNUNET_OK:
- /* continued below, 'pr' was modified */
- break;
- case GNUNET_NO:
- /* handled asynchronously! */
- return; /* ! */
- case GNUNET_SYSERR:
- GNUNET_break_op (0);
- response_code = 0;
- break;
- }
- break;
- }
+ TALER_MERCHANT_parse_error_details_ (json,
+ MHD_HTTP_CONFLICT,
+ &pr.hr);
+ break;
case MHD_HTTP_GONE:
TALER_MERCHANT_parse_error_details_ (json,
response_code,
@@ -565,8 +305,9 @@ TALER_MERCHANT_order_pay_frontend (
const char *merchant_url,
const char *order_id,
const char *session_id,
+ const json_t *wallet_data,
unsigned int num_coins,
- const struct TALER_MERCHANT_PaidCoin coins[],
+ const struct TALER_MERCHANT_PaidCoin coins[static num_coins],
TALER_MERCHANT_OrderPayCallback pay_cb,
void *pay_cb_cls)
{
@@ -655,6 +396,9 @@ TALER_MERCHANT_order_pay_frontend (
GNUNET_JSON_pack_array_steal ("coins",
j_coins),
GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("wallet_data",
+ (json_t *) wallet_data)),
+ GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("session_id",
session_id)));
@@ -684,9 +428,9 @@ TALER_MERCHANT_order_pay_frontend (
oph->num_coins = num_coins;
oph->coins = GNUNET_new_array (num_coins,
struct TALER_MERCHANT_PaidCoin);
- memcpy (oph->coins,
- coins,
- num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
+ GNUNET_memcpy (oph->coins,
+ coins,
+ num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
if (GNUNET_OK !=
@@ -717,6 +461,7 @@ TALER_MERCHANT_order_pay (
const char *merchant_url,
const char *session_id,
const struct TALER_PrivateContractHashP *h_contract_terms,
+ const json_t *wallet_data,
const struct TALER_Amount *amount,
const struct TALER_Amount *max_fee,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -727,10 +472,12 @@ TALER_MERCHANT_order_pay (
const struct TALER_MerchantWireHashP *h_wire,
const char *order_id,
unsigned int num_coins,
- const struct TALER_MERCHANT_PayCoin coins[],
+ const struct TALER_MERCHANT_PayCoin coins[static num_coins],
TALER_MERCHANT_OrderPayCallback pay_cb,
void *pay_cb_cls)
{
+ struct GNUNET_HashCode wallet_data_hash;
+
if (GNUNET_YES !=
TALER_amount_cmp_currency (amount,
max_fee))
@@ -738,7 +485,9 @@ TALER_MERCHANT_order_pay (
GNUNET_break (0);
return NULL;
}
-
+ if (NULL != wallet_data)
+ TALER_json_hash (wallet_data,
+ &wallet_data_hash);
{
struct TALER_MERCHANT_PaidCoin pc[num_coins];
@@ -765,6 +514,9 @@ TALER_MERCHANT_order_pay (
&fee,
h_wire,
h_contract_terms,
+ (NULL != wallet_data)
+ ? &wallet_data_hash
+ : NULL,
coin->h_age_commitment,
NULL /* h_extensions! */,
&h_denom_pub,
@@ -789,6 +541,7 @@ TALER_MERCHANT_order_pay (
merchant_url,
order_id,
session_id,
+ wallet_data,
num_coins,
pc,
pay_cb,
@@ -812,11 +565,6 @@ TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
GNUNET_CURL_job_cancel (oph->job);
oph->job = NULL;
}
- if (NULL != oph->exchange)
- {
- TALER_EXCHANGE_disconnect (oph->exchange);
- oph->exchange = NULL;
- }
TALER_curl_easy_post_finished (&oph->post_ctx);
json_decref (oph->error_history);
json_decref (oph->full_reply);