diff options
Diffstat (limited to 'src/lib/merchant_api_post_order_pay.c')
-rw-r--r-- | src/lib/merchant_api_post_order_pay.c | 314 |
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); |