From 3ebb5281bc62ca6231f6131e0601471d9ef03b8b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 10 Jul 2023 10:37:25 +0200 Subject: tip -> reward --- src/lib/Makefile.am | 12 +- src/lib/merchant_api_get_config.c | 2 +- src/lib/merchant_api_get_reserve.c | 38 +-- src/lib/merchant_api_get_rewards.c | 288 +++++++++++++++++++ src/lib/merchant_api_get_tips.c | 288 ------------------- src/lib/merchant_api_merchant_get_reward.c | 302 ++++++++++++++++++++ src/lib/merchant_api_merchant_get_tip.c | 302 -------------------- src/lib/merchant_api_reward_authorize.c | 371 ++++++++++++++++++++++++ src/lib/merchant_api_reward_pickup.c | 440 +++++++++++++++++++++++++++++ src/lib/merchant_api_reward_pickup2.c | 355 +++++++++++++++++++++++ src/lib/merchant_api_tip_authorize.c | 371 ------------------------ src/lib/merchant_api_tip_pickup.c | 440 ----------------------------- src/lib/merchant_api_tip_pickup2.c | 355 ----------------------- src/lib/merchant_api_wallet_get_reward.c | 220 +++++++++++++++ src/lib/merchant_api_wallet_get_tip.c | 220 --------------- 15 files changed, 2002 insertions(+), 2002 deletions(-) create mode 100644 src/lib/merchant_api_get_rewards.c delete mode 100644 src/lib/merchant_api_get_tips.c create mode 100644 src/lib/merchant_api_merchant_get_reward.c delete mode 100644 src/lib/merchant_api_merchant_get_tip.c create mode 100644 src/lib/merchant_api_reward_authorize.c create mode 100644 src/lib/merchant_api_reward_pickup.c create mode 100644 src/lib/merchant_api_reward_pickup2.c delete mode 100644 src/lib/merchant_api_tip_authorize.c delete mode 100644 src/lib/merchant_api_tip_pickup.c delete mode 100644 src/lib/merchant_api_tip_pickup2.c create mode 100644 src/lib/merchant_api_wallet_get_reward.c delete mode 100644 src/lib/merchant_api_wallet_get_tip.c (limited to 'src/lib') diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index e59bd432..a696f3ea 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -33,7 +33,7 @@ libtalermerchant_la_SOURCES = \ merchant_api_get_products.c \ merchant_api_get_reserve.c \ merchant_api_get_reserves.c \ - merchant_api_get_tips.c \ + merchant_api_get_rewards.c \ merchant_api_get_transfers.c \ merchant_api_get_template.c \ merchant_api_get_templates.c \ @@ -41,7 +41,7 @@ libtalermerchant_la_SOURCES = \ merchant_api_get_webhooks.c \ merchant_api_lock_product.c \ merchant_api_merchant_get_order.c \ - merchant_api_merchant_get_tip.c \ + merchant_api_merchant_get_reward.c \ merchant_api_patch_instance.c \ merchant_api_patch_order_forget.c \ merchant_api_patch_product.c \ @@ -62,10 +62,10 @@ libtalermerchant_la_SOURCES = \ merchant_api_post_templates.c \ merchant_api_post_using_templates.c \ merchant_api_post_webhooks.c \ - merchant_api_tip_authorize.c \ - merchant_api_tip_pickup.c \ - merchant_api_tip_pickup2.c \ - merchant_api_wallet_get_tip.c \ + merchant_api_reward_authorize.c \ + merchant_api_reward_pickup.c \ + merchant_api_reward_pickup2.c \ + merchant_api_wallet_get_reward.c \ merchant_api_wallet_get_order.c \ merchant_api_wallet_post_order_refund.c diff --git a/src/lib/merchant_api_get_config.c b/src/lib/merchant_api_get_config.c index b6023bdb..8eddf9d7 100644 --- a/src/lib/merchant_api_get_config.c +++ b/src/lib/merchant_api_get_config.c @@ -34,7 +34,7 @@ * Which version of the Taler protocol is implemented * by this library? Used to determine compatibility. */ -#define MERCHANT_PROTOCOL_CURRENT 4 +#define MERCHANT_PROTOCOL_CURRENT 5 /** * How many configs are we backwards compatible with? diff --git a/src/lib/merchant_api_get_reserve.c b/src/lib/merchant_api_get_reserve.c index b1c32cef..355008b0 100644 --- a/src/lib/merchant_api_get_reserve.c +++ b/src/lib/merchant_api_get_reserve.c @@ -96,7 +96,7 @@ handle_reserve_get_finished (void *cls, { struct TALER_MERCHANT_ReserveSummary *rs = &rgr.details.ok.rs; - const json_t *tips = NULL; + const json_t *rewards = NULL; const json_t *accounts = NULL; unsigned int accounts_len; struct GNUNET_JSON_Specification spec[] = { @@ -107,8 +107,8 @@ handle_reserve_get_finished (void *cls, GNUNET_JSON_spec_bool ("active", &rgr.details.ok.active), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("tips", - &tips), + GNUNET_JSON_spec_array_const ("rewards", + &rewards), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_array_const ("accounts", @@ -134,7 +134,7 @@ handle_reserve_get_finished (void *cls, GNUNET_JSON_spec_end () }; struct TALER_EXCHANGE_WireAccount *was = NULL; - struct TALER_MERCHANT_TipDetails *tds = NULL; + struct TALER_MERCHANT_RewardDetails *tds = NULL; if (GNUNET_OK != GNUNET_JSON_parse (json, @@ -169,20 +169,20 @@ handle_reserve_get_finished (void *cls, } } /* end 'have accounts' */ - if (NULL != tips) + if (NULL != rewards) { - size_t tds_length = json_array_size (tips); + size_t tds_length = json_array_size (rewards); bool ok = true; - json_t *tip; + json_t *reward; unsigned int i; tds = GNUNET_new_array (tds_length, - struct TALER_MERCHANT_TipDetails); - json_array_foreach (tips, i, tip) { - struct TALER_MERCHANT_TipDetails *td = &tds[i]; + struct TALER_MERCHANT_RewardDetails); + json_array_foreach (rewards, i, reward) { + struct TALER_MERCHANT_RewardDetails *td = &tds[i]; struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ("tip_id", - &td->tip_id), + GNUNET_JSON_spec_fixed_auto ("reward_id", + &td->reward_id), TALER_JSON_spec_amount_any ("total_amount", &td->amount), GNUNET_JSON_spec_string ("reason", @@ -191,7 +191,7 @@ handle_reserve_get_finished (void *cls, }; if (GNUNET_OK != - GNUNET_JSON_parse (tip, + GNUNET_JSON_parse (reward, ispec, NULL, NULL)) { @@ -208,10 +208,10 @@ handle_reserve_get_finished (void *cls, } else { - rgr.details.ok.tips = tds; - rgr.details.ok.tips_length = tds_length; + rgr.details.ok.rewards = tds; + rgr.details.ok.rewards_length = tds_length; } - } /* end 'have tips' */ + } /* end 'have rewards' */ rgh->cb (rgh->cb_cls, &rgr); @@ -258,7 +258,7 @@ struct TALER_MERCHANT_ReserveGetHandle * TALER_MERCHANT_reserve_get (struct GNUNET_CURL_Context *ctx, const char *backend_url, const struct TALER_ReservePublicKeyP *reserve_pub, - bool fetch_tips, + bool fetch_rewards, TALER_MERCHANT_ReserveGetCallback cb, void *cb_cls) { @@ -285,8 +285,8 @@ TALER_MERCHANT_reserve_get (struct GNUNET_CURL_Context *ctx, res_str); rgh->url = TALER_url_join (backend_url, arg_str, - "tips", - fetch_tips ? "yes" : "no", + "rewards", + fetch_rewards ? "yes" : "no", NULL); } if (NULL == rgh->url) diff --git a/src/lib/merchant_api_get_rewards.c b/src/lib/merchant_api_get_rewards.c new file mode 100644 index 00000000..96ec4d34 --- /dev/null +++ b/src/lib/merchant_api_get_rewards.c @@ -0,0 +1,288 @@ +/* + This file is part of TALER + Copyright (C) 2020-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 published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + +*/ +/** + * @file merchant_api_get_rewards.c + * @brief Implementation of the GET /private/rewards request of the merchant's HTTP API + * @author Jonathan Buchanan + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include +#include + + +/** + * Handle for a GET /private/rewards operation. + */ +struct TALER_MERCHANT_RewardsGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_RewardsGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse reward information from @a ia. + * + * @param ia JSON array (or NULL!) reward order data + * @param[in] tgr response to complete + * @param tgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_rewards (const json_t *ia, + struct TALER_MERCHANT_RewardsGetResponse *tgr, + struct TALER_MERCHANT_RewardsGetHandle *tgh) +{ + unsigned int tes_len = json_array_size (ia); + struct TALER_MERCHANT_RewardEntry tes[GNUNET_NZL (tes_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_RewardEntry *ie = &tes[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint64 ("row_id", + &ie->row_id), + GNUNET_JSON_spec_fixed_auto ("reward_id", + &ie->reward_id), + TALER_JSON_spec_amount_any ("reward_amount", + &ie->reward_amount), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.rewards_length = tes_len; + tgr->details.ok.rewards = tes; + tgh->cb (tgh->cb_cls, + tgr); + tgh->cb = NULL; /* just to be sure */ + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP GET /private/rewards request. + * + * @param cls the `struct TALER_MERCHANT_RewardsGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_rewards_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_RewardsGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_RewardsGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /private/rewards response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *rewards; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("rewards", + &rewards), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_rewards (rewards, + &tgr, + tgh)) + { + TALER_MERCHANT_rewards_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_rewards_get_cancel (tgh); +} + + +struct TALER_MERCHANT_RewardsGetHandle * +TALER_MERCHANT_rewards_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_RewardsGetCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_rewards_get2 (ctx, + backend_url, + TALER_EXCHANGE_YNA_NO, + -20, + UINT64_MAX, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_RewardsGetHandle * +TALER_MERCHANT_rewards_get2 (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + enum TALER_EXCHANGE_YesNoAll expired, + int64_t limit, + uint64_t offset, + TALER_MERCHANT_RewardsGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_RewardsGetHandle *tgh; + CURL *eh; + + GNUNET_assert (NULL != backend_url); + if (0 == limit) + { + GNUNET_break (0); + return NULL; + } + tgh = GNUNET_new (struct TALER_MERCHANT_RewardsGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + + /* build tgh->url with the various optional arguments */ + { + char cbuf[30]; + char lbuf[30]; + bool have_offset; + + GNUNET_snprintf (lbuf, + sizeof (lbuf), + "%lld", + (long long) limit); + + if (limit > 0) + have_offset = (0 != offset); + else + have_offset = (UINT64_MAX != offset); + GNUNET_snprintf (cbuf, + sizeof (cbuf), + "%llu", + (unsigned long long) offset); + tgh->url = TALER_url_join (backend_url, + "private/rewards", + "expired", + (TALER_EXCHANGE_YNA_ALL != expired) + ? TALER_yna_to_string (expired) + : NULL, + "offset", (have_offset) ? cbuf : NULL, + "limit", (-20 != limit) ? lbuf : NULL, + NULL); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_rewards_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_rewards_get_cancel ( + struct TALER_MERCHANT_RewardsGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_tips.c b/src/lib/merchant_api_get_tips.c deleted file mode 100644 index 4103c071..00000000 --- a/src/lib/merchant_api_get_tips.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-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 published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - -*/ -/** - * @file merchant_api_get_tips.c - * @brief Implementation of the GET /private/tips request of the merchant's HTTP API - * @author Jonathan Buchanan - */ -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include -#include - - -/** - * Handle for a GET /private/tips operation. - */ -struct TALER_MERCHANT_TipsGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipsGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse tip information from @a ia. - * - * @param ia JSON array (or NULL!) tip order data - * @param[in] tgr response to complete - * @param tgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_tips (const json_t *ia, - struct TALER_MERCHANT_TipsGetResponse *tgr, - struct TALER_MERCHANT_TipsGetHandle *tgh) -{ - unsigned int tes_len = json_array_size (ia); - struct TALER_MERCHANT_TipEntry tes[GNUNET_NZL (tes_len)]; - size_t index; - json_t *value; - - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_TipEntry *ie = &tes[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint64 ("row_id", - &ie->row_id), - GNUNET_JSON_spec_fixed_auto ("tip_id", - &ie->tip_id), - TALER_JSON_spec_amount_any ("tip_amount", - &ie->tip_amount), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - tgr->details.ok.tips_length = tes_len; - tgr->details.ok.tips = tes; - tgh->cb (tgh->cb_cls, - tgr); - tgh->cb = NULL; /* just to be sure */ - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP GET /private/tips request. - * - * @param cls the `struct TALER_MERCHANT_TipsGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_tips_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipsGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_TipsGetResponse tgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /private/tips response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *tips; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("tips", - &tips), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_tips (tips, - &tgr, - tgh)) - { - TALER_MERCHANT_tips_get_cancel (tgh); - return; - } - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_tips_get_cancel (tgh); -} - - -struct TALER_MERCHANT_TipsGetHandle * -TALER_MERCHANT_tips_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_TipsGetCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_tips_get2 (ctx, - backend_url, - TALER_EXCHANGE_YNA_NO, - -20, - UINT64_MAX, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_TipsGetHandle * -TALER_MERCHANT_tips_get2 (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - enum TALER_EXCHANGE_YesNoAll expired, - int64_t limit, - uint64_t offset, - TALER_MERCHANT_TipsGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TipsGetHandle *tgh; - CURL *eh; - - GNUNET_assert (NULL != backend_url); - if (0 == limit) - { - GNUNET_break (0); - return NULL; - } - tgh = GNUNET_new (struct TALER_MERCHANT_TipsGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - - /* build tgh->url with the various optional arguments */ - { - char cbuf[30]; - char lbuf[30]; - bool have_offset; - - GNUNET_snprintf (lbuf, - sizeof (lbuf), - "%lld", - (long long) limit); - - if (limit > 0) - have_offset = (0 != offset); - else - have_offset = (UINT64_MAX != offset); - GNUNET_snprintf (cbuf, - sizeof (cbuf), - "%llu", - (unsigned long long) offset); - tgh->url = TALER_url_join (backend_url, - "private/tips", - "expired", - (TALER_EXCHANGE_YNA_ALL != expired) - ? TALER_yna_to_string (expired) - : NULL, - "offset", (have_offset) ? cbuf : NULL, - "limit", (-20 != limit) ? lbuf : NULL, - NULL); - } - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_tips_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_tips_get_cancel ( - struct TALER_MERCHANT_TipsGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_merchant_get_reward.c b/src/lib/merchant_api_merchant_get_reward.c new file mode 100644 index 00000000..ba7306c0 --- /dev/null +++ b/src/lib/merchant_api_merchant_get_reward.c @@ -0,0 +1,302 @@ +/* + This file is part of TALER + Copyright (C) 2020 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 published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + +*/ +/** + * @file merchant_api_merchant_get_reward.c + * @brief Implementation of the GET /private/rewards/$REWARD_ID request of the merchant's HTTP API + * @author Jonathan Buchanan + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include +#include "taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include +#include + + +struct TALER_MERCHANT_RewardMerchantGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_RewardMerchantGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +static enum GNUNET_GenericReturnValue +parse_pickups (const json_t *pa, + struct TALER_MERCHANT_RewardStatusResponse *tsr, + struct TALER_MERCHANT_RewardMerchantGetHandle *tgh) +{ + unsigned int pa_len = json_array_size (pa); + struct TALER_MERCHANT_PickupDetail pickups[pa_len]; + size_t index; + json_t *value; + + json_array_foreach (pa, index, value) + { + struct TALER_MERCHANT_PickupDetail *pickup = &pickups[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("pickup_id", + &pickup->pickup_id), + GNUNET_JSON_spec_uint64 ("num_planchets", + &pickup->num_planchets), + TALER_JSON_spec_amount_any ("requested_amount", + &pickup->requested_amount), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tsr->details.ok.pickups_length = pa_len; + tsr->details.ok.pickups = pickups; + tgh->cb (tgh->cb_cls, + tsr); + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * GET /private/rewards/$REWARD_ID request. + * + * @param cls the `struct TALER_MERCHANT_RewardMerchantGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_merchant_reward_get_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_RewardMerchantGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_RewardStatusResponse tsr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /private/rewards/$REWARD_ID response with status code %u\n", + (unsigned int) response_code); + tgh->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount_any ("total_authorized", + &tsr.details.ok.total_authorized), + TALER_JSON_spec_amount_any ("total_picked_up", + &tsr.details.ok.total_picked_up), + GNUNET_JSON_spec_string ("reason", + &tsr.details.ok.reason), + GNUNET_JSON_spec_timestamp ("expiration", + &tsr.details.ok.expiration), + GNUNET_JSON_spec_fixed_auto ("reserve_pub", + &tsr.details.ok.reserve_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tsr.hr.http_status = 0; + tsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + } + else + { + json_t *pickups = json_object_get (json, + "pickups"); + if (! json_is_array (pickups)) + { + tgh->cb (tgh->cb_cls, + &tsr); + TALER_MERCHANT_merchant_reward_get_cancel (tgh); + return; + } + if (GNUNET_OK == + parse_pickups (pickups, + &tsr, + tgh)) + { + GNUNET_JSON_parse_free (spec); + TALER_MERCHANT_merchant_reward_get_cancel (tgh); + return; + } + tsr.hr.http_status = 0; + tsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + } + GNUNET_JSON_parse_free (spec); + break; + } + case MHD_HTTP_UNAUTHORIZED: + tsr.hr.ec = TALER_JSON_get_error_code (json); + tsr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + /* legal, can happen if instance or reward reserve is unknown */ + tsr.hr.ec = TALER_JSON_get_error_code (json); + tsr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + tsr.hr.ec = TALER_JSON_get_error_code (json); + tsr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &tsr.hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tsr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tsr); + TALER_MERCHANT_merchant_reward_get_cancel (tgh); +} + + +struct TALER_MERCHANT_RewardMerchantGetHandle * +TALER_MERCHANT_merchant_reward_get (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_RewardIdentifierP *reward_id, + const struct TALER_Amount *min_pick_up, + struct GNUNET_TIME_Relative lp_timeout, + bool pickups, + TALER_MERCHANT_RewardMerchantGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_RewardMerchantGetHandle *tgh; + CURL *eh; + + GNUNET_assert (NULL != backend_url); + tgh = GNUNET_new (struct TALER_MERCHANT_RewardMerchantGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + + { + char res_str[sizeof (*reward_id) * 2]; + char arg_str[sizeof (res_str) + 48]; + char timeout_str[32]; + char *end; + + end = GNUNET_STRINGS_data_to_string (reward_id, + sizeof (*reward_id), + res_str, + sizeof (res_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "private/rewards/%s", + res_str); + GNUNET_snprintf (timeout_str, + sizeof (timeout_str), + "%llu", + ((unsigned long long) + lp_timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us)); + tgh->url = TALER_url_join (backend_url, + arg_str, + "pickups", + pickups + ? "yes" + : NULL, + "min_amount", + min_pick_up + ? TALER_amount2s (min_pick_up) + : NULL, + "timeout_ms", + GNUNET_TIME_relative_is_zero (lp_timeout) + ? NULL + : timeout_str, + NULL); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_merchant_reward_get_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_merchant_reward_get_cancel ( + struct TALER_MERCHANT_RewardMerchantGetHandle *tgh) +{ + if (NULL != tgh->job) + { + GNUNET_CURL_job_cancel (tgh->job); + tgh->job = NULL; + } + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} + + +/* end of merchant_api_merchant_get_reward.c */ diff --git a/src/lib/merchant_api_merchant_get_tip.c b/src/lib/merchant_api_merchant_get_tip.c deleted file mode 100644 index 59c4b588..00000000 --- a/src/lib/merchant_api_merchant_get_tip.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 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 published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - -*/ -/** - * @file merchant_api_merchant_get_tip.c - * @brief Implementation of the GET /private/tips/$TIP_ID request of the merchant's HTTP API - * @author Jonathan Buchanan - */ -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include -#include "taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include -#include - - -struct TALER_MERCHANT_TipMerchantGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipMerchantGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -static enum GNUNET_GenericReturnValue -parse_pickups (const json_t *pa, - struct TALER_MERCHANT_TipStatusResponse *tsr, - struct TALER_MERCHANT_TipMerchantGetHandle *tgh) -{ - unsigned int pa_len = json_array_size (pa); - struct TALER_MERCHANT_PickupDetail pickups[pa_len]; - size_t index; - json_t *value; - - json_array_foreach (pa, index, value) - { - struct TALER_MERCHANT_PickupDetail *pickup = &pickups[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("pickup_id", - &pickup->pickup_id), - GNUNET_JSON_spec_uint64 ("num_planchets", - &pickup->num_planchets), - TALER_JSON_spec_amount_any ("requested_amount", - &pickup->requested_amount), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - tsr->details.ok.pickups_length = pa_len; - tsr->details.ok.pickups = pickups; - tgh->cb (tgh->cb_cls, - tsr); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * GET /private/tips/$TIP_ID request. - * - * @param cls the `struct TALER_MERCHANT_TipMerchantGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_merchant_tip_get_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipMerchantGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_TipStatusResponse tsr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /private/tips/$TIP_ID response with status code %u\n", - (unsigned int) response_code); - tgh->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ("total_authorized", - &tsr.details.ok.total_authorized), - TALER_JSON_spec_amount_any ("total_picked_up", - &tsr.details.ok.total_picked_up), - GNUNET_JSON_spec_string ("reason", - &tsr.details.ok.reason), - GNUNET_JSON_spec_timestamp ("expiration", - &tsr.details.ok.expiration), - GNUNET_JSON_spec_fixed_auto ("reserve_pub", - &tsr.details.ok.reserve_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tsr.hr.http_status = 0; - tsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } - else - { - json_t *pickups = json_object_get (json, - "pickups"); - if (! json_is_array (pickups)) - { - tgh->cb (tgh->cb_cls, - &tsr); - TALER_MERCHANT_merchant_tip_get_cancel (tgh); - return; - } - if (GNUNET_OK == - parse_pickups (pickups, - &tsr, - tgh)) - { - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_merchant_tip_get_cancel (tgh); - return; - } - tsr.hr.http_status = 0; - tsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } - GNUNET_JSON_parse_free (spec); - break; - } - case MHD_HTTP_UNAUTHORIZED: - tsr.hr.ec = TALER_JSON_get_error_code (json); - tsr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* legal, can happen if instance or tip reserve is unknown */ - tsr.hr.ec = TALER_JSON_get_error_code (json); - tsr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - tsr.hr.ec = TALER_JSON_get_error_code (json); - tsr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &tsr.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tsr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tsr); - TALER_MERCHANT_merchant_tip_get_cancel (tgh); -} - - -struct TALER_MERCHANT_TipMerchantGetHandle * -TALER_MERCHANT_merchant_tip_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_TipIdentifierP *tip_id, - const struct TALER_Amount *min_pick_up, - struct GNUNET_TIME_Relative lp_timeout, - bool pickups, - TALER_MERCHANT_TipMerchantGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TipMerchantGetHandle *tgh; - CURL *eh; - - GNUNET_assert (NULL != backend_url); - tgh = GNUNET_new (struct TALER_MERCHANT_TipMerchantGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - - { - char res_str[sizeof (*tip_id) * 2]; - char arg_str[sizeof (res_str) + 48]; - char timeout_str[32]; - char *end; - - end = GNUNET_STRINGS_data_to_string (tip_id, - sizeof (*tip_id), - res_str, - sizeof (res_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "private/tips/%s", - res_str); - GNUNET_snprintf (timeout_str, - sizeof (timeout_str), - "%llu", - ((unsigned long long) - lp_timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us)); - tgh->url = TALER_url_join (backend_url, - arg_str, - "pickups", - pickups - ? "yes" - : NULL, - "min_amount", - min_pick_up - ? TALER_amount2s (min_pick_up) - : NULL, - "timeout_ms", - GNUNET_TIME_relative_is_zero (lp_timeout) - ? NULL - : timeout_str, - NULL); - } - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_merchant_tip_get_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_merchant_tip_get_cancel ( - struct TALER_MERCHANT_TipMerchantGetHandle *tgh) -{ - if (NULL != tgh->job) - { - GNUNET_CURL_job_cancel (tgh->job); - tgh->job = NULL; - } - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} - - -/* end of merchant_api_merchant_get_tip.c */ diff --git a/src/lib/merchant_api_reward_authorize.c b/src/lib/merchant_api_reward_authorize.c new file mode 100644 index 00000000..b250031b --- /dev/null +++ b/src/lib/merchant_api_reward_authorize.c @@ -0,0 +1,371 @@ +/* + This file is part of TALER + 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 published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + +*/ +/** + * @file merchant_api_reward_authorize.c + * @brief Implementation of the /reward-authorize request of the merchant's HTTP API + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include +#include +#include + + +/** + * @brief A handle for reward authorizations. + */ +struct TALER_MERCHANT_RewardAuthorizeHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_RewardAuthorizeCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * We got a 200 response back from the exchange (or the merchant). + * Now we need to parse the response and if it is well-formed, + * call the callback (and set it to NULL afterwards). + * + * @param tao handle of the original authorization operation + * @param json cryptographic proof returned by the exchange/merchant + * @return #GNUNET_OK if response is valid + */ +static enum GNUNET_GenericReturnValue +check_ok (struct TALER_MERCHANT_RewardAuthorizeHandle *tao, + const json_t *json) +{ + const char *reward_status_url; + struct TALER_MERCHANT_RewardAuthorizeResponse tar = { + .hr.http_status = MHD_HTTP_OK, + .hr.reply = json + }; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("reward_status_url", + &reward_status_url), + GNUNET_JSON_spec_string ("taler_reward_uri", + &tar.details.ok.reward_uri), + GNUNET_JSON_spec_timestamp ("reward_expiration", + &tar.details.ok.reward_expiration), + GNUNET_JSON_spec_fixed_auto ("reward_id", + &tar.details.ok.reward_id), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + char *log; + + GNUNET_break_op (0); + log = json_dumps (json, + 0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "JSON %s\n", + log); + free (log); + return GNUNET_SYSERR; + } + tao->cb (tao->cb_cls, + &tar); + tao->cb = NULL; /* do not call twice */ + GNUNET_JSON_parse_free (spec); + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /reservers/$REWARD_ID/reward-authorize request. + * + * @param cls the `struct TALER_MERCHANT_RewardAuthorizeHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_reward_authorize_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_RewardAuthorizeHandle *tao = cls; + const json_t *json = response; + struct TALER_MERCHANT_RewardAuthorizeResponse tar = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tao->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + if (GNUNET_OK == + check_ok (tao, + json)) + { + TALER_MERCHANT_reward_authorize_cancel (tao); + return; + } + GNUNET_break_op (0); + tar.hr.http_status = 0; + tar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_UNAUTHORIZED: + tar.hr.ec = TALER_JSON_get_error_code (json); + tar.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + /* Well-defined status code, pass on to application! */ + tar.hr.ec = TALER_JSON_get_error_code (json); + tar.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_PRECONDITION_FAILED: + /* Well-defined status code, pass on to application! */ + tar.hr.ec = TALER_JSON_get_error_code (json); + tar.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + tar.hr.ec = TALER_JSON_get_error_code (json); + tar.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_SERVICE_UNAVAILABLE: + /* Server had an unclear (internal or external) issue; we should retry, + but this API leaves this to the application */ + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &tar.hr); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &tar.hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tar.hr.ec); + break; + } + if (NULL != tao->cb) + tao->cb (tao->cb_cls, + &tar); + TALER_MERCHANT_reward_authorize_cancel (tao); +} + + +struct TALER_MERCHANT_RewardAuthorizeHandle * +TALER_MERCHANT_reward_authorize2 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_ReservePublicKeyP *reserve_pub, + const char *next_url, + const struct TALER_Amount *amount, + const char *justification, + TALER_MERCHANT_RewardAuthorizeCallback authorize_cb, + void *authorize_cb_cls) +{ + struct TALER_MERCHANT_RewardAuthorizeHandle *tao; + CURL *eh; + json_t *te_obj; + + tao = GNUNET_new (struct TALER_MERCHANT_RewardAuthorizeHandle); + tao->ctx = ctx; + tao->cb = authorize_cb; + tao->cb_cls = authorize_cb_cls; + + { + char res_str[sizeof (*reserve_pub) * 2]; + char arg_str[sizeof (res_str) + 48]; + char *end; + + end = GNUNET_STRINGS_data_to_string (reserve_pub, + sizeof (*reserve_pub), + res_str, + sizeof (res_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "private/reserves/%s/authorize-reward", + res_str); + tao->url = TALER_url_join (backend_url, + arg_str, + NULL); + } + if (NULL == tao->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tao); + return NULL; + } + te_obj = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("amount", + amount), + GNUNET_JSON_pack_string ("justification", + justification), + GNUNET_JSON_pack_string ("next_url", + next_url)); + eh = curl_easy_init (); + GNUNET_assert (NULL != eh); + if (GNUNET_OK != + TALER_curl_easy_post (&tao->post_ctx, + eh, + te_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (te_obj); + GNUNET_free (tao->url); + GNUNET_free (tao); + return NULL; + } + + json_decref (te_obj); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tao->url); + GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, + CURLOPT_URL, + tao->url)); + + tao->job = GNUNET_CURL_job_add2 (ctx, + eh, + tao->post_ctx.headers, + &handle_reward_authorize_finished, + tao); + return tao; +} + + +struct TALER_MERCHANT_RewardAuthorizeHandle * +TALER_MERCHANT_reward_authorize (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *next_url, + const struct TALER_Amount *amount, + const char *justification, + TALER_MERCHANT_RewardAuthorizeCallback authorize_cb, + void *authorize_cb_cls) +{ + struct TALER_MERCHANT_RewardAuthorizeHandle *tao; + CURL *eh; + json_t *te_obj; + + tao = GNUNET_new (struct TALER_MERCHANT_RewardAuthorizeHandle); + tao->ctx = ctx; + tao->cb = authorize_cb; + tao->cb_cls = authorize_cb_cls; + + tao->url = TALER_url_join (backend_url, + "private/rewards", + NULL); + if (NULL == tao->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tao); + return NULL; + } + te_obj = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("amount", + amount), + GNUNET_JSON_pack_string ("justification", + justification), + GNUNET_JSON_pack_string ("next_url", + next_url)); + eh = TALER_MERCHANT_curl_easy_get_ (tao->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tao->post_ctx, + eh, + te_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (te_obj); + GNUNET_free (tao->url); + GNUNET_free (tao); + return NULL; + } + json_decref (te_obj); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tao->url); + tao->job = GNUNET_CURL_job_add2 (ctx, + eh, + tao->post_ctx.headers, + &handle_reward_authorize_finished, + tao); + return tao; +} + + +void +TALER_MERCHANT_reward_authorize_cancel ( + struct TALER_MERCHANT_RewardAuthorizeHandle *tao) +{ + if (NULL != tao->job) + { + GNUNET_CURL_job_cancel (tao->job); + tao->job = NULL; + } + TALER_curl_easy_post_finished (&tao->post_ctx); + GNUNET_free (tao->url); + GNUNET_free (tao); +} + + +/* end of merchant_api_reward_authorize.c */ diff --git a/src/lib/merchant_api_reward_pickup.c b/src/lib/merchant_api_reward_pickup.c new file mode 100644 index 00000000..1d884d48 --- /dev/null +++ b/src/lib/merchant_api_reward_pickup.c @@ -0,0 +1,440 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 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 published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + +*/ +/** + * @file merchant_api_reward_pickup.c + * @brief Implementation of the /reward-pickup request of the merchant's HTTP API + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include +#include "taler_merchant_service.h" +#include +#include +#include + + +/** + * Data we keep per planchet. + */ +struct PlanchetData +{ + /** + * Secrets of the planchet. + */ + struct TALER_PlanchetMasterSecretP ps; + + /** + * Denomination key we are withdrawing. + */ + struct TALER_EXCHANGE_DenomPublicKey pk; + + /** + * Hash of the public key of the coin we are signing. + */ + struct TALER_CoinPubHashP c_hash; + + /** + * Nonce used for @e csr request, if any. + */ + struct TALER_CsNonce nonce; + + /** + * Handle for a /csr request we may optionally need + * to trigger. + */ + struct TALER_EXCHANGE_CsRWithdrawHandle *csr; + + /** + * Handle for the /reward-pickup operation we are part of. + */ + struct TALER_MERCHANT_RewardPickupHandle *tp; + + /** + * Offset of this entry in the array. + */ + unsigned int off; +}; + + +/** + * Handle for a /reward-pickup operation. + */ +struct TALER_MERCHANT_RewardPickupHandle +{ + + /** + * Function to call with the result. + */ + TALER_MERCHANT_RewardPickupCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Handle for the actual (internal) withdraw operation. + */ + struct TALER_MERCHANT_RewardPickup2Handle *tpo2; + + /** + * Array of length @e num_planchets. + */ + struct PlanchetData *planchets; + + /** + * Array of length @e num_planchets. + */ + struct TALER_EXCHANGE_PrivateCoinDetails *pcds; + + /** + * Context for making HTTP requests. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * URL of the merchant backend. + */ + char *backend_url; + + /** + * ID of the reward we are picking up. + */ + struct TALER_RewardIdentifierP reward_id; + + /** + * Number of planchets/coins used for this operation. + */ + unsigned int num_planchets; + + /** + * Number of remaining active /csr-withdraw requests. + */ + unsigned int csr_active; +}; + + +/** + * Fail the pickup operation @a tp, returning @a ec. + * Also cancels @a tp. + * + * @param[in] tp operation to fail + * @param ec reason for the failure + */ +static void +fail_pickup (struct TALER_MERCHANT_RewardPickupHandle *tp, + enum TALER_ErrorCode ec) +{ + struct TALER_MERCHANT_PickupDetails pd = { + .hr.ec = ec + }; + + tp->cb (tp->cb_cls, + &pd); + TALER_MERCHANT_reward_pickup_cancel (tp); +} + + +/** + * Callback for a /reward-pickup request. Returns the result of the operation. + * Note that the client MUST still do the unblinding of the @a blind_sigs. + * + * @param cls closure, a `struct TALER_MERCHANT_RewardPickupHandle *` + * @param tpr response details + */ +static void +pickup_done_cb (void *cls, + const struct TALER_MERCHANT_RewardPickup2Response *tpr) +{ + struct TALER_MERCHANT_RewardPickupHandle *tp = cls; + struct TALER_MERCHANT_PickupDetails pd = { + .hr = tpr->hr + }; + + tp->tpo2 = NULL; + if (MHD_HTTP_OK != tpr->hr.http_status) + { + tp->cb (tp->cb_cls, + &pd); + TALER_MERCHANT_reward_pickup_cancel (tp); + return; + } + { + enum GNUNET_GenericReturnValue ok = GNUNET_OK; + + for (unsigned int i = 0; idetails.ok.num_blind_sigs; i++) + { + const struct TALER_BlindedDenominationSignature *blind_sig + = &tpr->details.ok.blind_sigs[i]; + struct TALER_EXCHANGE_PrivateCoinDetails *pcd + = &tp->pcds[i]; + struct TALER_FreshCoin fc; + + if (GNUNET_OK != + TALER_planchet_to_coin (&tp->planchets[i].pk.key, + blind_sig, + &pcd->bks, + &pcd->coin_priv, + NULL, + &tp->planchets[i].c_hash, + &pcd->exchange_vals, + &fc)) + { + ok = GNUNET_SYSERR; + break; + } + pcd->sig = fc.sig; + } + if (GNUNET_OK != ok) + { + pd.hr.ec = TALER_EC_MERCHANT_REWARD_PICKUP_UNBLIND_FAILURE; + } + else + { + pd.details.ok.num_sigs = tpr->details.ok.num_blind_sigs; + pd.details.ok.pcds = tp->pcds; + } + tp->cb (tp->cb_cls, + &pd); + } + TALER_MERCHANT_reward_pickup_cancel (tp); +} + + +/** + * We have obtained all of the exchange inputs. Continue the pickup. + * + * @param[in,out] tp operation to continue + */ +static void +pickup_post_csr (struct TALER_MERCHANT_RewardPickupHandle *tp) +{ + struct TALER_PlanchetDetail details[tp->num_planchets]; + + for (unsigned int i = 0; inum_planchets; i++) + { + const struct PlanchetData *pd = &tp->planchets[i]; + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; + + TALER_planchet_setup_coin_priv (&pd->ps, + &pcd->exchange_vals, + &pcd->coin_priv); + TALER_planchet_blinding_secret_create (&pd->ps, + &pcd->exchange_vals, + &pcd->bks); + if (TALER_DENOMINATION_CS == pcd->exchange_vals.cipher) + { + details[i].blinded_planchet.details.cs_blinded_planchet.nonce + = pd->nonce; + } + if (GNUNET_OK != + TALER_planchet_prepare (&pd->pk.key, + &pcd->exchange_vals, + &pcd->bks, + &pcd->coin_priv, + NULL, + &tp->planchets[i].c_hash, + &details[i])) + { + GNUNET_break (0); + for (unsigned int j = 0; jtpo2 = TALER_MERCHANT_reward_pickup2 (tp->ctx, + tp->backend_url, + &tp->reward_id, + tp->num_planchets, + details, + &pickup_done_cb, + tp); + for (unsigned int j = 0; jnum_planchets; j++) + TALER_planchet_detail_free (&details[j]); + if (NULL == tp->tpo2) + { + GNUNET_break (0); + fail_pickup (tp, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE); + return; + } +} + + +/** + * Callbacks of this type are used to serve the result of submitting a + * CS R request to a exchange. + * + * @param cls a `struct TALER_MERCHANT_RewardPickupHandle` + * @param csrr response details + */ +static void +csr_cb (void *cls, + const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr) +{ + struct PlanchetData *pd = cls; + struct TALER_MERCHANT_RewardPickupHandle *tp = pd->tp; + + pd->csr = NULL; + tp->csr_active--; + switch (csrr->hr.http_status) + { + case MHD_HTTP_OK: + { + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[pd->off]; + + pcd->exchange_vals = csrr->details.ok.alg_values; + } + if (0 != tp->csr_active) + return; + pickup_post_csr (tp); + return; + default: + { + struct TALER_MERCHANT_PickupDetails pd = { + .hr.hint = "/csr-withdraw failed", + .hr.exchange_http_status = csrr->hr.http_status + }; + + tp->cb (tp->cb_cls, + &pd); + TALER_MERCHANT_reward_pickup_cancel (tp); + return; + } + } +} + + +struct TALER_MERCHANT_RewardPickupHandle * +TALER_MERCHANT_reward_pickup ( + struct GNUNET_CURL_Context *ctx, + const char *exchange_url, + const char *backend_url, + const struct TALER_RewardIdentifierP *reward_id, + unsigned int num_planchets, + const struct TALER_MERCHANT_PlanchetData pds[static num_planchets], + TALER_MERCHANT_RewardPickupCallback pickup_cb, + void *pickup_cb_cls) +{ + struct TALER_MERCHANT_RewardPickupHandle *tp; + + if (0 == num_planchets) + { + GNUNET_break (0); + return NULL; + } + tp = GNUNET_new (struct TALER_MERCHANT_RewardPickupHandle); + tp->cb = pickup_cb; + tp->cb_cls = pickup_cb_cls; + tp->ctx = ctx; + tp->backend_url = GNUNET_strdup (backend_url); + tp->reward_id = *reward_id; + tp->num_planchets = num_planchets; + tp->planchets = GNUNET_new_array (num_planchets, + struct PlanchetData); + tp->pcds = GNUNET_new_array (num_planchets, + struct TALER_EXCHANGE_PrivateCoinDetails); + for (unsigned int i = 0; ipk; + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; + struct PlanchetData *pd = &tp->planchets[i]; + + pd->off = i; + pd->tp = tp; + tp->planchets[i].ps = mpd->ps; + tp->planchets[i].pk = *pds[i].pk; + TALER_denom_pub_deep_copy (&tp->planchets[i].pk.key, + &pds[i].pk->key); + switch (pk->key.cipher) + { + case TALER_DENOMINATION_RSA: + pcd->exchange_vals.cipher = TALER_DENOMINATION_RSA; + break; + case TALER_DENOMINATION_CS: + { + TALER_cs_withdraw_nonce_derive (&pd->ps, + &pd->nonce); + pd->csr = TALER_EXCHANGE_csr_withdraw (ctx, + exchange_url, + &pd->pk, + &pd->nonce, + &csr_cb, + pd); + if (NULL == pd->csr) + { + GNUNET_break (0); + TALER_MERCHANT_reward_pickup_cancel (tp); + return NULL; + } + tp->csr_active++; + break; + } + default: + GNUNET_break (0); + TALER_MERCHANT_reward_pickup_cancel (tp); + return NULL; + } + } + if (0 == tp->csr_active) + { + pickup_post_csr (tp); + return tp; + } + return tp; +} + + +void +TALER_MERCHANT_reward_pickup_cancel (struct TALER_MERCHANT_RewardPickupHandle *tp) +{ + for (unsigned int i = 0; inum_planchets; i++) + { + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; + struct PlanchetData *pd = &tp->planchets[i]; + + TALER_denom_sig_free (&pcd->sig); + TALER_denom_pub_free (&tp->planchets[i].pk.key); + if (NULL != pd->csr) + { + TALER_EXCHANGE_csr_withdraw_cancel (pd->csr); + pd->csr = NULL; + } + } + GNUNET_array_grow (tp->planchets, + tp->num_planchets, + 0); + if (NULL != tp->tpo2) + { + TALER_MERCHANT_reward_pickup2_cancel (tp->tpo2); + tp->tpo2 = NULL; + } + GNUNET_free (tp->backend_url); + GNUNET_free (tp->pcds); + GNUNET_free (tp); +} + + +/* end of merchant_api_reward_pickup.c */ diff --git a/src/lib/merchant_api_reward_pickup2.c b/src/lib/merchant_api_reward_pickup2.c new file mode 100644 index 00000000..397f1a2b --- /dev/null +++ b/src/lib/merchant_api_reward_pickup2.c @@ -0,0 +1,355 @@ +/* + This file is part of TALER + 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 published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + +*/ +/** + * @file merchant_api_reward_pickup2.c + * @brief Implementation of the /reward-pickup request of the merchant's HTTP API + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include +#include +#include + + +/** + * @brief A handle for tracking transactions. + */ +struct TALER_MERCHANT_RewardPickup2Handle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_RewardPickup2Callback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Expected number of planchets. + */ + unsigned int num_planchets; +}; + + +/** + * We got a 200 response back from the exchange (or the merchant). + * Now we need to parse the response and if it is well-formed, + * call the callback (and set it to NULL afterwards). + * + * @param tpo handle of the original authorization operation + * @param[in] tpr response to complete + * @param json cryptographic proof returned by the exchange/merchant + * @return #GNUNET_OK if response is valid + */ +static enum GNUNET_GenericReturnValue +check_ok (struct TALER_MERCHANT_RewardPickup2Handle *tpo, + struct TALER_MERCHANT_RewardPickup2Response *tpr, + const json_t *json) +{ + const json_t *ja; + unsigned int ja_len; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("blind_sigs", + &ja), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + ja_len = json_array_size (ja); + if (ja_len != tpo->num_planchets) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + { + struct TALER_BlindedDenominationSignature mblind_sigs[GNUNET_NZL (ja_len)]; + + for (unsigned int i = 0; idetails.ok.num_blind_sigs = ja_len; + tpr->details.ok.blind_sigs = mblind_sigs; + tpo->cb (tpo->cb_cls, + tpr); + tpo->cb = NULL; /* do not call twice */ + for (unsigned int i = 0; ijob = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + if (GNUNET_OK != + check_ok (tpo, + &tpr, + json)) + { + GNUNET_break_op (0); + tpr.hr.http_status = 0; + tpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + break; + case MHD_HTTP_BAD_REQUEST: + /* Can happen if we pickup an amount that exceeds the reward... */ + tpr.hr.ec = TALER_JSON_get_error_code (json); + tpr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break (TALER_EC_MERCHANT_REWARD_PICKUP_AMOUNT_EXCEEDS_REWARD_REMAINING == + tpr.hr.ec); + break; + case MHD_HTTP_CONFLICT: + /* legal, can happen if we pickup a reward twice... */ + tpr.hr.ec = TALER_JSON_get_error_code (json); + tpr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + /* legal, can happen if reward ID is unknown */ + tpr.hr.ec = TALER_JSON_get_error_code (json); + tpr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + tpr.hr.ec = TALER_JSON_get_error_code (json); + tpr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &tpr.hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tpr.hr.ec); + break; + } + if (NULL != tpo->cb) + { + tpo->cb (tpo->cb_cls, + &tpr); + tpo->cb = NULL; + } + TALER_MERCHANT_reward_pickup2_cancel (tpo); +} + + +struct TALER_MERCHANT_RewardPickup2Handle * +TALER_MERCHANT_reward_pickup2 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_RewardIdentifierP *reward_id, + unsigned int num_planchets, + const struct TALER_PlanchetDetail planchets[static num_planchets], + TALER_MERCHANT_RewardPickup2Callback pickup_cb, + void *pickup_cb_cls) +{ + struct TALER_MERCHANT_RewardPickup2Handle *tpo; + CURL *eh; + json_t *pa; + json_t *tp_obj; + + if (0 == num_planchets) + { + GNUNET_break (0); + return NULL; + } + pa = json_array (); + GNUNET_assert (NULL != pa); + for (unsigned int i = 0; idenom_pub_hash), + TALER_JSON_pack_blinded_planchet ("coin_ev", + &planchet->blinded_planchet)); + if (0 != + json_array_append_new (pa, + p)) + { + GNUNET_break (0); + json_decref (pa); + return NULL; + } + } + tp_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("planchets", + pa)); + tpo = GNUNET_new (struct TALER_MERCHANT_RewardPickup2Handle); + tpo->num_planchets = num_planchets; + tpo->ctx = ctx; + tpo->cb = pickup_cb; + tpo->cb_cls = pickup_cb_cls; + + { + char reward_str[sizeof (*reward_id) * 2]; + char arg_str[sizeof (reward_str) + 32]; + char *end; + + end = GNUNET_STRINGS_data_to_string (reward_id, + sizeof (*reward_id), + reward_str, + sizeof (reward_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "rewards/%s/pickup", + reward_str); + tpo->url = TALER_url_join (backend_url, + arg_str, + NULL); + } + if (NULL == tpo->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (tp_obj); + GNUNET_free (tpo); + return NULL; + } + eh = TALER_MERCHANT_curl_easy_get_ (tpo->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tpo->post_ctx, + eh, + tp_obj)) + { + GNUNET_break (0); + json_decref (tp_obj); + curl_easy_cleanup (eh); + GNUNET_free (tpo->url); + GNUNET_free (tpo); + return NULL; + } + json_decref (tp_obj); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tpo->url); + tpo->job = GNUNET_CURL_job_add2 (ctx, + eh, + tpo->post_ctx.headers, + &handle_reward_pickup_finished, + tpo); + if (NULL == tpo->job) + { + TALER_MERCHANT_reward_pickup2_cancel (tpo); + return NULL; + } + return tpo; +} + + +void +TALER_MERCHANT_reward_pickup2_cancel ( + struct TALER_MERCHANT_RewardPickup2Handle *tpo) +{ + if (NULL != tpo->job) + { + GNUNET_CURL_job_cancel (tpo->job); + tpo->job = NULL; + } + TALER_curl_easy_post_finished (&tpo->post_ctx); + GNUNET_free (tpo->url); + GNUNET_free (tpo); +} + + +/* end of merchant_api_reward_pickup2.c */ diff --git a/src/lib/merchant_api_tip_authorize.c b/src/lib/merchant_api_tip_authorize.c deleted file mode 100644 index 317023ac..00000000 --- a/src/lib/merchant_api_tip_authorize.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - This file is part of TALER - 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 published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - -*/ -/** - * @file merchant_api_tip_authorize.c - * @brief Implementation of the /tip-authorize request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include -#include -#include - - -/** - * @brief A handle for tip authorizations. - */ -struct TALER_MERCHANT_TipAuthorizeHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipAuthorizeCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * We got a 200 response back from the exchange (or the merchant). - * Now we need to parse the response and if it is well-formed, - * call the callback (and set it to NULL afterwards). - * - * @param tao handle of the original authorization operation - * @param json cryptographic proof returned by the exchange/merchant - * @return #GNUNET_OK if response is valid - */ -static enum GNUNET_GenericReturnValue -check_ok (struct TALER_MERCHANT_TipAuthorizeHandle *tao, - const json_t *json) -{ - const char *tip_status_url; - struct TALER_MERCHANT_TipAuthorizeResponse tar = { - .hr.http_status = MHD_HTTP_OK, - .hr.reply = json - }; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("tip_status_url", - &tip_status_url), - GNUNET_JSON_spec_string ("taler_tip_uri", - &tar.details.ok.tip_uri), - GNUNET_JSON_spec_timestamp ("tip_expiration", - &tar.details.ok.tip_expiration), - GNUNET_JSON_spec_fixed_auto ("tip_id", - &tar.details.ok.tip_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - char *log; - - GNUNET_break_op (0); - log = json_dumps (json, - 0); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "JSON %s\n", - log); - free (log); - return GNUNET_SYSERR; - } - tao->cb (tao->cb_cls, - &tar); - tao->cb = NULL; /* do not call twice */ - GNUNET_JSON_parse_free (spec); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /reservers/$TIP_ID/tip-authorize request. - * - * @param cls the `struct TALER_MERCHANT_TipAuthorizeHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_tip_authorize_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipAuthorizeHandle *tao = cls; - const json_t *json = response; - struct TALER_MERCHANT_TipAuthorizeResponse tar = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tao->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - if (GNUNET_OK == - check_ok (tao, - json)) - { - TALER_MERCHANT_tip_authorize_cancel (tao); - return; - } - GNUNET_break_op (0); - tar.hr.http_status = 0; - tar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_UNAUTHORIZED: - tar.hr.ec = TALER_JSON_get_error_code (json); - tar.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* Well-defined status code, pass on to application! */ - tar.hr.ec = TALER_JSON_get_error_code (json); - tar.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_PRECONDITION_FAILED: - /* Well-defined status code, pass on to application! */ - tar.hr.ec = TALER_JSON_get_error_code (json); - tar.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - tar.hr.ec = TALER_JSON_get_error_code (json); - tar.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_SERVICE_UNAVAILABLE: - /* Server had an unclear (internal or external) issue; we should retry, - but this API leaves this to the application */ - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &tar.hr); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &tar.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tar.hr.ec); - break; - } - if (NULL != tao->cb) - tao->cb (tao->cb_cls, - &tar); - TALER_MERCHANT_tip_authorize_cancel (tao); -} - - -struct TALER_MERCHANT_TipAuthorizeHandle * -TALER_MERCHANT_tip_authorize2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_ReservePublicKeyP *reserve_pub, - const char *next_url, - const struct TALER_Amount *amount, - const char *justification, - TALER_MERCHANT_TipAuthorizeCallback authorize_cb, - void *authorize_cb_cls) -{ - struct TALER_MERCHANT_TipAuthorizeHandle *tao; - CURL *eh; - json_t *te_obj; - - tao = GNUNET_new (struct TALER_MERCHANT_TipAuthorizeHandle); - tao->ctx = ctx; - tao->cb = authorize_cb; - tao->cb_cls = authorize_cb_cls; - - { - char res_str[sizeof (*reserve_pub) * 2]; - char arg_str[sizeof (res_str) + 48]; - char *end; - - end = GNUNET_STRINGS_data_to_string (reserve_pub, - sizeof (*reserve_pub), - res_str, - sizeof (res_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "private/reserves/%s/authorize-tip", - res_str); - tao->url = TALER_url_join (backend_url, - arg_str, - NULL); - } - if (NULL == tao->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tao); - return NULL; - } - te_obj = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("amount", - amount), - GNUNET_JSON_pack_string ("justification", - justification), - GNUNET_JSON_pack_string ("next_url", - next_url)); - eh = curl_easy_init (); - GNUNET_assert (NULL != eh); - if (GNUNET_OK != - TALER_curl_easy_post (&tao->post_ctx, - eh, - te_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (te_obj); - GNUNET_free (tao->url); - GNUNET_free (tao); - return NULL; - } - - json_decref (te_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tao->url); - GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, - CURLOPT_URL, - tao->url)); - - tao->job = GNUNET_CURL_job_add2 (ctx, - eh, - tao->post_ctx.headers, - &handle_tip_authorize_finished, - tao); - return tao; -} - - -struct TALER_MERCHANT_TipAuthorizeHandle * -TALER_MERCHANT_tip_authorize (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *next_url, - const struct TALER_Amount *amount, - const char *justification, - TALER_MERCHANT_TipAuthorizeCallback authorize_cb, - void *authorize_cb_cls) -{ - struct TALER_MERCHANT_TipAuthorizeHandle *tao; - CURL *eh; - json_t *te_obj; - - tao = GNUNET_new (struct TALER_MERCHANT_TipAuthorizeHandle); - tao->ctx = ctx; - tao->cb = authorize_cb; - tao->cb_cls = authorize_cb_cls; - - tao->url = TALER_url_join (backend_url, - "private/tips", - NULL); - if (NULL == tao->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tao); - return NULL; - } - te_obj = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("amount", - amount), - GNUNET_JSON_pack_string ("justification", - justification), - GNUNET_JSON_pack_string ("next_url", - next_url)); - eh = TALER_MERCHANT_curl_easy_get_ (tao->url); - if (GNUNET_OK != - TALER_curl_easy_post (&tao->post_ctx, - eh, - te_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (te_obj); - GNUNET_free (tao->url); - GNUNET_free (tao); - return NULL; - } - json_decref (te_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tao->url); - tao->job = GNUNET_CURL_job_add2 (ctx, - eh, - tao->post_ctx.headers, - &handle_tip_authorize_finished, - tao); - return tao; -} - - -void -TALER_MERCHANT_tip_authorize_cancel ( - struct TALER_MERCHANT_TipAuthorizeHandle *tao) -{ - if (NULL != tao->job) - { - GNUNET_CURL_job_cancel (tao->job); - tao->job = NULL; - } - TALER_curl_easy_post_finished (&tao->post_ctx); - GNUNET_free (tao->url); - GNUNET_free (tao); -} - - -/* end of merchant_api_tip_authorize.c */ diff --git a/src/lib/merchant_api_tip_pickup.c b/src/lib/merchant_api_tip_pickup.c deleted file mode 100644 index eb0e3fb3..00000000 --- a/src/lib/merchant_api_tip_pickup.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2022 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 published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - -*/ -/** - * @file merchant_api_tip_pickup.c - * @brief Implementation of the /tip-pickup request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include -#include "taler_merchant_service.h" -#include -#include -#include - - -/** - * Data we keep per planchet. - */ -struct PlanchetData -{ - /** - * Secrets of the planchet. - */ - struct TALER_PlanchetMasterSecretP ps; - - /** - * Denomination key we are withdrawing. - */ - struct TALER_EXCHANGE_DenomPublicKey pk; - - /** - * Hash of the public key of the coin we are signing. - */ - struct TALER_CoinPubHashP c_hash; - - /** - * Nonce used for @e csr request, if any. - */ - struct TALER_CsNonce nonce; - - /** - * Handle for a /csr request we may optionally need - * to trigger. - */ - struct TALER_EXCHANGE_CsRWithdrawHandle *csr; - - /** - * Handle for the /tip-pickup operation we are part of. - */ - struct TALER_MERCHANT_TipPickupHandle *tp; - - /** - * Offset of this entry in the array. - */ - unsigned int off; -}; - - -/** - * Handle for a /tip-pickup operation. - */ -struct TALER_MERCHANT_TipPickupHandle -{ - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipPickupCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Handle for the actual (internal) withdraw operation. - */ - struct TALER_MERCHANT_TipPickup2Handle *tpo2; - - /** - * Array of length @e num_planchets. - */ - struct PlanchetData *planchets; - - /** - * Array of length @e num_planchets. - */ - struct TALER_EXCHANGE_PrivateCoinDetails *pcds; - - /** - * Context for making HTTP requests. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * URL of the merchant backend. - */ - char *backend_url; - - /** - * ID of the tip we are picking up. - */ - struct TALER_TipIdentifierP tip_id; - - /** - * Number of planchets/coins used for this operation. - */ - unsigned int num_planchets; - - /** - * Number of remaining active /csr-withdraw requests. - */ - unsigned int csr_active; -}; - - -/** - * Fail the pickup operation @a tp, returning @a ec. - * Also cancels @a tp. - * - * @param[in] tp operation to fail - * @param ec reason for the failure - */ -static void -fail_pickup (struct TALER_MERCHANT_TipPickupHandle *tp, - enum TALER_ErrorCode ec) -{ - struct TALER_MERCHANT_PickupDetails pd = { - .hr.ec = ec - }; - - tp->cb (tp->cb_cls, - &pd); - TALER_MERCHANT_tip_pickup_cancel (tp); -} - - -/** - * Callback for a /tip-pickup request. Returns the result of the operation. - * Note that the client MUST still do the unblinding of the @a blind_sigs. - * - * @param cls closure, a `struct TALER_MERCHANT_TipPickupHandle *` - * @param tpr response details - */ -static void -pickup_done_cb (void *cls, - const struct TALER_MERCHANT_TipPickup2Response *tpr) -{ - struct TALER_MERCHANT_TipPickupHandle *tp = cls; - struct TALER_MERCHANT_PickupDetails pd = { - .hr = tpr->hr - }; - - tp->tpo2 = NULL; - if (MHD_HTTP_OK != tpr->hr.http_status) - { - tp->cb (tp->cb_cls, - &pd); - TALER_MERCHANT_tip_pickup_cancel (tp); - return; - } - { - enum GNUNET_GenericReturnValue ok = GNUNET_OK; - - for (unsigned int i = 0; idetails.ok.num_blind_sigs; i++) - { - const struct TALER_BlindedDenominationSignature *blind_sig - = &tpr->details.ok.blind_sigs[i]; - struct TALER_EXCHANGE_PrivateCoinDetails *pcd - = &tp->pcds[i]; - struct TALER_FreshCoin fc; - - if (GNUNET_OK != - TALER_planchet_to_coin (&tp->planchets[i].pk.key, - blind_sig, - &pcd->bks, - &pcd->coin_priv, - NULL, - &tp->planchets[i].c_hash, - &pcd->exchange_vals, - &fc)) - { - ok = GNUNET_SYSERR; - break; - } - pcd->sig = fc.sig; - } - if (GNUNET_OK != ok) - { - pd.hr.ec = TALER_EC_MERCHANT_TIP_PICKUP_UNBLIND_FAILURE; - } - else - { - pd.details.ok.num_sigs = tpr->details.ok.num_blind_sigs; - pd.details.ok.pcds = tp->pcds; - } - tp->cb (tp->cb_cls, - &pd); - } - TALER_MERCHANT_tip_pickup_cancel (tp); -} - - -/** - * We have obtained all of the exchange inputs. Continue the pickup. - * - * @param[in,out] tp operation to continue - */ -static void -pickup_post_csr (struct TALER_MERCHANT_TipPickupHandle *tp) -{ - struct TALER_PlanchetDetail details[tp->num_planchets]; - - for (unsigned int i = 0; inum_planchets; i++) - { - const struct PlanchetData *pd = &tp->planchets[i]; - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; - - TALER_planchet_setup_coin_priv (&pd->ps, - &pcd->exchange_vals, - &pcd->coin_priv); - TALER_planchet_blinding_secret_create (&pd->ps, - &pcd->exchange_vals, - &pcd->bks); - if (TALER_DENOMINATION_CS == pcd->exchange_vals.cipher) - { - details[i].blinded_planchet.details.cs_blinded_planchet.nonce - = pd->nonce; - } - if (GNUNET_OK != - TALER_planchet_prepare (&pd->pk.key, - &pcd->exchange_vals, - &pcd->bks, - &pcd->coin_priv, - NULL, - &tp->planchets[i].c_hash, - &details[i])) - { - GNUNET_break (0); - for (unsigned int j = 0; jtpo2 = TALER_MERCHANT_tip_pickup2 (tp->ctx, - tp->backend_url, - &tp->tip_id, - tp->num_planchets, - details, - &pickup_done_cb, - tp); - for (unsigned int j = 0; jnum_planchets; j++) - TALER_planchet_detail_free (&details[j]); - if (NULL == tp->tpo2) - { - GNUNET_break (0); - fail_pickup (tp, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE); - return; - } -} - - -/** - * Callbacks of this type are used to serve the result of submitting a - * CS R request to a exchange. - * - * @param cls a `struct TALER_MERCHANT_TipPickupHandle` - * @param csrr response details - */ -static void -csr_cb (void *cls, - const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr) -{ - struct PlanchetData *pd = cls; - struct TALER_MERCHANT_TipPickupHandle *tp = pd->tp; - - pd->csr = NULL; - tp->csr_active--; - switch (csrr->hr.http_status) - { - case MHD_HTTP_OK: - { - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[pd->off]; - - pcd->exchange_vals = csrr->details.ok.alg_values; - } - if (0 != tp->csr_active) - return; - pickup_post_csr (tp); - return; - default: - { - struct TALER_MERCHANT_PickupDetails pd = { - .hr.hint = "/csr-withdraw failed", - .hr.exchange_http_status = csrr->hr.http_status - }; - - tp->cb (tp->cb_cls, - &pd); - TALER_MERCHANT_tip_pickup_cancel (tp); - return; - } - } -} - - -struct TALER_MERCHANT_TipPickupHandle * -TALER_MERCHANT_tip_pickup ( - struct GNUNET_CURL_Context *ctx, - const char *exchange_url, - const char *backend_url, - const struct TALER_TipIdentifierP *tip_id, - unsigned int num_planchets, - const struct TALER_MERCHANT_PlanchetData pds[static num_planchets], - TALER_MERCHANT_TipPickupCallback pickup_cb, - void *pickup_cb_cls) -{ - struct TALER_MERCHANT_TipPickupHandle *tp; - - if (0 == num_planchets) - { - GNUNET_break (0); - return NULL; - } - tp = GNUNET_new (struct TALER_MERCHANT_TipPickupHandle); - tp->cb = pickup_cb; - tp->cb_cls = pickup_cb_cls; - tp->ctx = ctx; - tp->backend_url = GNUNET_strdup (backend_url); - tp->tip_id = *tip_id; - tp->num_planchets = num_planchets; - tp->planchets = GNUNET_new_array (num_planchets, - struct PlanchetData); - tp->pcds = GNUNET_new_array (num_planchets, - struct TALER_EXCHANGE_PrivateCoinDetails); - for (unsigned int i = 0; ipk; - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; - struct PlanchetData *pd = &tp->planchets[i]; - - pd->off = i; - pd->tp = tp; - tp->planchets[i].ps = mpd->ps; - tp->planchets[i].pk = *pds[i].pk; - TALER_denom_pub_deep_copy (&tp->planchets[i].pk.key, - &pds[i].pk->key); - switch (pk->key.cipher) - { - case TALER_DENOMINATION_RSA: - pcd->exchange_vals.cipher = TALER_DENOMINATION_RSA; - break; - case TALER_DENOMINATION_CS: - { - TALER_cs_withdraw_nonce_derive (&pd->ps, - &pd->nonce); - pd->csr = TALER_EXCHANGE_csr_withdraw (ctx, - exchange_url, - &pd->pk, - &pd->nonce, - &csr_cb, - pd); - if (NULL == pd->csr) - { - GNUNET_break (0); - TALER_MERCHANT_tip_pickup_cancel (tp); - return NULL; - } - tp->csr_active++; - break; - } - default: - GNUNET_break (0); - TALER_MERCHANT_tip_pickup_cancel (tp); - return NULL; - } - } - if (0 == tp->csr_active) - { - pickup_post_csr (tp); - return tp; - } - return tp; -} - - -void -TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupHandle *tp) -{ - for (unsigned int i = 0; inum_planchets; i++) - { - struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; - struct PlanchetData *pd = &tp->planchets[i]; - - TALER_denom_sig_free (&pcd->sig); - TALER_denom_pub_free (&tp->planchets[i].pk.key); - if (NULL != pd->csr) - { - TALER_EXCHANGE_csr_withdraw_cancel (pd->csr); - pd->csr = NULL; - } - } - GNUNET_array_grow (tp->planchets, - tp->num_planchets, - 0); - if (NULL != tp->tpo2) - { - TALER_MERCHANT_tip_pickup2_cancel (tp->tpo2); - tp->tpo2 = NULL; - } - GNUNET_free (tp->backend_url); - GNUNET_free (tp->pcds); - GNUNET_free (tp); -} - - -/* end of merchant_api_tip_pickup.c */ diff --git a/src/lib/merchant_api_tip_pickup2.c b/src/lib/merchant_api_tip_pickup2.c deleted file mode 100644 index 74e183ab..00000000 --- a/src/lib/merchant_api_tip_pickup2.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - This file is part of TALER - 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 published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - -*/ -/** - * @file merchant_api_tip_pickup2.c - * @brief Implementation of the /tip-pickup request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include -#include -#include - - -/** - * @brief A handle for tracking transactions. - */ -struct TALER_MERCHANT_TipPickup2Handle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipPickup2Callback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Expected number of planchets. - */ - unsigned int num_planchets; -}; - - -/** - * We got a 200 response back from the exchange (or the merchant). - * Now we need to parse the response and if it is well-formed, - * call the callback (and set it to NULL afterwards). - * - * @param tpo handle of the original authorization operation - * @param[in] tpr response to complete - * @param json cryptographic proof returned by the exchange/merchant - * @return #GNUNET_OK if response is valid - */ -static enum GNUNET_GenericReturnValue -check_ok (struct TALER_MERCHANT_TipPickup2Handle *tpo, - struct TALER_MERCHANT_TipPickup2Response *tpr, - const json_t *json) -{ - const json_t *ja; - unsigned int ja_len; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("blind_sigs", - &ja), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - ja_len = json_array_size (ja); - if (ja_len != tpo->num_planchets) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - { - struct TALER_BlindedDenominationSignature mblind_sigs[GNUNET_NZL (ja_len)]; - - for (unsigned int i = 0; idetails.ok.num_blind_sigs = ja_len; - tpr->details.ok.blind_sigs = mblind_sigs; - tpo->cb (tpo->cb_cls, - tpr); - tpo->cb = NULL; /* do not call twice */ - for (unsigned int i = 0; ijob = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - if (GNUNET_OK != - check_ok (tpo, - &tpr, - json)) - { - GNUNET_break_op (0); - tpr.hr.http_status = 0; - tpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - break; - case MHD_HTTP_BAD_REQUEST: - /* Can happen if we pickup an amount that exceeds the tip... */ - tpr.hr.ec = TALER_JSON_get_error_code (json); - tpr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_break (TALER_EC_MERCHANT_TIP_PICKUP_AMOUNT_EXCEEDS_TIP_REMAINING == - tpr.hr.ec); - break; - case MHD_HTTP_CONFLICT: - /* legal, can happen if we pickup a tip twice... */ - tpr.hr.ec = TALER_JSON_get_error_code (json); - tpr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - /* legal, can happen if tip ID is unknown */ - tpr.hr.ec = TALER_JSON_get_error_code (json); - tpr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - tpr.hr.ec = TALER_JSON_get_error_code (json); - tpr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &tpr.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tpr.hr.ec); - break; - } - if (NULL != tpo->cb) - { - tpo->cb (tpo->cb_cls, - &tpr); - tpo->cb = NULL; - } - TALER_MERCHANT_tip_pickup2_cancel (tpo); -} - - -struct TALER_MERCHANT_TipPickup2Handle * -TALER_MERCHANT_tip_pickup2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_TipIdentifierP *tip_id, - unsigned int num_planchets, - const struct TALER_PlanchetDetail planchets[static num_planchets], - TALER_MERCHANT_TipPickup2Callback pickup_cb, - void *pickup_cb_cls) -{ - struct TALER_MERCHANT_TipPickup2Handle *tpo; - CURL *eh; - json_t *pa; - json_t *tp_obj; - - if (0 == num_planchets) - { - GNUNET_break (0); - return NULL; - } - pa = json_array (); - GNUNET_assert (NULL != pa); - for (unsigned int i = 0; idenom_pub_hash), - TALER_JSON_pack_blinded_planchet ("coin_ev", - &planchet->blinded_planchet)); - if (0 != - json_array_append_new (pa, - p)) - { - GNUNET_break (0); - json_decref (pa); - return NULL; - } - } - tp_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("planchets", - pa)); - tpo = GNUNET_new (struct TALER_MERCHANT_TipPickup2Handle); - tpo->num_planchets = num_planchets; - tpo->ctx = ctx; - tpo->cb = pickup_cb; - tpo->cb_cls = pickup_cb_cls; - - { - char tip_str[sizeof (*tip_id) * 2]; - char arg_str[sizeof (tip_str) + 32]; - char *end; - - end = GNUNET_STRINGS_data_to_string (tip_id, - sizeof (*tip_id), - tip_str, - sizeof (tip_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "tips/%s/pickup", - tip_str); - tpo->url = TALER_url_join (backend_url, - arg_str, - NULL); - } - if (NULL == tpo->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (tp_obj); - GNUNET_free (tpo); - return NULL; - } - eh = TALER_MERCHANT_curl_easy_get_ (tpo->url); - if (GNUNET_OK != - TALER_curl_easy_post (&tpo->post_ctx, - eh, - tp_obj)) - { - GNUNET_break (0); - json_decref (tp_obj); - curl_easy_cleanup (eh); - GNUNET_free (tpo->url); - GNUNET_free (tpo); - return NULL; - } - json_decref (tp_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tpo->url); - tpo->job = GNUNET_CURL_job_add2 (ctx, - eh, - tpo->post_ctx.headers, - &handle_tip_pickup_finished, - tpo); - if (NULL == tpo->job) - { - TALER_MERCHANT_tip_pickup2_cancel (tpo); - return NULL; - } - return tpo; -} - - -void -TALER_MERCHANT_tip_pickup2_cancel ( - struct TALER_MERCHANT_TipPickup2Handle *tpo) -{ - if (NULL != tpo->job) - { - GNUNET_CURL_job_cancel (tpo->job); - tpo->job = NULL; - } - TALER_curl_easy_post_finished (&tpo->post_ctx); - GNUNET_free (tpo->url); - GNUNET_free (tpo); -} - - -/* end of merchant_api_tip_pickup2.c */ diff --git a/src/lib/merchant_api_wallet_get_reward.c b/src/lib/merchant_api_wallet_get_reward.c new file mode 100644 index 00000000..f548d692 --- /dev/null +++ b/src/lib/merchant_api_wallet_get_reward.c @@ -0,0 +1,220 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018, 2020 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 published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + +*/ +/** + * @file merchant_api_wallet_get_reward.c + * @brief Implementation of the GET /rewards/$REWARD_ID request of the merchant's HTTP API + * @author Florian Dold + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include +#include + + +/** + * @brief A handle for tracking /reward-get operations + */ +struct TALER_MERCHANT_RewardWalletGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_RewardWalletGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP /track/transaction request. + * + * @param cls the `struct TALER_MERCHANT_RewardGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_wallet_reward_get_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_RewardWalletGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_RewardWalletGetResponse wgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /reward/$REWARD_ID response with status code %u\n", + (unsigned int) response_code); + + tgh->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("expiration", + &wgr.details.ok.expiration), + GNUNET_JSON_spec_string ("exchange_url", + &wgr.details.ok.exchange_url), + GNUNET_JSON_spec_string ("next_url", + &wgr.details.ok.next_url), + TALER_JSON_spec_amount_any ("reward_amount", + &wgr.details.ok.amount_remaining), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + wgr.hr.http_status = 0; + wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + tgh->cb (tgh->cb_cls, + &wgr); + TALER_MERCHANT_wallet_reward_get_cancel (tgh); + return; + } + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + wgr.hr.ec = TALER_JSON_get_error_code (json); + wgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + /* legal, can happen if instance or reward reserve is unknown */ + wgr.hr.ec = TALER_JSON_get_error_code (json); + wgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &wgr.hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) wgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &wgr); + TALER_MERCHANT_wallet_reward_get_cancel (tgh); +} + + +struct TALER_MERCHANT_RewardWalletGetHandle * +TALER_MERCHANT_wallet_reward_get (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_RewardIdentifierP *reward_id, + TALER_MERCHANT_RewardWalletGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_RewardWalletGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_RewardWalletGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char res_str[sizeof (*reward_id) * 2]; + char arg_str[sizeof (res_str) + 48]; + char *end; + + end = GNUNET_STRINGS_data_to_string (reward_id, + sizeof (*reward_id), + res_str, + sizeof (res_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "rewards/%s", + res_str); + tgh->url = TALER_url_join (backend_url, + arg_str, + NULL); + } + + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_wallet_reward_get_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_wallet_reward_get_cancel ( + struct TALER_MERCHANT_RewardWalletGetHandle *tgh) +{ + if (NULL != tgh->job) + { + GNUNET_CURL_job_cancel (tgh->job); + tgh->job = NULL; + } + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} + + +/* end of merchant_api_wallet_get_reward.c */ diff --git a/src/lib/merchant_api_wallet_get_tip.c b/src/lib/merchant_api_wallet_get_tip.c deleted file mode 100644 index ee931d13..00000000 --- a/src/lib/merchant_api_wallet_get_tip.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018, 2020 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 published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - -*/ -/** - * @file merchant_api_wallet_get_tip.c - * @brief Implementation of the GET /tips/$TIP_ID request of the merchant's HTTP API - * @author Florian Dold - */ -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include -#include "taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include -#include - - -/** - * @brief A handle for tracking /tip-get operations - */ -struct TALER_MERCHANT_TipWalletGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TipWalletGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP /track/transaction request. - * - * @param cls the `struct TALER_MERCHANT_TipGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_wallet_tip_get_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipWalletGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_TipWalletGetResponse wgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /tip/$TIP_ID response with status code %u\n", - (unsigned int) response_code); - - tgh->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("expiration", - &wgr.details.ok.expiration), - GNUNET_JSON_spec_string ("exchange_url", - &wgr.details.ok.exchange_url), - GNUNET_JSON_spec_string ("next_url", - &wgr.details.ok.next_url), - TALER_JSON_spec_amount_any ("tip_amount", - &wgr.details.ok.amount_remaining), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - wgr.hr.http_status = 0; - wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - tgh->cb (tgh->cb_cls, - &wgr); - TALER_MERCHANT_wallet_tip_get_cancel (tgh); - return; - } - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - wgr.hr.ec = TALER_JSON_get_error_code (json); - wgr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - /* legal, can happen if instance or tip reserve is unknown */ - wgr.hr.ec = TALER_JSON_get_error_code (json); - wgr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &wgr.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) wgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &wgr); - TALER_MERCHANT_wallet_tip_get_cancel (tgh); -} - - -struct TALER_MERCHANT_TipWalletGetHandle * -TALER_MERCHANT_wallet_tip_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_TipIdentifierP *tip_id, - TALER_MERCHANT_TipWalletGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TipWalletGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_TipWalletGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - { - char res_str[sizeof (*tip_id) * 2]; - char arg_str[sizeof (res_str) + 48]; - char *end; - - end = GNUNET_STRINGS_data_to_string (tip_id, - sizeof (*tip_id), - res_str, - sizeof (res_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "tips/%s", - res_str); - tgh->url = TALER_url_join (backend_url, - arg_str, - NULL); - } - - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_wallet_tip_get_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_wallet_tip_get_cancel ( - struct TALER_MERCHANT_TipWalletGetHandle *tgh) -{ - if (NULL != tgh->job) - { - GNUNET_CURL_job_cancel (tgh->job); - tgh->job = NULL; - } - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} - - -/* end of merchant_api_wallet_get_tip.c */ -- cgit v1.2.3