commit 4ebac01e89badb29d361b49310cdd1cc6397b563 parent 3a8e0b0f2f56f31e1a2ae7afca8d1c4f456bca46 Author: Christian Blättler <blatc2@bfh.ch> Date: Mon, 18 Mar 2024 20:59:17 +0100 Merge branch 'master' into tokens Diffstat:
27 files changed, 683 insertions(+), 439 deletions(-)
diff --git a/configure.ac b/configure.ac @@ -18,7 +18,7 @@ # This configure file is in the public domain AC_PREREQ([2.69]) -AC_INIT([taler-merchant],[0.9.4],[taler-bug@gnunet.org]) +AC_INIT([taler-merchant],[0.10.0],[taler-bug@gnunet.org]) AC_CONFIG_SRCDIR([src/backend/taler-merchant-httpd.c]) AC_CONFIG_HEADERS([taler_merchant_config.h]) # support for non-recursive builds @@ -212,7 +212,7 @@ AC_CHECK_HEADERS([gnunet/gnunet_util_lib.h], AS_IF([test $libgnunetutil != 1], [AC_MSG_ERROR([[ *** -*** You need libgnunetutil >= 0.20.0 to build this program. +*** You need libgnunetutil >= 0.21.0 to build this program. *** This library is part of GNUnet, available at *** https://gnunet.org *** ]])]) @@ -253,6 +253,31 @@ AS_IF([test $libtalerutil != 1], *** https://taler.net *** ]])]) + +libtalermhd=0 +AC_MSG_CHECKING([for libtalermhd]) +AC_ARG_WITH(exchange, + [AS_HELP_STRING([--with-exchange=PFX], [base of Taler EXCHANGE installation])], + [AC_MSG_RESULT([given as $with_exchange])], + [AC_MSG_RESULT(not given) + with_exchange=yes]) +AS_CASE([$with_exchange], + [yes], [], + [no], [AC_MSG_ERROR([--with-exchange is required])], + [LDFLAGS="-L$with_exchange/lib $LDFLAGS" + CPPFLAGS="-I$with_exchange/include $CPPFLAGS $POSTGRESQL_CPPFLAGS"]) + +AC_CHECK_HEADERS([taler/taler_mhd_lib.h], + [AC_CHECK_LIB([talermhd], [TALER_MHD_parse_request_arg_amount], libtalermhd=1)]) +AM_CONDITIONAL(HAVE_TALERMHD, test x$libtalermhd = x1) +AS_IF([test $libtalermhd != 1], + [AC_MSG_ERROR([[ +*** +*** You need libtalermhd >= 0.9.4a to build this program. +*** This library is part of the GNU Taler exchange, available at +*** https://taler.net +*** ]])]) + libtalerjson=0 AC_CHECK_HEADERS([taler/taler_json_lib.h], [AC_CHECK_LIB([talerjson], [TALER_JSON_spec_otp_type], libtalerjson=1)]) @@ -381,6 +406,7 @@ AM_CONDITIONAL([HAVE_GNUNETPQ], [false]) AM_CONDITIONAL([HAVE_POSTGRESQL], [false]) AM_CONDITIONAL([HAVE_TALERUTIL], [false]) AM_CONDITIONAL([HAVE_TALERPQ], [false]) +AM_CONDITIONAL([HAVE_TALERMHD], [false]) AM_CONDITIONAL([HAVE_TALERJSON], [false]) AM_CONDITIONAL([HAVE_TALERFAKEBANK], [false]) AM_CONDITIONAL([USE_COVERAGE], [false]) diff --git a/contrib/ci/jobs/4-deb-package/version.sh b/contrib/ci/jobs/4-deb-package/version.sh @@ -10,7 +10,7 @@ else RECENT_VERSION_TAG=$(git describe --tags --match 'v*.*.*' --always --abbrev=0 HEAD || exit 1) commits="$(git rev-list ${RECENT_VERSION_TAG}..HEAD --count)" if [ "${commits}" = "0" ]; then - git describe --tag HEAD || exit 1 + git describe --tag HEAD | sed -r 's/^v//' || exit 1 else echo $(echo ${RECENT_VERSION_TAG} | sed -r 's/^v//')-${commits}-$(git rev-parse --short=8 HEAD) fi diff --git a/debian/changelog b/debian/changelog @@ -1,8 +1,20 @@ -taler-merchant (0.9.5~dev.3) unstable; urgency=low +taler-merchant (0.10.0) unstable; urgency=low - * Development version 0.9.5-dev.3. + * Implement public GET API for templates (#8608). - -- Florian Dold <dold@taler.net> Wed, 21 Feb 2024 11:40:20 +0100 + -- Christian Grothoff <grothoff@gnu.org> Sat, 9 Mar 2024 21:50:12 +0200 + +taler-merchant (0.9.4-3) unstable; urgency=low + + * v0.9.4b bugfix release (mostly updates SPA). + + -- Christian Grothoff <grothoff@gnu.org> Thu, 7 Mar 2024 21:50:12 +0200 + +taler-merchant (0.9.4-2) unstable; urgency=low + + * v0.9.4a bugfix release. + + -- Christian Grothoff <grothoff@gnu.org> Mon, 3 Mar 2024 21:50:12 +0200 taler-merchant (0.9.4-1) unstable; urgency=low diff --git a/debian/control b/debian/control @@ -56,6 +56,7 @@ Depends: ${shlibs:Depends} Recommends: postgresql (>=14.0), + taler-terms-generator, apache2 | nginx | httpd Description: GNU's payment system merchant backend. . diff --git a/doc/Makefile.am b/doc/Makefile.am @@ -15,17 +15,26 @@ info_TEXINFOS = \ prebuilt/texinfo/taler-merchant-api-tutorial.texi \ prebuilt/texinfo/taler-merchant.texi +install-info-local: + @echo " $(MKDIR_P) '$(DESTDIR)$(infodir)/taler-merchant-api-tutorial-figures'"; \ + $(MKDIR_P) "$(DESTDIR)$(infodir)/taler-merchant-api-tutorial-figures" || exit 1; \ + @echo " $(MKDIR_P) '$(DESTDIR)$(infodir)/taler-merchant-figures'"; \ + $(MKDIR_P) "$(DESTDIR)$(infodir)/taler-merchant-figures" || exit 1; \ + echo " $(INSTALL_DATA) arch-api.png '$(DESTDIR)$(infodir)/taler-merchant-api-tutorial-figures'"; \ + $(INSTALL_DATA) '$(srcdir)/prebuilt/texinfo/taler-merchant-api-tutorial-figures/arch-api.png' "$(DESTDIR)$(infodir)/taler-merchant-api-tutorial-figures" || exit 1; + echo " $(INSTALL_DATA) prebuilt/texinfo/taler-merchant-figures/*.png '$(DESTDIR)$(infodir)/taler-merchant-figures'"; \ + $(INSTALL_DATA) '$(srcdir)/prebuilt/texinfo/taler-merchant-figures/arch-api.png' '$(srcdir)/prebuilt/texinfo/taler-merchant-figures/merchant_first_login.png' '$(srcdir)/prebuilt/texinfo/taler-merchant-figures/no_default_account_yet.png' '$(srcdir)/prebuilt/texinfo/taler-merchant-figures/enter_instance_details.png' '$(srcdir)/prebuilt/texinfo/taler-merchant-figures/instance_iban_config.png' '$(srcdir)/prebuilt/texinfo/taler-merchant-figures/create_orders.png' '$(srcdir)/prebuilt/texinfo/taler-merchant-figures/payment_links.png' '$(srcdir)/prebuilt/texinfo/taler-merchant-figures/merchant-db.png' "$(DESTDIR)$(infodir)/taler-merchant-figures" || exit 1; + EXTRA_DIST = \ $(man_MANS) \ $(info_TEXINFOS) \ prebuilt/texinfo/taler-merchant-api-tutorial-figures/arch-api.png \ - prebuilt/texinfo/taler-merchant-api-tutorial-figures/auditor-db.png \ - prebuilt/texinfo/taler-merchant-api-tutorial-figures/exchange-db.png \ - prebuilt/texinfo/taler-merchant-api-tutorial-figures/merchant-db.png \ - prebuilt/texinfo/taler-merchant-api-tutorial-figures/replication.png \ prebuilt/texinfo/taler-merchant-figures/arch-api.png \ - prebuilt/texinfo/taler-merchant-figures/auditor-db.png \ - prebuilt/texinfo/taler-merchant-figures/exchange-db.png \ - prebuilt/texinfo/taler-merchant-figures/merchant-db.png \ - prebuilt/texinfo/taler-merchant-figures/replication.png + prebuilt/texinfo/taler-merchant-figures/merchant_first_login.png \ + prebuilt/texinfo/taler-merchant-figures/no_default_account_yet.png \ + prebuilt/texinfo/taler-merchant-figures/enter_instance_details.png \ + prebuilt/texinfo/taler-merchant-figures/instance_iban_config.png \ + prebuilt/texinfo/taler-merchant-figures/create_orders.png \ + prebuilt/texinfo/taler-merchant-figures/payment_links.png \ + prebuilt/texinfo/taler-merchant-figures/merchant-db.png diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am @@ -28,6 +28,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \ taler-merchant-httpd_get-orders-ID.c \ taler-merchant-httpd_get-orders-ID.h \ + taler-merchant-httpd_get-templates-ID.c \ + taler-merchant-httpd_get-templates-ID.h \ taler-merchant-httpd_helper.c \ taler-merchant-httpd_helper.h \ taler-merchant-httpd_mhd.c \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -31,6 +31,7 @@ #include "taler-merchant-httpd_config.h" #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_get-orders-ID.h" +#include "taler-merchant-httpd_get-templates-ID.h" #include "taler-merchant-httpd_mhd.h" #include "taler-merchant-httpd_private-delete-account-ID.h" #include "taler-merchant-httpd_private-delete-instances-ID.h" @@ -1268,7 +1269,6 @@ url_handler (void *cls, .url_prefix = "/tokenfamilies/", .method = MHD_HTTP_METHOD_GET, .have_id_segment = true, - .allow_deleted_instance = true, .handler = &TMH_private_get_tokenfamilies_SLUG }, /* DELETE /tokenfamilies/$SLUG/: */ @@ -1276,7 +1276,6 @@ url_handler (void *cls, .url_prefix = "/tokenfamilies/", .method = MHD_HTTP_METHOD_DELETE, .have_id_segment = true, - .allow_deleted_instance = true, .handler = &TMH_private_delete_token_families_SLUG }, /* PATCH /tokenfamilies/$SLUG/: */ @@ -1284,7 +1283,6 @@ url_handler (void *cls, .url_prefix = "/tokenfamilies/", .method = MHD_HTTP_METHOD_PATCH, .have_id_segment = true, - .allow_deleted_instance = true, .handler = &TMH_private_patch_token_family_SLUG, }, { @@ -1414,12 +1412,18 @@ url_handler (void *cls, .have_id_segment = true, .handler = &TMH_return_static }, + /* GET /templates/$ID/: */ + { + .url_prefix = "/templates/", + .method = MHD_HTTP_METHOD_GET, + .have_id_segment = true, + .handler = &TMH_get_templates_ID + }, /* POST /templates/$ID: */ { .url_prefix = "/templates/", .method = MHD_HTTP_METHOD_POST, .have_id_segment = true, - .allow_deleted_instance = true, .handler = &TMH_post_using_templates_ID, .max_upload = 1024 * 1024 }, diff --git a/src/backend/taler-merchant-httpd_config.c b/src/backend/taler-merchant-httpd_config.c @@ -42,7 +42,7 @@ * #MERCHANT_PROTOCOL_CURRENT and #MERCHANT_PROTOCOL_AGE in * merchant_api_config.c! */ -#define MERCHANT_PROTOCOL_VERSION "9:0:5" +#define MERCHANT_PROTOCOL_VERSION "11:0:7" /** diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -1588,31 +1588,15 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, "Awaiting refund obtained\n"); } + TALER_MHD_parse_request_amount (connection, + "refund", + &god->sc.refund_expected); + if (TALER_amount_is_valid (&god->sc.refund_expected)) { - const char *min_refund; - - min_refund = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "refund"); - if (NULL != min_refund) - { - if ( (GNUNET_OK != - TALER_string_to_amount (min_refund, - &god->sc.refund_expected)) || - (0 != strcasecmp (god->sc.refund_expected.currency, - TMH_currency) ) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "refund"); - } - god->sc.awaiting_refund = true; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Awaiting minimum refund of %s\n", - min_refund); - } + god->sc.awaiting_refund = true; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting minimum refund of %s\n", + TALER_amount2s (&god->sc.refund_expected)); } TALER_MHD_parse_request_timeout (connection, &god->sc.long_poll_timeout); diff --git a/src/backend/taler-merchant-httpd_get-templates-ID.c b/src/backend/taler-merchant-httpd_get-templates-ID.c @@ -0,0 +1,73 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-templates-ID.c + * @brief implement GET /templates/$ID + * @author Priscilla HUANG + */ +#include "platform.h" +#include "taler-merchant-httpd_private-get-templates-ID.h" +#include <taler/taler_json_lib.h> + + +MHD_RESULT +TMH_get_templates_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + struct TALER_MERCHANTDB_TemplateDetails tp = { 0 }; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_assert (NULL != mi); + qs = TMH_db->lookup_template (TMH_db->cls, + mi->settings.id, + hc->infix, + &tp); + if (0 > qs) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_template"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN, + hc->infix); + } + { + MHD_RESULT ret; + + ret = TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_object_steal ("template_contract", + tp.template_contract)); + GNUNET_free (tp.template_description); + GNUNET_free (tp.otp_id); + return ret; + } +} + + +/* end of taler-merchant-httpd_get-templates-ID.c */ diff --git a/src/backend/taler-merchant-httpd_get-templates-ID.h b/src/backend/taler-merchant-httpd_get-templates-ID.h @@ -0,0 +1,42 @@ +/* + This file is part of TALER + (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-templates-ID.h + * @brief implement GET /templates/$ID/ + * @author Priscilla HUANG + */ +#ifndef TALER_MERCHANT_HTTPD_GET_TEMPLATES_ID_H +#define TALER_MERCHANT_HTTPD_GET_TEMPLATES_ID_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a GET "/templates/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_get_templates_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_get-templates-ID.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -2106,7 +2106,7 @@ check_payment_sufficient (struct PayContext *pc) pay_end (pc, TALER_MHD_reply_with_error ( pc->connection, - MHD_HTTP_NOT_ACCEPTABLE, + MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES, "contract not paid up due to fees (client may have calculated them badly)")); return false; @@ -2115,7 +2115,7 @@ check_payment_sufficient (struct PayContext *pc) pay_end (pc, TALER_MHD_reply_with_error ( pc->connection, - MHD_HTTP_NOT_ACCEPTABLE, + MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT, "payment insufficient")); return false; diff --git a/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.c b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2022 Taler Systems SA + (C) 2022-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -39,7 +39,21 @@ TMH_private_get_otp_devices_ID (const struct TMH_RequestHandler *rh, struct TMH_MerchantInstance *mi = hc->instance; struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 }; enum GNUNET_DB_QueryStatus qs; + uint64_t faketime_s + = GNUNET_TIME_timestamp_to_s (GNUNET_TIME_timestamp_get ()); + struct GNUNET_TIME_Timestamp my_time; + struct TALER_Amount price; + TALER_MHD_parse_request_number (connection, + "faketime", + &faketime_s); + memset (&price, + 0, + sizeof (price)); + TALER_MHD_parse_request_amount (connection, + "price", + &price); + my_time = GNUNET_TIME_timestamp_from_s (faketime_s); GNUNET_assert (NULL != mi); qs = TMH_db->select_otp (TMH_db->cls, mi->settings.id, @@ -62,17 +76,30 @@ TMH_private_get_otp_devices_ID (const struct TMH_RequestHandler *rh, } { MHD_RESULT ret; + char *pos_confirmation; + pos_confirmation = (NULL == tp.otp_key) + ? NULL + : TALER_build_pos_confirmation (tp.otp_key, + tp.otp_algorithm, + &price, + my_time); /* Note: we deliberately (by design) do not return the otp_key */ ret = TALER_MHD_REPLY_JSON_PACK ( connection, MHD_HTTP_OK, GNUNET_JSON_pack_string ("device_description", tp.otp_description), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_code", + pos_confirmation)), + GNUNET_JSON_pack_uint64 ("otp_timestamp", + faketime_s), GNUNET_JSON_pack_uint64 ("otp_algorithm", tp.otp_algorithm), GNUNET_JSON_pack_uint64 ("otp_ctr", tp.otp_ctr)); + GNUNET_free (pos_confirmation); GNUNET_free (tp.otp_description); GNUNET_free (tp.otp_key); return ret; diff --git a/src/backend/taler-merchant-httpd_private-get-templates-ID.c b/src/backend/taler-merchant-httpd_private-get-templates-ID.c @@ -23,18 +23,11 @@ #include <taler/taler_json_lib.h> -/** - * Handle a GET "/templates/$ID" request. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] hc context with further information about the request - * @return MHD result code - */ MHD_RESULT -TMH_private_get_templates_ID (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) +TMH_private_get_templates_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { struct TMH_MerchantInstance *mi = hc->instance; struct TALER_MERCHANTDB_TemplateDetails tp = { 0 }; @@ -48,17 +41,19 @@ TMH_private_get_templates_ID (const struct TMH_RequestHandler *rh, if (0 > qs) { GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "lookup_template"); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_template"); } - if (0 == qs) + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN, - hc->infix); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN, + hc->infix); } { MHD_RESULT ret; diff --git a/src/backend/taler-merchant-httpd_private-get-templates-ID.h b/src/backend/taler-merchant-httpd_private-get-templates-ID.h @@ -33,9 +33,10 @@ * @return MHD result code */ MHD_RESULT -TMH_private_get_templates_ID (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc); +TMH_private_get_templates_ID ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); /* end of taler-merchant-httpd_private-get-templates-ID.h */ #endif diff --git a/src/backenddb/drop.sql b/src/backenddb/drop.sql @@ -17,15 +17,15 @@ -- Everything in one big transaction BEGIN; --- This script DROPs all of the tables we create, including the --- versioning schema! --- --- Unlike the other SQL files, it SHOULD be updated to reflect the --- latest requirements for dropping tables. +-- This script DROPs all of the tables we create. -SELECT _v.unregister_patch('merchant-0001'); -SELECT _v.unregister_patch('merchant-0002'); -SELECT _v.unregister_patch('merchant-0003'); +WITH xpatches AS ( + SELECT patch_name + FROM _v.patches + WHERE starts_with(patch_name,'merchant-') +) + SELECT _v.unregister_patch(xpatches.patch_name) + FROM xpatches; DROP SCHEMA merchant CASCADE; diff --git a/src/backenddb/pg_lookup_transfers.c b/src/backenddb/pg_lookup_transfers.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -25,7 +25,6 @@ #include "pg_lookup_transfers.h" #include "pg_helper.h" -/* FIXME: filter-logic can be written more cleanly, with less duplication! */ /** * Closure for #lookup_transfers_cb(). @@ -52,10 +51,6 @@ struct LookupTransfersContext */ enum GNUNET_DB_QueryStatus qs; - /** - * Filter to apply by verification status. - */ - enum TALER_EXCHANGE_YesNoAll verified; }; @@ -81,10 +76,9 @@ lookup_transfers_cb (void *cls, char *payto_uri; char *exchange_url; uint64_t transfer_serial_id; - struct GNUNET_TIME_Timestamp execution_time; - enum TALER_EXCHANGE_YesNoAll verified; - uint8_t verified8; - uint8_t confirmed8; + struct GNUNET_TIME_Timestamp execution_time = GNUNET_TIME_UNIT_FOREVER_TS; + bool verified; + bool confirmed; struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_result_spec_amount_with_currency ("credit_amount", &credit_amount), @@ -96,12 +90,14 @@ lookup_transfers_cb (void *cls, &exchange_url), GNUNET_PQ_result_spec_uint64 ("credit_serial", &transfer_serial_id), - GNUNET_PQ_result_spec_timestamp ("execution_time", - &execution_time), - GNUNET_PQ_result_spec_auto_from_type ("verified", - &verified8), - GNUNET_PQ_result_spec_auto_from_type ("confirmed", - &confirmed8), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_timestamp ("execution_time", + &execution_time), + NULL), + GNUNET_PQ_result_spec_bool ("verified", + &verified), + GNUNET_PQ_result_spec_bool ("confirmed", + &confirmed), GNUNET_PQ_result_spec_end }; @@ -114,23 +110,15 @@ lookup_transfers_cb (void *cls, ltc->qs = GNUNET_DB_STATUS_HARD_ERROR; return; } - if (0 == verified8) - verified = TALER_EXCHANGE_YNA_NO; - else - verified = TALER_EXCHANGE_YNA_YES; - if ( (ltc->verified == TALER_EXCHANGE_YNA_ALL) || - (ltc->verified == verified) ) - { - ltc->cb (ltc->cb_cls, - &credit_amount, - &wtid, - payto_uri, - exchange_url, - transfer_serial_id, - execution_time, - TALER_EXCHANGE_YNA_YES == verified, - 0 != confirmed8); - } + ltc->cb (ltc->cb_cls, + &credit_amount, + &wtid, + payto_uri, + exchange_url, + transfer_serial_id, + execution_time, + verified, + confirmed); GNUNET_PQ_cleanup_result (rs); } ltc->qs = num_results; @@ -151,298 +139,102 @@ TMH_PG_lookup_transfers (void *cls, { struct PostgresClosure *pg = cls; uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit); + bool by_time = ( (! GNUNET_TIME_absolute_is_never (before.abs_time)) || + (! GNUNET_TIME_absolute_is_zero (after.abs_time)) ); struct LookupTransfersContext ltc = { .cb = cb, .cb_cls = cb_cls, - .pg = pg, - .verified = verified + .pg = pg + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_timestamp (&before), + GNUNET_PQ_query_param_timestamp (&after), + GNUNET_PQ_query_param_uint64 (&offset), + GNUNET_PQ_query_param_uint64 (&plimit), + NULL == payto_uri + ? GNUNET_PQ_query_param_null () /* NULL: do not filter by payto URI */ + : GNUNET_PQ_query_param_string (payto_uri), + GNUNET_PQ_query_param_bool (! by_time), /* $7: filter by time? */ + GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_ALL == verified), /* filter by verified? */ + GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_YES == verified), + GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - bool by_time; - by_time = ( (! GNUNET_TIME_absolute_is_never (before.abs_time)) || - (! GNUNET_TIME_absolute_is_zero (after.abs_time)) ); check_connection (pg); - if (by_time) - { - if (NULL != payto_uri) - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_timestamp (&before), - GNUNET_PQ_query_param_timestamp (&after), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_uint64 (&plimit), - GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "lookup_transfers_time_payto_asc", - "SELECT" - " mt.credit_amount" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",merchant_transfer_signatures.execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE execution_time < $2" - " AND execution_time >= $3" - " AND credit_serial > $4" - " AND REGEXP_REPLACE(payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($6,'\\?.*','')" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial ASC" - " LIMIT $5"); - PREPARE (pg, - "lookup_transfers_time_payto_desc", - "SELECT" - " mt.credit_amount" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",merchant_transfer_signatures.execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE execution_time < $2" - " AND execution_time >= $3" - " AND credit_serial < $4" - " AND REGEXP_REPLACE(payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($6,'\\?.*','')" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial DESC" - " LIMIT $5"); - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - (limit > 0) - ? "lookup_transfers_time_payto_asc" - : "lookup_transfers_time_payto_desc", - params, - &lookup_transfers_cb, - <c); - } - else - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_timestamp (&before), - GNUNET_PQ_query_param_timestamp (&after), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_uint64 (&plimit), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "lookup_transfers_time_asc", - "SELECT" - " mt.credit_amount" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",merchant_transfer_signatures.execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE execution_time < $2" - " AND execution_time >= $3" - " AND credit_serial > $4" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial ASC" - " LIMIT $5"); - PREPARE (pg, - "lookup_transfers_time_desc", - "SELECT" - " mt.credit_amount" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",merchant_transfer_signatures.execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE execution_time < $2" - " AND execution_time >= $3" - " AND credit_serial < $4" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial DESC" - " LIMIT $5"); - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - (limit > 0) - ? "lookup_transfers_time_asc" - : "lookup_transfers_time_desc", - params, - &lookup_transfers_cb, - <c); - } - } - else - { - if (NULL != payto_uri) - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_uint64 (&plimit), - GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "lookup_transfers_payto_asc", - "SELECT" - " mt.credit_amount" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL" - " THEN 9223372036854775807" /* largest BIGINT possible */ - " ELSE merchant_transfer_signatures.execution_time" - " END AS execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE credit_serial > $2" - " AND REGEXP_REPLACE(payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($4,'\\?.*','')" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial ASC" - " LIMIT $3"); - PREPARE (pg, - "lookup_transfers_payto_desc", - "SELECT" - " mt.credit_amount" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL" - " THEN 9223372036854775807" /* largest BIGINT possible */ - " ELSE merchant_transfer_signatures.execution_time" - " END AS execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE credit_serial < $2" - " AND REGEXP_REPLACE(payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($4,'\\?.*','')" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial DESC" - " LIMIT $3"); - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - (limit > 0) - ? "lookup_transfers_payto_asc" - : "lookup_transfers_payto_desc", - params, - &lookup_transfers_cb, - <c); - } - else - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_uint64 (&plimit), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "lookup_transfers_desc", - "SELECT" - " mt.credit_amount" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL" - " THEN 9223372036854775807" /* largest BIGINT possible */ - " ELSE merchant_transfer_signatures.execution_time" - " END AS execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE credit_serial < $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial DESC" - " LIMIT $3"); - PREPARE (pg, - "lookup_transfers_asc", - "SELECT" - " mt.credit_amount" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL" - " THEN 9223372036854775807" /* largest BIGINT possible */ - " ELSE merchant_transfer_signatures.execution_time" - " END AS execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE credit_serial > $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial ASC" - " LIMIT $3"); - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - (limit > 0) - ? "lookup_transfers_asc" - : "lookup_transfers_desc", - params, - &lookup_transfers_cb, - <c); - } - } + PREPARE (pg, + "lookup_transfers_asc", + "SELECT" + " mt.credit_amount" + ",wtid" + ",mac.payto_uri" + ",exchange_url" + ",credit_serial" + ",mts.execution_time" + ",verified" + ",confirmed" + " FROM merchant_transfers mt" + " JOIN merchant_accounts mac" + " USING (account_serial)" + " LEFT JOIN merchant_transfer_signatures mts" + " USING (credit_serial)" + " WHERE ( $7 OR " + " (mts.execution_time IS NOT NULL AND" + " mts.execution_time < $2 AND" + " mts.execution_time >= $3) )" + " AND ( $8 OR " + " (verified = $9) )" + " AND ( (CAST($6 AS TEXT) IS NULL) OR " + " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" + " =REGEXP_REPLACE($6,'\\?.*','')) )" + " AND merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND (credit_serial > $4)" + " ORDER BY credit_serial ASC" + " LIMIT $5"); + PREPARE (pg, + "lookup_transfers_desc", + "SELECT" + " mt.credit_amount" + ",wtid" + ",mac.payto_uri" + ",exchange_url" + ",credit_serial" + ",mts.execution_time" + ",verified" + ",confirmed" + " FROM merchant_transfers mt" + " JOIN merchant_accounts mac" + " USING (account_serial)" + " LEFT JOIN merchant_transfer_signatures mts" + " USING (credit_serial)" + " WHERE ( $7 OR " + " (mts.execution_time IS NOT NULL AND" + " mts.execution_time < $2 AND" + " mts.execution_time >= $3) )" + " AND ( $8 OR " + " (verified = $9) )" + " AND ( (CAST($6 AS TEXT) IS NULL) OR " + " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" + " =REGEXP_REPLACE($6,'\\?.*','')) )" + " AND merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND (credit_serial < $4)" + " ORDER BY credit_serial DESC" + " LIMIT $5"); + qs = GNUNET_PQ_eval_prepared_multi_select ( + pg->conn, + (limit > 0) + ? "lookup_transfers_asc" + : "lookup_transfers_desc", + params, + &lookup_transfers_cb, + <c); if (0 >= qs) return qs; return ltc.qs; diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h @@ -34,7 +34,7 @@ /** * Library version (in hex) for compatibility tests. */ -#define TALER_MERCHANT_SERVICE_VERSION 0x00090402 +#define TALER_MERCHANT_SERVICE_VERSION 0x00090403 /** @@ -4269,9 +4269,14 @@ struct TALER_MERCHANT_OtpDeviceGetResponse const char *otp_device_description; /** - * OTP device key. + * POS confirmation text with OTP codes that + * would be returned for a purchase over the + * amount given in the query for the respective + * time and algorithm. NULL if the confirmation + * could not be computed based on the query and + * OTP algorithm. */ - const char *otp_key; + const char *otp_code; /** * current counter. @@ -4283,6 +4288,12 @@ struct TALER_MERCHANT_OtpDeviceGetResponse */ enum TALER_MerchantConfirmationAlgorithm otp_alg; + /** + * Timestamp in second used to compute + * @e otp_code. + */ + uint64_t otp_timestamp_s; + } ok; } details; @@ -4587,7 +4598,7 @@ TALER_MERCHANT_templates_get_cancel ( /** - * Handle for a GET /template/$ID operation. Gets details + * Handle for a GET /private/template/$ID operation. Gets details * about a single template. Do not confused with a * `struct TALER_MERCHANT_TemplatesGetHandle`, which * obtains a list of all templates. @@ -4596,7 +4607,7 @@ struct TALER_MERCHANT_TemplateGetHandle; /** - * Details in a response to a GET /templates request. + * Details in a response to a GET /private/templates/$ID request. */ struct TALER_MERCHANT_TemplateGetResponse { @@ -4639,7 +4650,7 @@ struct TALER_MERCHANT_TemplateGetResponse /** - * Function called with the result of the GET /template/$ID operation. + * Function called with the result of the GET /private/template/$ID operation. * * @param cls closure * @param tgr HTTP response details @@ -4651,7 +4662,7 @@ typedef void /** - * Make a GET /template/$ID request to get details about an + * Make a GET /private/template/$ID request to get details about an * individual template. * * @param ctx the context @@ -4671,7 +4682,7 @@ TALER_MERCHANT_template_get ( /** - * Cancel GET /templates/$ID operation. + * Cancel GET /private/templates/$ID operation. * * @param tgh operation to cancel */ @@ -4681,6 +4692,89 @@ TALER_MERCHANT_template_get_cancel ( /** + * Handle for a (public) GET /template/$ID operation. Gets details about a + * single template. Do not confused with a `struct + * TALER_MERCHANT_TemplateGetHandle`, which is for the private API. + */ +struct TALER_MERCHANT_WalletTemplateGetHandle; + + +/** + * Details in a response to a GET /templates request. + */ +struct TALER_MERCHANT_WalletTemplateGetResponse +{ + /** + * HTTP response details. + */ + struct TALER_MERCHANT_HttpResponse hr; + + /** + * Response details depending on the HTTP status. + */ + union + { + /** + * Information returned if the status was #MHD_HTTP_OK. + */ + struct + { + + /** + * Template for the contract. + */ + const json_t *template_contract; + + } ok; + + } details; + +}; + + +/** + * Function called with the result of the GET /template/$ID operation. + * + * @param cls closure + * @param tgr HTTP response details + */ +typedef void +(*TALER_MERCHANT_WalletTemplateGetCallback)( + void *cls, + const struct TALER_MERCHANT_WalletTemplateGetResponse *tgr); + + +/** + * Make a GET /template/$ID request to get details about an + * individual template. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param template_id identifier of the template to inquire about + * @param cb function to call with the backend's template information + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +struct TALER_MERCHANT_WalletTemplateGetHandle * +TALER_MERCHANT_wallet_template_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + TALER_MERCHANT_WalletTemplateGetCallback cb, + void *cb_cls); + + +/** + * Cancel GET /templates/$ID operation. + * + * @param tgh operation to cancel + */ +void +TALER_MERCHANT_wallet_template_get_cancel ( + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh); + + +/** * Handle for a POST /templates operation. */ struct TALER_MERCHANT_TemplatesPostHandle; diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h @@ -582,6 +582,7 @@ TALER_TESTING_cmd_merchant_post_orders2 ( * @param merchant_url base URL of the merchant serving * the proposal request * @param http_status expected HTTP status + * @param order_id ID of the order to create * @param refund_deadline the deadline for refunds on this order * @param pay_deadline the deadline for payment on this order * @param fulfillment_url the fulfillment URL to use @@ -684,7 +685,7 @@ TALER_TESTING_cmd_wallet_get_order ( * @param paid whether the order has been paid for or not. * @param refunded whether the order has been refunded. * @param refund_pending whether the order has refunds that haven't been obtained. - * @param repurchase_order_ref command of a paid equivalent order the merchant should be refering us to, or NULL + * @param repurchase_order_ref command of a paid equivalent order the merchant should be referring us to, or NULL * @param http_status expected HTTP response code for the request. */ struct TALER_TESTING_Command @@ -850,7 +851,7 @@ TALER_TESTING_cmd_merchant_get_order2 ( * @param order_reference reference to a command that created an order. * @param osc expected order status * @param session_id session ID the payment must be bound to - * @param repurchase_order_ref command of a paid equivalent order the merchant should be refering us to, or NULL + * @param repurchase_order_ref command of a paid equivalent order the merchant should be referring us to, or NULL * @param expected_http_status expected HTTP response code for the request. */ struct TALER_TESTING_Command @@ -1169,7 +1170,7 @@ TALER_TESTING_cmd_merchant_delete_transfer (const char *label, * of the bank account to check KYC for; NULL to check all accounts * @param exchange_url base URL of the exchange to check KYC status for * @param expected_http_status expected HTTP status - * @param expected_aml_state expected AML state (only effecitve if @e expected_http_status is #MHD_HTTP_OK) + * @param expected_aml_state expected AML state (only effective if @e expected_http_status is #MHD_HTTP_OK) * @return the command */ struct TALER_TESTING_Command diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -10,7 +10,7 @@ lib_LTLIBRARIES = \ libtalermerchant.la libtalermerchant_la_LDFLAGS = \ - -version-info 5:1:0 \ + -version-info 5:2:0 \ -no-undefined libtalermerchant_la_SOURCES = \ @@ -65,6 +65,7 @@ libtalermerchant_la_SOURCES = \ merchant_api_post_using_templates.c \ merchant_api_post_webhooks.c \ merchant_api_wallet_get_order.c \ + merchant_api_wallet_get_template.c \ merchant_api_wallet_post_order_refund.c libtalermerchant_la_LIBADD = \ diff --git a/src/lib/merchant_api_get_config.c b/src/lib/merchant_api_get_config.c @@ -34,12 +34,12 @@ * Which version of the Taler protocol is implemented * by this library? Used to determine compatibility. */ -#define MERCHANT_PROTOCOL_CURRENT 9 +#define MERCHANT_PROTOCOL_CURRENT 11 /** * How many configs are we backwards-compatible with? */ -#define MERCHANT_PROTOCOL_AGE 4 +#define MERCHANT_PROTOCOL_AGE 6 /** diff --git a/src/lib/merchant_api_get_otp_device.c b/src/lib/merchant_api_get_otp_device.c @@ -74,8 +74,8 @@ struct TALER_MERCHANT_OtpDeviceGetHandle */ static void handle_get_otp_device_finished (void *cls, - long response_code, - const void *response) + long response_code, + const void *response) { struct TALER_MERCHANT_OtpDeviceGetHandle *tgh = cls; const json_t *json = response; @@ -98,12 +98,18 @@ handle_get_otp_device_finished (void *cls, &tgr.details.ok.otp_device_description), GNUNET_JSON_spec_uint32 ("otp_algorithm", &alg32), - GNUNET_JSON_spec_string ("otp_key", - &tgr.details.ok.otp_key), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_uint64 ("otp_ctr", &tgr.details.ok.otp_ctr), NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("otp_timestamp", + &tgr.details.ok.otp_timestamp_s), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("otp_code", + &tgr.details.ok.otp_code), + NULL), GNUNET_JSON_spec_end () }; diff --git a/src/lib/merchant_api_wallet_get_template.c b/src/lib/merchant_api_wallet_get_template.c @@ -0,0 +1,195 @@ +/* + This file is part of TALER + Copyright (C) 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 + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_wallet_get_template.c + * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /templates/$ID operation. + */ +struct TALER_MERCHANT_WalletTemplateGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_WalletTemplateGetCallback 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 GET /templates/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TemplateGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_template_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_WalletTemplateGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /templates/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *contract; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_object_const ("template_contract", + &contract), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.details.ok.template_contract = contract; + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_wallet_template_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; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + 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_wallet_template_get_cancel (tgh); +} + + +struct TALER_MERCHANT_WalletTemplateGetHandle * +TALER_MERCHANT_wallet_template_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + TALER_MERCHANT_WalletTemplateGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_WalletTemplateGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "templates/%s", + template_id); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + 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_template_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_wallet_template_get_cancel ( + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c @@ -1133,7 +1133,7 @@ run (void *cls, "EUR:10.0"), TALER_TESTING_cmd_merchant_pay_order ("pay-fail-partial-double-11-good", merchant_url, - MHD_HTTP_NOT_ACCEPTABLE, + MHD_HTTP_BAD_REQUEST, "create-proposal-11", "withdraw-coin-11a", "EUR:5", diff --git a/src/testing/test_merchant_api_twisted.c b/src/testing/test_merchant_api_twisted.c @@ -226,7 +226,7 @@ run (void *cls, * so we'll then have the right to abort. */ TALER_TESTING_cmd_merchant_pay_order ("deposit-simple-for-abort", twister_merchant_url, - MHD_HTTP_NOT_ACCEPTABLE, + MHD_HTTP_BAD_REQUEST, "create-proposal-abort-1", "withdraw-coin-abort-1", "EUR:1.01", @@ -401,7 +401,7 @@ run (void *cls, "EUR:6.0"), TALER_TESTING_cmd_merchant_pay_order ("deposit-2", merchant_url, - MHD_HTTP_NOT_ACCEPTABLE, + MHD_HTTP_BAD_REQUEST, "create-proposal-2", "withdraw-coin-1", "EUR:5", diff --git a/src/testing/testing_api_cmd_get_otp_device.c b/src/testing/testing_api_cmd_get_otp_device.c @@ -102,7 +102,7 @@ get_otp_device_cb (void *cls, gis->otp_device_reference); if (GNUNET_OK != TALER_TESTING_get_trait_otp_device_description (otp_device_cmd, - &expected_description)) + &expected_description)) TALER_TESTING_interpreter_fail (gis->is); if (0 != strcmp (tgr->details.ok.otp_device_description, expected_description)) @@ -113,27 +113,6 @@ get_otp_device_cb (void *cls, return; } } - { - const char *expected_otp_key; - - if (GNUNET_OK != - TALER_TESTING_get_trait_otp_key (otp_device_cmd, - &expected_otp_key)) - TALER_TESTING_interpreter_fail (gis->is); - if ( ( (NULL == tgr->details.ok.otp_key) && (NULL != expected_otp_key)) || - ( (NULL != tgr->details.ok.otp_key) && (NULL == expected_otp_key)) || - ( (NULL != tgr->details.ok.otp_key) && - (0 != strcmp (tgr->details.ok.otp_key, - expected_otp_key)) ) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "OtpDevice otp_key `%s' does not match `%s'\n", - tgr->details.ok.otp_key, - expected_otp_key); - TALER_TESTING_interpreter_fail (gis->is); - return; - } - } break; case MHD_HTTP_UNAUTHORIZED: break; diff --git a/src/testing/testing_api_cmd_get_transfers.c b/src/testing/testing_api_cmd_get_transfers.c @@ -109,7 +109,7 @@ get_transfers_cb ( if (gtr->details.ok.transfers_length != gts->transfers_length) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Transfers length does not match (%u/%u)\n", + "Transfers length does not match (got %u/want %u)\n", gtr->details.ok.transfers_length, gts->transfers_length); TALER_TESTING_interpreter_fail (gts->is);