commit b29d3a74d6945731e105beb9cf6d0524d2ec8d84
parent 6bba22954678b9ece5ff59a6854cf248cafe01e6
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 4 Mar 2026 13:50:59 +0100
rename merchant_api_ files for better consistency, ensure MFA codes are 0-padded to 4+4 digits
Diffstat:
139 files changed, 19636 insertions(+), 18521 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_mfa.c b/src/backend/taler-merchant-httpd_mfa.c
@@ -260,10 +260,11 @@ mfa_challenge_start (
GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
1000 * 1000 * 100);
/* Note: if this is changed, the code in
+ taler-merchant-httpd_post-challenge-ID.c and
taler-merchant-httpd_post-challenge-ID-confirm.c must
possibly also be updated! */
GNUNET_asprintf (&code,
- "%llu-%llu",
+ "%04llu-%04llu",
challenge_num / 10000,
challenge_num % 10000);
qs = TMH_db->create_mfa_challenge (TMH_db->cls,
diff --git a/src/backend/taler-merchant-httpd_post-challenge-ID-confirm.c b/src/backend/taler-merchant-httpd_post-challenge-ID-confirm.c
@@ -84,7 +84,7 @@ TMH_post_challenge_ID_confirm (const struct TMH_RequestHandler *rh,
{
/* inject hyphen */
GNUNET_asprintf (&xtan,
- "%llu-%llu",
+ "%04llu-%04llu",
challenge_num / 10000,
challenge_num % 10000);
diff --git a/src/backend/taler-merchant-httpd_post-challenge-ID.c b/src/backend/taler-merchant-httpd_post-challenge-ID.c
@@ -375,7 +375,15 @@ static void
phase_send_challenge (struct MfaState *mfa)
{
const char *prog = NULL;
-
+ unsigned long long challenge_num;
+
+ challenge_num = (unsigned long long)
+ GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
+ 1000 * 1000 * 100);
+ GNUNET_asprintf (&mfa->code,
+ "%04llu-%04llu",
+ challenge_num / 10000,
+ challenge_num % 10000);
switch (mfa->channel)
{
case TALER_MERCHANT_MFA_CHANNEL_NONE:
@@ -386,11 +394,6 @@ phase_send_challenge (struct MfaState *mfa)
= GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
mfa->retransmission_date
= GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
- GNUNET_asprintf (&mfa->code,
- "%llu",
- (unsigned long long)
- GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
- 100000000));
prog = TMH_helper_sms;
break;
case TALER_MERCHANT_MFA_CHANNEL_EMAIL:
@@ -398,11 +401,6 @@ phase_send_challenge (struct MfaState *mfa)
= GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
mfa->retransmission_date
= GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
- GNUNET_asprintf (&mfa->code,
- "%llu",
- (unsigned long long)
- GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
- 100000000));
prog = TMH_helper_email;
break;
case TALER_MERCHANT_MFA_CHANNEL_TOTP:
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
@@ -16,69 +16,71 @@ libtalermerchant_la_LDFLAGS = \
libtalermerchant_la_SOURCES = \
merchant_api_curl_defaults.c merchant_api_curl_defaults.h \
merchant_api_common.c merchant_api_common.h \
- merchant_api_delete_account.c \
- merchant_api_delete_instance.c \
- merchant_api_delete_instance_token.c \
- merchant_api_delete_order.c \
- merchant_api_delete_otp_device.c \
- merchant_api_delete_product.c \
- merchant_api_delete_template.c \
- merchant_api_delete_unit.c \
- merchant_api_delete_transfer.c \
- merchant_api_delete_webhook.c \
- merchant_api_get_account.c \
- merchant_api_get_accounts.c \
- merchant_api_get_config.c \
- merchant_api_get_instance.c \
- merchant_api_get_instances.c \
- merchant_api_get_kyc.c \
- merchant_api_get_orders.c \
- merchant_api_get_otp_device.c \
- merchant_api_get_otp_devices.c \
- merchant_api_get_product.c \
- merchant_api_get_product_image.c \
- merchant_api_get_products.c \
- merchant_api_get_statistics.c \
- merchant_api_get_transfers.c \
- merchant_api_get_template.c \
- merchant_api_get_unit.c \
- merchant_api_get_units.c \
- merchant_api_get_templates.c \
- merchant_api_get_webhook.c \
- merchant_api_get_webhooks.c \
- merchant_api_lock_product.c \
- merchant_api_merchant_get_order.c \
- merchant_api_patch_account.c \
- merchant_api_patch_instance.c \
- merchant_api_patch_order_forget.c \
- merchant_api_patch_otp_device.c \
- merchant_api_patch_product.c \
- merchant_api_patch_template.c \
- merchant_api_patch_unit.c \
- merchant_api_patch_webhook.c \
- merchant_api_post_account.c \
- merchant_api_post_instance_auth.c \
- merchant_api_post_instance_token.c \
- merchant_api_post_instances.c \
- merchant_api_post_orders.c \
- merchant_api_post_order_abort.c \
- merchant_api_post_order_claim.c \
- merchant_api_post_order_paid.c \
- merchant_api_post_order_pay.c \
- merchant_api_post_order_refund.c \
- merchant_api_post_otp_devices.c \
- merchant_api_post_categories.c \
- merchant_api_post_products.c \
- merchant_api_post_units.c \
- merchant_api_post_transfers.c \
- merchant_api_post_templates.c \
- merchant_api_post_tokenfamilies.c \
- 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 \
- taler_merchant_pay_service.c
+ merchant_api_delete-management-instances-INSTANCE.c \
+ merchant_api_delete-private-accounts-H_WIRE.c \
+ merchant_api_delete-private-orders-ORDER_ID.c \
+ merchant_api_delete-private-otp-devices-DEVICE_ID.c \
+ merchant_api_delete-private-products-PRODUCT_ID.c \
+ merchant_api_delete-private-templates-TEMPLATE_ID.c \
+ merchant_api_delete-private-tokens-SERIAL.c \
+ merchant_api_delete-private-transfers-TID.c \
+ merchant_api_delete-private-units-UNIT.c \
+ merchant_api_delete-private-webhooks-WEBHOOK_ID.c \
+ merchant_api_get-config.c \
+ merchant_api_get-management-instances.c \
+ merchant_api_get-management-instances-INSTANCE.c \
+ merchant_api_get-orders-ORDER_ID.c \
+ merchant_api_get-private-accounts.c \
+ merchant_api_get-private-accounts-H_WIRE.c \
+ merchant_api_get-private-kyc.c \
+ merchant_api_get-private-orders.c \
+ merchant_api_get-private-orders-ORDER_ID.c \
+ merchant_api_get-private-otp-devices.c \
+ merchant_api_get-private-otp-devices-DEVICE_ID.c \
+ merchant_api_get-private-products.c \
+ merchant_api_get-private-products-PRODUCT_ID.c \
+ merchant_api_get-private-statistics-amount-SLUG.c \
+ merchant_api_get-private-statistics-counter-SLUG.c \
+ merchant_api_get-private-templates.c \
+ merchant_api_get-private-templates-TEMPLATE_ID.c \
+ merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.c \
+ merchant_api_get-private-transfers.c \
+ merchant_api_get-private-units.c \
+ merchant_api_get-private-units-UNIT.c \
+ merchant_api_get-private-webhooks.c \
+ merchant_api_get-private-webhooks-WEBHOOK_ID.c \
+ merchant_api_get-products-IMAGE_HASH-image.c \
+ merchant_api_get-templates-TEMPLATE_ID.c \
+ merchant_api_patch-management-instances-INSTANCE.c \
+ merchant_api_patch-private-accounts-H_WIRE.c \
+ merchant_api_patch-private-orders-ORDER_ID-forget.c \
+ merchant_api_patch-private-otp-devices-DEVICE_ID.c \
+ merchant_api_patch-private-products-PRODUCT_ID.c \
+ merchant_api_patch-private-templates-TEMPLATE_ID.c \
+ merchant_api_patch-private-units-UNIT.c \
+ merchant_api_patch-private-webhooks-WEBHOOK_ID.c \
+ merchant_api_pay_service.c \
+ merchant_api_post-management-instances.c \
+ merchant_api_post-management-instances-INSTANCE-auth.c \
+ merchant_api_post-orders-ORDER_ID-abort.c \
+ merchant_api_post-orders-ORDER_ID-claim.c \
+ merchant_api_post-orders-ORDER_ID-paid.c \
+ merchant_api_post-orders-ORDER_ID-pay.c \
+ merchant_api_post-orders-ORDER_ID-refund.c \
+ merchant_api_post-private-accounts.c \
+ merchant_api_post-private-categories.c \
+ merchant_api_post-private-orders.c \
+ merchant_api_post-private-orders-ORDER_ID-refund.c \
+ merchant_api_post-private-otp-devices.c \
+ merchant_api_post-private-products.c \
+ merchant_api_post-private-products-PRODUCT_ID-lock.c \
+ merchant_api_post-private-templates.c \
+ merchant_api_post-private-token.c \
+ merchant_api_post-private-tokenfamilies.c \
+ merchant_api_post-private-transfers.c \
+ merchant_api_post-private-units.c \
+ merchant_api_post-private-webhooks.c \
+ merchant_api_post-templates-TEMPLATE_ID.c
libtalermerchant_la_LIBADD = \
-ltalerexchange \
@@ -95,9 +97,9 @@ libtalermerchant_la_LIBADD = \
if HAVE_DONAU
libtalermerchant_la_SOURCES += \
- merchant_api_get_donau_instance.c \
- merchant_api_post_donau_instance.c \
- merchant_api_delete_donau_instance.c
+ merchant_api_get-private-donau.c \
+ merchant_api_post-private-donau.c \
+ merchant_api_delete-private-donau-DONAU_SERIAL.c
libtalermerchant_la_LIBADD += \
-ldonau
diff --git a/src/lib/merchant_api_curl_defaults.c b/src/lib/merchant_api_curl_defaults.c
@@ -15,7 +15,7 @@
<http://www.gnu.org/licenses/>
*/
/**
- * @file lib/merchant_api_curl_defaults.c
+ * @file merchant_api_curl_defaults.c
* @brief curl easy handle defaults
* @author Florian Dold
*/
diff --git a/src/lib/merchant_api_delete-management-instances-INSTANCE.c b/src/lib/merchant_api_delete-management-instances-INSTANCE.c
@@ -0,0 +1,236 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_delete-management-instances-INSTANCE.c
+ * @brief Implementation of the DELETE /instance/$ID request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /instances/$ID operation.
+ */
+struct TALER_MERCHANT_InstanceDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_InstanceDeleteCallback 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 /instances/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_InstanceDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_instance_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_InstanceDeleteHandle *idh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ idh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /instances/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /instance/ID\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ break;
+ }
+ idh->cb (idh->cb_cls,
+ &hr);
+ TALER_MERCHANT_instance_delete_cancel (idh);
+}
+
+
+/**
+ * Delete the private key of an instance of a backend, thereby disabling the
+ * instance for future requests. Will preserve the other instance data
+ * (i.e. for taxation).
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param instance_id which instance should be deleted
+ * @param purge purge instead of just deleting
+ * @param cb function to call with the
+ * backend's return
+ * @param cb_cls closure for @a config_cb
+ * @return the instances handle; NULL upon error
+ */
+static struct TALER_MERCHANT_InstanceDeleteHandle *
+instance_delete (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ bool purge,
+ TALER_MERCHANT_InstanceDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_InstanceDeleteHandle *idh;
+
+ idh = GNUNET_new (struct TALER_MERCHANT_InstanceDeleteHandle);
+ idh->ctx = ctx;
+ idh->cb = cb;
+ idh->cb_cls = cb_cls;
+ if (NULL != instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "management/instances/%s",
+ instance_id);
+ idh->url = TALER_url_join (backend_url,
+ path,
+ "purge",
+ (purge) ? "yes" : NULL,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ /* backend_url is already identifying the instance */
+ idh->url = TALER_url_join (backend_url,
+ "private",
+ "purge",
+ (purge) ? "yes" : NULL,
+ NULL);
+ }
+ if (NULL == idh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (idh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ idh->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (idh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ idh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_instance_finished,
+ idh);
+ }
+ return idh;
+}
+
+
+struct TALER_MERCHANT_InstanceDeleteHandle *
+TALER_MERCHANT_instance_delete (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ TALER_MERCHANT_InstanceDeleteCallback cb,
+ void *cb_cls)
+{
+ return instance_delete (ctx,
+ backend_url,
+ instance_id,
+ false,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_InstanceDeleteHandle *
+TALER_MERCHANT_instance_purge (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ TALER_MERCHANT_InstanceDeleteCallback cb,
+ void *cb_cls)
+{
+ return instance_delete (ctx,
+ backend_url,
+ instance_id,
+ true,
+ cb,
+ cb_cls);
+}
+
+
+void
+TALER_MERCHANT_instance_delete_cancel (
+ struct TALER_MERCHANT_InstanceDeleteHandle *idh)
+{
+ if (NULL != idh->job)
+ GNUNET_CURL_job_cancel (idh->job);
+ GNUNET_free (idh->url);
+ GNUNET_free (idh);
+}
diff --git a/src/lib/merchant_api_delete-private-accounts-H_WIRE.c b/src/lib/merchant_api_delete-private-accounts-H_WIRE.c
@@ -0,0 +1,185 @@
+/*
+ This file is part of TALER
+ Copyright (C) 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_delete-private-accounts-H_WIRE.c
+ * @brief Implementation of the DELETE /private/account/$H_WIRE request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /accounts/$ID operation.
+ */
+struct TALER_MERCHANT_AccountDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_AccountDeleteCallback 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 DELETE /accounts/$H_WIRE request.
+ *
+ * @param cls the `struct TALER_MERCHANT_AccountDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_account_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_AccountDeleteHandle *adh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_AccountDeleteResponse adr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ adh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /accounts/$H_WIRE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ adr.hr.ec = TALER_JSON_get_error_code (json);
+ adr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ break;
+ default:
+ /* unexpected response code */
+ adr.hr.ec = TALER_JSON_get_error_code (json);
+ adr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /account/ID\n",
+ (unsigned int) response_code,
+ (int) adr.hr.ec);
+ break;
+ }
+ adh->cb (adh->cb_cls,
+ &adr);
+ TALER_MERCHANT_account_delete_cancel (adh);
+}
+
+
+struct TALER_MERCHANT_AccountDeleteHandle *
+TALER_MERCHANT_account_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const struct TALER_MerchantWireHashP *h_wire,
+ TALER_MERCHANT_AccountDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_AccountDeleteHandle *adh;
+
+ adh = GNUNET_new (struct TALER_MERCHANT_AccountDeleteHandle);
+ adh->ctx = ctx;
+ adh->cb = cb;
+ adh->cb_cls = cb_cls;
+ {
+ char h_wire_str[sizeof (*h_wire) * 2];
+ char *path;
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (h_wire,
+ sizeof (*h_wire),
+ h_wire_str,
+ sizeof (h_wire_str));
+ *end = '\0';
+ GNUNET_asprintf (&path,
+ "private/account/%s",
+ h_wire_str);
+ adh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == adh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (adh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ adh->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (adh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ adh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_account_finished,
+ adh);
+ }
+ return adh;
+}
+
+
+void
+TALER_MERCHANT_account_delete_cancel (
+ struct TALER_MERCHANT_AccountDeleteHandle *adh)
+{
+ if (NULL != adh->job)
+ GNUNET_CURL_job_cancel (adh->job);
+ GNUNET_free (adh->url);
+ GNUNET_free (adh);
+}
diff --git a/src/lib/merchant_api_delete-private-donau-DONAU_SERIAL.c b/src/lib/merchant_api_delete-private-donau-DONAU_SERIAL.c
@@ -0,0 +1,198 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 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_delete-private-donau-DONAU_SERIAL.c
+ * @brief Implementation of the DELETE /donau/$charity_id request of the merchant's HTTP API
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+/* DONAU RELATED IMPORTS */
+#include "taler/taler_merchant_donau.h"
+#include <donau/donau_service.h>
+
+
+/**
+ * Handle for a DELETE /donau/$charity_id operation.
+ */
+struct TALER_MERCHANT_DonauInstanceDeleteHandle
+{
+ /**
+ * The URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DonauInstanceDeleteCallback 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 DELETE /donau/$charity_id request.
+ *
+ * @param cls the struct TALER_MERCHANT_DonauInstanceDeleteHandle
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_donau_instance_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ ddh->job = NULL;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /donau/$charity_id response with status code %u\n",
+ (unsigned int) response_code);
+
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* Unexpected response */
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /donau/$charity_id\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ break;
+ }
+ ddh->cb (ddh->cb_cls,
+ &hr);
+ TALER_MERCHANT_donau_instance_delete_cancel (ddh);
+}
+
+
+/**
+ * Initiates the DELETE /donau/$charity_id operation.
+ *
+ * @param ctx CURL context
+ * @param backend_url Base URL for the backend
+ * @param charity_id The ID of the charity to delete
+ * @param cb Callback function to handle the response
+ * @param cb_cls Closure for @a cb
+ * @return the handle for the operation, or NULL on error
+ */
+struct TALER_MERCHANT_DonauInstanceDeleteHandle *
+TALER_MERCHANT_donau_instance_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ uint64_t charity_id,
+ TALER_MERCHANT_DonauInstanceDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh;
+ char *charity_id_str;
+ CURL *eh;
+
+ GNUNET_asprintf (&charity_id_str,
+ "private/donau/%ld",
+ charity_id);
+ if (NULL == charity_id_str)
+ return NULL;
+
+ ddh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceDeleteHandle);
+ ddh->ctx = ctx;
+ ddh->cb = cb;
+ ddh->cb_cls = cb_cls;
+
+ ddh->url = TALER_url_join (backend_url,
+ charity_id_str,
+ NULL);
+ GNUNET_free (charity_id_str);
+
+ if (NULL == ddh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (ddh);
+ return NULL;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ ddh->url);
+
+ eh = TALER_MERCHANT_curl_easy_get_ (ddh->url);
+ GNUNET_assert (CURLE_OK == curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ "DELETE"));
+ ddh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_donau_instance_finished,
+ ddh);
+
+ return ddh;
+}
+
+
+/**
+ * Cancel the DELETE /donau/$charity_id operation.
+ *
+ * @param ddh Handle for the operation to cancel.
+ */
+void
+TALER_MERCHANT_donau_instance_delete_cancel (
+ struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh)
+{
+ if (NULL != ddh->job)
+ GNUNET_CURL_job_cancel (ddh->job);
+
+ GNUNET_free (ddh->url);
+ GNUNET_free (ddh);
+}
+\ No newline at end of file
diff --git a/src/lib/merchant_api_delete-private-orders-ORDER_ID.c b/src/lib/merchant_api_delete-private-orders-ORDER_ID.c
@@ -0,0 +1,177 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_delete-private-orders-ORDER_ID.c
+ * @brief Implementation of the DELETE /orders/$ORDER_ID request of the merchant's HTTP API
+ * @author Jonathan Buchanan
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+/**
+ * Handle for a DELETE /orders/$ID operation.
+ */
+struct TALER_MERCHANT_OrderDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OrderDeleteCallback 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 DELETE /orders/$ORDER_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_order_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderDeleteHandle *odh = cls;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = NULL,
+ };
+
+ odh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /orders/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (response);
+ hr.hint = TALER_JSON_get_error_hint (response);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ break;
+ case MHD_HTTP_CONFLICT:
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ (unsigned int) response_code);
+ break;
+ }
+ odh->cb (odh->cb_cls,
+ &hr);
+ TALER_MERCHANT_order_delete_cancel (odh);
+}
+
+
+struct TALER_MERCHANT_OrderDeleteHandle *
+TALER_MERCHANT_order_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *order_id,
+ bool force,
+ TALER_MERCHANT_OrderDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OrderDeleteHandle *odh;
+
+ odh = GNUNET_new (struct TALER_MERCHANT_OrderDeleteHandle);
+ odh->ctx = ctx;
+ odh->cb = cb;
+ odh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/orders/%s%s",
+ order_id,
+ force
+ ? "?force=yes"
+ : "");
+
+ odh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == odh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request url.\n");
+ GNUNET_free (odh);
+ return NULL;
+ }
+
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (odh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ odh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_order_finished,
+ odh);
+ }
+ return odh;
+}
+
+
+void
+TALER_MERCHANT_order_delete_cancel (
+ struct TALER_MERCHANT_OrderDeleteHandle *odh)
+{
+ if (NULL != odh->job)
+ GNUNET_CURL_job_cancel (odh->job);
+ GNUNET_free (odh->url);
+ GNUNET_free (odh);
+}
diff --git a/src/lib/merchant_api_delete-private-otp-devices-DEVICE_ID.c b/src/lib/merchant_api_delete-private-otp-devices-DEVICE_ID.c
@@ -0,0 +1,184 @@
+/*
+ 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_delete-private-otp-devices-DEVICE_ID.c
+ * @brief Implementation of the DELETE /otp-devices/$ID request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /otp-devices/$ID operation.
+ */
+struct TALER_MERCHANT_OtpDeviceDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OtpDeviceDeleteCallback 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 /otp-devices/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OtpDeviceDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_otp_device_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tdh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got DELETE /otp-devices/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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) hr.ec);
+ break;
+ }
+ tdh->cb (tdh->cb_cls,
+ &hr);
+ TALER_MERCHANT_otp_device_delete_cancel (tdh);
+}
+
+
+struct TALER_MERCHANT_OtpDeviceDeleteHandle *
+TALER_MERCHANT_otp_device_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *otp_device_id,
+ TALER_MERCHANT_OtpDeviceDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh;
+
+ tdh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceDeleteHandle);
+ tdh->ctx = ctx;
+ tdh->cb = cb;
+ tdh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/otp-devices/%s",
+ otp_device_id);
+ tdh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == tdh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (tdh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ tdh->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ tdh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_otp_device_finished,
+ tdh);
+ }
+ return tdh;
+}
+
+
+void
+TALER_MERCHANT_otp_device_delete_cancel (
+ struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh)
+{
+ if (NULL != tdh->job)
+ GNUNET_CURL_job_cancel (tdh->job);
+ GNUNET_free (tdh->url);
+ GNUNET_free (tdh);
+}
diff --git a/src/lib/merchant_api_delete-private-products-PRODUCT_ID.c b/src/lib/merchant_api_delete-private-products-PRODUCT_ID.c
@@ -0,0 +1,184 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_delete-private-products-PRODUCT_ID.c
+ * @brief Implementation of the DELETE /product/$ID request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /products/$ID operation.
+ */
+struct TALER_MERCHANT_ProductDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ProductDeleteCallback 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 /products/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_ProductDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_product_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_ProductDeleteHandle *pdh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ pdh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /products/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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) hr.ec);
+ break;
+ }
+ pdh->cb (pdh->cb_cls,
+ &hr);
+ TALER_MERCHANT_product_delete_cancel (pdh);
+}
+
+
+struct TALER_MERCHANT_ProductDeleteHandle *
+TALER_MERCHANT_product_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ TALER_MERCHANT_ProductDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_ProductDeleteHandle *pdh;
+
+ pdh = GNUNET_new (struct TALER_MERCHANT_ProductDeleteHandle);
+ pdh->ctx = ctx;
+ pdh->cb = cb;
+ pdh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/products/%s",
+ product_id);
+ pdh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pdh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (pdh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ pdh->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (pdh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ pdh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_product_finished,
+ pdh);
+ }
+ return pdh;
+}
+
+
+void
+TALER_MERCHANT_product_delete_cancel (
+ struct TALER_MERCHANT_ProductDeleteHandle *pdh)
+{
+ if (NULL != pdh->job)
+ GNUNET_CURL_job_cancel (pdh->job);
+ GNUNET_free (pdh->url);
+ GNUNET_free (pdh);
+}
diff --git a/src/lib/merchant_api_delete-private-templates-TEMPLATE_ID.c b/src/lib/merchant_api_delete-private-templates-TEMPLATE_ID.c
@@ -0,0 +1,184 @@
+/*
+ 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_delete-private-templates-TEMPLATE_ID.c
+ * @brief Implementation of the DELETE /templates/$ID request of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /templates/$ID operation.
+ */
+struct TALER_MERCHANT_TemplateDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TemplateDeleteCallback 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_TemplateDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_template_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_TemplateDeleteHandle *tdh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tdh->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_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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) hr.ec);
+ break;
+ }
+ tdh->cb (tdh->cb_cls,
+ &hr);
+ TALER_MERCHANT_template_delete_cancel (tdh);
+}
+
+
+struct TALER_MERCHANT_TemplateDeleteHandle *
+TALER_MERCHANT_template_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *template_id,
+ TALER_MERCHANT_TemplateDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TemplateDeleteHandle *tdh;
+
+ tdh = GNUNET_new (struct TALER_MERCHANT_TemplateDeleteHandle);
+ tdh->ctx = ctx;
+ tdh->cb = cb;
+ tdh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/templates/%s",
+ template_id);
+ tdh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == tdh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (tdh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ tdh->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ tdh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_template_finished,
+ tdh);
+ }
+ return tdh;
+}
+
+
+void
+TALER_MERCHANT_template_delete_cancel (
+ struct TALER_MERCHANT_TemplateDeleteHandle *tdh)
+{
+ if (NULL != tdh->job)
+ GNUNET_CURL_job_cancel (tdh->job);
+ GNUNET_free (tdh->url);
+ GNUNET_free (tdh);
+}
diff --git a/src/lib/merchant_api_delete-private-tokens-SERIAL.c b/src/lib/merchant_api_delete-private-tokens-SERIAL.c
@@ -0,0 +1,178 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_delete-private-tokens-SERIAL.c
+ * @brief Implementation of the DELETE /instance/$ID/private/token request of the merchant's HTTP API
+ * @author Martin Schanzenbach
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /instance/$ID/private/token operation.
+ */
+struct TALER_MERCHANT_InstanceTokenDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_InstanceTokenDeleteCallback 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 DELETE /instance/$ID/private/token request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TokenDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_token_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse tdr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tdh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /instances/$ID/private/token response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ tdr.ec = TALER_JSON_get_error_code (json);
+ tdr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ break;
+ default:
+ /* unexpected response code */
+ tdr.ec = TALER_JSON_get_error_code (json);
+ tdr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /instance/$ID/private/token\n",
+ (unsigned int) response_code,
+ (int) tdr.ec);
+ break;
+ }
+ tdh->cb (tdh->cb_cls,
+ &tdr);
+ TALER_MERCHANT_instance_token_delete_cancel (tdh);
+}
+
+
+struct TALER_MERCHANT_InstanceTokenDeleteHandle *
+TALER_MERCHANT_instance_token_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ TALER_MERCHANT_InstanceTokenDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh;
+
+ tdh = GNUNET_new (struct TALER_MERCHANT_InstanceTokenDeleteHandle);
+ tdh->ctx = ctx;
+ tdh->cb = cb;
+ tdh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "instances/%s/private/token",
+ instance_id);
+ tdh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == tdh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (tdh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ tdh->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ tdh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_token_finished,
+ tdh);
+ }
+ return tdh;
+}
+
+
+void
+TALER_MERCHANT_instance_token_delete_cancel (
+ struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh)
+{
+ if (NULL != tdh->job)
+ GNUNET_CURL_job_cancel (tdh->job);
+ GNUNET_free (tdh->url);
+ GNUNET_free (tdh);
+}
diff --git a/src/lib/merchant_api_delete-private-transfers-TID.c b/src/lib/merchant_api_delete-private-transfers-TID.c
@@ -0,0 +1,184 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 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_delete-private-transfers-TID.c
+ * @brief Implementation of the DELETE /transfer/$ID request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /transfers/$ID operation.
+ */
+struct TALER_MERCHANT_TransferDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TransferDeleteCallback 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 /transfers/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TransferDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_transfer_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_TransferDeleteHandle *tdh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tdh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /transfers/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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) hr.ec);
+ break;
+ }
+ tdh->cb (tdh->cb_cls,
+ &hr);
+ TALER_MERCHANT_transfer_delete_cancel (tdh);
+}
+
+
+struct TALER_MERCHANT_TransferDeleteHandle *
+TALER_MERCHANT_transfer_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ uint64_t wire_transfer_serial,
+ TALER_MERCHANT_TransferDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TransferDeleteHandle *tdh;
+
+ tdh = GNUNET_new (struct TALER_MERCHANT_TransferDeleteHandle);
+ tdh->ctx = ctx;
+ tdh->cb = cb;
+ tdh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/transfers/%llu",
+ (unsigned long long) wire_transfer_serial);
+ tdh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == tdh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (tdh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ tdh->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ tdh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_transfer_finished,
+ tdh);
+ }
+ return tdh;
+}
+
+
+void
+TALER_MERCHANT_transfer_delete_cancel (
+ struct TALER_MERCHANT_TransferDeleteHandle *tdh)
+{
+ if (NULL != tdh->job)
+ GNUNET_CURL_job_cancel (tdh->job);
+ GNUNET_free (tdh->url);
+ GNUNET_free (tdh);
+}
diff --git a/src/lib/merchant_api_delete-private-units-UNIT.c b/src/lib/merchant_api_delete-private-units-UNIT.c
@@ -0,0 +1,177 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_delete-private-units-UNIT.c
+ * @brief Implementation of DELETE /private/units/$ID
+ * @author Bohdan Potuzhnyi
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/units/$ID operation.
+ */
+struct TALER_MERCHANT_UnitDeleteHandle
+{
+ /**
+ * Fully qualified request URL.
+ */
+ char *url;
+
+ /**
+ * In-flight CURL job.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Completion callback.
+ */
+ TALER_MERCHANT_UnitDeleteCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Called when the HTTP request finishes.
+ *
+ * @param cls operation handle
+ * @param response_code HTTP status (0 on failure)
+ * @param response parsed JSON reply (NULL if unavailable)
+ */
+static void
+handle_delete_unit_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_UnitDeleteHandle *udh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ udh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "DELETE /private/units finished with status %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ case MHD_HTTP_UNAUTHORIZED:
+ case MHD_HTTP_FORBIDDEN:
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_CONFLICT:
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response %u/%d for DELETE /private/units\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ udh->cb (udh->cb_cls,
+ &hr);
+ TALER_MERCHANT_unit_delete_cancel (udh);
+}
+
+
+struct TALER_MERCHANT_UnitDeleteHandle *
+TALER_MERCHANT_unit_delete (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *unit_id,
+ TALER_MERCHANT_UnitDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_UnitDeleteHandle *udh;
+ CURL *eh;
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/units/%s",
+ unit_id);
+ udh = GNUNET_new (struct TALER_MERCHANT_UnitDeleteHandle);
+ udh->ctx = ctx;
+ udh->cb = cb;
+ udh->cb_cls = cb_cls;
+ udh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ if (NULL == udh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to build /private/units/%s URL\n",
+ unit_id);
+ GNUNET_free (udh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting DELETE on '%s'\n",
+ udh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (udh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ udh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_unit_finished,
+ udh);
+ return udh;
+}
+
+
+void
+TALER_MERCHANT_unit_delete_cancel (struct TALER_MERCHANT_UnitDeleteHandle *udh)
+{
+ if (NULL != udh->job)
+ GNUNET_CURL_job_cancel (udh->job);
+ GNUNET_free (udh->url);
+ GNUNET_free (udh);
+}
+
+
+/* end of merchant_api_delete_unit.c */
diff --git a/src/lib/merchant_api_delete-private-webhooks-WEBHOOK_ID.c b/src/lib/merchant_api_delete-private-webhooks-WEBHOOK_ID.c
@@ -0,0 +1,184 @@
+/*
+ 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_delete-private-webhooks-WEBHOOK_ID.c
+ * @brief Implementation of the DELETE /webhooks/$ID request of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /webhooks/$ID operation.
+ */
+struct TALER_MERCHANT_WebhookDeleteHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_WebhookDeleteCallback 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 /webhooks/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_WebhookDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_webhook_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_WebhookDeleteHandle *wdh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ wdh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /webhooks/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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) hr.ec);
+ break;
+ }
+ wdh->cb (wdh->cb_cls,
+ &hr);
+ TALER_MERCHANT_webhook_delete_cancel (wdh);
+}
+
+
+struct TALER_MERCHANT_WebhookDeleteHandle *
+TALER_MERCHANT_webhook_delete (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *webhook_id,
+ TALER_MERCHANT_WebhookDeleteCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_WebhookDeleteHandle *wdh;
+
+ wdh = GNUNET_new (struct TALER_MERCHANT_WebhookDeleteHandle);
+ wdh->ctx = ctx;
+ wdh->cb = cb;
+ wdh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/webhooks/%s",
+ webhook_id);
+ wdh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == wdh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (wdh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ wdh->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (wdh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ wdh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_delete_webhook_finished,
+ wdh);
+ }
+ return wdh;
+}
+
+
+void
+TALER_MERCHANT_webhook_delete_cancel (
+ struct TALER_MERCHANT_WebhookDeleteHandle *wdh)
+{
+ if (NULL != wdh->job)
+ GNUNET_CURL_job_cancel (wdh->job);
+ GNUNET_free (wdh->url);
+ GNUNET_free (wdh);
+}
diff --git a/src/lib/merchant_api_delete_account.c b/src/lib/merchant_api_delete_account.c
@@ -1,185 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_delete_account.c
- * @brief Implementation of the DELETE /private/account/$H_WIRE request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a DELETE /accounts/$ID operation.
- */
-struct TALER_MERCHANT_AccountDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_AccountDeleteCallback 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 DELETE /accounts/$H_WIRE request.
- *
- * @param cls the `struct TALER_MERCHANT_AccountDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_account_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_AccountDeleteHandle *adh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_AccountDeleteResponse adr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- adh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /accounts/$H_WIRE response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- adr.hr.ec = TALER_JSON_get_error_code (json);
- adr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- break;
- default:
- /* unexpected response code */
- adr.hr.ec = TALER_JSON_get_error_code (json);
- adr.hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d for DELETE /account/ID\n",
- (unsigned int) response_code,
- (int) adr.hr.ec);
- break;
- }
- adh->cb (adh->cb_cls,
- &adr);
- TALER_MERCHANT_account_delete_cancel (adh);
-}
-
-
-struct TALER_MERCHANT_AccountDeleteHandle *
-TALER_MERCHANT_account_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const struct TALER_MerchantWireHashP *h_wire,
- TALER_MERCHANT_AccountDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_AccountDeleteHandle *adh;
-
- adh = GNUNET_new (struct TALER_MERCHANT_AccountDeleteHandle);
- adh->ctx = ctx;
- adh->cb = cb;
- adh->cb_cls = cb_cls;
- {
- char h_wire_str[sizeof (*h_wire) * 2];
- char *path;
- char *end;
-
- end = GNUNET_STRINGS_data_to_string (h_wire,
- sizeof (*h_wire),
- h_wire_str,
- sizeof (h_wire_str));
- *end = '\0';
- GNUNET_asprintf (&path,
- "private/account/%s",
- h_wire_str);
- adh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == adh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (adh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- adh->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (adh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- adh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_account_finished,
- adh);
- }
- return adh;
-}
-
-
-void
-TALER_MERCHANT_account_delete_cancel (
- struct TALER_MERCHANT_AccountDeleteHandle *adh)
-{
- if (NULL != adh->job)
- GNUNET_CURL_job_cancel (adh->job);
- GNUNET_free (adh->url);
- GNUNET_free (adh);
-}
diff --git a/src/lib/merchant_api_delete_donau_instance.c b/src/lib/merchant_api_delete_donau_instance.c
@@ -1,198 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2024 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_delete_donau_instance.c
- * @brief Implementation of the DELETE /donau/$charity_id request of the merchant's HTTP API
- * @author Bohdan Potuzhnyi
- * @author Vlada Svirsh
- */
-
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-/* DONAU RELATED IMPORTS */
-#include "taler/taler_merchant_donau.h"
-#include <donau/donau_service.h>
-
-
-/**
- * Handle for a DELETE /donau/$charity_id operation.
- */
-struct TALER_MERCHANT_DonauInstanceDeleteHandle
-{
- /**
- * The URL for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_DonauInstanceDeleteCallback 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 DELETE /donau/$charity_id request.
- *
- * @param cls the struct TALER_MERCHANT_DonauInstanceDeleteHandle
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_donau_instance_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- ddh->job = NULL;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /donau/$charity_id response with status code %u\n",
- (unsigned int) response_code);
-
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_NOT_FOUND:
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* Unexpected response */
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d for DELETE /donau/$charity_id\n",
- (unsigned int) response_code,
- (int) hr.ec);
- break;
- }
- ddh->cb (ddh->cb_cls,
- &hr);
- TALER_MERCHANT_donau_instance_delete_cancel (ddh);
-}
-
-
-/**
- * Initiates the DELETE /donau/$charity_id operation.
- *
- * @param ctx CURL context
- * @param backend_url Base URL for the backend
- * @param charity_id The ID of the charity to delete
- * @param cb Callback function to handle the response
- * @param cb_cls Closure for @a cb
- * @return the handle for the operation, or NULL on error
- */
-struct TALER_MERCHANT_DonauInstanceDeleteHandle *
-TALER_MERCHANT_donau_instance_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- uint64_t charity_id,
- TALER_MERCHANT_DonauInstanceDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh;
- char *charity_id_str;
- CURL *eh;
-
- GNUNET_asprintf (&charity_id_str,
- "private/donau/%ld",
- charity_id);
- if (NULL == charity_id_str)
- return NULL;
-
- ddh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceDeleteHandle);
- ddh->ctx = ctx;
- ddh->cb = cb;
- ddh->cb_cls = cb_cls;
-
- ddh->url = TALER_url_join (backend_url,
- charity_id_str,
- NULL);
- GNUNET_free (charity_id_str);
-
- if (NULL == ddh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (ddh);
- return NULL;
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- ddh->url);
-
- eh = TALER_MERCHANT_curl_easy_get_ (ddh->url);
- GNUNET_assert (CURLE_OK == curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- "DELETE"));
- ddh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_donau_instance_finished,
- ddh);
-
- return ddh;
-}
-
-
-/**
- * Cancel the DELETE /donau/$charity_id operation.
- *
- * @param ddh Handle for the operation to cancel.
- */
-void
-TALER_MERCHANT_donau_instance_delete_cancel (
- struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh)
-{
- if (NULL != ddh->job)
- GNUNET_CURL_job_cancel (ddh->job);
-
- GNUNET_free (ddh->url);
- GNUNET_free (ddh);
-}
-\ No newline at end of file
diff --git a/src/lib/merchant_api_delete_instance.c b/src/lib/merchant_api_delete_instance.c
@@ -1,236 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_delete_instance.c
- * @brief Implementation of the DELETE /instance/$ID request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a DELETE /instances/$ID operation.
- */
-struct TALER_MERCHANT_InstanceDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_InstanceDeleteCallback 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 /instances/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_InstanceDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_instance_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_InstanceDeleteHandle *idh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- idh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /instances/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d for DELETE /instance/ID\n",
- (unsigned int) response_code,
- (int) hr.ec);
- break;
- }
- idh->cb (idh->cb_cls,
- &hr);
- TALER_MERCHANT_instance_delete_cancel (idh);
-}
-
-
-/**
- * Delete the private key of an instance of a backend, thereby disabling the
- * instance for future requests. Will preserve the other instance data
- * (i.e. for taxation).
- *
- * @param ctx the context
- * @param backend_url HTTP base URL for the backend
- * @param instance_id which instance should be deleted
- * @param purge purge instead of just deleting
- * @param cb function to call with the
- * backend's return
- * @param cb_cls closure for @a config_cb
- * @return the instances handle; NULL upon error
- */
-static struct TALER_MERCHANT_InstanceDeleteHandle *
-instance_delete (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- bool purge,
- TALER_MERCHANT_InstanceDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_InstanceDeleteHandle *idh;
-
- idh = GNUNET_new (struct TALER_MERCHANT_InstanceDeleteHandle);
- idh->ctx = ctx;
- idh->cb = cb;
- idh->cb_cls = cb_cls;
- if (NULL != instance_id)
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "management/instances/%s",
- instance_id);
- idh->url = TALER_url_join (backend_url,
- path,
- "purge",
- (purge) ? "yes" : NULL,
- NULL);
- GNUNET_free (path);
- }
- else
- {
- /* backend_url is already identifying the instance */
- idh->url = TALER_url_join (backend_url,
- "private",
- "purge",
- (purge) ? "yes" : NULL,
- NULL);
- }
- if (NULL == idh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (idh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- idh->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (idh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- idh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_instance_finished,
- idh);
- }
- return idh;
-}
-
-
-struct TALER_MERCHANT_InstanceDeleteHandle *
-TALER_MERCHANT_instance_delete (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- TALER_MERCHANT_InstanceDeleteCallback cb,
- void *cb_cls)
-{
- return instance_delete (ctx,
- backend_url,
- instance_id,
- false,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_InstanceDeleteHandle *
-TALER_MERCHANT_instance_purge (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- TALER_MERCHANT_InstanceDeleteCallback cb,
- void *cb_cls)
-{
- return instance_delete (ctx,
- backend_url,
- instance_id,
- true,
- cb,
- cb_cls);
-}
-
-
-void
-TALER_MERCHANT_instance_delete_cancel (
- struct TALER_MERCHANT_InstanceDeleteHandle *idh)
-{
- if (NULL != idh->job)
- GNUNET_CURL_job_cancel (idh->job);
- GNUNET_free (idh->url);
- GNUNET_free (idh);
-}
diff --git a/src/lib/merchant_api_delete_instance_token.c b/src/lib/merchant_api_delete_instance_token.c
@@ -1,178 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_delete_instance_token.c
- * @brief Implementation of the DELETE /instance/$ID/private/token request of the merchant's HTTP API
- * @author Martin Schanzenbach
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a DELETE /instance/$ID/private/token operation.
- */
-struct TALER_MERCHANT_InstanceTokenDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_InstanceTokenDeleteCallback 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 DELETE /instance/$ID/private/token request.
- *
- * @param cls the `struct TALER_MERCHANT_TokenDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_token_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse tdr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tdh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /instances/$ID/private/token response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- tdr.ec = TALER_JSON_get_error_code (json);
- tdr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- break;
- default:
- /* unexpected response code */
- tdr.ec = TALER_JSON_get_error_code (json);
- tdr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d for DELETE /instance/$ID/private/token\n",
- (unsigned int) response_code,
- (int) tdr.ec);
- break;
- }
- tdh->cb (tdh->cb_cls,
- &tdr);
- TALER_MERCHANT_instance_token_delete_cancel (tdh);
-}
-
-
-struct TALER_MERCHANT_InstanceTokenDeleteHandle *
-TALER_MERCHANT_instance_token_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- TALER_MERCHANT_InstanceTokenDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh;
-
- tdh = GNUNET_new (struct TALER_MERCHANT_InstanceTokenDeleteHandle);
- tdh->ctx = ctx;
- tdh->cb = cb;
- tdh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "instances/%s/private/token",
- instance_id);
- tdh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == tdh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (tdh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- tdh->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- tdh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_token_finished,
- tdh);
- }
- return tdh;
-}
-
-
-void
-TALER_MERCHANT_instance_token_delete_cancel (
- struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh)
-{
- if (NULL != tdh->job)
- GNUNET_CURL_job_cancel (tdh->job);
- GNUNET_free (tdh->url);
- GNUNET_free (tdh);
-}
diff --git a/src/lib/merchant_api_delete_order.c b/src/lib/merchant_api_delete_order.c
@@ -1,177 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2020-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_delete_order.c
- * @brief Implementation of the DELETE /orders/$ORDER_ID request of the merchant's HTTP API
- * @author Jonathan Buchanan
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-/**
- * Handle for a DELETE /orders/$ID operation.
- */
-struct TALER_MERCHANT_OrderDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OrderDeleteCallback 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 DELETE /orders/$ORDER_ID request.
- *
- * @param cls the `struct TALER_MERCHANT_OrderDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_order_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderDeleteHandle *odh = cls;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = NULL,
- };
-
- odh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /orders/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (response);
- hr.hint = TALER_JSON_get_error_hint (response);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- break;
- case MHD_HTTP_CONFLICT:
- break;
- default:
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u\n",
- (unsigned int) response_code);
- break;
- }
- odh->cb (odh->cb_cls,
- &hr);
- TALER_MERCHANT_order_delete_cancel (odh);
-}
-
-
-struct TALER_MERCHANT_OrderDeleteHandle *
-TALER_MERCHANT_order_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *order_id,
- bool force,
- TALER_MERCHANT_OrderDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OrderDeleteHandle *odh;
-
- odh = GNUNET_new (struct TALER_MERCHANT_OrderDeleteHandle);
- odh->ctx = ctx;
- odh->cb = cb;
- odh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/orders/%s%s",
- order_id,
- force
- ? "?force=yes"
- : "");
-
- odh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == odh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request url.\n");
- GNUNET_free (odh);
- return NULL;
- }
-
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (odh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- odh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_order_finished,
- odh);
- }
- return odh;
-}
-
-
-void
-TALER_MERCHANT_order_delete_cancel (
- struct TALER_MERCHANT_OrderDeleteHandle *odh)
-{
- if (NULL != odh->job)
- GNUNET_CURL_job_cancel (odh->job);
- GNUNET_free (odh->url);
- GNUNET_free (odh);
-}
diff --git a/src/lib/merchant_api_delete_otp_device.c b/src/lib/merchant_api_delete_otp_device.c
@@ -1,184 +0,0 @@
-/*
- 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_delete_otp_device.c
- * @brief Implementation of the DELETE /otp-devices/$ID request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a DELETE /otp-devices/$ID operation.
- */
-struct TALER_MERCHANT_OtpDeviceDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OtpDeviceDeleteCallback 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 /otp-devices/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_OtpDeviceDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_otp_device_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tdh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got DELETE /otp-devices/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- 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) hr.ec);
- break;
- }
- tdh->cb (tdh->cb_cls,
- &hr);
- TALER_MERCHANT_otp_device_delete_cancel (tdh);
-}
-
-
-struct TALER_MERCHANT_OtpDeviceDeleteHandle *
-TALER_MERCHANT_otp_device_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *otp_device_id,
- TALER_MERCHANT_OtpDeviceDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh;
-
- tdh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceDeleteHandle);
- tdh->ctx = ctx;
- tdh->cb = cb;
- tdh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/otp-devices/%s",
- otp_device_id);
- tdh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == tdh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (tdh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- tdh->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- tdh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_otp_device_finished,
- tdh);
- }
- return tdh;
-}
-
-
-void
-TALER_MERCHANT_otp_device_delete_cancel (
- struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh)
-{
- if (NULL != tdh->job)
- GNUNET_CURL_job_cancel (tdh->job);
- GNUNET_free (tdh->url);
- GNUNET_free (tdh);
-}
diff --git a/src/lib/merchant_api_delete_product.c b/src/lib/merchant_api_delete_product.c
@@ -1,184 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_delete_product.c
- * @brief Implementation of the DELETE /product/$ID request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a DELETE /products/$ID operation.
- */
-struct TALER_MERCHANT_ProductDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ProductDeleteCallback 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 /products/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_ProductDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_product_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_ProductDeleteHandle *pdh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- pdh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /products/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- 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) hr.ec);
- break;
- }
- pdh->cb (pdh->cb_cls,
- &hr);
- TALER_MERCHANT_product_delete_cancel (pdh);
-}
-
-
-struct TALER_MERCHANT_ProductDeleteHandle *
-TALER_MERCHANT_product_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- TALER_MERCHANT_ProductDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_ProductDeleteHandle *pdh;
-
- pdh = GNUNET_new (struct TALER_MERCHANT_ProductDeleteHandle);
- pdh->ctx = ctx;
- pdh->cb = cb;
- pdh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/products/%s",
- product_id);
- pdh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == pdh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (pdh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- pdh->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (pdh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- pdh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_product_finished,
- pdh);
- }
- return pdh;
-}
-
-
-void
-TALER_MERCHANT_product_delete_cancel (
- struct TALER_MERCHANT_ProductDeleteHandle *pdh)
-{
- if (NULL != pdh->job)
- GNUNET_CURL_job_cancel (pdh->job);
- GNUNET_free (pdh->url);
- GNUNET_free (pdh);
-}
diff --git a/src/lib/merchant_api_delete_template.c b/src/lib/merchant_api_delete_template.c
@@ -1,184 +0,0 @@
-/*
- 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_delete_template.c
- * @brief Implementation of the DELETE /templates/$ID request of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a DELETE /templates/$ID operation.
- */
-struct TALER_MERCHANT_TemplateDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_TemplateDeleteCallback 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_TemplateDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_template_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_TemplateDeleteHandle *tdh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tdh->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_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- 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) hr.ec);
- break;
- }
- tdh->cb (tdh->cb_cls,
- &hr);
- TALER_MERCHANT_template_delete_cancel (tdh);
-}
-
-
-struct TALER_MERCHANT_TemplateDeleteHandle *
-TALER_MERCHANT_template_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *template_id,
- TALER_MERCHANT_TemplateDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_TemplateDeleteHandle *tdh;
-
- tdh = GNUNET_new (struct TALER_MERCHANT_TemplateDeleteHandle);
- tdh->ctx = ctx;
- tdh->cb = cb;
- tdh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/templates/%s",
- template_id);
- tdh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == tdh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (tdh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- tdh->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- tdh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_template_finished,
- tdh);
- }
- return tdh;
-}
-
-
-void
-TALER_MERCHANT_template_delete_cancel (
- struct TALER_MERCHANT_TemplateDeleteHandle *tdh)
-{
- if (NULL != tdh->job)
- GNUNET_CURL_job_cancel (tdh->job);
- GNUNET_free (tdh->url);
- GNUNET_free (tdh);
-}
diff --git a/src/lib/merchant_api_delete_transfer.c b/src/lib/merchant_api_delete_transfer.c
@@ -1,184 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2021 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_delete_transfer.c
- * @brief Implementation of the DELETE /transfer/$ID request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a DELETE /transfers/$ID operation.
- */
-struct TALER_MERCHANT_TransferDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_TransferDeleteCallback 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 /transfers/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_TransferDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_transfer_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_TransferDeleteHandle *tdh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tdh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /transfers/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- 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) hr.ec);
- break;
- }
- tdh->cb (tdh->cb_cls,
- &hr);
- TALER_MERCHANT_transfer_delete_cancel (tdh);
-}
-
-
-struct TALER_MERCHANT_TransferDeleteHandle *
-TALER_MERCHANT_transfer_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- uint64_t wire_transfer_serial,
- TALER_MERCHANT_TransferDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_TransferDeleteHandle *tdh;
-
- tdh = GNUNET_new (struct TALER_MERCHANT_TransferDeleteHandle);
- tdh->ctx = ctx;
- tdh->cb = cb;
- tdh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/transfers/%llu",
- (unsigned long long) wire_transfer_serial);
- tdh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == tdh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (tdh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- tdh->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- tdh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_transfer_finished,
- tdh);
- }
- return tdh;
-}
-
-
-void
-TALER_MERCHANT_transfer_delete_cancel (
- struct TALER_MERCHANT_TransferDeleteHandle *tdh)
-{
- if (NULL != tdh->job)
- GNUNET_CURL_job_cancel (tdh->job);
- GNUNET_free (tdh->url);
- GNUNET_free (tdh);
-}
diff --git a/src/lib/merchant_api_delete_unit.c b/src/lib/merchant_api_delete_unit.c
@@ -1,177 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_delete_unit.c
- * @brief Implementation of DELETE /private/units/$ID
- * @author Bohdan Potuzhnyi
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-
-
-/**
- * Handle for a DELETE /private/units/$ID operation.
- */
-struct TALER_MERCHANT_UnitDeleteHandle
-{
- /**
- * Fully qualified request URL.
- */
- char *url;
-
- /**
- * In-flight CURL job.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Completion callback.
- */
- TALER_MERCHANT_UnitDeleteCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-};
-
-
-/**
- * Called when the HTTP request finishes.
- *
- * @param cls operation handle
- * @param response_code HTTP status (0 on failure)
- * @param response parsed JSON reply (NULL if unavailable)
- */
-static void
-handle_delete_unit_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_UnitDeleteHandle *udh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- udh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "DELETE /private/units finished with status %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- case MHD_HTTP_UNAUTHORIZED:
- case MHD_HTTP_FORBIDDEN:
- case MHD_HTTP_NOT_FOUND:
- case MHD_HTTP_CONFLICT:
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response %u/%d for DELETE /private/units\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- udh->cb (udh->cb_cls,
- &hr);
- TALER_MERCHANT_unit_delete_cancel (udh);
-}
-
-
-struct TALER_MERCHANT_UnitDeleteHandle *
-TALER_MERCHANT_unit_delete (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *unit_id,
- TALER_MERCHANT_UnitDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_UnitDeleteHandle *udh;
- CURL *eh;
- char *path;
-
- GNUNET_asprintf (&path,
- "private/units/%s",
- unit_id);
- udh = GNUNET_new (struct TALER_MERCHANT_UnitDeleteHandle);
- udh->ctx = ctx;
- udh->cb = cb;
- udh->cb_cls = cb_cls;
- udh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- if (NULL == udh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to build /private/units/%s URL\n",
- unit_id);
- GNUNET_free (udh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting DELETE on '%s'\n",
- udh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (udh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- udh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_unit_finished,
- udh);
- return udh;
-}
-
-
-void
-TALER_MERCHANT_unit_delete_cancel (struct TALER_MERCHANT_UnitDeleteHandle *udh)
-{
- if (NULL != udh->job)
- GNUNET_CURL_job_cancel (udh->job);
- GNUNET_free (udh->url);
- GNUNET_free (udh);
-}
-
-
-/* end of merchant_api_delete_unit.c */
diff --git a/src/lib/merchant_api_delete_webhook.c b/src/lib/merchant_api_delete_webhook.c
@@ -1,184 +0,0 @@
-/*
- 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_delete_template.c
- * @brief Implementation of the DELETE /webhooks/$ID request of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a DELETE /webhooks/$ID operation.
- */
-struct TALER_MERCHANT_WebhookDeleteHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_WebhookDeleteCallback 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 /webhooks/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_WebhookDeleteHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_delete_webhook_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_WebhookDeleteHandle *wdh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- wdh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /webhooks/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- 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) hr.ec);
- break;
- }
- wdh->cb (wdh->cb_cls,
- &hr);
- TALER_MERCHANT_webhook_delete_cancel (wdh);
-}
-
-
-struct TALER_MERCHANT_WebhookDeleteHandle *
-TALER_MERCHANT_webhook_delete (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *webhook_id,
- TALER_MERCHANT_WebhookDeleteCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_WebhookDeleteHandle *wdh;
-
- wdh = GNUNET_new (struct TALER_MERCHANT_WebhookDeleteHandle);
- wdh->ctx = ctx;
- wdh->cb = cb;
- wdh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/webhooks/%s",
- webhook_id);
- wdh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == wdh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (wdh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- wdh->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (wdh->url);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_DELETE));
- wdh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_delete_webhook_finished,
- wdh);
- }
- return wdh;
-}
-
-
-void
-TALER_MERCHANT_webhook_delete_cancel (
- struct TALER_MERCHANT_WebhookDeleteHandle *wdh)
-{
- if (NULL != wdh->job)
- GNUNET_CURL_job_cancel (wdh->job);
- GNUNET_free (wdh->url);
- GNUNET_free (wdh);
-}
diff --git a/src/lib/merchant_api_get-config.c b/src/lib/merchant_api_get-config.c
@@ -0,0 +1,320 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2026 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_get-config.c
+ * @brief Implementation of the /config request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+/**
+ * Which version of the Taler protocol is implemented
+ * by this library? Used to determine compatibility.
+ */
+#define MERCHANT_PROTOCOL_CURRENT 27
+
+/**
+ * How many configs are we backwards-compatible with?
+ */
+#define MERCHANT_PROTOCOL_AGE 3
+
+/**
+ * How many exchanges do we allow at most per merchant?
+ */
+#define MAX_EXCHANGES 1024
+
+/**
+ * How many currency specs do we allow at most per merchant?
+ */
+#define MAX_CURRENCIES 1024
+
+/**
+ * @brief A handle for /config operations
+ */
+struct TALER_MERCHANT_ConfigGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ConfigCallback 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 /config request.
+ *
+ * @param cls the `struct TALER_MERCHANT_ConfigGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_config_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_ConfigGetHandle *vgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_ConfigResponse cr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /config response with status code %u\n",
+ (unsigned int) response_code);
+
+ vgh->job = NULL;
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *jcs;
+ const json_t *exchanges = NULL;
+ struct TALER_MERCHANT_ExchangeConfigInfo *eci = NULL;
+ unsigned int num_eci = 0;
+ unsigned int nspec;
+ struct TALER_JSON_ProtocolVersion pv;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_object_const ("currencies",
+ &jcs),
+ GNUNET_JSON_spec_array_const ("exchanges",
+ &exchanges),
+ GNUNET_JSON_spec_string ("currency",
+ &cr.details.ok.ci.currency),
+ TALER_JSON_spec_version ("version",
+ &pv),
+ GNUNET_JSON_spec_string ("version",
+ &cr.details.ok.ci.version),
+ GNUNET_JSON_spec_end ()
+ };
+
+ cr.details.ok.compat = TALER_MERCHANT_VC_PROTOCOL_ERROR;
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ cr.hr.http_status = 0;
+ cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ cr.details.ok.compat = TALER_MERCHANT_VC_MATCH;
+ if (MERCHANT_PROTOCOL_CURRENT < pv.current)
+ {
+ cr.details.ok.compat |= TALER_MERCHANT_VC_NEWER;
+ if (MERCHANT_PROTOCOL_CURRENT < pv.current - pv.age)
+ cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE;
+ }
+ if (MERCHANT_PROTOCOL_CURRENT > pv.current)
+ {
+ cr.details.ok.compat |= TALER_MERCHANT_VC_OLDER;
+ if (MERCHANT_PROTOCOL_CURRENT - MERCHANT_PROTOCOL_AGE > pv.current)
+ cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE;
+ }
+
+ nspec = (unsigned int) json_object_size (jcs);
+ if ( (nspec > MAX_CURRENCIES) ||
+ (json_object_size (jcs) != (size_t) nspec) )
+ {
+ GNUNET_break_op (0);
+ cr.hr.http_status = 0;
+ cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (NULL != exchanges)
+ {
+ num_eci = (unsigned int) json_object_size (exchanges);
+ if ( (num_eci > MAX_EXCHANGES) ||
+ (json_object_size (exchanges) != (size_t) num_eci) )
+ {
+ GNUNET_break_op (0);
+ cr.hr.http_status = 0;
+ cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ eci = GNUNET_new_array (num_eci,
+ struct TALER_MERCHANT_ExchangeConfigInfo);
+ for (unsigned int i = 0; i<num_eci; i++)
+ {
+ struct TALER_MERCHANT_ExchangeConfigInfo *ei = &eci[i];
+ const json_t *ej = json_array_get (exchanges,
+ i);
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_string ("currency",
+ &ei->currency),
+ GNUNET_JSON_spec_string ("base_url",
+ &ei->base_url),
+ GNUNET_JSON_spec_fixed_auto ("master_pub",
+ &ei->master_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (ej,
+ ispec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ cr.hr.http_status = 0;
+ cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ GNUNET_free (eci);
+ break;
+ }
+ }
+ }
+ {
+ struct TALER_CurrencySpecification *cspecs;
+ unsigned int off = 0;
+ json_t *obj;
+ const char *curr;
+
+ cspecs = GNUNET_new_array (nspec,
+ struct TALER_CurrencySpecification);
+ cr.details.ok.num_cspecs = nspec;
+ cr.details.ok.cspecs = cspecs;
+ cr.details.ok.num_exchanges = (unsigned int) num_eci;
+ cr.details.ok.exchanges = eci;
+ json_object_foreach ((json_t *) jcs, curr, obj)
+ {
+ struct TALER_CurrencySpecification *cs = &cspecs[off++];
+ struct GNUNET_JSON_Specification cspec[] = {
+ TALER_JSON_spec_currency_specification (curr,
+ curr,
+ cs),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jcs,
+ cspec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ cr.hr.http_status = 0;
+ cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ GNUNET_free (eci);
+ TALER_CONFIG_free_currencies (off - 1,
+ cspecs);
+ break;
+ }
+ }
+ vgh->cb (vgh->cb_cls,
+ &cr);
+ GNUNET_free (eci);
+ TALER_CONFIG_free_currencies (nspec,
+ cspecs);
+ }
+ TALER_MERCHANT_config_get_cancel (vgh);
+ return;
+ }
+ default:
+ /* unexpected response code */
+ cr.hr.ec = TALER_JSON_get_error_code (json);
+ cr.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) cr.hr.ec);
+ break;
+ }
+ vgh->cb (vgh->cb_cls,
+ &cr);
+ TALER_MERCHANT_config_get_cancel (vgh);
+}
+
+
+struct TALER_MERCHANT_ConfigGetHandle *
+TALER_MERCHANT_config_get (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_ConfigCallback config_cb,
+ void *config_cb_cls)
+{
+ struct TALER_MERCHANT_ConfigGetHandle *vgh;
+ CURL *eh;
+
+ vgh = GNUNET_new (struct TALER_MERCHANT_ConfigGetHandle);
+ vgh->ctx = ctx;
+ vgh->cb = config_cb;
+ vgh->cb_cls = config_cb_cls;
+ vgh->url = TALER_url_join (backend_url,
+ "config",
+ NULL);
+ if (NULL == vgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (vgh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ vgh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (vgh->url);
+ vgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_config_finished,
+ vgh);
+ return vgh;
+}
+
+
+void
+TALER_MERCHANT_config_get_cancel (struct TALER_MERCHANT_ConfigGetHandle *vgh)
+{
+ if (NULL != vgh->job)
+ {
+ GNUNET_CURL_job_cancel (vgh->job);
+ vgh->job = NULL;
+ }
+ GNUNET_free (vgh->url);
+ GNUNET_free (vgh);
+}
+
+
+/* end of merchant_api_config_get.c */
diff --git a/src/lib/merchant_api_get-management-instances-INSTANCE.c b/src/lib/merchant_api_get-management-instances-INSTANCE.c
@@ -0,0 +1,235 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_get-management-instances-INSTANCE.c
+ * @brief Implementation of the GET /instance/$ID request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_kyclogic_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a GET /instances/$ID operation.
+ */
+struct TALER_MERCHANT_InstanceGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_InstanceGetCallback 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 /instances/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_InstanceGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_instance_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_InstanceGetHandle *igh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_InstanceGetResponse igr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ igh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /instances/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *address;
+ const json_t *jurisdiction;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string (
+ "name",
+ &igr.details.ok.details.name),
+ GNUNET_JSON_spec_fixed_auto (
+ "merchant_pub",
+ &igr.details.ok.details.merchant_pub),
+ GNUNET_JSON_spec_object_const (
+ "address",
+ &address),
+ GNUNET_JSON_spec_object_const (
+ "jurisdiction",
+ &jurisdiction),
+ GNUNET_JSON_spec_bool (
+ "use_stefan",
+ &igr.details.ok.details.use_stefan),
+ GNUNET_JSON_spec_relative_time (
+ "default_wire_transfer_delay",
+ &igr.details.ok.details.default_wire_transfer_delay),
+ GNUNET_JSON_spec_relative_time (
+ "default_pay_delay",
+ &igr.details.ok.details.default_pay_delay),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_relative_time (
+ "default_refund_delay",
+ &igr.details.ok.details.default_refund_delay),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_time_rounder_interval (
+ "default_wire_transfer_rounding_interval",
+ &igr.details.ok.details.default_wire_transfer_rounding_interval),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ igr.hr.http_status = 0;
+ igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ igr.details.ok.details.address = address;
+ igr.details.ok.details.jurisdiction = jurisdiction;
+ igh->cb (igh->cb_cls,
+ &igr);
+ TALER_MERCHANT_instance_get_cancel (igh);
+ return;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ igr.hr.ec = TALER_JSON_get_error_code (json);
+ igr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* instance does not exist */
+ igr.hr.ec = TALER_JSON_get_error_code (json);
+ igr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ igr.hr.ec = TALER_JSON_get_error_code (json);
+ igr.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) igr.hr.ec);
+ break;
+ }
+ igh->cb (igh->cb_cls,
+ &igr);
+ TALER_MERCHANT_instance_get_cancel (igh);
+}
+
+
+struct TALER_MERCHANT_InstanceGetHandle *
+TALER_MERCHANT_instance_get (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ TALER_MERCHANT_InstanceGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_InstanceGetHandle *igh;
+ CURL *eh;
+
+ igh = GNUNET_new (struct TALER_MERCHANT_InstanceGetHandle);
+ igh->ctx = ctx;
+ igh->cb = cb;
+ igh->cb_cls = cb_cls;
+ if (NULL != instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "instances/%s/private",
+ instance_id);
+ igh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ igh->url = TALER_url_join (backend_url,
+ "private",
+ NULL);
+ }
+ if (NULL == igh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (igh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ igh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (igh->url);
+ igh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_instance_finished,
+ igh);
+ return igh;
+}
+
+
+void
+TALER_MERCHANT_instance_get_cancel (
+ struct TALER_MERCHANT_InstanceGetHandle *igh)
+{
+ if (NULL != igh->job)
+ GNUNET_CURL_job_cancel (igh->job);
+ GNUNET_free (igh->url);
+ GNUNET_free (igh);
+}
diff --git a/src/lib/merchant_api_get-management-instances.c b/src/lib/merchant_api_get-management-instances.c
@@ -0,0 +1,263 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_get-management-instances.c
+ * @brief Implementation of the GET /instances request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Maximum number of instances permitted.
+ */
+#define MAX_INSTANCES 1024
+
+/**
+ * Handle for a GET /instances operation.
+ */
+struct TALER_MERCHANT_InstancesGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_InstancesGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse instance information from @a ia.
+ *
+ * @param json overall reply body
+ * @param ia JSON array (or NULL!) with instance data
+ * @param igh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_instances (const json_t *json,
+ const json_t *ia,
+ struct TALER_MERCHANT_InstancesGetHandle *igh)
+{
+ unsigned int iis_len = (unsigned int) json_array_size (ia);
+
+ if ( (json_array_size (ia) != (size_t) iis_len) ||
+ (iis_len > MAX_INSTANCES) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_InstanceInformation iis[GNUNET_NZL (iis_len)];
+ size_t index;
+ json_t *value;
+ struct TALER_MERCHANT_InstancesGetResponse igr = {
+ .hr.http_status = MHD_HTTP_OK,
+ .hr.reply = json,
+ .details.ok.iis_length = iis_len,
+ .details.ok.iis = iis
+ };
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_InstanceInformation *ii = &iis[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("name",
+ &ii->name),
+ GNUNET_JSON_spec_string ("id",
+ &ii->id),
+ GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+ &ii->merchant_pub),
+ GNUNET_JSON_spec_array_const ("payment_targets",
+ &ii->payment_targets),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ for (size_t i = 0; i<json_array_size (ii->payment_targets); i++)
+ {
+ if (! json_is_string (json_array_get (ii->payment_targets,
+ i)))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ } /* for all instances */
+ igh->cb (igh->cb_cls,
+ &igr);
+ igh->cb = NULL; /* just to be sure */
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /instances request.
+ *
+ * @param cls the `struct TALER_MERCHANT_InstancesGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_instances_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_InstancesGetHandle *igh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_InstancesGetResponse igr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ igh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /instances response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *instances;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("instances",
+ &instances),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ igr.hr.http_status = 0;
+ igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_instances (json,
+ instances,
+ igh))
+ {
+ TALER_MERCHANT_instances_get_cancel (igh);
+ return;
+ }
+ igr.hr.http_status = 0;
+ igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ igr.hr.ec = TALER_JSON_get_error_code (json);
+ igr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ igr.hr.ec = TALER_JSON_get_error_code (json);
+ igr.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) igr.hr.ec);
+ break;
+ }
+ igh->cb (igh->cb_cls,
+ &igr);
+ TALER_MERCHANT_instances_get_cancel (igh);
+}
+
+
+struct TALER_MERCHANT_InstancesGetHandle *
+TALER_MERCHANT_instances_get (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_InstancesGetCallback instances_cb,
+ void *instances_cb_cls)
+{
+ struct TALER_MERCHANT_InstancesGetHandle *igh;
+ CURL *eh;
+
+ igh = GNUNET_new (struct TALER_MERCHANT_InstancesGetHandle);
+ igh->ctx = ctx;
+ igh->cb = instances_cb;
+ igh->cb_cls = instances_cb_cls;
+ igh->url = TALER_url_join (backend_url,
+ "management/instances",
+ NULL);
+ if (NULL == igh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (igh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ igh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (igh->url);
+ igh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_instances_finished,
+ igh);
+ return igh;
+}
+
+
+void
+TALER_MERCHANT_instances_get_cancel (
+ struct TALER_MERCHANT_InstancesGetHandle *igh)
+{
+ if (NULL != igh->job)
+ GNUNET_CURL_job_cancel (igh->job);
+ GNUNET_free (igh->url);
+ GNUNET_free (igh);
+}
diff --git a/src/lib/merchant_api_get-orders-ORDER_ID.c b/src/lib/merchant_api_get-orders-ORDER_ID.c
@@ -0,0 +1,306 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018, 2020, 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_get-orders-ORDER_ID.c
+ * @brief Implementation of the GET /orders/$ID request
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ * @author Florian Dold
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * @brief A GET /orders/$ID handle
+ */
+struct TALER_MERCHANT_OrderWalletGetHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OrderWalletGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Convenience function to call the callback in @a owgh with an error code of
+ * @a ec and the exchange body being set to @a reply.
+ *
+ * @param owgh handle providing callback
+ * @param ec error code to return to application
+ * @param reply JSON reply we got from the exchange, can be NULL
+ */
+static void
+cb_failure (struct TALER_MERCHANT_OrderWalletGetHandle *owgh,
+ enum TALER_ErrorCode ec,
+ const json_t *reply)
+{
+ struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
+ .hr.ec = ec,
+ .hr.reply = reply
+ };
+
+ owgh->cb (owgh->cb_cls,
+ &owgr);
+}
+
+
+/**
+ * Function called when we're done processing the GET /check-payment request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderWalletGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, should be NULL
+ */
+static void
+handle_wallet_get_order_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderWalletGetHandle *owgh = cls;
+ const json_t *json = response;
+
+ owgh->job = NULL;
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
+ .hr.reply = json,
+ .hr.http_status = MHD_HTTP_OK
+ };
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_bool ("refunded",
+ &owgr.details.ok.refunded),
+ GNUNET_JSON_spec_bool ("refund_pending",
+ &owgr.details.ok.refund_pending),
+ TALER_JSON_spec_amount_any ("refund_amount",
+ &owgr.details.ok.refund_amount),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ cb_failure (owgh,
+ TALER_EC_GENERIC_REPLY_MALFORMED,
+ json);
+ TALER_MERCHANT_wallet_order_get_cancel (owgh);
+ return;
+ }
+ owgh->cb (owgh->cb_cls,
+ &owgr);
+ GNUNET_JSON_parse_free (spec);
+ break;
+ }
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ {
+ struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
+ .hr.reply = json,
+ .hr.http_status = MHD_HTTP_PAYMENT_REQUIRED
+ };
+
+ /* Status is: unpaid */
+ owgr.details.payment_required.taler_pay_uri
+ = json_string_value (json_object_get (json,
+ "taler_pay_uri"));
+ owgr.details.payment_required.already_paid_order_id
+ = json_string_value (json_object_get (json,
+ "already_paid_order_id"));
+ if (NULL == owgr.details.payment_required.taler_pay_uri)
+ {
+ GNUNET_break_op (0);
+ cb_failure (owgh,
+ TALER_EC_GENERIC_REPLY_MALFORMED,
+ json);
+ break;
+ }
+ owgh->cb (owgh->cb_cls,
+ &owgr);
+ break;
+ }
+ default:
+ {
+ struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
+ .hr.reply = json,
+ .hr.http_status = response_code
+ };
+
+ TALER_MERCHANT_parse_error_details_ (response,
+ response_code,
+ &owgr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Checking order status failed with HTTP status code %u/%d\n",
+ (unsigned int) response_code,
+ (int) owgr.hr.ec);
+ GNUNET_break_op (0);
+ owgh->cb (owgh->cb_cls,
+ &owgr);
+ break;
+ }
+ }
+ TALER_MERCHANT_wallet_order_get_cancel (owgh);
+}
+
+
+struct TALER_MERCHANT_OrderWalletGetHandle *
+TALER_MERCHANT_wallet_order_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *order_id,
+ const struct TALER_PrivateContractHashP *h_contract,
+ struct GNUNET_TIME_Relative timeout,
+ const char *session_id,
+ const struct TALER_Amount *min_refund,
+ bool await_refund_obtained,
+ TALER_MERCHANT_OrderWalletGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OrderWalletGetHandle *owgh;
+ unsigned int tms;
+
+ GNUNET_assert (NULL != backend_url);
+ GNUNET_assert (NULL != order_id);
+ owgh = GNUNET_new (struct TALER_MERCHANT_OrderWalletGetHandle);
+ owgh->ctx = ctx;
+ owgh->cb = cb;
+ owgh->cb_cls = cb_cls;
+ tms = (unsigned int) (timeout.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
+ {
+ char timeout_ms[32];
+ struct GNUNET_CRYPTO_HashAsciiEncoded h_contract_s;
+ char *path;
+
+ GNUNET_CRYPTO_hash_to_enc (&h_contract->hash,
+ &h_contract_s);
+ GNUNET_snprintf (timeout_ms,
+ sizeof (timeout_ms),
+ "%u",
+ tms);
+ GNUNET_asprintf (&path,
+ "orders/%s",
+ order_id);
+ owgh->url = TALER_url_join (backend_url,
+ path,
+ "h_contract",
+ h_contract_s.encoding,
+ "session_id",
+ session_id,
+ "timeout_ms",
+ (0 != tms)
+ ? timeout_ms
+ : NULL,
+ "refund",
+ (NULL != min_refund)
+ ? TALER_amount2s (min_refund)
+ : NULL,
+ "await_refund_obtained",
+ await_refund_obtained
+ ? "yes"
+ : NULL,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == owgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (owgh);
+ return NULL;
+ }
+
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (owgh->url);
+ if (0 != tms)
+ {
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT_MS,
+ (long) (tms + 100L)));
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Checking order status at %s\n",
+ owgh->url);
+ if (NULL == (owgh->job =
+ GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_wallet_get_order_finished,
+ owgh)))
+ {
+ GNUNET_break (0);
+ GNUNET_free (owgh->url);
+ GNUNET_free (owgh);
+ return NULL;
+ }
+ }
+ return owgh;
+}
+
+
+void
+TALER_MERCHANT_wallet_order_get_cancel (
+ struct TALER_MERCHANT_OrderWalletGetHandle *owgh)
+{
+ if (NULL != owgh->job)
+ {
+ GNUNET_CURL_job_cancel (owgh->job);
+ owgh->job = NULL;
+ }
+ GNUNET_free (owgh->url);
+ GNUNET_free (owgh);
+}
+
+
+/* end of merchant_api_wallet_get_order.c */
diff --git a/src/lib/merchant_api_get-private-accounts-H_WIRE.c b/src/lib/merchant_api_get-private-accounts-H_WIRE.c
@@ -0,0 +1,211 @@
+/*
+ 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_get-private-accounts-H_WIRE.c
+ * @brief Implementation of the GET /accounts/$ID request of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/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/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 /accounts/$ID operation.
+ */
+struct TALER_MERCHANT_AccountGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_AccountGetCallback 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 /accounts/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_AccountGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_account_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_AccountGetHandle *tgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_AccountGetResponse tgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ tgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /accounts/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("salt",
+ &tgr.details.ok.ad.salt),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_web_url ("credit_facade_url",
+ &tgr.details.ok.ad.credit_facade_url),
+ NULL),
+ TALER_JSON_spec_full_payto_uri ("payto_uri",
+ &tgr.details.ok.ad.payto_uri),
+ GNUNET_JSON_spec_fixed_auto ("h_wire",
+ &tgr.details.ok.ad.h_wire),
+ GNUNET_JSON_spec_bool ("active",
+ &tgr.details.ok.ad.active),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ tgh->cb (tgh->cb_cls,
+ &tgr);
+ TALER_MERCHANT_account_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_account_get_cancel (tgh);
+}
+
+
+struct TALER_MERCHANT_AccountGetHandle *
+TALER_MERCHANT_account_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ const struct TALER_MerchantWireHashP *h_wire,
+ TALER_MERCHANT_AccountGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_AccountGetHandle *tgh;
+ CURL *eh;
+
+ tgh = GNUNET_new (struct TALER_MERCHANT_AccountGetHandle);
+ tgh->ctx = ctx;
+ tgh->cb = cb;
+ tgh->cb_cls = cb_cls;
+ {
+ char w_str[sizeof (*h_wire) * 2];
+ char *path;
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (h_wire,
+ sizeof (*h_wire),
+ w_str,
+ sizeof (w_str));
+ *end = '\0';
+ GNUNET_asprintf (&path,
+ "private/accounts/%s",
+ w_str);
+ 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_account_finished,
+ tgh);
+ return tgh;
+}
+
+
+void
+TALER_MERCHANT_account_get_cancel (
+ struct TALER_MERCHANT_AccountGetHandle *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-private-accounts.c b/src/lib/merchant_api_get-private-accounts.c
@@ -0,0 +1,247 @@
+/*
+ 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_get-private-accounts.c
+ * @brief Implementation of the GET /accounts request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+/**
+ * Maximum number of accounts permitted.
+ */
+#define MAX_ACCOUNTS 1024
+
+/**
+ * Handle for a GET /accounts operation.
+ */
+struct TALER_MERCHANT_AccountsGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_AccountsGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse account information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with account data
+ * @param[in] tgr partially filled response
+ * @param tgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_accounts (const json_t *ia,
+ struct TALER_MERCHANT_AccountsGetResponse *tgr,
+ struct TALER_MERCHANT_AccountsGetHandle *tgh)
+{
+ unsigned int tmpl_len = (unsigned int) json_array_size (ia);
+
+ if ( (json_array_size (ia) != (size_t) tmpl_len) ||
+ (tmpl_len > MAX_ACCOUNTS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_AccountEntry tmpl[GNUNET_NZL (tmpl_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_AccountEntry *ie = &tmpl[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_full_payto_uri ("payto_uri",
+ &ie->payto_uri),
+ GNUNET_JSON_spec_fixed_auto ("h_wire",
+ &ie->h_wire),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ tgr->details.ok.accounts_length = tmpl_len;
+ tgr->details.ok.accounts = tmpl;
+ 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 /accounts request.
+ *
+ * @param cls the `struct TALER_MERCHANT_AccountsGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_accounts_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_AccountsGetHandle *tgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_AccountsGetResponse tgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ tgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /accounts response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *accounts;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("accounts",
+ &accounts),
+ 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_accounts (accounts,
+ &tgr,
+ tgh))
+ {
+ TALER_MERCHANT_accounts_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_accounts_get_cancel (tgh);
+}
+
+
+struct TALER_MERCHANT_AccountsGetHandle *
+TALER_MERCHANT_accounts_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_AccountsGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_AccountsGetHandle *tgh;
+ CURL *eh;
+
+ tgh = GNUNET_new (struct TALER_MERCHANT_AccountsGetHandle);
+ tgh->ctx = ctx;
+ tgh->cb = cb;
+ tgh->cb_cls = cb_cls;
+ tgh->url = TALER_url_join (backend_url,
+ "private/accounts",
+ 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_accounts_finished,
+ tgh);
+ return tgh;
+}
+
+
+void
+TALER_MERCHANT_accounts_get_cancel (
+ struct TALER_MERCHANT_AccountsGetHandle *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-private-donau.c b/src/lib/merchant_api_get-private-donau.c
@@ -0,0 +1,299 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 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_get-private-donau.c
+ * @brief Implementation of the GET /donau request of the merchant's HTTP API
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+/* DONAU RELATED IMPORTS */
+#include "taler/taler_merchant_donau.h"
+#include <donau/donau_service.h>
+
+/**
+ * Handle for a GET /donau operation.
+ */
+struct TALER_MERCHANT_DonauInstanceGetHandle
+{
+ /**
+ * The URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DonauInstanceGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+/**
+ * Parse Donau instance information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with Donau instance data
+ * @param igr response to fill
+ * @param dgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_donau_instances (const json_t *ia,
+ struct TALER_MERCHANT_DonauInstanceGetResponse *igr,
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh)
+{
+ unsigned int instances_len = (unsigned int) json_array_size (ia);
+ struct TALER_MERCHANT_DonauInstanceEntry
+ instances[GNUNET_NZL (instances_len)];
+ size_t index;
+ json_t *value;
+ struct DONAU_Keys *donau_keys_ptr = NULL;
+
+ if ((json_array_size (ia) != (size_t) instances_len))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ json_array_foreach (ia,
+ index,
+ value)
+ {
+ struct TALER_MERCHANT_DonauInstanceEntry *instance = &instances[index];
+ const json_t *donau_keys_json = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint64 ("donau_instance_serial",
+ &instance->donau_instance_serial),
+ GNUNET_JSON_spec_string ("donau_url",
+ &instance->donau_url),
+ GNUNET_JSON_spec_string ("charity_name",
+ &instance->charity_name),
+ GNUNET_JSON_spec_fixed_auto ("charity_pub_key",
+ &instance->charity_pub_key),
+ GNUNET_JSON_spec_uint64 ("charity_id",
+ &instance->charity_id),
+ TALER_JSON_spec_amount_any ("charity_max_per_year",
+ &instance->charity_max_per_year),
+ TALER_JSON_spec_amount_any ("charity_receipts_to_date",
+ &instance->charity_receipts_to_date),
+ GNUNET_JSON_spec_int64 ("current_year",
+ &instance->current_year),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("donau_keys_json",
+ &donau_keys_json),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* Parse the Donau keys */
+ if (NULL != donau_keys_json)
+ {
+ donau_keys_ptr = DONAU_keys_from_json (donau_keys_json);
+ if (NULL == donau_keys_ptr)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to convert donau keys from JSON\n");
+ return GNUNET_SYSERR;
+ }
+ instance->donau_keys = donau_keys_ptr;
+ }
+ }
+
+ igr->details.ok.donau_instances_length = instances_len;
+ igr->details.ok.donau_instances = instances;
+ dgh->cb (dgh->cb_cls,
+ igr);
+ dgh->cb = NULL;
+ if (NULL != donau_keys_ptr)
+ {
+ DONAU_keys_decref (donau_keys_ptr);
+ donau_keys_ptr= NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /donau request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DonauInstanceGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_donau_instances_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DonauInstanceGetResponse igr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ dgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /donau response with status code %u\n",
+ (unsigned int) response_code);
+
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *donau_instances;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("donau_instances",
+ &donau_instances),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL,
+ NULL))
+ {
+ igr.hr.http_status = 0;
+ igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+
+ if (GNUNET_OK ==
+ parse_donau_instances (donau_instances,
+ &igr,
+ dgh))
+ {
+ TALER_MERCHANT_donau_instances_get_cancel (dgh);
+ return;
+ }
+
+ igr.hr.http_status = 0;
+ igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+
+ case MHD_HTTP_UNAUTHORIZED:
+ case MHD_HTTP_NOT_FOUND:
+ default:
+ igr.hr.ec = TALER_JSON_get_error_code (json);
+ igr.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) igr.hr.ec);
+ break;
+ }
+
+ dgh->cb (dgh->cb_cls,
+ &igr);
+ TALER_MERCHANT_donau_instances_get_cancel (dgh);
+}
+
+
+/**
+ * Initiate the GET /donau request.
+ *
+ * @param ctx CURL context
+ * @param backend_url base URL for the backend
+ * @param cb callback function to handle the response
+ * @param cb_cls closure for the callback function
+ * @return the handle for the operation, or NULL on error
+ */
+struct TALER_MERCHANT_DonauInstanceGetHandle *
+TALER_MERCHANT_donau_instances_get (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_DonauInstanceGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh;
+ CURL *eh;
+
+ dgh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceGetHandle);
+ dgh->ctx = ctx;
+ dgh->cb = cb;
+ dgh->cb_cls = cb_cls;
+ dgh->url = TALER_url_join (backend_url,
+ "private/donau",
+ NULL);
+ if (NULL == dgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (dgh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ dgh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (dgh->url);
+ dgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_donau_instances_finished,
+ dgh);
+
+ return dgh;
+}
+
+
+/**
+ * Cancel the GET /donau instances operation.
+ *
+ * @param dgh request to cancel.
+ */
+void
+TALER_MERCHANT_donau_instances_get_cancel (
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh)
+{
+ if (NULL != dgh->job)
+ GNUNET_CURL_job_cancel (dgh->job);
+ GNUNET_free (dgh->url);
+ GNUNET_free (dgh);
+}
diff --git a/src/lib/merchant_api_get-private-kyc.c b/src/lib/merchant_api_get-private-kyc.c
@@ -0,0 +1,515 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023--2024 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_get-private-kyc.c
+ * @brief Implementation of the GET /kyc request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Maximum length of the KYC arrays supported.
+ */
+#define MAX_KYC 1024
+
+/**
+ * Handle for a GET /kyc operation.
+ */
+struct TALER_MERCHANT_KycGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_KycGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse @a kyc response and call the continuation on success.
+ *
+ * @param kyc operation handle
+ * @param[in,out] kr response details
+ * @param jkyc array from the reply
+ * @return #GNUNET_OK on success (callback was called)
+ */
+static enum GNUNET_GenericReturnValue
+parse_kyc (struct TALER_MERCHANT_KycGetHandle *kyc,
+ struct TALER_MERCHANT_KycResponse *kr,
+ const json_t *jkyc)
+{
+ unsigned int num_kycs = (unsigned int) json_array_size (jkyc);
+ unsigned int num_limits = 0;
+ unsigned int num_kycauths = 0;
+ unsigned int pos_limits = 0;
+ unsigned int pos_kycauths = 0;
+
+ if ( (json_array_size (jkyc) != (size_t) num_kycs) ||
+ (num_kycs > MAX_KYC) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ for (unsigned int i = 0; i<num_kycs; i++)
+ {
+ const json_t *jlimits = NULL;
+ const json_t *jkycauths = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "limits",
+ &jlimits),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "payto_kycauths",
+ &jkycauths),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json_array_get (jkyc,
+ i),
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (json_array_get (jkyc,
+ i),
+ stderr,
+ JSON_INDENT (2));
+ return GNUNET_SYSERR;
+ }
+ num_limits += json_array_size (jlimits);
+ num_kycauths += json_array_size (jkycauths);
+ }
+
+
+ {
+ struct TALER_MERCHANT_AccountKycRedirectDetail kycs[
+ GNUNET_NZL (num_kycs)];
+ struct TALER_EXCHANGE_AccountLimit limits[
+ GNUNET_NZL (num_limits)];
+ struct TALER_FullPayto payto_kycauths[
+ GNUNET_NZL (num_kycauths)];
+
+ memset (kycs,
+ 0,
+ sizeof (kycs));
+ for (unsigned int i = 0; i<num_kycs; i++)
+ {
+ struct TALER_MERCHANT_AccountKycRedirectDetail *rd
+ = &kycs[i];
+ const json_t *jlimits = NULL;
+ const json_t *jkycauths = NULL;
+ uint32_t hs;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_full_payto_uri (
+ "payto_uri",
+ &rd->payto_uri),
+ TALER_JSON_spec_web_url (
+ "exchange_url",
+ &rd->exchange_url),
+ GNUNET_JSON_spec_uint32 (
+ "exchange_http_status",
+ &hs),
+ GNUNET_JSON_spec_bool (
+ "no_keys",
+ &rd->no_keys),
+ GNUNET_JSON_spec_bool (
+ "auth_conflict",
+ &rd->auth_conflict),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_ec (
+ "exchange_code",
+ &rd->exchange_code),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto (
+ "access_token",
+ &rd->access_token),
+ &rd->no_access_token),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "limits",
+ &jlimits),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "payto_kycauths",
+ &jkycauths),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ size_t j;
+ json_t *jlimit;
+ json_t *jkycauth;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json_array_get (jkyc,
+ i),
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (json_array_get (jkyc,
+ i),
+ stderr,
+ JSON_INDENT (2));
+ return GNUNET_SYSERR;
+ }
+ rd->exchange_http_status = (unsigned int) hs;
+ rd->limits = &limits[pos_limits];
+ rd->limits_length = json_array_size (jlimits);
+ json_array_foreach (jlimits, j, jlimit)
+ {
+ struct TALER_EXCHANGE_AccountLimit *limit
+ = &limits[pos_limits];
+ struct GNUNET_JSON_Specification jspec[] = {
+ TALER_JSON_spec_kycte (
+ "operation_type",
+ &limit->operation_type),
+ GNUNET_JSON_spec_relative_time (
+ "timeframe",
+ &limit->timeframe),
+ TALER_JSON_spec_amount_any (
+ "threshold",
+ &limit->threshold),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool (
+ "soft_limit",
+ &limit->soft_limit),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ GNUNET_assert (pos_limits < num_limits);
+ limit->soft_limit = false;
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jlimit,
+ jspec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (json_array_get (jkyc,
+ i),
+ stderr,
+ JSON_INDENT (2));
+ return GNUNET_SYSERR;
+ }
+ pos_limits++;
+ }
+ rd->payto_kycauths = &payto_kycauths[pos_kycauths];
+ rd->pkycauth_length = json_array_size (jkycauths);
+ json_array_foreach (jkycauths, j, jkycauth)
+ {
+ GNUNET_assert (pos_kycauths < num_kycauths);
+ payto_kycauths[pos_kycauths].full_payto
+ = (char *) json_string_value (jkycauth);
+ if (NULL == payto_kycauths[pos_kycauths].full_payto)
+ {
+ GNUNET_break (0);
+ json_dumpf (json_array_get (jkyc,
+ i),
+ stderr,
+ JSON_INDENT (2));
+ return GNUNET_SYSERR;
+ }
+ pos_kycauths++;
+ }
+ }
+ kr->details.ok.kycs = kycs;
+ kr->details.ok.kycs_length = num_kycs;
+ kyc->cb (kyc->cb_cls,
+ kr);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /kyc request.
+ *
+ * @param cls the `struct TALER_MERCHANT_KycGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_kyc_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_KycGetHandle *kyc = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_KycResponse kr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ kyc->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /kyc response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *jkyc;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("kyc_data",
+ &jkyc),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ kr.hr.http_status = 0;
+ kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK !=
+ parse_kyc (kyc,
+ &kr,
+ jkyc))
+ {
+ kr.hr.http_status = 0;
+ kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ /* parse_kyc called the continuation already */
+ TALER_MERCHANT_kyc_get_cancel (kyc);
+ return;
+ }
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ kr.hr.ec = TALER_JSON_get_error_code (json);
+ kr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_SERVICE_UNAVAILABLE:
+ break;
+ default:
+ /* unexpected response code */
+ kr.hr.ec = TALER_JSON_get_error_code (json);
+ kr.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) kr.hr.ec);
+ break;
+ }
+ kyc->cb (kyc->cb_cls,
+ &kr);
+ TALER_MERCHANT_kyc_get_cancel (kyc);
+}
+
+
+/**
+ * Issue a GET KYC request to the backend.
+ * Returns KYC status of bank accounts.
+ *
+ * @param ctx execution context
+ * @param[in] url URL to use for the request, consumed!
+ * @param h_wire which bank account to query, NULL for all
+ * @param exchange_url which exchange to query, NULL for all
+ * @param lpt target for long polling
+ * @param timeout how long to wait for a reply
+ * @param cb function to call with the result
+ * @param cb_cls closure for @a cb
+ * @return handle for this operation, NULL upon errors
+ */
+static struct TALER_MERCHANT_KycGetHandle *
+kyc_get (struct GNUNET_CURL_Context *ctx,
+ char *url,
+ const struct TALER_MerchantWireHashP *h_wire,
+ const char *exchange_url,
+ enum TALER_EXCHANGE_KycLongPollTarget lpt,
+ struct GNUNET_TIME_Relative timeout,
+ TALER_MERCHANT_KycGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_KycGetHandle *kyc;
+ CURL *eh;
+ char timeout_ms[32];
+ char lpt_str[32];
+ unsigned long long tms;
+
+ kyc = GNUNET_new (struct TALER_MERCHANT_KycGetHandle);
+ kyc->ctx = ctx;
+ kyc->cb = cb;
+ kyc->cb_cls = cb_cls;
+ GNUNET_snprintf (lpt_str,
+ sizeof (lpt_str),
+ "%d",
+ (int) lpt);
+ tms = timeout.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
+ GNUNET_snprintf (timeout_ms,
+ sizeof (timeout_ms),
+ "%llu",
+ tms);
+ kyc->url
+ = TALER_url_join (
+ url,
+ "kyc",
+ "h_wire",
+ NULL == h_wire
+ ? NULL
+ : GNUNET_h2s_full (&h_wire->hash),
+ "exchange_url",
+ NULL == exchange_url
+ ? NULL
+ : exchange_url,
+ "timeout_ms",
+ GNUNET_TIME_relative_is_zero (timeout)
+ ? NULL
+ : timeout_ms,
+ "lpt",
+ TALER_EXCHANGE_KLPT_NONE == lpt
+ ? NULL
+ : lpt_str,
+ NULL);
+ GNUNET_free (url);
+ if (NULL == kyc->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (kyc);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ kyc->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (kyc->url);
+ if (0 != tms)
+ {
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT_MS,
+ (long) (tms + 100L)));
+ }
+ kyc->job
+ = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_kyc_finished,
+ kyc);
+ return kyc;
+}
+
+
+struct TALER_MERCHANT_KycGetHandle *
+TALER_MERCHANT_kyc_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const struct TALER_MerchantWireHashP *h_wire,
+ const char *exchange_url,
+ enum TALER_EXCHANGE_KycLongPollTarget lpt,
+ struct GNUNET_TIME_Relative timeout,
+ TALER_MERCHANT_KycGetCallback cb,
+ void *cb_cls)
+{
+ char *url;
+
+ GNUNET_asprintf (&url,
+ "%sprivate/",
+ backend_url);
+ return kyc_get (ctx,
+ url, /* consumed! */
+ h_wire,
+ exchange_url,
+ lpt,
+ timeout,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_KycGetHandle *
+TALER_MERCHANT_management_kyc_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ const struct TALER_MerchantWireHashP *h_wire,
+ const char *exchange_url,
+ enum TALER_EXCHANGE_KycLongPollTarget lpt,
+ struct GNUNET_TIME_Relative timeout,
+ TALER_MERCHANT_KycGetCallback cb,
+ void *cb_cls)
+{
+ char *url;
+
+ GNUNET_asprintf (&url,
+ "%smanagement/instances/%s/",
+ backend_url,
+ instance_id);
+ return kyc_get (ctx,
+ url, /* consumed! */
+ h_wire,
+ exchange_url,
+ lpt,
+ timeout,
+ cb,
+ cb_cls);
+}
+
+
+void
+TALER_MERCHANT_kyc_get_cancel (
+ struct TALER_MERCHANT_KycGetHandle *kyc)
+{
+ if (NULL != kyc->job)
+ GNUNET_CURL_job_cancel (kyc->job);
+ GNUNET_free (kyc->url);
+ GNUNET_free (kyc);
+}
diff --git a/src/lib/merchant_api_get-private-orders-ORDER_ID.c b/src/lib/merchant_api_get-private-orders-ORDER_ID.c
@@ -0,0 +1,538 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018, 2019, 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_get-private-orders-ORDER_ID.c
+ * @brief Implementation of the GET /private/orders/$ORDER request
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ * @author Florian Dold
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Maximum number of refund details we return.
+ */
+#define MAX_REFUND_DETAILS 1024
+
+/**
+ * Maximum number of wire details we return.
+ */
+#define MAX_WIRE_DETAILS 1024
+
+
+/**
+ * @brief A GET /private/orders/$ORDER handle
+ */
+struct TALER_MERCHANT_OrderMerchantGetHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OrderMerchantGetCallback 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 GET /private/orders/$ORDER
+ * request and we got an HTTP status of OK and the order was unpaid. Parse
+ * the response and call the callback.
+ *
+ * @param omgh handle for the request
+ * @param[in,out] osr HTTP response we got
+ */
+static void
+handle_unpaid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh,
+ struct TALER_MERCHANT_OrderStatusResponse *osr)
+{
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any (
+ "total_amount",
+ &osr->details.ok.details.unpaid.contract_amount),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string (
+ "already_paid_order_id",
+ &osr->details.ok.details.unpaid.already_paid_order_id),
+ NULL),
+ GNUNET_JSON_spec_string (
+ "taler_pay_uri",
+ &osr->details.ok.details.unpaid.taler_pay_uri),
+ GNUNET_JSON_spec_string (
+ "summary",
+ &osr->details.ok.details.unpaid.summary),
+ GNUNET_JSON_spec_timestamp (
+ "creation_time",
+ &osr->details.ok.details.unpaid.creation_time),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (osr->hr.reply,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ osr->hr.http_status = 0;
+ osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ omgh->cb (omgh->cb_cls,
+ osr);
+ return;
+ }
+ osr->details.ok.status = TALER_MERCHANT_OSC_UNPAID;
+ omgh->cb (omgh->cb_cls,
+ osr);
+}
+
+
+/**
+ * Function called when we're done processing the GET /private/orders/$ORDER
+ * request and we got an HTTP status of OK and the order was claimed but not
+ * paid. Parse the response and call the callback.
+ *
+ * @param omgh handle for the request
+ * @param[in,out] osr HTTP response we got
+ */
+static void
+handle_claimed (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh,
+ struct TALER_MERCHANT_OrderStatusResponse *osr)
+{
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_object_const (
+ "contract_terms",
+ &osr->details.ok.details.claimed.contract_terms),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (osr->hr.reply,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ osr->hr.http_status = 0;
+ osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ omgh->cb (omgh->cb_cls,
+ osr);
+ return;
+ }
+ osr->details.ok.status = TALER_MERCHANT_OSC_CLAIMED;
+ omgh->cb (omgh->cb_cls,
+ osr);
+}
+
+
+/**
+ * Function called when we're done processing the GET /private/orders/$ORDER
+ * request and we got an HTTP status of OK and the order was paid. Parse
+ * the response and call the callback.
+ *
+ * @param omgh handle for the request
+ * @param[in,out] osr HTTP response we got
+ */
+static void
+handle_paid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh,
+ struct TALER_MERCHANT_OrderStatusResponse *osr)
+{
+ uint32_t hc32;
+ const json_t *wire_details;
+ const json_t *refund_details;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_bool ("refunded",
+ &osr->details.ok.details.paid.refunded),
+ GNUNET_JSON_spec_bool ("refund_pending",
+ &osr->details.ok.details.paid.refund_pending),
+ GNUNET_JSON_spec_bool ("wired",
+ &osr->details.ok.details.paid.wired),
+ TALER_JSON_spec_amount_any ("deposit_total",
+ &osr->details.ok.details.paid.deposit_total),
+ TALER_JSON_spec_ec ("exchange_code",
+ &osr->details.ok.details.paid.exchange_ec),
+ GNUNET_JSON_spec_uint32 ("exchange_http_status",
+ &hc32),
+ TALER_JSON_spec_amount_any ("refund_amount",
+ &osr->details.ok.details.paid.refund_amount),
+ GNUNET_JSON_spec_object_const (
+ "contract_terms",
+ &osr->details.ok.details.paid.contract_terms),
+ GNUNET_JSON_spec_array_const ("wire_details",
+ &wire_details),
+ GNUNET_JSON_spec_array_const ("refund_details",
+ &refund_details),
+ /* Only available since **v14** */
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp ("last_payment",
+ &osr->details.ok.details.paid.last_payment),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (osr->hr.reply,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ osr->hr.http_status = 0;
+ osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ omgh->cb (omgh->cb_cls,
+ osr);
+ return;
+ }
+ osr->details.ok.status = TALER_MERCHANT_OSC_PAID;
+
+ osr->details.ok.details.paid.exchange_hc = (unsigned int) hc32;
+ {
+ unsigned int wts_len = (unsigned int) json_array_size (wire_details);
+ unsigned int ref_len = (unsigned int) json_array_size (refund_details);
+
+ if ( (json_array_size (wire_details) != (size_t) wts_len) ||
+ (wts_len > MAX_WIRE_DETAILS) )
+ {
+ GNUNET_break (0);
+ osr->hr.http_status = 0;
+ osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
+ omgh->cb (omgh->cb_cls,
+ osr);
+ return;
+ }
+ if ( (json_array_size (refund_details) != (size_t) ref_len) ||
+ (ref_len > MAX_REFUND_DETAILS) )
+ {
+ GNUNET_break (0);
+ osr->hr.http_status = 0;
+ osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
+ omgh->cb (omgh->cb_cls,
+ osr);
+ return;
+ }
+ {
+ struct TALER_MERCHANT_WireTransfer wts[GNUNET_NZL (wts_len)];
+ struct TALER_MERCHANT_RefundOrderDetail ref[GNUNET_NZL (ref_len)];
+
+ for (unsigned int i = 0; i<wts_len; i++)
+ {
+ struct TALER_MERCHANT_WireTransfer *wt = &wts[i];
+ const json_t *w = json_array_get (wire_details,
+ i);
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_web_url ("exchange_url",
+ &wt->exchange_url),
+ GNUNET_JSON_spec_fixed_auto ("wtid",
+ &wt->wtid),
+ GNUNET_JSON_spec_timestamp ("execution_time",
+ &wt->execution_time),
+ TALER_JSON_spec_amount_any ("amount",
+ &wt->total_amount),
+ GNUNET_JSON_spec_bool ("confirmed",
+ &wt->confirmed),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (w,
+ ispec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ osr->hr.http_status = 0;
+ osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ omgh->cb (omgh->cb_cls,
+ osr);
+ return;
+ }
+ }
+
+ for (unsigned int i = 0; i<ref_len; i++)
+ {
+ struct TALER_MERCHANT_RefundOrderDetail *ro = &ref[i];
+ const json_t *w = json_array_get (refund_details,
+ i);
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_amount_any ("amount",
+ &ro->refund_amount),
+ GNUNET_JSON_spec_string ("reason",
+ &ro->reason),
+ GNUNET_JSON_spec_timestamp ("timestamp",
+ &ro->refund_time),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (w,
+ ispec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ osr->hr.http_status = 0;
+ osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ omgh->cb (omgh->cb_cls,
+ osr);
+ return;
+ }
+ }
+
+ osr->details.ok.details.paid.wts = wts;
+ osr->details.ok.details.paid.wts_len = wts_len;
+ osr->details.ok.details.paid.refunds = ref;
+ osr->details.ok.details.paid.refunds_len = ref_len;
+ omgh->cb (omgh->cb_cls,
+ osr);
+ }
+ }
+}
+
+
+/**
+ * Function called when we're done processing the GET /private/orders/$ORDER
+ * request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderMerchantGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_merchant_order_get_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderMerchantGetHandle *omgh = cls;
+ const json_t *json = response;
+ const char *order_status;
+ struct TALER_MERCHANT_OrderStatusResponse osr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ omgh->job = NULL;
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ /* see below */
+ break;
+ case MHD_HTTP_ACCEPTED:
+ /* see below */
+ omgh->cb (omgh->cb_cls,
+ &osr);
+ TALER_MERCHANT_merchant_order_get_cancel (omgh);
+ return;
+ case MHD_HTTP_UNAUTHORIZED:
+ osr.hr.ec = TALER_JSON_get_error_code (json);
+ osr.hr.hint = TALER_JSON_get_error_hint (json);
+ omgh->cb (omgh->cb_cls,
+ &osr);
+ TALER_MERCHANT_merchant_order_get_cancel (omgh);
+ return;
+ case MHD_HTTP_NOT_FOUND:
+ osr.hr.ec = TALER_JSON_get_error_code (json);
+ osr.hr.hint = TALER_JSON_get_error_hint (json);
+ omgh->cb (omgh->cb_cls,
+ &osr);
+ TALER_MERCHANT_merchant_order_get_cancel (omgh);
+ return;
+ case MHD_HTTP_GATEWAY_TIMEOUT:
+ osr.hr.ec = TALER_JSON_get_error_code (json);
+ osr.hr.hint = TALER_JSON_get_error_hint (json);
+ omgh->cb (omgh->cb_cls,
+ &osr);
+ TALER_MERCHANT_merchant_order_get_cancel (omgh);
+ return;
+ default:
+ osr.hr.ec = TALER_JSON_get_error_code (json);
+ osr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Polling payment failed with HTTP status code %u/%d\n",
+ (unsigned int) response_code,
+ (int) osr.hr.ec);
+ GNUNET_break_op (0);
+ omgh->cb (omgh->cb_cls,
+ &osr);
+ TALER_MERCHANT_merchant_order_get_cancel (omgh);
+ return;
+ }
+
+ order_status = json_string_value (json_object_get (json,
+ "order_status"));
+
+ if (NULL == order_status)
+ {
+ GNUNET_break_op (0);
+ osr.hr.http_status = 0;
+ osr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ omgh->cb (omgh->cb_cls,
+ &osr);
+ TALER_MERCHANT_merchant_order_get_cancel (omgh);
+ return;
+ }
+
+ if (0 == strcmp ("paid",
+ order_status))
+ {
+ handle_paid (omgh,
+ &osr);
+ }
+ else if (0 == strcmp ("claimed",
+ order_status))
+ {
+ handle_claimed (omgh,
+ &osr);
+ }
+ else if (0 == strcmp ("unpaid",
+ order_status))
+ {
+ handle_unpaid (omgh,
+ &osr);
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ osr.hr.http_status = 0;
+ osr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ omgh->cb (omgh->cb_cls,
+ &osr);
+ }
+ TALER_MERCHANT_merchant_order_get_cancel (omgh);
+}
+
+
+struct TALER_MERCHANT_OrderMerchantGetHandle *
+TALER_MERCHANT_merchant_order_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *order_id,
+ const char *session_id,
+ struct GNUNET_TIME_Relative timeout,
+ TALER_MERCHANT_OrderMerchantGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OrderMerchantGetHandle *omgh;
+ unsigned int tms;
+
+ tms = (unsigned int) (timeout.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
+ omgh = GNUNET_new (struct TALER_MERCHANT_OrderMerchantGetHandle);
+ omgh->ctx = ctx;
+ omgh->cb = cb;
+ omgh->cb_cls = cb_cls;
+ {
+ char *path;
+ char timeout_ms[32];
+
+ GNUNET_snprintf (timeout_ms,
+ sizeof (timeout_ms),
+ "%u",
+ tms);
+ GNUNET_asprintf (&path,
+ "private/orders/%s",
+ order_id);
+ omgh->url = TALER_url_join (backend_url,
+ path,
+ "session_id", session_id,
+ "timeout_ms", (0 != tms) ? timeout_ms : NULL,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == omgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (omgh);
+ return NULL;
+ }
+
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (omgh->url);
+ if (NULL == eh)
+ {
+ GNUNET_break (0);
+ GNUNET_free (omgh->url);
+ GNUNET_free (omgh);
+ return NULL;
+ }
+ if (0 != tms)
+ {
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT_MS,
+ (long) (tms + 100L)));
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Getting order status from %s\n",
+ omgh->url);
+ if (NULL == (omgh->job =
+ GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_merchant_order_get_finished,
+ omgh)))
+ {
+ GNUNET_break (0);
+ GNUNET_free (omgh->url);
+ GNUNET_free (omgh);
+ return NULL;
+ }
+ }
+ return omgh;
+}
+
+
+void
+TALER_MERCHANT_merchant_order_get_cancel (
+ struct TALER_MERCHANT_OrderMerchantGetHandle *omgh)
+{
+ if (NULL != omgh->job)
+ {
+ GNUNET_CURL_job_cancel (omgh->job);
+ omgh->job = NULL;
+ }
+ GNUNET_free (omgh->url);
+ GNUNET_free (omgh);
+}
+
+
+/* end of merchant_api_merchant_get_order.c */
diff --git a/src/lib/merchant_api_get-private-orders.c b/src/lib/merchant_api_get-private-orders.c
@@ -0,0 +1,442 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_get-private-orders.c
+ * @brief Implementation of the GET /private/orders request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+/**
+ * Maximum number of orders we return.
+ */
+#define MAX_ORDERS 1024
+
+/**
+ * Handle for a GET /orders operation.
+ */
+struct TALER_MERCHANT_OrdersGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OrdersGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse order information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with order data
+ * @param[in] ogr response to fill
+ * @param ogh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_orders (const json_t *ia,
+ struct TALER_MERCHANT_OrdersGetResponse *ogr,
+ struct TALER_MERCHANT_OrdersGetHandle *ogh)
+{
+ unsigned int oes_len = (unsigned int) json_array_size (ia);
+
+ if ( (json_array_size (ia) != (size_t) oes_len) ||
+ (oes_len > MAX_ORDERS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_OrderEntry oes[GNUNET_NZL (oes_len)];
+ size_t index;
+ json_t *value;
+
+ memset (oes,
+ 0,
+ sizeof (oes));
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_OrderEntry *ie = &oes[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("order_id",
+ &ie->order_id),
+ GNUNET_JSON_spec_timestamp ("timestamp",
+ &ie->timestamp),
+ GNUNET_JSON_spec_uint64 ("row_id",
+ &ie->order_serial),
+ TALER_JSON_spec_amount_any ("amount",
+ &ie->amount),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("refund_amount",
+ &ie->refund_amount),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("pending_refund_amount",
+ &ie->pending_refund_amount),
+ NULL),
+ GNUNET_JSON_spec_string ("summary",
+ &ie->summary),
+ GNUNET_JSON_spec_bool ("refundable",
+ &ie->refundable),
+ GNUNET_JSON_spec_bool ("paid",
+ &ie->paid),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ ogr->details.ok.orders_length = oes_len;
+ ogr->details.ok.orders = oes;
+ ogh->cb (ogh->cb_cls,
+ ogr);
+ ogh->cb = NULL; /* just to be sure */
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /orders request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OrdersGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_orders_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrdersGetHandle *ogh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_OrdersGetResponse ogr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ogh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /orders response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *orders;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("orders",
+ &orders),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ ogr.hr.http_status = 0;
+ ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_orders (orders,
+ &ogr,
+ ogh))
+ {
+ TALER_MERCHANT_orders_get_cancel (ogh);
+ return;
+ }
+ ogr.hr.http_status = 0;
+ ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ ogr.hr.ec = TALER_JSON_get_error_code (json);
+ ogr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ ogr.hr.ec = TALER_JSON_get_error_code (json);
+ ogr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ ogr.hr.ec = TALER_JSON_get_error_code (json);
+ ogr.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) ogr.hr.ec);
+ break;
+ }
+ ogh->cb (ogh->cb_cls,
+ &ogr);
+ TALER_MERCHANT_orders_get_cancel (ogh);
+}
+
+
+struct TALER_MERCHANT_OrdersGetHandle *
+TALER_MERCHANT_orders_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_OrdersGetCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_orders_get2 (ctx,
+ backend_url,
+ TALER_EXCHANGE_YNA_ALL,
+ TALER_EXCHANGE_YNA_ALL,
+ TALER_EXCHANGE_YNA_ALL,
+ GNUNET_TIME_UNIT_FOREVER_TS,
+ UINT64_MAX,
+ -20, /* default is most recent 20 entries */
+ GNUNET_TIME_UNIT_ZERO,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_OrdersGetHandle *
+TALER_MERCHANT_orders_get2 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ enum TALER_EXCHANGE_YesNoAll paid,
+ enum TALER_EXCHANGE_YesNoAll refunded,
+ enum TALER_EXCHANGE_YesNoAll wired,
+ struct GNUNET_TIME_Timestamp date,
+ uint64_t start_row,
+ int64_t delta,
+ struct GNUNET_TIME_Relative timeout,
+ TALER_MERCHANT_OrdersGetCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_orders_get3 (
+ ctx,
+ backend_url,
+ paid,
+ refunded,
+ wired,
+ NULL,
+ NULL,
+ date,
+ start_row,
+ delta,
+ timeout,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_OrdersGetHandle *
+TALER_MERCHANT_orders_get3 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ enum TALER_EXCHANGE_YesNoAll paid,
+ enum TALER_EXCHANGE_YesNoAll refunded,
+ enum TALER_EXCHANGE_YesNoAll wired,
+ const char *session_id,
+ const char *fulfillment_url,
+ struct GNUNET_TIME_Timestamp date,
+ uint64_t start_row,
+ int64_t delta,
+ struct GNUNET_TIME_Relative timeout,
+ TALER_MERCHANT_OrdersGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OrdersGetHandle *ogh;
+ CURL *eh;
+ unsigned int tms = timeout.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
+
+ GNUNET_assert (NULL != backend_url);
+ if ( (delta > MAX_ORDERS) ||
+ (delta < -MAX_ORDERS) )
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ if (0 == delta)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ ogh = GNUNET_new (struct TALER_MERCHANT_OrdersGetHandle);
+ ogh->ctx = ctx;
+ ogh->cb = cb;
+ ogh->cb_cls = cb_cls;
+
+ /* build ogh->url with the various optional arguments */
+ {
+ char dstr[30];
+ char *fec = NULL;
+ char *sid = NULL;
+ bool have_date;
+ bool have_srow;
+ char cbuf[30];
+ char dbuf[30];
+ char tbuf[30];
+
+ GNUNET_snprintf (tbuf,
+ sizeof (tbuf),
+ "%u",
+ tms);
+ GNUNET_snprintf (dbuf,
+ sizeof (dbuf),
+ "%lld",
+ (long long) delta);
+ GNUNET_snprintf (cbuf,
+ sizeof (cbuf),
+ "%llu",
+ (unsigned long long) start_row);
+ if (NULL != session_id)
+ (void) GNUNET_STRINGS_urlencode (strlen (session_id),
+ session_id,
+ &sid);
+ if (NULL != fulfillment_url)
+ (void) GNUNET_STRINGS_urlencode (strlen (fulfillment_url),
+ fulfillment_url,
+ &fec);
+ GNUNET_snprintf (dstr,
+ sizeof (dstr),
+ "%llu",
+ (unsigned long long) GNUNET_TIME_timestamp_to_s (date));
+ if (delta > 0)
+ {
+ have_date = ! GNUNET_TIME_absolute_is_zero (date.abs_time);
+ have_srow = (0 != start_row);
+ }
+ else
+ {
+ have_date = ! GNUNET_TIME_absolute_is_never (date.abs_time);
+ have_srow = (UINT64_MAX != start_row);
+ }
+ ogh->url = TALER_url_join (backend_url,
+ "private/orders",
+ "paid",
+ (TALER_EXCHANGE_YNA_ALL != paid)
+ ? TALER_yna_to_string (paid)
+ : NULL,
+ "refunded",
+ (TALER_EXCHANGE_YNA_ALL != refunded)
+ ? TALER_yna_to_string (refunded)
+ : NULL,
+ "wired",
+ (TALER_EXCHANGE_YNA_ALL != wired)
+ ? TALER_yna_to_string (wired)
+ : NULL,
+ "date_s",
+ (have_date)
+ ? dstr
+ : NULL,
+ "start",
+ (have_srow)
+ ? cbuf
+ : NULL,
+ "delta",
+ (-20 != delta)
+ ? dbuf
+ : NULL,
+ "timeout_ms",
+ (0 != tms)
+ ? tbuf
+ : NULL,
+ "session_id",
+ sid,
+ "fulfillment_url",
+ fec,
+ NULL);
+ GNUNET_free (sid);
+ GNUNET_free (fec);
+ }
+ if (NULL == ogh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (ogh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ ogh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (ogh->url);
+ if (NULL == eh)
+ {
+ GNUNET_break (0);
+ GNUNET_free (ogh->url);
+ GNUNET_free (ogh);
+ return NULL;
+ }
+ if (0 != tms)
+ {
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT_MS,
+ (long) (tms + 100L)));
+ }
+ ogh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_orders_finished,
+ ogh);
+ return ogh;
+}
+
+
+void
+TALER_MERCHANT_orders_get_cancel (
+ struct TALER_MERCHANT_OrdersGetHandle *ogh)
+{
+ if (NULL != ogh->job)
+ GNUNET_CURL_job_cancel (ogh->job);
+ GNUNET_free (ogh->url);
+ GNUNET_free (ogh);
+}
diff --git a/src/lib/merchant_api_get-private-otp-devices-DEVICE_ID.c b/src/lib/merchant_api_get-private-otp-devices-DEVICE_ID.c
@@ -0,0 +1,210 @@
+/*
+ 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_get-private-otp-devices-DEVICE_ID.c
+ * @brief Implementation of the GET /otp-devices/$ID request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/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 /otp-devices/$ID operation.
+ */
+struct TALER_MERCHANT_OtpDeviceGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OtpDeviceGetCallback 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 /otp-devices/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OtpDeviceGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_otp_device_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OtpDeviceGetHandle *tgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_OtpDeviceGetResponse tgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ tgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /otp-devices/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ uint32_t alg32;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("otp_device_description",
+ &tgr.details.ok.otp_device_description),
+ GNUNET_JSON_spec_uint32 ("otp_algorithm",
+ &alg32),
+ 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 ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ tgr.details.ok.otp_alg =
+ (enum TALER_MerchantConfirmationAlgorithm) alg32;
+ tgh->cb (tgh->cb_cls,
+ &tgr);
+ TALER_MERCHANT_otp_device_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_otp_device_get_cancel (tgh);
+}
+
+
+struct TALER_MERCHANT_OtpDeviceGetHandle *
+TALER_MERCHANT_otp_device_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *otp_device_id,
+ TALER_MERCHANT_OtpDeviceGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OtpDeviceGetHandle *tgh;
+ CURL *eh;
+
+ tgh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceGetHandle);
+ tgh->ctx = ctx;
+ tgh->cb = cb;
+ tgh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/otp-devices/%s",
+ otp_device_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_otp_device_finished,
+ tgh);
+ return tgh;
+}
+
+
+void
+TALER_MERCHANT_otp_device_get_cancel (
+ struct TALER_MERCHANT_OtpDeviceGetHandle *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-private-otp-devices.c b/src/lib/merchant_api_get-private-otp-devices.c
@@ -0,0 +1,248 @@
+/*
+ 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_get-private-otp-devices.c
+ * @brief Implementation of the GET /otp-devices request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+/**
+ * Maximum number of OTP devices we return.
+ */
+#define MAX_OTP 1024
+
+
+/**
+ * Handle for a GET /otp-devices operation.
+ */
+struct TALER_MERCHANT_OtpDevicesGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OtpDevicesGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse OTP device information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with otp_device data
+ * @param[in] tgr partially filled response
+ * @param tgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_otp_devices (const json_t *ia,
+ struct TALER_MERCHANT_OtpDevicesGetResponse *tgr,
+ struct TALER_MERCHANT_OtpDevicesGetHandle *tgh)
+{
+ unsigned int otp_len = (unsigned int) json_array_size (ia);
+
+ if ( (json_array_size (ia) != (size_t) otp_len) ||
+ (otp_len > MAX_OTP) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_OtpDeviceEntry otp[GNUNET_NZL (otp_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_OtpDeviceEntry *ie = &otp[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("otp_device_id",
+ &ie->otp_device_id),
+ GNUNET_JSON_spec_string ("device_description",
+ &ie->otp_device_description),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ tgr->details.ok.otp_devices_length = otp_len;
+ tgr->details.ok.otp_devices = otp;
+ 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 /otp-devices request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OtpDevicesGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_otp_devices_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OtpDevicesGetHandle *tgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_OtpDevicesGetResponse tgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ tgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /otp-devices response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *otp_devices;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("otp_devices",
+ &otp_devices),
+ 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_otp_devices (otp_devices,
+ &tgr,
+ tgh))
+ {
+ TALER_MERCHANT_otp_devices_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_otp_devices_get_cancel (tgh);
+}
+
+
+struct TALER_MERCHANT_OtpDevicesGetHandle *
+TALER_MERCHANT_otp_devices_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_OtpDevicesGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OtpDevicesGetHandle *tgh;
+ CURL *eh;
+
+ tgh = GNUNET_new (struct TALER_MERCHANT_OtpDevicesGetHandle);
+ tgh->ctx = ctx;
+ tgh->cb = cb;
+ tgh->cb_cls = cb_cls;
+ tgh->url = TALER_url_join (backend_url,
+ "private/otp-devices",
+ 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_otp_devices_finished,
+ tgh);
+ return tgh;
+}
+
+
+void
+TALER_MERCHANT_otp_devices_get_cancel (
+ struct TALER_MERCHANT_OtpDevicesGetHandle *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-private-products-PRODUCT_ID.c b/src/lib/merchant_api_get-private-products-PRODUCT_ID.c
@@ -0,0 +1,249 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014--2025 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_get-private-products-PRODUCT_ID.c
+ * @brief Implementation of the GET /product/$ID request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/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 /products/$ID operation.
+ */
+struct TALER_MERCHANT_ProductGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ProductGetCallback 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 /products/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_ProductGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_product_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_ProductGetHandle *pgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_ProductGetResponse pgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /products/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string (
+ "product_name",
+ &pgr.details.ok.product_name),
+ GNUNET_JSON_spec_string (
+ "description",
+ &pgr.details.ok.description),
+ GNUNET_JSON_spec_object_const (
+ "description_i18n",
+ &pgr.details.ok.description_i18n),
+ GNUNET_JSON_spec_string (
+ "unit",
+ &pgr.details.ok.unit),
+ TALER_JSON_spec_amount_any_array (
+ "unit_price",
+ &pgr.details.ok.unit_price_len,
+ (struct TALER_Amount **) &pgr.details.ok.unit_price),
+ TALER_JSON_spec_amount_any (
+ "price",
+ &pgr.details.ok.price),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string (
+ "image",
+ &pgr.details.ok.image),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "taxes",
+ &pgr.details.ok.taxes),
+ NULL),
+ GNUNET_JSON_spec_int64 (
+ "total_stock",
+ &pgr.details.ok.total_stock),
+ GNUNET_JSON_spec_string (
+ "unit_total_stock",
+ &pgr.details.ok.unit_total_stock),
+ GNUNET_JSON_spec_bool (
+ "unit_allow_fraction",
+ &pgr.details.ok.unit_allow_fraction),
+ GNUNET_JSON_spec_uint32 (
+ "unit_precision_level",
+ &pgr.details.ok.unit_precision_level),
+ GNUNET_JSON_spec_uint64 (
+ "total_sold",
+ &pgr.details.ok.total_sold),
+ GNUNET_JSON_spec_uint64 (
+ "total_lost",
+ &pgr.details.ok.total_lost),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const (
+ "address",
+ &pgr.details.ok.location),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp (
+ "next_restock",
+ &pgr.details.ok.next_restock),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ pgh->cb (pgh->cb_cls,
+ &pgr);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_product_get_cancel (pgh);
+ return;
+ }
+ pgr.hr.http_status = 0;
+ pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ pgr.hr.ec = TALER_JSON_get_error_code (json);
+ pgr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ pgr.hr.ec = TALER_JSON_get_error_code (json);
+ pgr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ pgr.hr.ec = TALER_JSON_get_error_code (json);
+ pgr.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) pgr.hr.ec);
+ break;
+ }
+ pgh->cb (pgh->cb_cls,
+ &pgr);
+ TALER_MERCHANT_product_get_cancel (pgh);
+}
+
+
+struct TALER_MERCHANT_ProductGetHandle *
+TALER_MERCHANT_product_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ TALER_MERCHANT_ProductGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_ProductGetHandle *pgh;
+ CURL *eh;
+
+ pgh = GNUNET_new (struct TALER_MERCHANT_ProductGetHandle);
+ pgh->ctx = ctx;
+ pgh->cb = cb;
+ pgh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/products/%s",
+ product_id);
+ pgh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (pgh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ pgh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (pgh->url);
+ pgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_product_finished,
+ pgh);
+ return pgh;
+}
+
+
+void
+TALER_MERCHANT_product_get_cancel (
+ struct TALER_MERCHANT_ProductGetHandle *pgh)
+{
+ if (NULL != pgh->job)
+ GNUNET_CURL_job_cancel (pgh->job);
+ GNUNET_free (pgh->url);
+ GNUNET_free (pgh);
+}
diff --git a/src/lib/merchant_api_get-private-products.c b/src/lib/merchant_api_get-private-products.c
@@ -0,0 +1,262 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2024 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_get-private-products.c
+ * @brief Implementation of the GET /products request of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Maximum number of products we return.
+ */
+#define MAX_PRODUCTS 1024
+
+
+/**
+ * Handle for a GET /products operation.
+ */
+struct TALER_MERCHANT_ProductsGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ProductsGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse product information from @a ia.
+ *
+ * @param json overall JSON reply
+ * @param ia JSON array (or NULL!) with product data
+ * @param pgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_products (const json_t *json,
+ const json_t *ia,
+ struct TALER_MERCHANT_ProductsGetHandle *pgh)
+{
+ unsigned int ies_len = json_array_size (ia);
+
+ if ( (json_array_size (ia) != (size_t) ies_len) ||
+ (ies_len > MAX_PRODUCTS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_InventoryEntry ies[GNUNET_NZL (ies_len)];
+ size_t index;
+ json_t *value;
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = GNUNET_OK;
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_InventoryEntry *ie = &ies[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("product_id",
+ &ie->product_id),
+ GNUNET_JSON_spec_uint64 ("product_serial",
+ &ie->product_serial),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ret = GNUNET_SYSERR;
+ continue;
+ }
+ if (GNUNET_SYSERR == ret)
+ break;
+ }
+ if (GNUNET_OK == ret)
+ {
+ struct TALER_MERCHANT_GetProductsResponse gpr = {
+ .hr.http_status = MHD_HTTP_OK,
+ .hr.reply = json,
+ .details.ok.products_length = ies_len,
+ .details.ok.products = ies
+ };
+
+ pgh->cb (pgh->cb_cls,
+ &gpr);
+ pgh->cb = NULL; /* just to be sure */
+ }
+ return ret;
+ }
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /products request.
+ *
+ * @param cls the `struct TALER_MERCHANT_ProductsGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_products_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_ProductsGetHandle *pgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetProductsResponse gpr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /products response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *products;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("products",
+ &products),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ gpr.hr.http_status = 0;
+ gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_products (json,
+ products,
+ pgh))
+ {
+ TALER_MERCHANT_products_get_cancel (pgh);
+ return;
+ }
+ gpr.hr.http_status = 0;
+ gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ gpr.hr.ec = TALER_JSON_get_error_code (json);
+ gpr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ default:
+ /* unexpected response code */
+ gpr.hr.ec = TALER_JSON_get_error_code (json);
+ gpr.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) gpr.hr.ec);
+ break;
+ }
+ pgh->cb (pgh->cb_cls,
+ &gpr);
+ TALER_MERCHANT_products_get_cancel (pgh);
+}
+
+
+struct TALER_MERCHANT_ProductsGetHandle *
+TALER_MERCHANT_products_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_ProductsGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_ProductsGetHandle *pgh;
+ CURL *eh;
+
+ pgh = GNUNET_new (struct TALER_MERCHANT_ProductsGetHandle);
+ pgh->ctx = ctx;
+ pgh->cb = cb;
+ pgh->cb_cls = cb_cls;
+ pgh->url = TALER_url_join (backend_url,
+ "private/products",
+ NULL);
+ if (NULL == pgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (pgh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ pgh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (pgh->url);
+ pgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_products_finished,
+ pgh);
+ return pgh;
+}
+
+
+void
+TALER_MERCHANT_products_get_cancel (
+ struct TALER_MERCHANT_ProductsGetHandle *pgh)
+{
+ if (NULL != pgh->job)
+ GNUNET_CURL_job_cancel (pgh->job);
+ GNUNET_free (pgh->url);
+ GNUNET_free (pgh);
+}
diff --git a/src/lib/merchant_api_get-private-statistics-amount-SLUG.c b/src/lib/merchant_api_get-private-statistics-amount-SLUG.c
@@ -0,0 +1,408 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_get-private-statistics-amount-SLUG.c
+ * @brief Implementation of the GET /private/statistics-amount/$SLUG request of the merchant's HTTP API
+ * @author Martin Schanzenbach
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_json_lib.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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+/**
+ * Maximum number of statistics we return
+ */
+#define MAX_STATISTICS 1024
+
+/**
+ * Handle for a GET /statistics-amount/$SLUG operation.
+ */
+struct TALER_MERCHANT_StatisticsAmountGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_StatisticsAmountGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse interval information from buckets and intervals.
+ *
+ * @param json overall JSON reply
+ * @param jbuckets JSON array (or NULL!) with bucket data
+ * @param buckets_description human-readable description for the buckets
+ * @param jintervals JSON array (or NULL!) with bucket data
+ * @param intervals_description human-readable description for the intervals
+ * @param sgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_intervals_and_buckets_amt (
+ const json_t *json,
+ const json_t *jbuckets,
+ const char *buckets_description,
+ const json_t *jintervals,
+ const char *intervals_description,
+ struct TALER_MERCHANT_StatisticsAmountGetHandle *sgh
+ )
+{
+ unsigned int resp_buckets_len = json_array_size (jbuckets);
+ unsigned int resp_intervals_len = json_array_size (jintervals);
+
+ if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
+ (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
+ (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_StatisticAmountByBucket resp_buckets[
+ GNUNET_NZL (resp_buckets_len)];
+ struct TALER_MERCHANT_StatisticAmountByInterval resp_intervals[
+ GNUNET_NZL (resp_intervals_len)];
+ size_t index;
+ json_t *value;
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = GNUNET_OK;
+ json_array_foreach (jintervals, index, value) {
+ struct TALER_MERCHANT_StatisticAmountByInterval *jinterval
+ = &resp_intervals[index];
+ const json_t *amounts_arr;
+ size_t amounts_len;
+
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &jinterval->start_time),
+ GNUNET_JSON_spec_array_const ("cumulative_amounts",
+ &amounts_arr),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ret = GNUNET_SYSERR;
+ continue;
+ }
+ if (GNUNET_SYSERR == ret)
+ break;
+ amounts_len = json_array_size (amounts_arr);
+ {
+ struct TALER_Amount amt_arr[amounts_len];
+ size_t aindex;
+ json_t *avalue;
+
+ jinterval->cumulative_amount_len = amounts_len;
+ jinterval->cumulative_amounts = amt_arr;
+ json_array_foreach (amounts_arr, aindex, avalue) {
+ if (! json_is_string (avalue))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_string_to_amount (json_string_value (avalue),
+ &amt_arr[aindex]))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+ }
+ ret = GNUNET_OK;
+ json_array_foreach (jbuckets, index, value) {
+ struct TALER_MERCHANT_StatisticAmountByBucket *jbucket
+ = &resp_buckets[index];
+ const json_t *amounts_arr;
+ size_t amounts_len;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &jbucket->start_time),
+ GNUNET_JSON_spec_timestamp ("end_time",
+ &jbucket->end_time),
+ GNUNET_JSON_spec_string ("range",
+ &jbucket->range),
+ GNUNET_JSON_spec_array_const ("cumulative_amounts",
+ &amounts_arr),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ret = GNUNET_SYSERR;
+ continue;
+ }
+ if (GNUNET_SYSERR == ret)
+ break;
+ amounts_len = json_array_size (amounts_arr);
+ if (0 > amounts_len)
+ {
+ GNUNET_break_op (0);
+ ret = GNUNET_SYSERR;
+ break;
+ }
+ {
+ struct TALER_Amount amt_arr[amounts_len];
+ size_t aindex;
+ json_t *avalue;
+ jbucket->cumulative_amount_len = amounts_len;
+ jbucket->cumulative_amounts = amt_arr;
+ json_array_foreach (amounts_arr, aindex, avalue) {
+ if (! json_is_string (avalue))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_string_to_amount (json_string_value (avalue),
+ &amt_arr[aindex]))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+ }
+ if (GNUNET_OK == ret)
+ {
+ struct TALER_MERCHANT_StatisticsAmountGetResponse gsr = {
+ .hr.http_status = MHD_HTTP_OK,
+ .hr.reply = json,
+ .details.ok.buckets_length = resp_buckets_len,
+ .details.ok.buckets = resp_buckets,
+ .details.ok.buckets_description = buckets_description,
+ .details.ok.intervals_length = resp_intervals_len,
+ .details.ok.intervals = resp_intervals,
+ .details.ok.intervals_description = intervals_description,
+ };
+ sgh->cb (sgh->cb_cls,
+ &gsr);
+ sgh->cb = NULL; /* just to be sure */
+ }
+ return ret;
+ }
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /statistics-amount/$SLUG request.
+ *
+ * @param cls the `struct TALER_MERCHANT_StatisticsAmountGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_statistics_amount_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_StatisticsAmountGetHandle *handle = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_StatisticsAmountGetResponse res = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ handle->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /statistics-amount/$SLUG response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *buckets;
+ const json_t *intervals;
+ const char *buckets_description = NULL;
+ const char *intervals_description = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("buckets",
+ &buckets),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("buckets_description",
+ &buckets_description),
+ NULL),
+ GNUNET_JSON_spec_array_const ("intervals",
+ &intervals),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("intervals_description",
+ &intervals_description),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ res.hr.http_status = 0;
+ res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_intervals_and_buckets_amt (json,
+ buckets,
+ buckets_description,
+ intervals,
+ intervals_description,
+ handle))
+ {
+ TALER_MERCHANT_statistic_amount_get_cancel (handle);
+ return;
+ }
+ res.hr.http_status = 0;
+ res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.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) res.hr.ec);
+ break;
+ }
+}
+
+
+struct TALER_MERCHANT_StatisticsAmountGetHandle *
+TALER_MERCHANT_statistic_amount_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *slug,
+ enum TALER_MERCHANT_StatisticsType stype,
+ TALER_MERCHANT_StatisticsAmountGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_StatisticsAmountGetHandle *handle;
+ CURL *eh;
+
+ handle = GNUNET_new (struct TALER_MERCHANT_StatisticsAmountGetHandle);
+ handle->ctx = ctx;
+ handle->cb = cb;
+ handle->cb_cls = cb_cls;
+ {
+ const char *filter = NULL;
+ char *path;
+
+ switch (stype)
+ {
+ case TALER_MERCHANT_STATISTICS_BY_BUCKET:
+ filter = "bucket";
+ break;
+ case TALER_MERCHANT_STATISTICS_BY_INTERVAL:
+ filter = "interval";
+ break;
+ case TALER_MERCHANT_STATISTICS_ALL:
+ filter = NULL;
+ break;
+ }
+ GNUNET_asprintf (&path,
+ "private/statistics-amount/%s",
+ slug);
+ handle->url = TALER_url_join (backend_url,
+ path,
+ "by",
+ filter,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == handle->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (handle);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ handle->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
+ handle->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_statistics_amount_finished,
+ handle);
+ return handle;
+}
+
+
+void
+TALER_MERCHANT_statistic_amount_get_cancel (
+ struct TALER_MERCHANT_StatisticsAmountGetHandle *handle)
+{
+ if (NULL != handle->job)
+ GNUNET_CURL_job_cancel (handle->job);
+ GNUNET_free (handle->url);
+ GNUNET_free (handle);
+}
+
+
+/* end of merchant_api_get-private-statistics-amount-SLUG.c */
diff --git a/src/lib/merchant_api_get-private-statistics-counter-SLUG.c b/src/lib/merchant_api_get-private-statistics-counter-SLUG.c
@@ -0,0 +1,353 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_get-private-statistics-counter-SLUG.c
+ * @brief Implementation of the GET /private/statistics-counter/$SLUG request of the merchant's HTTP API
+ * @author Martin Schanzenbach
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_json_lib.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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+/**
+ * Maximum number of statistics we return
+ */
+#define MAX_STATISTICS 1024
+
+/**
+ * Handle for a GET /statistics-counter/$SLUG operation.
+ */
+struct TALER_MERCHANT_StatisticsCounterGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_StatisticsCounterGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse interval information from @a ia.
+ *
+ * @param json overall JSON reply
+ * @param jbuckets JSON array (or NULL!) with bucket data
+ * @param buckets_description human-readable description for the buckets
+ * @param jintervals JSON array (or NULL!) with bucket data
+ * @param intervals_description human-readable description for the intervals
+ * @param scgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_intervals_and_buckets (
+ const json_t *json,
+ const json_t *jbuckets,
+ const char *buckets_description,
+ const json_t *jintervals,
+ const char *intervals_description,
+ struct TALER_MERCHANT_StatisticsCounterGetHandle *scgh)
+{
+ unsigned int resp_buckets_len = json_array_size (jbuckets);
+ unsigned int resp_intervals_len = json_array_size (jintervals);
+
+ if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
+ (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
+ (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_StatisticCounterByBucket resp_buckets[
+ GNUNET_NZL (resp_buckets_len)];
+ struct TALER_MERCHANT_StatisticCounterByInterval resp_intervals[
+ GNUNET_NZL (resp_intervals_len)];
+ size_t index;
+ json_t *value;
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = GNUNET_OK;
+ json_array_foreach (jintervals, index, value) {
+ struct TALER_MERCHANT_StatisticCounterByInterval *jinterval
+ = &resp_intervals[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &jinterval->start_time),
+ GNUNET_JSON_spec_uint64 ("cumulative_counter",
+ &jinterval->cumulative_counter),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ret = GNUNET_SYSERR;
+ continue;
+ }
+ if (GNUNET_SYSERR == ret)
+ break;
+ }
+ ret = GNUNET_OK;
+ json_array_foreach (jbuckets, index, value) {
+ struct TALER_MERCHANT_StatisticCounterByBucket *jbucket = &resp_buckets[
+ index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &jbucket->start_time),
+ GNUNET_JSON_spec_timestamp ("end_time",
+ &jbucket->end_time),
+ GNUNET_JSON_spec_string ("range",
+ &jbucket->range),
+ GNUNET_JSON_spec_uint64 ("cumulative_counter",
+ &jbucket->cumulative_counter),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ret = GNUNET_SYSERR;
+ continue;
+ }
+ if (GNUNET_SYSERR == ret)
+ break;
+ }
+ if (GNUNET_OK == ret)
+ {
+ struct TALER_MERCHANT_StatisticsCounterGetResponse gsr = {
+ .hr.http_status = MHD_HTTP_OK,
+ .hr.reply = json,
+ .details.ok.buckets_length = resp_buckets_len,
+ .details.ok.buckets = resp_buckets,
+ .details.ok.buckets_description = buckets_description,
+ .details.ok.intervals_length = resp_intervals_len,
+ .details.ok.intervals = resp_intervals,
+ .details.ok.intervals_description = intervals_description,
+ };
+ scgh->cb (scgh->cb_cls,
+ &gsr);
+ scgh->cb = NULL; /* just to be sure */
+ }
+ return ret;
+ }
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /statistics-counter/$SLUG request.
+ *
+ * @param cls the `struct TALER_MERCHANT_StatisticsCounterGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_statistics_counter_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_StatisticsCounterGetHandle *handle = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_StatisticsCounterGetResponse res = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ handle->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /statistics-counter/$SLUG response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *buckets;
+ const json_t *intervals;
+ const char *buckets_description;
+ const char *intervals_description;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("buckets",
+ &buckets),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("buckets_description",
+ &buckets_description),
+ NULL),
+ GNUNET_JSON_spec_array_const ("intervals",
+ &intervals),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("intervals_description",
+ &intervals_description),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ res.hr.http_status = 0;
+ res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "%s\n", json_dumps (json, JSON_INDENT (1)));
+ if (GNUNET_OK ==
+ parse_intervals_and_buckets (json,
+ buckets,
+ buckets_description,
+ intervals,
+ intervals_description,
+ handle))
+ {
+ TALER_MERCHANT_statistic_counter_get_cancel (handle);
+ return;
+ }
+ res.hr.http_status = 0;
+ res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.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) res.hr.ec);
+ break;
+ }
+}
+
+
+struct TALER_MERCHANT_StatisticsCounterGetHandle *
+TALER_MERCHANT_statistic_counter_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *slug,
+ enum TALER_MERCHANT_StatisticsType stype,
+ TALER_MERCHANT_StatisticsCounterGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_StatisticsCounterGetHandle *handle;
+ CURL *eh;
+
+ handle = GNUNET_new (struct TALER_MERCHANT_StatisticsCounterGetHandle);
+ handle->ctx = ctx;
+ handle->cb = cb;
+ handle->cb_cls = cb_cls;
+ {
+ const char *filter = NULL;
+ char *path;
+
+ switch (stype)
+ {
+ case TALER_MERCHANT_STATISTICS_BY_BUCKET:
+ filter = "bucket";
+ break;
+ case TALER_MERCHANT_STATISTICS_BY_INTERVAL:
+ filter = "interval";
+ break;
+ case TALER_MERCHANT_STATISTICS_ALL:
+ filter = NULL;
+ break;
+ }
+ GNUNET_asprintf (&path,
+ "private/statistics-counter/%s",
+ slug);
+ handle->url = TALER_url_join (backend_url,
+ path,
+ "by",
+ filter,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == handle->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (handle);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ handle->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
+ handle->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_statistics_counter_finished,
+ handle);
+ return handle;
+}
+
+
+void
+TALER_MERCHANT_statistic_counter_get_cancel (
+ struct TALER_MERCHANT_StatisticsCounterGetHandle *handle)
+{
+ if (NULL != handle->job)
+ GNUNET_CURL_job_cancel (handle->job);
+ GNUNET_free (handle->url);
+ GNUNET_free (handle);
+}
+
+
+/* end of merchant_api_get-private-statistics-counter-SLUG.c */
diff --git a/src/lib/merchant_api_get-private-templates-TEMPLATE_ID.c b/src/lib/merchant_api_get-private-templates-TEMPLATE_ID.c
@@ -0,0 +1,201 @@
+/*
+ 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_get-private-templates-TEMPLATE_ID.c
+ * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/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/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_TemplateGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TemplateGetCallback 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_TemplateGetHandle *tgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_TemplateGetResponse 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_string ("template_description",
+ &tgr.details.ok.template_description),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("otp_id",
+ &tgr.details.ok.otp_id),
+ NULL),
+ 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_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_template_get_cancel (tgh);
+}
+
+
+struct TALER_MERCHANT_TemplateGetHandle *
+TALER_MERCHANT_template_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *template_id,
+ TALER_MERCHANT_TemplateGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TemplateGetHandle *tgh;
+ CURL *eh;
+
+ tgh = GNUNET_new (struct TALER_MERCHANT_TemplateGetHandle);
+ tgh->ctx = ctx;
+ tgh->cb = cb;
+ tgh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/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_template_get_cancel (
+ struct TALER_MERCHANT_TemplateGetHandle *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-private-templates.c b/src/lib/merchant_api_get-private-templates.c
@@ -0,0 +1,247 @@
+/*
+ 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_get-private-templates.c
+ * @brief Implementation of the GET /templates request of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Maximum number of templates we return.
+ */
+#define MAX_TEMPLATES 1024
+
+
+/**
+ * Handle for a GET /templates operation.
+ */
+struct TALER_MERCHANT_TemplatesGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TemplatesGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse template information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with template data
+ * @param[in] tgr partially filled response
+ * @param tgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_templates (const json_t *ia,
+ struct TALER_MERCHANT_TemplatesGetResponse *tgr,
+ struct TALER_MERCHANT_TemplatesGetHandle *tgh)
+{
+ unsigned int tmpl_len = (unsigned int) json_array_size (ia);
+
+ if ( (json_array_size (ia) != (size_t) tmpl_len) ||
+ (tmpl_len > MAX_TEMPLATES) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_TemplateEntry tmpl[GNUNET_NZL (tmpl_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_TemplateEntry *ie = &tmpl[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("template_id",
+ &ie->template_id),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ tgr->details.ok.templates_length = tmpl_len;
+ tgr->details.ok.templates = tmpl;
+ 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 /templates request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TemplatesGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_templates_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_TemplatesGetHandle *tgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_TemplatesGetResponse tgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ tgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /templates response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *templates;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("templates",
+ &templates),
+ 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_templates (templates,
+ &tgr,
+ tgh))
+ {
+ TALER_MERCHANT_templates_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_templates_get_cancel (tgh);
+}
+
+
+struct TALER_MERCHANT_TemplatesGetHandle *
+TALER_MERCHANT_templates_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_TemplatesGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TemplatesGetHandle *tgh;
+ CURL *eh;
+
+ tgh = GNUNET_new (struct TALER_MERCHANT_TemplatesGetHandle);
+ tgh->ctx = ctx;
+ tgh->cb = cb;
+ tgh->cb_cls = cb_cls;
+ tgh->url = TALER_url_join (backend_url,
+ "private/templates",
+ 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_templates_finished,
+ tgh);
+ return tgh;
+}
+
+
+void
+TALER_MERCHANT_templates_get_cancel (
+ struct TALER_MERCHANT_TemplatesGetHandle *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-private-tokenfamilies-TOKEN_FAMILY_SLUG.c b/src/lib/merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.c
@@ -0,0 +1,218 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023-2024 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_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.c
+ * @brief Implementation of the GET /tokenfamily/$ID request of the merchant's HTTP API
+ * @author Christian Blättler
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_json_lib.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/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 /tokenfamilies/$SLUG operation.
+ */
+struct TALER_MERCHANT_TokenFamilyGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TokenFamilyGetCallback 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 /tokenfamilies/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TokenFamilyGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_token_family_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_TokenFamilyGetHandle *handle = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_TokenFamilyGetResponse res = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ handle->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /tokenfamilies/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ // Parse token family response
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("slug",
+ &res.details.ok.slug),
+ GNUNET_JSON_spec_string ("name",
+ &res.details.ok.name),
+ GNUNET_JSON_spec_string ("description",
+ &res.details.ok.description),
+ GNUNET_JSON_spec_object_const ("description_i18n",
+ &res.details.ok.description_i18n),
+ GNUNET_JSON_spec_object_const ("extra_data",
+ &res.details.ok.extra_data),
+ GNUNET_JSON_spec_timestamp ("valid_after",
+ &res.details.ok.valid_after),
+ GNUNET_JSON_spec_timestamp ("valid_before",
+ &res.details.ok.valid_before),
+ GNUNET_JSON_spec_relative_time ("duation",
+ &res.details.ok.duration),
+ GNUNET_JSON_spec_relative_time ("validity_granularity",
+ &res.details.ok.validity_granularity),
+ GNUNET_JSON_spec_relative_time ("start_offset",
+ &res.details.ok.start_offset),
+ GNUNET_JSON_spec_string ("kind",
+ &res.details.ok.kind),
+ GNUNET_JSON_spec_uint64 ("issued",
+ &res.details.ok.issued),
+ GNUNET_JSON_spec_uint64 ("used",
+ &res.details.ok.used),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ handle->cb (handle->cb_cls,
+ &res);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_token_family_get_cancel (handle);
+ return;
+ }
+ res.hr.http_status = 0;
+ res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ res.hr.ec = TALER_JSON_get_error_code (json);
+ res.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) res.hr.ec);
+ break;
+ }
+}
+
+
+struct TALER_MERCHANT_TokenFamilyGetHandle *
+TALER_MERCHANT_token_family_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *token_family_slug,
+ TALER_MERCHANT_TokenFamilyGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TokenFamilyGetHandle *handle;
+ CURL *eh;
+
+ handle = GNUNET_new (struct TALER_MERCHANT_TokenFamilyGetHandle);
+ handle->ctx = ctx;
+ handle->cb = cb;
+ handle->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/tokenfamilies/%s",
+ token_family_slug);
+ handle->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == handle->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (handle);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ handle->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
+ handle->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_token_family_finished,
+ handle);
+ return handle;
+}
+
+
+void
+TALER_MERCHANT_token_family_get_cancel (
+ struct TALER_MERCHANT_TokenFamilyGetHandle *handle)
+{
+ if (NULL != handle->job)
+ GNUNET_CURL_job_cancel (handle->job);
+ GNUNET_free (handle->url);
+ GNUNET_free (handle);
+}
diff --git a/src/lib/merchant_api_get-private-transfers.c b/src/lib/merchant_api_get-private-transfers.c
@@ -0,0 +1,314 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_get-private-transfers.c
+ * @brief Implementation of the GET /transfers request of the merchant's HTTP API
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * @brief A Handle for tracking wire transfers.
+ */
+struct TALER_MERCHANT_GetTransfersHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetTransfersCallback 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 /transfers request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetTransfersHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_transfers_get_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetTransfersHandle *gth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetTransfersResponse gtr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gth->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ const json_t *transfers;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("transfers",
+ &transfers),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ gtr.hr.http_status = 0;
+ gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+
+ {
+ size_t tds_length;
+ struct TALER_MERCHANT_TransferData *tds;
+ json_t *transfer;
+ size_t i;
+ bool ok;
+
+ tds_length = json_array_size (transfers);
+ tds = GNUNET_new_array (tds_length,
+ struct TALER_MERCHANT_TransferData);
+ ok = true;
+ json_array_foreach (transfers, i, transfer) {
+ struct TALER_MERCHANT_TransferData *td = &tds[i];
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_amount_any ("credit_amount",
+ &td->credit_amount),
+ GNUNET_JSON_spec_fixed_auto ("wtid",
+ &td->wtid),
+ TALER_JSON_spec_full_payto_uri ("payto_uri",
+ &td->payto_uri),
+ TALER_JSON_spec_web_url ("exchange_url",
+ &td->exchange_url),
+ GNUNET_JSON_spec_uint64 ("transfer_serial_id",
+ &td->credit_serial),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp ("execution_time",
+ &td->execution_time),
+ NULL),
+ GNUNET_JSON_spec_bool ("expected",
+ &td->expected),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (transfer,
+ ispec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ok = false;
+ break;
+ }
+ }
+
+ if (! ok)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (tds);
+ gtr.hr.http_status = 0;
+ gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ gtr.details.ok.transfers = tds;
+ gtr.details.ok.transfers_length = tds_length;
+ gth->cb (gth->cb_cls,
+ >r);
+ GNUNET_free (tds);
+ TALER_MERCHANT_transfers_get_cancel (gth);
+ return;
+ }
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ gtr.hr.ec = TALER_JSON_get_error_code (json);
+ gtr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ gtr.hr.ec = TALER_JSON_get_error_code (json);
+ gtr.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 */
+ gtr.hr.ec = TALER_JSON_get_error_code (json);
+ gtr.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,
+ >r.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) gtr.hr.ec);
+ gtr.hr.http_status = 0;
+ break;
+ }
+ gth->cb (gth->cb_cls,
+ >r);
+ TALER_MERCHANT_transfers_get_cancel (gth);
+}
+
+
+struct TALER_MERCHANT_GetTransfersHandle *
+TALER_MERCHANT_transfers_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ struct TALER_FullPayto payto_uri,
+ const struct GNUNET_TIME_Timestamp before,
+ const struct GNUNET_TIME_Timestamp after,
+ int64_t limit,
+ uint64_t offset,
+ enum TALER_EXCHANGE_YesNoAll expected,
+ TALER_MERCHANT_GetTransfersCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_GetTransfersHandle *gth;
+ CURL *eh;
+ const char *expected_s = NULL;
+ char limit_s[30];
+ char offset_s[30];
+ char before_s[30];
+ char after_s[30];
+
+ gth = GNUNET_new (struct TALER_MERCHANT_GetTransfersHandle);
+ gth->ctx = ctx;
+ gth->cb = cb;
+ gth->cb_cls = cb_cls;
+ expected_s = TALER_yna_to_string (expected);
+ GNUNET_snprintf (limit_s,
+ sizeof (limit_s),
+ "%lld",
+ (long long) limit);
+ GNUNET_snprintf (offset_s,
+ sizeof (offset_s),
+ "%lld",
+ (unsigned long long) offset);
+ GNUNET_snprintf (before_s,
+ sizeof (before_s),
+ "%llu",
+ (unsigned long long) GNUNET_TIME_timestamp_to_s (before));
+ GNUNET_snprintf (after_s,
+ sizeof (after_s),
+ "%llu",
+ (unsigned long long) GNUNET_TIME_timestamp_to_s (after));
+ {
+ char *enc_payto = TALER_urlencode (payto_uri.full_payto);
+
+ gth->url = TALER_url_join (backend_url,
+ "private/transfers",
+ "payto_uri",
+ enc_payto,
+ "expected",
+ (TALER_EXCHANGE_YNA_ALL != expected)
+ ? expected_s
+ : NULL,
+ "limit",
+ 0 != limit
+ ? limit_s
+ : NULL,
+ "offset",
+ ((0 != offset) && (UINT64_MAX != offset))
+ ? offset_s
+ : NULL,
+ "before",
+ GNUNET_TIME_absolute_is_never (before.abs_time)
+ ? NULL
+ : before_s,
+ "after",
+ GNUNET_TIME_absolute_is_zero (after.abs_time)
+ ? NULL
+ : after_s,
+ NULL);
+ GNUNET_free (enc_payto);
+ }
+ if (NULL == gth->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (gth);
+ return NULL;
+ }
+ eh = TALER_MERCHANT_curl_easy_get_ (gth->url);
+ gth->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_transfers_get_finished,
+ gth);
+ return gth;
+}
+
+
+void
+TALER_MERCHANT_transfers_get_cancel (
+ struct TALER_MERCHANT_GetTransfersHandle *gth)
+{
+ if (NULL != gth->job)
+ {
+ GNUNET_CURL_job_cancel (gth->job);
+ gth->job = NULL;
+ }
+ GNUNET_free (gth->url);
+ GNUNET_free (gth);
+}
+
+
+/* end of merchant_api_get_transfers.c */
diff --git a/src/lib/merchant_api_get-private-units-UNIT.c b/src/lib/merchant_api_get-private-units-UNIT.c
@@ -0,0 +1,249 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_get-private-units-UNIT.c
+ * @brief Implementation of GET /private/units/$ID
+ * @author Bohdan Potuzhnyi
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /private/units/$ID operation.
+ */
+struct TALER_MERCHANT_UnitGetHandle
+{
+ /**
+ * Fully qualified request URL.
+ */
+ char *url;
+
+ /**
+ * In-flight job handle.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Callback to invoke with the response.
+ */
+ TALER_MERCHANT_UnitGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Parse the JSON response into @a ugr.
+ *
+ * @param json full JSON reply
+ * @param ugr response descriptor to populate
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_unit (const json_t *json,
+ struct TALER_MERCHANT_UnitGetResponse *ugr)
+{
+ struct TALER_MERCHANT_UnitEntry *entry = &ugr->details.ok.unit;
+ const char *unit;
+ const char *unit_name_long;
+ const char *unit_name_short;
+ const json_t *unit_name_long_i18n = NULL;
+ const json_t *unit_name_short_i18n = NULL;
+ bool unit_allow_fraction;
+ bool unit_active;
+ bool unit_builtin;
+ uint32_t unit_precision_level;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("unit",
+ &unit),
+ GNUNET_JSON_spec_string ("unit_name_long",
+ &unit_name_long),
+ GNUNET_JSON_spec_string ("unit_name_short",
+ &unit_name_short),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("unit_name_long_i18n",
+ &unit_name_long_i18n),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("unit_name_short_i18n",
+ &unit_name_short_i18n),
+ NULL),
+ GNUNET_JSON_spec_bool ("unit_allow_fraction",
+ &unit_allow_fraction),
+ GNUNET_JSON_spec_uint32 ("unit_precision_level",
+ &unit_precision_level),
+ GNUNET_JSON_spec_bool ("unit_active",
+ &unit_active),
+ GNUNET_JSON_spec_bool ("unit_builtin",
+ &unit_builtin),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_JSON_parse_free (spec);
+ entry->unit = unit;
+ entry->unit_name_long = unit_name_long;
+ entry->unit_name_short = unit_name_short;
+ entry->unit_name_long_i18n = unit_name_long_i18n;
+ entry->unit_name_short_i18n = unit_name_short_i18n;
+ entry->unit_allow_fraction = unit_allow_fraction;
+ entry->unit_precision_level = unit_precision_level;
+ entry->unit_active = unit_active;
+ entry->unit_builtin = unit_builtin;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Called once the HTTP request completes.
+ *
+ * @param cls operation handle
+ * @param response_code HTTP status (0 on client-side errors)
+ * @param response parsed JSON reply (NULL if parsing failed)
+ */
+static void
+handle_get_unit_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_UnitGetHandle *ugh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_UnitGetResponse ugr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ugh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GET /private/units/$ID finished with status %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ parse_unit (json,
+ &ugr))
+ {
+ ugr.hr.http_status = 0;
+ ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ ugh->cb (ugh->cb_cls,
+ &ugr);
+ TALER_MERCHANT_unit_get_cancel (ugh);
+ return;
+ case MHD_HTTP_UNAUTHORIZED:
+ case MHD_HTTP_FORBIDDEN:
+ case MHD_HTTP_NOT_FOUND:
+ ugr.hr.ec = TALER_JSON_get_error_code (json);
+ ugr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case 0:
+ ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ default:
+ ugr.hr.ec = TALER_JSON_get_error_code (json);
+ ugr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unexpected response %u/%d for GET /private/units/$ID\n",
+ (unsigned int) response_code,
+ (int) ugr.hr.ec);
+ break;
+ }
+ ugh->cb (ugh->cb_cls,
+ &ugr);
+ TALER_MERCHANT_unit_get_cancel (ugh);
+}
+
+
+struct TALER_MERCHANT_UnitGetHandle *
+TALER_MERCHANT_unit_get (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *unit_id,
+ TALER_MERCHANT_UnitGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_UnitGetHandle *ugh;
+ CURL *eh;
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/units/%s",
+ unit_id);
+ ugh = GNUNET_new (struct TALER_MERCHANT_UnitGetHandle);
+ ugh->ctx = ctx;
+ ugh->cb = cb;
+ ugh->cb_cls = cb_cls;
+ ugh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ if (NULL == ugh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to build /private/units/%s URL\n",
+ unit_id);
+ GNUNET_free (ugh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ ugh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (ugh->url);
+ ugh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_unit_finished,
+ ugh);
+ return ugh;
+}
+
+
+void
+TALER_MERCHANT_unit_get_cancel (struct TALER_MERCHANT_UnitGetHandle *ugh)
+{
+ if (NULL != ugh->job)
+ GNUNET_CURL_job_cancel (ugh->job);
+ GNUNET_free (ugh->url);
+ GNUNET_free (ugh);
+}
+
+
+/* end of merchant_api_get_unit.c */
diff --git a/src/lib/merchant_api_get-private-units.c b/src/lib/merchant_api_get-private-units.c
@@ -0,0 +1,329 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_get-private-units.c
+ * @brief Implementation of GET /private/units
+ * @author Bohdan Potuzhnyi
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum number of units returned in a single response.
+ */
+#define MAX_UNITS 1024
+
+
+/**
+ * Handle for a GET /private/units operation.
+ */
+struct TALER_MERCHANT_UnitsGetHandle
+{
+ /**
+ * Fully qualified request URL.
+ */
+ char *url;
+
+ /**
+ * In-flight job handle.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Callback to invoke with the outcome.
+ */
+ TALER_MERCHANT_UnitsGetCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Parse an individual unit entry from @a value.
+ *
+ * @param value JSON object describing the unit
+ * @param[out] ue set to the parsed values
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_unit_entry (const json_t *value,
+ struct TALER_MERCHANT_UnitEntry *ue)
+{
+ const char *unit;
+ const char *unit_name_long;
+ const char *unit_name_short;
+ const json_t *unit_name_long_i18n = NULL;
+ const json_t *unit_name_short_i18n = NULL;
+ bool unit_allow_fraction;
+ bool unit_active;
+ bool unit_builtin;
+ uint32_t unit_precision_level;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("unit",
+ &unit),
+ GNUNET_JSON_spec_string ("unit_name_long",
+ &unit_name_long),
+ GNUNET_JSON_spec_string ("unit_name_short",
+ &unit_name_short),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("unit_name_long_i18n",
+ &unit_name_long_i18n),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("unit_name_short_i18n",
+ &unit_name_short_i18n),
+ NULL),
+ GNUNET_JSON_spec_bool ("unit_allow_fraction",
+ &unit_allow_fraction),
+ GNUNET_JSON_spec_uint32 ("unit_precision_level",
+ &unit_precision_level),
+ GNUNET_JSON_spec_bool ("unit_active",
+ &unit_active),
+ GNUNET_JSON_spec_bool ("unit_builtin",
+ &unit_builtin),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_JSON_parse_free (spec);
+ ue->unit = unit;
+ ue->unit_name_long = unit_name_long;
+ ue->unit_name_short = unit_name_short;
+ ue->unit_name_long_i18n = unit_name_long_i18n;
+ ue->unit_name_short_i18n = unit_name_short_i18n;
+ ue->unit_allow_fraction = unit_allow_fraction;
+ ue->unit_precision_level = unit_precision_level;
+ ue->unit_active = unit_active;
+ ue->unit_builtin = unit_builtin;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse the list of units from @a units and call the callback.
+ *
+ * @param json complete response JSON
+ * @param units array of units
+ * @param ugh ongoing operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_units (const json_t *json,
+ const json_t *units,
+ struct TALER_MERCHANT_UnitsGetHandle *ugh)
+{
+ size_t len;
+
+ len = json_array_size (units);
+ if (len > MAX_UNITS)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ {
+ struct TALER_MERCHANT_UnitEntry entries[GNUNET_NZL (len)];
+ size_t idx;
+ json_t *value;
+
+ json_array_foreach (units, idx, value) {
+ if (GNUNET_OK !=
+ parse_unit_entry (value,
+ &entries[idx]))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ {
+ struct TALER_MERCHANT_UnitsGetResponse ugr = {
+ .hr.http_status = MHD_HTTP_OK,
+ .hr.reply = json,
+ .details = {
+ .ok = {
+ .units = entries,
+ .units_length = (unsigned int) len
+ }
+
+
+ }
+
+
+ };
+
+ ugh->cb (ugh->cb_cls,
+ &ugr);
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Called when the HTTP transfer finishes.
+ *
+ * @param cls closure, the operation handle
+ * @param response_code HTTP status (0 on network errors)
+ * @param response parsed JSON body (NULL if parsing failed)
+ */
+static void
+handle_get_units_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_UnitsGetHandle *ugh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_UnitsGetResponse ugr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ugh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GET /private/units finished with status %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *units;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("units",
+ &units),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ ugr.hr.http_status = 0;
+ ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_units (json,
+ units,
+ ugh))
+ {
+ TALER_MERCHANT_units_get_cancel (ugh);
+ return;
+ }
+ ugr.hr.http_status = 0;
+ ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ case MHD_HTTP_FORBIDDEN:
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_CONFLICT:
+ ugr.hr.ec = TALER_JSON_get_error_code (json);
+ ugr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case 0:
+ ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ default:
+ ugr.hr.ec = TALER_JSON_get_error_code (json);
+ ugr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unexpected response code %u/%d for GET /private/units\n",
+ (unsigned int) response_code,
+ (int) ugr.hr.ec);
+ break;
+ }
+ ugh->cb (ugh->cb_cls,
+ &ugr);
+ TALER_MERCHANT_units_get_cancel (ugh);
+}
+
+
+struct TALER_MERCHANT_UnitsGetHandle *
+TALER_MERCHANT_units_get (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_UnitsGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_UnitsGetHandle *ugh;
+ CURL *eh;
+
+ ugh = GNUNET_new (struct TALER_MERCHANT_UnitsGetHandle);
+ ugh->ctx = ctx;
+ ugh->cb = cb;
+ ugh->cb_cls = cb_cls;
+ ugh->url = TALER_url_join (backend_url,
+ "private/units",
+ NULL);
+ if (NULL == ugh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to build /private/units URL\n");
+ GNUNET_free (ugh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ ugh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (ugh->url);
+ ugh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_units_finished,
+ ugh);
+ return ugh;
+}
+
+
+void
+TALER_MERCHANT_units_get_cancel (struct TALER_MERCHANT_UnitsGetHandle *ugh)
+{
+ if (NULL != ugh->job)
+ GNUNET_CURL_job_cancel (ugh->job);
+ GNUNET_free (ugh->url);
+ GNUNET_free (ugh);
+}
+
+
+/* end of merchant_api_get_units.c */
diff --git a/src/lib/merchant_api_get-private-webhooks-WEBHOOK_ID.c b/src/lib/merchant_api_get-private-webhooks-WEBHOOK_ID.c
@@ -0,0 +1,221 @@
+/*
+ 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_get-private-webhooks-WEBHOOK_ID.c
+ * @brief Implementation of the GET /webhooks/$ID request of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/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/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 /webhooks/$ID operation.
+ */
+struct TALER_MERCHANT_WebhookGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_WebhookGetCallback 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 /webhooks/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_WebhookGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_webhook_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_WebhookGetHandle *wgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ wgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /webhooks/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const char *event_type;
+ const char *url;
+ const char *http_method;
+ const char *header_template;
+ const char *body_template;
+ bool rst_ok = true;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("event_type",
+ &event_type),
+ TALER_JSON_spec_web_url ("url",
+ &url),
+ GNUNET_JSON_spec_string ("http_method",
+ &http_method),
+ GNUNET_JSON_spec_string ("header_template",
+ &header_template),
+ GNUNET_JSON_spec_string ("body_template",
+ &body_template),
+ GNUNET_JSON_spec_end ()
+ };
+
+
+ if ( (rst_ok) &&
+ (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL)) )
+ {
+ wgh->cb (wgh->cb_cls,
+ &hr,
+ event_type,
+ url,
+ http_method,
+ header_template,
+ body_template);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_webhook_get_cancel (wgh);
+ return;
+ }
+ hr.http_status = 0;
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ GNUNET_JSON_parse_free (spec);
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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) hr.ec);
+ break;
+ }
+ wgh->cb (wgh->cb_cls,
+ &hr,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ TALER_MERCHANT_webhook_get_cancel (wgh);
+}
+
+
+struct TALER_MERCHANT_WebhookGetHandle *
+TALER_MERCHANT_webhook_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *webhook_id,
+ TALER_MERCHANT_WebhookGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_WebhookGetHandle *wgh;
+ CURL *eh;
+
+ wgh = GNUNET_new (struct TALER_MERCHANT_WebhookGetHandle);
+ wgh->ctx = ctx;
+ wgh->cb = cb;
+ wgh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/webhooks/%s",
+ webhook_id);
+ wgh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == wgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (wgh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ wgh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (wgh->url);
+ wgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_webhook_finished,
+ wgh);
+ return wgh;
+}
+
+
+void
+TALER_MERCHANT_webhook_get_cancel (
+ struct TALER_MERCHANT_WebhookGetHandle *wgh)
+{
+ if (NULL != wgh->job)
+ GNUNET_CURL_job_cancel (wgh->job);
+ GNUNET_free (wgh->url);
+ GNUNET_free (wgh);
+}
diff --git a/src/lib/merchant_api_get-private-webhooks.c b/src/lib/merchant_api_get-private-webhooks.c
@@ -0,0 +1,246 @@
+/*
+ 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_get-private-webhooks.c
+ * @brief Implementation of the GET /webhooks request of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Maximum number of webhooks we return.
+ */
+#define MAX_WEBHOOKS 1024
+
+/**
+ * Handle for a GET /webhooks operation.
+ */
+struct TALER_MERCHANT_WebhooksGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_WebhooksGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Parse webhook information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with webhook data
+ * @param[in] wgr partially filled webhook response
+ * @param wgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_webhooks (const json_t *ia,
+ struct TALER_MERCHANT_WebhooksGetResponse *wgr,
+ struct TALER_MERCHANT_WebhooksGetHandle *wgh)
+{
+ unsigned int whook_len = (unsigned int) json_array_size (ia);
+
+ if ( (json_array_size (ia) != (size_t) whook_len) ||
+ (whook_len > MAX_WEBHOOKS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_WebhookEntry whook[GNUNET_NZL (whook_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_WebhookEntry *ie = &whook[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("webhook_id",
+ &ie->webhook_id),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ wgr->details.ok.webhooks_length = whook_len;
+ wgr->details.ok.webhooks = whook;
+ wgh->cb (wgh->cb_cls,
+ wgr);
+ wgh->cb = NULL; /* just to be sure */
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /webhooks request.
+ *
+ * @param cls the `struct TALER_MERCHANT_WebhooksGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_webhooks_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_WebhooksGetHandle *wgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_WebhooksGetResponse wgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ wgh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /webhooks response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *webhooks;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("webhooks",
+ &webhooks),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ wgr.hr.http_status = 0;
+ wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_webhooks (webhooks,
+ &wgr,
+ wgh))
+ {
+ TALER_MERCHANT_webhooks_get_cancel (wgh);
+ return;
+ }
+ wgr.hr.http_status = 0;
+ wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ wgr.hr.ec = TALER_JSON_get_error_code (json);
+ wgr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ default:
+ /* unexpected response code */
+ wgr.hr.ec = TALER_JSON_get_error_code (json);
+ wgr.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) wgr.hr.ec);
+ break;
+ }
+ wgh->cb (wgh->cb_cls,
+ &wgr);
+ TALER_MERCHANT_webhooks_get_cancel (wgh);
+}
+
+
+struct TALER_MERCHANT_WebhooksGetHandle *
+TALER_MERCHANT_webhooks_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_WebhooksGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_WebhooksGetHandle *wgh;
+ CURL *eh;
+
+ wgh = GNUNET_new (struct TALER_MERCHANT_WebhooksGetHandle);
+ wgh->ctx = ctx;
+ wgh->cb = cb;
+ wgh->cb_cls = cb_cls;
+ wgh->url = TALER_url_join (backend_url,
+ "private/webhooks",
+ NULL);
+ if (NULL == wgh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (wgh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ wgh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (wgh->url);
+ wgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_webhooks_finished,
+ wgh);
+ return wgh;
+}
+
+
+void
+TALER_MERCHANT_webhooks_get_cancel (
+ struct TALER_MERCHANT_WebhooksGetHandle *wgh)
+{
+ if (NULL != wgh->job)
+ GNUNET_CURL_job_cancel (wgh->job);
+ GNUNET_free (wgh->url);
+ GNUNET_free (wgh);
+}
diff --git a/src/lib/merchant_api_get-products-IMAGE_HASH-image.c b/src/lib/merchant_api_get-products-IMAGE_HASH-image.c
@@ -0,0 +1,188 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_get-products-IMAGE_HASH-image.c
+ * @brief Implementation of the GET /products/$HASH/image request
+ * @author Bohdan Potuzhnyi
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include <taler/taler_error_codes.h>
+#include <taler/taler_json_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+
+
+/**
+ * Handle for a GET /products/$HASH/image operation.
+ */
+struct TALER_MERCHANT_ProductImageGetHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ProductImageGetCallback 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 /products/$HASH/image request.
+ *
+ * @param cls the `struct TALER_MERCHANT_ProductImageGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_product_image_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_ProductImageGetHandle *pigh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_ProductImageGetResponse pir = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pigh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /products/$HASH/image response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("image",
+ &pir.details.ok.image),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ pigh->cb (pigh->cb_cls,
+ &pir);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_product_image_get_cancel (pigh);
+ return;
+ }
+ pir.hr.http_status = 0;
+ pir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_BAD_REQUEST:
+ pir.hr.ec = TALER_JSON_get_error_code (json);
+ pir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ pir.hr.ec = TALER_JSON_get_error_code (json);
+ pir.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) pir.hr.ec);
+ break;
+ }
+ pigh->cb (pigh->cb_cls,
+ &pir);
+ TALER_MERCHANT_product_image_get_cancel (pigh);
+}
+
+
+struct TALER_MERCHANT_ProductImageGetHandle *
+TALER_MERCHANT_product_image_get (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *image_hash,
+ TALER_MERCHANT_ProductImageGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_ProductImageGetHandle *pigh;
+ CURL *eh;
+
+ pigh = GNUNET_new (struct TALER_MERCHANT_ProductImageGetHandle);
+ pigh->ctx = ctx;
+ pigh->cb = cb;
+ pigh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "products/%s/image",
+ image_hash);
+ pigh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pigh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (pigh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ pigh->url);
+ eh = TALER_MERCHANT_curl_easy_get_ (pigh->url);
+ pigh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_get_product_image_finished,
+ pigh);
+ return pigh;
+}
+
+
+void
+TALER_MERCHANT_product_image_get_cancel (
+ struct TALER_MERCHANT_ProductImageGetHandle *pigh)
+{
+ if (NULL != pigh->job)
+ GNUNET_CURL_job_cancel (pigh->job);
+ GNUNET_free (pigh->url);
+ GNUNET_free (pigh);
+}
+\ No newline at end of file
diff --git a/src/lib/merchant_api_get-templates-TEMPLATE_ID.c b/src/lib/merchant_api_get-templates-TEMPLATE_ID.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_get-templates-TEMPLATE_ID.c
+ * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/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/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/lib/merchant_api_get_account.c b/src/lib/merchant_api_get_account.c
@@ -1,211 +0,0 @@
-/*
- 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_get_account.c
- * @brief Implementation of the GET /accounts/$ID request of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/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/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 /accounts/$ID operation.
- */
-struct TALER_MERCHANT_AccountGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_AccountGetCallback 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 /accounts/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_AccountGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_account_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_AccountGetHandle *tgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_AccountGetResponse tgr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- tgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /accounts/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("salt",
- &tgr.details.ok.ad.salt),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_web_url ("credit_facade_url",
- &tgr.details.ok.ad.credit_facade_url),
- NULL),
- TALER_JSON_spec_full_payto_uri ("payto_uri",
- &tgr.details.ok.ad.payto_uri),
- GNUNET_JSON_spec_fixed_auto ("h_wire",
- &tgr.details.ok.ad.h_wire),
- GNUNET_JSON_spec_bool ("active",
- &tgr.details.ok.ad.active),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK ==
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- tgh->cb (tgh->cb_cls,
- &tgr);
- TALER_MERCHANT_account_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_account_get_cancel (tgh);
-}
-
-
-struct TALER_MERCHANT_AccountGetHandle *
-TALER_MERCHANT_account_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- const struct TALER_MerchantWireHashP *h_wire,
- TALER_MERCHANT_AccountGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_AccountGetHandle *tgh;
- CURL *eh;
-
- tgh = GNUNET_new (struct TALER_MERCHANT_AccountGetHandle);
- tgh->ctx = ctx;
- tgh->cb = cb;
- tgh->cb_cls = cb_cls;
- {
- char w_str[sizeof (*h_wire) * 2];
- char *path;
- char *end;
-
- end = GNUNET_STRINGS_data_to_string (h_wire,
- sizeof (*h_wire),
- w_str,
- sizeof (w_str));
- *end = '\0';
- GNUNET_asprintf (&path,
- "private/accounts/%s",
- w_str);
- 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_account_finished,
- tgh);
- return tgh;
-}
-
-
-void
-TALER_MERCHANT_account_get_cancel (
- struct TALER_MERCHANT_AccountGetHandle *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_accounts.c b/src/lib/merchant_api_get_accounts.c
@@ -1,247 +0,0 @@
-/*
- 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_get_accounts.c
- * @brief Implementation of the GET /accounts request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-/**
- * Maximum number of accounts permitted.
- */
-#define MAX_ACCOUNTS 1024
-
-/**
- * Handle for a GET /accounts operation.
- */
-struct TALER_MERCHANT_AccountsGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_AccountsGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse account information from @a ia.
- *
- * @param ia JSON array (or NULL!) with account data
- * @param[in] tgr partially filled response
- * @param tgh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_accounts (const json_t *ia,
- struct TALER_MERCHANT_AccountsGetResponse *tgr,
- struct TALER_MERCHANT_AccountsGetHandle *tgh)
-{
- unsigned int tmpl_len = (unsigned int) json_array_size (ia);
-
- if ( (json_array_size (ia) != (size_t) tmpl_len) ||
- (tmpl_len > MAX_ACCOUNTS) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_AccountEntry tmpl[GNUNET_NZL (tmpl_len)];
- size_t index;
- json_t *value;
-
- json_array_foreach (ia, index, value) {
- struct TALER_MERCHANT_AccountEntry *ie = &tmpl[index];
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_full_payto_uri ("payto_uri",
- &ie->payto_uri),
- GNUNET_JSON_spec_fixed_auto ("h_wire",
- &ie->h_wire),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- tgr->details.ok.accounts_length = tmpl_len;
- tgr->details.ok.accounts = tmpl;
- 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 /accounts request.
- *
- * @param cls the `struct TALER_MERCHANT_AccountsGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_accounts_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_AccountsGetHandle *tgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_AccountsGetResponse tgr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- tgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /accounts response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *accounts;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("accounts",
- &accounts),
- 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_accounts (accounts,
- &tgr,
- tgh))
- {
- TALER_MERCHANT_accounts_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_accounts_get_cancel (tgh);
-}
-
-
-struct TALER_MERCHANT_AccountsGetHandle *
-TALER_MERCHANT_accounts_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_AccountsGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_AccountsGetHandle *tgh;
- CURL *eh;
-
- tgh = GNUNET_new (struct TALER_MERCHANT_AccountsGetHandle);
- tgh->ctx = ctx;
- tgh->cb = cb;
- tgh->cb_cls = cb_cls;
- tgh->url = TALER_url_join (backend_url,
- "private/accounts",
- 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_accounts_finished,
- tgh);
- return tgh;
-}
-
-
-void
-TALER_MERCHANT_accounts_get_cancel (
- struct TALER_MERCHANT_AccountsGetHandle *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_config.c b/src/lib/merchant_api_get_config.c
@@ -1,320 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2026 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_get_config.c
- * @brief Implementation of the /config request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-/**
- * Which version of the Taler protocol is implemented
- * by this library? Used to determine compatibility.
- */
-#define MERCHANT_PROTOCOL_CURRENT 27
-
-/**
- * How many configs are we backwards-compatible with?
- */
-#define MERCHANT_PROTOCOL_AGE 3
-
-/**
- * How many exchanges do we allow at most per merchant?
- */
-#define MAX_EXCHANGES 1024
-
-/**
- * How many currency specs do we allow at most per merchant?
- */
-#define MAX_CURRENCIES 1024
-
-/**
- * @brief A handle for /config operations
- */
-struct TALER_MERCHANT_ConfigGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ConfigCallback 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 /config request.
- *
- * @param cls the `struct TALER_MERCHANT_ConfigGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_config_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_ConfigGetHandle *vgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_ConfigResponse cr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /config response with status code %u\n",
- (unsigned int) response_code);
-
- vgh->job = NULL;
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *jcs;
- const json_t *exchanges = NULL;
- struct TALER_MERCHANT_ExchangeConfigInfo *eci = NULL;
- unsigned int num_eci = 0;
- unsigned int nspec;
- struct TALER_JSON_ProtocolVersion pv;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_object_const ("currencies",
- &jcs),
- GNUNET_JSON_spec_array_const ("exchanges",
- &exchanges),
- GNUNET_JSON_spec_string ("currency",
- &cr.details.ok.ci.currency),
- TALER_JSON_spec_version ("version",
- &pv),
- GNUNET_JSON_spec_string ("version",
- &cr.details.ok.ci.version),
- GNUNET_JSON_spec_end ()
- };
-
- cr.details.ok.compat = TALER_MERCHANT_VC_PROTOCOL_ERROR;
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- cr.hr.http_status = 0;
- cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- cr.details.ok.compat = TALER_MERCHANT_VC_MATCH;
- if (MERCHANT_PROTOCOL_CURRENT < pv.current)
- {
- cr.details.ok.compat |= TALER_MERCHANT_VC_NEWER;
- if (MERCHANT_PROTOCOL_CURRENT < pv.current - pv.age)
- cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE;
- }
- if (MERCHANT_PROTOCOL_CURRENT > pv.current)
- {
- cr.details.ok.compat |= TALER_MERCHANT_VC_OLDER;
- if (MERCHANT_PROTOCOL_CURRENT - MERCHANT_PROTOCOL_AGE > pv.current)
- cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE;
- }
-
- nspec = (unsigned int) json_object_size (jcs);
- if ( (nspec > MAX_CURRENCIES) ||
- (json_object_size (jcs) != (size_t) nspec) )
- {
- GNUNET_break_op (0);
- cr.hr.http_status = 0;
- cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- if (NULL != exchanges)
- {
- num_eci = (unsigned int) json_object_size (exchanges);
- if ( (num_eci > MAX_EXCHANGES) ||
- (json_object_size (exchanges) != (size_t) num_eci) )
- {
- GNUNET_break_op (0);
- cr.hr.http_status = 0;
- cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- eci = GNUNET_new_array (num_eci,
- struct TALER_MERCHANT_ExchangeConfigInfo);
- for (unsigned int i = 0; i<num_eci; i++)
- {
- struct TALER_MERCHANT_ExchangeConfigInfo *ei = &eci[i];
- const json_t *ej = json_array_get (exchanges,
- i);
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_string ("currency",
- &ei->currency),
- GNUNET_JSON_spec_string ("base_url",
- &ei->base_url),
- GNUNET_JSON_spec_fixed_auto ("master_pub",
- &ei->master_pub),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (ej,
- ispec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- cr.hr.http_status = 0;
- cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- GNUNET_free (eci);
- break;
- }
- }
- }
- {
- struct TALER_CurrencySpecification *cspecs;
- unsigned int off = 0;
- json_t *obj;
- const char *curr;
-
- cspecs = GNUNET_new_array (nspec,
- struct TALER_CurrencySpecification);
- cr.details.ok.num_cspecs = nspec;
- cr.details.ok.cspecs = cspecs;
- cr.details.ok.num_exchanges = (unsigned int) num_eci;
- cr.details.ok.exchanges = eci;
- json_object_foreach ((json_t *) jcs, curr, obj)
- {
- struct TALER_CurrencySpecification *cs = &cspecs[off++];
- struct GNUNET_JSON_Specification cspec[] = {
- TALER_JSON_spec_currency_specification (curr,
- curr,
- cs),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (jcs,
- cspec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- cr.hr.http_status = 0;
- cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- GNUNET_free (eci);
- TALER_CONFIG_free_currencies (off - 1,
- cspecs);
- break;
- }
- }
- vgh->cb (vgh->cb_cls,
- &cr);
- GNUNET_free (eci);
- TALER_CONFIG_free_currencies (nspec,
- cspecs);
- }
- TALER_MERCHANT_config_get_cancel (vgh);
- return;
- }
- default:
- /* unexpected response code */
- cr.hr.ec = TALER_JSON_get_error_code (json);
- cr.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) cr.hr.ec);
- break;
- }
- vgh->cb (vgh->cb_cls,
- &cr);
- TALER_MERCHANT_config_get_cancel (vgh);
-}
-
-
-struct TALER_MERCHANT_ConfigGetHandle *
-TALER_MERCHANT_config_get (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_ConfigCallback config_cb,
- void *config_cb_cls)
-{
- struct TALER_MERCHANT_ConfigGetHandle *vgh;
- CURL *eh;
-
- vgh = GNUNET_new (struct TALER_MERCHANT_ConfigGetHandle);
- vgh->ctx = ctx;
- vgh->cb = config_cb;
- vgh->cb_cls = config_cb_cls;
- vgh->url = TALER_url_join (backend_url,
- "config",
- NULL);
- if (NULL == vgh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (vgh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- vgh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (vgh->url);
- vgh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_config_finished,
- vgh);
- return vgh;
-}
-
-
-void
-TALER_MERCHANT_config_get_cancel (struct TALER_MERCHANT_ConfigGetHandle *vgh)
-{
- if (NULL != vgh->job)
- {
- GNUNET_CURL_job_cancel (vgh->job);
- vgh->job = NULL;
- }
- GNUNET_free (vgh->url);
- GNUNET_free (vgh);
-}
-
-
-/* end of merchant_api_config_get.c */
diff --git a/src/lib/merchant_api_get_donau_instance.c b/src/lib/merchant_api_get_donau_instance.c
@@ -1,299 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2024 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_get_donau_instance.c
- * @brief Implementation of the GET /donau request of the merchant's HTTP API
- * @author Bohdan Potuzhnyi
- * @author Vlada Svirsh
- */
-
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-/* DONAU RELATED IMPORTS */
-#include "taler/taler_merchant_donau.h"
-#include <donau/donau_service.h>
-
-/**
- * Handle for a GET /donau operation.
- */
-struct TALER_MERCHANT_DonauInstanceGetHandle
-{
- /**
- * The URL for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_DonauInstanceGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-};
-
-/**
- * Parse Donau instance information from @a ia.
- *
- * @param ia JSON array (or NULL!) with Donau instance data
- * @param igr response to fill
- * @param dgh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_donau_instances (const json_t *ia,
- struct TALER_MERCHANT_DonauInstanceGetResponse *igr,
- struct TALER_MERCHANT_DonauInstanceGetHandle *dgh)
-{
- unsigned int instances_len = (unsigned int) json_array_size (ia);
- struct TALER_MERCHANT_DonauInstanceEntry
- instances[GNUNET_NZL (instances_len)];
- size_t index;
- json_t *value;
- struct DONAU_Keys *donau_keys_ptr = NULL;
-
- if ((json_array_size (ia) != (size_t) instances_len))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
- json_array_foreach (ia,
- index,
- value)
- {
- struct TALER_MERCHANT_DonauInstanceEntry *instance = &instances[index];
- const json_t *donau_keys_json = NULL;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_uint64 ("donau_instance_serial",
- &instance->donau_instance_serial),
- GNUNET_JSON_spec_string ("donau_url",
- &instance->donau_url),
- GNUNET_JSON_spec_string ("charity_name",
- &instance->charity_name),
- GNUNET_JSON_spec_fixed_auto ("charity_pub_key",
- &instance->charity_pub_key),
- GNUNET_JSON_spec_uint64 ("charity_id",
- &instance->charity_id),
- TALER_JSON_spec_amount_any ("charity_max_per_year",
- &instance->charity_max_per_year),
- TALER_JSON_spec_amount_any ("charity_receipts_to_date",
- &instance->charity_receipts_to_date),
- GNUNET_JSON_spec_int64 ("current_year",
- &instance->current_year),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const ("donau_keys_json",
- &donau_keys_json),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- /* Parse the Donau keys */
- if (NULL != donau_keys_json)
- {
- donau_keys_ptr = DONAU_keys_from_json (donau_keys_json);
- if (NULL == donau_keys_ptr)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to convert donau keys from JSON\n");
- return GNUNET_SYSERR;
- }
- instance->donau_keys = donau_keys_ptr;
- }
- }
-
- igr->details.ok.donau_instances_length = instances_len;
- igr->details.ok.donau_instances = instances;
- dgh->cb (dgh->cb_cls,
- igr);
- dgh->cb = NULL;
- if (NULL != donau_keys_ptr)
- {
- DONAU_keys_decref (donau_keys_ptr);
- donau_keys_ptr= NULL;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /donau request.
- *
- * @param cls the `struct TALER_MERCHANT_DonauInstanceGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_donau_instances_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_DonauInstanceGetHandle *dgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_DonauInstanceGetResponse igr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- dgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /donau response with status code %u\n",
- (unsigned int) response_code);
-
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *donau_instances;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("donau_instances",
- &donau_instances),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL,
- NULL))
- {
- igr.hr.http_status = 0;
- igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
-
- if (GNUNET_OK ==
- parse_donau_instances (donau_instances,
- &igr,
- dgh))
- {
- TALER_MERCHANT_donau_instances_get_cancel (dgh);
- return;
- }
-
- igr.hr.http_status = 0;
- igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
-
- case MHD_HTTP_UNAUTHORIZED:
- case MHD_HTTP_NOT_FOUND:
- default:
- igr.hr.ec = TALER_JSON_get_error_code (json);
- igr.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) igr.hr.ec);
- break;
- }
-
- dgh->cb (dgh->cb_cls,
- &igr);
- TALER_MERCHANT_donau_instances_get_cancel (dgh);
-}
-
-
-/**
- * Initiate the GET /donau request.
- *
- * @param ctx CURL context
- * @param backend_url base URL for the backend
- * @param cb callback function to handle the response
- * @param cb_cls closure for the callback function
- * @return the handle for the operation, or NULL on error
- */
-struct TALER_MERCHANT_DonauInstanceGetHandle *
-TALER_MERCHANT_donau_instances_get (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_DonauInstanceGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_DonauInstanceGetHandle *dgh;
- CURL *eh;
-
- dgh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceGetHandle);
- dgh->ctx = ctx;
- dgh->cb = cb;
- dgh->cb_cls = cb_cls;
- dgh->url = TALER_url_join (backend_url,
- "private/donau",
- NULL);
- if (NULL == dgh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (dgh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- dgh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (dgh->url);
- dgh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_donau_instances_finished,
- dgh);
-
- return dgh;
-}
-
-
-/**
- * Cancel the GET /donau instances operation.
- *
- * @param dgh request to cancel.
- */
-void
-TALER_MERCHANT_donau_instances_get_cancel (
- struct TALER_MERCHANT_DonauInstanceGetHandle *dgh)
-{
- if (NULL != dgh->job)
- GNUNET_CURL_job_cancel (dgh->job);
- GNUNET_free (dgh->url);
- GNUNET_free (dgh);
-}
diff --git a/src/lib/merchant_api_get_instance.c b/src/lib/merchant_api_get_instance.c
@@ -1,235 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_get_instance.c
- * @brief Implementation of the GET /instance/$ID request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_kyclogic_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Handle for a GET /instances/$ID operation.
- */
-struct TALER_MERCHANT_InstanceGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_InstanceGetCallback 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 /instances/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_InstanceGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_instance_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_InstanceGetHandle *igh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_InstanceGetResponse igr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- igh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /instances/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *address;
- const json_t *jurisdiction;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string (
- "name",
- &igr.details.ok.details.name),
- GNUNET_JSON_spec_fixed_auto (
- "merchant_pub",
- &igr.details.ok.details.merchant_pub),
- GNUNET_JSON_spec_object_const (
- "address",
- &address),
- GNUNET_JSON_spec_object_const (
- "jurisdiction",
- &jurisdiction),
- GNUNET_JSON_spec_bool (
- "use_stefan",
- &igr.details.ok.details.use_stefan),
- GNUNET_JSON_spec_relative_time (
- "default_wire_transfer_delay",
- &igr.details.ok.details.default_wire_transfer_delay),
- GNUNET_JSON_spec_relative_time (
- "default_pay_delay",
- &igr.details.ok.details.default_pay_delay),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_relative_time (
- "default_refund_delay",
- &igr.details.ok.details.default_refund_delay),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_time_rounder_interval (
- "default_wire_transfer_rounding_interval",
- &igr.details.ok.details.default_wire_transfer_rounding_interval),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- igr.hr.http_status = 0;
- igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- igr.details.ok.details.address = address;
- igr.details.ok.details.jurisdiction = jurisdiction;
- igh->cb (igh->cb_cls,
- &igr);
- TALER_MERCHANT_instance_get_cancel (igh);
- return;
- }
- case MHD_HTTP_UNAUTHORIZED:
- igr.hr.ec = TALER_JSON_get_error_code (json);
- igr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- /* instance does not exist */
- igr.hr.ec = TALER_JSON_get_error_code (json);
- igr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- igr.hr.ec = TALER_JSON_get_error_code (json);
- igr.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) igr.hr.ec);
- break;
- }
- igh->cb (igh->cb_cls,
- &igr);
- TALER_MERCHANT_instance_get_cancel (igh);
-}
-
-
-struct TALER_MERCHANT_InstanceGetHandle *
-TALER_MERCHANT_instance_get (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- TALER_MERCHANT_InstanceGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_InstanceGetHandle *igh;
- CURL *eh;
-
- igh = GNUNET_new (struct TALER_MERCHANT_InstanceGetHandle);
- igh->ctx = ctx;
- igh->cb = cb;
- igh->cb_cls = cb_cls;
- if (NULL != instance_id)
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "instances/%s/private",
- instance_id);
- igh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- else
- {
- igh->url = TALER_url_join (backend_url,
- "private",
- NULL);
- }
- if (NULL == igh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (igh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- igh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (igh->url);
- igh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_instance_finished,
- igh);
- return igh;
-}
-
-
-void
-TALER_MERCHANT_instance_get_cancel (
- struct TALER_MERCHANT_InstanceGetHandle *igh)
-{
- if (NULL != igh->job)
- GNUNET_CURL_job_cancel (igh->job);
- GNUNET_free (igh->url);
- GNUNET_free (igh);
-}
diff --git a/src/lib/merchant_api_get_instances.c b/src/lib/merchant_api_get_instances.c
@@ -1,263 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_get_instances.c
- * @brief Implementation of the GET /instances request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Maximum number of instances permitted.
- */
-#define MAX_INSTANCES 1024
-
-/**
- * Handle for a GET /instances operation.
- */
-struct TALER_MERCHANT_InstancesGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_InstancesGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse instance information from @a ia.
- *
- * @param json overall reply body
- * @param ia JSON array (or NULL!) with instance data
- * @param igh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_instances (const json_t *json,
- const json_t *ia,
- struct TALER_MERCHANT_InstancesGetHandle *igh)
-{
- unsigned int iis_len = (unsigned int) json_array_size (ia);
-
- if ( (json_array_size (ia) != (size_t) iis_len) ||
- (iis_len > MAX_INSTANCES) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_InstanceInformation iis[GNUNET_NZL (iis_len)];
- size_t index;
- json_t *value;
- struct TALER_MERCHANT_InstancesGetResponse igr = {
- .hr.http_status = MHD_HTTP_OK,
- .hr.reply = json,
- .details.ok.iis_length = iis_len,
- .details.ok.iis = iis
- };
-
- json_array_foreach (ia, index, value) {
- struct TALER_MERCHANT_InstanceInformation *ii = &iis[index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("name",
- &ii->name),
- GNUNET_JSON_spec_string ("id",
- &ii->id),
- GNUNET_JSON_spec_fixed_auto ("merchant_pub",
- &ii->merchant_pub),
- GNUNET_JSON_spec_array_const ("payment_targets",
- &ii->payment_targets),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- for (size_t i = 0; i<json_array_size (ii->payment_targets); i++)
- {
- if (! json_is_string (json_array_get (ii->payment_targets,
- i)))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- } /* for all instances */
- igh->cb (igh->cb_cls,
- &igr);
- igh->cb = NULL; /* just to be sure */
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /instances request.
- *
- * @param cls the `struct TALER_MERCHANT_InstancesGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_instances_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_InstancesGetHandle *igh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_InstancesGetResponse igr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- igh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /instances response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *instances;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("instances",
- &instances),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- igr.hr.http_status = 0;
- igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- if (GNUNET_OK ==
- parse_instances (json,
- instances,
- igh))
- {
- TALER_MERCHANT_instances_get_cancel (igh);
- return;
- }
- igr.hr.http_status = 0;
- igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- igr.hr.ec = TALER_JSON_get_error_code (json);
- igr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- igr.hr.ec = TALER_JSON_get_error_code (json);
- igr.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) igr.hr.ec);
- break;
- }
- igh->cb (igh->cb_cls,
- &igr);
- TALER_MERCHANT_instances_get_cancel (igh);
-}
-
-
-struct TALER_MERCHANT_InstancesGetHandle *
-TALER_MERCHANT_instances_get (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_InstancesGetCallback instances_cb,
- void *instances_cb_cls)
-{
- struct TALER_MERCHANT_InstancesGetHandle *igh;
- CURL *eh;
-
- igh = GNUNET_new (struct TALER_MERCHANT_InstancesGetHandle);
- igh->ctx = ctx;
- igh->cb = instances_cb;
- igh->cb_cls = instances_cb_cls;
- igh->url = TALER_url_join (backend_url,
- "management/instances",
- NULL);
- if (NULL == igh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (igh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- igh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (igh->url);
- igh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_instances_finished,
- igh);
- return igh;
-}
-
-
-void
-TALER_MERCHANT_instances_get_cancel (
- struct TALER_MERCHANT_InstancesGetHandle *igh)
-{
- if (NULL != igh->job)
- GNUNET_CURL_job_cancel (igh->job);
- GNUNET_free (igh->url);
- GNUNET_free (igh);
-}
diff --git a/src/lib/merchant_api_get_kyc.c b/src/lib/merchant_api_get_kyc.c
@@ -1,515 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2023--2024 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_get_kyc.c
- * @brief Implementation of the GET /kyc request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Maximum length of the KYC arrays supported.
- */
-#define MAX_KYC 1024
-
-/**
- * Handle for a GET /kyc operation.
- */
-struct TALER_MERCHANT_KycGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_KycGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse @a kyc response and call the continuation on success.
- *
- * @param kyc operation handle
- * @param[in,out] kr response details
- * @param jkyc array from the reply
- * @return #GNUNET_OK on success (callback was called)
- */
-static enum GNUNET_GenericReturnValue
-parse_kyc (struct TALER_MERCHANT_KycGetHandle *kyc,
- struct TALER_MERCHANT_KycResponse *kr,
- const json_t *jkyc)
-{
- unsigned int num_kycs = (unsigned int) json_array_size (jkyc);
- unsigned int num_limits = 0;
- unsigned int num_kycauths = 0;
- unsigned int pos_limits = 0;
- unsigned int pos_kycauths = 0;
-
- if ( (json_array_size (jkyc) != (size_t) num_kycs) ||
- (num_kycs > MAX_KYC) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
- for (unsigned int i = 0; i<num_kycs; i++)
- {
- const json_t *jlimits = NULL;
- const json_t *jkycauths = NULL;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const (
- "limits",
- &jlimits),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const (
- "payto_kycauths",
- &jkycauths),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json_array_get (jkyc,
- i),
- spec,
- NULL, NULL))
- {
- GNUNET_break (0);
- json_dumpf (json_array_get (jkyc,
- i),
- stderr,
- JSON_INDENT (2));
- return GNUNET_SYSERR;
- }
- num_limits += json_array_size (jlimits);
- num_kycauths += json_array_size (jkycauths);
- }
-
-
- {
- struct TALER_MERCHANT_AccountKycRedirectDetail kycs[
- GNUNET_NZL (num_kycs)];
- struct TALER_EXCHANGE_AccountLimit limits[
- GNUNET_NZL (num_limits)];
- struct TALER_FullPayto payto_kycauths[
- GNUNET_NZL (num_kycauths)];
-
- memset (kycs,
- 0,
- sizeof (kycs));
- for (unsigned int i = 0; i<num_kycs; i++)
- {
- struct TALER_MERCHANT_AccountKycRedirectDetail *rd
- = &kycs[i];
- const json_t *jlimits = NULL;
- const json_t *jkycauths = NULL;
- uint32_t hs;
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_full_payto_uri (
- "payto_uri",
- &rd->payto_uri),
- TALER_JSON_spec_web_url (
- "exchange_url",
- &rd->exchange_url),
- GNUNET_JSON_spec_uint32 (
- "exchange_http_status",
- &hs),
- GNUNET_JSON_spec_bool (
- "no_keys",
- &rd->no_keys),
- GNUNET_JSON_spec_bool (
- "auth_conflict",
- &rd->auth_conflict),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_ec (
- "exchange_code",
- &rd->exchange_code),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_fixed_auto (
- "access_token",
- &rd->access_token),
- &rd->no_access_token),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const (
- "limits",
- &jlimits),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const (
- "payto_kycauths",
- &jkycauths),
- NULL),
- GNUNET_JSON_spec_end ()
- };
- size_t j;
- json_t *jlimit;
- json_t *jkycauth;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json_array_get (jkyc,
- i),
- spec,
- NULL, NULL))
- {
- GNUNET_break (0);
- json_dumpf (json_array_get (jkyc,
- i),
- stderr,
- JSON_INDENT (2));
- return GNUNET_SYSERR;
- }
- rd->exchange_http_status = (unsigned int) hs;
- rd->limits = &limits[pos_limits];
- rd->limits_length = json_array_size (jlimits);
- json_array_foreach (jlimits, j, jlimit)
- {
- struct TALER_EXCHANGE_AccountLimit *limit
- = &limits[pos_limits];
- struct GNUNET_JSON_Specification jspec[] = {
- TALER_JSON_spec_kycte (
- "operation_type",
- &limit->operation_type),
- GNUNET_JSON_spec_relative_time (
- "timeframe",
- &limit->timeframe),
- TALER_JSON_spec_amount_any (
- "threshold",
- &limit->threshold),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool (
- "soft_limit",
- &limit->soft_limit),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- GNUNET_assert (pos_limits < num_limits);
- limit->soft_limit = false;
- if (GNUNET_OK !=
- GNUNET_JSON_parse (jlimit,
- jspec,
- NULL, NULL))
- {
- GNUNET_break (0);
- json_dumpf (json_array_get (jkyc,
- i),
- stderr,
- JSON_INDENT (2));
- return GNUNET_SYSERR;
- }
- pos_limits++;
- }
- rd->payto_kycauths = &payto_kycauths[pos_kycauths];
- rd->pkycauth_length = json_array_size (jkycauths);
- json_array_foreach (jkycauths, j, jkycauth)
- {
- GNUNET_assert (pos_kycauths < num_kycauths);
- payto_kycauths[pos_kycauths].full_payto
- = (char *) json_string_value (jkycauth);
- if (NULL == payto_kycauths[pos_kycauths].full_payto)
- {
- GNUNET_break (0);
- json_dumpf (json_array_get (jkyc,
- i),
- stderr,
- JSON_INDENT (2));
- return GNUNET_SYSERR;
- }
- pos_kycauths++;
- }
- }
- kr->details.ok.kycs = kycs;
- kr->details.ok.kycs_length = num_kycs;
- kyc->cb (kyc->cb_cls,
- kr);
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /kyc request.
- *
- * @param cls the `struct TALER_MERCHANT_KycGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_kyc_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_KycGetHandle *kyc = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_KycResponse kr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- kyc->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /kyc response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *jkyc;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("kyc_data",
- &jkyc),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- kr.hr.http_status = 0;
- kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- if (GNUNET_OK !=
- parse_kyc (kyc,
- &kr,
- jkyc))
- {
- kr.hr.http_status = 0;
- kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- /* parse_kyc called the continuation already */
- TALER_MERCHANT_kyc_get_cancel (kyc);
- return;
- }
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- kr.hr.ec = TALER_JSON_get_error_code (json);
- kr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_SERVICE_UNAVAILABLE:
- break;
- default:
- /* unexpected response code */
- kr.hr.ec = TALER_JSON_get_error_code (json);
- kr.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) kr.hr.ec);
- break;
- }
- kyc->cb (kyc->cb_cls,
- &kr);
- TALER_MERCHANT_kyc_get_cancel (kyc);
-}
-
-
-/**
- * Issue a GET KYC request to the backend.
- * Returns KYC status of bank accounts.
- *
- * @param ctx execution context
- * @param[in] url URL to use for the request, consumed!
- * @param h_wire which bank account to query, NULL for all
- * @param exchange_url which exchange to query, NULL for all
- * @param lpt target for long polling
- * @param timeout how long to wait for a reply
- * @param cb function to call with the result
- * @param cb_cls closure for @a cb
- * @return handle for this operation, NULL upon errors
- */
-static struct TALER_MERCHANT_KycGetHandle *
-kyc_get (struct GNUNET_CURL_Context *ctx,
- char *url,
- const struct TALER_MerchantWireHashP *h_wire,
- const char *exchange_url,
- enum TALER_EXCHANGE_KycLongPollTarget lpt,
- struct GNUNET_TIME_Relative timeout,
- TALER_MERCHANT_KycGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_KycGetHandle *kyc;
- CURL *eh;
- char timeout_ms[32];
- char lpt_str[32];
- unsigned long long tms;
-
- kyc = GNUNET_new (struct TALER_MERCHANT_KycGetHandle);
- kyc->ctx = ctx;
- kyc->cb = cb;
- kyc->cb_cls = cb_cls;
- GNUNET_snprintf (lpt_str,
- sizeof (lpt_str),
- "%d",
- (int) lpt);
- tms = timeout.rel_value_us
- / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
- GNUNET_snprintf (timeout_ms,
- sizeof (timeout_ms),
- "%llu",
- tms);
- kyc->url
- = TALER_url_join (
- url,
- "kyc",
- "h_wire",
- NULL == h_wire
- ? NULL
- : GNUNET_h2s_full (&h_wire->hash),
- "exchange_url",
- NULL == exchange_url
- ? NULL
- : exchange_url,
- "timeout_ms",
- GNUNET_TIME_relative_is_zero (timeout)
- ? NULL
- : timeout_ms,
- "lpt",
- TALER_EXCHANGE_KLPT_NONE == lpt
- ? NULL
- : lpt_str,
- NULL);
- GNUNET_free (url);
- if (NULL == kyc->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (kyc);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- kyc->url);
- eh = TALER_MERCHANT_curl_easy_get_ (kyc->url);
- if (0 != tms)
- {
- GNUNET_break (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_TIMEOUT_MS,
- (long) (tms + 100L)));
- }
- kyc->job
- = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_kyc_finished,
- kyc);
- return kyc;
-}
-
-
-struct TALER_MERCHANT_KycGetHandle *
-TALER_MERCHANT_kyc_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const struct TALER_MerchantWireHashP *h_wire,
- const char *exchange_url,
- enum TALER_EXCHANGE_KycLongPollTarget lpt,
- struct GNUNET_TIME_Relative timeout,
- TALER_MERCHANT_KycGetCallback cb,
- void *cb_cls)
-{
- char *url;
-
- GNUNET_asprintf (&url,
- "%sprivate/",
- backend_url);
- return kyc_get (ctx,
- url, /* consumed! */
- h_wire,
- exchange_url,
- lpt,
- timeout,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_KycGetHandle *
-TALER_MERCHANT_management_kyc_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- const struct TALER_MerchantWireHashP *h_wire,
- const char *exchange_url,
- enum TALER_EXCHANGE_KycLongPollTarget lpt,
- struct GNUNET_TIME_Relative timeout,
- TALER_MERCHANT_KycGetCallback cb,
- void *cb_cls)
-{
- char *url;
-
- GNUNET_asprintf (&url,
- "%smanagement/instances/%s/",
- backend_url,
- instance_id);
- return kyc_get (ctx,
- url, /* consumed! */
- h_wire,
- exchange_url,
- lpt,
- timeout,
- cb,
- cb_cls);
-}
-
-
-void
-TALER_MERCHANT_kyc_get_cancel (
- struct TALER_MERCHANT_KycGetHandle *kyc)
-{
- if (NULL != kyc->job)
- GNUNET_CURL_job_cancel (kyc->job);
- GNUNET_free (kyc->url);
- GNUNET_free (kyc);
-}
diff --git a/src/lib/merchant_api_get_orders.c b/src/lib/merchant_api_get_orders.c
@@ -1,442 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_get_orders.c
- * @brief Implementation of the GET /private/orders request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-/**
- * Maximum number of orders we return.
- */
-#define MAX_ORDERS 1024
-
-/**
- * Handle for a GET /orders operation.
- */
-struct TALER_MERCHANT_OrdersGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OrdersGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse order information from @a ia.
- *
- * @param ia JSON array (or NULL!) with order data
- * @param[in] ogr response to fill
- * @param ogh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_orders (const json_t *ia,
- struct TALER_MERCHANT_OrdersGetResponse *ogr,
- struct TALER_MERCHANT_OrdersGetHandle *ogh)
-{
- unsigned int oes_len = (unsigned int) json_array_size (ia);
-
- if ( (json_array_size (ia) != (size_t) oes_len) ||
- (oes_len > MAX_ORDERS) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_OrderEntry oes[GNUNET_NZL (oes_len)];
- size_t index;
- json_t *value;
-
- memset (oes,
- 0,
- sizeof (oes));
- json_array_foreach (ia, index, value) {
- struct TALER_MERCHANT_OrderEntry *ie = &oes[index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("order_id",
- &ie->order_id),
- GNUNET_JSON_spec_timestamp ("timestamp",
- &ie->timestamp),
- GNUNET_JSON_spec_uint64 ("row_id",
- &ie->order_serial),
- TALER_JSON_spec_amount_any ("amount",
- &ie->amount),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("refund_amount",
- &ie->refund_amount),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("pending_refund_amount",
- &ie->pending_refund_amount),
- NULL),
- GNUNET_JSON_spec_string ("summary",
- &ie->summary),
- GNUNET_JSON_spec_bool ("refundable",
- &ie->refundable),
- GNUNET_JSON_spec_bool ("paid",
- &ie->paid),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- ogr->details.ok.orders_length = oes_len;
- ogr->details.ok.orders = oes;
- ogh->cb (ogh->cb_cls,
- ogr);
- ogh->cb = NULL; /* just to be sure */
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /orders request.
- *
- * @param cls the `struct TALER_MERCHANT_OrdersGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_orders_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrdersGetHandle *ogh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_OrdersGetResponse ogr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- ogh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /orders response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *orders;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("orders",
- &orders),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- ogr.hr.http_status = 0;
- ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- if (GNUNET_OK ==
- parse_orders (orders,
- &ogr,
- ogh))
- {
- TALER_MERCHANT_orders_get_cancel (ogh);
- return;
- }
- ogr.hr.http_status = 0;
- ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- ogr.hr.ec = TALER_JSON_get_error_code (json);
- ogr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- ogr.hr.ec = TALER_JSON_get_error_code (json);
- ogr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- ogr.hr.ec = TALER_JSON_get_error_code (json);
- ogr.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) ogr.hr.ec);
- break;
- }
- ogh->cb (ogh->cb_cls,
- &ogr);
- TALER_MERCHANT_orders_get_cancel (ogh);
-}
-
-
-struct TALER_MERCHANT_OrdersGetHandle *
-TALER_MERCHANT_orders_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_OrdersGetCallback cb,
- void *cb_cls)
-{
- return TALER_MERCHANT_orders_get2 (ctx,
- backend_url,
- TALER_EXCHANGE_YNA_ALL,
- TALER_EXCHANGE_YNA_ALL,
- TALER_EXCHANGE_YNA_ALL,
- GNUNET_TIME_UNIT_FOREVER_TS,
- UINT64_MAX,
- -20, /* default is most recent 20 entries */
- GNUNET_TIME_UNIT_ZERO,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_OrdersGetHandle *
-TALER_MERCHANT_orders_get2 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- enum TALER_EXCHANGE_YesNoAll paid,
- enum TALER_EXCHANGE_YesNoAll refunded,
- enum TALER_EXCHANGE_YesNoAll wired,
- struct GNUNET_TIME_Timestamp date,
- uint64_t start_row,
- int64_t delta,
- struct GNUNET_TIME_Relative timeout,
- TALER_MERCHANT_OrdersGetCallback cb,
- void *cb_cls)
-{
- return TALER_MERCHANT_orders_get3 (
- ctx,
- backend_url,
- paid,
- refunded,
- wired,
- NULL,
- NULL,
- date,
- start_row,
- delta,
- timeout,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_OrdersGetHandle *
-TALER_MERCHANT_orders_get3 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- enum TALER_EXCHANGE_YesNoAll paid,
- enum TALER_EXCHANGE_YesNoAll refunded,
- enum TALER_EXCHANGE_YesNoAll wired,
- const char *session_id,
- const char *fulfillment_url,
- struct GNUNET_TIME_Timestamp date,
- uint64_t start_row,
- int64_t delta,
- struct GNUNET_TIME_Relative timeout,
- TALER_MERCHANT_OrdersGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OrdersGetHandle *ogh;
- CURL *eh;
- unsigned int tms = timeout.rel_value_us
- / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
-
- GNUNET_assert (NULL != backend_url);
- if ( (delta > MAX_ORDERS) ||
- (delta < -MAX_ORDERS) )
- {
- GNUNET_break (0);
- return NULL;
- }
- if (0 == delta)
- {
- GNUNET_break (0);
- return NULL;
- }
- ogh = GNUNET_new (struct TALER_MERCHANT_OrdersGetHandle);
- ogh->ctx = ctx;
- ogh->cb = cb;
- ogh->cb_cls = cb_cls;
-
- /* build ogh->url with the various optional arguments */
- {
- char dstr[30];
- char *fec = NULL;
- char *sid = NULL;
- bool have_date;
- bool have_srow;
- char cbuf[30];
- char dbuf[30];
- char tbuf[30];
-
- GNUNET_snprintf (tbuf,
- sizeof (tbuf),
- "%u",
- tms);
- GNUNET_snprintf (dbuf,
- sizeof (dbuf),
- "%lld",
- (long long) delta);
- GNUNET_snprintf (cbuf,
- sizeof (cbuf),
- "%llu",
- (unsigned long long) start_row);
- if (NULL != session_id)
- (void) GNUNET_STRINGS_urlencode (strlen (session_id),
- session_id,
- &sid);
- if (NULL != fulfillment_url)
- (void) GNUNET_STRINGS_urlencode (strlen (fulfillment_url),
- fulfillment_url,
- &fec);
- GNUNET_snprintf (dstr,
- sizeof (dstr),
- "%llu",
- (unsigned long long) GNUNET_TIME_timestamp_to_s (date));
- if (delta > 0)
- {
- have_date = ! GNUNET_TIME_absolute_is_zero (date.abs_time);
- have_srow = (0 != start_row);
- }
- else
- {
- have_date = ! GNUNET_TIME_absolute_is_never (date.abs_time);
- have_srow = (UINT64_MAX != start_row);
- }
- ogh->url = TALER_url_join (backend_url,
- "private/orders",
- "paid",
- (TALER_EXCHANGE_YNA_ALL != paid)
- ? TALER_yna_to_string (paid)
- : NULL,
- "refunded",
- (TALER_EXCHANGE_YNA_ALL != refunded)
- ? TALER_yna_to_string (refunded)
- : NULL,
- "wired",
- (TALER_EXCHANGE_YNA_ALL != wired)
- ? TALER_yna_to_string (wired)
- : NULL,
- "date_s",
- (have_date)
- ? dstr
- : NULL,
- "start",
- (have_srow)
- ? cbuf
- : NULL,
- "delta",
- (-20 != delta)
- ? dbuf
- : NULL,
- "timeout_ms",
- (0 != tms)
- ? tbuf
- : NULL,
- "session_id",
- sid,
- "fulfillment_url",
- fec,
- NULL);
- GNUNET_free (sid);
- GNUNET_free (fec);
- }
- if (NULL == ogh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (ogh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- ogh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (ogh->url);
- if (NULL == eh)
- {
- GNUNET_break (0);
- GNUNET_free (ogh->url);
- GNUNET_free (ogh);
- return NULL;
- }
- if (0 != tms)
- {
- GNUNET_break (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_TIMEOUT_MS,
- (long) (tms + 100L)));
- }
- ogh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_orders_finished,
- ogh);
- return ogh;
-}
-
-
-void
-TALER_MERCHANT_orders_get_cancel (
- struct TALER_MERCHANT_OrdersGetHandle *ogh)
-{
- if (NULL != ogh->job)
- GNUNET_CURL_job_cancel (ogh->job);
- GNUNET_free (ogh->url);
- GNUNET_free (ogh);
-}
diff --git a/src/lib/merchant_api_get_otp_device.c b/src/lib/merchant_api_get_otp_device.c
@@ -1,210 +0,0 @@
-/*
- 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_get_otp_device.c
- * @brief Implementation of the GET /otp-devices/$ID request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/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 /otp-devices/$ID operation.
- */
-struct TALER_MERCHANT_OtpDeviceGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OtpDeviceGetCallback 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 /otp-devices/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_OtpDeviceGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_otp_device_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OtpDeviceGetHandle *tgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_OtpDeviceGetResponse tgr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- tgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /otp-devices/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- uint32_t alg32;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("otp_device_description",
- &tgr.details.ok.otp_device_description),
- GNUNET_JSON_spec_uint32 ("otp_algorithm",
- &alg32),
- 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 ()
- };
-
- if (GNUNET_OK ==
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- tgr.details.ok.otp_alg =
- (enum TALER_MerchantConfirmationAlgorithm) alg32;
- tgh->cb (tgh->cb_cls,
- &tgr);
- TALER_MERCHANT_otp_device_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_otp_device_get_cancel (tgh);
-}
-
-
-struct TALER_MERCHANT_OtpDeviceGetHandle *
-TALER_MERCHANT_otp_device_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *otp_device_id,
- TALER_MERCHANT_OtpDeviceGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OtpDeviceGetHandle *tgh;
- CURL *eh;
-
- tgh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceGetHandle);
- tgh->ctx = ctx;
- tgh->cb = cb;
- tgh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/otp-devices/%s",
- otp_device_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_otp_device_finished,
- tgh);
- return tgh;
-}
-
-
-void
-TALER_MERCHANT_otp_device_get_cancel (
- struct TALER_MERCHANT_OtpDeviceGetHandle *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_otp_devices.c b/src/lib/merchant_api_get_otp_devices.c
@@ -1,248 +0,0 @@
-/*
- 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_get_otp_devices.c
- * @brief Implementation of the GET /otp-devices request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-/**
- * Maximum number of OTP devices we return.
- */
-#define MAX_OTP 1024
-
-
-/**
- * Handle for a GET /otp-devices operation.
- */
-struct TALER_MERCHANT_OtpDevicesGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OtpDevicesGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse OTP device information from @a ia.
- *
- * @param ia JSON array (or NULL!) with otp_device data
- * @param[in] tgr partially filled response
- * @param tgh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_otp_devices (const json_t *ia,
- struct TALER_MERCHANT_OtpDevicesGetResponse *tgr,
- struct TALER_MERCHANT_OtpDevicesGetHandle *tgh)
-{
- unsigned int otp_len = (unsigned int) json_array_size (ia);
-
- if ( (json_array_size (ia) != (size_t) otp_len) ||
- (otp_len > MAX_OTP) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_OtpDeviceEntry otp[GNUNET_NZL (otp_len)];
- size_t index;
- json_t *value;
-
- json_array_foreach (ia, index, value) {
- struct TALER_MERCHANT_OtpDeviceEntry *ie = &otp[index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("otp_device_id",
- &ie->otp_device_id),
- GNUNET_JSON_spec_string ("device_description",
- &ie->otp_device_description),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- tgr->details.ok.otp_devices_length = otp_len;
- tgr->details.ok.otp_devices = otp;
- 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 /otp-devices request.
- *
- * @param cls the `struct TALER_MERCHANT_OtpDevicesGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_otp_devices_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OtpDevicesGetHandle *tgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_OtpDevicesGetResponse tgr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- tgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /otp-devices response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *otp_devices;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("otp_devices",
- &otp_devices),
- 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_otp_devices (otp_devices,
- &tgr,
- tgh))
- {
- TALER_MERCHANT_otp_devices_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_otp_devices_get_cancel (tgh);
-}
-
-
-struct TALER_MERCHANT_OtpDevicesGetHandle *
-TALER_MERCHANT_otp_devices_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_OtpDevicesGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OtpDevicesGetHandle *tgh;
- CURL *eh;
-
- tgh = GNUNET_new (struct TALER_MERCHANT_OtpDevicesGetHandle);
- tgh->ctx = ctx;
- tgh->cb = cb;
- tgh->cb_cls = cb_cls;
- tgh->url = TALER_url_join (backend_url,
- "private/otp-devices",
- 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_otp_devices_finished,
- tgh);
- return tgh;
-}
-
-
-void
-TALER_MERCHANT_otp_devices_get_cancel (
- struct TALER_MERCHANT_OtpDevicesGetHandle *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_product.c b/src/lib/merchant_api_get_product.c
@@ -1,249 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014--2025 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_get_product.c
- * @brief Implementation of the GET /product/$ID request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/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 /products/$ID operation.
- */
-struct TALER_MERCHANT_ProductGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ProductGetCallback 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 /products/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_ProductGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_product_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_ProductGetHandle *pgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_ProductGetResponse pgr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- pgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /products/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string (
- "product_name",
- &pgr.details.ok.product_name),
- GNUNET_JSON_spec_string (
- "description",
- &pgr.details.ok.description),
- GNUNET_JSON_spec_object_const (
- "description_i18n",
- &pgr.details.ok.description_i18n),
- GNUNET_JSON_spec_string (
- "unit",
- &pgr.details.ok.unit),
- TALER_JSON_spec_amount_any_array (
- "unit_price",
- &pgr.details.ok.unit_price_len,
- (struct TALER_Amount **) &pgr.details.ok.unit_price),
- TALER_JSON_spec_amount_any (
- "price",
- &pgr.details.ok.price),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string (
- "image",
- &pgr.details.ok.image),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const (
- "taxes",
- &pgr.details.ok.taxes),
- NULL),
- GNUNET_JSON_spec_int64 (
- "total_stock",
- &pgr.details.ok.total_stock),
- GNUNET_JSON_spec_string (
- "unit_total_stock",
- &pgr.details.ok.unit_total_stock),
- GNUNET_JSON_spec_bool (
- "unit_allow_fraction",
- &pgr.details.ok.unit_allow_fraction),
- GNUNET_JSON_spec_uint32 (
- "unit_precision_level",
- &pgr.details.ok.unit_precision_level),
- GNUNET_JSON_spec_uint64 (
- "total_sold",
- &pgr.details.ok.total_sold),
- GNUNET_JSON_spec_uint64 (
- "total_lost",
- &pgr.details.ok.total_lost),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const (
- "address",
- &pgr.details.ok.location),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_timestamp (
- "next_restock",
- &pgr.details.ok.next_restock),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK ==
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- pgh->cb (pgh->cb_cls,
- &pgr);
- GNUNET_JSON_parse_free (spec);
- TALER_MERCHANT_product_get_cancel (pgh);
- return;
- }
- pgr.hr.http_status = 0;
- pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- pgr.hr.ec = TALER_JSON_get_error_code (json);
- pgr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- pgr.hr.ec = TALER_JSON_get_error_code (json);
- pgr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- pgr.hr.ec = TALER_JSON_get_error_code (json);
- pgr.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) pgr.hr.ec);
- break;
- }
- pgh->cb (pgh->cb_cls,
- &pgr);
- TALER_MERCHANT_product_get_cancel (pgh);
-}
-
-
-struct TALER_MERCHANT_ProductGetHandle *
-TALER_MERCHANT_product_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- TALER_MERCHANT_ProductGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_ProductGetHandle *pgh;
- CURL *eh;
-
- pgh = GNUNET_new (struct TALER_MERCHANT_ProductGetHandle);
- pgh->ctx = ctx;
- pgh->cb = cb;
- pgh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/products/%s",
- product_id);
- pgh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == pgh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (pgh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- pgh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (pgh->url);
- pgh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_product_finished,
- pgh);
- return pgh;
-}
-
-
-void
-TALER_MERCHANT_product_get_cancel (
- struct TALER_MERCHANT_ProductGetHandle *pgh)
-{
- if (NULL != pgh->job)
- GNUNET_CURL_job_cancel (pgh->job);
- GNUNET_free (pgh->url);
- GNUNET_free (pgh);
-}
diff --git a/src/lib/merchant_api_get_product_image.c b/src/lib/merchant_api_get_product_image.c
@@ -1,188 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_get_product_image.c
- * @brief Implementation of the GET /products/$HASH/image request
- * @author Bohdan Potuzhnyi
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include <taler/taler_error_codes.h>
-#include <taler/taler_json_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-
-
-/**
- * Handle for a GET /products/$HASH/image operation.
- */
-struct TALER_MERCHANT_ProductImageGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ProductImageGetCallback 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 /products/$HASH/image request.
- *
- * @param cls the `struct TALER_MERCHANT_ProductImageGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_product_image_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_ProductImageGetHandle *pigh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_ProductImageGetResponse pir = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- pigh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /products/$HASH/image response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("image",
- &pir.details.ok.image),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK ==
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- pigh->cb (pigh->cb_cls,
- &pir);
- GNUNET_JSON_parse_free (spec);
- TALER_MERCHANT_product_image_get_cancel (pigh);
- return;
- }
- pir.hr.http_status = 0;
- pir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_NOT_FOUND:
- case MHD_HTTP_BAD_REQUEST:
- pir.hr.ec = TALER_JSON_get_error_code (json);
- pir.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- pir.hr.ec = TALER_JSON_get_error_code (json);
- pir.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) pir.hr.ec);
- break;
- }
- pigh->cb (pigh->cb_cls,
- &pir);
- TALER_MERCHANT_product_image_get_cancel (pigh);
-}
-
-
-struct TALER_MERCHANT_ProductImageGetHandle *
-TALER_MERCHANT_product_image_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *image_hash,
- TALER_MERCHANT_ProductImageGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_ProductImageGetHandle *pigh;
- CURL *eh;
-
- pigh = GNUNET_new (struct TALER_MERCHANT_ProductImageGetHandle);
- pigh->ctx = ctx;
- pigh->cb = cb;
- pigh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "products/%s/image",
- image_hash);
- pigh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == pigh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (pigh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- pigh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (pigh->url);
- pigh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_product_image_finished,
- pigh);
- return pigh;
-}
-
-
-void
-TALER_MERCHANT_product_image_get_cancel (
- struct TALER_MERCHANT_ProductImageGetHandle *pigh)
-{
- if (NULL != pigh->job)
- GNUNET_CURL_job_cancel (pigh->job);
- GNUNET_free (pigh->url);
- GNUNET_free (pigh);
-}
-\ No newline at end of file
diff --git a/src/lib/merchant_api_get_products.c b/src/lib/merchant_api_get_products.c
@@ -1,262 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2024 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_get_products.c
- * @brief Implementation of the GET /products request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Maximum number of products we return.
- */
-#define MAX_PRODUCTS 1024
-
-
-/**
- * Handle for a GET /products operation.
- */
-struct TALER_MERCHANT_ProductsGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ProductsGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse product information from @a ia.
- *
- * @param json overall JSON reply
- * @param ia JSON array (or NULL!) with product data
- * @param pgh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_products (const json_t *json,
- const json_t *ia,
- struct TALER_MERCHANT_ProductsGetHandle *pgh)
-{
- unsigned int ies_len = json_array_size (ia);
-
- if ( (json_array_size (ia) != (size_t) ies_len) ||
- (ies_len > MAX_PRODUCTS) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_InventoryEntry ies[GNUNET_NZL (ies_len)];
- size_t index;
- json_t *value;
- enum GNUNET_GenericReturnValue ret;
-
- ret = GNUNET_OK;
- json_array_foreach (ia, index, value) {
- struct TALER_MERCHANT_InventoryEntry *ie = &ies[index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("product_id",
- &ie->product_id),
- GNUNET_JSON_spec_uint64 ("product_serial",
- &ie->product_serial),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ret = GNUNET_SYSERR;
- continue;
- }
- if (GNUNET_SYSERR == ret)
- break;
- }
- if (GNUNET_OK == ret)
- {
- struct TALER_MERCHANT_GetProductsResponse gpr = {
- .hr.http_status = MHD_HTTP_OK,
- .hr.reply = json,
- .details.ok.products_length = ies_len,
- .details.ok.products = ies
- };
-
- pgh->cb (pgh->cb_cls,
- &gpr);
- pgh->cb = NULL; /* just to be sure */
- }
- return ret;
- }
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /products request.
- *
- * @param cls the `struct TALER_MERCHANT_ProductsGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_products_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_ProductsGetHandle *pgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_GetProductsResponse gpr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- pgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /products response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *products;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("products",
- &products),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- gpr.hr.http_status = 0;
- gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- if (GNUNET_OK ==
- parse_products (json,
- products,
- pgh))
- {
- TALER_MERCHANT_products_get_cancel (pgh);
- return;
- }
- gpr.hr.http_status = 0;
- gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- gpr.hr.ec = TALER_JSON_get_error_code (json);
- gpr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- default:
- /* unexpected response code */
- gpr.hr.ec = TALER_JSON_get_error_code (json);
- gpr.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) gpr.hr.ec);
- break;
- }
- pgh->cb (pgh->cb_cls,
- &gpr);
- TALER_MERCHANT_products_get_cancel (pgh);
-}
-
-
-struct TALER_MERCHANT_ProductsGetHandle *
-TALER_MERCHANT_products_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_ProductsGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_ProductsGetHandle *pgh;
- CURL *eh;
-
- pgh = GNUNET_new (struct TALER_MERCHANT_ProductsGetHandle);
- pgh->ctx = ctx;
- pgh->cb = cb;
- pgh->cb_cls = cb_cls;
- pgh->url = TALER_url_join (backend_url,
- "private/products",
- NULL);
- if (NULL == pgh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (pgh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- pgh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (pgh->url);
- pgh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_products_finished,
- pgh);
- return pgh;
-}
-
-
-void
-TALER_MERCHANT_products_get_cancel (
- struct TALER_MERCHANT_ProductsGetHandle *pgh)
-{
- if (NULL != pgh->job)
- GNUNET_CURL_job_cancel (pgh->job);
- GNUNET_free (pgh->url);
- GNUNET_free (pgh);
-}
diff --git a/src/lib/merchant_api_get_statistics.c b/src/lib/merchant_api_get_statistics.c
@@ -1,717 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_get_statistics.c
- * @brief Implementation of the GET /statistics-[counter,amount]/$SLUG request of the merchant's HTTP API
- * @author Martin Schanzenbach
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <gnunet/gnunet_common.h>
-#include <gnunet/gnunet_json_lib.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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-/**
- * Maximum number of statistics we return
- */
-#define MAX_STATISTICS 1024
-
-/**
- * Handle for a GET /statistics-amount/$SLUG operation.
- */
-struct TALER_MERCHANT_StatisticsAmountGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_StatisticsAmountGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-/**
- * Handle for a GET /statistics-counter/$SLUG operation.
- */
-struct TALER_MERCHANT_StatisticsCounterGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_StatisticsCounterGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse interval information from buckets and intervals.
- *
- * @param json overall JSON reply
- * @param jbuckets JSON array (or NULL!) with bucket data
- * @param buckets_description human-readable description for the buckets
- * @param jintervals JSON array (or NULL!) with bucket data
- * @param intervals_description human-readable description for the intervals
- * @param sgh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_intervals_and_buckets_amt (
- const json_t *json,
- const json_t *jbuckets,
- const char *buckets_description,
- const json_t *jintervals,
- const char *intervals_description,
- struct TALER_MERCHANT_StatisticsAmountGetHandle *sgh
- )
-{
- unsigned int resp_buckets_len = json_array_size (jbuckets);
- unsigned int resp_intervals_len = json_array_size (jintervals);
-
- if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
- (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
- (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_StatisticAmountByBucket resp_buckets[
- GNUNET_NZL (resp_buckets_len)];
- struct TALER_MERCHANT_StatisticAmountByInterval resp_intervals[
- GNUNET_NZL (resp_intervals_len)];
- size_t index;
- json_t *value;
- enum GNUNET_GenericReturnValue ret;
-
- ret = GNUNET_OK;
- json_array_foreach (jintervals, index, value) {
- struct TALER_MERCHANT_StatisticAmountByInterval *jinterval
- = &resp_intervals[index];
- const json_t *amounts_arr;
- size_t amounts_len;
-
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_timestamp ("start_time",
- &jinterval->start_time),
- GNUNET_JSON_spec_array_const ("cumulative_amounts",
- &amounts_arr),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ret = GNUNET_SYSERR;
- continue;
- }
- if (GNUNET_SYSERR == ret)
- break;
- amounts_len = json_array_size (amounts_arr);
- {
- struct TALER_Amount amt_arr[amounts_len];
- size_t aindex;
- json_t *avalue;
-
- jinterval->cumulative_amount_len = amounts_len;
- jinterval->cumulative_amounts = amt_arr;
- json_array_foreach (amounts_arr, aindex, avalue) {
- if (! json_is_string (avalue))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_string_to_amount (json_string_value (avalue),
- &amt_arr[aindex]))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- }
- }
- ret = GNUNET_OK;
- json_array_foreach (jbuckets, index, value) {
- struct TALER_MERCHANT_StatisticAmountByBucket *jbucket
- = &resp_buckets[index];
- const json_t *amounts_arr;
- size_t amounts_len;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_timestamp ("start_time",
- &jbucket->start_time),
- GNUNET_JSON_spec_timestamp ("end_time",
- &jbucket->end_time),
- GNUNET_JSON_spec_string ("range",
- &jbucket->range),
- GNUNET_JSON_spec_array_const ("cumulative_amounts",
- &amounts_arr),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ret = GNUNET_SYSERR;
- continue;
- }
- if (GNUNET_SYSERR == ret)
- break;
- amounts_len = json_array_size (amounts_arr);
- if (0 > amounts_len)
- {
- GNUNET_break_op (0);
- ret = GNUNET_SYSERR;
- break;
- }
- {
- struct TALER_Amount amt_arr[amounts_len];
- size_t aindex;
- json_t *avalue;
- jbucket->cumulative_amount_len = amounts_len;
- jbucket->cumulative_amounts = amt_arr;
- json_array_foreach (amounts_arr, aindex, avalue) {
- if (! json_is_string (avalue))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_string_to_amount (json_string_value (avalue),
- &amt_arr[aindex]))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- }
- }
- if (GNUNET_OK == ret)
- {
- struct TALER_MERCHANT_StatisticsAmountGetResponse gsr = {
- .hr.http_status = MHD_HTTP_OK,
- .hr.reply = json,
- .details.ok.buckets_length = resp_buckets_len,
- .details.ok.buckets = resp_buckets,
- .details.ok.buckets_description = buckets_description,
- .details.ok.intervals_length = resp_intervals_len,
- .details.ok.intervals = resp_intervals,
- .details.ok.intervals_description = intervals_description,
- };
- sgh->cb (sgh->cb_cls,
- &gsr);
- sgh->cb = NULL; /* just to be sure */
- }
- return ret;
- }
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP GET /statistics-amount/$SLUG request.
- *
- * @param cls the `struct TALER_MERCHANT_StatisticsAmountGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_statistics_amount_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_StatisticsAmountGetHandle *handle = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_StatisticsAmountGetResponse res = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- handle->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /statistics-amount/$SLUG response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *buckets;
- const json_t *intervals;
- const char *buckets_description = NULL;
- const char *intervals_description = NULL;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("buckets",
- &buckets),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("buckets_description",
- &buckets_description),
- NULL),
- GNUNET_JSON_spec_array_const ("intervals",
- &intervals),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("intervals_description",
- &intervals_description),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- res.hr.http_status = 0;
- res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- if (GNUNET_OK ==
- parse_intervals_and_buckets_amt (json,
- buckets,
- buckets_description,
- intervals,
- intervals_description,
- handle))
- {
- TALER_MERCHANT_statistic_amount_get_cancel (handle);
- return;
- }
- res.hr.http_status = 0;
- res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.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) res.hr.ec);
- break;
- }
-}
-
-
-/**
- * Parse interval information from @a ia.
- *
- * @param json overall JSON reply
- * @param jbuckets JSON array (or NULL!) with bucket data
- * @param buckets_description human-readable description for the buckets
- * @param jintervals JSON array (or NULL!) with bucket data
- * @param intervals_description human-readable description for the intervals
- * @param scgh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_intervals_and_buckets (
- const json_t *json,
- const json_t *jbuckets,
- const char *buckets_description,
- const json_t *jintervals,
- const char *intervals_description,
- struct TALER_MERCHANT_StatisticsCounterGetHandle *scgh)
-{
- unsigned int resp_buckets_len = json_array_size (jbuckets);
- unsigned int resp_intervals_len = json_array_size (jintervals);
-
- if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
- (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
- (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_StatisticCounterByBucket resp_buckets[
- GNUNET_NZL (resp_buckets_len)];
- struct TALER_MERCHANT_StatisticCounterByInterval resp_intervals[
- GNUNET_NZL (resp_intervals_len)];
- size_t index;
- json_t *value;
- enum GNUNET_GenericReturnValue ret;
-
- ret = GNUNET_OK;
- json_array_foreach (jintervals, index, value) {
- struct TALER_MERCHANT_StatisticCounterByInterval *jinterval
- = &resp_intervals[index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_timestamp ("start_time",
- &jinterval->start_time),
- GNUNET_JSON_spec_uint64 ("cumulative_counter",
- &jinterval->cumulative_counter),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ret = GNUNET_SYSERR;
- continue;
- }
- if (GNUNET_SYSERR == ret)
- break;
- }
- ret = GNUNET_OK;
- json_array_foreach (jbuckets, index, value) {
- struct TALER_MERCHANT_StatisticCounterByBucket *jbucket = &resp_buckets[
- index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_timestamp ("start_time",
- &jbucket->start_time),
- GNUNET_JSON_spec_timestamp ("end_time",
- &jbucket->end_time),
- GNUNET_JSON_spec_string ("range",
- &jbucket->range),
- GNUNET_JSON_spec_uint64 ("cumulative_counter",
- &jbucket->cumulative_counter),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ret = GNUNET_SYSERR;
- continue;
- }
- if (GNUNET_SYSERR == ret)
- break;
- }
- if (GNUNET_OK == ret)
- {
- struct TALER_MERCHANT_StatisticsCounterGetResponse gsr = {
- .hr.http_status = MHD_HTTP_OK,
- .hr.reply = json,
- .details.ok.buckets_length = resp_buckets_len,
- .details.ok.buckets = resp_buckets,
- .details.ok.buckets_description = buckets_description,
- .details.ok.intervals_length = resp_intervals_len,
- .details.ok.intervals = resp_intervals,
- .details.ok.intervals_description = intervals_description,
- };
- scgh->cb (scgh->cb_cls,
- &gsr);
- scgh->cb = NULL; /* just to be sure */
- }
- return ret;
- }
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP GET /statistics-counter/$SLUG request.
- *
- * @param cls the `struct TALER_MERCHANT_StatisticsCounterGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_statistics_counter_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_StatisticsCounterGetHandle *handle = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_StatisticsCounterGetResponse res = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- handle->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /statistics-counter/$SLUG response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *buckets;
- const json_t *intervals;
- const char *buckets_description;
- const char *intervals_description;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("buckets",
- &buckets),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("buckets_description",
- &buckets_description),
- NULL),
- GNUNET_JSON_spec_array_const ("intervals",
- &intervals),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("intervals_description",
- &intervals_description),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- res.hr.http_status = 0;
- res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "%s\n", json_dumps (json, JSON_INDENT (1)));
- if (GNUNET_OK ==
- parse_intervals_and_buckets (json,
- buckets,
- buckets_description,
- intervals,
- intervals_description,
- handle))
- {
- TALER_MERCHANT_statistic_counter_get_cancel (handle);
- return;
- }
- res.hr.http_status = 0;
- res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.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) res.hr.ec);
- break;
- }
-}
-
-
-struct TALER_MERCHANT_StatisticsCounterGetHandle *
-TALER_MERCHANT_statistic_counter_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *slug,
- enum TALER_MERCHANT_StatisticsType stype,
- TALER_MERCHANT_StatisticsCounterGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_StatisticsCounterGetHandle *handle;
- CURL *eh;
-
- handle = GNUNET_new (struct TALER_MERCHANT_StatisticsCounterGetHandle);
- handle->ctx = ctx;
- handle->cb = cb;
- handle->cb_cls = cb_cls;
- {
- const char *filter = NULL;
- char *path;
-
- switch (stype)
- {
- case TALER_MERCHANT_STATISTICS_BY_BUCKET:
- filter = "bucket";
- break;
- case TALER_MERCHANT_STATISTICS_BY_INTERVAL:
- filter = "interval";
- break;
- case TALER_MERCHANT_STATISTICS_ALL:
- filter = NULL;
- break;
- }
- GNUNET_asprintf (&path,
- "private/statistics-counter/%s",
- slug);
- handle->url = TALER_url_join (backend_url,
- path,
- "by",
- filter,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == handle->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (handle);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- handle->url);
- eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
- handle->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_statistics_counter_finished,
- handle);
- return handle;
-}
-
-
-void
-TALER_MERCHANT_statistic_counter_get_cancel (
- struct TALER_MERCHANT_StatisticsCounterGetHandle *handle)
-{
- if (NULL != handle->job)
- GNUNET_CURL_job_cancel (handle->job);
- GNUNET_free (handle->url);
- GNUNET_free (handle);
-}
-
-
-struct TALER_MERCHANT_StatisticsAmountGetHandle *
-TALER_MERCHANT_statistic_amount_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *slug,
- enum TALER_MERCHANT_StatisticsType stype,
- TALER_MERCHANT_StatisticsAmountGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_StatisticsAmountGetHandle *handle;
- CURL *eh;
-
- handle = GNUNET_new (struct TALER_MERCHANT_StatisticsAmountGetHandle);
- handle->ctx = ctx;
- handle->cb = cb;
- handle->cb_cls = cb_cls;
- {
- const char *filter = NULL;
- char *path;
-
- switch (stype)
- {
- case TALER_MERCHANT_STATISTICS_BY_BUCKET:
- filter = "bucket";
- break;
- case TALER_MERCHANT_STATISTICS_BY_INTERVAL:
- filter = "interval";
- break;
- case TALER_MERCHANT_STATISTICS_ALL:
- filter = NULL;
- break;
- }
- GNUNET_asprintf (&path,
- "private/statistics-amount/%s",
- slug);
- handle->url = TALER_url_join (backend_url,
- path,
- "by",
- filter,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == handle->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (handle);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- handle->url);
- eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
- handle->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_statistics_amount_finished,
- handle);
- return handle;
-}
-
-
-void
-TALER_MERCHANT_statistic_amount_get_cancel (
- struct TALER_MERCHANT_StatisticsAmountGetHandle *handle)
-{
- if (NULL != handle->job)
- GNUNET_CURL_job_cancel (handle->job);
- GNUNET_free (handle->url);
- GNUNET_free (handle);
-}
diff --git a/src/lib/merchant_api_get_template.c b/src/lib/merchant_api_get_template.c
@@ -1,201 +0,0 @@
-/*
- 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_get_template.c
- * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/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/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_TemplateGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_TemplateGetCallback 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_TemplateGetHandle *tgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_TemplateGetResponse 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_string ("template_description",
- &tgr.details.ok.template_description),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("otp_id",
- &tgr.details.ok.otp_id),
- NULL),
- 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_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_template_get_cancel (tgh);
-}
-
-
-struct TALER_MERCHANT_TemplateGetHandle *
-TALER_MERCHANT_template_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *template_id,
- TALER_MERCHANT_TemplateGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_TemplateGetHandle *tgh;
- CURL *eh;
-
- tgh = GNUNET_new (struct TALER_MERCHANT_TemplateGetHandle);
- tgh->ctx = ctx;
- tgh->cb = cb;
- tgh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/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_template_get_cancel (
- struct TALER_MERCHANT_TemplateGetHandle *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_templates.c b/src/lib/merchant_api_get_templates.c
@@ -1,247 +0,0 @@
-/*
- 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_get_templates.c
- * @brief Implementation of the GET /templates request of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Maximum number of templates we return.
- */
-#define MAX_TEMPLATES 1024
-
-
-/**
- * Handle for a GET /templates operation.
- */
-struct TALER_MERCHANT_TemplatesGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_TemplatesGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse template information from @a ia.
- *
- * @param ia JSON array (or NULL!) with template data
- * @param[in] tgr partially filled response
- * @param tgh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_templates (const json_t *ia,
- struct TALER_MERCHANT_TemplatesGetResponse *tgr,
- struct TALER_MERCHANT_TemplatesGetHandle *tgh)
-{
- unsigned int tmpl_len = (unsigned int) json_array_size (ia);
-
- if ( (json_array_size (ia) != (size_t) tmpl_len) ||
- (tmpl_len > MAX_TEMPLATES) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_TemplateEntry tmpl[GNUNET_NZL (tmpl_len)];
- size_t index;
- json_t *value;
-
- json_array_foreach (ia, index, value) {
- struct TALER_MERCHANT_TemplateEntry *ie = &tmpl[index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("template_id",
- &ie->template_id),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- tgr->details.ok.templates_length = tmpl_len;
- tgr->details.ok.templates = tmpl;
- 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 /templates request.
- *
- * @param cls the `struct TALER_MERCHANT_TemplatesGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_templates_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_TemplatesGetHandle *tgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_TemplatesGetResponse tgr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- tgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /templates response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *templates;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("templates",
- &templates),
- 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_templates (templates,
- &tgr,
- tgh))
- {
- TALER_MERCHANT_templates_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_templates_get_cancel (tgh);
-}
-
-
-struct TALER_MERCHANT_TemplatesGetHandle *
-TALER_MERCHANT_templates_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_TemplatesGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_TemplatesGetHandle *tgh;
- CURL *eh;
-
- tgh = GNUNET_new (struct TALER_MERCHANT_TemplatesGetHandle);
- tgh->ctx = ctx;
- tgh->cb = cb;
- tgh->cb_cls = cb_cls;
- tgh->url = TALER_url_join (backend_url,
- "private/templates",
- 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_templates_finished,
- tgh);
- return tgh;
-}
-
-
-void
-TALER_MERCHANT_templates_get_cancel (
- struct TALER_MERCHANT_TemplatesGetHandle *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_tokenfamily.c b/src/lib/merchant_api_get_tokenfamily.c
@@ -1,218 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2023-2024 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_get_tokenfamily.c
- * @brief Implementation of the GET /tokenfamily/$ID request of the merchant's HTTP API
- * @author Christian Blättler
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <gnunet/gnunet_common.h>
-#include <gnunet/gnunet_json_lib.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/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 /tokenfamilies/$SLUG operation.
- */
-struct TALER_MERCHANT_TokenFamilyGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_TokenFamilyGetCallback 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 /tokenfamilies/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_TokenFamilyGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_token_family_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_TokenFamilyGetHandle *handle = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_TokenFamilyGetResponse res = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- handle->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /tokenfamilies/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- // Parse token family response
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("slug",
- &res.details.ok.slug),
- GNUNET_JSON_spec_string ("name",
- &res.details.ok.name),
- GNUNET_JSON_spec_string ("description",
- &res.details.ok.description),
- GNUNET_JSON_spec_object_const ("description_i18n",
- &res.details.ok.description_i18n),
- GNUNET_JSON_spec_object_const ("extra_data",
- &res.details.ok.extra_data),
- GNUNET_JSON_spec_timestamp ("valid_after",
- &res.details.ok.valid_after),
- GNUNET_JSON_spec_timestamp ("valid_before",
- &res.details.ok.valid_before),
- GNUNET_JSON_spec_relative_time ("duation",
- &res.details.ok.duration),
- GNUNET_JSON_spec_relative_time ("validity_granularity",
- &res.details.ok.validity_granularity),
- GNUNET_JSON_spec_relative_time ("start_offset",
- &res.details.ok.start_offset),
- GNUNET_JSON_spec_string ("kind",
- &res.details.ok.kind),
- GNUNET_JSON_spec_uint64 ("issued",
- &res.details.ok.issued),
- GNUNET_JSON_spec_uint64 ("used",
- &res.details.ok.used),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK ==
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- handle->cb (handle->cb_cls,
- &res);
- GNUNET_JSON_parse_free (spec);
- TALER_MERCHANT_token_family_get_cancel (handle);
- return;
- }
- res.hr.http_status = 0;
- res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- res.hr.ec = TALER_JSON_get_error_code (json);
- res.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) res.hr.ec);
- break;
- }
-}
-
-
-struct TALER_MERCHANT_TokenFamilyGetHandle *
-TALER_MERCHANT_token_family_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *token_family_slug,
- TALER_MERCHANT_TokenFamilyGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_TokenFamilyGetHandle *handle;
- CURL *eh;
-
- handle = GNUNET_new (struct TALER_MERCHANT_TokenFamilyGetHandle);
- handle->ctx = ctx;
- handle->cb = cb;
- handle->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/tokenfamilies/%s",
- token_family_slug);
- handle->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == handle->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (handle);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- handle->url);
- eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
- handle->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_token_family_finished,
- handle);
- return handle;
-}
-
-
-void
-TALER_MERCHANT_token_family_get_cancel (
- struct TALER_MERCHANT_TokenFamilyGetHandle *handle)
-{
- if (NULL != handle->job)
- GNUNET_CURL_job_cancel (handle->job);
- GNUNET_free (handle->url);
- GNUNET_free (handle);
-}
diff --git a/src/lib/merchant_api_get_transfers.c b/src/lib/merchant_api_get_transfers.c
@@ -1,314 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_get_transfers.c
- * @brief Implementation of the GET /transfers request of the merchant's HTTP API
- * @author Marcello Stanisci
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * @brief A Handle for tracking wire transfers.
- */
-struct TALER_MERCHANT_GetTransfersHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_GetTransfersCallback 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 /transfers request.
- *
- * @param cls the `struct TALER_MERCHANT_GetTransfersHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_transfers_get_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_GetTransfersHandle *gth = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_GetTransfersResponse gtr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- gth->job = NULL;
- switch (response_code)
- {
- case 0:
- gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- {
- const json_t *transfers;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("transfers",
- &transfers),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- gtr.hr.http_status = 0;
- gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
-
- {
- size_t tds_length;
- struct TALER_MERCHANT_TransferData *tds;
- json_t *transfer;
- size_t i;
- bool ok;
-
- tds_length = json_array_size (transfers);
- tds = GNUNET_new_array (tds_length,
- struct TALER_MERCHANT_TransferData);
- ok = true;
- json_array_foreach (transfers, i, transfer) {
- struct TALER_MERCHANT_TransferData *td = &tds[i];
- struct GNUNET_JSON_Specification ispec[] = {
- TALER_JSON_spec_amount_any ("credit_amount",
- &td->credit_amount),
- GNUNET_JSON_spec_fixed_auto ("wtid",
- &td->wtid),
- TALER_JSON_spec_full_payto_uri ("payto_uri",
- &td->payto_uri),
- TALER_JSON_spec_web_url ("exchange_url",
- &td->exchange_url),
- GNUNET_JSON_spec_uint64 ("transfer_serial_id",
- &td->credit_serial),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_timestamp ("execution_time",
- &td->execution_time),
- NULL),
- GNUNET_JSON_spec_bool ("expected",
- &td->expected),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (transfer,
- ispec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ok = false;
- break;
- }
- }
-
- if (! ok)
- {
- GNUNET_break_op (0);
- GNUNET_free (tds);
- gtr.hr.http_status = 0;
- gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- gtr.details.ok.transfers = tds;
- gtr.details.ok.transfers_length = tds_length;
- gth->cb (gth->cb_cls,
- >r);
- GNUNET_free (tds);
- TALER_MERCHANT_transfers_get_cancel (gth);
- return;
- }
- }
- case MHD_HTTP_UNAUTHORIZED:
- gtr.hr.ec = TALER_JSON_get_error_code (json);
- gtr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the application */
- gtr.hr.ec = TALER_JSON_get_error_code (json);
- gtr.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 */
- gtr.hr.ec = TALER_JSON_get_error_code (json);
- gtr.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,
- >r.hr);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) gtr.hr.ec);
- gtr.hr.http_status = 0;
- break;
- }
- gth->cb (gth->cb_cls,
- >r);
- TALER_MERCHANT_transfers_get_cancel (gth);
-}
-
-
-struct TALER_MERCHANT_GetTransfersHandle *
-TALER_MERCHANT_transfers_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- struct TALER_FullPayto payto_uri,
- const struct GNUNET_TIME_Timestamp before,
- const struct GNUNET_TIME_Timestamp after,
- int64_t limit,
- uint64_t offset,
- enum TALER_EXCHANGE_YesNoAll expected,
- TALER_MERCHANT_GetTransfersCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_GetTransfersHandle *gth;
- CURL *eh;
- const char *expected_s = NULL;
- char limit_s[30];
- char offset_s[30];
- char before_s[30];
- char after_s[30];
-
- gth = GNUNET_new (struct TALER_MERCHANT_GetTransfersHandle);
- gth->ctx = ctx;
- gth->cb = cb;
- gth->cb_cls = cb_cls;
- expected_s = TALER_yna_to_string (expected);
- GNUNET_snprintf (limit_s,
- sizeof (limit_s),
- "%lld",
- (long long) limit);
- GNUNET_snprintf (offset_s,
- sizeof (offset_s),
- "%lld",
- (unsigned long long) offset);
- GNUNET_snprintf (before_s,
- sizeof (before_s),
- "%llu",
- (unsigned long long) GNUNET_TIME_timestamp_to_s (before));
- GNUNET_snprintf (after_s,
- sizeof (after_s),
- "%llu",
- (unsigned long long) GNUNET_TIME_timestamp_to_s (after));
- {
- char *enc_payto = TALER_urlencode (payto_uri.full_payto);
-
- gth->url = TALER_url_join (backend_url,
- "private/transfers",
- "payto_uri",
- enc_payto,
- "expected",
- (TALER_EXCHANGE_YNA_ALL != expected)
- ? expected_s
- : NULL,
- "limit",
- 0 != limit
- ? limit_s
- : NULL,
- "offset",
- ((0 != offset) && (UINT64_MAX != offset))
- ? offset_s
- : NULL,
- "before",
- GNUNET_TIME_absolute_is_never (before.abs_time)
- ? NULL
- : before_s,
- "after",
- GNUNET_TIME_absolute_is_zero (after.abs_time)
- ? NULL
- : after_s,
- NULL);
- GNUNET_free (enc_payto);
- }
- if (NULL == gth->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (gth);
- return NULL;
- }
- eh = TALER_MERCHANT_curl_easy_get_ (gth->url);
- gth->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_transfers_get_finished,
- gth);
- return gth;
-}
-
-
-void
-TALER_MERCHANT_transfers_get_cancel (
- struct TALER_MERCHANT_GetTransfersHandle *gth)
-{
- if (NULL != gth->job)
- {
- GNUNET_CURL_job_cancel (gth->job);
- gth->job = NULL;
- }
- GNUNET_free (gth->url);
- GNUNET_free (gth);
-}
-
-
-/* end of merchant_api_get_transfers.c */
diff --git a/src/lib/merchant_api_get_unit.c b/src/lib/merchant_api_get_unit.c
@@ -1,249 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_get_unit.c
- * @brief Implementation of GET /private/units/$ID
- * @author Bohdan Potuzhnyi
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-
-
-/**
- * Handle for a GET /private/units/$ID operation.
- */
-struct TALER_MERCHANT_UnitGetHandle
-{
- /**
- * Fully qualified request URL.
- */
- char *url;
-
- /**
- * In-flight job handle.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Callback to invoke with the response.
- */
- TALER_MERCHANT_UnitGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-};
-
-
-/**
- * Parse the JSON response into @a ugr.
- *
- * @param json full JSON reply
- * @param ugr response descriptor to populate
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_unit (const json_t *json,
- struct TALER_MERCHANT_UnitGetResponse *ugr)
-{
- struct TALER_MERCHANT_UnitEntry *entry = &ugr->details.ok.unit;
- const char *unit;
- const char *unit_name_long;
- const char *unit_name_short;
- const json_t *unit_name_long_i18n = NULL;
- const json_t *unit_name_short_i18n = NULL;
- bool unit_allow_fraction;
- bool unit_active;
- bool unit_builtin;
- uint32_t unit_precision_level;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("unit",
- &unit),
- GNUNET_JSON_spec_string ("unit_name_long",
- &unit_name_long),
- GNUNET_JSON_spec_string ("unit_name_short",
- &unit_name_short),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const ("unit_name_long_i18n",
- &unit_name_long_i18n),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const ("unit_name_short_i18n",
- &unit_name_short_i18n),
- NULL),
- GNUNET_JSON_spec_bool ("unit_allow_fraction",
- &unit_allow_fraction),
- GNUNET_JSON_spec_uint32 ("unit_precision_level",
- &unit_precision_level),
- GNUNET_JSON_spec_bool ("unit_active",
- &unit_active),
- GNUNET_JSON_spec_bool ("unit_builtin",
- &unit_builtin),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return GNUNET_SYSERR;
- }
- GNUNET_JSON_parse_free (spec);
- entry->unit = unit;
- entry->unit_name_long = unit_name_long;
- entry->unit_name_short = unit_name_short;
- entry->unit_name_long_i18n = unit_name_long_i18n;
- entry->unit_name_short_i18n = unit_name_short_i18n;
- entry->unit_allow_fraction = unit_allow_fraction;
- entry->unit_precision_level = unit_precision_level;
- entry->unit_active = unit_active;
- entry->unit_builtin = unit_builtin;
- return GNUNET_OK;
-}
-
-
-/**
- * Called once the HTTP request completes.
- *
- * @param cls operation handle
- * @param response_code HTTP status (0 on client-side errors)
- * @param response parsed JSON reply (NULL if parsing failed)
- */
-static void
-handle_get_unit_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_UnitGetHandle *ugh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_UnitGetResponse ugr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- ugh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "GET /private/units/$ID finished with status %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- if (GNUNET_OK !=
- parse_unit (json,
- &ugr))
- {
- ugr.hr.http_status = 0;
- ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- ugh->cb (ugh->cb_cls,
- &ugr);
- TALER_MERCHANT_unit_get_cancel (ugh);
- return;
- case MHD_HTTP_UNAUTHORIZED:
- case MHD_HTTP_FORBIDDEN:
- case MHD_HTTP_NOT_FOUND:
- ugr.hr.ec = TALER_JSON_get_error_code (json);
- ugr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case 0:
- ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- default:
- ugr.hr.ec = TALER_JSON_get_error_code (json);
- ugr.hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unexpected response %u/%d for GET /private/units/$ID\n",
- (unsigned int) response_code,
- (int) ugr.hr.ec);
- break;
- }
- ugh->cb (ugh->cb_cls,
- &ugr);
- TALER_MERCHANT_unit_get_cancel (ugh);
-}
-
-
-struct TALER_MERCHANT_UnitGetHandle *
-TALER_MERCHANT_unit_get (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *unit_id,
- TALER_MERCHANT_UnitGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_UnitGetHandle *ugh;
- CURL *eh;
- char *path;
-
- GNUNET_asprintf (&path,
- "private/units/%s",
- unit_id);
- ugh = GNUNET_new (struct TALER_MERCHANT_UnitGetHandle);
- ugh->ctx = ctx;
- ugh->cb = cb;
- ugh->cb_cls = cb_cls;
- ugh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- if (NULL == ugh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to build /private/units/%s URL\n",
- unit_id);
- GNUNET_free (ugh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- ugh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (ugh->url);
- ugh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_unit_finished,
- ugh);
- return ugh;
-}
-
-
-void
-TALER_MERCHANT_unit_get_cancel (struct TALER_MERCHANT_UnitGetHandle *ugh)
-{
- if (NULL != ugh->job)
- GNUNET_CURL_job_cancel (ugh->job);
- GNUNET_free (ugh->url);
- GNUNET_free (ugh);
-}
-
-
-/* end of merchant_api_get_unit.c */
diff --git a/src/lib/merchant_api_get_units.c b/src/lib/merchant_api_get_units.c
@@ -1,329 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_get_units.c
- * @brief Implementation of GET /private/units
- * @author Bohdan Potuzhnyi
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-
-
-/**
- * Maximum number of units returned in a single response.
- */
-#define MAX_UNITS 1024
-
-
-/**
- * Handle for a GET /private/units operation.
- */
-struct TALER_MERCHANT_UnitsGetHandle
-{
- /**
- * Fully qualified request URL.
- */
- char *url;
-
- /**
- * In-flight job handle.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Callback to invoke with the outcome.
- */
- TALER_MERCHANT_UnitsGetCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-};
-
-
-/**
- * Parse an individual unit entry from @a value.
- *
- * @param value JSON object describing the unit
- * @param[out] ue set to the parsed values
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_unit_entry (const json_t *value,
- struct TALER_MERCHANT_UnitEntry *ue)
-{
- const char *unit;
- const char *unit_name_long;
- const char *unit_name_short;
- const json_t *unit_name_long_i18n = NULL;
- const json_t *unit_name_short_i18n = NULL;
- bool unit_allow_fraction;
- bool unit_active;
- bool unit_builtin;
- uint32_t unit_precision_level;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("unit",
- &unit),
- GNUNET_JSON_spec_string ("unit_name_long",
- &unit_name_long),
- GNUNET_JSON_spec_string ("unit_name_short",
- &unit_name_short),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const ("unit_name_long_i18n",
- &unit_name_long_i18n),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const ("unit_name_short_i18n",
- &unit_name_short_i18n),
- NULL),
- GNUNET_JSON_spec_bool ("unit_allow_fraction",
- &unit_allow_fraction),
- GNUNET_JSON_spec_uint32 ("unit_precision_level",
- &unit_precision_level),
- GNUNET_JSON_spec_bool ("unit_active",
- &unit_active),
- GNUNET_JSON_spec_bool ("unit_builtin",
- &unit_builtin),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return GNUNET_SYSERR;
- }
- GNUNET_JSON_parse_free (spec);
- ue->unit = unit;
- ue->unit_name_long = unit_name_long;
- ue->unit_name_short = unit_name_short;
- ue->unit_name_long_i18n = unit_name_long_i18n;
- ue->unit_name_short_i18n = unit_name_short_i18n;
- ue->unit_allow_fraction = unit_allow_fraction;
- ue->unit_precision_level = unit_precision_level;
- ue->unit_active = unit_active;
- ue->unit_builtin = unit_builtin;
- return GNUNET_OK;
-}
-
-
-/**
- * Parse the list of units from @a units and call the callback.
- *
- * @param json complete response JSON
- * @param units array of units
- * @param ugh ongoing operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_units (const json_t *json,
- const json_t *units,
- struct TALER_MERCHANT_UnitsGetHandle *ugh)
-{
- size_t len;
-
- len = json_array_size (units);
- if (len > MAX_UNITS)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- {
- struct TALER_MERCHANT_UnitEntry entries[GNUNET_NZL (len)];
- size_t idx;
- json_t *value;
-
- json_array_foreach (units, idx, value) {
- if (GNUNET_OK !=
- parse_unit_entry (value,
- &entries[idx]))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- {
- struct TALER_MERCHANT_UnitsGetResponse ugr = {
- .hr.http_status = MHD_HTTP_OK,
- .hr.reply = json,
- .details = {
- .ok = {
- .units = entries,
- .units_length = (unsigned int) len
- }
-
-
- }
-
-
- };
-
- ugh->cb (ugh->cb_cls,
- &ugr);
- }
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Called when the HTTP transfer finishes.
- *
- * @param cls closure, the operation handle
- * @param response_code HTTP status (0 on network errors)
- * @param response parsed JSON body (NULL if parsing failed)
- */
-static void
-handle_get_units_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_UnitsGetHandle *ugh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_UnitsGetResponse ugr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- ugh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "GET /private/units finished with status %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *units;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("units",
- &units),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- ugr.hr.http_status = 0;
- ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- if (GNUNET_OK ==
- parse_units (json,
- units,
- ugh))
- {
- TALER_MERCHANT_units_get_cancel (ugh);
- return;
- }
- ugr.hr.http_status = 0;
- ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- case MHD_HTTP_FORBIDDEN:
- case MHD_HTTP_NOT_FOUND:
- case MHD_HTTP_CONFLICT:
- ugr.hr.ec = TALER_JSON_get_error_code (json);
- ugr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case 0:
- ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- default:
- ugr.hr.ec = TALER_JSON_get_error_code (json);
- ugr.hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unexpected response code %u/%d for GET /private/units\n",
- (unsigned int) response_code,
- (int) ugr.hr.ec);
- break;
- }
- ugh->cb (ugh->cb_cls,
- &ugr);
- TALER_MERCHANT_units_get_cancel (ugh);
-}
-
-
-struct TALER_MERCHANT_UnitsGetHandle *
-TALER_MERCHANT_units_get (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_UnitsGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_UnitsGetHandle *ugh;
- CURL *eh;
-
- ugh = GNUNET_new (struct TALER_MERCHANT_UnitsGetHandle);
- ugh->ctx = ctx;
- ugh->cb = cb;
- ugh->cb_cls = cb_cls;
- ugh->url = TALER_url_join (backend_url,
- "private/units",
- NULL);
- if (NULL == ugh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to build /private/units URL\n");
- GNUNET_free (ugh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- ugh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (ugh->url);
- ugh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_units_finished,
- ugh);
- return ugh;
-}
-
-
-void
-TALER_MERCHANT_units_get_cancel (struct TALER_MERCHANT_UnitsGetHandle *ugh)
-{
- if (NULL != ugh->job)
- GNUNET_CURL_job_cancel (ugh->job);
- GNUNET_free (ugh->url);
- GNUNET_free (ugh);
-}
-
-
-/* end of merchant_api_get_units.c */
diff --git a/src/lib/merchant_api_get_webhook.c b/src/lib/merchant_api_get_webhook.c
@@ -1,221 +0,0 @@
-/*
- 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_get_webhook.c
- * @brief Implementation of the GET /webhooks/$ID request of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/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/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 /webhooks/$ID operation.
- */
-struct TALER_MERCHANT_WebhookGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_WebhookGetCallback 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 /webhooks/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_WebhookGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_webhook_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_WebhookGetHandle *wgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- wgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /webhooks/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const char *event_type;
- const char *url;
- const char *http_method;
- const char *header_template;
- const char *body_template;
- bool rst_ok = true;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("event_type",
- &event_type),
- TALER_JSON_spec_web_url ("url",
- &url),
- GNUNET_JSON_spec_string ("http_method",
- &http_method),
- GNUNET_JSON_spec_string ("header_template",
- &header_template),
- GNUNET_JSON_spec_string ("body_template",
- &body_template),
- GNUNET_JSON_spec_end ()
- };
-
-
- if ( (rst_ok) &&
- (GNUNET_OK ==
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL)) )
- {
- wgh->cb (wgh->cb_cls,
- &hr,
- event_type,
- url,
- http_method,
- header_template,
- body_template);
- GNUNET_JSON_parse_free (spec);
- TALER_MERCHANT_webhook_get_cancel (wgh);
- return;
- }
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- GNUNET_JSON_parse_free (spec);
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- 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) hr.ec);
- break;
- }
- wgh->cb (wgh->cb_cls,
- &hr,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL);
- TALER_MERCHANT_webhook_get_cancel (wgh);
-}
-
-
-struct TALER_MERCHANT_WebhookGetHandle *
-TALER_MERCHANT_webhook_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *webhook_id,
- TALER_MERCHANT_WebhookGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_WebhookGetHandle *wgh;
- CURL *eh;
-
- wgh = GNUNET_new (struct TALER_MERCHANT_WebhookGetHandle);
- wgh->ctx = ctx;
- wgh->cb = cb;
- wgh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/webhooks/%s",
- webhook_id);
- wgh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == wgh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (wgh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- wgh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (wgh->url);
- wgh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_webhook_finished,
- wgh);
- return wgh;
-}
-
-
-void
-TALER_MERCHANT_webhook_get_cancel (
- struct TALER_MERCHANT_WebhookGetHandle *wgh)
-{
- if (NULL != wgh->job)
- GNUNET_CURL_job_cancel (wgh->job);
- GNUNET_free (wgh->url);
- GNUNET_free (wgh);
-}
diff --git a/src/lib/merchant_api_get_webhooks.c b/src/lib/merchant_api_get_webhooks.c
@@ -1,246 +0,0 @@
-/*
- 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_get_webhooks.c
- * @brief Implementation of the GET /webhooks request of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Maximum number of webhooks we return.
- */
-#define MAX_WEBHOOKS 1024
-
-/**
- * Handle for a GET /webhooks operation.
- */
-struct TALER_MERCHANT_WebhooksGetHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_WebhooksGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
-};
-
-
-/**
- * Parse webhook information from @a ia.
- *
- * @param ia JSON array (or NULL!) with webhook data
- * @param[in] wgr partially filled webhook response
- * @param wgh operation handle
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_webhooks (const json_t *ia,
- struct TALER_MERCHANT_WebhooksGetResponse *wgr,
- struct TALER_MERCHANT_WebhooksGetHandle *wgh)
-{
- unsigned int whook_len = (unsigned int) json_array_size (ia);
-
- if ( (json_array_size (ia) != (size_t) whook_len) ||
- (whook_len > MAX_WEBHOOKS) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_MERCHANT_WebhookEntry whook[GNUNET_NZL (whook_len)];
- size_t index;
- json_t *value;
-
- json_array_foreach (ia, index, value) {
- struct TALER_MERCHANT_WebhookEntry *ie = &whook[index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("webhook_id",
- &ie->webhook_id),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (value,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- wgr->details.ok.webhooks_length = whook_len;
- wgr->details.ok.webhooks = whook;
- wgh->cb (wgh->cb_cls,
- wgr);
- wgh->cb = NULL; /* just to be sure */
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /webhooks request.
- *
- * @param cls the `struct TALER_MERCHANT_WebhooksGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_get_webhooks_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_WebhooksGetHandle *wgh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_WebhooksGetResponse wgr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- wgh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /webhooks response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const json_t *webhooks;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("webhooks",
- &webhooks),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- wgr.hr.http_status = 0;
- wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- if (GNUNET_OK ==
- parse_webhooks (webhooks,
- &wgr,
- wgh))
- {
- TALER_MERCHANT_webhooks_get_cancel (wgh);
- return;
- }
- wgr.hr.http_status = 0;
- wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- wgr.hr.ec = TALER_JSON_get_error_code (json);
- wgr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- default:
- /* unexpected response code */
- wgr.hr.ec = TALER_JSON_get_error_code (json);
- wgr.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) wgr.hr.ec);
- break;
- }
- wgh->cb (wgh->cb_cls,
- &wgr);
- TALER_MERCHANT_webhooks_get_cancel (wgh);
-}
-
-
-struct TALER_MERCHANT_WebhooksGetHandle *
-TALER_MERCHANT_webhooks_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- TALER_MERCHANT_WebhooksGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_WebhooksGetHandle *wgh;
- CURL *eh;
-
- wgh = GNUNET_new (struct TALER_MERCHANT_WebhooksGetHandle);
- wgh->ctx = ctx;
- wgh->cb = cb;
- wgh->cb_cls = cb_cls;
- wgh->url = TALER_url_join (backend_url,
- "private/webhooks",
- NULL);
- if (NULL == wgh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (wgh);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- wgh->url);
- eh = TALER_MERCHANT_curl_easy_get_ (wgh->url);
- wgh->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_get_webhooks_finished,
- wgh);
- return wgh;
-}
-
-
-void
-TALER_MERCHANT_webhooks_get_cancel (
- struct TALER_MERCHANT_WebhooksGetHandle *wgh)
-{
- if (NULL != wgh->job)
- GNUNET_CURL_job_cancel (wgh->job);
- GNUNET_free (wgh->url);
- GNUNET_free (wgh);
-}
diff --git a/src/lib/merchant_api_lock_product.c b/src/lib/merchant_api_lock_product.c
@@ -1,279 +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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_lock_product.c
- * @brief Implementation of the POST /products/$ID/lock request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /products/$ID/lock operation.
- */
-struct TALER_MERCHANT_ProductLockHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ProductLockCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /products/$ID/lock request.
- *
- * @param cls the `struct TALER_MERCHANT_ProductLockHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_lock_product_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_ProductLockHandle *plh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- plh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "LOCK /products/$ID completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_break_op (0);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_GONE:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- plh->cb (plh->cb_cls,
- &hr);
- TALER_MERCHANT_product_lock_cancel (plh);
-}
-
-
-struct TALER_MERCHANT_ProductLockHandle *
-TALER_MERCHANT_product_lock2 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- const char *uuid,
- struct GNUNET_TIME_Relative duration,
- uint64_t quantity,
- uint32_t quantity_frac,
- bool use_fractional_quantity,
- TALER_MERCHANT_ProductLockCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_ProductLockHandle *plh;
- json_t *req_obj;
- char unit_quantity_buf[64];
-
- TALER_MERCHANT_format_quantity_string (quantity,
- quantity_frac,
- unit_quantity_buf,
- sizeof (unit_quantity_buf));
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("lock_uuid",
- uuid),
- GNUNET_JSON_pack_time_rel ("duration",
- duration),
- GNUNET_JSON_pack_string ("unit_quantity",
- unit_quantity_buf));
- (void) use_fractional_quantity;
- GNUNET_assert ( (0 == quantity_frac) || use_fractional_quantity);
- plh = GNUNET_new (struct TALER_MERCHANT_ProductLockHandle);
- plh->ctx = ctx;
- plh->cb = cb;
- plh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/products/%s/lock",
- product_id);
- plh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == plh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (plh);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (plh->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&plh->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (plh->url);
- GNUNET_free (plh);
- return NULL;
- }
- json_decref (req_obj);
- plh->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- plh->post_ctx.headers,
- &handle_lock_product_finished,
- plh);
- }
- return plh;
-}
-
-
-void
-TALER_MERCHANT_product_lock_cancel (
- struct TALER_MERCHANT_ProductLockHandle *plh)
-{
- if (NULL != plh->job)
- {
- GNUNET_CURL_job_cancel (plh->job);
- plh->job = NULL;
- }
- TALER_curl_easy_post_finished (&plh->post_ctx);
- GNUNET_free (plh->url);
- GNUNET_free (plh);
-}
-
-
-struct TALER_MERCHANT_ProductLockHandle *
-TALER_MERCHANT_product_lock (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- const char *uuid,
- struct GNUNET_TIME_Relative duration,
- uint32_t quantity,
- TALER_MERCHANT_ProductLockCallback cb,
- void *cb_cls)
-{
- return TALER_MERCHANT_product_lock2 (ctx,
- backend_url,
- product_id,
- uuid,
- duration,
- quantity,
- 0,
- false,
- cb,
- cb_cls);
-}
-
-
-/* end of merchant_api_lock_product.c */
diff --git a/src/lib/merchant_api_merchant_get_order.c b/src/lib/merchant_api_merchant_get_order.c
@@ -1,538 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2018, 2019, 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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_merchant_get_order.c
- * @brief Implementation of the GET /private/orders/$ORDER request
- * @author Christian Grothoff
- * @author Marcello Stanisci
- * @author Florian Dold
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * Maximum number of refund details we return.
- */
-#define MAX_REFUND_DETAILS 1024
-
-/**
- * Maximum number of wire details we return.
- */
-#define MAX_WIRE_DETAILS 1024
-
-
-/**
- * @brief A GET /private/orders/$ORDER handle
- */
-struct TALER_MERCHANT_OrderMerchantGetHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OrderMerchantGetCallback 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 GET /private/orders/$ORDER
- * request and we got an HTTP status of OK and the order was unpaid. Parse
- * the response and call the callback.
- *
- * @param omgh handle for the request
- * @param[in,out] osr HTTP response we got
- */
-static void
-handle_unpaid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh,
- struct TALER_MERCHANT_OrderStatusResponse *osr)
-{
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_amount_any (
- "total_amount",
- &osr->details.ok.details.unpaid.contract_amount),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string (
- "already_paid_order_id",
- &osr->details.ok.details.unpaid.already_paid_order_id),
- NULL),
- GNUNET_JSON_spec_string (
- "taler_pay_uri",
- &osr->details.ok.details.unpaid.taler_pay_uri),
- GNUNET_JSON_spec_string (
- "summary",
- &osr->details.ok.details.unpaid.summary),
- GNUNET_JSON_spec_timestamp (
- "creation_time",
- &osr->details.ok.details.unpaid.creation_time),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (osr->hr.reply,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- osr->hr.http_status = 0;
- osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- omgh->cb (omgh->cb_cls,
- osr);
- return;
- }
- osr->details.ok.status = TALER_MERCHANT_OSC_UNPAID;
- omgh->cb (omgh->cb_cls,
- osr);
-}
-
-
-/**
- * Function called when we're done processing the GET /private/orders/$ORDER
- * request and we got an HTTP status of OK and the order was claimed but not
- * paid. Parse the response and call the callback.
- *
- * @param omgh handle for the request
- * @param[in,out] osr HTTP response we got
- */
-static void
-handle_claimed (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh,
- struct TALER_MERCHANT_OrderStatusResponse *osr)
-{
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_object_const (
- "contract_terms",
- &osr->details.ok.details.claimed.contract_terms),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (osr->hr.reply,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- osr->hr.http_status = 0;
- osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- omgh->cb (omgh->cb_cls,
- osr);
- return;
- }
- osr->details.ok.status = TALER_MERCHANT_OSC_CLAIMED;
- omgh->cb (omgh->cb_cls,
- osr);
-}
-
-
-/**
- * Function called when we're done processing the GET /private/orders/$ORDER
- * request and we got an HTTP status of OK and the order was paid. Parse
- * the response and call the callback.
- *
- * @param omgh handle for the request
- * @param[in,out] osr HTTP response we got
- */
-static void
-handle_paid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh,
- struct TALER_MERCHANT_OrderStatusResponse *osr)
-{
- uint32_t hc32;
- const json_t *wire_details;
- const json_t *refund_details;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_bool ("refunded",
- &osr->details.ok.details.paid.refunded),
- GNUNET_JSON_spec_bool ("refund_pending",
- &osr->details.ok.details.paid.refund_pending),
- GNUNET_JSON_spec_bool ("wired",
- &osr->details.ok.details.paid.wired),
- TALER_JSON_spec_amount_any ("deposit_total",
- &osr->details.ok.details.paid.deposit_total),
- TALER_JSON_spec_ec ("exchange_code",
- &osr->details.ok.details.paid.exchange_ec),
- GNUNET_JSON_spec_uint32 ("exchange_http_status",
- &hc32),
- TALER_JSON_spec_amount_any ("refund_amount",
- &osr->details.ok.details.paid.refund_amount),
- GNUNET_JSON_spec_object_const (
- "contract_terms",
- &osr->details.ok.details.paid.contract_terms),
- GNUNET_JSON_spec_array_const ("wire_details",
- &wire_details),
- GNUNET_JSON_spec_array_const ("refund_details",
- &refund_details),
- /* Only available since **v14** */
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_timestamp ("last_payment",
- &osr->details.ok.details.paid.last_payment),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (osr->hr.reply,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- osr->hr.http_status = 0;
- osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- omgh->cb (omgh->cb_cls,
- osr);
- return;
- }
- osr->details.ok.status = TALER_MERCHANT_OSC_PAID;
-
- osr->details.ok.details.paid.exchange_hc = (unsigned int) hc32;
- {
- unsigned int wts_len = (unsigned int) json_array_size (wire_details);
- unsigned int ref_len = (unsigned int) json_array_size (refund_details);
-
- if ( (json_array_size (wire_details) != (size_t) wts_len) ||
- (wts_len > MAX_WIRE_DETAILS) )
- {
- GNUNET_break (0);
- osr->hr.http_status = 0;
- osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
- omgh->cb (omgh->cb_cls,
- osr);
- return;
- }
- if ( (json_array_size (refund_details) != (size_t) ref_len) ||
- (ref_len > MAX_REFUND_DETAILS) )
- {
- GNUNET_break (0);
- osr->hr.http_status = 0;
- osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
- omgh->cb (omgh->cb_cls,
- osr);
- return;
- }
- {
- struct TALER_MERCHANT_WireTransfer wts[GNUNET_NZL (wts_len)];
- struct TALER_MERCHANT_RefundOrderDetail ref[GNUNET_NZL (ref_len)];
-
- for (unsigned int i = 0; i<wts_len; i++)
- {
- struct TALER_MERCHANT_WireTransfer *wt = &wts[i];
- const json_t *w = json_array_get (wire_details,
- i);
- struct GNUNET_JSON_Specification ispec[] = {
- TALER_JSON_spec_web_url ("exchange_url",
- &wt->exchange_url),
- GNUNET_JSON_spec_fixed_auto ("wtid",
- &wt->wtid),
- GNUNET_JSON_spec_timestamp ("execution_time",
- &wt->execution_time),
- TALER_JSON_spec_amount_any ("amount",
- &wt->total_amount),
- GNUNET_JSON_spec_bool ("confirmed",
- &wt->confirmed),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (w,
- ispec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- osr->hr.http_status = 0;
- osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- omgh->cb (omgh->cb_cls,
- osr);
- return;
- }
- }
-
- for (unsigned int i = 0; i<ref_len; i++)
- {
- struct TALER_MERCHANT_RefundOrderDetail *ro = &ref[i];
- const json_t *w = json_array_get (refund_details,
- i);
- struct GNUNET_JSON_Specification ispec[] = {
- TALER_JSON_spec_amount_any ("amount",
- &ro->refund_amount),
- GNUNET_JSON_spec_string ("reason",
- &ro->reason),
- GNUNET_JSON_spec_timestamp ("timestamp",
- &ro->refund_time),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (w,
- ispec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- osr->hr.http_status = 0;
- osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- omgh->cb (omgh->cb_cls,
- osr);
- return;
- }
- }
-
- osr->details.ok.details.paid.wts = wts;
- osr->details.ok.details.paid.wts_len = wts_len;
- osr->details.ok.details.paid.refunds = ref;
- osr->details.ok.details.paid.refunds_len = ref_len;
- omgh->cb (omgh->cb_cls,
- osr);
- }
- }
-}
-
-
-/**
- * Function called when we're done processing the GET /private/orders/$ORDER
- * request.
- *
- * @param cls the `struct TALER_MERCHANT_OrderMerchantGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_merchant_order_get_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderMerchantGetHandle *omgh = cls;
- const json_t *json = response;
- const char *order_status;
- struct TALER_MERCHANT_OrderStatusResponse osr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- omgh->job = NULL;
- switch (response_code)
- {
- case MHD_HTTP_OK:
- /* see below */
- break;
- case MHD_HTTP_ACCEPTED:
- /* see below */
- omgh->cb (omgh->cb_cls,
- &osr);
- TALER_MERCHANT_merchant_order_get_cancel (omgh);
- return;
- case MHD_HTTP_UNAUTHORIZED:
- osr.hr.ec = TALER_JSON_get_error_code (json);
- osr.hr.hint = TALER_JSON_get_error_hint (json);
- omgh->cb (omgh->cb_cls,
- &osr);
- TALER_MERCHANT_merchant_order_get_cancel (omgh);
- return;
- case MHD_HTTP_NOT_FOUND:
- osr.hr.ec = TALER_JSON_get_error_code (json);
- osr.hr.hint = TALER_JSON_get_error_hint (json);
- omgh->cb (omgh->cb_cls,
- &osr);
- TALER_MERCHANT_merchant_order_get_cancel (omgh);
- return;
- case MHD_HTTP_GATEWAY_TIMEOUT:
- osr.hr.ec = TALER_JSON_get_error_code (json);
- osr.hr.hint = TALER_JSON_get_error_hint (json);
- omgh->cb (omgh->cb_cls,
- &osr);
- TALER_MERCHANT_merchant_order_get_cancel (omgh);
- return;
- default:
- osr.hr.ec = TALER_JSON_get_error_code (json);
- osr.hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Polling payment failed with HTTP status code %u/%d\n",
- (unsigned int) response_code,
- (int) osr.hr.ec);
- GNUNET_break_op (0);
- omgh->cb (omgh->cb_cls,
- &osr);
- TALER_MERCHANT_merchant_order_get_cancel (omgh);
- return;
- }
-
- order_status = json_string_value (json_object_get (json,
- "order_status"));
-
- if (NULL == order_status)
- {
- GNUNET_break_op (0);
- osr.hr.http_status = 0;
- osr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- omgh->cb (omgh->cb_cls,
- &osr);
- TALER_MERCHANT_merchant_order_get_cancel (omgh);
- return;
- }
-
- if (0 == strcmp ("paid",
- order_status))
- {
- handle_paid (omgh,
- &osr);
- }
- else if (0 == strcmp ("claimed",
- order_status))
- {
- handle_claimed (omgh,
- &osr);
- }
- else if (0 == strcmp ("unpaid",
- order_status))
- {
- handle_unpaid (omgh,
- &osr);
- }
- else
- {
- GNUNET_break_op (0);
- osr.hr.http_status = 0;
- osr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- omgh->cb (omgh->cb_cls,
- &osr);
- }
- TALER_MERCHANT_merchant_order_get_cancel (omgh);
-}
-
-
-struct TALER_MERCHANT_OrderMerchantGetHandle *
-TALER_MERCHANT_merchant_order_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *order_id,
- const char *session_id,
- struct GNUNET_TIME_Relative timeout,
- TALER_MERCHANT_OrderMerchantGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OrderMerchantGetHandle *omgh;
- unsigned int tms;
-
- tms = (unsigned int) (timeout.rel_value_us
- / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
- omgh = GNUNET_new (struct TALER_MERCHANT_OrderMerchantGetHandle);
- omgh->ctx = ctx;
- omgh->cb = cb;
- omgh->cb_cls = cb_cls;
- {
- char *path;
- char timeout_ms[32];
-
- GNUNET_snprintf (timeout_ms,
- sizeof (timeout_ms),
- "%u",
- tms);
- GNUNET_asprintf (&path,
- "private/orders/%s",
- order_id);
- omgh->url = TALER_url_join (backend_url,
- path,
- "session_id", session_id,
- "timeout_ms", (0 != tms) ? timeout_ms : NULL,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == omgh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (omgh);
- return NULL;
- }
-
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (omgh->url);
- if (NULL == eh)
- {
- GNUNET_break (0);
- GNUNET_free (omgh->url);
- GNUNET_free (omgh);
- return NULL;
- }
- if (0 != tms)
- {
- GNUNET_break (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_TIMEOUT_MS,
- (long) (tms + 100L)));
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Getting order status from %s\n",
- omgh->url);
- if (NULL == (omgh->job =
- GNUNET_CURL_job_add (ctx,
- eh,
- &handle_merchant_order_get_finished,
- omgh)))
- {
- GNUNET_break (0);
- GNUNET_free (omgh->url);
- GNUNET_free (omgh);
- return NULL;
- }
- }
- return omgh;
-}
-
-
-void
-TALER_MERCHANT_merchant_order_get_cancel (
- struct TALER_MERCHANT_OrderMerchantGetHandle *omgh)
-{
- if (NULL != omgh->job)
- {
- GNUNET_CURL_job_cancel (omgh->job);
- omgh->job = NULL;
- }
- GNUNET_free (omgh->url);
- GNUNET_free (omgh);
-}
-
-
-/* end of merchant_api_merchant_get_order.c */
diff --git a/src/lib/merchant_api_patch-management-instances-INSTANCE.c b/src/lib/merchant_api_patch-management-instances-INSTANCE.c
@@ -0,0 +1,271 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_patch-management-instances-INSTANCE.c
+ * @brief Implementation of the PATCH /instances/$ID request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_kyclogic_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a PATCH /instances/$ID operation.
+ */
+struct TALER_MERCHANT_InstancePatchHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_InstancePatchCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /instances/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_InstancePatchHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_instance_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_InstancePatchHandle *iph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ iph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /instances/$ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ iph->cb (iph->cb_cls,
+ &hr);
+ TALER_MERCHANT_instance_patch_cancel (iph);
+}
+
+
+struct TALER_MERCHANT_InstancePatchHandle *
+TALER_MERCHANT_instance_patch (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ const char *name,
+ const json_t *address,
+ const json_t *jurisdiction,
+ bool use_stefan,
+ struct GNUNET_TIME_Relative default_wire_transfer_delay,
+ struct GNUNET_TIME_Relative default_pay_delay,
+ struct GNUNET_TIME_Relative default_refund_delay,
+ TALER_MERCHANT_InstancePatchCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_InstancePatchHandle *iph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("name",
+ name),
+ GNUNET_JSON_pack_object_incref ("address",
+ (json_t *) address),
+ GNUNET_JSON_pack_object_incref ("jurisdiction",
+ (json_t *) jurisdiction),
+ GNUNET_JSON_pack_bool ("use_stefan",
+ use_stefan),
+ GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay",
+ default_wire_transfer_delay),
+ GNUNET_JSON_pack_time_rel ("default_pay_delay",
+ default_pay_delay),
+ GNUNET_JSON_pack_time_rel ("default_refund_delay",
+ default_refund_delay),
+ /* FIXME: add eventually to arguments when we transform the API... */
+ GNUNET_JSON_pack_time_rounder_interval (
+ "default_wire_transfer_rounding_interval",
+ GNUNET_TIME_RI_NONE)
+ );
+ iph = GNUNET_new (struct TALER_MERCHANT_InstancePatchHandle);
+ iph->ctx = ctx;
+ iph->cb = cb;
+ iph->cb_cls = cb_cls;
+ if (NULL != instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "management/instances/%s",
+ instance_id);
+ iph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ /* backend_url is already identifying the instance */
+ iph->url = TALER_url_join (backend_url,
+ "private",
+ NULL);
+ }
+ if (NULL == iph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (iph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (iph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&iph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (iph->url);
+ GNUNET_free (iph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ iph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ iph->post_ctx.headers,
+ &handle_patch_instance_finished,
+ iph);
+ }
+ return iph;
+}
+
+
+void
+TALER_MERCHANT_instance_patch_cancel (
+ struct TALER_MERCHANT_InstancePatchHandle *iph)
+{
+ if (NULL != iph->job)
+ {
+ GNUNET_CURL_job_cancel (iph->job);
+ iph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&iph->post_ctx);
+ GNUNET_free (iph->url);
+ GNUNET_free (iph);
+}
+
+
+/* end of merchant_api_patch_instance.c */
diff --git a/src/lib/merchant_api_patch-private-accounts-H_WIRE.c b/src/lib/merchant_api_patch-private-accounts-H_WIRE.c
@@ -0,0 +1,254 @@
+/*
+ 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_patch-private-accounts-H_WIRE.c
+ * @brief Implementation of the PATCH /accounts/$ID request
+ * of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a PATCH /accounts/$ID operation.
+ */
+struct TALER_MERCHANT_AccountPatchHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_AccountPatchCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /accounts/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_AccountPatchHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_account_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_AccountPatchHandle *tph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /accounts/$ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_break_op (0);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ tph->cb (tph->cb_cls,
+ &hr);
+ TALER_MERCHANT_account_patch_cancel (tph);
+}
+
+
+struct TALER_MERCHANT_AccountPatchHandle *
+TALER_MERCHANT_account_patch (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const struct TALER_MerchantWireHashP *h_wire,
+ const char *credit_facade_url,
+ const json_t *credit_facade_credentials,
+ TALER_MERCHANT_AccountPatchCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_AccountPatchHandle *tph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("credit_facade_url",
+ credit_facade_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("credit_facade_credentials",
+ (json_t *) credit_facade_credentials)));
+ tph = GNUNET_new (struct TALER_MERCHANT_AccountPatchHandle);
+ tph->ctx = ctx;
+ tph->cb = cb;
+ tph->cb_cls = cb_cls;
+ {
+ char w_str[sizeof (*h_wire) * 2];
+ char *path;
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (h_wire,
+ sizeof (*h_wire),
+ w_str,
+ sizeof (w_str));
+ *end = '\0';
+ GNUNET_asprintf (&path,
+ "private/accounts/%s",
+ w_str);
+ tph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == tph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (tph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&tph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (tph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ tph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ tph->post_ctx.headers,
+ &handle_patch_account_finished,
+ tph);
+ }
+ return tph;
+}
+
+
+void
+TALER_MERCHANT_account_patch_cancel (
+ struct TALER_MERCHANT_AccountPatchHandle *tph)
+{
+ if (NULL != tph->job)
+ {
+ GNUNET_CURL_job_cancel (tph->job);
+ tph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&tph->post_ctx);
+ GNUNET_free (tph->url);
+ GNUNET_free (tph);
+}
+
+
+/* end of merchant_api_patch_account.c */
diff --git a/src/lib/merchant_api_patch-private-orders-ORDER_ID-forget.c b/src/lib/merchant_api_patch-private-orders-ORDER_ID-forget.c
@@ -0,0 +1,254 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020, 2021, 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_patch-private-orders-ORDER_ID-forget.c
+ * @brief Implementation of the PATCH /orders/$ID/forget request
+ * of the merchant's HTTP API
+ * @author Jonathan Buchanan
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a PATCH /orders/$ORDER_ID/forget operation.
+ */
+struct TALER_MERCHANT_OrderForgetHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ForgetCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /orders/$ORDER_ID/forget request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderForgetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_forget_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderForgetHandle *ofh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ ofh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /orders/$ORDER_ID/forget completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ /* fields were NOW forgotten */
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ /* fields were already forgotten before */
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ofh->cb (ofh->cb_cls,
+ &hr);
+ TALER_MERCHANT_order_forget_cancel (ofh);
+}
+
+
+struct TALER_MERCHANT_OrderForgetHandle *
+TALER_MERCHANT_order_forget (
+ struct GNUNET_CURL_Context *ctx,
+ const char *merchant_url,
+ const char *order_id,
+ unsigned int fields_length,
+ const char *fields[static fields_length],
+ TALER_MERCHANT_ForgetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OrderForgetHandle *ofh;
+ json_t *req_fields;
+ json_t *req_obj;
+
+ req_fields = json_array ();
+ if (NULL == req_fields)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ for (unsigned int i = 0; i<fields_length; i++)
+ {
+ if (0 !=
+ json_array_append_new (req_fields,
+ json_string (fields[i])))
+ {
+ GNUNET_break (0);
+ json_decref (req_fields);
+ return NULL;
+ }
+ }
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("fields",
+ req_fields));
+ ofh = GNUNET_new (struct TALER_MERCHANT_OrderForgetHandle);
+ ofh->ctx = ctx;
+ ofh->cb = cb;
+ ofh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/orders/%s/forget",
+ order_id);
+ ofh->url = TALER_url_join (merchant_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == ofh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (ofh);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (ofh->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&ofh->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (ofh);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ ofh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ ofh->post_ctx.headers,
+ &handle_forget_finished,
+ ofh);
+ }
+ return ofh;
+}
+
+
+void
+TALER_MERCHANT_order_forget_cancel (
+ struct TALER_MERCHANT_OrderForgetHandle *ofh)
+{
+ if (NULL != ofh->job)
+ {
+ GNUNET_CURL_job_cancel (ofh->job);
+ ofh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ofh->post_ctx);
+ GNUNET_free (ofh->url);
+ GNUNET_free (ofh);
+}
+
+
+/* end of merchant_api_patch_order_forget.c */
diff --git a/src/lib/merchant_api_patch-private-otp-devices-DEVICE_ID.c b/src/lib/merchant_api_patch-private-otp-devices-DEVICE_ID.c
@@ -0,0 +1,252 @@
+/*
+ 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_patch-private-otp-devices-DEVICE_ID.c
+ * @brief Implementation of the PATCH /otp-devices/$ID request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a PATCH /otp-devices/$ID operation.
+ */
+struct TALER_MERCHANT_OtpDevicePatchHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OtpDevicePatchCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /otp-devices/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OtpDevicePatchHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_otp_device_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OtpDevicePatchHandle *tph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /otp-devices/$ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_break_op (0);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ tph->cb (tph->cb_cls,
+ &hr);
+ TALER_MERCHANT_otp_device_patch_cancel (tph);
+}
+
+
+struct TALER_MERCHANT_OtpDevicePatchHandle *
+TALER_MERCHANT_otp_device_patch (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *otp_device_id,
+ const char *otp_device_description,
+ const char *otp_key,
+ enum TALER_MerchantConfirmationAlgorithm mca,
+ uint64_t otp_ctr,
+ TALER_MERCHANT_OtpDevicePatchCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OtpDevicePatchHandle *tph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("otp_device_description",
+ otp_device_description),
+ GNUNET_JSON_pack_uint64 ("otp_algorithm",
+ (uint32_t) mca),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_key",
+ otp_key)),
+ GNUNET_JSON_pack_uint64 ("otp_ctr",
+ otp_ctr));
+ tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicePatchHandle);
+ tph->ctx = ctx;
+ tph->cb = cb;
+ tph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/otp-devices/%s",
+ otp_device_id);
+ tph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == tph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (tph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&tph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (tph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ tph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ tph->post_ctx.headers,
+ &handle_patch_otp_device_finished,
+ tph);
+ }
+ return tph;
+}
+
+
+void
+TALER_MERCHANT_otp_device_patch_cancel (
+ struct TALER_MERCHANT_OtpDevicePatchHandle *tph)
+{
+ if (NULL != tph->job)
+ {
+ GNUNET_CURL_job_cancel (tph->job);
+ tph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&tph->post_ctx);
+ GNUNET_free (tph->url);
+ GNUNET_free (tph);
+}
+
+
+/* end of merchant_api_patch_otp_device.c */
diff --git a/src/lib/merchant_api_patch-private-products-PRODUCT_ID.c b/src/lib/merchant_api_patch-private-products-PRODUCT_ID.c
@@ -0,0 +1,346 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020, 2021 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_patch-private-products-PRODUCT_ID.c
+ * @brief Implementation of the PATCH /products/$ID request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a PATCH /products/$ID operation.
+ */
+struct TALER_MERCHANT_ProductPatchHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ProductPatchCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /products/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_ProductPatchHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_product_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_ProductPatchHandle *pph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ pph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /products/$ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_break_op (0);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ pph->cb (pph->cb_cls,
+ &hr);
+ TALER_MERCHANT_product_patch_cancel (pph);
+}
+
+
+struct TALER_MERCHANT_ProductPatchHandle *
+TALER_MERCHANT_product_patch2 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ const char *description,
+ const json_t *description_i18n,
+ const char *unit,
+ const struct TALER_Amount *unit_prices,
+ size_t unit_price_len,
+ const char *image,
+ const json_t *taxes,
+ int64_t total_stock,
+ uint32_t total_stock_frac,
+ bool unit_allow_fraction,
+ const uint32_t *unit_precision_level,
+ uint64_t total_lost,
+ const json_t *address,
+ struct GNUNET_TIME_Timestamp next_restock,
+ TALER_MERCHANT_ProductPatchCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_ProductPatchHandle *pph;
+ json_t *req_obj;
+ char unit_total_stock_buf[64];
+
+ TALER_MERCHANT_format_stock_string (total_stock,
+ total_stock_frac,
+ unit_total_stock_buf,
+ sizeof (unit_total_stock_buf));
+
+ {
+ req_obj = GNUNET_JSON_PACK (
+ /* FIXME: once we move to the new-style API,
+ allow applications to set the product name properly! */
+ GNUNET_JSON_pack_string ("product_name",
+ description),
+ GNUNET_JSON_pack_string ("description",
+ description),
+ GNUNET_JSON_pack_object_incref ("description_i18n",
+ (json_t *) description_i18n),
+ GNUNET_JSON_pack_string ("unit",
+ unit),
+ TALER_JSON_pack_amount_array ("unit_price",
+ unit_price_len,
+ unit_prices),
+ GNUNET_JSON_pack_string ("image",
+ image),
+ GNUNET_JSON_pack_array_incref ("taxes",
+ (json_t *) taxes),
+ GNUNET_JSON_pack_string ("unit_total_stock",
+ unit_total_stock_buf),
+ GNUNET_JSON_pack_bool ("unit_allow_fraction",
+ unit_allow_fraction),
+ GNUNET_JSON_pack_uint64 ("total_lost",
+ total_lost),
+ GNUNET_JSON_pack_object_incref ("address",
+ (json_t *) address),
+ GNUNET_JSON_pack_timestamp ("next_restock",
+ next_restock));
+ }
+ if (NULL != unit_precision_level)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_precision_level",
+ json_integer (
+ *unit_precision_level)));
+ }
+ if (! unit_allow_fraction)
+ {
+ GNUNET_assert (0 ==
+ json_object_del (req_obj,
+ "unit_allow_fraction"));
+ if (NULL != unit_precision_level)
+ GNUNET_assert (0 ==
+ json_object_del (req_obj,
+ "unit_precision_level"));
+ }
+ pph = GNUNET_new (struct TALER_MERCHANT_ProductPatchHandle);
+ pph->ctx = ctx;
+ pph->cb = cb;
+ pph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/products/%s",
+ product_id);
+ pph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (pph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (pph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&pph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (pph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ pph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ pph->post_ctx.headers,
+ &handle_patch_product_finished,
+ pph);
+ }
+ return pph;
+}
+
+
+struct TALER_MERCHANT_ProductPatchHandle *
+TALER_MERCHANT_product_patch (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ const char *description,
+ const json_t *description_i18n,
+ const char *unit,
+ const struct TALER_Amount *price,
+ const char *image,
+ const json_t *taxes,
+ int64_t total_stock,
+ uint64_t total_lost,
+ const json_t *address,
+ struct GNUNET_TIME_Timestamp next_restock,
+ TALER_MERCHANT_ProductPatchCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_product_patch2 (ctx,
+ backend_url,
+ product_id,
+ description,
+ description_i18n,
+ unit,
+ price,
+ 1,
+ image,
+ taxes,
+ total_stock,
+ 0,
+ false,
+ NULL,
+ total_lost,
+ address,
+ next_restock,
+ cb,
+ cb_cls);
+}
+
+
+void
+TALER_MERCHANT_product_patch_cancel (
+ struct TALER_MERCHANT_ProductPatchHandle *pph)
+{
+ if (NULL != pph->job)
+ {
+ GNUNET_CURL_job_cancel (pph->job);
+ pph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pph->post_ctx);
+ GNUNET_free (pph->url);
+ GNUNET_free (pph);
+}
+
+
+/* end of merchant_api_patch_product.c */
diff --git a/src/lib/merchant_api_patch-private-templates-TEMPLATE_ID.c b/src/lib/merchant_api_patch-private-templates-TEMPLATE_ID.c
@@ -0,0 +1,249 @@
+/*
+ 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_patch-private-templates-TEMPLATE_ID.c
+ * @brief Implementation of the PATCH /templates/$ID request
+ * of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a PATCH /templates/$ID operation.
+ */
+struct TALER_MERCHANT_TemplatePatchHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TemplatePatchCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /templates/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TemplatePatchHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_template_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_TemplatePatchHandle *tph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /templates/$ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_break_op (0);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ tph->cb (tph->cb_cls,
+ &hr);
+ TALER_MERCHANT_template_patch_cancel (tph);
+}
+
+
+struct TALER_MERCHANT_TemplatePatchHandle *
+TALER_MERCHANT_template_patch (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *template_id,
+ const char *template_description,
+ const char *otp_id,
+ json_t *template_contract,
+ TALER_MERCHANT_TemplatePatchCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TemplatePatchHandle *tph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("template_description",
+ template_description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_id",
+ otp_id)),
+ GNUNET_JSON_pack_object_incref ("template_contract",
+ (json_t *) template_contract));
+ tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle);
+ tph->ctx = ctx;
+ tph->cb = cb;
+ tph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/templates/%s",
+ template_id);
+ tph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == tph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (tph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&tph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (tph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ tph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ tph->post_ctx.headers,
+ &handle_patch_template_finished,
+ tph);
+ }
+ return tph;
+}
+
+
+void
+TALER_MERCHANT_template_patch_cancel (
+ struct TALER_MERCHANT_TemplatePatchHandle *tph)
+{
+ if (NULL != tph->job)
+ {
+ GNUNET_CURL_job_cancel (tph->job);
+ tph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&tph->post_ctx);
+ GNUNET_free (tph->url);
+ GNUNET_free (tph);
+}
+
+
+/* end of merchant_api_patch_template.c */
diff --git a/src/lib/merchant_api_patch-private-units-UNIT.c b/src/lib/merchant_api_patch-private-units-UNIT.c
@@ -0,0 +1,291 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_patch-private-units-UNIT.c
+ * @brief Implementation of PATCH /private/units/$ID
+ * @author Bohdan Potuzhnyi
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a PATCH /private/units/$ID operation.
+ */
+struct TALER_MERCHANT_UnitPatchHandle
+{
+ /**
+ * Fully qualified request URL.
+ */
+ char *url;
+
+ /**
+ * In-flight CURL job.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Completion callback.
+ */
+ TALER_MERCHANT_UnitPatchCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Keeps POST body and headers alive.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+};
+
+
+/**
+ * Called when the HTTP transfer finishes.
+ *
+ * @param cls operation handle
+ * @param response_code HTTP status (0 on failure)
+ * @param response parsed JSON reply (NULL if unavailable)
+ */
+static void
+handle_patch_unit_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_UnitPatchHandle *uph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ uph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /private/units completed with status %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ case MHD_HTTP_UNAUTHORIZED:
+ case MHD_HTTP_FORBIDDEN:
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_CONFLICT:
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response %u/%d for PATCH /private/units\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ uph->cb (uph->cb_cls,
+ &hr);
+ TALER_MERCHANT_unit_patch_cancel (uph);
+}
+
+
+struct TALER_MERCHANT_UnitPatchHandle *
+TALER_MERCHANT_unit_patch (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *unit_id,
+ const char *unit_name_long,
+ const char *unit_name_short,
+ const json_t *unit_name_long_i18n,
+ const json_t *unit_name_short_i18n,
+ const bool *unit_allow_fraction,
+ const uint32_t *unit_precision_level,
+ const bool *unit_active,
+ TALER_MERCHANT_UnitPatchCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_UnitPatchHandle *uph;
+ json_t *req_obj;
+ char *path;
+
+ req_obj = json_object ();
+ if (NULL == req_obj)
+ return NULL;
+ if (NULL != unit_name_long)
+ {
+ if (0 != json_object_set_new (req_obj,
+ "unit_name_long",
+ json_string (unit_name_long)))
+ {
+ json_decref (req_obj);
+ return NULL;
+ }
+ }
+ if (NULL != unit_name_short)
+ {
+ if (0 != json_object_set_new (req_obj,
+ "unit_name_short",
+ json_string (unit_name_short)))
+ {
+ json_decref (req_obj);
+ return NULL;
+ }
+ }
+ if (NULL != unit_name_long_i18n)
+ {
+ if (0 != json_object_set_new (req_obj,
+ "unit_name_long_i18n",
+ json_incref ((json_t *) unit_name_long_i18n)))
+ {
+ json_decref (req_obj);
+ return NULL;
+ }
+ }
+ if (NULL != unit_name_short_i18n)
+ {
+ if (0 != json_object_set_new (req_obj,
+ "unit_name_short_i18n",
+ json_incref (
+ (json_t *) unit_name_short_i18n)))
+ {
+ json_decref (req_obj);
+ return NULL;
+ }
+ }
+ if (NULL != unit_allow_fraction)
+ {
+ if (0 != json_object_set_new (req_obj,
+ "unit_allow_fraction",
+ json_boolean (*unit_allow_fraction)))
+ {
+ json_decref (req_obj);
+ return NULL;
+ }
+ }
+ if (NULL != unit_precision_level)
+ {
+ if (0 != json_object_set_new (req_obj,
+ "unit_precision_level",
+ json_integer (
+ (json_int_t) *unit_precision_level)))
+ {
+ json_decref (req_obj);
+ return NULL;
+ }
+ }
+ if (NULL != unit_active)
+ {
+ if (0 != json_object_set_new (req_obj,
+ "unit_active",
+ json_boolean (*unit_active)))
+ {
+ json_decref (req_obj);
+ return NULL;
+ }
+ }
+ if (0 == json_object_size (req_obj))
+ {
+ json_decref (req_obj);
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ GNUNET_asprintf (&path,
+ "private/units/%s",
+ unit_id);
+ uph = GNUNET_new (struct TALER_MERCHANT_UnitPatchHandle);
+ uph->ctx = ctx;
+ uph->cb = cb;
+ uph->cb_cls = cb_cls;
+ uph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ if (NULL == uph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to build /private/units/%s URL\n",
+ unit_id);
+ json_decref (req_obj);
+ GNUNET_free (uph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (uph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&uph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (uph->url);
+ GNUNET_free (uph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ uph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ uph->post_ctx.headers,
+ &handle_patch_unit_finished,
+ uph);
+ }
+ return uph;
+}
+
+
+void
+TALER_MERCHANT_unit_patch_cancel (struct TALER_MERCHANT_UnitPatchHandle *uph)
+{
+ if (NULL != uph->job)
+ {
+ GNUNET_CURL_job_cancel (uph->job);
+ uph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&uph->post_ctx);
+ GNUNET_free (uph->url);
+ GNUNET_free (uph);
+}
+
+
+/* end of merchant_api_patch_unit.c */
diff --git a/src/lib/merchant_api_patch-private-webhooks-WEBHOOK_ID.c b/src/lib/merchant_api_patch-private-webhooks-WEBHOOK_ID.c
@@ -0,0 +1,254 @@
+/*
+ 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_patch-private-webhooks-WEBHOOK_ID.c
+ * @brief Implementation of the PATCH /webhooks/$ID request
+ * of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a PATCH /webhooks/$ID operation.
+ */
+struct TALER_MERCHANT_WebhookPatchHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_WebhookPatchCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /webhooks/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_WebhookPatchHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_webhook_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_WebhookPatchHandle *wph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ wph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /webhooks/$ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_break_op (0);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ wph->cb (wph->cb_cls,
+ &hr);
+ TALER_MERCHANT_webhook_patch_cancel (wph);
+}
+
+
+struct TALER_MERCHANT_WebhookPatchHandle *
+TALER_MERCHANT_webhook_patch (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *webhook_id,
+ const char *event_type,
+ const char *url,
+ const char *http_method,
+ const char *header_template,
+ const char *body_template,
+ TALER_MERCHANT_WebhookPatchCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_WebhookPatchHandle *wph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("event_type",
+ event_type),
+ GNUNET_JSON_pack_string ("url",
+ url),
+ GNUNET_JSON_pack_string ("http_method",
+ http_method),
+ GNUNET_JSON_pack_string ("header_template",
+ header_template),
+ GNUNET_JSON_pack_string ("body_template",
+ body_template));
+ wph = GNUNET_new (struct TALER_MERCHANT_WebhookPatchHandle);
+ wph->ctx = ctx;
+ wph->cb = cb;
+ wph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/webhooks/%s",
+ webhook_id);
+ wph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == wph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (wph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (wph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&wph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (wph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ wph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ wph->post_ctx.headers,
+ &handle_patch_webhook_finished,
+ wph);
+ }
+ return wph;
+}
+
+
+void
+TALER_MERCHANT_webhook_patch_cancel (
+ struct TALER_MERCHANT_WebhookPatchHandle *wph)
+{
+ if (NULL != wph->job)
+ {
+ GNUNET_CURL_job_cancel (wph->job);
+ wph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&wph->post_ctx);
+ GNUNET_free (wph->url);
+ GNUNET_free (wph);
+}
+
+
+/* end of merchant_api_patch_webhook.c */
diff --git a/src/lib/merchant_api_patch_account.c b/src/lib/merchant_api_patch_account.c
@@ -1,254 +0,0 @@
-/*
- 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_patch_account.c
- * @brief Implementation of the PATCH /accounts/$ID request
- * of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a PATCH /accounts/$ID operation.
- */
-struct TALER_MERCHANT_AccountPatchHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_AccountPatchCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP PATCH /accounts/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_AccountPatchHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_patch_account_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_AccountPatchHandle *tph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "PATCH /accounts/$ID completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_break_op (0);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- tph->cb (tph->cb_cls,
- &hr);
- TALER_MERCHANT_account_patch_cancel (tph);
-}
-
-
-struct TALER_MERCHANT_AccountPatchHandle *
-TALER_MERCHANT_account_patch (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const struct TALER_MerchantWireHashP *h_wire,
- const char *credit_facade_url,
- const json_t *credit_facade_credentials,
- TALER_MERCHANT_AccountPatchCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_AccountPatchHandle *tph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("credit_facade_url",
- credit_facade_url)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("credit_facade_credentials",
- (json_t *) credit_facade_credentials)));
- tph = GNUNET_new (struct TALER_MERCHANT_AccountPatchHandle);
- tph->ctx = ctx;
- tph->cb = cb;
- tph->cb_cls = cb_cls;
- {
- char w_str[sizeof (*h_wire) * 2];
- char *path;
- char *end;
-
- end = GNUNET_STRINGS_data_to_string (h_wire,
- sizeof (*h_wire),
- w_str,
- sizeof (w_str));
- *end = '\0';
- GNUNET_asprintf (&path,
- "private/accounts/%s",
- w_str);
- tph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == tph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (tph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&tph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (tph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_PATCH));
- tph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- tph->post_ctx.headers,
- &handle_patch_account_finished,
- tph);
- }
- return tph;
-}
-
-
-void
-TALER_MERCHANT_account_patch_cancel (
- struct TALER_MERCHANT_AccountPatchHandle *tph)
-{
- if (NULL != tph->job)
- {
- GNUNET_CURL_job_cancel (tph->job);
- tph->job = NULL;
- }
- TALER_curl_easy_post_finished (&tph->post_ctx);
- GNUNET_free (tph->url);
- GNUNET_free (tph);
-}
-
-
-/* end of merchant_api_patch_account.c */
diff --git a/src/lib/merchant_api_patch_instance.c b/src/lib/merchant_api_patch_instance.c
@@ -1,271 +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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_patch_instance.c
- * @brief Implementation of the PATCH /instances/$ID request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_kyclogic_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a PATCH /instances/$ID operation.
- */
-struct TALER_MERCHANT_InstancePatchHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_InstancePatchCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP PATCH /instances/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_InstancePatchHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_patch_instance_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_InstancePatchHandle *iph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- iph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "PATCH /instances/$ID completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- iph->cb (iph->cb_cls,
- &hr);
- TALER_MERCHANT_instance_patch_cancel (iph);
-}
-
-
-struct TALER_MERCHANT_InstancePatchHandle *
-TALER_MERCHANT_instance_patch (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- const char *name,
- const json_t *address,
- const json_t *jurisdiction,
- bool use_stefan,
- struct GNUNET_TIME_Relative default_wire_transfer_delay,
- struct GNUNET_TIME_Relative default_pay_delay,
- struct GNUNET_TIME_Relative default_refund_delay,
- TALER_MERCHANT_InstancePatchCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_InstancePatchHandle *iph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("name",
- name),
- GNUNET_JSON_pack_object_incref ("address",
- (json_t *) address),
- GNUNET_JSON_pack_object_incref ("jurisdiction",
- (json_t *) jurisdiction),
- GNUNET_JSON_pack_bool ("use_stefan",
- use_stefan),
- GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay",
- default_wire_transfer_delay),
- GNUNET_JSON_pack_time_rel ("default_pay_delay",
- default_pay_delay),
- GNUNET_JSON_pack_time_rel ("default_refund_delay",
- default_refund_delay),
- /* FIXME: add eventually to arguments when we transform the API... */
- GNUNET_JSON_pack_time_rounder_interval (
- "default_wire_transfer_rounding_interval",
- GNUNET_TIME_RI_NONE)
- );
- iph = GNUNET_new (struct TALER_MERCHANT_InstancePatchHandle);
- iph->ctx = ctx;
- iph->cb = cb;
- iph->cb_cls = cb_cls;
- if (NULL != instance_id)
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "management/instances/%s",
- instance_id);
- iph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- else
- {
- /* backend_url is already identifying the instance */
- iph->url = TALER_url_join (backend_url,
- "private",
- NULL);
- }
- if (NULL == iph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (iph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (iph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&iph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (iph->url);
- GNUNET_free (iph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_PATCH));
- iph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- iph->post_ctx.headers,
- &handle_patch_instance_finished,
- iph);
- }
- return iph;
-}
-
-
-void
-TALER_MERCHANT_instance_patch_cancel (
- struct TALER_MERCHANT_InstancePatchHandle *iph)
-{
- if (NULL != iph->job)
- {
- GNUNET_CURL_job_cancel (iph->job);
- iph->job = NULL;
- }
- TALER_curl_easy_post_finished (&iph->post_ctx);
- GNUNET_free (iph->url);
- GNUNET_free (iph);
-}
-
-
-/* end of merchant_api_patch_instance.c */
diff --git a/src/lib/merchant_api_patch_order_forget.c b/src/lib/merchant_api_patch_order_forget.c
@@ -1,254 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2020, 2021, 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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_patch_order_forget.c
- * @brief Implementation of the PATCH /orders/$ID/forget request
- * of the merchant's HTTP API
- * @author Jonathan Buchanan
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a PATCH /orders/$ORDER_ID/forget operation.
- */
-struct TALER_MERCHANT_OrderForgetHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ForgetCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP PATCH /orders/$ORDER_ID/forget request.
- *
- * @param cls the `struct TALER_MERCHANT_OrderForgetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_forget_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderForgetHandle *ofh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- ofh->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "PATCH /orders/$ORDER_ID/forget completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- /* fields were NOW forgotten */
- break;
- case MHD_HTTP_NO_CONTENT:
- /* fields were already forgotten before */
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- ofh->cb (ofh->cb_cls,
- &hr);
- TALER_MERCHANT_order_forget_cancel (ofh);
-}
-
-
-struct TALER_MERCHANT_OrderForgetHandle *
-TALER_MERCHANT_order_forget (
- struct GNUNET_CURL_Context *ctx,
- const char *merchant_url,
- const char *order_id,
- unsigned int fields_length,
- const char *fields[static fields_length],
- TALER_MERCHANT_ForgetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OrderForgetHandle *ofh;
- json_t *req_fields;
- json_t *req_obj;
-
- req_fields = json_array ();
- if (NULL == req_fields)
- {
- GNUNET_break (0);
- return NULL;
- }
- for (unsigned int i = 0; i<fields_length; i++)
- {
- if (0 !=
- json_array_append_new (req_fields,
- json_string (fields[i])))
- {
- GNUNET_break (0);
- json_decref (req_fields);
- return NULL;
- }
- }
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_array_steal ("fields",
- req_fields));
- ofh = GNUNET_new (struct TALER_MERCHANT_OrderForgetHandle);
- ofh->ctx = ctx;
- ofh->cb = cb;
- ofh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/orders/%s/forget",
- order_id);
- ofh->url = TALER_url_join (merchant_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == ofh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (ofh);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (ofh->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&ofh->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (ofh);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_PATCH));
- ofh->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- ofh->post_ctx.headers,
- &handle_forget_finished,
- ofh);
- }
- return ofh;
-}
-
-
-void
-TALER_MERCHANT_order_forget_cancel (
- struct TALER_MERCHANT_OrderForgetHandle *ofh)
-{
- if (NULL != ofh->job)
- {
- GNUNET_CURL_job_cancel (ofh->job);
- ofh->job = NULL;
- }
- TALER_curl_easy_post_finished (&ofh->post_ctx);
- GNUNET_free (ofh->url);
- GNUNET_free (ofh);
-}
-
-
-/* end of merchant_api_patch_order_forget.c */
diff --git a/src/lib/merchant_api_patch_otp_device.c b/src/lib/merchant_api_patch_otp_device.c
@@ -1,252 +0,0 @@
-/*
- 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_patch_otp_device.c
- * @brief Implementation of the PATCH /otp-devices/$ID request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a PATCH /otp-devices/$ID operation.
- */
-struct TALER_MERCHANT_OtpDevicePatchHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OtpDevicePatchCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP PATCH /otp-devices/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_OtpDevicePatchHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_patch_otp_device_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OtpDevicePatchHandle *tph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "PATCH /otp-devices/$ID completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_break_op (0);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- tph->cb (tph->cb_cls,
- &hr);
- TALER_MERCHANT_otp_device_patch_cancel (tph);
-}
-
-
-struct TALER_MERCHANT_OtpDevicePatchHandle *
-TALER_MERCHANT_otp_device_patch (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *otp_device_id,
- const char *otp_device_description,
- const char *otp_key,
- enum TALER_MerchantConfirmationAlgorithm mca,
- uint64_t otp_ctr,
- TALER_MERCHANT_OtpDevicePatchCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OtpDevicePatchHandle *tph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("otp_device_description",
- otp_device_description),
- GNUNET_JSON_pack_uint64 ("otp_algorithm",
- (uint32_t) mca),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("otp_key",
- otp_key)),
- GNUNET_JSON_pack_uint64 ("otp_ctr",
- otp_ctr));
- tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicePatchHandle);
- tph->ctx = ctx;
- tph->cb = cb;
- tph->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/otp-devices/%s",
- otp_device_id);
- tph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == tph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (tph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&tph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (tph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_PATCH));
- tph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- tph->post_ctx.headers,
- &handle_patch_otp_device_finished,
- tph);
- }
- return tph;
-}
-
-
-void
-TALER_MERCHANT_otp_device_patch_cancel (
- struct TALER_MERCHANT_OtpDevicePatchHandle *tph)
-{
- if (NULL != tph->job)
- {
- GNUNET_CURL_job_cancel (tph->job);
- tph->job = NULL;
- }
- TALER_curl_easy_post_finished (&tph->post_ctx);
- GNUNET_free (tph->url);
- GNUNET_free (tph);
-}
-
-
-/* end of merchant_api_patch_otp_device.c */
diff --git a/src/lib/merchant_api_patch_product.c b/src/lib/merchant_api_patch_product.c
@@ -1,346 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2020, 2021 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_patch_product.c
- * @brief Implementation of the PATCH /products/$ID request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a PATCH /products/$ID operation.
- */
-struct TALER_MERCHANT_ProductPatchHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ProductPatchCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP PATCH /products/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_ProductPatchHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_patch_product_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_ProductPatchHandle *pph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- pph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "PATCH /products/$ID completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_break_op (0);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- pph->cb (pph->cb_cls,
- &hr);
- TALER_MERCHANT_product_patch_cancel (pph);
-}
-
-
-struct TALER_MERCHANT_ProductPatchHandle *
-TALER_MERCHANT_product_patch2 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- const char *description,
- const json_t *description_i18n,
- const char *unit,
- const struct TALER_Amount *unit_prices,
- size_t unit_price_len,
- const char *image,
- const json_t *taxes,
- int64_t total_stock,
- uint32_t total_stock_frac,
- bool unit_allow_fraction,
- const uint32_t *unit_precision_level,
- uint64_t total_lost,
- const json_t *address,
- struct GNUNET_TIME_Timestamp next_restock,
- TALER_MERCHANT_ProductPatchCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_ProductPatchHandle *pph;
- json_t *req_obj;
- char unit_total_stock_buf[64];
-
- TALER_MERCHANT_format_stock_string (total_stock,
- total_stock_frac,
- unit_total_stock_buf,
- sizeof (unit_total_stock_buf));
-
- {
- req_obj = GNUNET_JSON_PACK (
- /* FIXME: once we move to the new-style API,
- allow applications to set the product name properly! */
- GNUNET_JSON_pack_string ("product_name",
- description),
- GNUNET_JSON_pack_string ("description",
- description),
- GNUNET_JSON_pack_object_incref ("description_i18n",
- (json_t *) description_i18n),
- GNUNET_JSON_pack_string ("unit",
- unit),
- TALER_JSON_pack_amount_array ("unit_price",
- unit_price_len,
- unit_prices),
- GNUNET_JSON_pack_string ("image",
- image),
- GNUNET_JSON_pack_array_incref ("taxes",
- (json_t *) taxes),
- GNUNET_JSON_pack_string ("unit_total_stock",
- unit_total_stock_buf),
- GNUNET_JSON_pack_bool ("unit_allow_fraction",
- unit_allow_fraction),
- GNUNET_JSON_pack_uint64 ("total_lost",
- total_lost),
- GNUNET_JSON_pack_object_incref ("address",
- (json_t *) address),
- GNUNET_JSON_pack_timestamp ("next_restock",
- next_restock));
- }
- if (NULL != unit_precision_level)
- {
- GNUNET_assert (0 ==
- json_object_set_new (req_obj,
- "unit_precision_level",
- json_integer (
- *unit_precision_level)));
- }
- if (! unit_allow_fraction)
- {
- GNUNET_assert (0 ==
- json_object_del (req_obj,
- "unit_allow_fraction"));
- if (NULL != unit_precision_level)
- GNUNET_assert (0 ==
- json_object_del (req_obj,
- "unit_precision_level"));
- }
- pph = GNUNET_new (struct TALER_MERCHANT_ProductPatchHandle);
- pph->ctx = ctx;
- pph->cb = cb;
- pph->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/products/%s",
- product_id);
- pph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == pph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (pph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (pph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&pph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (pph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_PATCH));
- pph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- pph->post_ctx.headers,
- &handle_patch_product_finished,
- pph);
- }
- return pph;
-}
-
-
-struct TALER_MERCHANT_ProductPatchHandle *
-TALER_MERCHANT_product_patch (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- const char *description,
- const json_t *description_i18n,
- const char *unit,
- const struct TALER_Amount *price,
- const char *image,
- const json_t *taxes,
- int64_t total_stock,
- uint64_t total_lost,
- const json_t *address,
- struct GNUNET_TIME_Timestamp next_restock,
- TALER_MERCHANT_ProductPatchCallback cb,
- void *cb_cls)
-{
- return TALER_MERCHANT_product_patch2 (ctx,
- backend_url,
- product_id,
- description,
- description_i18n,
- unit,
- price,
- 1,
- image,
- taxes,
- total_stock,
- 0,
- false,
- NULL,
- total_lost,
- address,
- next_restock,
- cb,
- cb_cls);
-}
-
-
-void
-TALER_MERCHANT_product_patch_cancel (
- struct TALER_MERCHANT_ProductPatchHandle *pph)
-{
- if (NULL != pph->job)
- {
- GNUNET_CURL_job_cancel (pph->job);
- pph->job = NULL;
- }
- TALER_curl_easy_post_finished (&pph->post_ctx);
- GNUNET_free (pph->url);
- GNUNET_free (pph);
-}
-
-
-/* end of merchant_api_patch_product.c */
diff --git a/src/lib/merchant_api_patch_template.c b/src/lib/merchant_api_patch_template.c
@@ -1,249 +0,0 @@
-/*
- 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_patch_template.c
- * @brief Implementation of the PATCH /templates/$ID request
- * of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a PATCH /templates/$ID operation.
- */
-struct TALER_MERCHANT_TemplatePatchHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_TemplatePatchCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP PATCH /templates/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_TemplatePatchHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_patch_template_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_TemplatePatchHandle *tph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "PATCH /templates/$ID completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_break_op (0);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- tph->cb (tph->cb_cls,
- &hr);
- TALER_MERCHANT_template_patch_cancel (tph);
-}
-
-
-struct TALER_MERCHANT_TemplatePatchHandle *
-TALER_MERCHANT_template_patch (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *template_id,
- const char *template_description,
- const char *otp_id,
- json_t *template_contract,
- TALER_MERCHANT_TemplatePatchCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_TemplatePatchHandle *tph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("template_description",
- template_description),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("otp_id",
- otp_id)),
- GNUNET_JSON_pack_object_incref ("template_contract",
- (json_t *) template_contract));
- tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle);
- tph->ctx = ctx;
- tph->cb = cb;
- tph->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/templates/%s",
- template_id);
- tph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == tph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (tph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&tph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (tph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_PATCH));
- tph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- tph->post_ctx.headers,
- &handle_patch_template_finished,
- tph);
- }
- return tph;
-}
-
-
-void
-TALER_MERCHANT_template_patch_cancel (
- struct TALER_MERCHANT_TemplatePatchHandle *tph)
-{
- if (NULL != tph->job)
- {
- GNUNET_CURL_job_cancel (tph->job);
- tph->job = NULL;
- }
- TALER_curl_easy_post_finished (&tph->post_ctx);
- GNUNET_free (tph->url);
- GNUNET_free (tph);
-}
-
-
-/* end of merchant_api_patch_template.c */
diff --git a/src/lib/merchant_api_patch_unit.c b/src/lib/merchant_api_patch_unit.c
@@ -1,291 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_patch_unit.c
- * @brief Implementation of PATCH /private/units/$ID
- * @author Bohdan Potuzhnyi
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a PATCH /private/units/$ID operation.
- */
-struct TALER_MERCHANT_UnitPatchHandle
-{
- /**
- * Fully qualified request URL.
- */
- char *url;
-
- /**
- * In-flight CURL job.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Completion callback.
- */
- TALER_MERCHANT_UnitPatchCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
- /**
- * Keeps POST body and headers alive.
- */
- struct TALER_CURL_PostContext post_ctx;
-};
-
-
-/**
- * Called when the HTTP transfer finishes.
- *
- * @param cls operation handle
- * @param response_code HTTP status (0 on failure)
- * @param response parsed JSON reply (NULL if unavailable)
- */
-static void
-handle_patch_unit_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_UnitPatchHandle *uph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- uph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "PATCH /private/units completed with status %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- case MHD_HTTP_UNAUTHORIZED:
- case MHD_HTTP_FORBIDDEN:
- case MHD_HTTP_NOT_FOUND:
- case MHD_HTTP_CONFLICT:
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response %u/%d for PATCH /private/units\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- uph->cb (uph->cb_cls,
- &hr);
- TALER_MERCHANT_unit_patch_cancel (uph);
-}
-
-
-struct TALER_MERCHANT_UnitPatchHandle *
-TALER_MERCHANT_unit_patch (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *unit_id,
- const char *unit_name_long,
- const char *unit_name_short,
- const json_t *unit_name_long_i18n,
- const json_t *unit_name_short_i18n,
- const bool *unit_allow_fraction,
- const uint32_t *unit_precision_level,
- const bool *unit_active,
- TALER_MERCHANT_UnitPatchCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_UnitPatchHandle *uph;
- json_t *req_obj;
- char *path;
-
- req_obj = json_object ();
- if (NULL == req_obj)
- return NULL;
- if (NULL != unit_name_long)
- {
- if (0 != json_object_set_new (req_obj,
- "unit_name_long",
- json_string (unit_name_long)))
- {
- json_decref (req_obj);
- return NULL;
- }
- }
- if (NULL != unit_name_short)
- {
- if (0 != json_object_set_new (req_obj,
- "unit_name_short",
- json_string (unit_name_short)))
- {
- json_decref (req_obj);
- return NULL;
- }
- }
- if (NULL != unit_name_long_i18n)
- {
- if (0 != json_object_set_new (req_obj,
- "unit_name_long_i18n",
- json_incref ((json_t *) unit_name_long_i18n)))
- {
- json_decref (req_obj);
- return NULL;
- }
- }
- if (NULL != unit_name_short_i18n)
- {
- if (0 != json_object_set_new (req_obj,
- "unit_name_short_i18n",
- json_incref (
- (json_t *) unit_name_short_i18n)))
- {
- json_decref (req_obj);
- return NULL;
- }
- }
- if (NULL != unit_allow_fraction)
- {
- if (0 != json_object_set_new (req_obj,
- "unit_allow_fraction",
- json_boolean (*unit_allow_fraction)))
- {
- json_decref (req_obj);
- return NULL;
- }
- }
- if (NULL != unit_precision_level)
- {
- if (0 != json_object_set_new (req_obj,
- "unit_precision_level",
- json_integer (
- (json_int_t) *unit_precision_level)))
- {
- json_decref (req_obj);
- return NULL;
- }
- }
- if (NULL != unit_active)
- {
- if (0 != json_object_set_new (req_obj,
- "unit_active",
- json_boolean (*unit_active)))
- {
- json_decref (req_obj);
- return NULL;
- }
- }
- if (0 == json_object_size (req_obj))
- {
- json_decref (req_obj);
- GNUNET_break (0);
- return NULL;
- }
-
- GNUNET_asprintf (&path,
- "private/units/%s",
- unit_id);
- uph = GNUNET_new (struct TALER_MERCHANT_UnitPatchHandle);
- uph->ctx = ctx;
- uph->cb = cb;
- uph->cb_cls = cb_cls;
- uph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- if (NULL == uph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to build /private/units/%s URL\n",
- unit_id);
- json_decref (req_obj);
- GNUNET_free (uph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (uph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&uph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (uph->url);
- GNUNET_free (uph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_PATCH));
- uph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- uph->post_ctx.headers,
- &handle_patch_unit_finished,
- uph);
- }
- return uph;
-}
-
-
-void
-TALER_MERCHANT_unit_patch_cancel (struct TALER_MERCHANT_UnitPatchHandle *uph)
-{
- if (NULL != uph->job)
- {
- GNUNET_CURL_job_cancel (uph->job);
- uph->job = NULL;
- }
- TALER_curl_easy_post_finished (&uph->post_ctx);
- GNUNET_free (uph->url);
- GNUNET_free (uph);
-}
-
-
-/* end of merchant_api_patch_unit.c */
diff --git a/src/lib/merchant_api_patch_webhook.c b/src/lib/merchant_api_patch_webhook.c
@@ -1,254 +0,0 @@
-/*
- 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_patch_webhook.c
- * @brief Implementation of the PATCH /webhooks/$ID request
- * of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a PATCH /webhooks/$ID operation.
- */
-struct TALER_MERCHANT_WebhookPatchHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_WebhookPatchCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP PATCH /webhooks/$ID request.
- *
- * @param cls the `struct TALER_MERCHANT_WebhookPatchHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_patch_webhook_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_WebhookPatchHandle *wph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- wph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "PATCH /webhooks/$ID completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_break_op (0);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- wph->cb (wph->cb_cls,
- &hr);
- TALER_MERCHANT_webhook_patch_cancel (wph);
-}
-
-
-struct TALER_MERCHANT_WebhookPatchHandle *
-TALER_MERCHANT_webhook_patch (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *webhook_id,
- const char *event_type,
- const char *url,
- const char *http_method,
- const char *header_template,
- const char *body_template,
- TALER_MERCHANT_WebhookPatchCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_WebhookPatchHandle *wph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("event_type",
- event_type),
- GNUNET_JSON_pack_string ("url",
- url),
- GNUNET_JSON_pack_string ("http_method",
- http_method),
- GNUNET_JSON_pack_string ("header_template",
- header_template),
- GNUNET_JSON_pack_string ("body_template",
- body_template));
- wph = GNUNET_new (struct TALER_MERCHANT_WebhookPatchHandle);
- wph->ctx = ctx;
- wph->cb = cb;
- wph->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/webhooks/%s",
- webhook_id);
- wph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == wph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (wph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (wph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&wph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (wph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_PATCH));
- wph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- wph->post_ctx.headers,
- &handle_patch_webhook_finished,
- wph);
- }
- return wph;
-}
-
-
-void
-TALER_MERCHANT_webhook_patch_cancel (
- struct TALER_MERCHANT_WebhookPatchHandle *wph)
-{
- if (NULL != wph->job)
- {
- GNUNET_CURL_job_cancel (wph->job);
- wph->job = NULL;
- }
- TALER_curl_easy_post_finished (&wph->post_ctx);
- GNUNET_free (wph->url);
- GNUNET_free (wph);
-}
-
-
-/* end of merchant_api_patch_webhook.c */
diff --git a/src/lib/merchant_api_pay_service.c b/src/lib/merchant_api_pay_service.c
@@ -0,0 +1,1070 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-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 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.LIB. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_pay_service.c
+ * @brief Implementation of the the ideology
+ * from the pay_service as copy of
+ * merchant_api_post_order_pay.c
+ * @author Bohdan Potuzhnyi
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "taler/taler_merchant_pay_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <stdio.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_curl_lib.h>
+
+/**
+ * @brief A Pay Handle
+ */
+struct TALER_MERCHANT_OrderPayHandle
+{
+ /**
+ * Reference to the GNUNET CURL execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Callback to invoke with the payment result ("pay" mode).
+ */
+ TALER_MERCHANT_OrderPayCallback cb;
+
+ /**
+ * Closure data for @a cb.
+ */
+ TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *cb_cls;
+
+ /* Mandatory scalars: */
+
+ /**
+ * Base URL of the merchant service.
+ */
+ char *merchant_url;
+
+ /**
+ * Identifier of the order being paid.
+ */
+ char *order_id;
+
+ /**
+ * Session identifier for this payment attempt.
+ */
+ char *session_id;
+
+ /**
+ * Timestamp when the payment request was created.
+ */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /**
+ * Deadline after which refunds are no longer allowed.
+ */
+ struct GNUNET_TIME_Timestamp refund_deadline;
+
+ /**
+ * Wire hash for communicating payment details.
+ */
+ struct TALER_MerchantWireHashP h_wire;
+
+ /**
+ * Indicates whether @a h_wire has been set.
+ */
+ bool has_h_wire;
+
+ /* Wallet mode fields: */
+
+ /**
+ * Indicates whether a contract hash was provided.
+ */
+ bool has_h_contract;
+
+ /**
+ * Hash of the private contract terms (wallet mode only).
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ /**
+ * Indicates whether the merchant public key was provided.
+ */
+ bool has_merchant_pub;
+
+ /**
+ * Merchant’s public key for verifying signatures (wallet mode).
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Indicates whether a choice index was provided.
+ */
+ bool has_choice_index;
+
+ /**
+ * Selected index of the contract choice (for token operations).
+ */
+ int choice_index;
+
+ /**
+ * Legacy: pointer to the amount structure for strcmp checks.
+ */
+ const struct TALER_Amount *amount;
+
+ /**
+ * Legacy: pointer to the maximum fee structure for strcmp checks.
+ */
+ const struct TALER_Amount *max_fee;
+
+ /* Raw arrays as passed in via set_options(): */
+
+ /**
+ * Coins used for payment.
+ */
+ struct
+ {
+ /**
+ * Number of coins provided.
+ */
+ unsigned int num_coins;
+ /**
+ * Array of coins to spend.
+ */
+ const struct TALER_MERCHANT_PayCoin *coins;
+ } coins;
+
+ /**
+ * Input tokens to use (wallet mode).
+ */
+ struct
+ {
+ /**
+ * Number of tokens provided.
+ */
+ unsigned int num_tokens;
+ /**
+ * Array of tokens to redeem.
+ */
+ const struct TALER_MERCHANT_UseToken *tokens;
+ } input_tokens;
+
+ /**
+ * Output tokens expected from the merchant.
+ */
+ struct
+ {
+ /**
+ * Number of output tokens expected.
+ */
+ unsigned int num_output_tokens;
+ /**
+ * Array of expected output tokens.
+ */
+ const struct TALER_MERCHANT_OutputToken *output_tokens;
+ } output_tokens;
+
+ /**
+ * JSON array of token envelope events (from Donau).
+ */
+ json_t *tokens_evs;
+
+ /* Computed once both choice_index and tokens_evs are available: */
+
+ /**
+ * JSON object containing wallet-specific data payload.
+ */
+ json_t *wallet_data;
+
+ /**
+ * Hash code of @a wallet_data for integrity checks.
+ */
+ struct GNUNET_HashCode wallet_data_hash;
+
+ /**
+ * JSON body being constructed for the HTTP POST.
+ */
+ json_t *body;
+
+ /* Final URL and CURL plumbing: */
+
+ /**
+ * Fully formed URL for the POST /order/$ID/pay request.
+ */
+ char *url;
+
+ /**
+ * CURL post context managing headers and body.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+
+ /**
+ * Handle for the asynchronous CURL job.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Flags indicating which payment options have been set.
+ */
+ bool field_seen[TALER_MERCHANT_OrderPayOptionType_LENGTH];
+
+ /**
+ * True if operating in wallet mode (using tokens/contracts).
+ */
+ bool am_wallet;
+
+ /**
+ * Raw JSON data of `donau` for `wallet_data`.
+ */
+ json_t *donau_data;
+};
+
+/**
+ * Parse blindly signed output tokens from response.
+ *
+ * @param token_sigs the JSON array with the token signatures. Can be NULL.
+ * @param tokens where to store the parsed tokens.
+ * @param num_tokens where to store the length of the @a tokens array.
+ */
+static enum GNUNET_GenericReturnValue
+parse_tokens (const json_t *token_sigs,
+ struct TALER_MERCHANT_OutputToken **tokens,
+ unsigned int *num_tokens)
+{
+ GNUNET_array_grow (*tokens,
+ *num_tokens,
+ json_array_size (token_sigs));
+
+ for (unsigned int i = 0; i<(*num_tokens); i++)
+ {
+ struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i];
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_blinded_token_issue_sig ("blind_sig",
+ &token->blinded_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ const json_t *jtoken
+ = json_array_get (token_sigs,
+ i);
+
+ if (NULL == jtoken)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jtoken,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /pay request.
+ *
+ * @param cls the `struct TALER_MERCHANT_Pay`
+ * @param response_code HTTP response code, 0 on error
+ * @param resp response body, NULL if not in JSON
+ */
+static void
+handle_finished (void *cls,
+ long response_code,
+ const void *resp)
+{
+ struct TALER_MERCHANT_OrderPayHandle *oph = cls;
+ const json_t *json = resp;
+ struct TALER_MERCHANT_PayResponse pr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received /pay response with status code %u\n",
+ (unsigned int) response_code);
+
+ json_dumpf (json,
+ stderr,
+ JSON_INDENT (2));
+
+ oph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "/pay completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (oph->am_wallet)
+ {
+ const json_t *token_sigs = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("sig",
+ &pr.details.ok.merchant_sig),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("pos_confirmation",
+ &pr.details.ok.pos_confirmation),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("token_sigs",
+ &token_sigs),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "sig field missing in response";
+ break;
+ }
+
+ if (GNUNET_OK !=
+ parse_tokens (token_sigs,
+ &pr.details.ok.tokens,
+ &pr.details.ok.num_tokens))
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "failed to parse token_sigs field in response";
+ break;
+ }
+
+ if (GNUNET_OK !=
+ TALER_merchant_pay_verify (&oph->h_contract_terms,
+ &oph->merchant_pub,
+ &pr.details.ok.merchant_sig))
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "signature invalid";
+ }
+ }
+ break;
+ /* Tolerating Not Acceptable because sometimes
+ * - especially in tests - we might want to POST
+ * coins one at a time. */
+ case MHD_HTTP_NOT_ACCEPTABLE:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ /* was originally paid, but then refunded */
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_REQUEST_TIMEOUT:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* The merchant couldn't generate a timely response, likely because
+ it itself waited too long on the exchange.
+ Pass on to application. */
+ break;
+ case MHD_HTTP_CONFLICT:
+ TALER_MERCHANT_parse_error_details_ (json,
+ MHD_HTTP_CONFLICT,
+ &pr.hr);
+ break;
+ case MHD_HTTP_GONE:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* The merchant says we are too late, the offer has expired or some
+ denomination key of a coin involved has expired.
+ Might be a disagreement in timestamps? Still, pass on to application. */
+ break;
+ case MHD_HTTP_PRECONDITION_FAILED:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* Nothing really to verify, the merchant is blaming us for failing to
+ satisfy some constraint (likely it does not like our exchange because
+ of some disagreement on the PKI). We should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
+ {
+ json_t *ebus = json_object_get (json,
+ "exchange_base_urls");
+ if (NULL == ebus)
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "failed to parse exchange_base_urls field in response";
+ break;
+ }
+ {
+ size_t alen = json_array_size (ebus);
+ const char *ebua[GNUNET_NZL (alen)];
+ size_t idx;
+ json_t *jebu;
+ bool ok = true;
+
+ GNUNET_assert (alen <= UINT_MAX);
+ json_array_foreach (ebus, idx, jebu)
+ {
+ ebua[idx] = json_string_value (jebu);
+ if (NULL == ebua[idx])
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "non-string value in exchange_base_urls in response";
+ ok = false;
+ break;
+ }
+ }
+ if (! ok)
+ break;
+ pr.details.unavailable_for_legal_reasons.num_exchanges
+ = (unsigned int) alen;
+ pr.details.unavailable_for_legal_reasons.exchanges
+ = ebua;
+ oph->cb (oph->cb_cls,
+ &pr);
+ TALER_MERCHANT_order_pay_cancel1 (oph);
+ return;
+ }
+ }
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ case MHD_HTTP_BAD_GATEWAY:
+ /* Nothing really to verify, the merchant is blaming the exchange.
+ We should pass the JSON reply to the application */
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ break;
+ case MHD_HTTP_SERVICE_UNAVAILABLE:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* Exchange couldn't respond properly; the retry is
+ left to the application */
+ break;
+ case MHD_HTTP_GATEWAY_TIMEOUT:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* Exchange couldn't respond in a timely fashion;
+ the retry is left to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) pr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ oph->cb (oph->cb_cls,
+ &pr);
+
+ if (pr.details.ok.tokens)
+ {
+ GNUNET_free (pr.details.ok.tokens);
+ pr.details.ok.tokens = NULL;
+ pr.details.ok.num_tokens = 0;
+ }
+
+ TALER_MERCHANT_order_pay_cancel1 (oph);
+}
+
+
+/**
+ * @brief Create and initialize a new payment handle
+ *
+ * Allocates a TALER_MERCHANT_OrderPayHandle, sets up its context and
+ * prepares an empty JSON body for the /orders/$ID/pay request.
+ */
+struct TALER_MERCHANT_OrderPayHandle *
+TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx,
+ TALER_MERCHANT_OrderPayCallback cb,
+ TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE
+ *cb_cls)
+{
+ struct TALER_MERCHANT_OrderPayHandle *ph =
+ GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
+ ph->ctx = ctx;
+ ph->cb = cb;
+ ph->cb_cls = cb_cls;
+ ph->body = json_object ();
+ GNUNET_assert (ph->body);
+ return ph;
+}
+
+
+/**
+ * @brief Cancel and free a payment handle
+ *
+ * Aborts any in-flight CURL job, releases all JSON objects and internal
+ * buffers, and frees the handle structure itself.
+ */
+void
+TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph)
+{
+ if (ph->job)
+ GNUNET_CURL_job_cancel (ph->job);
+ ph->job = NULL;
+
+ TALER_curl_easy_post_finished (&ph->post_ctx);
+
+ if (ph->body)
+ json_decref (ph->body);
+ ph->body = NULL;
+
+ if (ph->wallet_data)
+ json_decref (ph->wallet_data);
+ ph->wallet_data = NULL;
+
+ if (ph->tokens_evs)
+ json_decref (ph->tokens_evs);
+ ph->tokens_evs = NULL;
+
+ if (ph->donau_data)
+ json_decref (ph->donau_data);
+
+ GNUNET_free (ph->url);
+ GNUNET_free (ph->merchant_url);
+ GNUNET_free (ph->session_id);
+ GNUNET_free (ph->order_id);
+ GNUNET_free (ph);
+}
+
+
+/**
+ * @brief Store a JSON snippet under a payment option key
+ *
+ * Ensures that an option of type @a ot has not already been set,
+ * then merges @a snippet into the handle's JSON @c body. Marks the
+ * field_seen flag and frees @a snippet.
+ *
+ * @param ph payment handle receiving the snippet
+ * @param ot option type under which to store @a snippet
+ * @param snippet JSON object representing the option payload
+ * @return #TALER_MERCHANT_OPOEC_OK if stored; appropriate error code otherwise
+ */
+static enum TALER_MERCHANT_OrderPayErrorCode
+store_json_option (struct TALER_MERCHANT_OrderPayHandle *ph,
+ enum TALER_MERCHANT_OrderPayOptionType ot,
+ json_t *snippet)
+{
+ if (ph->field_seen[ot])
+ {
+ json_decref (snippet);
+ return TALER_MERCHANT_OPOEC_DUPLICATE_OPTION;
+ }
+ ph->field_seen[ot] = true;
+ GNUNET_assert (0 == json_object_update (ph->body,
+ snippet));
+ json_decref (snippet);
+ return TALER_MERCHANT_OPOEC_OK;
+}
+
+
+/**
+ * @brief Apply user-supplied options to a payment handle
+ *
+ * Iterates through a NULL-terminated array of #TALER_MERCHANT_OrderPayOption
+ * entries, validates and stores each into @a ph. Handles JSON packing and
+ * internal state updates for coins, tokens, deadlines, Donau data, etc.
+ */
+enum TALER_MERCHANT_OrderPayErrorCode
+TALER_MERCHANT_order_pay_set_options (
+ struct TALER_MERCHANT_OrderPayHandle *ph,
+ const struct TALER_MERCHANT_OrderPayOption options[],
+ size_t max_options)
+{
+ for (size_t i = 0; i < max_options
+ && options[i].ot != TALER_MERCHANT_OrderPayOptionType_END; i++)
+ {
+ const struct TALER_MERCHANT_OrderPayOption *o = &options[i];
+
+ switch (o->ot)
+ {
+ case TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL:
+ ph->merchant_url = GNUNET_strdup (o->details.merchant_url);
+ break;
+
+ case TALER_MERCHANT_OrderPayOptionType_SESSION_ID:
+ ph->session_id = GNUNET_strdup (o->details.session_id);
+ /* add straight into JSON body */
+ {
+ json_t *js = GNUNET_JSON_PACK (GNUNET_JSON_pack_string ("session_id",
+ o->details.
+ session_id));
+ enum TALER_MERCHANT_OrderPayErrorCode ec =
+ store_json_option (ph,
+ o->ot,
+ js);
+ if (TALER_MERCHANT_OPOEC_OK != ec)
+ return ec;
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_ORDER_ID:
+ {
+ ph->order_id = GNUNET_strdup (o->details.order_id);
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_H_CONTRACT:
+ {
+ ph->h_contract_terms = *o->details.h_contract;
+ ph->has_h_contract = true;
+
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX:
+ ph->choice_index = o->details.choice_index;
+ ph->has_choice_index = true;
+ break;
+
+ case TALER_MERCHANT_OrderPayOptionType_AMOUNT:
+ {
+ ph->amount = &o->details.amount;
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_MAX_FEE:
+ {
+ ph->max_fee = &o->details.max_fee;
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB:
+ {
+ ph->merchant_pub = o->details.merchant_pub;
+ ph->has_merchant_pub = true;
+
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_TIMESTAMP:
+ {
+ ph->timestamp = o->details.timestamp;
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE:
+ {
+ ph->refund_deadline = o->details.refund_deadline;
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE:
+ {
+ /* FIXME: This one comes from the merchant_api_post_order_pay
+ no idea do we still need it or not? */
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_H_WIRE:
+ {
+ ph->h_wire = o->details.h_wire;
+ ph->has_h_wire = true;
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_COINS:
+ /* stash for later signing */
+ ph->coins.num_coins = o->details.coins.num_coins;
+ ph->coins.coins = o->details.coins.coins;
+ break;
+
+ case TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS:
+ /* stash for later signing */
+ ph->input_tokens.num_tokens = o->details.input_tokens.num_tokens;
+ ph->input_tokens.tokens = o->details.input_tokens.tokens;
+ break;
+
+ case TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS:
+ /* store JSON array directly, *and* stash for hash */
+ ph->output_tokens.num_output_tokens =
+ o->details.output_tokens.num_output_tokens;
+ ph->output_tokens.output_tokens = o->details.output_tokens.output_tokens;
+ {
+ /* build and store tokens_evs */
+ json_t *arr = json_array ();
+
+ GNUNET_assert (NULL != arr);
+ for (unsigned j = 0; j < ph->output_tokens.num_output_tokens; j++)
+ {
+ const struct TALER_MERCHANT_OutputToken *otk =
+ &ph->output_tokens.output_tokens[j];
+ json_t *je = GNUNET_JSON_PACK (TALER_JSON_pack_token_envelope (NULL,
+ &otk->
+ envelope));
+ GNUNET_assert (0 ==
+ json_array_append_new (arr,
+ je));
+ }
+
+ ph->tokens_evs = arr;
+
+ ph->field_seen[o->ot] = true;
+ }
+ break;
+
+ case TALER_MERCHANT_OrderPayOptionType_DONAU_URL:
+ if (NULL == ph->donau_data)
+ ph->donau_data = json_object ();
+ GNUNET_assert (0 ==
+ json_object_set_new (
+ ph->donau_data,
+ "url",
+ json_string (o->details.donau_url)));
+ break;
+
+ case TALER_MERCHANT_OrderPayOptionType_DONAU_YEAR:
+ {
+ if (ph->donau_data == NULL)
+ ph->donau_data = json_object ();
+ GNUNET_assert (0 == json_object_set_new (
+ ph->donau_data,
+ "year",
+ json_integer ((json_int_t) o->details.donau_year)));
+ break;
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_DONAU_BUDIS:
+ {
+ if (ph->donau_data == NULL)
+ ph->donau_data = json_object ();
+ GNUNET_assert (0 == json_object_set_new (
+ ph->donau_data,
+ "budikeypairs",
+ json_incref (o->details.donau_budis_json)));
+ break;
+ }
+
+ default:
+ return TALER_MERCHANT_OPOEC_UNKNOWN_OPTION;
+ }
+ }
+ return TALER_MERCHANT_OPOEC_OK;
+}
+
+
+/**
+ * @brief Dispatch the /orders/$ID/pay request
+ *
+ * Validates that all mandatory parameters (merchant_url, order_id, coins)
+ * have been set, builds the final JSON payload, constructs the URL,
+ * and issues an asynchronous HTTP POST. The payment handle's callback
+ * will receive completion notifications.
+ */
+enum TALER_MERCHANT_OrderPayErrorCode
+TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph)
+{
+ /* all the old mandatory checks */
+ if ( (! ph->merchant_url) ||
+ (! ph->order_id) )
+ {
+ GNUNET_break (0);
+ return TALER_MERCHANT_OPOEC_MISSING_MANDATORY;
+ }
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (ph->amount,
+ ph->max_fee))
+ {
+ GNUNET_break (0);
+ return TALER_MERCHANT_OPOEC_INVALID_VALUE;
+ }
+
+ /* build wallet_data hash for signing coins & tokens */
+ if (ph->has_choice_index)
+ {
+ /* base fields */
+ json_t *wd = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("choice_index",
+ ph->choice_index),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_incref ("tokens_evs",
+ ph->tokens_evs))
+ );
+
+ /* Putting prepared donau_data into the wallet_data */
+ if (ph->donau_data)
+ GNUNET_assert (0 == json_object_set_new (
+ wd,
+ "donau",
+ json_incref (ph->donau_data)));
+
+ ph->wallet_data = wd;
+
+ TALER_json_hash (ph->wallet_data,
+ &ph->wallet_data_hash);
+
+ store_json_option (ph,
+ TALER_MERCHANT_OrderPayOptionType_WALLET_DATA,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_object_incref ("wallet_data",
+ ph->wallet_data)));
+ }
+
+ /* sign coins AND build the “coins” JSON in one pass */
+ {
+ struct TALER_Amount total_fee;
+ struct TALER_Amount total_amount;
+ json_t *arr = json_array ();
+
+ GNUNET_assert (NULL != arr);
+ for (unsigned i = 0; i < ph->coins.num_coins; i++)
+ {
+ const struct TALER_MERCHANT_PayCoin *c = &ph->coins.coins[i];
+ struct TALER_MERCHANT_PaidCoin pc;
+ json_t *je;
+
+ /* sign */
+ struct TALER_Amount fee;
+ struct TALER_DenominationHashP h_denom_pub;
+
+ TALER_denom_pub_hash (&c->denom_pub,
+ &h_denom_pub);
+ if (0 > TALER_amount_subtract (&fee,
+ &c->amount_with_fee,
+ &c->amount_without_fee))
+ return TALER_MERCHANT_OPOEC_INVALID_VALUE;
+
+
+ TALER_wallet_deposit_sign (&c->amount_with_fee,
+ &fee,
+ &ph->h_wire,
+ &ph->h_contract_terms,
+ (NULL != ph->wallet_data)
+ ? &ph->wallet_data_hash
+ : NULL,
+ c->h_age_commitment,
+ NULL,
+ &h_denom_pub,
+ ph->timestamp,
+ &ph->merchant_pub,
+ ph->refund_deadline,
+ &c->coin_priv,
+ &pc.coin_sig);
+
+ pc.denom_pub = c->denom_pub;
+ pc.denom_sig = c->denom_sig;
+ pc.denom_value = c->denom_value;
+ pc.amount_with_fee = c->amount_with_fee;
+ pc.amount_without_fee = c->amount_without_fee;
+ pc.exchange_url = c->exchange_url;
+ GNUNET_CRYPTO_eddsa_key_get_public (&c->coin_priv.eddsa_priv,
+ &pc.coin_pub.eddsa_pub);
+
+ /* JSON */
+ je = GNUNET_JSON_PACK (TALER_JSON_pack_amount ("contribution",
+ &pc.amount_with_fee),
+ GNUNET_JSON_pack_data_auto ("coin_pub",
+ &pc.coin_pub),
+ GNUNET_JSON_pack_string ("exchange_url",
+ pc.exchange_url),
+ GNUNET_JSON_pack_data_auto ("h_denom",
+ &h_denom_pub),
+ TALER_JSON_pack_denom_sig ("ub_sig",
+ &pc.denom_sig),
+ GNUNET_JSON_pack_data_auto ("coin_sig",
+ &pc.coin_sig));
+ GNUNET_assert (0 ==
+ json_array_append_new (arr,
+ je));
+
+ /* optional totals if you need them later
+ (kept here because they existed in the legacy code) */
+ if (0 == i)
+ {
+ total_fee = fee;
+ total_amount = pc.amount_with_fee;
+ }
+ else
+ {
+ if ( (0 >
+ TALER_amount_add (&total_fee,
+ &total_fee,
+ &fee)) ||
+ (0 >
+ TALER_amount_add (&total_amount,
+ &total_amount,
+ &pc.amount_with_fee)) )
+ {
+ return TALER_MERCHANT_OPOEC_INVALID_VALUE;
+ }
+ }
+ }
+
+ /* Putting coins to the body*/
+ {
+ enum TALER_MERCHANT_OrderPayErrorCode ec =
+ store_json_option (ph,
+ TALER_MERCHANT_OrderPayOptionType_COINS,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("coins",
+ arr)
+ ));
+ if (TALER_MERCHANT_OPOEC_OK != ec)
+ {
+ return ec;
+ }
+ }
+ }
+
+ /* sign & pack input_tokens into used_tokens array in body */
+ if (ph->input_tokens.num_tokens > 0)
+ {
+ struct TALER_MERCHANT_UsedToken ut[ph->input_tokens.num_tokens];
+ json_t *arr = json_array ();
+
+ GNUNET_assert (NULL != arr);
+ for (unsigned i = 0; i < ph->input_tokens.num_tokens; i++)
+ {
+ json_t *je;
+ const struct TALER_MERCHANT_UseToken *in = &ph->input_tokens.tokens[i];
+ struct TALER_MERCHANT_UsedToken *t = &ut[i];
+
+ TALER_wallet_token_use_sign (&ph->h_contract_terms,
+ &ph->wallet_data_hash,
+ &in->token_priv,
+ &t->token_sig);
+
+ t->ub_sig = in->ub_sig;
+ t->issue_pub = in->issue_pub;
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&in->token_priv.private_key,
+ &t->token_pub.public_key);
+
+ je = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("token_sig",
+ &t->token_sig),
+ TALER_JSON_pack_token_issue_sig ("ub_sig",
+ &t->ub_sig),
+ GNUNET_JSON_pack_data_auto ("h_issue",
+ &t->issue_pub.public_key->pub_key_hash),
+ GNUNET_JSON_pack_data_auto ("token_pub",
+ &t->token_pub)
+ );
+ GNUNET_assert (0 ==
+ json_array_append_new (arr,
+ je));
+ }
+
+ store_json_option (ph,
+ TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("tokens",
+ arr)
+ )
+ );
+ }
+
+
+ /* post the request */
+ {
+ char *path;
+ CURL *eh;
+ GNUNET_asprintf (&path,
+ "orders/%s/pay",
+ ph->order_id);
+ ph->url = TALER_url_join (ph->merchant_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+
+ if (NULL == ph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (ph->body);
+ GNUNET_free (ph);
+ return TALER_MERCHANT_OPOEC_URL_FAILURE;
+ }
+
+ eh = TALER_MERCHANT_curl_easy_get_ (ph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&ph->post_ctx,
+ eh,
+ ph->body))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ GNUNET_free (ph->url);
+ GNUNET_free (ph);
+ return TALER_MERCHANT_OPOEC_CURL_FAILURE;
+ }
+
+ ph->job = GNUNET_CURL_job_add2 (ph->ctx,
+ eh,
+ ph->post_ctx.headers,
+ &handle_finished,
+ ph);
+
+ ph->am_wallet = true;
+ return TALER_MERCHANT_OPOEC_OK;
+ }
+}
diff --git a/src/lib/merchant_api_post-management-instances-INSTANCE-auth.c b/src/lib/merchant_api_post-management-instances-INSTANCE-auth.c
@@ -0,0 +1,227 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2021 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_post-management-instances-INSTANCE-auth.c
+ * @brief Implementation of the POST /instance/$ID/private/auth request
+ * @author Christian Grothoff
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /instances/$ID/private/auth operation.
+ */
+struct TALER_MERCHANT_InstanceAuthPostHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_InstanceAuthPostCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /instances/$ID/private/auth request.
+ *
+ * @param cls the `struct TALER_MERCHANT_InstanceAuthPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_instance_auth_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_InstanceAuthPostHandle *iaph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ iaph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /instances/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* happens if the auth token is malformed */
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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) hr.ec);
+ break;
+ }
+ iaph->cb (iaph->cb_cls,
+ &hr);
+ TALER_MERCHANT_instance_auth_post_cancel (iaph);
+}
+
+
+struct TALER_MERCHANT_InstanceAuthPostHandle *
+TALER_MERCHANT_instance_auth_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ const char *auth_password,
+ TALER_MERCHANT_InstanceAuthPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_InstanceAuthPostHandle *iaph;
+ json_t *req_obj;
+
+ iaph = GNUNET_new (struct TALER_MERCHANT_InstanceAuthPostHandle);
+ iaph->ctx = ctx;
+ iaph->cb = cb;
+ iaph->cb_cls = cb_cls;
+ if (NULL != instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "management/instances/%s/auth",
+ instance_id);
+ iaph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ /* backend_url is already identifying the instance */
+ iaph->url = TALER_url_join (backend_url,
+ "private/auth",
+ NULL);
+ }
+ if (NULL == iaph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (iaph);
+ return NULL;
+ }
+ if (NULL == auth_password)
+ {
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("method",
+ "external"));
+ }
+ else
+ {
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("method",
+ "token"),
+ GNUNET_JSON_pack_string ("password",
+ auth_password));
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ iaph->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (iaph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&iaph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (iaph->url);
+ GNUNET_free (iaph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_POST));
+ iaph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ iaph->post_ctx.headers,
+ &handle_post_instance_auth_finished,
+ iaph);
+ }
+ return iaph;
+}
+
+
+void
+TALER_MERCHANT_instance_auth_post_cancel (
+ struct TALER_MERCHANT_InstanceAuthPostHandle *iaph)
+{
+ if (NULL != iaph->job)
+ GNUNET_CURL_job_cancel (iaph->job);
+ TALER_curl_easy_post_finished (&iaph->post_ctx);
+ GNUNET_free (iaph->url);
+ GNUNET_free (iaph);
+}
diff --git a/src/lib/merchant_api_post-management-instances.c b/src/lib/merchant_api_post-management-instances.c
@@ -0,0 +1,280 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-management-instances.c
+ * @brief Implementation of the POST /instances request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+#include <taler/taler_kyclogic_lib.h>
+
+
+/**
+ * Handle for a POST /instances/$ID operation.
+ */
+struct TALER_MERCHANT_InstancesPostHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_InstancesPostCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /instances request.
+ *
+ * @param cls the `struct TALER_MERCHANT_InstancesPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_instances_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_InstancesPostHandle *iph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ iph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /management/instances completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ iph->cb (iph->cb_cls,
+ &hr);
+ TALER_MERCHANT_instances_post_cancel (iph);
+}
+
+
+struct TALER_MERCHANT_InstancesPostHandle *
+TALER_MERCHANT_instances_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ const char *name,
+ const json_t *address,
+ const json_t *jurisdiction,
+ bool use_stefan,
+ struct GNUNET_TIME_Relative default_wire_transfer_delay,
+ struct GNUNET_TIME_Relative default_pay_delay,
+ struct GNUNET_TIME_Relative default_refund_delay,
+ const char *auth_password,
+ TALER_MERCHANT_InstancesPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_InstancesPostHandle *iph;
+ json_t *req_obj;
+ json_t *auth_obj;
+
+ if (NULL != auth_password)
+ {
+ auth_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("method",
+ "token"),
+ GNUNET_JSON_pack_string ("password",
+ auth_password));
+ }
+ else
+ {
+ auth_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("method",
+ "external"));
+ }
+ if (NULL == auth_obj)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("id",
+ instance_id),
+ GNUNET_JSON_pack_string ("name",
+ name),
+ GNUNET_JSON_pack_object_incref ("address",
+ (json_t *) address),
+ GNUNET_JSON_pack_object_incref ("jurisdiction",
+ (json_t *) jurisdiction),
+ GNUNET_JSON_pack_bool ("use_stefan",
+ use_stefan),
+ GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay",
+ default_wire_transfer_delay),
+ GNUNET_JSON_pack_time_rel ("default_pay_delay",
+ default_pay_delay),
+ GNUNET_JSON_pack_time_rel ("default_refund_delay",
+ default_refund_delay),
+ /* FIXME: add eventually to arguments when we transform the API... */
+ GNUNET_JSON_pack_time_rounder_interval (
+ "default_wire_transfer_rounding_interval",
+ GNUNET_TIME_RI_NONE),
+ GNUNET_JSON_pack_object_steal ("auth",
+ auth_obj));
+ iph = GNUNET_new (struct TALER_MERCHANT_InstancesPostHandle);
+ iph->ctx = ctx;
+ iph->cb = cb;
+ iph->cb_cls = cb_cls;
+ iph->url = TALER_url_join (backend_url,
+ "management/instances",
+ NULL);
+ if (NULL == iph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (iph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (iph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&iph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (iph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ iph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ iph->post_ctx.headers,
+ &handle_post_instances_finished,
+ iph);
+ }
+ return iph;
+}
+
+
+void
+TALER_MERCHANT_instances_post_cancel (
+ struct TALER_MERCHANT_InstancesPostHandle *iph)
+{
+ if (NULL != iph->job)
+ {
+ GNUNET_CURL_job_cancel (iph->job);
+ iph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&iph->post_ctx);
+ GNUNET_free (iph->url);
+ GNUNET_free (iph);
+}
+
+
+/* end of merchant_api_post_instances.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-abort.c b/src/lib/merchant_api_post-orders-ORDER_ID-abort.c
@@ -0,0 +1,436 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-orders-ORDER_ID-abort.c
+ * @brief Implementation of the POST /orders/$ID/abort request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Maximum number of refunds we return.
+ */
+#define MAX_REFUNDS 1024
+
+
+/**
+ * @brief An abort Handle
+ */
+struct TALER_MERCHANT_OrderAbortHandle
+{
+ /**
+ * Hash of the contract.
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ /**
+ * Public key of the merchant.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_AbortCallback abort_cb;
+
+ /**
+ * Closure for @a abort_cb.
+ */
+ void *abort_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;
+
+ /**
+ * The coins we are aborting on.
+ */
+ struct TALER_MERCHANT_AbortCoin *coins;
+
+ /**
+ * Number of @e coins we are paying with.
+ */
+ unsigned int num_coins;
+
+};
+
+
+/**
+ * Check that the response for an abort is well-formed,
+ * and call the application callback with the result if it is
+ * OK. Otherwise returns #GNUNET_SYSERR.
+ *
+ * @param oah handle to operation that created the reply
+ * @param[in] ar abort response, partially initialized
+ * @param json the reply to parse
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah,
+ struct TALER_MERCHANT_AbortResponse *ar,
+ const json_t *json)
+{
+ const json_t *refunds;
+ unsigned int num_refunds;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("refunds",
+ &refunds),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ num_refunds = (unsigned int) json_array_size (refunds);
+ if ( (json_array_size (refunds) != (size_t) num_refunds) ||
+ (num_refunds > MAX_REFUNDS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ {
+ struct TALER_MERCHANT_AbortedCoin res[GNUNET_NZL (num_refunds)];
+
+ for (unsigned int i = 0; i<num_refunds; i++)
+ {
+ json_t *refund = json_array_get (refunds, i);
+ uint32_t exchange_status;
+ struct GNUNET_JSON_Specification spec_es[] = {
+ GNUNET_JSON_spec_uint32 ("exchange_status",
+ &exchange_status),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (refund,
+ spec_es,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (MHD_HTTP_OK == exchange_status)
+ {
+ struct GNUNET_JSON_Specification spec_detail[] = {
+ GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+ &res[i].exchange_sig),
+ GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+ &res[i].exchange_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (refund,
+ spec_detail,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK !=
+ TALER_exchange_online_refund_confirmation_verify (
+ &oah->h_contract_terms,
+ &oah->coins[i].coin_pub,
+ &oah->merchant_pub,
+ 0, /* transaction id */
+ &oah->coins[i].amount_with_fee,
+ &res[i].exchange_pub,
+ &res[i].exchange_sig))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+ ar->details.ok.merchant_pub = &oah->merchant_pub;
+ ar->details.ok.num_aborts = num_refunds;
+ ar->details.ok.aborts = res;
+ oah->abort_cb (oah->abort_cb_cls,
+ ar);
+ oah->abort_cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * abort request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderAbortHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_abort_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderAbortHandle *oah = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_AbortResponse ar = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ oah->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "/pay completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK ==
+ check_abort_refund (oah,
+ &ar,
+ json))
+ {
+ TALER_MERCHANT_order_abort_cancel (oah);
+ return;
+ }
+ ar.hr.http_status = 0;
+ ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ ar.hr.ec = TALER_JSON_get_error_code (json);
+ ar.hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us or the
+ merchant is buggy (or API version conflict); just
+ pass JSON reply to the application */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ ar.hr.ec = TALER_JSON_get_error_code (json);
+ ar.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ ar.hr.ec = TALER_JSON_get_error_code (json);
+ ar.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_REQUEST_TIMEOUT:
+ ar.hr.ec = TALER_JSON_get_error_code (json);
+ ar.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says one of
+ the signatures is invalid; as we checked them,
+ this should never happen, we should pass the JSON
+ reply to the application */
+ break;
+ case MHD_HTTP_PRECONDITION_FAILED:
+ /* Our *payment* already succeeded fully. */
+ ar.hr.ec = TALER_JSON_get_error_code (json);
+ ar.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ ar.hr.ec = TALER_JSON_get_error_code (json);
+ ar.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ case MHD_HTTP_BAD_GATEWAY:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &ar.hr);
+ /* Nothing really to verify, the merchant is blaming the exchange.
+ We should pass the JSON reply to the application */
+ break;
+ default:
+ /* unexpected response code */
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &ar.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) ar.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ oah->abort_cb (oah->abort_cb_cls,
+ &ar);
+ TALER_MERCHANT_order_abort_cancel (oah);
+}
+
+
+struct TALER_MERCHANT_OrderAbortHandle *
+TALER_MERCHANT_order_abort (
+ struct GNUNET_CURL_Context *ctx,
+ const char *merchant_url,
+ const char *order_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_PrivateContractHashP *h_contract,
+ unsigned int num_coins,
+ const struct TALER_MERCHANT_AbortCoin coins[static num_coins],
+ TALER_MERCHANT_AbortCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OrderAbortHandle *oah;
+ json_t *abort_obj;
+ json_t *j_coins;
+
+ j_coins = json_array ();
+ if (NULL == j_coins)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ for (unsigned int i = 0; i<num_coins; i++)
+ {
+ const struct TALER_MERCHANT_AbortCoin *ac = &coins[i];
+ json_t *j_coin;
+
+ /* create JSON for this coin */
+ j_coin = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("coin_pub",
+ &ac->coin_pub),
+ /* FIXME: no longer needed since **v18**, remove eventually! */
+ TALER_JSON_pack_amount ("contribution",
+ &ac->amount_with_fee),
+ GNUNET_JSON_pack_string ("exchange_url",
+ ac->exchange_url));
+ if (0 !=
+ json_array_append_new (j_coins,
+ j_coin))
+ {
+ GNUNET_break (0);
+ json_decref (j_coins);
+ return NULL;
+ }
+ }
+ abort_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("coins",
+ j_coins),
+ GNUNET_JSON_pack_data_auto ("h_contract",
+ h_contract));
+ oah = GNUNET_new (struct TALER_MERCHANT_OrderAbortHandle);
+ oah->h_contract_terms = *h_contract;
+ oah->merchant_pub = *merchant_pub;
+ oah->ctx = ctx;
+ oah->abort_cb = cb;
+ oah->abort_cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/abort",
+ order_id);
+ oah->url = TALER_url_join (merchant_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == oah->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (abort_obj);
+ GNUNET_free (oah);
+ return NULL;
+ }
+ oah->num_coins = num_coins;
+ oah->coins = GNUNET_new_array (num_coins,
+ struct TALER_MERCHANT_AbortCoin);
+ GNUNET_memcpy (oah->coins,
+ coins,
+ num_coins * sizeof (struct TALER_MERCHANT_AbortCoin));
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (oah->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&oah->post_ctx,
+ eh,
+ abort_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (abort_obj);
+ GNUNET_free (oah);
+ return NULL;
+ }
+ json_decref (abort_obj);
+ oah->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ oah->post_ctx.headers,
+ &handle_abort_finished,
+ oah);
+ }
+ return oah;
+}
+
+
+void
+TALER_MERCHANT_order_abort_cancel (
+ struct TALER_MERCHANT_OrderAbortHandle *oah)
+{
+ if (NULL != oah->job)
+ {
+ GNUNET_CURL_job_cancel (oah->job);
+ oah->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&oah->post_ctx);
+ GNUNET_free (oah->coins);
+ GNUNET_free (oah->url);
+ GNUNET_free (oah);
+}
+
+
+/* end of merchant_api_post_order_abort.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-claim.c b/src/lib/merchant_api_post-orders-ORDER_ID-claim.c
@@ -0,0 +1,242 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-orders-ORDER_ID-claim.c
+ * @brief Implementation of POST /orders/$ID/claim
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Structure representing a POST /orders/$ID/claim operation.
+ */
+struct TALER_MERCHANT_OrderClaimHandle
+{
+ /**
+ * Full URL, includes "/orders/$ID/claim".
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OrderClaimCallback 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;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * POST /orders/$ID/claim request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderClaimHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, should be NULL
+ */
+static void
+handle_post_order_claim_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderClaimHandle *och = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_OrderClaimResponse ocr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_object_const (
+ "contract_terms",
+ &ocr.details.ok.contract_terms),
+ GNUNET_JSON_spec_fixed_auto (
+ "sig",
+ &ocr.details.ok.sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ och->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Order claimed with status %u\n",
+ (unsigned int) response_code);
+
+ if (MHD_HTTP_OK != response_code)
+ {
+ ocr.hr.ec = TALER_JSON_get_error_code (json);
+ ocr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Proposal lookup failed with HTTP status code %u/%d\n",
+ (unsigned int) response_code,
+ (int) ocr.hr.ec);
+ och->cb (och->cb_cls,
+ &ocr);
+ TALER_MERCHANT_order_claim_cancel (och);
+ return;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Claiming order failed: could not parse JSON response\n");
+ GNUNET_break_op (0);
+ ocr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ ocr.hr.http_status = 0;
+ och->cb (och->cb_cls,
+ &ocr);
+ TALER_MERCHANT_order_claim_cancel (och);
+ return;
+ }
+
+ if (GNUNET_OK !=
+ TALER_JSON_contract_hash (ocr.details.ok.contract_terms,
+ &ocr.details.ok.h_contract_terms))
+ {
+ GNUNET_break (0);
+ ocr.hr.ec = TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE;
+ ocr.hr.http_status = 0;
+ GNUNET_JSON_parse_free (spec);
+ och->cb (och->cb_cls,
+ &ocr);
+ TALER_MERCHANT_order_claim_cancel (och);
+ return;
+ }
+ och->cb (och->cb_cls,
+ &ocr);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_order_claim_cancel (och);
+}
+
+
+struct TALER_MERCHANT_OrderClaimHandle *
+TALER_MERCHANT_order_claim (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *order_id,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *nonce,
+ const struct TALER_ClaimTokenP *claim_token,
+ TALER_MERCHANT_OrderClaimCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OrderClaimHandle *och;
+ json_t *req_obj;
+
+ if (NULL == order_id)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("nonce",
+ nonce),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_data_auto ("token",
+ claim_token)));
+ och = GNUNET_new (struct TALER_MERCHANT_OrderClaimHandle);
+ och->ctx = ctx;
+ och->cb = cb;
+ och->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/claim",
+ order_id);
+ och->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == och->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (och);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Claiming order at %s\n",
+ och->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (och->url);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_curl_easy_post (&och->post_ctx,
+ eh,
+ req_obj));
+ json_decref (req_obj);
+ och->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ och->post_ctx.headers,
+ &handle_post_order_claim_finished,
+ och);
+ GNUNET_assert (NULL != och->job);
+ }
+ return och;
+}
+
+
+void
+TALER_MERCHANT_order_claim_cancel (struct TALER_MERCHANT_OrderClaimHandle *och)
+{
+ if (NULL != och->job)
+ {
+ GNUNET_CURL_job_cancel (och->job);
+ och->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&och->post_ctx);
+ GNUNET_free (och->url);
+ GNUNET_free (och);
+}
+
+
+/* end of merchant_api_post_order_claim.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-paid.c b/src/lib/merchant_api_post-orders-ORDER_ID-paid.c
@@ -0,0 +1,271 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-2021 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_post-orders-ORDER_ID-paid.c
+ * @brief Implementation of the POST /order/$ID/paid request
+ * of the merchant's HTTP API
+ * @author Jonathan Buchanan
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * @brief Handle to a POST /orders/$ID/paid operation at a merchant.
+ */
+struct TALER_MERCHANT_OrderPaidHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OrderPaidCallback paid_cb;
+
+ /**
+ * Closure for @a paid_cb.
+ */
+ void *paid_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;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /paid request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderPaidHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_paid_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderPaidHandle *oph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_OrderPaidResponse opr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ oph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "/paid completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ bool refunded;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_bool ("refunded",
+ &refunded),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (opr.hr.reply,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ break;
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* The signature provided was invalid */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_CONFLICT:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* The hashed contract terms don't match with the order_id. */
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ case MHD_HTTP_SERVICE_UNAVAILABLE:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &opr.hr);
+ /* Exchange couldn't respond properly; the retry is
+ left to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &opr.hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) opr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ oph->paid_cb (oph->paid_cb_cls,
+ &opr);
+ TALER_MERCHANT_order_paid_cancel (oph);
+}
+
+
+struct TALER_MERCHANT_OrderPaidHandle *
+TALER_MERCHANT_order_paid (
+ struct GNUNET_CURL_Context *ctx,
+ const char *merchant_url,
+ const char *order_id,
+ const char *session_id,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ const struct GNUNET_HashCode *wallet_data_hash,
+ const struct TALER_MerchantSignatureP *merchant_sig,
+ TALER_MERCHANT_OrderPaidCallback paid_cb,
+ void *paid_cb_cls)
+{
+ struct TALER_MERCHANT_OrderPaidHandle *oph;
+ json_t *req_obj;
+
+ (void) wallet_data_hash;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("sig",
+ merchant_sig),
+ GNUNET_JSON_pack_data_auto ("h_contract",
+ h_contract_terms),
+ GNUNET_JSON_pack_string ("session_id",
+ session_id));
+ oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle);
+ oph->ctx = ctx;
+ oph->paid_cb = paid_cb;
+ oph->paid_cb_cls = paid_cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/paid",
+ order_id);
+ oph->url = TALER_url_join (merchant_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == oph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (oph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&oph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (oph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ oph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ oph->post_ctx.headers,
+ &handle_paid_finished,
+ oph);
+ }
+ return oph;
+}
+
+
+void
+TALER_MERCHANT_order_paid_cancel (
+ struct TALER_MERCHANT_OrderPaidHandle *oph)
+{
+ if (NULL != oph->job)
+ {
+ GNUNET_CURL_job_cancel (oph->job);
+ oph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&oph->post_ctx);
+ GNUNET_free (oph->url);
+ GNUNET_free (oph);
+}
+
+
+/* end of merchant_api_post_order_paid.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-pay.c b/src/lib/merchant_api_post-orders-ORDER_ID-pay.c
@@ -0,0 +1,790 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-orders-ORDER_ID-pay.c
+ * @brief Implementation of the POST /order/$ID/pay request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_json_lib.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/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <stdio.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * @brief A Pay Handle
+ */
+struct TALER_MERCHANT_OrderPayHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result in "pay" @e mode.
+ */
+ TALER_MERCHANT_OrderPayCallback pay_cb;
+
+ /**
+ * Closure for @a pay_cb.
+ */
+ void *pay_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;
+
+ /**
+ * The coins we are paying with.
+ */
+ struct TALER_MERCHANT_PaidCoin *coins;
+
+ /**
+ * Hash of the contract we are paying, set
+ * if @e am_wallet is true.
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ /**
+ * Public key of the merchant (instance) being paid, set
+ * if @e am_wallet is true.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * JSON with the full reply, used during async
+ * processing.
+ */
+ json_t *full_reply;
+
+ /**
+ * Pointer into @e coins array for the coin that
+ * created a conflict (that we are checking).
+ */
+ const struct TALER_MERCHANT_PaidCoin *error_pc;
+
+ /**
+ * Coin history that proves a conflict.
+ */
+ json_t *error_history;
+
+ /**
+ * Number of @e coins we are paying with.
+ */
+ unsigned int num_coins;
+
+ /**
+ * Set to true if this is the wallet API and we have
+ * initialized @e h_contract_terms and @e merchant_pub.
+ */
+ bool am_wallet;
+
+};
+
+
+/**
+ * Parse blindly signed output tokens from response.
+ *
+ * @param token_sigs the JSON array with the token signatures. Can be NULL.
+ * @param tokens where to store the parsed tokens.
+ * @param num_tokens where to store the length of the @a tokens array.
+ */
+static enum GNUNET_GenericReturnValue
+parse_tokens (const json_t *token_sigs,
+ struct TALER_MERCHANT_OutputToken **tokens,
+ unsigned int *num_tokens)
+{
+ GNUNET_array_grow (*tokens,
+ *num_tokens,
+ json_array_size (token_sigs));
+
+ for (unsigned int i = 0; i<(*num_tokens); i++)
+ {
+ struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i];
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_blinded_token_issue_sig ("blind_sig",
+ &token->blinded_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ const json_t *jtoken
+ = json_array_get (token_sigs,
+ i);
+
+ if (NULL == jtoken)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jtoken,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /pay request.
+ *
+ * @param cls the `struct TALER_MERCHANT_Pay`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_pay_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderPayHandle *oph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PayResponse pr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received /pay response with status code %u\n",
+ (unsigned int) response_code);
+
+ json_dumpf (json,
+ stderr,
+ JSON_INDENT (2));
+
+ oph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "/pay completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (oph->am_wallet)
+ {
+ const json_t *token_sigs = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("sig",
+ &pr.details.ok.merchant_sig),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("pos_confirmation",
+ &pr.details.ok.pos_confirmation),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("token_sigs",
+ &token_sigs),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "sig field missing in response";
+ break;
+ }
+
+ if (GNUNET_OK !=
+ parse_tokens (token_sigs,
+ &pr.details.ok.tokens,
+ &pr.details.ok.num_tokens))
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "failed to parse token_sigs field in response";
+ break;
+ }
+
+ if (GNUNET_OK !=
+ TALER_merchant_pay_verify (&oph->h_contract_terms,
+ &oph->merchant_pub,
+ &pr.details.ok.merchant_sig))
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "signature invalid";
+ }
+ }
+ break;
+ /* Tolerating Not Acceptable because sometimes
+ * - especially in tests - we might want to POST
+ * coins one at a time. */
+ case MHD_HTTP_NOT_ACCEPTABLE:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ /* was originally paid, but then refunded */
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_REQUEST_TIMEOUT:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* The merchant couldn't generate a timely response, likely because
+ it itself waited too long on the exchange.
+ Pass on to application. */
+ break;
+ case MHD_HTTP_CONFLICT:
+ TALER_MERCHANT_parse_error_details_ (json,
+ MHD_HTTP_CONFLICT,
+ &pr.hr);
+ break;
+ case MHD_HTTP_GONE:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* The merchant says we are too late, the offer has expired or some
+ denomination key of a coin involved has expired.
+ Might be a disagreement in timestamps? Still, pass on to application. */
+ break;
+ case MHD_HTTP_PRECONDITION_FAILED:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* Nothing really to verify, the merchant is blaming us for failing to
+ satisfy some constraint (likely it does not like our exchange because
+ of some disagreement on the PKI). We should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
+ {
+ json_t *ebus = json_object_get (json,
+ "exchange_base_urls");
+ if (NULL == ebus)
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "failed to parse exchange_base_urls field in response";
+ break;
+ }
+ {
+ size_t alen = json_array_size (ebus);
+ const char *ebua[GNUNET_NZL (alen)];
+ size_t idx;
+ json_t *jebu;
+ bool ok = true;
+
+ GNUNET_assert (alen <= UINT_MAX);
+ json_array_foreach (ebus, idx, jebu)
+ {
+ ebua[idx] = json_string_value (jebu);
+ if (NULL == ebua[idx])
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "non-string value in exchange_base_urls in response";
+ ok = false;
+ break;
+ }
+ }
+ if (! ok)
+ break;
+ pr.details.unavailable_for_legal_reasons.num_exchanges
+ = (unsigned int) alen;
+ pr.details.unavailable_for_legal_reasons.exchanges
+ = ebua;
+ oph->pay_cb (oph->pay_cb_cls,
+ &pr);
+ TALER_MERCHANT_order_pay_cancel (oph);
+ return;
+ }
+ }
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ case MHD_HTTP_BAD_GATEWAY:
+ /* Nothing really to verify, the merchant is blaming the exchange.
+ We should pass the JSON reply to the application */
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ break;
+ case MHD_HTTP_SERVICE_UNAVAILABLE:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* Exchange couldn't respond properly; the retry is
+ left to the application */
+ break;
+ case MHD_HTTP_GATEWAY_TIMEOUT:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* Exchange couldn't respond in a timely fashion;
+ the retry is left to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) pr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ oph->pay_cb (oph->pay_cb_cls,
+ &pr);
+ TALER_MERCHANT_order_pay_cancel (oph);
+}
+
+
+struct TALER_MERCHANT_OrderPayHandle *
+TALER_MERCHANT_order_pay_frontend (
+ struct GNUNET_CURL_Context *ctx,
+ const char *merchant_url,
+ const char *order_id,
+ const char *session_id,
+ const json_t *wallet_data,
+ unsigned int num_coins,
+ const struct TALER_MERCHANT_PaidCoin coins[static num_coins],
+ unsigned int num_tokens,
+ const struct TALER_MERCHANT_UsedToken *tokens,
+ json_t *j_output_tokens, // FIXME: not used, remove?
+ TALER_MERCHANT_OrderPayCallback pay_cb,
+ void *pay_cb_cls)
+{
+ struct TALER_MERCHANT_OrderPayHandle *oph;
+ json_t *pay_obj;
+ json_t *j_coins;
+ json_t *j_tokens = NULL;
+ CURL *eh;
+ struct TALER_Amount total_fee;
+ struct TALER_Amount total_amount;
+
+ j_coins = json_array ();
+ GNUNET_assert (NULL != j_coins);
+ for (unsigned int i = 0; i<num_coins; i++)
+ {
+ json_t *j_coin;
+ const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
+ struct TALER_Amount fee;
+ struct TALER_DenominationHashP denom_hash;
+
+ if (0 >
+ TALER_amount_subtract (&fee,
+ &pc->amount_with_fee,
+ &pc->amount_without_fee))
+ {
+ /* Integer underflow, fee larger than total amount?
+ This should not happen (client violated API!) */
+ GNUNET_break (0);
+ json_decref (j_coins);
+ return NULL;
+ }
+ if (0 == i)
+ {
+ total_fee = fee;
+ total_amount = pc->amount_with_fee;
+ }
+ else
+ {
+ if ( (0 >
+ TALER_amount_add (&total_fee,
+ &total_fee,
+ &fee)) ||
+ (0 >
+ TALER_amount_add (&total_amount,
+ &total_amount,
+ &pc->amount_with_fee)) )
+ {
+ /* integer overflow */
+ GNUNET_break (0);
+ json_decref (j_coins);
+ return NULL;
+ }
+ }
+
+ TALER_denom_pub_hash (&pc->denom_pub,
+ &denom_hash);
+ /* create JSON for this coin */
+ j_coin = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("contribution",
+ &pc->amount_with_fee),
+ GNUNET_JSON_pack_data_auto ("coin_pub",
+ &pc->coin_pub),
+ GNUNET_JSON_pack_string ("exchange_url",
+ pc->exchange_url),
+ GNUNET_JSON_pack_data_auto ("h_denom",
+ &denom_hash),
+ TALER_JSON_pack_denom_sig ("ub_sig",
+ &pc->denom_sig),
+ GNUNET_JSON_pack_data_auto ("coin_sig",
+ &pc->coin_sig));
+ if (0 !=
+ json_array_append_new (j_coins,
+ j_coin))
+ {
+ GNUNET_break (0);
+ json_decref (j_coins);
+ return NULL;
+ }
+ }
+
+ if (0 < num_tokens)
+ {
+ j_tokens = json_array ();
+ GNUNET_assert (NULL != j_tokens);
+ for (unsigned int i = 0; i<num_tokens; i++)
+ {
+ json_t *j_token;
+ const struct TALER_MERCHANT_UsedToken *ut = &tokens[i];
+
+ j_token = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("token_sig",
+ &ut->token_sig),
+ GNUNET_JSON_pack_data_auto ("token_pub",
+ &ut->token_pub),
+ GNUNET_JSON_pack_data_auto ("h_issue",
+ &ut->issue_pub.public_key->pub_key_hash),
+ TALER_JSON_pack_token_issue_sig ("ub_sig",
+ &ut->ub_sig));
+ if (0 !=
+ json_array_append_new (j_tokens,
+ j_token))
+ {
+ GNUNET_break (0);
+ json_decref (j_tokens);
+ return NULL;
+ }
+ }
+ }
+
+ pay_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("coins",
+ j_coins),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_steal ("tokens",
+ j_tokens)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("wallet_data",
+ (json_t *) wallet_data)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("session_id",
+ session_id)));
+
+ json_dumpf (pay_obj,
+ stderr,
+ JSON_INDENT (2));
+
+ oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
+ oph->ctx = ctx;
+ oph->pay_cb = pay_cb;
+ oph->pay_cb_cls = pay_cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/pay",
+ order_id);
+ oph->url = TALER_url_join (merchant_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == oph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (pay_obj);
+ GNUNET_free (oph);
+ return NULL;
+ }
+ oph->num_coins = num_coins;
+ oph->coins = GNUNET_new_array (num_coins,
+ struct TALER_MERCHANT_PaidCoin);
+ GNUNET_memcpy (oph->coins,
+ coins,
+ num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
+
+ eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&oph->post_ctx,
+ eh,
+ pay_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (pay_obj);
+ GNUNET_free (oph->url);
+ GNUNET_free (oph);
+ return NULL;
+ }
+ json_decref (pay_obj);
+ oph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ oph->post_ctx.headers,
+ &handle_pay_finished,
+ oph);
+ return oph;
+}
+
+
+struct TALER_MERCHANT_OrderPayHandle *
+TALER_MERCHANT_order_pay (
+ struct GNUNET_CURL_Context *ctx,
+ const char *merchant_url,
+ const char *session_id,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ int choice_index,
+ const struct TALER_Amount *amount,
+ const struct TALER_Amount *max_fee,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_MerchantSignatureP *merchant_sig,
+ struct GNUNET_TIME_Timestamp timestamp,
+ struct GNUNET_TIME_Timestamp refund_deadline,
+ struct GNUNET_TIME_Timestamp pay_deadline,
+ const struct TALER_MerchantWireHashP *h_wire,
+ const char *order_id,
+ unsigned int num_coins,
+ const struct TALER_MERCHANT_PayCoin coins[static num_coins],
+ unsigned int num_tokens,
+ const struct TALER_MERCHANT_UseToken *tokens,
+ unsigned int num_output_tokens,
+ const struct TALER_MERCHANT_OutputToken *output_tokens,
+ TALER_MERCHANT_OrderPayCallback pay_cb,
+ void *pay_cb_cls)
+{
+ json_t *j_output_tokens = NULL;
+ const json_t *wallet_data = NULL;
+ struct GNUNET_HashCode wallet_data_hash;
+
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (amount,
+ max_fee))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ if ((0 < num_tokens || 0 < num_output_tokens) && 0 > choice_index)
+ {
+ /* Tokens (input or output) require a valid choice_index to be set.
+ Only contracts with coices can use or issue tokens. */
+ GNUNET_break (0);
+ return NULL;
+ }
+ if (0 < num_output_tokens)
+ {
+ /* Build token envelopes json array. */
+ j_output_tokens = json_array ();
+ GNUNET_assert (NULL != j_output_tokens);
+ for (unsigned int i = 0; i<num_output_tokens; i++)
+ {
+ json_t *j_token_ev;
+ const struct TALER_MERCHANT_OutputToken *ev = &output_tokens[i];
+
+ j_token_ev = GNUNET_JSON_PACK (
+ TALER_JSON_pack_token_envelope (NULL,
+ &ev->envelope));
+
+ if (0 !=
+ json_array_append_new (j_output_tokens,
+ j_token_ev))
+ {
+ GNUNET_break (0);
+ json_decref (j_output_tokens);
+ return NULL;
+ }
+ }
+ }
+ if (0 <= choice_index)
+ {
+ wallet_data = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("choice_index",
+ choice_index),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_incref ("tokens_evs",
+ j_output_tokens)));
+ TALER_json_hash (wallet_data,
+ &wallet_data_hash);
+ }
+ {
+ struct TALER_MERCHANT_PaidCoin pc[num_coins];
+ struct TALER_MERCHANT_UsedToken ut[num_tokens];
+
+ for (unsigned int i = 0; i<num_coins; i++)
+ {
+ const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
+ struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
+ struct TALER_Amount fee;
+ struct TALER_DenominationHashP h_denom_pub;
+
+ if (0 >
+ TALER_amount_subtract (&fee,
+ &coin->amount_with_fee,
+ &coin->amount_without_fee))
+ {
+ /* Integer underflow, fee larger than total amount?
+ This should not happen (client violated API!) */
+ GNUNET_break (0);
+ return NULL;
+ }
+ TALER_denom_pub_hash (&coin->denom_pub,
+ &h_denom_pub);
+ TALER_wallet_deposit_sign (&coin->amount_with_fee,
+ &fee,
+ h_wire,
+ h_contract_terms,
+ (NULL != wallet_data)
+ ? &wallet_data_hash
+ : NULL,
+ coin->h_age_commitment,
+ NULL /* h_extensions! */,
+ &h_denom_pub,
+ timestamp,
+ merchant_pub,
+ refund_deadline,
+ &coin->coin_priv,
+ &p->coin_sig);
+ p->denom_pub = coin->denom_pub;
+ p->denom_sig = coin->denom_sig;
+ p->denom_value = coin->denom_value;
+ GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
+ &p->coin_pub.eddsa_pub);
+ p->amount_with_fee = coin->amount_with_fee;
+ p->amount_without_fee = coin->amount_without_fee;
+ p->exchange_url = coin->exchange_url;
+ }
+ for (unsigned int i = 0; i<num_tokens; i++)
+ {
+ const struct TALER_MERCHANT_UseToken *token = &tokens[i];
+ struct TALER_MERCHANT_UsedToken *t = &ut[i];
+
+ TALER_wallet_token_use_sign (h_contract_terms,
+ &wallet_data_hash, // checked for != NULL above
+ &token->token_priv,
+ &t->token_sig);
+ t->ub_sig = token->ub_sig;
+ t->issue_pub = token->issue_pub;
+ GNUNET_CRYPTO_eddsa_key_get_public (&token->token_priv.private_key,
+ &t->token_pub.public_key);
+ }
+ {
+ struct TALER_MERCHANT_OrderPayHandle *oph;
+
+ oph = TALER_MERCHANT_order_pay_frontend (ctx,
+ merchant_url,
+ order_id,
+ session_id,
+ wallet_data,
+ num_coins,
+ pc,
+ num_tokens,
+ ut,
+ j_output_tokens,
+ pay_cb,
+ pay_cb_cls);
+ if (NULL == oph)
+ return NULL;
+ oph->h_contract_terms = *h_contract_terms;
+ oph->merchant_pub = *merchant_pub;
+ oph->am_wallet = true;
+ return oph;
+ }
+ }
+}
+
+
+void
+TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
+{
+ if (NULL != oph->job)
+ {
+ GNUNET_CURL_job_cancel (oph->job);
+ oph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&oph->post_ctx);
+ json_decref (oph->error_history);
+ json_decref (oph->full_reply);
+ GNUNET_free (oph->coins);
+ GNUNET_free (oph->url);
+ GNUNET_free (oph);
+}
+
+
+/* end of merchant_api_post_order_pay.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-refund.c b/src/lib/merchant_api_post-orders-ORDER_ID-refund.c
@@ -0,0 +1,345 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-orders-ORDER_ID-refund.c
+ * @brief Implementation of the (public) POST /orders/ID/refund request
+ * @author Jonathan Buchanan
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
+
+/**
+ * Maximum number of refunds we return.
+ */
+#define MAX_REFUNDS 1024
+
+/**
+ * Handle for a (public) POST /orders/ID/refund operation.
+ */
+struct TALER_MERCHANT_WalletOrderRefundHandle
+{
+ /**
+ * Complete URL where the backend offers /refund
+ */
+ char *url;
+
+ /**
+ * Minor context that holds body and headers.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+
+ /**
+ * The CURL context to connect to the backend
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * The callback to pass the backend response to
+ */
+ TALER_MERCHANT_WalletRefundCallback cb;
+
+ /**
+ * Clasure to pass to the callback
+ */
+ void *cb_cls;
+
+ /**
+ * Handle for the request
+ */
+ struct GNUNET_CURL_Job *job;
+};
+
+
+/**
+ * Callback to process (public) POST /orders/ID/refund response
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderRefundHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not JSON
+ */
+static void
+handle_refund_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_WalletOrderRefundHandle *orh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_WalletRefundResponse wrr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ orh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ wrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ const json_t *refunds;
+ unsigned int refund_len;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any (
+ "refund_amount",
+ &wrr.details.ok.refund_amount),
+ GNUNET_JSON_spec_array_const (
+ "refunds",
+ &refunds),
+ GNUNET_JSON_spec_fixed_auto (
+ "merchant_pub",
+ &wrr.details.ok.merchant_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wrr.hr.http_status = 0;
+ break;
+ }
+ refund_len = json_array_size (refunds);
+ if ( (json_array_size (refunds) != (size_t) refund_len) ||
+ (refund_len > MAX_REFUNDS) )
+ {
+ GNUNET_break (0);
+ wrr.hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
+ wrr.hr.http_status = 0;
+ break;
+ }
+ {
+ struct TALER_MERCHANT_RefundDetail rds[GNUNET_NZL (refund_len)];
+
+ memset (rds,
+ 0,
+ sizeof (rds));
+ for (unsigned int i = 0; i<refund_len; i++)
+ {
+ struct TALER_MERCHANT_RefundDetail *rd = &rds[i];
+ const json_t *jrefund = json_array_get (refunds,
+ i);
+ const char *refund_status_type;
+ uint32_t exchange_status;
+ uint32_t eec = 0;
+ struct GNUNET_JSON_Specification espec[] = {
+ GNUNET_JSON_spec_string ("type",
+ &refund_status_type),
+ GNUNET_JSON_spec_uint32 ("exchange_status",
+ &exchange_status),
+ GNUNET_JSON_spec_uint64 ("rtransaction_id",
+ &rd->rtransaction_id),
+ GNUNET_JSON_spec_fixed_auto ("coin_pub",
+ &rd->coin_pub),
+ TALER_JSON_spec_amount_any ("refund_amount",
+ &rd->refund_amount),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("exchange_reply",
+ &rd->hr.reply),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("exchange_code",
+ &eec),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jrefund,
+ espec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wrr.hr.http_status = 0;
+ goto finish;
+ }
+
+ rd->hr.http_status = exchange_status;
+ rd->hr.ec = (enum TALER_ErrorCode) eec;
+ switch (exchange_status)
+ {
+ case MHD_HTTP_OK:
+ {
+ struct GNUNET_JSON_Specification rspec[] = {
+ GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+ &rd->details.ok.exchange_sig),
+ GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+ &rd->details.ok.exchange_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jrefund,
+ rspec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wrr.hr.http_status = 0;
+ goto finish;
+ }
+ /* check that type field is correct */
+ if (0 != strcmp ("success",
+ refund_status_type))
+ {
+ GNUNET_break_op (0);
+ wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wrr.hr.http_status = 0;
+ goto finish;
+ }
+ }
+ break; /* end MHD_HTTP_OK */
+ default:
+ if (0 != strcmp ("failure",
+ refund_status_type))
+ {
+ GNUNET_break_op (0);
+ wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ wrr.hr.http_status = 0;
+ goto finish;
+ }
+ } /* switch on exchange status code */
+ } /* for all refunds */
+
+ wrr.details.ok.refunds = rds;
+ wrr.details.ok.refunds_length = refund_len;
+ orh->cb (orh->cb_cls,
+ &wrr);
+ TALER_MERCHANT_wallet_post_order_refund_cancel (orh);
+ return;
+ } /* end 'rds' scope */
+ } /* case MHD_HTTP_OK */
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_CONFLICT:
+ case MHD_HTTP_NOT_FOUND:
+ wrr.hr.ec = TALER_JSON_get_error_code (json);
+ wrr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ GNUNET_break_op (0); /* unexpected status code */
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &wrr.hr);
+ break;
+ }
+finish:
+ orh->cb (orh->cb_cls,
+ &wrr);
+ TALER_MERCHANT_wallet_post_order_refund_cancel (orh);
+}
+
+
+struct TALER_MERCHANT_WalletOrderRefundHandle *
+TALER_MERCHANT_wallet_post_order_refund (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *order_id,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ TALER_MERCHANT_WalletRefundCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_WalletOrderRefundHandle *orh;
+ json_t *req;
+ CURL *eh;
+
+ orh = GNUNET_new (struct TALER_MERCHANT_WalletOrderRefundHandle);
+ orh->ctx = ctx;
+ orh->cb = cb;
+ orh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/refund",
+ order_id);
+ orh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == orh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (orh);
+ return NULL;
+ }
+ req = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_contract",
+ h_contract_terms));
+ eh = TALER_MERCHANT_curl_easy_get_ (orh->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&orh->post_ctx,
+ eh,
+ req))
+ {
+ GNUNET_break (0);
+ json_decref (req);
+ curl_easy_cleanup (eh);
+ GNUNET_free (orh->url);
+ GNUNET_free (orh);
+ return NULL;
+ }
+ json_decref (req);
+ orh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ orh->post_ctx.headers,
+ &handle_refund_finished,
+ orh);
+ if (NULL == orh->job)
+ {
+ GNUNET_free (orh->url);
+ GNUNET_free (orh);
+ return NULL;
+ }
+ return orh;
+}
+
+
+void
+TALER_MERCHANT_wallet_post_order_refund_cancel (
+ struct TALER_MERCHANT_WalletOrderRefundHandle *orh)
+{
+ if (NULL != orh->job)
+ {
+ GNUNET_CURL_job_cancel (orh->job);
+ orh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&orh->post_ctx);
+ GNUNET_free (orh->url);
+ GNUNET_free (orh);
+}
+
+
+/* end of merchant_api_wallet_post_order_refund.c */
diff --git a/src/lib/merchant_api_post-private-accounts.c b/src/lib/merchant_api_post-private-accounts.c
@@ -0,0 +1,250 @@
+/*
+ This file is part of TALER
+ Copyright (C) 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-private-accounts.c
+ * @brief Implementation of the POST /account request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /private/accounts operation.
+ */
+struct TALER_MERCHANT_AccountsPostHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_AccountsPostCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /account request.
+ *
+ * @param cls the `struct TALER_MERCHANT_AccountPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_account_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_AccountsPostHandle *aph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_AccountsPostResponse apr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ aph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /accounts completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("h_wire",
+ &apr.details.ok.h_wire),
+ GNUNET_JSON_spec_fixed_auto ("salt",
+ &apr.details.ok.salt),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ apr.hr.http_status = 0;
+ apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ GNUNET_break_op (0);
+ apr.hr.ec = TALER_JSON_get_error_code (json);
+ apr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ apr.hr.ec = TALER_JSON_get_error_code (json);
+ apr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ apr.hr.ec = TALER_JSON_get_error_code (json);
+ apr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_CONFLICT:
+ apr.hr.ec = TALER_JSON_get_error_code (json);
+ apr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ apr.hr.ec = TALER_JSON_get_error_code (json);
+ apr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &apr.hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) apr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ aph->cb (aph->cb_cls,
+ &apr);
+ TALER_MERCHANT_accounts_post_cancel (aph);
+}
+
+
+struct TALER_MERCHANT_AccountsPostHandle *
+TALER_MERCHANT_accounts_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ struct TALER_FullPayto payto_uri,
+ const char *credit_facade_url,
+ const json_t *credit_facade_credentials,
+ TALER_MERCHANT_AccountsPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_AccountsPostHandle *aph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ TALER_JSON_pack_full_payto (
+ "payto_uri",
+ payto_uri),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string (
+ "credit_facade_url",
+ credit_facade_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref (
+ "credit_facade_credentials",
+ (json_t *) credit_facade_credentials))
+ );
+ aph = GNUNET_new (struct TALER_MERCHANT_AccountsPostHandle);
+ aph->ctx = ctx;
+ aph->cb = cb;
+ aph->cb_cls = cb_cls;
+ aph->url = TALER_url_join (backend_url,
+ "private/accounts",
+ NULL);
+ if (NULL == aph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (aph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (aph->url);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_curl_easy_post (&aph->post_ctx,
+ eh,
+ req_obj));
+ json_decref (req_obj);
+ aph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ aph->post_ctx.headers,
+ &handle_post_account_finished,
+ aph);
+ GNUNET_assert (NULL != aph->job);
+ }
+ return aph;
+}
+
+
+void
+TALER_MERCHANT_accounts_post_cancel (
+ struct TALER_MERCHANT_AccountsPostHandle *aph)
+{
+ if (NULL != aph->job)
+ {
+ GNUNET_CURL_job_cancel (aph->job);
+ aph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&aph->post_ctx);
+ GNUNET_free (aph->url);
+ GNUNET_free (aph);
+}
+
+
+/* end of merchant_api_post_account.c */
diff --git a/src/lib/merchant_api_post-private-categories.c b/src/lib/merchant_api_post-private-categories.c
@@ -0,0 +1,223 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_post-private-categories.c
+ * @brief Implementation of POST /private/categories
+ * @author Bohdan Potuzhnyi
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+/**
+ * Handle for a POST /private/categories operation.
+ */
+struct TALER_MERCHANT_CategoriesPostHandle
+{
+ /**
+ * Fully qualified request URL.
+ */
+ char *url;
+
+ /**
+ * CURL job handle.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Callback to invoke with the result.
+ */
+ TALER_MERCHANT_CategoriesPostCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Helper keeping POST body and headers alive.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+};
+
+/**
+ * Called when the HTTP transfer finishes.
+ *
+ * @param cls operation handle
+ * @param response_code HTTP status (0 on network / parsing failures)
+ * @param response parsed JSON reply (NULL if unavailable)
+ */
+static void
+handle_post_categories_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_CategoriesPostHandle *cph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_CategoriesPostResponse cpr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ cph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/categories completed with status %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint64 ("category_id",
+ &cpr.category_id),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid response for field %s\n",
+ err_name);
+ cpr.hr.http_status = 0;
+ cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ }
+ break;
+ }
+ case MHD_HTTP_BAD_REQUEST:
+ case MHD_HTTP_UNAUTHORIZED:
+ case MHD_HTTP_FORBIDDEN:
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_CONFLICT:
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ cpr.hr.ec = TALER_JSON_get_error_code (json);
+ cpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case 0:
+ cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &cpr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response %u/%d for POST /private/categories\n",
+ (unsigned int) response_code,
+ (int) cpr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ cph->cb (cph->cb_cls,
+ &cpr);
+ TALER_MERCHANT_categories_post_cancel (cph);
+}
+
+
+struct TALER_MERCHANT_CategoriesPostHandle *
+TALER_MERCHANT_categories_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *name,
+ const json_t *name_i18n,
+ TALER_MERCHANT_CategoriesPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_CategoriesPostHandle *cph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("name",
+ name),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("name_i18n",
+ (json_t *) name_i18n)));
+ cph = GNUNET_new (struct TALER_MERCHANT_CategoriesPostHandle);
+ cph->ctx = ctx;
+ cph->cb = cb;
+ cph->cb_cls = cb_cls;
+ cph->url = TALER_url_join (backend_url,
+ "private/categories",
+ NULL);
+ if (NULL == cph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to build /private/categories URL\n");
+ json_decref (req_obj);
+ GNUNET_free (cph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (cph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&cph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (cph->url);
+ GNUNET_free (cph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ cph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ cph->post_ctx.headers,
+ &handle_post_categories_finished,
+ cph);
+ }
+ return cph;
+}
+
+
+void
+TALER_MERCHANT_categories_post_cancel (
+ struct TALER_MERCHANT_CategoriesPostHandle *cph)
+{
+ if (NULL != cph->job)
+ {
+ GNUNET_CURL_job_cancel (cph->job);
+ cph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&cph->post_ctx);
+ GNUNET_free (cph->url);
+ GNUNET_free (cph);
+}
+
+
+/* end of merchant_api_post_categories.c */
diff --git a/src/lib/merchant_api_post-private-donau.c b/src/lib/merchant_api_post-private-donau.c
@@ -0,0 +1,237 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 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_post-private-donau.c
+ * @brief Implementation of the POST /donau request
+ * of the merchant's HTTP API
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
+#include <taler/taler_kyclogic_lib.h>
+/* DONAU RELATED IMPORTS */
+#include "taler/taler_merchant_donau.h"
+#include <donau/donau_service.h>
+
+
+/**
+ * Handle for a POST /donau operation.
+ */
+struct TALER_MERCHANT_DonauInstancePostHandle
+{
+ /**
+ * URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the CURL job.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DonauInstancePostCallback 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;
+};
+
+
+/**
+ * Function called when the POST /donau operation is finished.
+ *
+ * @param cls the `struct TALER_MERCHANT_DonauInstancePostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_donau_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_DonauInstancePostHandle *dph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ dph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /donau completed with response code %u\n",
+ (unsigned int) response_code);
+
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ break;
+ }
+
+ dph->cb (dph->cb_cls,
+ &hr);
+ TALER_MERCHANT_donau_instances_post_cancel (dph);
+}
+
+
+struct TALER_MERCHANT_DonauInstancePostHandle *
+TALER_MERCHANT_donau_instances_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const struct TALER_MERCHANT_Charity *charity,
+ const char *auth_token,
+ TALER_MERCHANT_DonauInstancePostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_DonauInstancePostHandle *dph;
+ json_t *req_obj;
+ json_t *auth_obj;
+
+ if (NULL != auth_token)
+ {
+ if (0 != strncasecmp (RFC_8959_PREFIX,
+ auth_token,
+ strlen (RFC_8959_PREFIX)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Authentication token must start with `%s'\n",
+ RFC_8959_PREFIX);
+ return NULL;
+ }
+ auth_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("method",
+ "token"),
+ GNUNET_JSON_pack_string ("token",
+ auth_token));
+ }
+ else
+ {
+ auth_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("method",
+ "external"));
+ }
+ if (NULL == auth_obj)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("donau_url",
+ charity->charity_url),
+ GNUNET_JSON_pack_uint64 ("charity_id",
+ charity->charity_id)
+ );
+
+ dph = GNUNET_new (struct TALER_MERCHANT_DonauInstancePostHandle);
+ dph->ctx = ctx;
+ dph->cb = cb;
+ dph->cb_cls = cb_cls;
+ dph->url = TALER_url_join (backend_url,
+ "private/donau",
+ NULL);
+
+ if (NULL == dph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to construct request URL\n");
+ json_decref (req_obj);
+ GNUNET_free (dph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (dph->url);
+ if (GNUNET_OK != TALER_curl_easy_post (&dph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (dph);
+ return NULL;
+ }
+
+ json_decref (req_obj);
+
+ dph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ dph->post_ctx.headers,
+ &handle_post_donau_finished,
+ dph);
+ }
+ return dph;
+}
+
+
+void
+TALER_MERCHANT_donau_instances_post_cancel (
+ struct TALER_MERCHANT_DonauInstancePostHandle *dph)
+{
+ if (NULL != dph->job)
+ {
+ GNUNET_CURL_job_cancel (dph->job);
+ dph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&dph->post_ctx);
+ GNUNET_free (dph->url);
+ GNUNET_free (dph);
+}
+
+
+/* end of merchant_api_post_donau_instance.c */
diff --git a/src/lib/merchant_api_post-private-orders-ORDER_ID-refund.c b/src/lib/merchant_api_post-private-orders-ORDER_ID-refund.c
@@ -0,0 +1,246 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-private-orders-ORDER_ID-refund.c
+ * @brief Implementation of the POST /orders/ID/refund request
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /orders/ID/refund operation.
+ */
+struct TALER_MERCHANT_OrderRefundHandle
+{
+ /**
+ * Complete URL where the backend offers /refund
+ */
+ char *url;
+
+ /**
+ * Minor context that holds body and headers.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+
+ /**
+ * The CURL context to connect to the backend
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * The callback to pass the backend response to
+ */
+ TALER_MERCHANT_RefundCallback cb;
+
+ /**
+ * Clasure to pass to the callback
+ */
+ void *cb_cls;
+
+ /**
+ * Handle for the request
+ */
+ struct GNUNET_CURL_Job *job;
+};
+
+
+/**
+ * Callback to process POST /orders/ID/refund response
+ *
+ * @param cls the `struct TALER_MERCHANT_OrderRefundHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not JSON
+ */
+static void
+handle_refund_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OrderRefundHandle *orh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_RefundResponse rr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ orh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string (
+ "taler_refund_uri",
+ &rr.details.ok.taler_refund_uri),
+ GNUNET_JSON_spec_fixed_auto (
+ "h_contract",
+ &rr.details.ok.h_contract),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ rr.hr.http_status = 0;
+ rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* order unknown (or not paid) */
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ /* amount not acceptable */
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_GONE:
+ /* too late, wire deadline is past */
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ GNUNET_break_op (0); /* unexpected status code */
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &rr.hr);
+ break;
+ }
+ orh->cb (orh->cb_cls,
+ &rr);
+ TALER_MERCHANT_post_order_refund_cancel (orh);
+}
+
+
+void
+TALER_MERCHANT_post_order_refund_cancel (
+ struct TALER_MERCHANT_OrderRefundHandle *orh)
+{
+ if (NULL != orh->job)
+ {
+ GNUNET_CURL_job_cancel (orh->job);
+ orh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&orh->post_ctx);
+ GNUNET_free (orh->url);
+ GNUNET_free (orh);
+}
+
+
+struct TALER_MERCHANT_OrderRefundHandle *
+TALER_MERCHANT_post_order_refund (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *order_id,
+ const struct TALER_Amount *refund,
+ const char *reason,
+ TALER_MERCHANT_RefundCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OrderRefundHandle *orh;
+ json_t *req;
+ CURL *eh;
+
+ orh = GNUNET_new (struct TALER_MERCHANT_OrderRefundHandle);
+ orh->ctx = ctx;
+ orh->cb = cb;
+ orh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/orders/%s/refund",
+ order_id);
+ orh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == orh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (orh);
+ return NULL;
+ }
+ req = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("refund",
+ refund),
+ GNUNET_JSON_pack_string ("reason",
+ reason));
+ GNUNET_assert (NULL != req);
+ eh = TALER_MERCHANT_curl_easy_get_ (orh->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&orh->post_ctx,
+ eh,
+ req))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req);
+ GNUNET_free (orh->url);
+ GNUNET_free (orh);
+ return NULL;
+ }
+ json_decref (req);
+ orh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ orh->post_ctx.headers,
+ &handle_refund_finished,
+ orh);
+ if (NULL == orh->job)
+ {
+ GNUNET_free (orh->url);
+ GNUNET_free (orh);
+ return NULL;
+ }
+ return orh;
+}
+
+
+/* end of merchant_api_post_order_refund.c */
diff --git a/src/lib/merchant_api_post-private-orders.c b/src/lib/merchant_api_post-private-orders.c
@@ -0,0 +1,297 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-private-orders.c
+ * @brief Implementation of the POST /orders
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * @brief A POST /orders Handle
+ */
+struct TALER_MERCHANT_PostOrdersHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostOrdersCallback 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;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /orders request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostOrdersHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not JSON
+ */
+static void
+handle_post_order_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostOrdersHandle *po = cls;
+ const json_t *json = response;
+
+ po->job = NULL;
+ TALER_MERCHANT_handle_order_creation_response_ (po->cb,
+ po->cb_cls,
+ response_code,
+ json);
+ TALER_MERCHANT_orders_post_cancel (po);
+}
+
+
+struct TALER_MERCHANT_PostOrdersHandle *
+TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const json_t *order,
+ struct GNUNET_TIME_Relative refund_delay,
+ TALER_MERCHANT_PostOrdersCallback cb,
+ void *cb_cls)
+{
+ static const char *no_uuids[GNUNET_NZL (0)];
+
+ return TALER_MERCHANT_orders_post2 (ctx,
+ backend_url,
+ order,
+ refund_delay,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ no_uuids,
+ true,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_PostOrdersHandle *
+TALER_MERCHANT_orders_post2 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const json_t *order,
+ struct GNUNET_TIME_Relative refund_delay,
+ const char *payment_target,
+ unsigned int inventory_products_length,
+ const struct TALER_MERCHANT_InventoryProduct inventory_products[],
+ unsigned int uuids_length,
+ const char *uuids[],
+ bool create_token,
+ TALER_MERCHANT_PostOrdersCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_orders_post3 (
+ ctx,
+ backend_url,
+ order,
+ NULL, /* session ID */
+ refund_delay,
+ payment_target,
+ inventory_products_length,
+ inventory_products,
+ uuids_length,
+ uuids,
+ create_token,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_PostOrdersHandle *
+TALER_MERCHANT_orders_post3 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const json_t *order,
+ const char *session_id,
+ struct GNUNET_TIME_Relative refund_delay,
+ const char *payment_target,
+ unsigned int inventory_products_length,
+ const struct TALER_MERCHANT_InventoryProduct inventory_products[],
+ unsigned int uuids_length,
+ const char *uuids[],
+ bool create_token,
+ TALER_MERCHANT_PostOrdersCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_PostOrdersHandle *po;
+ json_t *req;
+ CURL *eh;
+
+ po = GNUNET_new (struct TALER_MERCHANT_PostOrdersHandle);
+ po->ctx = ctx;
+ po->cb = cb;
+ po->cb_cls = cb_cls;
+ po->url = TALER_url_join (backend_url,
+ "private/orders",
+ NULL);
+ req = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_object_incref ("order",
+ (json_t *) order),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("session_id",
+ session_id)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("payment_target",
+ payment_target)));
+ if (0 != refund_delay.rel_value_us)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req,
+ "refund_delay",
+ GNUNET_JSON_from_time_rel (
+ refund_delay)));
+ }
+ if (0 != inventory_products_length)
+ {
+ json_t *ipa = json_array ();
+
+ GNUNET_assert (NULL != ipa);
+ for (unsigned int i = 0; i<inventory_products_length; i++)
+ {
+ json_t *ip;
+ char unit_quantity_buf[64];
+
+ TALER_MERCHANT_format_quantity_string (inventory_products[i].quantity,
+ inventory_products[i].quantity_frac
+ ,
+ unit_quantity_buf,
+ sizeof (unit_quantity_buf));
+
+ ip = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("product_id",
+ inventory_products[i].product_id),
+ GNUNET_JSON_pack_string ("unit_quantity",
+ unit_quantity_buf));
+ GNUNET_assert (NULL != ip);
+ GNUNET_assert (0 ==
+ json_array_append_new (ipa,
+ ip));
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (req,
+ "inventory_products",
+ ipa));
+ }
+ if (0 != uuids_length)
+ {
+ json_t *ua = json_array ();
+
+ GNUNET_assert (NULL != ua);
+ for (unsigned int i = 0; i<uuids_length; i++)
+ {
+ json_t *u;
+
+ u = json_string (uuids[i]);
+ GNUNET_assert (0 ==
+ json_array_append_new (ua,
+ u));
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (req,
+ "lock_uuids",
+ ua));
+ }
+ if (! create_token)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req,
+ "create_token",
+ json_boolean (create_token)));
+ }
+ eh = TALER_MERCHANT_curl_easy_get_ (po->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&po->post_ctx,
+ eh,
+ req))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req);
+ GNUNET_free (po);
+ return NULL;
+ }
+ json_decref (req);
+ po->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ po->post_ctx.headers,
+ &handle_post_order_finished,
+ po);
+ return po;
+}
+
+
+void
+TALER_MERCHANT_orders_post_cancel (
+ struct TALER_MERCHANT_PostOrdersHandle *po)
+{
+ if (NULL != po->job)
+ {
+ GNUNET_CURL_job_cancel (po->job);
+ po->job = NULL;
+ }
+ GNUNET_free (po->url);
+ TALER_curl_easy_post_finished (&po->post_ctx);
+ GNUNET_free (po);
+}
+
+
+/* end of merchant_api_post_orders.c */
diff --git a/src/lib/merchant_api_post-private-otp-devices.c b/src/lib/merchant_api_post-private-otp-devices.c
@@ -0,0 +1,237 @@
+/*
+ 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_post-private-otp-devices.c
+ * @brief Implementation of the POST /otp-devices request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /otp-devices/$ID operation.
+ */
+struct TALER_MERCHANT_OtpDevicesPostHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_OtpDevicesPostCallback 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;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /otp-devices request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OtpDevicesPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_otp_devices_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_OtpDevicesPostHandle *tph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /otp-devices completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ tph->cb (tph->cb_cls,
+ &hr);
+ TALER_MERCHANT_otp_devices_post_cancel (tph);
+}
+
+
+struct TALER_MERCHANT_OtpDevicesPostHandle *
+TALER_MERCHANT_otp_devices_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *otp_device_id,
+ const char *otp_device_description,
+ const char *otp_key,
+ enum TALER_MerchantConfirmationAlgorithm otp_algorithm,
+ uint64_t otp_ctr,
+ TALER_MERCHANT_OtpDevicesPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_OtpDevicesPostHandle *tph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("otp_device_id",
+ otp_device_id),
+ GNUNET_JSON_pack_string ("otp_device_description",
+ otp_device_description),
+ GNUNET_JSON_pack_uint64 ("otp_algorithm",
+ (uint32_t) otp_algorithm),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_key",
+ otp_key)),
+ GNUNET_JSON_pack_uint64 ("otp_ctr",
+ otp_ctr));
+ tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicesPostHandle);
+ tph->ctx = ctx;
+ tph->cb = cb;
+ tph->cb_cls = cb_cls;
+ tph->url = TALER_url_join (backend_url,
+ "private/otp-devices",
+ NULL);
+ if (NULL == tph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (tph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_curl_easy_post (&tph->post_ctx,
+ eh,
+ req_obj));
+ json_decref (req_obj);
+ tph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ tph->post_ctx.headers,
+ &handle_post_otp_devices_finished,
+ tph);
+ GNUNET_assert (NULL != tph->job);
+ }
+ return tph;
+}
+
+
+void
+TALER_MERCHANT_otp_devices_post_cancel (
+ struct TALER_MERCHANT_OtpDevicesPostHandle *tph)
+{
+ if (NULL != tph->job)
+ {
+ GNUNET_CURL_job_cancel (tph->job);
+ tph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&tph->post_ctx);
+ GNUNET_free (tph->url);
+ GNUNET_free (tph);
+}
+
+
+/* end of merchant_api_post_otp_devices.c */
diff --git a/src/lib/merchant_api_post-private-products-PRODUCT_ID-lock.c b/src/lib/merchant_api_post-private-products-PRODUCT_ID-lock.c
@@ -0,0 +1,279 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-private-products-PRODUCT_ID-lock.c
+ * @brief Implementation of the POST /products/$ID/lock request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /products/$ID/lock operation.
+ */
+struct TALER_MERCHANT_ProductLockHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ProductLockCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /products/$ID/lock request.
+ *
+ * @param cls the `struct TALER_MERCHANT_ProductLockHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_lock_product_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_ProductLockHandle *plh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ plh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "LOCK /products/$ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_break_op (0);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_GONE:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ plh->cb (plh->cb_cls,
+ &hr);
+ TALER_MERCHANT_product_lock_cancel (plh);
+}
+
+
+struct TALER_MERCHANT_ProductLockHandle *
+TALER_MERCHANT_product_lock2 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ const char *uuid,
+ struct GNUNET_TIME_Relative duration,
+ uint64_t quantity,
+ uint32_t quantity_frac,
+ bool use_fractional_quantity,
+ TALER_MERCHANT_ProductLockCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_ProductLockHandle *plh;
+ json_t *req_obj;
+ char unit_quantity_buf[64];
+
+ TALER_MERCHANT_format_quantity_string (quantity,
+ quantity_frac,
+ unit_quantity_buf,
+ sizeof (unit_quantity_buf));
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("lock_uuid",
+ uuid),
+ GNUNET_JSON_pack_time_rel ("duration",
+ duration),
+ GNUNET_JSON_pack_string ("unit_quantity",
+ unit_quantity_buf));
+ (void) use_fractional_quantity;
+ GNUNET_assert ( (0 == quantity_frac) || use_fractional_quantity);
+ plh = GNUNET_new (struct TALER_MERCHANT_ProductLockHandle);
+ plh->ctx = ctx;
+ plh->cb = cb;
+ plh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/products/%s/lock",
+ product_id);
+ plh->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == plh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (plh);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (plh->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&plh->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (plh->url);
+ GNUNET_free (plh);
+ return NULL;
+ }
+ json_decref (req_obj);
+ plh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ plh->post_ctx.headers,
+ &handle_lock_product_finished,
+ plh);
+ }
+ return plh;
+}
+
+
+void
+TALER_MERCHANT_product_lock_cancel (
+ struct TALER_MERCHANT_ProductLockHandle *plh)
+{
+ if (NULL != plh->job)
+ {
+ GNUNET_CURL_job_cancel (plh->job);
+ plh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&plh->post_ctx);
+ GNUNET_free (plh->url);
+ GNUNET_free (plh);
+}
+
+
+struct TALER_MERCHANT_ProductLockHandle *
+TALER_MERCHANT_product_lock (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ const char *uuid,
+ struct GNUNET_TIME_Relative duration,
+ uint32_t quantity,
+ TALER_MERCHANT_ProductLockCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_product_lock2 (ctx,
+ backend_url,
+ product_id,
+ uuid,
+ duration,
+ quantity,
+ 0,
+ false,
+ cb,
+ cb_cls);
+}
+
+
+/* end of merchant_api_lock_product.c */
diff --git a/src/lib/merchant_api_post-private-products.c b/src/lib/merchant_api_post-private-products.c
@@ -0,0 +1,433 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-2024 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_post-private-products.c
+ * @brief Implementation of the POST /products request
+ * of the merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /products/$ID operation.
+ */
+struct TALER_MERCHANT_ProductsPostHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_ProductsPostCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /products request.
+ *
+ * @param cls the `struct TALER_MERCHANT_ProductsPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_products_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_ProductsPostHandle *pph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ pph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /products completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ pph->cb (pph->cb_cls,
+ &hr);
+ TALER_MERCHANT_products_post_cancel (pph);
+}
+
+
+struct TALER_MERCHANT_ProductsPostHandle *
+TALER_MERCHANT_products_post4 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ const char *description,
+ const json_t *description_i18n,
+ const char *unit,
+ const struct TALER_Amount *unit_prices,
+ size_t unit_price_len,
+ const char *image,
+ const json_t *taxes,
+ int64_t total_stock,
+ uint32_t total_stock_frac,
+ bool unit_allow_fraction,
+ const uint32_t *unit_precision_level,
+ const json_t *address,
+ struct GNUNET_TIME_Timestamp next_restock,
+ uint32_t minimum_age,
+ unsigned int num_cats,
+ const uint64_t *cats,
+ TALER_MERCHANT_ProductsPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_ProductsPostHandle *pph;
+ json_t *req_obj;
+ json_t *categories;
+ char unit_total_stock_buf[64];
+
+ TALER_MERCHANT_format_stock_string (total_stock,
+ total_stock_frac,
+ unit_total_stock_buf,
+ sizeof (unit_total_stock_buf));
+
+ if (0 == num_cats)
+ {
+ categories = NULL;
+ }
+ else
+ {
+ categories = json_array ();
+ GNUNET_assert (NULL != categories);
+ for (unsigned int i = 0; i<num_cats; i++)
+ GNUNET_assert (0 ==
+ json_array_append_new (categories,
+ json_integer (cats[i])));
+ }
+ {
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("product_id",
+ product_id),
+ /* FIXME: once we move to the new-style API,
+ allow applications to set the product name properly! */
+ GNUNET_JSON_pack_string ("product_name",
+ description),
+ GNUNET_JSON_pack_string ("description",
+ description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("description_i18n",
+ (json_t *) description_i18n)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_steal ("categories",
+ categories)),
+ GNUNET_JSON_pack_string ("unit",
+ unit),
+ TALER_JSON_pack_amount_array ("unit_price",
+ unit_price_len,
+ unit_prices),
+ GNUNET_JSON_pack_string ("image",
+ image),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_incref ("taxes",
+ (json_t *) taxes)),
+ GNUNET_JSON_pack_string ("unit_total_stock",
+ unit_total_stock_buf),
+ GNUNET_JSON_pack_bool ("unit_allow_fraction",
+ unit_allow_fraction),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_uint64 ("minimum_age",
+ minimum_age)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("address",
+ (json_t *) address)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp ("next_restock",
+ next_restock)));
+ }
+ if (NULL != unit_precision_level)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_precision_level",
+ json_integer (
+ *unit_precision_level)));
+ }
+ if (! unit_allow_fraction)
+ {
+ GNUNET_assert (0 ==
+ json_object_del (req_obj,
+ "unit_allow_fraction"));
+ if (NULL != unit_precision_level)
+ GNUNET_assert (0 ==
+ json_object_del (req_obj,
+ "unit_precision_level"));
+ }
+ pph = GNUNET_new (struct TALER_MERCHANT_ProductsPostHandle);
+ pph->ctx = ctx;
+ pph->cb = cb;
+ pph->cb_cls = cb_cls;
+ pph->url = TALER_url_join (backend_url,
+ "private/products",
+ NULL);
+ if (NULL == pph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (pph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (pph->url);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_curl_easy_post (&pph->post_ctx,
+ eh,
+ req_obj));
+ json_decref (req_obj);
+ pph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ pph->post_ctx.headers,
+ &handle_post_products_finished,
+ pph);
+ GNUNET_assert (NULL != pph->job);
+ }
+ return pph;
+}
+
+
+struct TALER_MERCHANT_ProductsPostHandle *
+TALER_MERCHANT_products_post3 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ const char *description,
+ const json_t *description_i18n,
+ const char *unit,
+ const struct TALER_Amount *price,
+ const char *image,
+ const json_t *taxes,
+ int64_t total_stock,
+ const json_t *address,
+ struct GNUNET_TIME_Timestamp next_restock,
+ uint32_t minimum_age,
+ unsigned int num_cats,
+ const uint64_t *cats,
+ TALER_MERCHANT_ProductsPostCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_products_post4 (ctx,
+ backend_url,
+ product_id,
+ description,
+ description_i18n,
+ unit,
+ price,
+ 1,
+ image,
+ taxes,
+ total_stock,
+ 0,
+ false,
+ NULL,
+ address,
+ next_restock,
+ minimum_age,
+ num_cats,
+ cats,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_ProductsPostHandle *
+TALER_MERCHANT_products_post2 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ const char *description,
+ const json_t *description_i18n,
+ const char *unit,
+ const struct TALER_Amount *price,
+ const char *image,
+ const json_t *taxes,
+ int64_t total_stock,
+ const json_t *address,
+ struct GNUNET_TIME_Timestamp next_restock,
+ uint32_t minimum_age,
+ TALER_MERCHANT_ProductsPostCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_products_post3 (ctx,
+ backend_url,
+ product_id,
+ description,
+ description_i18n,
+ unit,
+ price,
+ image,
+ taxes,
+ total_stock,
+ address,
+ next_restock,
+ minimum_age,
+ 0,
+ NULL,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_ProductsPostHandle *
+TALER_MERCHANT_products_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *product_id,
+ const char *description,
+ const json_t *description_i18n,
+ const char *unit,
+ const struct TALER_Amount *price,
+ const char *image,
+ const json_t *taxes,
+ int64_t total_stock,
+ const json_t *address,
+ struct GNUNET_TIME_Timestamp next_restock,
+ TALER_MERCHANT_ProductsPostCallback cb,
+ void *cb_cls)
+{
+ return TALER_MERCHANT_products_post2 (ctx,
+ backend_url,
+ product_id,
+ description,
+ description_i18n,
+ unit,
+ price,
+ image,
+ taxes,
+ total_stock,
+ address,
+ next_restock,
+ 0,
+ cb,
+ cb_cls);
+}
+
+
+void
+TALER_MERCHANT_products_post_cancel (
+ struct TALER_MERCHANT_ProductsPostHandle *pph)
+{
+ if (NULL != pph->job)
+ {
+ GNUNET_CURL_job_cancel (pph->job);
+ pph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pph->post_ctx);
+ GNUNET_free (pph->url);
+ GNUNET_free (pph);
+}
+
+
+/* end of merchant_api_post_products.c */
diff --git a/src/lib/merchant_api_post-private-templates.c b/src/lib/merchant_api_post-private-templates.c
@@ -0,0 +1,495 @@
+/*
+ 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_post-private-templates.c
+ * @brief Implementation of the POST /templates request
+ * of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+#include "taler/taler_merchant_util.h"
+
+/* FIXME: Bohdan is to stupid to figure out how util can be used here */
+static enum TALER_MERCHANT_TemplateType
+template_type_from_string (const char *template_type)
+{
+ if (NULL == template_type)
+ return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
+ if (0 == strcmp (template_type,
+ "fixed-order"))
+ return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
+ if (0 == strcmp (template_type,
+ "inventory-cart"))
+ return TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART;
+ if (0 == strcmp (template_type,
+ "paivana"))
+ return TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA;
+ return TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
+}
+
+
+/**
+ * Validate a fixed-order template contract.
+ *
+ * @param template_contract JSON template contract
+ * @param summary summary pointer
+ * @param amount amount storage
+ * @param minimum_age minimum age storage
+ * @param pay_duration pay duration storage
+ * @return true if valid
+ */
+static bool
+validate_template_contract_fixed (const json_t *template_contract,
+ const char **summary,
+ struct TALER_Amount *amount,
+ uint32_t *minimum_age,
+ struct GNUNET_TIME_Relative *pay_duration)
+{
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("summary",
+ summary),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("amount",
+ amount),
+ NULL),
+ GNUNET_JSON_spec_uint32 ("minimum_age",
+ minimum_age),
+ GNUNET_JSON_spec_relative_time ("pay_duration",
+ pay_duration),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *ename;
+ unsigned int eline;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (template_contract,
+ spec,
+ &ename,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid template_contract for field %s\n",
+ ename);
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Validate an inventory-cart template contract.
+ *
+ * @param template_contract JSON template contract
+ * @param summary summary pointer
+ * @param pay_duration pay duration storage
+ * @return true if valid
+ */
+static bool
+validate_template_contract_inventory (const json_t *template_contract,
+ const char **summary,
+ struct GNUNET_TIME_Relative *pay_duration)
+{
+ bool selected_all = false;
+ bool choose_one = false;
+ bool request_tip = false;
+ const json_t *selected_categories = NULL;
+ const json_t *selected_products = NULL;
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("summary",
+ summary),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("request_tip",
+ &request_tip),
+ NULL),
+ GNUNET_JSON_spec_relative_time ("pay_duration",
+ pay_duration),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("selected_all",
+ &selected_all),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("selected_categories",
+ &selected_categories),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("selected_products",
+ &selected_products),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("choose_one",
+ &choose_one),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *ename;
+ unsigned int eline;
+
+ (void) request_tip;
+ (void) selected_all;
+ (void) choose_one;
+ (void) selected_categories;
+ (void) selected_products;
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (template_contract,
+ ispec,
+ &ename,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid template_contract for field %s\n",
+ ename);
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Validate a paivana template contract.
+ *
+ * @param template_contract JSON template contract
+ * @param summary summary pointer
+ * @param amount amount storage
+ * @param minimum_age minimum age storage
+ * @param pay_duration pay duration storage
+ * @return true if valid
+ */
+static bool
+validate_template_contract_paivana (const json_t *template_contract,
+ const char **summary,
+ struct TALER_Amount *amount,
+ uint32_t *minimum_age,
+ struct GNUNET_TIME_Relative *pay_duration)
+{
+ /* TODO: PAIVANA validate paivana-specific fields beyond presence. */
+ const char *paivana_id;
+ struct GNUNET_JSON_Specification pspec[] = {
+ GNUNET_JSON_spec_string ("paivana_id",
+ &paivana_id),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *ename;
+ unsigned int eline;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (template_contract,
+ pspec,
+ &ename,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid paivana template_contract for field %s\n",
+ ename);
+ return false;
+ }
+
+ if (! validate_template_contract_fixed (template_contract,
+ summary,
+ amount,
+ minimum_age,
+ pay_duration))
+ return false;
+
+ (void) paivana_id;
+ return true;
+}
+
+
+/**
+ * Handle for a POST /templates/$ID operation.
+ */
+struct TALER_MERCHANT_TemplatesPostHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TemplatesPostCallback 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;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /templates request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TemplatesPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_templates_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_TemplatesPostHandle *tph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ tph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /templates completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ tph->cb (tph->cb_cls,
+ &hr);
+ TALER_MERCHANT_templates_post_cancel (tph);
+}
+
+
+static bool
+test_template_contract_valid (const json_t *template_contract)
+{
+ const char *template_type = NULL;
+ enum TALER_MERCHANT_TemplateType template_type_enum;
+ struct GNUNET_JSON_Specification type_spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("template_type",
+ &template_type),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *summary;
+ struct TALER_Amount amount = { .value = 0};
+ uint32_t minimum_age = 0;
+ struct GNUNET_TIME_Relative pay_duration = { 0 };
+ const char *ename;
+ unsigned int eline;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (template_contract,
+ type_spec,
+ &ename,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid template_contract for field %s\n",
+ ename);
+ return false;
+ }
+
+ template_type_enum = template_type_from_string (template_type);
+ if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == template_type_enum)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid template_type '%s'\n",
+ template_type);
+ return false;
+ }
+
+ /* FIXME: Bohdan understands that links have to be changed, but worried, that can crash something */
+ switch (template_type_enum)
+ {
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
+ return validate_template_contract_inventory (template_contract,
+ &summary,
+ &pay_duration);
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ return validate_template_contract_fixed (template_contract,
+ &summary,
+ &amount,
+ &minimum_age,
+ &pay_duration);
+ case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
+ return validate_template_contract_paivana (template_contract,
+ &summary,
+ &amount,
+ &minimum_age,
+ &pay_duration);
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
+ break;
+ }
+ return false;
+}
+
+
+struct TALER_MERCHANT_TemplatesPostHandle *
+TALER_MERCHANT_templates_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *template_id,
+ const char *template_description,
+ const char *otp_id,
+ const json_t *template_contract,
+ TALER_MERCHANT_TemplatesPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TemplatesPostHandle *tph;
+ json_t *req_obj;
+
+ if (! test_template_contract_valid (template_contract))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("template_id",
+ template_id),
+ GNUNET_JSON_pack_string ("template_description",
+ template_description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_id",
+ otp_id)),
+ GNUNET_JSON_pack_object_incref ("template_contract",
+ (json_t *) template_contract));
+ tph = GNUNET_new (struct TALER_MERCHANT_TemplatesPostHandle);
+ tph->ctx = ctx;
+ tph->cb = cb;
+ tph->cb_cls = cb_cls;
+ tph->url = TALER_url_join (backend_url,
+ "private/templates",
+ NULL);
+ if (NULL == tph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (tph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_curl_easy_post (&tph->post_ctx,
+ eh,
+ req_obj));
+ json_decref (req_obj);
+ tph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ tph->post_ctx.headers,
+ &handle_post_templates_finished,
+ tph);
+ GNUNET_assert (NULL != tph->job);
+ }
+ return tph;
+}
+
+
+void
+TALER_MERCHANT_templates_post_cancel (
+ struct TALER_MERCHANT_TemplatesPostHandle *tph)
+{
+ if (NULL != tph->job)
+ {
+ GNUNET_CURL_job_cancel (tph->job);
+ tph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&tph->post_ctx);
+ GNUNET_free (tph->url);
+ GNUNET_free (tph);
+}
+
+
+/* end of merchant_api_post_templates.c */
diff --git a/src/lib/merchant_api_post-private-token.c b/src/lib/merchant_api_post-private-token.c
@@ -0,0 +1,235 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_post-private-token.c
+ * @brief Implementation of the POST /instance/$ID/private/token request
+ * @author Martin Schanzenbach
+ */
+#include "taler/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/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /instances/$ID/private/token operation.
+ */
+struct TALER_MERCHANT_InstanceTokenPostHandle
+{
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_InstanceTokenPostCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /instances/$ID/private/token request.
+ *
+ * @param cls the `struct TALER_MERCHANT_InstanceTokenPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_instance_token_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_InstanceTokenPostHandle *itph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ itph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /instances/$ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* happens if the auth token is malformed */
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ default:
+ /* unexpected response code */
+ hr.ec = TALER_JSON_get_error_code (json);
+ 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) hr.ec);
+ break;
+ }
+ itph->cb (itph->cb_cls,
+ &hr);
+ TALER_MERCHANT_instance_token_post_cancel (itph);
+}
+
+
+struct TALER_MERCHANT_InstanceTokenPostHandle *
+TALER_MERCHANT_instance_token_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *instance_id,
+ const char *scope,
+ struct GNUNET_TIME_Relative duration,
+ bool refreshable,
+ TALER_MERCHANT_InstanceTokenPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_InstanceTokenPostHandle *itph;
+ json_t *req_obj;
+
+ itph = GNUNET_new (struct TALER_MERCHANT_InstanceTokenPostHandle);
+ itph->ctx = ctx;
+ itph->cb = cb;
+ itph->cb_cls = cb_cls;
+ if (NULL != instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "instances/%s/private/token",
+ instance_id);
+ itph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ /* backend_url is already identifying the instance */
+ itph->url = TALER_url_join (backend_url,
+ "private/token",
+ NULL);
+ }
+ if (NULL == itph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (itph);
+ return NULL;
+ }
+ if (NULL == scope)
+ {
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_time_rel ("duration",
+ duration),
+ GNUNET_JSON_pack_bool ("refreshable",
+ refreshable),
+ GNUNET_JSON_pack_string ("scope",
+ "readonly"));
+ }
+ else
+ {
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_time_rel ("duration",
+ duration),
+ GNUNET_JSON_pack_bool ("refreshable",
+ refreshable),
+ GNUNET_JSON_pack_string ("scope",
+ scope));
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n",
+ itph->url);
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (itph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&itph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (itph->url);
+ GNUNET_free (itph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_POST));
+ itph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ itph->post_ctx.headers,
+ &handle_post_instance_token_finished,
+ itph);
+ }
+ return itph;
+}
+
+
+void
+TALER_MERCHANT_instance_token_post_cancel (
+ struct TALER_MERCHANT_InstanceTokenPostHandle *itph)
+{
+ if (NULL != itph->job)
+ GNUNET_CURL_job_cancel (itph->job);
+ TALER_curl_easy_post_finished (&itph->post_ctx);
+ GNUNET_free (itph->url);
+ GNUNET_free (itph);
+}
diff --git a/src/lib/merchant_api_post-private-tokenfamilies.c b/src/lib/merchant_api_post-private-tokenfamilies.c
@@ -0,0 +1,257 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-2024 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_post-private-tokenfamilies.c
+ * @brief Implementation of the POST /tokenfamilies request
+ * of the merchant's HTTP API
+ * @author Christian Blättler
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_time_lib.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /tokenfamilies operation.
+ */
+struct TALER_MERCHANT_TokenFamiliesPostHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_TokenFamiliesPostCallback 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;
+
+};
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /tokenfamilies request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TokenFamiliesPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_token_families_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_TokenFamiliesPostHandle *handle = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ handle->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /tokenfamilies completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ handle->cb (handle->cb_cls,
+ &hr);
+ TALER_MERCHANT_token_families_post_cancel (handle);
+}
+
+
+struct TALER_MERCHANT_TokenFamiliesPostHandle *
+TALER_MERCHANT_token_families_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *slug,
+ const char *name,
+ const char *description,
+ const json_t *description_i18n,
+ const json_t *extra_data,
+ struct GNUNET_TIME_Timestamp valid_after,
+ struct GNUNET_TIME_Timestamp valid_before,
+ struct GNUNET_TIME_Relative duration,
+ struct GNUNET_TIME_Relative validity_granularity,
+ struct GNUNET_TIME_Relative start_offset,
+ const char *kind,
+ TALER_MERCHANT_TokenFamiliesPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_TokenFamiliesPostHandle *handle;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("slug",
+ slug),
+ GNUNET_JSON_pack_string ("name",
+ name),
+ GNUNET_JSON_pack_string ("description",
+ description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("description_i18n",
+ (json_t *) description_i18n)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("extra_data",
+ (json_t *) extra_data)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp ("valid_after",
+ valid_after)),
+ GNUNET_JSON_pack_timestamp ("valid_before",
+ valid_before),
+ GNUNET_JSON_pack_time_rel ("duration",
+ duration),
+ GNUNET_JSON_pack_time_rel ("validity_granularity",
+ validity_granularity),
+ GNUNET_JSON_pack_time_rel ("start_offset",
+ start_offset),
+ GNUNET_JSON_pack_string ("kind",
+ kind));
+ handle = GNUNET_new (struct TALER_MERCHANT_TokenFamiliesPostHandle);
+ handle->ctx = ctx;
+ handle->cb = cb;
+ handle->cb_cls = cb_cls;
+ handle->url = TALER_url_join (backend_url,
+ "private/tokenfamilies",
+ NULL);
+ if (NULL == handle->url)
+ {
+
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (handle);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_curl_easy_post (&handle->post_ctx,
+ eh,
+ req_obj));
+ json_decref (req_obj);
+ handle->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ handle->post_ctx.headers,
+ &handle_post_token_families_finished,
+ handle);
+ GNUNET_assert (NULL != handle->job);
+ }
+ return handle;
+}
+
+
+void
+TALER_MERCHANT_token_families_post_cancel (
+ struct TALER_MERCHANT_TokenFamiliesPostHandle *pph)
+{
+ if (NULL != pph->job)
+ {
+ GNUNET_CURL_job_cancel (pph->job);
+ pph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pph->post_ctx);
+ GNUNET_free (pph->url);
+ GNUNET_free (pph);
+}
diff --git a/src/lib/merchant_api_post-private-transfers.c b/src/lib/merchant_api_post-private-transfers.c
@@ -0,0 +1,256 @@
+/*
+ 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
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-private-transfers.c
+ * @brief Implementation of the POST /transfers request of the merchant's HTTP API
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_curl_lib.h>
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * @brief A handle for POSTing transfer data.
+ */
+struct TALER_MERCHANT_PostTransfersHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostTransfersCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /transfers request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostTransfersHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_transfers_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostTransfersHandle *pth = cls;
+ struct TALER_MERCHANT_PostTransfersResponse ptr = {
+ .hr.reply = response,
+ .hr.http_status = (unsigned int) response_code
+ };
+
+ pth->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
+ ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Did not find any data\n");
+ ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
+ ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
+ ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
+ break;
+ case MHD_HTTP_BAD_GATEWAY:
+ /* Exchange had an issue; we should retry, but this API
+ leaves this to the application */
+ ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
+ ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
+ {
+ uint32_t ehc;
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_ec ("exchange_code",
+ &ptr.details.bad_gateway.exchange_ec),
+ GNUNET_JSON_spec_uint32 ("exchange_http_status",
+ &ehc),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (ptr.hr.reply,
+ ispec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ptr.details.bad_gateway.exchange_http_status = 0;
+ ptr.details.bad_gateway.exchange_ec = TALER_EC_NONE;
+ break;
+ }
+ else
+ {
+ ptr.details.bad_gateway.exchange_http_status
+ = (unsigned int) ehc;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Exchange returned %u/%u\n",
+ (unsigned int) ptr.details.bad_gateway.exchange_ec,
+ (unsigned int) ehc);
+ }
+ }
+ break;
+ case MHD_HTTP_GATEWAY_TIMEOUT:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
+ ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_break_op (0);
+ TALER_MERCHANT_parse_error_details_ (ptr.hr.reply,
+ response_code,
+ &ptr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) ptr.hr.http_status,
+ (int) ptr.hr.ec);
+ break;
+ }
+ pth->cb (pth->cb_cls,
+ &ptr);
+ TALER_MERCHANT_transfers_post_cancel (pth);
+}
+
+
+struct TALER_MERCHANT_PostTransfersHandle *
+TALER_MERCHANT_transfers_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const struct TALER_Amount *credit_amount,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ struct TALER_FullPayto payto_uri,
+ const char *exchange_url,
+ TALER_MERCHANT_PostTransfersCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_PostTransfersHandle *pth;
+ CURL *eh;
+ json_t *req;
+
+ pth = GNUNET_new (struct TALER_MERCHANT_PostTransfersHandle);
+ pth->ctx = ctx;
+ pth->cb = cb;
+ pth->cb_cls = cb_cls;
+ pth->url = TALER_url_join (backend_url,
+ "private/transfers",
+ NULL);
+ if (NULL == pth->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free (pth);
+ return NULL;
+ }
+ req = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("credit_amount",
+ credit_amount),
+ GNUNET_JSON_pack_data_auto ("wtid",
+ wtid),
+ TALER_JSON_pack_full_payto ("payto_uri",
+ payto_uri),
+ GNUNET_JSON_pack_string ("exchange_url",
+ exchange_url));
+ eh = TALER_MERCHANT_curl_easy_get_ (pth->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&pth->post_ctx,
+ eh,
+ req))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req);
+ GNUNET_free (pth->url);
+ GNUNET_free (pth);
+ return NULL;
+ }
+ json_decref (req);
+ pth->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ pth->post_ctx.headers,
+ &handle_post_transfers_finished,
+ pth);
+ return pth;
+}
+
+
+void
+TALER_MERCHANT_transfers_post_cancel (
+ struct TALER_MERCHANT_PostTransfersHandle *pth)
+{
+ if (NULL != pth->job)
+ {
+ GNUNET_CURL_job_cancel (pth->job);
+ pth->job = NULL;
+ }
+ GNUNET_free (pth->url);
+ TALER_curl_easy_post_finished (&pth->post_ctx);
+ GNUNET_free (pth);
+}
+
+
+/* end of merchant_api_post_transfers.c */
diff --git a/src/lib/merchant_api_post-private-units.c b/src/lib/merchant_api_post-private-units.c
@@ -0,0 +1,218 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 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_post-private-units.c
+ * @brief Implementation of POST /private/units
+ * @author Bohdan Potuzhnyi
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /private/units operation.
+ */
+struct TALER_MERCHANT_UnitsPostHandle
+{
+ /**
+ * Fully qualified request URL.
+ */
+ char *url;
+
+ /**
+ * CURL job handle.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Callback to invoke with the result.
+ */
+ TALER_MERCHANT_UnitsPostCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Helper keeping POST body and headers alive.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+};
+
+
+/**
+ * Called when the HTTP transfer finishes.
+ *
+ * @param cls operation handle
+ * @param response_code HTTP status (0 on network / parsing failures)
+ * @param response parsed JSON reply (NULL if unavailable)
+ */
+static void
+handle_post_units_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_UnitsPostHandle *uph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ uph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/units completed with status %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ case MHD_HTTP_UNAUTHORIZED:
+ case MHD_HTTP_FORBIDDEN:
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_CONFLICT:
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response %u/%d for POST /private/units\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ uph->cb (uph->cb_cls,
+ &hr);
+ TALER_MERCHANT_units_post_cancel (uph);
+}
+
+
+struct TALER_MERCHANT_UnitsPostHandle *
+TALER_MERCHANT_units_post (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *unit_id,
+ const char *unit_name_long,
+ const char *unit_name_short,
+ bool unit_allow_fraction,
+ uint32_t unit_precision_level,
+ bool unit_active,
+ const json_t *unit_name_long_i18n,
+ const json_t *unit_name_short_i18n,
+ TALER_MERCHANT_UnitsPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_UnitsPostHandle *uph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("unit",
+ unit_id),
+ GNUNET_JSON_pack_string ("unit_name_long",
+ unit_name_long),
+ GNUNET_JSON_pack_string ("unit_name_short",
+ unit_name_short),
+ GNUNET_JSON_pack_bool ("unit_allow_fraction",
+ unit_allow_fraction),
+ GNUNET_JSON_pack_uint64 ("unit_precision_level",
+ (uint64_t) unit_precision_level),
+ GNUNET_JSON_pack_bool ("unit_active",
+ unit_active),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("unit_name_long_i18n",
+ (json_t *) unit_name_long_i18n)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("unit_name_short_i18n",
+ (json_t *) unit_name_short_i18n)));
+ uph = GNUNET_new (struct TALER_MERCHANT_UnitsPostHandle);
+ uph->ctx = ctx;
+ uph->cb = cb;
+ uph->cb_cls = cb_cls;
+ uph->url = TALER_url_join (backend_url,
+ "private/units",
+ NULL);
+ if (NULL == uph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to build /private/units URL\n");
+ json_decref (req_obj);
+ GNUNET_free (uph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (uph->url);
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&uph->post_ctx,
+ eh,
+ req_obj))
+ {
+ GNUNET_break (0);
+ curl_easy_cleanup (eh);
+ json_decref (req_obj);
+ GNUNET_free (uph->url);
+ GNUNET_free (uph);
+ return NULL;
+ }
+ json_decref (req_obj);
+ uph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ uph->post_ctx.headers,
+ &handle_post_units_finished,
+ uph);
+ }
+ return uph;
+}
+
+
+void
+TALER_MERCHANT_units_post_cancel (struct TALER_MERCHANT_UnitsPostHandle *uph)
+{
+ if (NULL != uph->job)
+ {
+ GNUNET_CURL_job_cancel (uph->job);
+ uph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&uph->post_ctx);
+ GNUNET_free (uph->url);
+ GNUNET_free (uph);
+}
+
+
+/* end of merchant_api_post_units.c */
diff --git a/src/lib/merchant_api_post-private-webhooks.c b/src/lib/merchant_api_post-private-webhooks.c
@@ -0,0 +1,240 @@
+/*
+ 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_post-private-webhooks.c
+ * @brief Implementation of the POST /webhooks request
+ * of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /webhooks/$ID operation.
+ */
+struct TALER_MERCHANT_WebhooksPostHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_WebhooksPostCallback 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;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /webhooks request.
+ *
+ * @param cls the `struct TALER_MERCHANT_WebhooksPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_webhooks_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_WebhooksPostHandle *wph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = (unsigned int) response_code,
+ .reply = json
+ };
+
+ wph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /webhooks completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* This should never happen, either us
+ * or the merchant is buggy (or API version conflict);
+ * just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we tried to abort the payment
+ * after it was successful. We should pass the JSON reply to the
+ * application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the
+ application */
+ break;
+ case MHD_HTTP_CONFLICT:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ /* Server had an internal issue; we should retry,
+ but this API leaves this to the application */
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ wph->cb (wph->cb_cls,
+ &hr);
+ TALER_MERCHANT_webhooks_post_cancel (wph);
+}
+
+
+struct TALER_MERCHANT_WebhooksPostHandle *
+TALER_MERCHANT_webhooks_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *webhook_id,
+ const char *event_type,
+ const char *url,
+ const char *http_method,
+ const char *header_template,
+ const char *body_template,
+ TALER_MERCHANT_WebhooksPostCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_WebhooksPostHandle *wph;
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("webhook_id",
+ webhook_id),
+ GNUNET_JSON_pack_string ("event_type",
+ event_type),
+ GNUNET_JSON_pack_string ("url",
+ url),
+ GNUNET_JSON_pack_string ("http_method",
+ http_method),
+ GNUNET_JSON_pack_string ("header_template",
+ header_template),
+ GNUNET_JSON_pack_string ("body_template",
+ body_template));
+ wph = GNUNET_new (struct TALER_MERCHANT_WebhooksPostHandle);
+ wph->ctx = ctx;
+ wph->cb = cb;
+ wph->cb_cls = cb_cls;
+ wph->url = TALER_url_join (backend_url,
+ "private/webhooks",
+ NULL);
+ if (NULL == wph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (wph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (wph->url);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_curl_easy_post (&wph->post_ctx,
+ eh,
+ req_obj));
+ json_decref (req_obj);
+ wph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ wph->post_ctx.headers,
+ &handle_post_webhooks_finished,
+ wph);
+ GNUNET_assert (NULL != wph->job);
+ }
+ return wph;
+}
+
+
+void
+TALER_MERCHANT_webhooks_post_cancel (
+ struct TALER_MERCHANT_WebhooksPostHandle *wph)
+{
+ if (NULL != wph->job)
+ {
+ GNUNET_CURL_job_cancel (wph->job);
+ wph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&wph->post_ctx);
+ GNUNET_free (wph->url);
+ GNUNET_free (wph);
+}
+
+
+/* end of merchant_api_post_webhooks.c */
diff --git a/src/lib/merchant_api_post-templates-TEMPLATE_ID.c b/src/lib/merchant_api_post-templates-TEMPLATE_ID.c
@@ -0,0 +1,233 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_post-templates-TEMPLATE_ID.c
+ * @brief Implementation of the POST /using_templates request
+ * of the merchant's HTTP API
+ * @author Priscilla HUANG
+ */
+#include "taler/platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler/taler_merchant_service.h"
+#include "merchant_api_common.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_curl_lib.h>
+
+
+/**
+ * Handle for a POST /templates/$ID operation.
+ */
+struct TALER_MERCHANT_UsingTemplatesPostHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostOrdersCallback 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;
+};
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /using-templates request.
+ *
+ * @param cls the `struct TALER_MERCHANT_UsingTemplatesPostHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_using_templates_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_UsingTemplatesPostHandle *utph = cls;
+ const json_t *json = response;
+
+ utph->job = NULL;
+ TALER_MERCHANT_handle_order_creation_response_ (utph->cb,
+ utph->cb_cls,
+ response_code,
+ json);
+ TALER_MERCHANT_using_templates_post_cancel (utph);
+}
+
+
+/**
+ * Helper to submit a POST /templates/$ID request.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param template_id identifier of the template to use
+ * @param req_obj JSON request body (consumed)
+ * @param cb function to call with the backend's result
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+static struct TALER_MERCHANT_UsingTemplatesPostHandle *
+using_templates_post_internal (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *template_id,
+ json_t *req_obj,
+ TALER_MERCHANT_PostOrdersCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_UsingTemplatesPostHandle *utph;
+
+ utph = GNUNET_new (struct TALER_MERCHANT_UsingTemplatesPostHandle);
+ utph->ctx = ctx;
+ utph->cb = cb;
+ utph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "templates/%s",
+ template_id);
+ utph->url = TALER_url_join (backend_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == utph->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ json_decref (req_obj);
+ GNUNET_free (utph);
+ return NULL;
+ }
+ {
+ CURL *eh;
+
+ eh = TALER_MERCHANT_curl_easy_get_ (utph->url);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_curl_easy_post (&utph->post_ctx,
+ eh,
+ req_obj));
+ json_decref (req_obj);
+ utph->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ utph->post_ctx.headers,
+ &handle_post_using_templates_finished,
+ utph);
+ GNUNET_assert (NULL != utph->job);
+ }
+ return utph;
+}
+
+
+struct TALER_MERCHANT_UsingTemplatesPostHandle *
+TALER_MERCHANT_using_templates_post (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *template_id,
+ const char *summary,
+ const struct TALER_Amount *amount,
+ TALER_MERCHANT_PostOrdersCallback cb,
+ void *cb_cls)
+{
+ json_t *req_obj;
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("template_type",
+ "fixed-order"),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("summary",
+ summary)),
+ GNUNET_JSON_pack_allow_null (
+ TALER_JSON_pack_amount ("amount",
+ amount)));
+ return using_templates_post_internal (ctx,
+ backend_url,
+ template_id,
+ req_obj,
+ cb,
+ cb_cls);
+}
+
+
+struct TALER_MERCHANT_UsingTemplatesPostHandle *
+TALER_MERCHANT_using_templates_post2 (
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ const char *template_id,
+ const json_t *details,
+ TALER_MERCHANT_PostOrdersCallback cb,
+ void *cb_cls)
+{
+ if (NULL == details)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No request body provided for using templates.\n");
+ return NULL;
+ }
+ return using_templates_post_internal (ctx,
+ backend_url,
+ template_id,
+ (json_t *) details,
+ cb,
+ cb_cls);
+}
+
+
+void
+TALER_MERCHANT_using_templates_post_cancel (
+ struct TALER_MERCHANT_UsingTemplatesPostHandle *utph)
+{
+ if (NULL != utph->job)
+ {
+ GNUNET_CURL_job_cancel (utph->job);
+ utph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&utph->post_ctx);
+ GNUNET_free (utph->url);
+ GNUNET_free (utph);
+}
+
+
+/* end of merchant_api_post_using_templates.c */
diff --git a/src/lib/merchant_api_post_account.c b/src/lib/merchant_api_post_account.c
@@ -1,250 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_account.c
- * @brief Implementation of the POST /account request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /private/accounts operation.
- */
-struct TALER_MERCHANT_AccountsPostHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_AccountsPostCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /account request.
- *
- * @param cls the `struct TALER_MERCHANT_AccountPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_account_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_AccountsPostHandle *aph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_AccountsPostResponse apr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- aph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /accounts completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("h_wire",
- &apr.details.ok.h_wire),
- GNUNET_JSON_spec_fixed_auto ("salt",
- &apr.details.ok.salt),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- apr.hr.http_status = 0;
- apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- }
- break;
- case MHD_HTTP_BAD_REQUEST:
- GNUNET_break_op (0);
- apr.hr.ec = TALER_JSON_get_error_code (json);
- apr.hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_FORBIDDEN:
- apr.hr.ec = TALER_JSON_get_error_code (json);
- apr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_NOT_FOUND:
- apr.hr.ec = TALER_JSON_get_error_code (json);
- apr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_CONFLICT:
- apr.hr.ec = TALER_JSON_get_error_code (json);
- apr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- apr.hr.ec = TALER_JSON_get_error_code (json);
- apr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &apr.hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) apr.hr.ec);
- GNUNET_break_op (0);
- break;
- }
- aph->cb (aph->cb_cls,
- &apr);
- TALER_MERCHANT_accounts_post_cancel (aph);
-}
-
-
-struct TALER_MERCHANT_AccountsPostHandle *
-TALER_MERCHANT_accounts_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- struct TALER_FullPayto payto_uri,
- const char *credit_facade_url,
- const json_t *credit_facade_credentials,
- TALER_MERCHANT_AccountsPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_AccountsPostHandle *aph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- TALER_JSON_pack_full_payto (
- "payto_uri",
- payto_uri),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string (
- "credit_facade_url",
- credit_facade_url)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref (
- "credit_facade_credentials",
- (json_t *) credit_facade_credentials))
- );
- aph = GNUNET_new (struct TALER_MERCHANT_AccountsPostHandle);
- aph->ctx = ctx;
- aph->cb = cb;
- aph->cb_cls = cb_cls;
- aph->url = TALER_url_join (backend_url,
- "private/accounts",
- NULL);
- if (NULL == aph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (aph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (aph->url);
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&aph->post_ctx,
- eh,
- req_obj));
- json_decref (req_obj);
- aph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- aph->post_ctx.headers,
- &handle_post_account_finished,
- aph);
- GNUNET_assert (NULL != aph->job);
- }
- return aph;
-}
-
-
-void
-TALER_MERCHANT_accounts_post_cancel (
- struct TALER_MERCHANT_AccountsPostHandle *aph)
-{
- if (NULL != aph->job)
- {
- GNUNET_CURL_job_cancel (aph->job);
- aph->job = NULL;
- }
- TALER_curl_easy_post_finished (&aph->post_ctx);
- GNUNET_free (aph->url);
- GNUNET_free (aph);
-}
-
-
-/* end of merchant_api_post_account.c */
diff --git a/src/lib/merchant_api_post_categories.c b/src/lib/merchant_api_post_categories.c
@@ -1,223 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_post_categories.c
- * @brief Implementation of POST /private/categories
- * @author Bohdan Potuzhnyi
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-/**
- * Handle for a POST /private/categories operation.
- */
-struct TALER_MERCHANT_CategoriesPostHandle
-{
- /**
- * Fully qualified request URL.
- */
- char *url;
-
- /**
- * CURL job handle.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Callback to invoke with the result.
- */
- TALER_MERCHANT_CategoriesPostCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
- /**
- * Helper keeping POST body and headers alive.
- */
- struct TALER_CURL_PostContext post_ctx;
-};
-
-/**
- * Called when the HTTP transfer finishes.
- *
- * @param cls operation handle
- * @param response_code HTTP status (0 on network / parsing failures)
- * @param response parsed JSON reply (NULL if unavailable)
- */
-static void
-handle_post_categories_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_CategoriesPostHandle *cph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_CategoriesPostResponse cpr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- cph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /private/categories completed with status %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- const char *err_name;
- unsigned int err_line;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_uint64 ("category_id",
- &cpr.category_id),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- &err_name,
- &err_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid response for field %s\n",
- err_name);
- cpr.hr.http_status = 0;
- cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- }
- break;
- }
- case MHD_HTTP_BAD_REQUEST:
- case MHD_HTTP_UNAUTHORIZED:
- case MHD_HTTP_FORBIDDEN:
- case MHD_HTTP_NOT_FOUND:
- case MHD_HTTP_CONFLICT:
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- cpr.hr.ec = TALER_JSON_get_error_code (json);
- cpr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case 0:
- cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &cpr.hr);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response %u/%d for POST /private/categories\n",
- (unsigned int) response_code,
- (int) cpr.hr.ec);
- GNUNET_break_op (0);
- break;
- }
- cph->cb (cph->cb_cls,
- &cpr);
- TALER_MERCHANT_categories_post_cancel (cph);
-}
-
-
-struct TALER_MERCHANT_CategoriesPostHandle *
-TALER_MERCHANT_categories_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *name,
- const json_t *name_i18n,
- TALER_MERCHANT_CategoriesPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_CategoriesPostHandle *cph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("name",
- name),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("name_i18n",
- (json_t *) name_i18n)));
- cph = GNUNET_new (struct TALER_MERCHANT_CategoriesPostHandle);
- cph->ctx = ctx;
- cph->cb = cb;
- cph->cb_cls = cb_cls;
- cph->url = TALER_url_join (backend_url,
- "private/categories",
- NULL);
- if (NULL == cph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to build /private/categories URL\n");
- json_decref (req_obj);
- GNUNET_free (cph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (cph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&cph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (cph->url);
- GNUNET_free (cph);
- return NULL;
- }
- json_decref (req_obj);
- cph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- cph->post_ctx.headers,
- &handle_post_categories_finished,
- cph);
- }
- return cph;
-}
-
-
-void
-TALER_MERCHANT_categories_post_cancel (
- struct TALER_MERCHANT_CategoriesPostHandle *cph)
-{
- if (NULL != cph->job)
- {
- GNUNET_CURL_job_cancel (cph->job);
- cph->job = NULL;
- }
- TALER_curl_easy_post_finished (&cph->post_ctx);
- GNUNET_free (cph->url);
- GNUNET_free (cph);
-}
-
-
-/* end of merchant_api_post_categories.c */
diff --git a/src/lib/merchant_api_post_donau_instance.c b/src/lib/merchant_api_post_donau_instance.c
@@ -1,237 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2024 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_post_donau_instance.c
- * @brief Implementation of the POST /donau request
- * of the merchant's HTTP API
- * @author Bohdan Potuzhnyi
- * @author Vlada Svirsh
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_curl_lib.h>
-#include <taler/taler_kyclogic_lib.h>
-/* DONAU RELATED IMPORTS */
-#include "taler/taler_merchant_donau.h"
-#include <donau/donau_service.h>
-
-
-/**
- * Handle for a POST /donau operation.
- */
-struct TALER_MERCHANT_DonauInstancePostHandle
-{
- /**
- * URL for this request.
- */
- char *url;
-
- /**
- * Handle for the CURL job.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_DonauInstancePostCallback 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;
-};
-
-
-/**
- * Function called when the POST /donau operation is finished.
- *
- * @param cls the `struct TALER_MERCHANT_DonauInstancePostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_donau_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_DonauInstancePostHandle *dph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- dph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /donau completed with response code %u\n",
- (unsigned int) response_code);
-
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- break;
- }
-
- dph->cb (dph->cb_cls,
- &hr);
- TALER_MERCHANT_donau_instances_post_cancel (dph);
-}
-
-
-struct TALER_MERCHANT_DonauInstancePostHandle *
-TALER_MERCHANT_donau_instances_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const struct TALER_MERCHANT_Charity *charity,
- const char *auth_token,
- TALER_MERCHANT_DonauInstancePostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_DonauInstancePostHandle *dph;
- json_t *req_obj;
- json_t *auth_obj;
-
- if (NULL != auth_token)
- {
- if (0 != strncasecmp (RFC_8959_PREFIX,
- auth_token,
- strlen (RFC_8959_PREFIX)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Authentication token must start with `%s'\n",
- RFC_8959_PREFIX);
- return NULL;
- }
- auth_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("method",
- "token"),
- GNUNET_JSON_pack_string ("token",
- auth_token));
- }
- else
- {
- auth_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("method",
- "external"));
- }
- if (NULL == auth_obj)
- {
- GNUNET_break (0);
- return NULL;
- }
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("donau_url",
- charity->charity_url),
- GNUNET_JSON_pack_uint64 ("charity_id",
- charity->charity_id)
- );
-
- dph = GNUNET_new (struct TALER_MERCHANT_DonauInstancePostHandle);
- dph->ctx = ctx;
- dph->cb = cb;
- dph->cb_cls = cb_cls;
- dph->url = TALER_url_join (backend_url,
- "private/donau",
- NULL);
-
- if (NULL == dph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to construct request URL\n");
- json_decref (req_obj);
- GNUNET_free (dph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (dph->url);
- if (GNUNET_OK != TALER_curl_easy_post (&dph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (dph);
- return NULL;
- }
-
- json_decref (req_obj);
-
- dph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- dph->post_ctx.headers,
- &handle_post_donau_finished,
- dph);
- }
- return dph;
-}
-
-
-void
-TALER_MERCHANT_donau_instances_post_cancel (
- struct TALER_MERCHANT_DonauInstancePostHandle *dph)
-{
- if (NULL != dph->job)
- {
- GNUNET_CURL_job_cancel (dph->job);
- dph->job = NULL;
- }
- TALER_curl_easy_post_finished (&dph->post_ctx);
- GNUNET_free (dph->url);
- GNUNET_free (dph);
-}
-
-
-/* end of merchant_api_post_donau_instance.c */
diff --git a/src/lib/merchant_api_post_instance_auth.c b/src/lib/merchant_api_post_instance_auth.c
@@ -1,227 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2021 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_post_instance_auth.c
- * @brief Implementation of the POST /instance/$ID/private/auth request
- * @author Christian Grothoff
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /instances/$ID/private/auth operation.
- */
-struct TALER_MERCHANT_InstanceAuthPostHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_InstanceAuthPostCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP GET /instances/$ID/private/auth request.
- *
- * @param cls the `struct TALER_MERCHANT_InstanceAuthPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_instance_auth_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_InstanceAuthPostHandle *iaph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- iaph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /instances/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- /* happens if the auth token is malformed */
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- 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) hr.ec);
- break;
- }
- iaph->cb (iaph->cb_cls,
- &hr);
- TALER_MERCHANT_instance_auth_post_cancel (iaph);
-}
-
-
-struct TALER_MERCHANT_InstanceAuthPostHandle *
-TALER_MERCHANT_instance_auth_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- const char *auth_password,
- TALER_MERCHANT_InstanceAuthPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_InstanceAuthPostHandle *iaph;
- json_t *req_obj;
-
- iaph = GNUNET_new (struct TALER_MERCHANT_InstanceAuthPostHandle);
- iaph->ctx = ctx;
- iaph->cb = cb;
- iaph->cb_cls = cb_cls;
- if (NULL != instance_id)
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "management/instances/%s/auth",
- instance_id);
- iaph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- else
- {
- /* backend_url is already identifying the instance */
- iaph->url = TALER_url_join (backend_url,
- "private/auth",
- NULL);
- }
- if (NULL == iaph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (iaph);
- return NULL;
- }
- if (NULL == auth_password)
- {
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("method",
- "external"));
- }
- else
- {
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("method",
- "token"),
- GNUNET_JSON_pack_string ("password",
- auth_password));
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- iaph->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (iaph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&iaph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (iaph->url);
- GNUNET_free (iaph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_POST));
- iaph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- iaph->post_ctx.headers,
- &handle_post_instance_auth_finished,
- iaph);
- }
- return iaph;
-}
-
-
-void
-TALER_MERCHANT_instance_auth_post_cancel (
- struct TALER_MERCHANT_InstanceAuthPostHandle *iaph)
-{
- if (NULL != iaph->job)
- GNUNET_CURL_job_cancel (iaph->job);
- TALER_curl_easy_post_finished (&iaph->post_ctx);
- GNUNET_free (iaph->url);
- GNUNET_free (iaph);
-}
diff --git a/src/lib/merchant_api_post_instance_token.c b/src/lib/merchant_api_post_instance_token.c
@@ -1,235 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_post_instance_token.c
- * @brief Implementation of the POST /instance/$ID/private/token request
- * @author Martin Schanzenbach
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /instances/$ID/private/token operation.
- */
-struct TALER_MERCHANT_InstanceTokenPostHandle
-{
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_InstanceTokenPostCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP GET /instances/$ID/private/token request.
- *
- * @param cls the `struct TALER_MERCHANT_InstanceTokenPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_instance_token_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_InstanceTokenPostHandle *itph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- itph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Got /instances/$ID response with status code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_OK:
- break;
- case MHD_HTTP_BAD_REQUEST:
- /* happens if the auth token is malformed */
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- default:
- /* unexpected response code */
- hr.ec = TALER_JSON_get_error_code (json);
- 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) hr.ec);
- break;
- }
- itph->cb (itph->cb_cls,
- &hr);
- TALER_MERCHANT_instance_token_post_cancel (itph);
-}
-
-
-struct TALER_MERCHANT_InstanceTokenPostHandle *
-TALER_MERCHANT_instance_token_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- const char *scope,
- struct GNUNET_TIME_Relative duration,
- bool refreshable,
- TALER_MERCHANT_InstanceTokenPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_InstanceTokenPostHandle *itph;
- json_t *req_obj;
-
- itph = GNUNET_new (struct TALER_MERCHANT_InstanceTokenPostHandle);
- itph->ctx = ctx;
- itph->cb = cb;
- itph->cb_cls = cb_cls;
- if (NULL != instance_id)
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "instances/%s/private/token",
- instance_id);
- itph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- else
- {
- /* backend_url is already identifying the instance */
- itph->url = TALER_url_join (backend_url,
- "private/token",
- NULL);
- }
- if (NULL == itph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (itph);
- return NULL;
- }
- if (NULL == scope)
- {
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_time_rel ("duration",
- duration),
- GNUNET_JSON_pack_bool ("refreshable",
- refreshable),
- GNUNET_JSON_pack_string ("scope",
- "readonly"));
- }
- else
- {
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_time_rel ("duration",
- duration),
- GNUNET_JSON_pack_bool ("refreshable",
- refreshable),
- GNUNET_JSON_pack_string ("scope",
- scope));
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting URL '%s'\n",
- itph->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (itph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&itph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (itph->url);
- GNUNET_free (itph);
- return NULL;
- }
- json_decref (req_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_CUSTOMREQUEST,
- MHD_HTTP_METHOD_POST));
- itph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- itph->post_ctx.headers,
- &handle_post_instance_token_finished,
- itph);
- }
- return itph;
-}
-
-
-void
-TALER_MERCHANT_instance_token_post_cancel (
- struct TALER_MERCHANT_InstanceTokenPostHandle *itph)
-{
- if (NULL != itph->job)
- GNUNET_CURL_job_cancel (itph->job);
- TALER_curl_easy_post_finished (&itph->post_ctx);
- GNUNET_free (itph->url);
- GNUNET_free (itph);
-}
diff --git a/src/lib/merchant_api_post_instances.c b/src/lib/merchant_api_post_instances.c
@@ -1,280 +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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_instances.c
- * @brief Implementation of the POST /instances request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-#include <taler/taler_kyclogic_lib.h>
-
-
-/**
- * Handle for a POST /instances/$ID operation.
- */
-struct TALER_MERCHANT_InstancesPostHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_InstancesPostCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /instances request.
- *
- * @param cls the `struct TALER_MERCHANT_InstancesPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_instances_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_InstancesPostHandle *iph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- iph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /management/instances completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- iph->cb (iph->cb_cls,
- &hr);
- TALER_MERCHANT_instances_post_cancel (iph);
-}
-
-
-struct TALER_MERCHANT_InstancesPostHandle *
-TALER_MERCHANT_instances_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *instance_id,
- const char *name,
- const json_t *address,
- const json_t *jurisdiction,
- bool use_stefan,
- struct GNUNET_TIME_Relative default_wire_transfer_delay,
- struct GNUNET_TIME_Relative default_pay_delay,
- struct GNUNET_TIME_Relative default_refund_delay,
- const char *auth_password,
- TALER_MERCHANT_InstancesPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_InstancesPostHandle *iph;
- json_t *req_obj;
- json_t *auth_obj;
-
- if (NULL != auth_password)
- {
- auth_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("method",
- "token"),
- GNUNET_JSON_pack_string ("password",
- auth_password));
- }
- else
- {
- auth_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("method",
- "external"));
- }
- if (NULL == auth_obj)
- {
- GNUNET_break (0);
- return NULL;
- }
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("id",
- instance_id),
- GNUNET_JSON_pack_string ("name",
- name),
- GNUNET_JSON_pack_object_incref ("address",
- (json_t *) address),
- GNUNET_JSON_pack_object_incref ("jurisdiction",
- (json_t *) jurisdiction),
- GNUNET_JSON_pack_bool ("use_stefan",
- use_stefan),
- GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay",
- default_wire_transfer_delay),
- GNUNET_JSON_pack_time_rel ("default_pay_delay",
- default_pay_delay),
- GNUNET_JSON_pack_time_rel ("default_refund_delay",
- default_refund_delay),
- /* FIXME: add eventually to arguments when we transform the API... */
- GNUNET_JSON_pack_time_rounder_interval (
- "default_wire_transfer_rounding_interval",
- GNUNET_TIME_RI_NONE),
- GNUNET_JSON_pack_object_steal ("auth",
- auth_obj));
- iph = GNUNET_new (struct TALER_MERCHANT_InstancesPostHandle);
- iph->ctx = ctx;
- iph->cb = cb;
- iph->cb_cls = cb_cls;
- iph->url = TALER_url_join (backend_url,
- "management/instances",
- NULL);
- if (NULL == iph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (iph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (iph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&iph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (iph);
- return NULL;
- }
- json_decref (req_obj);
- iph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- iph->post_ctx.headers,
- &handle_post_instances_finished,
- iph);
- }
- return iph;
-}
-
-
-void
-TALER_MERCHANT_instances_post_cancel (
- struct TALER_MERCHANT_InstancesPostHandle *iph)
-{
- if (NULL != iph->job)
- {
- GNUNET_CURL_job_cancel (iph->job);
- iph->job = NULL;
- }
- TALER_curl_easy_post_finished (&iph->post_ctx);
- GNUNET_free (iph->url);
- GNUNET_free (iph);
-}
-
-
-/* end of merchant_api_post_instances.c */
diff --git a/src/lib/merchant_api_post_order_abort.c b/src/lib/merchant_api_post_order_abort.c
@@ -1,436 +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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_order_abort.c
- * @brief Implementation of the POST /orders/$ID/abort request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- * @author Marcello Stanisci
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_exchange_service.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Maximum number of refunds we return.
- */
-#define MAX_REFUNDS 1024
-
-
-/**
- * @brief An abort Handle
- */
-struct TALER_MERCHANT_OrderAbortHandle
-{
- /**
- * Hash of the contract.
- */
- struct TALER_PrivateContractHashP h_contract_terms;
-
- /**
- * Public key of the merchant.
- */
- struct TALER_MerchantPublicKeyP merchant_pub;
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_AbortCallback abort_cb;
-
- /**
- * Closure for @a abort_cb.
- */
- void *abort_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;
-
- /**
- * The coins we are aborting on.
- */
- struct TALER_MERCHANT_AbortCoin *coins;
-
- /**
- * Number of @e coins we are paying with.
- */
- unsigned int num_coins;
-
-};
-
-
-/**
- * Check that the response for an abort is well-formed,
- * and call the application callback with the result if it is
- * OK. Otherwise returns #GNUNET_SYSERR.
- *
- * @param oah handle to operation that created the reply
- * @param[in] ar abort response, partially initialized
- * @param json the reply to parse
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah,
- struct TALER_MERCHANT_AbortResponse *ar,
- const json_t *json)
-{
- const json_t *refunds;
- unsigned int num_refunds;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("refunds",
- &refunds),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- num_refunds = (unsigned int) json_array_size (refunds);
- if ( (json_array_size (refunds) != (size_t) num_refunds) ||
- (num_refunds > MAX_REFUNDS) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
- {
- struct TALER_MERCHANT_AbortedCoin res[GNUNET_NZL (num_refunds)];
-
- for (unsigned int i = 0; i<num_refunds; i++)
- {
- json_t *refund = json_array_get (refunds, i);
- uint32_t exchange_status;
- struct GNUNET_JSON_Specification spec_es[] = {
- GNUNET_JSON_spec_uint32 ("exchange_status",
- &exchange_status),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (refund,
- spec_es,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (MHD_HTTP_OK == exchange_status)
- {
- struct GNUNET_JSON_Specification spec_detail[] = {
- GNUNET_JSON_spec_fixed_auto ("exchange_sig",
- &res[i].exchange_sig),
- GNUNET_JSON_spec_fixed_auto ("exchange_pub",
- &res[i].exchange_pub),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (refund,
- spec_detail,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- if (GNUNET_OK !=
- TALER_exchange_online_refund_confirmation_verify (
- &oah->h_contract_terms,
- &oah->coins[i].coin_pub,
- &oah->merchant_pub,
- 0, /* transaction id */
- &oah->coins[i].amount_with_fee,
- &res[i].exchange_pub,
- &res[i].exchange_sig))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- }
- ar->details.ok.merchant_pub = &oah->merchant_pub;
- ar->details.ok.num_aborts = num_refunds;
- ar->details.ok.aborts = res;
- oah->abort_cb (oah->abort_cb_cls,
- ar);
- oah->abort_cb = NULL;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * abort request.
- *
- * @param cls the `struct TALER_MERCHANT_OrderAbortHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_abort_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderAbortHandle *oah = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_AbortResponse ar = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- oah->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "/pay completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- if (GNUNET_OK ==
- check_abort_refund (oah,
- &ar,
- json))
- {
- TALER_MERCHANT_order_abort_cancel (oah);
- return;
- }
- ar.hr.http_status = 0;
- ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_BAD_REQUEST:
- ar.hr.ec = TALER_JSON_get_error_code (json);
- ar.hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us or the
- merchant is buggy (or API version conflict); just
- pass JSON reply to the application */
- break;
- case MHD_HTTP_FORBIDDEN:
- ar.hr.ec = TALER_JSON_get_error_code (json);
- ar.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_NOT_FOUND:
- ar.hr.ec = TALER_JSON_get_error_code (json);
- ar.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_REQUEST_TIMEOUT:
- ar.hr.ec = TALER_JSON_get_error_code (json);
- ar.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says one of
- the signatures is invalid; as we checked them,
- this should never happen, we should pass the JSON
- reply to the application */
- break;
- case MHD_HTTP_PRECONDITION_FAILED:
- /* Our *payment* already succeeded fully. */
- ar.hr.ec = TALER_JSON_get_error_code (json);
- ar.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- ar.hr.ec = TALER_JSON_get_error_code (json);
- ar.hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- case MHD_HTTP_BAD_GATEWAY:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &ar.hr);
- /* Nothing really to verify, the merchant is blaming the exchange.
- We should pass the JSON reply to the application */
- break;
- default:
- /* unexpected response code */
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &ar.hr);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) ar.hr.ec);
- GNUNET_break_op (0);
- break;
- }
- oah->abort_cb (oah->abort_cb_cls,
- &ar);
- TALER_MERCHANT_order_abort_cancel (oah);
-}
-
-
-struct TALER_MERCHANT_OrderAbortHandle *
-TALER_MERCHANT_order_abort (
- struct GNUNET_CURL_Context *ctx,
- const char *merchant_url,
- const char *order_id,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct TALER_PrivateContractHashP *h_contract,
- unsigned int num_coins,
- const struct TALER_MERCHANT_AbortCoin coins[static num_coins],
- TALER_MERCHANT_AbortCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OrderAbortHandle *oah;
- json_t *abort_obj;
- json_t *j_coins;
-
- j_coins = json_array ();
- if (NULL == j_coins)
- {
- GNUNET_break (0);
- return NULL;
- }
- for (unsigned int i = 0; i<num_coins; i++)
- {
- const struct TALER_MERCHANT_AbortCoin *ac = &coins[i];
- json_t *j_coin;
-
- /* create JSON for this coin */
- j_coin = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("coin_pub",
- &ac->coin_pub),
- /* FIXME: no longer needed since **v18**, remove eventually! */
- TALER_JSON_pack_amount ("contribution",
- &ac->amount_with_fee),
- GNUNET_JSON_pack_string ("exchange_url",
- ac->exchange_url));
- if (0 !=
- json_array_append_new (j_coins,
- j_coin))
- {
- GNUNET_break (0);
- json_decref (j_coins);
- return NULL;
- }
- }
- abort_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_array_steal ("coins",
- j_coins),
- GNUNET_JSON_pack_data_auto ("h_contract",
- h_contract));
- oah = GNUNET_new (struct TALER_MERCHANT_OrderAbortHandle);
- oah->h_contract_terms = *h_contract;
- oah->merchant_pub = *merchant_pub;
- oah->ctx = ctx;
- oah->abort_cb = cb;
- oah->abort_cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "orders/%s/abort",
- order_id);
- oah->url = TALER_url_join (merchant_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == oah->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (abort_obj);
- GNUNET_free (oah);
- return NULL;
- }
- oah->num_coins = num_coins;
- oah->coins = GNUNET_new_array (num_coins,
- struct TALER_MERCHANT_AbortCoin);
- GNUNET_memcpy (oah->coins,
- coins,
- num_coins * sizeof (struct TALER_MERCHANT_AbortCoin));
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (oah->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&oah->post_ctx,
- eh,
- abort_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (abort_obj);
- GNUNET_free (oah);
- return NULL;
- }
- json_decref (abort_obj);
- oah->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- oah->post_ctx.headers,
- &handle_abort_finished,
- oah);
- }
- return oah;
-}
-
-
-void
-TALER_MERCHANT_order_abort_cancel (
- struct TALER_MERCHANT_OrderAbortHandle *oah)
-{
- if (NULL != oah->job)
- {
- GNUNET_CURL_job_cancel (oah->job);
- oah->job = NULL;
- }
- TALER_curl_easy_post_finished (&oah->post_ctx);
- GNUNET_free (oah->coins);
- GNUNET_free (oah->url);
- GNUNET_free (oah);
-}
-
-
-/* end of merchant_api_post_order_abort.c */
diff --git a/src/lib/merchant_api_post_order_claim.c b/src/lib/merchant_api_post_order_claim.c
@@ -1,242 +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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_order_claim.c
- * @brief Implementation of POST /orders/$ID/claim
- * @author Christian Grothoff
- * @author Marcello Stanisci
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Structure representing a POST /orders/$ID/claim operation.
- */
-struct TALER_MERCHANT_OrderClaimHandle
-{
- /**
- * Full URL, includes "/orders/$ID/claim".
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OrderClaimCallback 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;
-};
-
-
-/**
- * Function called when we're done processing the
- * POST /orders/$ID/claim request.
- *
- * @param cls the `struct TALER_MERCHANT_OrderClaimHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, should be NULL
- */
-static void
-handle_post_order_claim_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderClaimHandle *och = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_OrderClaimResponse ocr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_object_const (
- "contract_terms",
- &ocr.details.ok.contract_terms),
- GNUNET_JSON_spec_fixed_auto (
- "sig",
- &ocr.details.ok.sig),
- GNUNET_JSON_spec_end ()
- };
-
- och->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Order claimed with status %u\n",
- (unsigned int) response_code);
-
- if (MHD_HTTP_OK != response_code)
- {
- ocr.hr.ec = TALER_JSON_get_error_code (json);
- ocr.hr.hint = TALER_JSON_get_error_hint (json);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Proposal lookup failed with HTTP status code %u/%d\n",
- (unsigned int) response_code,
- (int) ocr.hr.ec);
- och->cb (och->cb_cls,
- &ocr);
- TALER_MERCHANT_order_claim_cancel (och);
- return;
- }
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Claiming order failed: could not parse JSON response\n");
- GNUNET_break_op (0);
- ocr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- ocr.hr.http_status = 0;
- och->cb (och->cb_cls,
- &ocr);
- TALER_MERCHANT_order_claim_cancel (och);
- return;
- }
-
- if (GNUNET_OK !=
- TALER_JSON_contract_hash (ocr.details.ok.contract_terms,
- &ocr.details.ok.h_contract_terms))
- {
- GNUNET_break (0);
- ocr.hr.ec = TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE;
- ocr.hr.http_status = 0;
- GNUNET_JSON_parse_free (spec);
- och->cb (och->cb_cls,
- &ocr);
- TALER_MERCHANT_order_claim_cancel (och);
- return;
- }
- och->cb (och->cb_cls,
- &ocr);
- GNUNET_JSON_parse_free (spec);
- TALER_MERCHANT_order_claim_cancel (och);
-}
-
-
-struct TALER_MERCHANT_OrderClaimHandle *
-TALER_MERCHANT_order_claim (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *order_id,
- const struct GNUNET_CRYPTO_EddsaPublicKey *nonce,
- const struct TALER_ClaimTokenP *claim_token,
- TALER_MERCHANT_OrderClaimCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OrderClaimHandle *och;
- json_t *req_obj;
-
- if (NULL == order_id)
- {
- GNUNET_break (0);
- return NULL;
- }
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("nonce",
- nonce),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_data_auto ("token",
- claim_token)));
- och = GNUNET_new (struct TALER_MERCHANT_OrderClaimHandle);
- och->ctx = ctx;
- och->cb = cb;
- och->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "orders/%s/claim",
- order_id);
- och->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == och->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (och);
- return NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Claiming order at %s\n",
- och->url);
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (och->url);
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&och->post_ctx,
- eh,
- req_obj));
- json_decref (req_obj);
- och->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- och->post_ctx.headers,
- &handle_post_order_claim_finished,
- och);
- GNUNET_assert (NULL != och->job);
- }
- return och;
-}
-
-
-void
-TALER_MERCHANT_order_claim_cancel (struct TALER_MERCHANT_OrderClaimHandle *och)
-{
- if (NULL != och->job)
- {
- GNUNET_CURL_job_cancel (och->job);
- och->job = NULL;
- }
- TALER_curl_easy_post_finished (&och->post_ctx);
- GNUNET_free (och->url);
- GNUNET_free (och);
-}
-
-
-/* end of merchant_api_post_order_claim.c */
diff --git a/src/lib/merchant_api_post_order_paid.c b/src/lib/merchant_api_post_order_paid.c
@@ -1,271 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2020-2021 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_post_order_paid.c
- * @brief Implementation of the POST /order/$ID/paid request
- * of the merchant's HTTP API
- * @author Jonathan Buchanan
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_exchange_service.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * @brief Handle to a POST /orders/$ID/paid operation at a merchant.
- */
-struct TALER_MERCHANT_OrderPaidHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OrderPaidCallback paid_cb;
-
- /**
- * Closure for @a paid_cb.
- */
- void *paid_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;
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP /paid request.
- *
- * @param cls the `struct TALER_MERCHANT_OrderPaidHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_paid_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderPaidHandle *oph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_OrderPaidResponse opr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- oph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "/paid completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- {
- bool refunded;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_bool ("refunded",
- &refunded),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (opr.hr.reply,
- spec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- break;
- }
- break;
- case MHD_HTTP_BAD_REQUEST:
- opr.hr.ec = TALER_JSON_get_error_code (json);
- opr.hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_FORBIDDEN:
- opr.hr.ec = TALER_JSON_get_error_code (json);
- opr.hr.hint = TALER_JSON_get_error_hint (json);
- /* The signature provided was invalid */
- break;
- case MHD_HTTP_NOT_FOUND:
- opr.hr.ec = TALER_JSON_get_error_code (json);
- opr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_CONFLICT:
- opr.hr.ec = TALER_JSON_get_error_code (json);
- opr.hr.hint = TALER_JSON_get_error_hint (json);
- /* The hashed contract terms don't match with the order_id. */
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- opr.hr.ec = TALER_JSON_get_error_code (json);
- opr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- case MHD_HTTP_SERVICE_UNAVAILABLE:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &opr.hr);
- /* Exchange couldn't respond properly; the retry is
- left to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &opr.hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) opr.hr.ec);
- GNUNET_break_op (0);
- break;
- }
- oph->paid_cb (oph->paid_cb_cls,
- &opr);
- TALER_MERCHANT_order_paid_cancel (oph);
-}
-
-
-struct TALER_MERCHANT_OrderPaidHandle *
-TALER_MERCHANT_order_paid (
- struct GNUNET_CURL_Context *ctx,
- const char *merchant_url,
- const char *order_id,
- const char *session_id,
- const struct TALER_PrivateContractHashP *h_contract_terms,
- const struct GNUNET_HashCode *wallet_data_hash,
- const struct TALER_MerchantSignatureP *merchant_sig,
- TALER_MERCHANT_OrderPaidCallback paid_cb,
- void *paid_cb_cls)
-{
- struct TALER_MERCHANT_OrderPaidHandle *oph;
- json_t *req_obj;
-
- (void) wallet_data_hash;
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("sig",
- merchant_sig),
- GNUNET_JSON_pack_data_auto ("h_contract",
- h_contract_terms),
- GNUNET_JSON_pack_string ("session_id",
- session_id));
- oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle);
- oph->ctx = ctx;
- oph->paid_cb = paid_cb;
- oph->paid_cb_cls = paid_cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "orders/%s/paid",
- order_id);
- oph->url = TALER_url_join (merchant_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == oph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (oph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&oph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (oph);
- return NULL;
- }
- json_decref (req_obj);
- oph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- oph->post_ctx.headers,
- &handle_paid_finished,
- oph);
- }
- return oph;
-}
-
-
-void
-TALER_MERCHANT_order_paid_cancel (
- struct TALER_MERCHANT_OrderPaidHandle *oph)
-{
- if (NULL != oph->job)
- {
- GNUNET_CURL_job_cancel (oph->job);
- oph->job = NULL;
- }
- TALER_curl_easy_post_finished (&oph->post_ctx);
- GNUNET_free (oph->url);
- GNUNET_free (oph);
-}
-
-
-/* end of merchant_api_post_order_paid.c */
diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c
@@ -1,790 +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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_order_pay.c
- * @brief Implementation of the POST /order/$ID/pay request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- * @author Marcello Stanisci
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <gnunet/gnunet_common.h>
-#include <gnunet/gnunet_json_lib.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/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <stdio.h>
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_exchange_service.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * @brief A Pay Handle
- */
-struct TALER_MERCHANT_OrderPayHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result in "pay" @e mode.
- */
- TALER_MERCHANT_OrderPayCallback pay_cb;
-
- /**
- * Closure for @a pay_cb.
- */
- void *pay_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;
-
- /**
- * The coins we are paying with.
- */
- struct TALER_MERCHANT_PaidCoin *coins;
-
- /**
- * Hash of the contract we are paying, set
- * if @e am_wallet is true.
- */
- struct TALER_PrivateContractHashP h_contract_terms;
-
- /**
- * Public key of the merchant (instance) being paid, set
- * if @e am_wallet is true.
- */
- struct TALER_MerchantPublicKeyP merchant_pub;
-
- /**
- * JSON with the full reply, used during async
- * processing.
- */
- json_t *full_reply;
-
- /**
- * Pointer into @e coins array for the coin that
- * created a conflict (that we are checking).
- */
- const struct TALER_MERCHANT_PaidCoin *error_pc;
-
- /**
- * Coin history that proves a conflict.
- */
- json_t *error_history;
-
- /**
- * Number of @e coins we are paying with.
- */
- unsigned int num_coins;
-
- /**
- * Set to true if this is the wallet API and we have
- * initialized @e h_contract_terms and @e merchant_pub.
- */
- bool am_wallet;
-
-};
-
-
-/**
- * Parse blindly signed output tokens from response.
- *
- * @param token_sigs the JSON array with the token signatures. Can be NULL.
- * @param tokens where to store the parsed tokens.
- * @param num_tokens where to store the length of the @a tokens array.
- */
-static enum GNUNET_GenericReturnValue
-parse_tokens (const json_t *token_sigs,
- struct TALER_MERCHANT_OutputToken **tokens,
- unsigned int *num_tokens)
-{
- GNUNET_array_grow (*tokens,
- *num_tokens,
- json_array_size (token_sigs));
-
- for (unsigned int i = 0; i<(*num_tokens); i++)
- {
- struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i];
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_blinded_token_issue_sig ("blind_sig",
- &token->blinded_sig),
- GNUNET_JSON_spec_end ()
- };
- const json_t *jtoken
- = json_array_get (token_sigs,
- i);
-
- if (NULL == jtoken)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- GNUNET_JSON_parse (jtoken,
- spec,
- NULL, NULL))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- }
-
- return GNUNET_YES;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /pay request.
- *
- * @param cls the `struct TALER_MERCHANT_Pay`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_pay_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderPayHandle *oph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_PayResponse pr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Received /pay response with status code %u\n",
- (unsigned int) response_code);
-
- json_dumpf (json,
- stderr,
- JSON_INDENT (2));
-
- oph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "/pay completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- if (oph->am_wallet)
- {
- const json_t *token_sigs = NULL;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("sig",
- &pr.details.ok.merchant_sig),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("pos_confirmation",
- &pr.details.ok.pos_confirmation),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("token_sigs",
- &token_sigs),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- pr.hr.http_status = 0;
- pr.hr.hint = "sig field missing in response";
- break;
- }
-
- if (GNUNET_OK !=
- parse_tokens (token_sigs,
- &pr.details.ok.tokens,
- &pr.details.ok.num_tokens))
- {
- GNUNET_break_op (0);
- pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- pr.hr.http_status = 0;
- pr.hr.hint = "failed to parse token_sigs field in response";
- break;
- }
-
- if (GNUNET_OK !=
- TALER_merchant_pay_verify (&oph->h_contract_terms,
- &oph->merchant_pub,
- &pr.details.ok.merchant_sig))
- {
- GNUNET_break_op (0);
- pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- pr.hr.http_status = 0;
- pr.hr.hint = "signature invalid";
- }
- }
- break;
- /* Tolerating Not Acceptable because sometimes
- * - especially in tests - we might want to POST
- * coins one at a time. */
- case MHD_HTTP_NOT_ACCEPTABLE:
- pr.hr.ec = TALER_JSON_get_error_code (json);
- pr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_BAD_REQUEST:
- pr.hr.ec = TALER_JSON_get_error_code (json);
- pr.hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_PAYMENT_REQUIRED:
- /* was originally paid, but then refunded */
- pr.hr.ec = TALER_JSON_get_error_code (json);
- pr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_FORBIDDEN:
- pr.hr.ec = TALER_JSON_get_error_code (json);
- pr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_NOT_FOUND:
- pr.hr.ec = TALER_JSON_get_error_code (json);
- pr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_REQUEST_TIMEOUT:
- pr.hr.ec = TALER_JSON_get_error_code (json);
- pr.hr.hint = TALER_JSON_get_error_hint (json);
- /* The merchant couldn't generate a timely response, likely because
- it itself waited too long on the exchange.
- Pass on to application. */
- break;
- case MHD_HTTP_CONFLICT:
- TALER_MERCHANT_parse_error_details_ (json,
- MHD_HTTP_CONFLICT,
- &pr.hr);
- break;
- case MHD_HTTP_GONE:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &pr.hr);
- /* The merchant says we are too late, the offer has expired or some
- denomination key of a coin involved has expired.
- Might be a disagreement in timestamps? Still, pass on to application. */
- break;
- case MHD_HTTP_PRECONDITION_FAILED:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &pr.hr);
- /* Nothing really to verify, the merchant is blaming us for failing to
- satisfy some constraint (likely it does not like our exchange because
- of some disagreement on the PKI). We should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
- {
- json_t *ebus = json_object_get (json,
- "exchange_base_urls");
- if (NULL == ebus)
- {
- GNUNET_break_op (0);
- pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- pr.hr.http_status = 0;
- pr.hr.hint = "failed to parse exchange_base_urls field in response";
- break;
- }
- {
- size_t alen = json_array_size (ebus);
- const char *ebua[GNUNET_NZL (alen)];
- size_t idx;
- json_t *jebu;
- bool ok = true;
-
- GNUNET_assert (alen <= UINT_MAX);
- json_array_foreach (ebus, idx, jebu)
- {
- ebua[idx] = json_string_value (jebu);
- if (NULL == ebua[idx])
- {
- GNUNET_break_op (0);
- pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- pr.hr.http_status = 0;
- pr.hr.hint = "non-string value in exchange_base_urls in response";
- ok = false;
- break;
- }
- }
- if (! ok)
- break;
- pr.details.unavailable_for_legal_reasons.num_exchanges
- = (unsigned int) alen;
- pr.details.unavailable_for_legal_reasons.exchanges
- = ebua;
- oph->pay_cb (oph->pay_cb_cls,
- &pr);
- TALER_MERCHANT_order_pay_cancel (oph);
- return;
- }
- }
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &pr.hr);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- case MHD_HTTP_BAD_GATEWAY:
- /* Nothing really to verify, the merchant is blaming the exchange.
- We should pass the JSON reply to the application */
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &pr.hr);
- break;
- case MHD_HTTP_SERVICE_UNAVAILABLE:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &pr.hr);
- /* Exchange couldn't respond properly; the retry is
- left to the application */
- break;
- case MHD_HTTP_GATEWAY_TIMEOUT:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &pr.hr);
- /* Exchange couldn't respond in a timely fashion;
- the retry is left to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &pr.hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) pr.hr.ec);
- GNUNET_break_op (0);
- break;
- }
- oph->pay_cb (oph->pay_cb_cls,
- &pr);
- TALER_MERCHANT_order_pay_cancel (oph);
-}
-
-
-struct TALER_MERCHANT_OrderPayHandle *
-TALER_MERCHANT_order_pay_frontend (
- struct GNUNET_CURL_Context *ctx,
- const char *merchant_url,
- const char *order_id,
- const char *session_id,
- const json_t *wallet_data,
- unsigned int num_coins,
- const struct TALER_MERCHANT_PaidCoin coins[static num_coins],
- unsigned int num_tokens,
- const struct TALER_MERCHANT_UsedToken *tokens,
- json_t *j_output_tokens, // FIXME: not used, remove?
- TALER_MERCHANT_OrderPayCallback pay_cb,
- void *pay_cb_cls)
-{
- struct TALER_MERCHANT_OrderPayHandle *oph;
- json_t *pay_obj;
- json_t *j_coins;
- json_t *j_tokens = NULL;
- CURL *eh;
- struct TALER_Amount total_fee;
- struct TALER_Amount total_amount;
-
- j_coins = json_array ();
- GNUNET_assert (NULL != j_coins);
- for (unsigned int i = 0; i<num_coins; i++)
- {
- json_t *j_coin;
- const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
- struct TALER_Amount fee;
- struct TALER_DenominationHashP denom_hash;
-
- if (0 >
- TALER_amount_subtract (&fee,
- &pc->amount_with_fee,
- &pc->amount_without_fee))
- {
- /* Integer underflow, fee larger than total amount?
- This should not happen (client violated API!) */
- GNUNET_break (0);
- json_decref (j_coins);
- return NULL;
- }
- if (0 == i)
- {
- total_fee = fee;
- total_amount = pc->amount_with_fee;
- }
- else
- {
- if ( (0 >
- TALER_amount_add (&total_fee,
- &total_fee,
- &fee)) ||
- (0 >
- TALER_amount_add (&total_amount,
- &total_amount,
- &pc->amount_with_fee)) )
- {
- /* integer overflow */
- GNUNET_break (0);
- json_decref (j_coins);
- return NULL;
- }
- }
-
- TALER_denom_pub_hash (&pc->denom_pub,
- &denom_hash);
- /* create JSON for this coin */
- j_coin = GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("contribution",
- &pc->amount_with_fee),
- GNUNET_JSON_pack_data_auto ("coin_pub",
- &pc->coin_pub),
- GNUNET_JSON_pack_string ("exchange_url",
- pc->exchange_url),
- GNUNET_JSON_pack_data_auto ("h_denom",
- &denom_hash),
- TALER_JSON_pack_denom_sig ("ub_sig",
- &pc->denom_sig),
- GNUNET_JSON_pack_data_auto ("coin_sig",
- &pc->coin_sig));
- if (0 !=
- json_array_append_new (j_coins,
- j_coin))
- {
- GNUNET_break (0);
- json_decref (j_coins);
- return NULL;
- }
- }
-
- if (0 < num_tokens)
- {
- j_tokens = json_array ();
- GNUNET_assert (NULL != j_tokens);
- for (unsigned int i = 0; i<num_tokens; i++)
- {
- json_t *j_token;
- const struct TALER_MERCHANT_UsedToken *ut = &tokens[i];
-
- j_token = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("token_sig",
- &ut->token_sig),
- GNUNET_JSON_pack_data_auto ("token_pub",
- &ut->token_pub),
- GNUNET_JSON_pack_data_auto ("h_issue",
- &ut->issue_pub.public_key->pub_key_hash),
- TALER_JSON_pack_token_issue_sig ("ub_sig",
- &ut->ub_sig));
- if (0 !=
- json_array_append_new (j_tokens,
- j_token))
- {
- GNUNET_break (0);
- json_decref (j_tokens);
- return NULL;
- }
- }
- }
-
- pay_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_array_steal ("coins",
- j_coins),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_steal ("tokens",
- j_tokens)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("wallet_data",
- (json_t *) wallet_data)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("session_id",
- session_id)));
-
- json_dumpf (pay_obj,
- stderr,
- JSON_INDENT (2));
-
- oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
- oph->ctx = ctx;
- oph->pay_cb = pay_cb;
- oph->pay_cb_cls = pay_cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "orders/%s/pay",
- order_id);
- oph->url = TALER_url_join (merchant_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == oph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (pay_obj);
- GNUNET_free (oph);
- return NULL;
- }
- oph->num_coins = num_coins;
- oph->coins = GNUNET_new_array (num_coins,
- struct TALER_MERCHANT_PaidCoin);
- GNUNET_memcpy (oph->coins,
- coins,
- num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
-
- eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&oph->post_ctx,
- eh,
- pay_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (pay_obj);
- GNUNET_free (oph->url);
- GNUNET_free (oph);
- return NULL;
- }
- json_decref (pay_obj);
- oph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- oph->post_ctx.headers,
- &handle_pay_finished,
- oph);
- return oph;
-}
-
-
-struct TALER_MERCHANT_OrderPayHandle *
-TALER_MERCHANT_order_pay (
- struct GNUNET_CURL_Context *ctx,
- const char *merchant_url,
- const char *session_id,
- const struct TALER_PrivateContractHashP *h_contract_terms,
- int choice_index,
- const struct TALER_Amount *amount,
- const struct TALER_Amount *max_fee,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct TALER_MerchantSignatureP *merchant_sig,
- struct GNUNET_TIME_Timestamp timestamp,
- struct GNUNET_TIME_Timestamp refund_deadline,
- struct GNUNET_TIME_Timestamp pay_deadline,
- const struct TALER_MerchantWireHashP *h_wire,
- const char *order_id,
- unsigned int num_coins,
- const struct TALER_MERCHANT_PayCoin coins[static num_coins],
- unsigned int num_tokens,
- const struct TALER_MERCHANT_UseToken *tokens,
- unsigned int num_output_tokens,
- const struct TALER_MERCHANT_OutputToken *output_tokens,
- TALER_MERCHANT_OrderPayCallback pay_cb,
- void *pay_cb_cls)
-{
- json_t *j_output_tokens = NULL;
- const json_t *wallet_data = NULL;
- struct GNUNET_HashCode wallet_data_hash;
-
- if (GNUNET_YES !=
- TALER_amount_cmp_currency (amount,
- max_fee))
- {
- GNUNET_break (0);
- return NULL;
- }
- if ((0 < num_tokens || 0 < num_output_tokens) && 0 > choice_index)
- {
- /* Tokens (input or output) require a valid choice_index to be set.
- Only contracts with coices can use or issue tokens. */
- GNUNET_break (0);
- return NULL;
- }
- if (0 < num_output_tokens)
- {
- /* Build token envelopes json array. */
- j_output_tokens = json_array ();
- GNUNET_assert (NULL != j_output_tokens);
- for (unsigned int i = 0; i<num_output_tokens; i++)
- {
- json_t *j_token_ev;
- const struct TALER_MERCHANT_OutputToken *ev = &output_tokens[i];
-
- j_token_ev = GNUNET_JSON_PACK (
- TALER_JSON_pack_token_envelope (NULL,
- &ev->envelope));
-
- if (0 !=
- json_array_append_new (j_output_tokens,
- j_token_ev))
- {
- GNUNET_break (0);
- json_decref (j_output_tokens);
- return NULL;
- }
- }
- }
- if (0 <= choice_index)
- {
- wallet_data = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_int64 ("choice_index",
- choice_index),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_incref ("tokens_evs",
- j_output_tokens)));
- TALER_json_hash (wallet_data,
- &wallet_data_hash);
- }
- {
- struct TALER_MERCHANT_PaidCoin pc[num_coins];
- struct TALER_MERCHANT_UsedToken ut[num_tokens];
-
- for (unsigned int i = 0; i<num_coins; i++)
- {
- const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
- struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
- struct TALER_Amount fee;
- struct TALER_DenominationHashP h_denom_pub;
-
- if (0 >
- TALER_amount_subtract (&fee,
- &coin->amount_with_fee,
- &coin->amount_without_fee))
- {
- /* Integer underflow, fee larger than total amount?
- This should not happen (client violated API!) */
- GNUNET_break (0);
- return NULL;
- }
- TALER_denom_pub_hash (&coin->denom_pub,
- &h_denom_pub);
- TALER_wallet_deposit_sign (&coin->amount_with_fee,
- &fee,
- h_wire,
- h_contract_terms,
- (NULL != wallet_data)
- ? &wallet_data_hash
- : NULL,
- coin->h_age_commitment,
- NULL /* h_extensions! */,
- &h_denom_pub,
- timestamp,
- merchant_pub,
- refund_deadline,
- &coin->coin_priv,
- &p->coin_sig);
- p->denom_pub = coin->denom_pub;
- p->denom_sig = coin->denom_sig;
- p->denom_value = coin->denom_value;
- GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
- &p->coin_pub.eddsa_pub);
- p->amount_with_fee = coin->amount_with_fee;
- p->amount_without_fee = coin->amount_without_fee;
- p->exchange_url = coin->exchange_url;
- }
- for (unsigned int i = 0; i<num_tokens; i++)
- {
- const struct TALER_MERCHANT_UseToken *token = &tokens[i];
- struct TALER_MERCHANT_UsedToken *t = &ut[i];
-
- TALER_wallet_token_use_sign (h_contract_terms,
- &wallet_data_hash, // checked for != NULL above
- &token->token_priv,
- &t->token_sig);
- t->ub_sig = token->ub_sig;
- t->issue_pub = token->issue_pub;
- GNUNET_CRYPTO_eddsa_key_get_public (&token->token_priv.private_key,
- &t->token_pub.public_key);
- }
- {
- struct TALER_MERCHANT_OrderPayHandle *oph;
-
- oph = TALER_MERCHANT_order_pay_frontend (ctx,
- merchant_url,
- order_id,
- session_id,
- wallet_data,
- num_coins,
- pc,
- num_tokens,
- ut,
- j_output_tokens,
- pay_cb,
- pay_cb_cls);
- if (NULL == oph)
- return NULL;
- oph->h_contract_terms = *h_contract_terms;
- oph->merchant_pub = *merchant_pub;
- oph->am_wallet = true;
- return oph;
- }
- }
-}
-
-
-void
-TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
-{
- if (NULL != oph->job)
- {
- GNUNET_CURL_job_cancel (oph->job);
- oph->job = NULL;
- }
- TALER_curl_easy_post_finished (&oph->post_ctx);
- json_decref (oph->error_history);
- json_decref (oph->full_reply);
- GNUNET_free (oph->coins);
- GNUNET_free (oph->url);
- GNUNET_free (oph);
-}
-
-
-/* end of merchant_api_post_order_pay.c */
diff --git a/src/lib/merchant_api_post_order_refund.c b/src/lib/merchant_api_post_order_refund.c
@@ -1,246 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_order_refund.c
- * @brief Implementation of the POST /orders/ID/refund request
- * @author Christian Grothoff
- * @author Marcello Stanisci
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /orders/ID/refund operation.
- */
-struct TALER_MERCHANT_OrderRefundHandle
-{
- /**
- * Complete URL where the backend offers /refund
- */
- char *url;
-
- /**
- * Minor context that holds body and headers.
- */
- struct TALER_CURL_PostContext post_ctx;
-
- /**
- * The CURL context to connect to the backend
- */
- struct GNUNET_CURL_Context *ctx;
-
- /**
- * The callback to pass the backend response to
- */
- TALER_MERCHANT_RefundCallback cb;
-
- /**
- * Clasure to pass to the callback
- */
- void *cb_cls;
-
- /**
- * Handle for the request
- */
- struct GNUNET_CURL_Job *job;
-};
-
-
-/**
- * Callback to process POST /orders/ID/refund response
- *
- * @param cls the `struct TALER_MERCHANT_OrderRefundHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not JSON
- */
-static void
-handle_refund_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderRefundHandle *orh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_RefundResponse rr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- orh->job = NULL;
- switch (response_code)
- {
- case 0:
- rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string (
- "taler_refund_uri",
- &rr.details.ok.taler_refund_uri),
- GNUNET_JSON_spec_fixed_auto (
- "h_contract",
- &rr.details.ok.h_contract),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- rr.hr.http_status = 0;
- rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
- break;
- }
- case MHD_HTTP_UNAUTHORIZED:
- rr.hr.ec = TALER_JSON_get_error_code (json);
- rr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- rr.hr.ec = TALER_JSON_get_error_code (json);
- rr.hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- /* order unknown (or not paid) */
- rr.hr.ec = TALER_JSON_get_error_code (json);
- rr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_CONFLICT:
- /* amount not acceptable */
- rr.hr.ec = TALER_JSON_get_error_code (json);
- rr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_GONE:
- /* too late, wire deadline is past */
- rr.hr.ec = TALER_JSON_get_error_code (json);
- rr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- GNUNET_break_op (0); /* unexpected status code */
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &rr.hr);
- break;
- }
- orh->cb (orh->cb_cls,
- &rr);
- TALER_MERCHANT_post_order_refund_cancel (orh);
-}
-
-
-void
-TALER_MERCHANT_post_order_refund_cancel (
- struct TALER_MERCHANT_OrderRefundHandle *orh)
-{
- if (NULL != orh->job)
- {
- GNUNET_CURL_job_cancel (orh->job);
- orh->job = NULL;
- }
- TALER_curl_easy_post_finished (&orh->post_ctx);
- GNUNET_free (orh->url);
- GNUNET_free (orh);
-}
-
-
-struct TALER_MERCHANT_OrderRefundHandle *
-TALER_MERCHANT_post_order_refund (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *order_id,
- const struct TALER_Amount *refund,
- const char *reason,
- TALER_MERCHANT_RefundCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OrderRefundHandle *orh;
- json_t *req;
- CURL *eh;
-
- orh = GNUNET_new (struct TALER_MERCHANT_OrderRefundHandle);
- orh->ctx = ctx;
- orh->cb = cb;
- orh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "private/orders/%s/refund",
- order_id);
- orh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == orh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (orh);
- return NULL;
- }
- req = GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("refund",
- refund),
- GNUNET_JSON_pack_string ("reason",
- reason));
- GNUNET_assert (NULL != req);
- eh = TALER_MERCHANT_curl_easy_get_ (orh->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&orh->post_ctx,
- eh,
- req))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req);
- GNUNET_free (orh->url);
- GNUNET_free (orh);
- return NULL;
- }
- json_decref (req);
- orh->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- orh->post_ctx.headers,
- &handle_refund_finished,
- orh);
- if (NULL == orh->job)
- {
- GNUNET_free (orh->url);
- GNUNET_free (orh);
- return NULL;
- }
- return orh;
-}
-
-
-/* end of merchant_api_post_order_refund.c */
diff --git a/src/lib/merchant_api_post_orders.c b/src/lib/merchant_api_post_orders.c
@@ -1,297 +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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_orders.c
- * @brief Implementation of the POST /orders
- * @author Christian Grothoff
- * @author Marcello Stanisci
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * @brief A POST /orders Handle
- */
-struct TALER_MERCHANT_PostOrdersHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_PostOrdersCallback 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;
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /orders request.
- *
- * @param cls the `struct TALER_MERCHANT_PostOrdersHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not JSON
- */
-static void
-handle_post_order_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_PostOrdersHandle *po = cls;
- const json_t *json = response;
-
- po->job = NULL;
- TALER_MERCHANT_handle_order_creation_response_ (po->cb,
- po->cb_cls,
- response_code,
- json);
- TALER_MERCHANT_orders_post_cancel (po);
-}
-
-
-struct TALER_MERCHANT_PostOrdersHandle *
-TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const json_t *order,
- struct GNUNET_TIME_Relative refund_delay,
- TALER_MERCHANT_PostOrdersCallback cb,
- void *cb_cls)
-{
- static const char *no_uuids[GNUNET_NZL (0)];
-
- return TALER_MERCHANT_orders_post2 (ctx,
- backend_url,
- order,
- refund_delay,
- NULL,
- 0,
- NULL,
- 0,
- no_uuids,
- true,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_PostOrdersHandle *
-TALER_MERCHANT_orders_post2 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const json_t *order,
- struct GNUNET_TIME_Relative refund_delay,
- const char *payment_target,
- unsigned int inventory_products_length,
- const struct TALER_MERCHANT_InventoryProduct inventory_products[],
- unsigned int uuids_length,
- const char *uuids[],
- bool create_token,
- TALER_MERCHANT_PostOrdersCallback cb,
- void *cb_cls)
-{
- return TALER_MERCHANT_orders_post3 (
- ctx,
- backend_url,
- order,
- NULL, /* session ID */
- refund_delay,
- payment_target,
- inventory_products_length,
- inventory_products,
- uuids_length,
- uuids,
- create_token,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_PostOrdersHandle *
-TALER_MERCHANT_orders_post3 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const json_t *order,
- const char *session_id,
- struct GNUNET_TIME_Relative refund_delay,
- const char *payment_target,
- unsigned int inventory_products_length,
- const struct TALER_MERCHANT_InventoryProduct inventory_products[],
- unsigned int uuids_length,
- const char *uuids[],
- bool create_token,
- TALER_MERCHANT_PostOrdersCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_PostOrdersHandle *po;
- json_t *req;
- CURL *eh;
-
- po = GNUNET_new (struct TALER_MERCHANT_PostOrdersHandle);
- po->ctx = ctx;
- po->cb = cb;
- po->cb_cls = cb_cls;
- po->url = TALER_url_join (backend_url,
- "private/orders",
- NULL);
- req = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_object_incref ("order",
- (json_t *) order),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("session_id",
- session_id)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("payment_target",
- payment_target)));
- if (0 != refund_delay.rel_value_us)
- {
- GNUNET_assert (0 ==
- json_object_set_new (req,
- "refund_delay",
- GNUNET_JSON_from_time_rel (
- refund_delay)));
- }
- if (0 != inventory_products_length)
- {
- json_t *ipa = json_array ();
-
- GNUNET_assert (NULL != ipa);
- for (unsigned int i = 0; i<inventory_products_length; i++)
- {
- json_t *ip;
- char unit_quantity_buf[64];
-
- TALER_MERCHANT_format_quantity_string (inventory_products[i].quantity,
- inventory_products[i].quantity_frac
- ,
- unit_quantity_buf,
- sizeof (unit_quantity_buf));
-
- ip = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("product_id",
- inventory_products[i].product_id),
- GNUNET_JSON_pack_string ("unit_quantity",
- unit_quantity_buf));
- GNUNET_assert (NULL != ip);
- GNUNET_assert (0 ==
- json_array_append_new (ipa,
- ip));
- }
- GNUNET_assert (0 ==
- json_object_set_new (req,
- "inventory_products",
- ipa));
- }
- if (0 != uuids_length)
- {
- json_t *ua = json_array ();
-
- GNUNET_assert (NULL != ua);
- for (unsigned int i = 0; i<uuids_length; i++)
- {
- json_t *u;
-
- u = json_string (uuids[i]);
- GNUNET_assert (0 ==
- json_array_append_new (ua,
- u));
- }
- GNUNET_assert (0 ==
- json_object_set_new (req,
- "lock_uuids",
- ua));
- }
- if (! create_token)
- {
- GNUNET_assert (0 ==
- json_object_set_new (req,
- "create_token",
- json_boolean (create_token)));
- }
- eh = TALER_MERCHANT_curl_easy_get_ (po->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&po->post_ctx,
- eh,
- req))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req);
- GNUNET_free (po);
- return NULL;
- }
- json_decref (req);
- po->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- po->post_ctx.headers,
- &handle_post_order_finished,
- po);
- return po;
-}
-
-
-void
-TALER_MERCHANT_orders_post_cancel (
- struct TALER_MERCHANT_PostOrdersHandle *po)
-{
- if (NULL != po->job)
- {
- GNUNET_CURL_job_cancel (po->job);
- po->job = NULL;
- }
- GNUNET_free (po->url);
- TALER_curl_easy_post_finished (&po->post_ctx);
- GNUNET_free (po);
-}
-
-
-/* end of merchant_api_post_orders.c */
diff --git a/src/lib/merchant_api_post_otp_devices.c b/src/lib/merchant_api_post_otp_devices.c
@@ -1,237 +0,0 @@
-/*
- 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_post_otp_devices.c
- * @brief Implementation of the POST /otp-devices request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /otp-devices/$ID operation.
- */
-struct TALER_MERCHANT_OtpDevicesPostHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OtpDevicesPostCallback 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;
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /otp-devices request.
- *
- * @param cls the `struct TALER_MERCHANT_OtpDevicesPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_otp_devices_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OtpDevicesPostHandle *tph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /otp-devices completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- tph->cb (tph->cb_cls,
- &hr);
- TALER_MERCHANT_otp_devices_post_cancel (tph);
-}
-
-
-struct TALER_MERCHANT_OtpDevicesPostHandle *
-TALER_MERCHANT_otp_devices_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *otp_device_id,
- const char *otp_device_description,
- const char *otp_key,
- enum TALER_MerchantConfirmationAlgorithm otp_algorithm,
- uint64_t otp_ctr,
- TALER_MERCHANT_OtpDevicesPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OtpDevicesPostHandle *tph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("otp_device_id",
- otp_device_id),
- GNUNET_JSON_pack_string ("otp_device_description",
- otp_device_description),
- GNUNET_JSON_pack_uint64 ("otp_algorithm",
- (uint32_t) otp_algorithm),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("otp_key",
- otp_key)),
- GNUNET_JSON_pack_uint64 ("otp_ctr",
- otp_ctr));
- tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicesPostHandle);
- tph->ctx = ctx;
- tph->cb = cb;
- tph->cb_cls = cb_cls;
- tph->url = TALER_url_join (backend_url,
- "private/otp-devices",
- NULL);
- if (NULL == tph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (tph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&tph->post_ctx,
- eh,
- req_obj));
- json_decref (req_obj);
- tph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- tph->post_ctx.headers,
- &handle_post_otp_devices_finished,
- tph);
- GNUNET_assert (NULL != tph->job);
- }
- return tph;
-}
-
-
-void
-TALER_MERCHANT_otp_devices_post_cancel (
- struct TALER_MERCHANT_OtpDevicesPostHandle *tph)
-{
- if (NULL != tph->job)
- {
- GNUNET_CURL_job_cancel (tph->job);
- tph->job = NULL;
- }
- TALER_curl_easy_post_finished (&tph->post_ctx);
- GNUNET_free (tph->url);
- GNUNET_free (tph);
-}
-
-
-/* end of merchant_api_post_otp_devices.c */
diff --git a/src/lib/merchant_api_post_products.c b/src/lib/merchant_api_post_products.c
@@ -1,433 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2020-2024 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_post_products.c
- * @brief Implementation of the POST /products request
- * of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /products/$ID operation.
- */
-struct TALER_MERCHANT_ProductsPostHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_ProductsPostCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /products request.
- *
- * @param cls the `struct TALER_MERCHANT_ProductsPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_products_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_ProductsPostHandle *pph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- pph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /products completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- pph->cb (pph->cb_cls,
- &hr);
- TALER_MERCHANT_products_post_cancel (pph);
-}
-
-
-struct TALER_MERCHANT_ProductsPostHandle *
-TALER_MERCHANT_products_post4 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- const char *description,
- const json_t *description_i18n,
- const char *unit,
- const struct TALER_Amount *unit_prices,
- size_t unit_price_len,
- const char *image,
- const json_t *taxes,
- int64_t total_stock,
- uint32_t total_stock_frac,
- bool unit_allow_fraction,
- const uint32_t *unit_precision_level,
- const json_t *address,
- struct GNUNET_TIME_Timestamp next_restock,
- uint32_t minimum_age,
- unsigned int num_cats,
- const uint64_t *cats,
- TALER_MERCHANT_ProductsPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_ProductsPostHandle *pph;
- json_t *req_obj;
- json_t *categories;
- char unit_total_stock_buf[64];
-
- TALER_MERCHANT_format_stock_string (total_stock,
- total_stock_frac,
- unit_total_stock_buf,
- sizeof (unit_total_stock_buf));
-
- if (0 == num_cats)
- {
- categories = NULL;
- }
- else
- {
- categories = json_array ();
- GNUNET_assert (NULL != categories);
- for (unsigned int i = 0; i<num_cats; i++)
- GNUNET_assert (0 ==
- json_array_append_new (categories,
- json_integer (cats[i])));
- }
- {
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("product_id",
- product_id),
- /* FIXME: once we move to the new-style API,
- allow applications to set the product name properly! */
- GNUNET_JSON_pack_string ("product_name",
- description),
- GNUNET_JSON_pack_string ("description",
- description),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("description_i18n",
- (json_t *) description_i18n)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_steal ("categories",
- categories)),
- GNUNET_JSON_pack_string ("unit",
- unit),
- TALER_JSON_pack_amount_array ("unit_price",
- unit_price_len,
- unit_prices),
- GNUNET_JSON_pack_string ("image",
- image),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_incref ("taxes",
- (json_t *) taxes)),
- GNUNET_JSON_pack_string ("unit_total_stock",
- unit_total_stock_buf),
- GNUNET_JSON_pack_bool ("unit_allow_fraction",
- unit_allow_fraction),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_uint64 ("minimum_age",
- minimum_age)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("address",
- (json_t *) address)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_timestamp ("next_restock",
- next_restock)));
- }
- if (NULL != unit_precision_level)
- {
- GNUNET_assert (0 ==
- json_object_set_new (req_obj,
- "unit_precision_level",
- json_integer (
- *unit_precision_level)));
- }
- if (! unit_allow_fraction)
- {
- GNUNET_assert (0 ==
- json_object_del (req_obj,
- "unit_allow_fraction"));
- if (NULL != unit_precision_level)
- GNUNET_assert (0 ==
- json_object_del (req_obj,
- "unit_precision_level"));
- }
- pph = GNUNET_new (struct TALER_MERCHANT_ProductsPostHandle);
- pph->ctx = ctx;
- pph->cb = cb;
- pph->cb_cls = cb_cls;
- pph->url = TALER_url_join (backend_url,
- "private/products",
- NULL);
- if (NULL == pph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (pph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (pph->url);
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&pph->post_ctx,
- eh,
- req_obj));
- json_decref (req_obj);
- pph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- pph->post_ctx.headers,
- &handle_post_products_finished,
- pph);
- GNUNET_assert (NULL != pph->job);
- }
- return pph;
-}
-
-
-struct TALER_MERCHANT_ProductsPostHandle *
-TALER_MERCHANT_products_post3 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- const char *description,
- const json_t *description_i18n,
- const char *unit,
- const struct TALER_Amount *price,
- const char *image,
- const json_t *taxes,
- int64_t total_stock,
- const json_t *address,
- struct GNUNET_TIME_Timestamp next_restock,
- uint32_t minimum_age,
- unsigned int num_cats,
- const uint64_t *cats,
- TALER_MERCHANT_ProductsPostCallback cb,
- void *cb_cls)
-{
- return TALER_MERCHANT_products_post4 (ctx,
- backend_url,
- product_id,
- description,
- description_i18n,
- unit,
- price,
- 1,
- image,
- taxes,
- total_stock,
- 0,
- false,
- NULL,
- address,
- next_restock,
- minimum_age,
- num_cats,
- cats,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_ProductsPostHandle *
-TALER_MERCHANT_products_post2 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- const char *description,
- const json_t *description_i18n,
- const char *unit,
- const struct TALER_Amount *price,
- const char *image,
- const json_t *taxes,
- int64_t total_stock,
- const json_t *address,
- struct GNUNET_TIME_Timestamp next_restock,
- uint32_t minimum_age,
- TALER_MERCHANT_ProductsPostCallback cb,
- void *cb_cls)
-{
- return TALER_MERCHANT_products_post3 (ctx,
- backend_url,
- product_id,
- description,
- description_i18n,
- unit,
- price,
- image,
- taxes,
- total_stock,
- address,
- next_restock,
- minimum_age,
- 0,
- NULL,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_ProductsPostHandle *
-TALER_MERCHANT_products_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *product_id,
- const char *description,
- const json_t *description_i18n,
- const char *unit,
- const struct TALER_Amount *price,
- const char *image,
- const json_t *taxes,
- int64_t total_stock,
- const json_t *address,
- struct GNUNET_TIME_Timestamp next_restock,
- TALER_MERCHANT_ProductsPostCallback cb,
- void *cb_cls)
-{
- return TALER_MERCHANT_products_post2 (ctx,
- backend_url,
- product_id,
- description,
- description_i18n,
- unit,
- price,
- image,
- taxes,
- total_stock,
- address,
- next_restock,
- 0,
- cb,
- cb_cls);
-}
-
-
-void
-TALER_MERCHANT_products_post_cancel (
- struct TALER_MERCHANT_ProductsPostHandle *pph)
-{
- if (NULL != pph->job)
- {
- GNUNET_CURL_job_cancel (pph->job);
- pph->job = NULL;
- }
- TALER_curl_easy_post_finished (&pph->post_ctx);
- GNUNET_free (pph->url);
- GNUNET_free (pph);
-}
-
-
-/* end of merchant_api_post_products.c */
diff --git a/src/lib/merchant_api_post_templates.c b/src/lib/merchant_api_post_templates.c
@@ -1,495 +0,0 @@
-/*
- 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_post_templates.c
- * @brief Implementation of the POST /templates request
- * of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-#include "taler/taler_merchant_util.h"
-
-/* FIXME: Bohdan is to stupid to figure out how util can be used here */
-static enum TALER_MERCHANT_TemplateType
-template_type_from_string (const char *template_type)
-{
- if (NULL == template_type)
- return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
- if (0 == strcmp (template_type,
- "fixed-order"))
- return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
- if (0 == strcmp (template_type,
- "inventory-cart"))
- return TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART;
- if (0 == strcmp (template_type,
- "paivana"))
- return TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA;
- return TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
-}
-
-
-/**
- * Validate a fixed-order template contract.
- *
- * @param template_contract JSON template contract
- * @param summary summary pointer
- * @param amount amount storage
- * @param minimum_age minimum age storage
- * @param pay_duration pay duration storage
- * @return true if valid
- */
-static bool
-validate_template_contract_fixed (const json_t *template_contract,
- const char **summary,
- struct TALER_Amount *amount,
- uint32_t *minimum_age,
- struct GNUNET_TIME_Relative *pay_duration)
-{
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- summary),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("amount",
- amount),
- NULL),
- GNUNET_JSON_spec_uint32 ("minimum_age",
- minimum_age),
- GNUNET_JSON_spec_relative_time ("pay_duration",
- pay_duration),
- GNUNET_JSON_spec_end ()
- };
- const char *ename;
- unsigned int eline;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (template_contract,
- spec,
- &ename,
- &eline))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid template_contract for field %s\n",
- ename);
- return false;
- }
- return true;
-}
-
-
-/**
- * Validate an inventory-cart template contract.
- *
- * @param template_contract JSON template contract
- * @param summary summary pointer
- * @param pay_duration pay duration storage
- * @return true if valid
- */
-static bool
-validate_template_contract_inventory (const json_t *template_contract,
- const char **summary,
- struct GNUNET_TIME_Relative *pay_duration)
-{
- bool selected_all = false;
- bool choose_one = false;
- bool request_tip = false;
- const json_t *selected_categories = NULL;
- const json_t *selected_products = NULL;
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- summary),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("request_tip",
- &request_tip),
- NULL),
- GNUNET_JSON_spec_relative_time ("pay_duration",
- pay_duration),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("selected_all",
- &selected_all),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("selected_categories",
- &selected_categories),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("selected_products",
- &selected_products),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("choose_one",
- &choose_one),
- NULL),
- GNUNET_JSON_spec_end ()
- };
- const char *ename;
- unsigned int eline;
-
- (void) request_tip;
- (void) selected_all;
- (void) choose_one;
- (void) selected_categories;
- (void) selected_products;
- if (GNUNET_OK !=
- GNUNET_JSON_parse (template_contract,
- ispec,
- &ename,
- &eline))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid template_contract for field %s\n",
- ename);
- return false;
- }
- return true;
-}
-
-
-/**
- * Validate a paivana template contract.
- *
- * @param template_contract JSON template contract
- * @param summary summary pointer
- * @param amount amount storage
- * @param minimum_age minimum age storage
- * @param pay_duration pay duration storage
- * @return true if valid
- */
-static bool
-validate_template_contract_paivana (const json_t *template_contract,
- const char **summary,
- struct TALER_Amount *amount,
- uint32_t *minimum_age,
- struct GNUNET_TIME_Relative *pay_duration)
-{
- /* TODO: PAIVANA validate paivana-specific fields beyond presence. */
- const char *paivana_id;
- struct GNUNET_JSON_Specification pspec[] = {
- GNUNET_JSON_spec_string ("paivana_id",
- &paivana_id),
- GNUNET_JSON_spec_end ()
- };
- const char *ename;
- unsigned int eline;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (template_contract,
- pspec,
- &ename,
- &eline))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid paivana template_contract for field %s\n",
- ename);
- return false;
- }
-
- if (! validate_template_contract_fixed (template_contract,
- summary,
- amount,
- minimum_age,
- pay_duration))
- return false;
-
- (void) paivana_id;
- return true;
-}
-
-
-/**
- * Handle for a POST /templates/$ID operation.
- */
-struct TALER_MERCHANT_TemplatesPostHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_TemplatesPostCallback 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;
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /templates request.
- *
- * @param cls the `struct TALER_MERCHANT_TemplatesPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_templates_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_TemplatesPostHandle *tph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- tph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /templates completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- tph->cb (tph->cb_cls,
- &hr);
- TALER_MERCHANT_templates_post_cancel (tph);
-}
-
-
-static bool
-test_template_contract_valid (const json_t *template_contract)
-{
- const char *template_type = NULL;
- enum TALER_MERCHANT_TemplateType template_type_enum;
- struct GNUNET_JSON_Specification type_spec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("template_type",
- &template_type),
- NULL),
- GNUNET_JSON_spec_end ()
- };
- const char *summary;
- struct TALER_Amount amount = { .value = 0};
- uint32_t minimum_age = 0;
- struct GNUNET_TIME_Relative pay_duration = { 0 };
- const char *ename;
- unsigned int eline;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (template_contract,
- type_spec,
- &ename,
- &eline))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid template_contract for field %s\n",
- ename);
- return false;
- }
-
- template_type_enum = template_type_from_string (template_type);
- if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == template_type_enum)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid template_type '%s'\n",
- template_type);
- return false;
- }
-
- /* FIXME: Bohdan understands that links have to be changed, but worried, that can crash something */
- switch (template_type_enum)
- {
- case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- return validate_template_contract_inventory (template_contract,
- &summary,
- &pay_duration);
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- return validate_template_contract_fixed (template_contract,
- &summary,
- &amount,
- &minimum_age,
- &pay_duration);
- case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- return validate_template_contract_paivana (template_contract,
- &summary,
- &amount,
- &minimum_age,
- &pay_duration);
- case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- break;
- }
- return false;
-}
-
-
-struct TALER_MERCHANT_TemplatesPostHandle *
-TALER_MERCHANT_templates_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *template_id,
- const char *template_description,
- const char *otp_id,
- const json_t *template_contract,
- TALER_MERCHANT_TemplatesPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_TemplatesPostHandle *tph;
- json_t *req_obj;
-
- if (! test_template_contract_valid (template_contract))
- {
- GNUNET_break (0);
- return NULL;
- }
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("template_id",
- template_id),
- GNUNET_JSON_pack_string ("template_description",
- template_description),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("otp_id",
- otp_id)),
- GNUNET_JSON_pack_object_incref ("template_contract",
- (json_t *) template_contract));
- tph = GNUNET_new (struct TALER_MERCHANT_TemplatesPostHandle);
- tph->ctx = ctx;
- tph->cb = cb;
- tph->cb_cls = cb_cls;
- tph->url = TALER_url_join (backend_url,
- "private/templates",
- NULL);
- if (NULL == tph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (tph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&tph->post_ctx,
- eh,
- req_obj));
- json_decref (req_obj);
- tph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- tph->post_ctx.headers,
- &handle_post_templates_finished,
- tph);
- GNUNET_assert (NULL != tph->job);
- }
- return tph;
-}
-
-
-void
-TALER_MERCHANT_templates_post_cancel (
- struct TALER_MERCHANT_TemplatesPostHandle *tph)
-{
- if (NULL != tph->job)
- {
- GNUNET_CURL_job_cancel (tph->job);
- tph->job = NULL;
- }
- TALER_curl_easy_post_finished (&tph->post_ctx);
- GNUNET_free (tph->url);
- GNUNET_free (tph);
-}
-
-
-/* end of merchant_api_post_templates.c */
diff --git a/src/lib/merchant_api_post_tokenfamilies.c b/src/lib/merchant_api_post_tokenfamilies.c
@@ -1,257 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2020-2024 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_post_tokenfamilies.c
- * @brief Implementation of the POST /tokenfamilies request
- * of the merchant's HTTP API
- * @author Christian Blättler
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_time_lib.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /tokenfamilies operation.
- */
-struct TALER_MERCHANT_TokenFamiliesPostHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_TokenFamiliesPostCallback 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;
-
-};
-
-/**
- * Function called when we're done processing the
- * HTTP POST /tokenfamilies request.
- *
- * @param cls the `struct TALER_MERCHANT_TokenFamiliesPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_token_families_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_TokenFamiliesPostHandle *handle = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- handle->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /tokenfamilies completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- handle->cb (handle->cb_cls,
- &hr);
- TALER_MERCHANT_token_families_post_cancel (handle);
-}
-
-
-struct TALER_MERCHANT_TokenFamiliesPostHandle *
-TALER_MERCHANT_token_families_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *slug,
- const char *name,
- const char *description,
- const json_t *description_i18n,
- const json_t *extra_data,
- struct GNUNET_TIME_Timestamp valid_after,
- struct GNUNET_TIME_Timestamp valid_before,
- struct GNUNET_TIME_Relative duration,
- struct GNUNET_TIME_Relative validity_granularity,
- struct GNUNET_TIME_Relative start_offset,
- const char *kind,
- TALER_MERCHANT_TokenFamiliesPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_TokenFamiliesPostHandle *handle;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("slug",
- slug),
- GNUNET_JSON_pack_string ("name",
- name),
- GNUNET_JSON_pack_string ("description",
- description),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("description_i18n",
- (json_t *) description_i18n)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("extra_data",
- (json_t *) extra_data)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_timestamp ("valid_after",
- valid_after)),
- GNUNET_JSON_pack_timestamp ("valid_before",
- valid_before),
- GNUNET_JSON_pack_time_rel ("duration",
- duration),
- GNUNET_JSON_pack_time_rel ("validity_granularity",
- validity_granularity),
- GNUNET_JSON_pack_time_rel ("start_offset",
- start_offset),
- GNUNET_JSON_pack_string ("kind",
- kind));
- handle = GNUNET_new (struct TALER_MERCHANT_TokenFamiliesPostHandle);
- handle->ctx = ctx;
- handle->cb = cb;
- handle->cb_cls = cb_cls;
- handle->url = TALER_url_join (backend_url,
- "private/tokenfamilies",
- NULL);
- if (NULL == handle->url)
- {
-
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (handle);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (handle->url);
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&handle->post_ctx,
- eh,
- req_obj));
- json_decref (req_obj);
- handle->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- handle->post_ctx.headers,
- &handle_post_token_families_finished,
- handle);
- GNUNET_assert (NULL != handle->job);
- }
- return handle;
-}
-
-
-void
-TALER_MERCHANT_token_families_post_cancel (
- struct TALER_MERCHANT_TokenFamiliesPostHandle *pph)
-{
- if (NULL != pph->job)
- {
- GNUNET_CURL_job_cancel (pph->job);
- pph->job = NULL;
- }
- TALER_curl_easy_post_finished (&pph->post_ctx);
- GNUNET_free (pph->url);
- GNUNET_free (pph);
-}
diff --git a/src/lib/merchant_api_post_transfers.c b/src/lib/merchant_api_post_transfers.c
@@ -1,256 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_transfers.c
- * @brief Implementation of the POST /transfers request of the merchant's HTTP API
- * @author Marcello Stanisci
- * @author Christian Grothoff
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_curl_lib.h>
-#include <taler/taler_json_lib.h>
-
-
-/**
- * @brief A handle for POSTing transfer data.
- */
-struct TALER_MERCHANT_PostTransfersHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_PostTransfersCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /transfers request.
- *
- * @param cls the `struct TALER_MERCHANT_PostTransfersHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_transfers_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_PostTransfersHandle *pth = cls;
- struct TALER_MERCHANT_PostTransfersResponse ptr = {
- .hr.reply = response,
- .hr.http_status = (unsigned int) response_code
- };
-
- pth->job = NULL;
- switch (response_code)
- {
- case 0:
- ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_UNAUTHORIZED:
- ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
- ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_NOT_FOUND:
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the application */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Did not find any data\n");
- ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
- ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- /* Server had an internal issue; we should retry, but this API
- leaves this to the application */
- ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
- ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
- break;
- case MHD_HTTP_BAD_GATEWAY:
- /* Exchange had an issue; we should retry, but this API
- leaves this to the application */
- ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
- ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
- {
- uint32_t ehc;
- struct GNUNET_JSON_Specification ispec[] = {
- TALER_JSON_spec_ec ("exchange_code",
- &ptr.details.bad_gateway.exchange_ec),
- GNUNET_JSON_spec_uint32 ("exchange_http_status",
- &ehc),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (ptr.hr.reply,
- ispec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ptr.details.bad_gateway.exchange_http_status = 0;
- ptr.details.bad_gateway.exchange_ec = TALER_EC_NONE;
- break;
- }
- else
- {
- ptr.details.bad_gateway.exchange_http_status
- = (unsigned int) ehc;
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Exchange returned %u/%u\n",
- (unsigned int) ptr.details.bad_gateway.exchange_ec,
- (unsigned int) ehc);
- }
- }
- break;
- case MHD_HTTP_GATEWAY_TIMEOUT:
- /* Server had an internal issue; we should retry, but this API
- leaves this to the application */
- ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
- ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
- break;
- default:
- /* unexpected response code */
- GNUNET_break_op (0);
- TALER_MERCHANT_parse_error_details_ (ptr.hr.reply,
- response_code,
- &ptr.hr);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) ptr.hr.http_status,
- (int) ptr.hr.ec);
- break;
- }
- pth->cb (pth->cb_cls,
- &ptr);
- TALER_MERCHANT_transfers_post_cancel (pth);
-}
-
-
-struct TALER_MERCHANT_PostTransfersHandle *
-TALER_MERCHANT_transfers_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const struct TALER_Amount *credit_amount,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- struct TALER_FullPayto payto_uri,
- const char *exchange_url,
- TALER_MERCHANT_PostTransfersCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_PostTransfersHandle *pth;
- CURL *eh;
- json_t *req;
-
- pth = GNUNET_new (struct TALER_MERCHANT_PostTransfersHandle);
- pth->ctx = ctx;
- pth->cb = cb;
- pth->cb_cls = cb_cls;
- pth->url = TALER_url_join (backend_url,
- "private/transfers",
- NULL);
- if (NULL == pth->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (pth);
- return NULL;
- }
- req = GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("credit_amount",
- credit_amount),
- GNUNET_JSON_pack_data_auto ("wtid",
- wtid),
- TALER_JSON_pack_full_payto ("payto_uri",
- payto_uri),
- GNUNET_JSON_pack_string ("exchange_url",
- exchange_url));
- eh = TALER_MERCHANT_curl_easy_get_ (pth->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&pth->post_ctx,
- eh,
- req))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req);
- GNUNET_free (pth->url);
- GNUNET_free (pth);
- return NULL;
- }
- json_decref (req);
- pth->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- pth->post_ctx.headers,
- &handle_post_transfers_finished,
- pth);
- return pth;
-}
-
-
-void
-TALER_MERCHANT_transfers_post_cancel (
- struct TALER_MERCHANT_PostTransfersHandle *pth)
-{
- if (NULL != pth->job)
- {
- GNUNET_CURL_job_cancel (pth->job);
- pth->job = NULL;
- }
- GNUNET_free (pth->url);
- TALER_curl_easy_post_finished (&pth->post_ctx);
- GNUNET_free (pth);
-}
-
-
-/* end of merchant_api_post_transfers.c */
diff --git a/src/lib/merchant_api_post_units.c b/src/lib/merchant_api_post_units.c
@@ -1,218 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025 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_post_units.c
- * @brief Implementation of POST /private/units
- * @author Bohdan Potuzhnyi
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /private/units operation.
- */
-struct TALER_MERCHANT_UnitsPostHandle
-{
- /**
- * Fully qualified request URL.
- */
- char *url;
-
- /**
- * CURL job handle.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Callback to invoke with the result.
- */
- TALER_MERCHANT_UnitsPostCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-
- /**
- * Helper keeping POST body and headers alive.
- */
- struct TALER_CURL_PostContext post_ctx;
-};
-
-
-/**
- * Called when the HTTP transfer finishes.
- *
- * @param cls operation handle
- * @param response_code HTTP status (0 on network / parsing failures)
- * @param response parsed JSON reply (NULL if unavailable)
- */
-static void
-handle_post_units_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_UnitsPostHandle *uph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- uph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /private/units completed with status %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- case MHD_HTTP_UNAUTHORIZED:
- case MHD_HTTP_FORBIDDEN:
- case MHD_HTTP_NOT_FOUND:
- case MHD_HTTP_CONFLICT:
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response %u/%d for POST /private/units\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- uph->cb (uph->cb_cls,
- &hr);
- TALER_MERCHANT_units_post_cancel (uph);
-}
-
-
-struct TALER_MERCHANT_UnitsPostHandle *
-TALER_MERCHANT_units_post (struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *unit_id,
- const char *unit_name_long,
- const char *unit_name_short,
- bool unit_allow_fraction,
- uint32_t unit_precision_level,
- bool unit_active,
- const json_t *unit_name_long_i18n,
- const json_t *unit_name_short_i18n,
- TALER_MERCHANT_UnitsPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_UnitsPostHandle *uph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("unit",
- unit_id),
- GNUNET_JSON_pack_string ("unit_name_long",
- unit_name_long),
- GNUNET_JSON_pack_string ("unit_name_short",
- unit_name_short),
- GNUNET_JSON_pack_bool ("unit_allow_fraction",
- unit_allow_fraction),
- GNUNET_JSON_pack_uint64 ("unit_precision_level",
- (uint64_t) unit_precision_level),
- GNUNET_JSON_pack_bool ("unit_active",
- unit_active),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("unit_name_long_i18n",
- (json_t *) unit_name_long_i18n)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("unit_name_short_i18n",
- (json_t *) unit_name_short_i18n)));
- uph = GNUNET_new (struct TALER_MERCHANT_UnitsPostHandle);
- uph->ctx = ctx;
- uph->cb = cb;
- uph->cb_cls = cb_cls;
- uph->url = TALER_url_join (backend_url,
- "private/units",
- NULL);
- if (NULL == uph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to build /private/units URL\n");
- json_decref (req_obj);
- GNUNET_free (uph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (uph->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&uph->post_ctx,
- eh,
- req_obj))
- {
- GNUNET_break (0);
- curl_easy_cleanup (eh);
- json_decref (req_obj);
- GNUNET_free (uph->url);
- GNUNET_free (uph);
- return NULL;
- }
- json_decref (req_obj);
- uph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- uph->post_ctx.headers,
- &handle_post_units_finished,
- uph);
- }
- return uph;
-}
-
-
-void
-TALER_MERCHANT_units_post_cancel (struct TALER_MERCHANT_UnitsPostHandle *uph)
-{
- if (NULL != uph->job)
- {
- GNUNET_CURL_job_cancel (uph->job);
- uph->job = NULL;
- }
- TALER_curl_easy_post_finished (&uph->post_ctx);
- GNUNET_free (uph->url);
- GNUNET_free (uph);
-}
-
-
-/* end of merchant_api_post_units.c */
diff --git a/src/lib/merchant_api_post_using_templates.c b/src/lib/merchant_api_post_using_templates.c
@@ -1,233 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2022-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 <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_post_using_templates.c
- * @brief Implementation of the POST /using_templates request
- * of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /templates/$ID operation.
- */
-struct TALER_MERCHANT_UsingTemplatesPostHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_PostOrdersCallback 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;
-};
-
-/**
- * Function called when we're done processing the
- * HTTP POST /using-templates request.
- *
- * @param cls the `struct TALER_MERCHANT_UsingTemplatesPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_using_templates_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_UsingTemplatesPostHandle *utph = cls;
- const json_t *json = response;
-
- utph->job = NULL;
- TALER_MERCHANT_handle_order_creation_response_ (utph->cb,
- utph->cb_cls,
- response_code,
- json);
- TALER_MERCHANT_using_templates_post_cancel (utph);
-}
-
-
-/**
- * Helper to submit a POST /templates/$ID request.
- *
- * @param ctx the context
- * @param backend_url HTTP base URL for the backend
- * @param template_id identifier of the template to use
- * @param req_obj JSON request body (consumed)
- * @param cb function to call with the backend's result
- * @param cb_cls closure for @a cb
- * @return the request handle; NULL upon error
- */
-static struct TALER_MERCHANT_UsingTemplatesPostHandle *
-using_templates_post_internal (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *template_id,
- json_t *req_obj,
- TALER_MERCHANT_PostOrdersCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_UsingTemplatesPostHandle *utph;
-
- utph = GNUNET_new (struct TALER_MERCHANT_UsingTemplatesPostHandle);
- utph->ctx = ctx;
- utph->cb = cb;
- utph->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "templates/%s",
- template_id);
- utph->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == utph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (utph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (utph->url);
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&utph->post_ctx,
- eh,
- req_obj));
- json_decref (req_obj);
- utph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- utph->post_ctx.headers,
- &handle_post_using_templates_finished,
- utph);
- GNUNET_assert (NULL != utph->job);
- }
- return utph;
-}
-
-
-struct TALER_MERCHANT_UsingTemplatesPostHandle *
-TALER_MERCHANT_using_templates_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *template_id,
- const char *summary,
- const struct TALER_Amount *amount,
- TALER_MERCHANT_PostOrdersCallback cb,
- void *cb_cls)
-{
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("template_type",
- "fixed-order"),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("summary",
- summary)),
- GNUNET_JSON_pack_allow_null (
- TALER_JSON_pack_amount ("amount",
- amount)));
- return using_templates_post_internal (ctx,
- backend_url,
- template_id,
- req_obj,
- cb,
- cb_cls);
-}
-
-
-struct TALER_MERCHANT_UsingTemplatesPostHandle *
-TALER_MERCHANT_using_templates_post2 (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *template_id,
- const json_t *details,
- TALER_MERCHANT_PostOrdersCallback cb,
- void *cb_cls)
-{
- if (NULL == details)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "No request body provided for using templates.\n");
- return NULL;
- }
- return using_templates_post_internal (ctx,
- backend_url,
- template_id,
- (json_t *) details,
- cb,
- cb_cls);
-}
-
-
-void
-TALER_MERCHANT_using_templates_post_cancel (
- struct TALER_MERCHANT_UsingTemplatesPostHandle *utph)
-{
- if (NULL != utph->job)
- {
- GNUNET_CURL_job_cancel (utph->job);
- utph->job = NULL;
- }
- TALER_curl_easy_post_finished (&utph->post_ctx);
- GNUNET_free (utph->url);
- GNUNET_free (utph);
-}
-
-
-/* end of merchant_api_post_using_templates.c */
diff --git a/src/lib/merchant_api_post_webhooks.c b/src/lib/merchant_api_post_webhooks.c
@@ -1,240 +0,0 @@
-/*
- 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_post_webhooks.c
- * @brief Implementation of the POST /webhooks request
- * of the merchant's HTTP API
- * @author Priscilla HUANG
- */
-#include "taler/platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include "taler/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_curl_lib.h>
-
-
-/**
- * Handle for a POST /webhooks/$ID operation.
- */
-struct TALER_MERCHANT_WebhooksPostHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_WebhooksPostCallback 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;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP POST /webhooks request.
- *
- * @param cls the `struct TALER_MERCHANT_WebhooksPostHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not in JSON
- */
-static void
-handle_post_webhooks_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_WebhooksPostHandle *wph = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
- };
-
- wph->job = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "POST /webhooks completed with response code %u\n",
- (unsigned int) response_code);
- switch (response_code)
- {
- case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_BAD_REQUEST:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* This should never happen, either us
- * or the merchant is buggy (or API version conflict);
- * just pass JSON reply to the application */
- break;
- case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we need to authenticate. */
- break;
- case MHD_HTTP_FORBIDDEN:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, merchant says we tried to abort the payment
- * after it was successful. We should pass the JSON reply to the
- * application */
- break;
- case MHD_HTTP_NOT_FOUND:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the
- application */
- break;
- case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- /* Server had an internal issue; we should retry,
- but this API leaves this to the application */
- break;
- default:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u/%d\n",
- (unsigned int) response_code,
- (int) hr.ec);
- GNUNET_break_op (0);
- break;
- }
- wph->cb (wph->cb_cls,
- &hr);
- TALER_MERCHANT_webhooks_post_cancel (wph);
-}
-
-
-struct TALER_MERCHANT_WebhooksPostHandle *
-TALER_MERCHANT_webhooks_post (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *webhook_id,
- const char *event_type,
- const char *url,
- const char *http_method,
- const char *header_template,
- const char *body_template,
- TALER_MERCHANT_WebhooksPostCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_WebhooksPostHandle *wph;
- json_t *req_obj;
-
- req_obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("webhook_id",
- webhook_id),
- GNUNET_JSON_pack_string ("event_type",
- event_type),
- GNUNET_JSON_pack_string ("url",
- url),
- GNUNET_JSON_pack_string ("http_method",
- http_method),
- GNUNET_JSON_pack_string ("header_template",
- header_template),
- GNUNET_JSON_pack_string ("body_template",
- body_template));
- wph = GNUNET_new (struct TALER_MERCHANT_WebhooksPostHandle);
- wph->ctx = ctx;
- wph->cb = cb;
- wph->cb_cls = cb_cls;
- wph->url = TALER_url_join (backend_url,
- "private/webhooks",
- NULL);
- if (NULL == wph->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- json_decref (req_obj);
- GNUNET_free (wph);
- return NULL;
- }
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (wph->url);
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&wph->post_ctx,
- eh,
- req_obj));
- json_decref (req_obj);
- wph->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- wph->post_ctx.headers,
- &handle_post_webhooks_finished,
- wph);
- GNUNET_assert (NULL != wph->job);
- }
- return wph;
-}
-
-
-void
-TALER_MERCHANT_webhooks_post_cancel (
- struct TALER_MERCHANT_WebhooksPostHandle *wph)
-{
- if (NULL != wph->job)
- {
- GNUNET_CURL_job_cancel (wph->job);
- wph->job = NULL;
- }
- TALER_curl_easy_post_finished (&wph->post_ctx);
- GNUNET_free (wph->url);
- GNUNET_free (wph);
-}
-
-
-/* end of merchant_api_post_webhooks.c */
diff --git a/src/lib/merchant_api_wallet_get_order.c b/src/lib/merchant_api_wallet_get_order.c
@@ -1,306 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2018, 2020, 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_order.c
- * @brief Implementation of the GET /orders/$ID request
- * @author Christian Grothoff
- * @author Marcello Stanisci
- * @author Florian Dold
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_curl_defaults.h"
-#include "merchant_api_common.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-
-
-/**
- * @brief A GET /orders/$ID handle
- */
-struct TALER_MERCHANT_OrderWalletGetHandle
-{
-
- /**
- * The url for this request.
- */
- char *url;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * Function to call with the result.
- */
- TALER_MERCHANT_OrderWalletGetCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
- /**
- * Reference to the execution context.
- */
- struct GNUNET_CURL_Context *ctx;
-};
-
-
-/**
- * Convenience function to call the callback in @a owgh with an error code of
- * @a ec and the exchange body being set to @a reply.
- *
- * @param owgh handle providing callback
- * @param ec error code to return to application
- * @param reply JSON reply we got from the exchange, can be NULL
- */
-static void
-cb_failure (struct TALER_MERCHANT_OrderWalletGetHandle *owgh,
- enum TALER_ErrorCode ec,
- const json_t *reply)
-{
- struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
- .hr.ec = ec,
- .hr.reply = reply
- };
-
- owgh->cb (owgh->cb_cls,
- &owgr);
-}
-
-
-/**
- * Function called when we're done processing the GET /check-payment request.
- *
- * @param cls the `struct TALER_MERCHANT_OrderWalletGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, should be NULL
- */
-static void
-handle_wallet_get_order_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_OrderWalletGetHandle *owgh = cls;
- const json_t *json = response;
-
- owgh->job = NULL;
- switch (response_code)
- {
- case MHD_HTTP_OK:
- {
- struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
- .hr.reply = json,
- .hr.http_status = MHD_HTTP_OK
- };
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_bool ("refunded",
- &owgr.details.ok.refunded),
- GNUNET_JSON_spec_bool ("refund_pending",
- &owgr.details.ok.refund_pending),
- TALER_JSON_spec_amount_any ("refund_amount",
- &owgr.details.ok.refund_amount),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- cb_failure (owgh,
- TALER_EC_GENERIC_REPLY_MALFORMED,
- json);
- TALER_MERCHANT_wallet_order_get_cancel (owgh);
- return;
- }
- owgh->cb (owgh->cb_cls,
- &owgr);
- GNUNET_JSON_parse_free (spec);
- break;
- }
- case MHD_HTTP_PAYMENT_REQUIRED:
- {
- struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
- .hr.reply = json,
- .hr.http_status = MHD_HTTP_PAYMENT_REQUIRED
- };
-
- /* Status is: unpaid */
- owgr.details.payment_required.taler_pay_uri
- = json_string_value (json_object_get (json,
- "taler_pay_uri"));
- owgr.details.payment_required.already_paid_order_id
- = json_string_value (json_object_get (json,
- "already_paid_order_id"));
- if (NULL == owgr.details.payment_required.taler_pay_uri)
- {
- GNUNET_break_op (0);
- cb_failure (owgh,
- TALER_EC_GENERIC_REPLY_MALFORMED,
- json);
- break;
- }
- owgh->cb (owgh->cb_cls,
- &owgr);
- break;
- }
- default:
- {
- struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
- .hr.reply = json,
- .hr.http_status = response_code
- };
-
- TALER_MERCHANT_parse_error_details_ (response,
- response_code,
- &owgr.hr);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Checking order status failed with HTTP status code %u/%d\n",
- (unsigned int) response_code,
- (int) owgr.hr.ec);
- GNUNET_break_op (0);
- owgh->cb (owgh->cb_cls,
- &owgr);
- break;
- }
- }
- TALER_MERCHANT_wallet_order_get_cancel (owgh);
-}
-
-
-struct TALER_MERCHANT_OrderWalletGetHandle *
-TALER_MERCHANT_wallet_order_get (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *order_id,
- const struct TALER_PrivateContractHashP *h_contract,
- struct GNUNET_TIME_Relative timeout,
- const char *session_id,
- const struct TALER_Amount *min_refund,
- bool await_refund_obtained,
- TALER_MERCHANT_OrderWalletGetCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_OrderWalletGetHandle *owgh;
- unsigned int tms;
-
- GNUNET_assert (NULL != backend_url);
- GNUNET_assert (NULL != order_id);
- owgh = GNUNET_new (struct TALER_MERCHANT_OrderWalletGetHandle);
- owgh->ctx = ctx;
- owgh->cb = cb;
- owgh->cb_cls = cb_cls;
- tms = (unsigned int) (timeout.rel_value_us
- / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
- {
- char timeout_ms[32];
- struct GNUNET_CRYPTO_HashAsciiEncoded h_contract_s;
- char *path;
-
- GNUNET_CRYPTO_hash_to_enc (&h_contract->hash,
- &h_contract_s);
- GNUNET_snprintf (timeout_ms,
- sizeof (timeout_ms),
- "%u",
- tms);
- GNUNET_asprintf (&path,
- "orders/%s",
- order_id);
- owgh->url = TALER_url_join (backend_url,
- path,
- "h_contract",
- h_contract_s.encoding,
- "session_id",
- session_id,
- "timeout_ms",
- (0 != tms)
- ? timeout_ms
- : NULL,
- "refund",
- (NULL != min_refund)
- ? TALER_amount2s (min_refund)
- : NULL,
- "await_refund_obtained",
- await_refund_obtained
- ? "yes"
- : NULL,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == owgh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (owgh);
- return NULL;
- }
-
- {
- CURL *eh;
-
- eh = TALER_MERCHANT_curl_easy_get_ (owgh->url);
- if (0 != tms)
- {
- GNUNET_break (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_TIMEOUT_MS,
- (long) (tms + 100L)));
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Checking order status at %s\n",
- owgh->url);
- if (NULL == (owgh->job =
- GNUNET_CURL_job_add (ctx,
- eh,
- &handle_wallet_get_order_finished,
- owgh)))
- {
- GNUNET_break (0);
- GNUNET_free (owgh->url);
- GNUNET_free (owgh);
- return NULL;
- }
- }
- return owgh;
-}
-
-
-void
-TALER_MERCHANT_wallet_order_get_cancel (
- struct TALER_MERCHANT_OrderWalletGetHandle *owgh)
-{
- if (NULL != owgh->job)
- {
- GNUNET_CURL_job_cancel (owgh->job);
- owgh->job = NULL;
- }
- GNUNET_free (owgh->url);
- GNUNET_free (owgh);
-}
-
-
-/* end of merchant_api_wallet_get_order.c */
diff --git a/src/lib/merchant_api_wallet_get_template.c b/src/lib/merchant_api_wallet_get_template.c
@@ -1,195 +0,0 @@
-/*
- 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 "taler/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/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/lib/merchant_api_wallet_post_order_refund.c b/src/lib/merchant_api_wallet_post_order_refund.c
@@ -1,345 +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
- <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant_api_wallet_post_order_refund.c
- * @brief Implementation of the (public) POST /orders/ID/refund request
- * @author Jonathan Buchanan
- */
-#include "taler/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/taler_merchant_service.h"
-#include "merchant_api_common.h"
-#include "merchant_api_curl_defaults.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_curl_lib.h>
-
-/**
- * Maximum number of refunds we return.
- */
-#define MAX_REFUNDS 1024
-
-/**
- * Handle for a (public) POST /orders/ID/refund operation.
- */
-struct TALER_MERCHANT_WalletOrderRefundHandle
-{
- /**
- * Complete URL where the backend offers /refund
- */
- char *url;
-
- /**
- * Minor context that holds body and headers.
- */
- struct TALER_CURL_PostContext post_ctx;
-
- /**
- * The CURL context to connect to the backend
- */
- struct GNUNET_CURL_Context *ctx;
-
- /**
- * The callback to pass the backend response to
- */
- TALER_MERCHANT_WalletRefundCallback cb;
-
- /**
- * Clasure to pass to the callback
- */
- void *cb_cls;
-
- /**
- * Handle for the request
- */
- struct GNUNET_CURL_Job *job;
-};
-
-
-/**
- * Callback to process (public) POST /orders/ID/refund response
- *
- * @param cls the `struct TALER_MERCHANT_OrderRefundHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response response body, NULL if not JSON
- */
-static void
-handle_refund_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_MERCHANT_WalletOrderRefundHandle *orh = cls;
- const json_t *json = response;
- struct TALER_MERCHANT_WalletRefundResponse wrr = {
- .hr.http_status = (unsigned int) response_code,
- .hr.reply = json
- };
-
- orh->job = NULL;
- switch (response_code)
- {
- case 0:
- wrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- {
- const json_t *refunds;
- unsigned int refund_len;
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_amount_any (
- "refund_amount",
- &wrr.details.ok.refund_amount),
- GNUNET_JSON_spec_array_const (
- "refunds",
- &refunds),
- GNUNET_JSON_spec_fixed_auto (
- "merchant_pub",
- &wrr.details.ok.merchant_pub),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- wrr.hr.http_status = 0;
- break;
- }
- refund_len = json_array_size (refunds);
- if ( (json_array_size (refunds) != (size_t) refund_len) ||
- (refund_len > MAX_REFUNDS) )
- {
- GNUNET_break (0);
- wrr.hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
- wrr.hr.http_status = 0;
- break;
- }
- {
- struct TALER_MERCHANT_RefundDetail rds[GNUNET_NZL (refund_len)];
-
- memset (rds,
- 0,
- sizeof (rds));
- for (unsigned int i = 0; i<refund_len; i++)
- {
- struct TALER_MERCHANT_RefundDetail *rd = &rds[i];
- const json_t *jrefund = json_array_get (refunds,
- i);
- const char *refund_status_type;
- uint32_t exchange_status;
- uint32_t eec = 0;
- struct GNUNET_JSON_Specification espec[] = {
- GNUNET_JSON_spec_string ("type",
- &refund_status_type),
- GNUNET_JSON_spec_uint32 ("exchange_status",
- &exchange_status),
- GNUNET_JSON_spec_uint64 ("rtransaction_id",
- &rd->rtransaction_id),
- GNUNET_JSON_spec_fixed_auto ("coin_pub",
- &rd->coin_pub),
- TALER_JSON_spec_amount_any ("refund_amount",
- &rd->refund_amount),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const ("exchange_reply",
- &rd->hr.reply),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_uint32 ("exchange_code",
- &eec),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (jrefund,
- espec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- wrr.hr.http_status = 0;
- goto finish;
- }
-
- rd->hr.http_status = exchange_status;
- rd->hr.ec = (enum TALER_ErrorCode) eec;
- switch (exchange_status)
- {
- case MHD_HTTP_OK:
- {
- struct GNUNET_JSON_Specification rspec[] = {
- GNUNET_JSON_spec_fixed_auto ("exchange_sig",
- &rd->details.ok.exchange_sig),
- GNUNET_JSON_spec_fixed_auto ("exchange_pub",
- &rd->details.ok.exchange_pub),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (jrefund,
- rspec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- wrr.hr.http_status = 0;
- goto finish;
- }
- /* check that type field is correct */
- if (0 != strcmp ("success",
- refund_status_type))
- {
- GNUNET_break_op (0);
- wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- wrr.hr.http_status = 0;
- goto finish;
- }
- }
- break; /* end MHD_HTTP_OK */
- default:
- if (0 != strcmp ("failure",
- refund_status_type))
- {
- GNUNET_break_op (0);
- wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- wrr.hr.http_status = 0;
- goto finish;
- }
- } /* switch on exchange status code */
- } /* for all refunds */
-
- wrr.details.ok.refunds = rds;
- wrr.details.ok.refunds_length = refund_len;
- orh->cb (orh->cb_cls,
- &wrr);
- TALER_MERCHANT_wallet_post_order_refund_cancel (orh);
- return;
- } /* end 'rds' scope */
- } /* case MHD_HTTP_OK */
- break;
- case MHD_HTTP_NO_CONTENT:
- break;
- case MHD_HTTP_CONFLICT:
- case MHD_HTTP_NOT_FOUND:
- wrr.hr.ec = TALER_JSON_get_error_code (json);
- wrr.hr.hint = TALER_JSON_get_error_hint (json);
- break;
- default:
- GNUNET_break_op (0); /* unexpected status code */
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &wrr.hr);
- break;
- }
-finish:
- orh->cb (orh->cb_cls,
- &wrr);
- TALER_MERCHANT_wallet_post_order_refund_cancel (orh);
-}
-
-
-struct TALER_MERCHANT_WalletOrderRefundHandle *
-TALER_MERCHANT_wallet_post_order_refund (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const char *order_id,
- const struct TALER_PrivateContractHashP *h_contract_terms,
- TALER_MERCHANT_WalletRefundCallback cb,
- void *cb_cls)
-{
- struct TALER_MERCHANT_WalletOrderRefundHandle *orh;
- json_t *req;
- CURL *eh;
-
- orh = GNUNET_new (struct TALER_MERCHANT_WalletOrderRefundHandle);
- orh->ctx = ctx;
- orh->cb = cb;
- orh->cb_cls = cb_cls;
- {
- char *path;
-
- GNUNET_asprintf (&path,
- "orders/%s/refund",
- order_id);
- orh->url = TALER_url_join (backend_url,
- path,
- NULL);
- GNUNET_free (path);
- }
- if (NULL == orh->url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not construct request URL.\n");
- GNUNET_free (orh);
- return NULL;
- }
- req = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("h_contract",
- h_contract_terms));
- eh = TALER_MERCHANT_curl_easy_get_ (orh->url);
- if (GNUNET_OK !=
- TALER_curl_easy_post (&orh->post_ctx,
- eh,
- req))
- {
- GNUNET_break (0);
- json_decref (req);
- curl_easy_cleanup (eh);
- GNUNET_free (orh->url);
- GNUNET_free (orh);
- return NULL;
- }
- json_decref (req);
- orh->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- orh->post_ctx.headers,
- &handle_refund_finished,
- orh);
- if (NULL == orh->job)
- {
- GNUNET_free (orh->url);
- GNUNET_free (orh);
- return NULL;
- }
- return orh;
-}
-
-
-void
-TALER_MERCHANT_wallet_post_order_refund_cancel (
- struct TALER_MERCHANT_WalletOrderRefundHandle *orh)
-{
- if (NULL != orh->job)
- {
- GNUNET_CURL_job_cancel (orh->job);
- orh->job = NULL;
- }
- TALER_curl_easy_post_finished (&orh->post_ctx);
- GNUNET_free (orh->url);
- GNUNET_free (orh);
-}
-
-
-/* end of merchant_api_wallet_post_order_refund.c */