commit 374146b643d5238ef1fdad0b5b1ceffc27d6906a
parent f9b298b57046bc065350d99efaafbbb222d3572e
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 20 Mar 2026 00:06:23 +0100
wip on major refactoring, donau support on pay still missing, plus needs more testing
Diffstat:
144 files changed, 22768 insertions(+), 2290 deletions(-)
diff --git a/src/include/taler/taler-merchant/common.h b/src/include/taler/taler-merchant/common.h
@@ -223,6 +223,45 @@ struct TALER_MERCHANT_UnitEntry
/**
+ * Wire transfer information associated with a paid order.
+ */
+struct TALER_MERCHANT_WireTransfer
+{
+
+ /**
+ * Base URL of the exchange that made the transfer.
+ */
+ const char *exchange_url;
+
+ /**
+ * When the transfer took place (note: may not be exact,
+ * as the time at which the exchange initiated the transfer
+ * may differ from the time the bank or the merchant observed).
+ * Zero if not known.
+ */
+ struct GNUNET_TIME_Timestamp execution_time;
+
+ /**
+ * Wire transfer subject.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Total amount that was transferred.
+ */
+ struct TALER_Amount total_amount;
+
+ /**
+ * Whether this transfer was confirmed by the merchant using
+ * the POST /transfers API, or whether it was merely claimed
+ * by the exchange.
+ */
+ bool confirmed;
+
+};
+
+
+/**
* Type of a statistics metric / aggregation mode.
*/
enum TALER_MERCHANT_StatisticsType
diff --git a/src/include/taler/taler-merchant/get-management-instances-INSTANCE-new.h b/src/include/taler/taler-merchant/get-management-instances-INSTANCE-new.h
@@ -127,7 +127,8 @@ struct TALER_MERCHANT_GetManagementInstanceResponse
*
* @param ctx the context
* @param url base URL of the merchant backend
- * @param instance_id identifier of the instance to retrieve
+ * @param instance_id identifier of the instance to retrieve,
+ * NULL for admin instance
* @return handle to operation
*/
struct TALER_MERCHANT_GetManagementInstanceHandle *
diff --git a/src/include/taler/taler-merchant/get-private-donau-new.h b/src/include/taler/taler-merchant/get-private-donau-new.h
@@ -23,6 +23,7 @@
#define _TALER_MERCHANT__GET_PRIVATE_DONAU_NEW_H
#include <taler/taler-merchant/common.h>
+#include <donau/donau_service.h>
/**
diff --git a/src/include/taler/taler-merchant/post-management-instances-INSTANCE-auth-new.h b/src/include/taler/taler-merchant/post-management-instances-INSTANCE-auth-new.h
@@ -61,7 +61,7 @@ TALER_MERCHANT_post_management_instances_auth_create (
struct GNUNET_CURL_Context *ctx,
const char *url,
const char *instance_id,
- const char *auth_password);
+ const char *auth_password); // FIXME: make password an option!
#ifndef TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_AUTH_RESULT_CLOSURE
diff --git a/src/include/taler/taler-merchant/post-orders-ORDER_ID-claim-new.h b/src/include/taler/taler-merchant/post-orders-ORDER_ID-claim-new.h
@@ -106,6 +106,11 @@ struct TALER_MERCHANT_PostOrdersClaimResponse
const json_t *contract_terms;
/**
+ * Hash of the contract terms.
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ /**
* Merchant signature over the contract terms.
*/
struct TALER_MerchantSignatureP merchant_sig;
diff --git a/src/include/taler/taler-merchant/post-orders-ORDER_ID-pay-new.h b/src/include/taler/taler-merchant/post-orders-ORDER_ID-pay-new.h
@@ -90,7 +90,7 @@ struct TALER_MERCHANT_PostOrdersPayPaidCoin
/**
* Base URL of the exchange this coin was issued by.
*/
- const char *exchange_url;
+ char *exchange_url;
};
@@ -122,9 +122,10 @@ struct TALER_MERCHANT_PostOrdersPayCoin
struct TALER_CoinSpendPrivateKeyP coin_priv;
/**
- * Hash of the age commitment for this coin, or NULL if none.
+ * Hash of the age commitment for this coin, or
+ * all zeros if none.
*/
- const struct TALER_AgeCommitmentHashP *h_age_commitment;
+ struct TALER_AgeCommitmentHashP h_age_commitment;
/**
* Total amount contributed including deposit fee.
@@ -139,7 +140,7 @@ struct TALER_MERCHANT_PostOrdersPayCoin
/**
* URL of the exchange that issued @e coin_priv.
*/
- const char *exchange_url;
+ char *exchange_url;
};
@@ -583,7 +584,7 @@ TALER_MERCHANT_post_orders_pay_create (
const char *url,
const char *order_id,
const struct TALER_PrivateContractHashP *h_contract_terms,
- int choice_index,
+ int choice_index, // fIXME: make this an option?
const struct TALER_Amount *amount,
const struct TALER_Amount *max_fee,
const struct TALER_MerchantPublicKeyP *merchant_pub,
diff --git a/src/include/taler/taler-merchant/post-orders-ORDER_ID-refund.h b/src/include/taler/taler-merchant/post-orders-ORDER_ID-refund.h
@@ -26,12 +26,6 @@
/**
- * Handle for a POST /orders/$ORDER_ID/refund request (wallet-facing).
- */
-struct TALER_MERCHANT_WalletOrderRefundHandle;
-
-
-/**
* Refund detail in a wallet refund response.
*/
struct TALER_MERCHANT_RefundDetail
@@ -85,6 +79,12 @@ struct TALER_MERCHANT_RefundDetail
/**
+ * Handle for a POST /orders/$ORDER_ID/refund request (wallet-facing).
+ */
+struct TALER_MERCHANT_WalletOrderRefundHandle;
+
+
+/**
* Response details for a POST /orders/$ORDER_ID/refund request (wallet-facing).
*/
struct TALER_MERCHANT_WalletRefundResponse
diff --git a/src/include/taler/taler-merchant/post-private-products-PRODUCT_ID-lock-new.h b/src/include/taler/taler-merchant/post-private-products-PRODUCT_ID-lock-new.h
@@ -128,18 +128,16 @@ struct TALER_MERCHANT_PostPrivateProductsLockResponse
}
/**
- * Set whether to use fractional quantity.
+ * Set to use fractional quantity.
*
- * @param b true to use fractional quantity
* @return representation of the option
*/
-#define TALER_MERCHANT_post_private_products_lock_option_use_fractional_quantity \
- (b) \
+#define TALER_MERCHANT_post_private_products_lock_option_use_frac_quantity() \
(const struct TALER_MERCHANT_PostPrivateProductsLockOptionValue) \
{ \
.option = \
TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_OPTION_USE_FRACTIONAL_QUANTITY, \
- .details.use_fractional_quantity = (b) \
+ .details.use_fractional_quantity = true \
}
diff --git a/src/include/taler/taler-merchant/post-private-transfers.h b/src/include/taler/taler-merchant/post-private-transfers.h
@@ -32,45 +32,6 @@ struct TALER_MERCHANT_PostTransfersHandle;
/**
- * Wire transfer information associated with a paid order.
- */
-struct TALER_MERCHANT_WireTransfer
-{
-
- /**
- * Base URL of the exchange that made the transfer.
- */
- const char *exchange_url;
-
- /**
- * When the transfer took place (note: may not be exact,
- * as the time at which the exchange initiated the transfer
- * may differ from the time the bank or the merchant observed).
- * Zero if not known.
- */
- struct GNUNET_TIME_Timestamp execution_time;
-
- /**
- * Wire transfer subject.
- */
- struct TALER_WireTransferIdentifierRawP wtid;
-
- /**
- * Total amount that was transferred.
- */
- struct TALER_Amount total_amount;
-
- /**
- * Whether this transfer was confirmed by the merchant using
- * the POST /transfers API, or whether it was merely claimed
- * by the exchange.
- */
- bool confirmed;
-
-};
-
-
-/**
* Response details for a POST /private/transfers request.
*/
struct TALER_MERCHANT_PostTransfersResponse
diff --git a/src/include/taler/taler_merchant_service.h b/src/include/taler/taler_merchant_service.h
@@ -33,69 +33,70 @@
#define TALER_MERCHANT_API_VERSION 0x000C0000
#include <taler/taler-merchant/common.h>
-#include <taler/taler-merchant/get-config.h>
-#include <taler/taler-merchant/get-management-instances.h>
-#include <taler/taler-merchant/get-management-instances-INSTANCE.h>
-#include <taler/taler-merchant/get-private-kyc.h>
-#include <taler/taler-merchant/get-private-statistics-amount-SLUG.h>
-#include <taler/taler-merchant/get-private-statistics-counter-SLUG.h>
-#include <taler/taler-merchant/delete-management-instances-INSTANCE.h>
-#include <taler/taler-merchant/patch-management-instances-INSTANCE.h>
-#include <taler/taler-merchant/post-management-instances.h>
-#include <taler/taler-merchant/post-management-instances-INSTANCE-auth.h>
-#include <taler/taler-merchant/post-private-token.h>
-#include <taler/taler-merchant/delete-private-tokens-SERIAL.h>
-#include <taler/taler-merchant/get-private-orders.h>
-#include <taler/taler-merchant/post-private-orders.h>
-#include <taler/taler-merchant/get-private-orders-ORDER_ID.h>
-#include <taler/taler-merchant/get-orders-ORDER_ID.h>
-#include <taler/taler-merchant/delete-private-orders-ORDER_ID.h>
-#include <taler/taler-merchant/post-orders-ORDER_ID-abort.h>
-#include <taler/taler-merchant/post-orders-ORDER_ID-claim.h>
-#include <taler/taler-merchant/post-orders-ORDER_ID-paid.h>
-#include <taler/taler-merchant/post-orders-ORDER_ID-pay.h>
-#include <taler/taler-merchant/post-private-orders-ORDER_ID-refund.h>
-#include <taler/taler-merchant/post-orders-ORDER_ID-refund.h>
-#include <taler/taler-merchant/patch-private-orders-ORDER_ID-forget.h>
-#include <taler/taler-merchant/get-private-products.h>
-#include <taler/taler-merchant/get-private-products-PRODUCT_ID.h>
-#include <taler/taler-merchant/post-private-products.h>
-#include <taler/taler-merchant/patch-private-products-PRODUCT_ID.h>
-#include <taler/taler-merchant/delete-private-products-PRODUCT_ID.h>
-#include <taler/taler-merchant/post-private-products-PRODUCT_ID-lock.h>
-#include <taler/taler-merchant/get-products-IMAGE_HASH-image.h>
-#include <taler/taler-merchant/get-private-transfers.h>
-#include <taler/taler-merchant/post-private-transfers.h>
-#include <taler/taler-merchant/delete-private-transfers-TID.h>
-#include <taler/taler-merchant/get-private-accounts.h>
-#include <taler/taler-merchant/get-private-accounts-H_WIRE.h>
-#include <taler/taler-merchant/post-private-accounts.h>
-#include <taler/taler-merchant/patch-private-accounts-H_WIRE.h>
-#include <taler/taler-merchant/delete-private-accounts-H_WIRE.h>
-#include <taler/taler-merchant/get-private-otp-devices.h>
-#include <taler/taler-merchant/get-private-otp-devices-DEVICE_ID.h>
-#include <taler/taler-merchant/post-private-otp-devices.h>
-#include <taler/taler-merchant/patch-private-otp-devices-DEVICE_ID.h>
-#include <taler/taler-merchant/delete-private-otp-devices-DEVICE_ID.h>
-#include <taler/taler-merchant/get-private-templates.h>
-#include <taler/taler-merchant/get-private-templates-TEMPLATE_ID.h>
-#include <taler/taler-merchant/post-private-templates.h>
-#include <taler/taler-merchant/patch-private-templates-TEMPLATE_ID.h>
-#include <taler/taler-merchant/delete-private-templates-TEMPLATE_ID.h>
-#include <taler/taler-merchant/post-templates-TEMPLATE_ID.h>
-#include <taler/taler-merchant/get-templates-TEMPLATE_ID.h>
-#include <taler/taler-merchant/get-private-tokenfamilies-TOKEN_FAMILY_SLUG.h>
-#include <taler/taler-merchant/post-private-tokenfamilies.h>
-#include <taler/taler-merchant/get-private-webhooks.h>
-#include <taler/taler-merchant/get-private-webhooks-WEBHOOK_ID.h>
-#include <taler/taler-merchant/post-private-webhooks.h>
-#include <taler/taler-merchant/patch-private-webhooks-WEBHOOK_ID.h>
-#include <taler/taler-merchant/delete-private-webhooks-WEBHOOK_ID.h>
-#include <taler/taler-merchant/get-private-units.h>
-#include <taler/taler-merchant/get-private-units-UNIT.h>
-#include <taler/taler-merchant/post-private-units.h>
-#include <taler/taler-merchant/patch-private-units-UNIT.h>
-#include <taler/taler-merchant/delete-private-units-UNIT.h>
-#include <taler/taler-merchant/post-private-categories.h>
+#include <taler/taler-merchant/get-config-new.h>
+#include <taler/taler-merchant/get-management-instances-new.h>
+#include <taler/taler-merchant/get-management-instances-INSTANCE-new.h>
+#include <taler/taler-merchant/get-private-kyc-new.h>
+#include <taler/taler-merchant/get-private-statistics-amount-SLUG-new.h>
+#include <taler/taler-merchant/get-private-statistics-counter-SLUG-new.h>
+#include <taler/taler-merchant/delete-management-instances-INSTANCE-new.h>
+#include <taler/taler-merchant/patch-management-instances-INSTANCE-new.h>
+#include <taler/taler-merchant/post-management-instances-new.h>
+#include <taler/taler-merchant/post-management-instances-INSTANCE-auth-new.h>
+#include <taler/taler-merchant/post-private-token-new.h>
+#include <taler/taler-merchant/delete-private-tokens-SERIAL-new.h>
+#include <taler/taler-merchant/get-private-orders-new.h>
+#include <taler/taler-merchant/post-private-orders-new.h>
+#include <taler/taler-merchant/get-private-orders-ORDER_ID-new.h>
+#include <taler/taler-merchant/get-orders-ORDER_ID-new.h>
+#include <taler/taler-merchant/delete-private-orders-ORDER_ID-new.h>
+#include <taler/taler-merchant/post-orders-ORDER_ID-abort-new.h>
+#include <taler/taler-merchant/post-orders-ORDER_ID-claim-new.h>
+#include <taler/taler-merchant/post-orders-ORDER_ID-paid-new.h>
+#include <taler/taler-merchant/post-orders-ORDER_ID-pay-new.h>
+#include <taler/taler-merchant/post-private-orders-ORDER_ID-refund-new.h>
+#include <taler/taler-merchant/post-orders-ORDER_ID-refund-new.h>
+#include <taler/taler-merchant/patch-private-orders-ORDER_ID-forget-new.h>
+#include <taler/taler-merchant/get-private-products-new.h>
+#include <taler/taler-merchant/get-private-products-PRODUCT_ID-new.h>
+#include <taler/taler-merchant/post-private-products-new.h>
+#include <taler/taler-merchant/patch-private-products-PRODUCT_ID-new.h>
+#include <taler/taler-merchant/delete-private-products-PRODUCT_ID-new.h>
+#include <taler/taler-merchant/post-private-products-PRODUCT_ID-lock-new.h>
+#include <taler/taler-merchant/get-products-IMAGE_HASH-image-new.h>
+#include <taler/taler-merchant/get-private-transfers-new.h>
+#include <taler/taler-merchant/post-private-transfers-new.h>
+#include <taler/taler-merchant/delete-private-transfers-TID-new.h>
+#include <taler/taler-merchant/get-private-accounts-new.h>
+#include <taler/taler-merchant/get-private-accounts-H_WIRE-new.h>
+#include <taler/taler-merchant/post-private-accounts-new.h>
+#include <taler/taler-merchant/patch-private-accounts-H_WIRE-new.h>
+#include <taler/taler-merchant/delete-private-accounts-H_WIRE-new.h>
+#include <taler/taler-merchant/get-private-otp-devices-new.h>
+#include <taler/taler-merchant/get-private-otp-devices-DEVICE_ID-new.h>
+#include <taler/taler-merchant/post-private-otp-devices-new.h>
+#include <taler/taler-merchant/patch-private-otp-devices-DEVICE_ID-new.h>
+#include <taler/taler-merchant/delete-private-otp-devices-DEVICE_ID-new.h>
+#include <taler/taler-merchant/get-private-templates-new.h>
+#include <taler/taler-merchant/get-private-templates-TEMPLATE_ID-new.h>
+#include <taler/taler-merchant/post-private-templates-new.h>
+#include <taler/taler-merchant/patch-private-templates-TEMPLATE_ID-new.h>
+#include <taler/taler-merchant/delete-private-templates-TEMPLATE_ID-new.h>
+#include <taler/taler-merchant/post-templates-TEMPLATE_ID-new.h>
+#include <taler/taler-merchant/get-templates-TEMPLATE_ID-new.h>
+#include \
+ <taler/taler-merchant/get-private-tokenfamilies-TOKEN_FAMILY_SLUG-new.h>
+#include <taler/taler-merchant/post-private-tokenfamilies-new.h>
+#include <taler/taler-merchant/get-private-webhooks-new.h>
+#include <taler/taler-merchant/get-private-webhooks-WEBHOOK_ID-new.h>
+#include <taler/taler-merchant/post-private-webhooks-new.h>
+#include <taler/taler-merchant/patch-private-webhooks-WEBHOOK_ID-new.h>
+#include <taler/taler-merchant/delete-private-webhooks-WEBHOOK_ID-new.h>
+#include <taler/taler-merchant/get-private-units-new.h>
+#include <taler/taler-merchant/get-private-units-UNIT-new.h>
+#include <taler/taler-merchant/post-private-units-new.h>
+#include <taler/taler-merchant/patch-private-units-UNIT-new.h>
+#include <taler/taler-merchant/delete-private-units-UNIT-new.h>
+#include <taler/taler-merchant/post-private-categories-new.h>
#endif
diff --git a/src/include/taler/taler_merchant_testing_lib.h b/src/include/taler/taler_merchant_testing_lib.h
@@ -2460,7 +2460,8 @@ TALER_TESTING_cmd_exec_donaukeyupdate (const char *label,
op (jurisdiction, const json_t) \
op (wire_delay, const struct GNUNET_TIME_Relative) \
op (pay_delay, const struct GNUNET_TIME_Relative) \
- op (refund_entry, const struct TALER_MERCHANT_RefundDetail) \
+ op (refund_detail_entry, \
+ const struct TALER_MERCHANT_PostOrdersRefundDetail) \
op (order_terms, const json_t) \
op (h_contract_terms, const struct TALER_PrivateContractHashP) \
op (h_wire, const struct TALER_MerchantWireHashP) \
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
@@ -16,71 +16,70 @@ 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-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
+ merchant_api_delete-management-instances-INSTANCE-new.c \
+ merchant_api_delete-private-accounts-H_WIRE-new.c \
+ merchant_api_delete-private-orders-ORDER_ID-new.c \
+ merchant_api_delete-private-otp-devices-DEVICE_ID-new.c \
+ merchant_api_delete-private-products-PRODUCT_ID-new.c \
+ merchant_api_delete-private-templates-TEMPLATE_ID-new.c \
+ merchant_api_delete-private-tokens-SERIAL-new.c \
+ merchant_api_delete-private-transfers-TID-new.c \
+ merchant_api_delete-private-units-UNIT-new.c \
+ merchant_api_delete-private-webhooks-WEBHOOK_ID-new.c \
+ merchant_api_get-config-new.c \
+ merchant_api_get-management-instances-new.c \
+ merchant_api_get-management-instances-INSTANCE-new.c \
+ merchant_api_get-orders-ORDER_ID-new.c \
+ merchant_api_get-private-accounts-new.c \
+ merchant_api_get-private-accounts-H_WIRE-new.c \
+ merchant_api_get-private-kyc-new.c \
+ merchant_api_get-private-orders-new.c \
+ merchant_api_get-private-orders-ORDER_ID-new.c \
+ merchant_api_get-private-otp-devices-new.c \
+ merchant_api_get-private-otp-devices-DEVICE_ID-new.c \
+ merchant_api_get-private-products-new.c \
+ merchant_api_get-private-products-PRODUCT_ID-new.c \
+ merchant_api_get-private-statistics-amount-SLUG-new.c \
+ merchant_api_get-private-statistics-counter-SLUG-new.c \
+ merchant_api_get-private-templates-new.c \
+ merchant_api_get-private-templates-TEMPLATE_ID-new.c \
+ merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG-new.c \
+ merchant_api_get-private-transfers-new.c \
+ merchant_api_get-private-units-new.c \
+ merchant_api_get-private-units-UNIT-new.c \
+ merchant_api_get-private-webhooks-new.c \
+ merchant_api_get-private-webhooks-WEBHOOK_ID-new.c \
+ merchant_api_get-products-IMAGE_HASH-image-new.c \
+ merchant_api_get-templates-TEMPLATE_ID-new.c \
+ merchant_api_patch-management-instances-INSTANCE-new.c \
+ merchant_api_patch-private-accounts-H_WIRE-new.c \
+ merchant_api_patch-private-orders-ORDER_ID-forget-new.c \
+ merchant_api_patch-private-otp-devices-DEVICE_ID-new.c \
+ merchant_api_patch-private-products-PRODUCT_ID-new.c \
+ merchant_api_patch-private-templates-TEMPLATE_ID-new.c \
+ merchant_api_patch-private-units-UNIT-new.c \
+ merchant_api_patch-private-webhooks-WEBHOOK_ID-new.c \
+ merchant_api_post-management-instances-new.c \
+ merchant_api_post-management-instances-INSTANCE-auth-new.c \
+ merchant_api_post-orders-ORDER_ID-abort-new.c \
+ merchant_api_post-orders-ORDER_ID-claim-new.c \
+ merchant_api_post-orders-ORDER_ID-paid-new.c \
+ merchant_api_post-orders-ORDER_ID-pay-new.c \
+ merchant_api_post-orders-ORDER_ID-refund-new.c \
+ merchant_api_post-private-accounts-new.c \
+ merchant_api_post-private-categories-new.c \
+ merchant_api_post-private-orders-new.c \
+ merchant_api_post-private-orders-ORDER_ID-refund-new.c \
+ merchant_api_post-private-otp-devices-new.c \
+ merchant_api_post-private-products-new.c \
+ merchant_api_post-private-products-PRODUCT_ID-lock-new.c \
+ merchant_api_post-private-templates-new.c \
+ merchant_api_post-private-token-new.c \
+ merchant_api_post-private-tokenfamilies-new.c \
+ merchant_api_post-private-transfers-new.c \
+ merchant_api_post-private-units-new.c \
+ merchant_api_post-private-webhooks-new.c \
+ merchant_api_post-templates-TEMPLATE_ID-new.c
libtalermerchant_la_LIBADD = \
-ltalerexchange \
@@ -97,9 +96,9 @@ libtalermerchant_la_LIBADD = \
if HAVE_DONAU
libtalermerchant_la_SOURCES += \
- merchant_api_get-private-donau.c \
- merchant_api_post-private-donau.c \
- merchant_api_delete-private-donau-DONAU_SERIAL.c
+ merchant_api_get-private-donau-new.c \
+ merchant_api_post-private-donau-new.c \
+ merchant_api_delete-private-donau-DONAU_SERIAL-new.c
libtalermerchant_la_LIBADD += \
-ldonau
diff --git a/src/lib/merchant_api_common.c b/src/lib/merchant_api_common.c
@@ -435,12 +435,12 @@ TALER_MERCHANT_parse_refund_uri_free (
void
TALER_MERCHANT_handle_order_creation_response_ (
- TALER_MERCHANT_PostOrdersCallback cb,
+ TALER_MERCHANT_PostPrivateOrdersCallback cb,
void *cb_cls,
long response_code,
const json_t *json)
{
- struct TALER_MERCHANT_PostOrdersReply por = {
+ struct TALER_MERCHANT_PostPrivateOrdersResponse por = {
.hr.http_status = (unsigned int) response_code,
.hr.reply = json
};
diff --git a/src/lib/merchant_api_common.h b/src/lib/merchant_api_common.h
@@ -36,7 +36,7 @@
*/
void
TALER_MERCHANT_handle_order_creation_response_ (
- TALER_MERCHANT_PostOrdersCallback cb,
+ TALER_MERCHANT_PostPrivateOrdersCallback cb,
void *cb_cls,
long response_code,
const json_t *json);
diff --git a/src/lib/merchant_api_delete-management-instances-INSTANCE-new.c b/src/lib/merchant_api_delete-management-instances-INSTANCE-new.c
@@ -0,0 +1,240 @@
+/*
+ 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_delete-management-instances-INSTANCE-new.c
+ * @brief Implementation of the DELETE /management/instances/$INSTANCE 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/delete-management-instances-INSTANCE-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /management/instances/$INSTANCE operation.
+ */
+struct TALER_MERCHANT_DeleteManagementInstanceHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeleteManagementInstanceCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_MANAGEMENT_INSTANCE_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Identifier of the instance to delete.
+ */
+ char *instance_id;
+
+ /**
+ * Whether to purge (hard delete) the instance.
+ */
+ bool purge;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /management/instances/$INSTANCE request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeleteManagementInstanceHandle`
+ * @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_DeleteManagementInstanceHandle *dih = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeleteManagementInstanceResponse dir = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ dih->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /management/instances/$INSTANCE DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dir.hr.ec = TALER_JSON_get_error_code (json);
+ dir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dir.hr.ec = TALER_JSON_get_error_code (json);
+ dir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ dir.hr.ec = TALER_JSON_get_error_code (json);
+ dir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dir.hr.ec = TALER_JSON_get_error_code (json);
+ dir.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /management/instances/$INSTANCE\n",
+ (unsigned int) response_code,
+ (int) dir.hr.ec);
+ break;
+ }
+ dih->cb (dih->cb_cls,
+ &dir);
+ TALER_MERCHANT_delete_management_instance_cancel (dih);
+}
+
+
+struct TALER_MERCHANT_DeleteManagementInstanceHandle *
+TALER_MERCHANT_delete_management_instance_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *instance_id)
+{
+ struct TALER_MERCHANT_DeleteManagementInstanceHandle *dih;
+
+ dih = GNUNET_new (struct TALER_MERCHANT_DeleteManagementInstanceHandle);
+ dih->ctx = ctx;
+ dih->base_url = GNUNET_strdup (url);
+ if (NULL != instance_id)
+ dih->instance_id = GNUNET_strdup (instance_id);
+ return dih;
+}
+
+
+void
+TALER_MERCHANT_delete_management_instance_set_options_ (
+ struct TALER_MERCHANT_DeleteManagementInstanceHandle *handle,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_DeleteManagementInstanceOptionValue options[])
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_DELETE_MANAGEMENT_INSTANCE_OPTION_END:
+ return;
+ case TALER_MERCHANT_DELETE_MANAGEMENT_INSTANCE_OPTION_PURGE:
+ handle->purge = true;
+ break;
+ }
+ }
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_management_instance_start (
+ struct TALER_MERCHANT_DeleteManagementInstanceHandle *dih,
+ TALER_MERCHANT_DeleteManagementInstanceCallback cb,
+ TALER_MERCHANT_DELETE_MANAGEMENT_INSTANCE_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ dih->cb = cb;
+ dih->cb_cls = cb_cls;
+ if (NULL != dih->instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "management/instances/%s",
+ dih->instance_id);
+ dih->url = TALER_url_join (dih->base_url,
+ path,
+ "purge",
+ (dih->purge) ? "yes" : NULL,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ /* backend_url is already identifying the instance */
+ dih->url = TALER_url_join (dih->base_url,
+ "private",
+ "purge",
+ (dih->purge) ? "yes" : NULL,
+ NULL);
+ }
+ if (NULL == dih->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (dih->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ dih->job = GNUNET_CURL_job_add (dih->ctx,
+ eh,
+ &handle_delete_instance_finished,
+ dih);
+ if (NULL == dih->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_management_instance_cancel (
+ struct TALER_MERCHANT_DeleteManagementInstanceHandle *dih)
+{
+ if (NULL != dih->job)
+ {
+ GNUNET_CURL_job_cancel (dih->job);
+ dih->job = NULL;
+ }
+ GNUNET_free (dih->url);
+ GNUNET_free (dih->instance_id);
+ GNUNET_free (dih->base_url);
+ GNUNET_free (dih);
+}
+
+
+/* end of merchant_api_delete-management-instances-INSTANCE-new.c */
diff --git a/src/lib/merchant_api_delete-private-accounts-H_WIRE-new.c b/src/lib/merchant_api_delete-private-accounts-H_WIRE-new.c
@@ -0,0 +1,204 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023-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_delete-private-accounts-H_WIRE-new.c
+ * @brief Implementation of the DELETE /private/accounts/$H_WIRE 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/delete-private-accounts-H_WIRE-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/accounts/$H_WIRE operation.
+ */
+struct TALER_MERCHANT_DeletePrivateAccountHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateAccountCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_ACCOUNT_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Hash of the wire details identifying the account to delete.
+ */
+ struct TALER_MerchantWireHashP h_wire;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/accounts/$H_WIRE request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateAccountHandle`
+ * @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_DeletePrivateAccountHandle *dah = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateAccountResponse dar = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ dah->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/accounts/$H_WIRE DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dar.hr.ec = TALER_JSON_get_error_code (json);
+ dar.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dar.hr.ec = TALER_JSON_get_error_code (json);
+ dar.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dar.hr.ec = TALER_JSON_get_error_code (json);
+ dar.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/accounts/$H_WIRE\n",
+ (unsigned int) response_code,
+ (int) dar.hr.ec);
+ break;
+ }
+ dah->cb (dah->cb_cls,
+ &dar);
+ TALER_MERCHANT_delete_private_account_cancel (dah);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateAccountHandle *
+TALER_MERCHANT_delete_private_account_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const struct TALER_MerchantWireHashP *h_wire)
+{
+ struct TALER_MERCHANT_DeletePrivateAccountHandle *dah;
+
+ dah = GNUNET_new (struct TALER_MERCHANT_DeletePrivateAccountHandle);
+ dah->ctx = ctx;
+ dah->base_url = GNUNET_strdup (url);
+ dah->h_wire = *h_wire;
+ return dah;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_account_start (
+ struct TALER_MERCHANT_DeletePrivateAccountHandle *dah,
+ TALER_MERCHANT_DeletePrivateAccountCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_ACCOUNT_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ dah->cb = cb;
+ dah->cb_cls = cb_cls;
+ {
+ char h_wire_str[sizeof (dah->h_wire) * 2];
+ char *path;
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (&dah->h_wire,
+ sizeof (dah->h_wire),
+ h_wire_str,
+ sizeof (h_wire_str));
+ *end = '\0';
+ GNUNET_asprintf (&path,
+ "private/accounts/%s",
+ h_wire_str);
+ dah->url = TALER_url_join (dah->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == dah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (dah->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ dah->job = GNUNET_CURL_job_add (dah->ctx,
+ eh,
+ &handle_delete_account_finished,
+ dah);
+ if (NULL == dah->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_account_cancel (
+ struct TALER_MERCHANT_DeletePrivateAccountHandle *dah)
+{
+ if (NULL != dah->job)
+ {
+ GNUNET_CURL_job_cancel (dah->job);
+ dah->job = NULL;
+ }
+ GNUNET_free (dah->url);
+ GNUNET_free (dah->base_url);
+ GNUNET_free (dah);
+}
+
+
+/* end of merchant_api_delete-private-accounts-H_WIRE-new.c */
diff --git a/src/lib/merchant_api_delete-private-donau-DONAU_SERIAL-new.c b/src/lib/merchant_api_delete-private-donau-DONAU_SERIAL-new.c
@@ -0,0 +1,194 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024-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_delete-private-donau-DONAU_SERIAL-new.c
+ * @brief Implementation of the DELETE /private/donau/$DONAU_SERIAL 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/delete-private-donau-DONAU_SERIAL-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/donau/$DONAU_SERIAL operation.
+ */
+struct TALER_MERCHANT_DeletePrivateDonauHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateDonauCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_DONAU_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Charity identifier to delete.
+ */
+ uint64_t charity_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/donau/$DONAU_SERIAL request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateDonauHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_donau_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_DeletePrivateDonauHandle *ddh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateDonauResponse ddr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ddh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/donau/$DONAU_SERIAL DELETE 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:
+ ddr.hr.ec = TALER_JSON_get_error_code (json);
+ ddr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ ddr.hr.ec = TALER_JSON_get_error_code (json);
+ ddr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/donau/$DONAU_SERIAL\n",
+ (unsigned int) response_code,
+ (int) ddr.hr.ec);
+ break;
+ }
+ ddh->cb (ddh->cb_cls,
+ &ddr);
+ TALER_MERCHANT_delete_private_donau_cancel (ddh);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateDonauHandle *
+TALER_MERCHANT_delete_private_donau_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ uint64_t charity_id)
+{
+ struct TALER_MERCHANT_DeletePrivateDonauHandle *ddh;
+
+ ddh = GNUNET_new (struct TALER_MERCHANT_DeletePrivateDonauHandle);
+ ddh->ctx = ctx;
+ ddh->base_url = GNUNET_strdup (url);
+ ddh->charity_id = charity_id;
+ return ddh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_donau_start (
+ struct TALER_MERCHANT_DeletePrivateDonauHandle *ddh,
+ TALER_MERCHANT_DeletePrivateDonauCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_DONAU_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ ddh->cb = cb;
+ ddh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/donau/%llu",
+ (unsigned long long) ddh->charity_id);
+ ddh->url = TALER_url_join (ddh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == ddh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (ddh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ ddh->job = GNUNET_CURL_job_add (ddh->ctx,
+ eh,
+ &handle_delete_donau_finished,
+ ddh);
+ if (NULL == ddh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_donau_cancel (
+ struct TALER_MERCHANT_DeletePrivateDonauHandle *ddh)
+{
+ if (NULL != ddh->job)
+ {
+ GNUNET_CURL_job_cancel (ddh->job);
+ ddh->job = NULL;
+ }
+ GNUNET_free (ddh->url);
+ GNUNET_free (ddh->base_url);
+ GNUNET_free (ddh);
+}
+
+
+/* end of merchant_api_delete-private-donau-DONAU_SERIAL-new.c */
diff --git a/src/lib/merchant_api_delete-private-orders-ORDER_ID-new.c b/src/lib/merchant_api_delete-private-orders-ORDER_ID-new.c
@@ -0,0 +1,230 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_delete-private-orders-ORDER_ID-new.c
+ * @brief Implementation of the DELETE /private/orders/$ORDER_ID 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/delete-private-orders-ORDER_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/orders/$ORDER_ID operation.
+ */
+struct TALER_MERCHANT_DeletePrivateOrderHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateOrderCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_ORDER_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Identifier of the order to delete.
+ */
+ char *order_id;
+
+ /**
+ * Whether to force deletion even if order has payments.
+ */
+ bool force;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/orders/$ORDER_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateOrderHandle`
+ * @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_DeletePrivateOrderHandle *doh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateOrderResponse dor = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ doh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/orders/$ORDER_ID DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dor.hr.ec = TALER_JSON_get_error_code (json);
+ dor.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dor.hr.ec = TALER_JSON_get_error_code (json);
+ dor.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ dor.hr.ec = TALER_JSON_get_error_code (json);
+ dor.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dor.hr.ec = TALER_JSON_get_error_code (json);
+ dor.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/orders/$ORDER_ID\n",
+ (unsigned int) response_code,
+ (int) dor.hr.ec);
+ break;
+ }
+ doh->cb (doh->cb_cls,
+ &dor);
+ TALER_MERCHANT_delete_private_order_cancel (doh);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateOrderHandle *
+TALER_MERCHANT_delete_private_order_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id)
+{
+ struct TALER_MERCHANT_DeletePrivateOrderHandle *doh;
+
+ doh = GNUNET_new (struct TALER_MERCHANT_DeletePrivateOrderHandle);
+ doh->ctx = ctx;
+ doh->base_url = GNUNET_strdup (url);
+ doh->order_id = GNUNET_strdup (order_id);
+ doh->force = false;
+ return doh;
+}
+
+
+void
+TALER_MERCHANT_delete_private_order_set_options_ (
+ struct TALER_MERCHANT_DeletePrivateOrderHandle *handle,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_DeletePrivateOrderOptionValue options[])
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_DELETE_PRIVATE_ORDER_OPTION_END:
+ return;
+ case TALER_MERCHANT_DELETE_PRIVATE_ORDER_OPTION_FORCE:
+ handle->force = true;
+ break;
+ }
+ }
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_order_start (
+ struct TALER_MERCHANT_DeletePrivateOrderHandle *doh,
+ TALER_MERCHANT_DeletePrivateOrderCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_ORDER_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ doh->cb = cb;
+ doh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/orders/%s",
+ doh->order_id);
+ doh->url = TALER_url_join (doh->base_url,
+ path,
+ "force",
+ (doh->force) ? "yes" : NULL,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == doh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (doh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ doh->job = GNUNET_CURL_job_add (doh->ctx,
+ eh,
+ &handle_delete_order_finished,
+ doh);
+ if (NULL == doh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_order_cancel (
+ struct TALER_MERCHANT_DeletePrivateOrderHandle *doh)
+{
+ if (NULL != doh->job)
+ {
+ GNUNET_CURL_job_cancel (doh->job);
+ doh->job = NULL;
+ }
+ GNUNET_free (doh->url);
+ GNUNET_free (doh->order_id);
+ GNUNET_free (doh->base_url);
+ GNUNET_free (doh);
+}
+
+
+/* end of merchant_api_delete-private-orders-ORDER_ID-new.c */
diff --git a/src/lib/merchant_api_delete-private-otp-devices-DEVICE_ID-new.c b/src/lib/merchant_api_delete-private-otp-devices-DEVICE_ID-new.c
@@ -0,0 +1,202 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_delete-private-otp-devices-DEVICE_ID-new.c
+ * @brief Implementation of the DELETE /private/otp-devices/$DEVICE_ID 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/delete-private-otp-devices-DEVICE_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/otp-devices/$DEVICE_ID operation.
+ */
+struct TALER_MERCHANT_DeletePrivateOtpDeviceHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateOtpDeviceCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_OTP_DEVICE_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Identifier of the OTP device to delete.
+ */
+ char *otp_device_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/otp-devices/$DEVICE_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateOtpDeviceHandle`
+ * @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_DeletePrivateOtpDeviceHandle *doh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateOtpDeviceResponse dor = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ doh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/otp-devices/$DEVICE_ID DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dor.hr.ec = TALER_JSON_get_error_code (json);
+ dor.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dor.hr.ec = TALER_JSON_get_error_code (json);
+ dor.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ dor.hr.ec = TALER_JSON_get_error_code (json);
+ dor.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dor.hr.ec = TALER_JSON_get_error_code (json);
+ dor.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/otp-devices/$DEVICE_ID\n",
+ (unsigned int) response_code,
+ (int) dor.hr.ec);
+ break;
+ }
+ doh->cb (doh->cb_cls,
+ &dor);
+ TALER_MERCHANT_delete_private_otp_device_cancel (doh);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateOtpDeviceHandle *
+TALER_MERCHANT_delete_private_otp_device_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *otp_device_id)
+{
+ struct TALER_MERCHANT_DeletePrivateOtpDeviceHandle *doh;
+
+ doh = GNUNET_new (struct TALER_MERCHANT_DeletePrivateOtpDeviceHandle);
+ doh->ctx = ctx;
+ doh->base_url = GNUNET_strdup (url);
+ doh->otp_device_id = GNUNET_strdup (otp_device_id);
+ return doh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_otp_device_start (
+ struct TALER_MERCHANT_DeletePrivateOtpDeviceHandle *doh,
+ TALER_MERCHANT_DeletePrivateOtpDeviceCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_OTP_DEVICE_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ doh->cb = cb;
+ doh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/otp-devices/%s",
+ doh->otp_device_id);
+ doh->url = TALER_url_join (doh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == doh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (doh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ doh->job = GNUNET_CURL_job_add (doh->ctx,
+ eh,
+ &handle_delete_otp_device_finished,
+ doh);
+ if (NULL == doh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_otp_device_cancel (
+ struct TALER_MERCHANT_DeletePrivateOtpDeviceHandle *doh)
+{
+ if (NULL != doh->job)
+ {
+ GNUNET_CURL_job_cancel (doh->job);
+ doh->job = NULL;
+ }
+ GNUNET_free (doh->url);
+ GNUNET_free (doh->otp_device_id);
+ GNUNET_free (doh->base_url);
+ GNUNET_free (doh);
+}
+
+
+/* end of merchant_api_delete-private-otp-devices-DEVICE_ID-new.c */
diff --git a/src/lib/merchant_api_delete-private-products-PRODUCT_ID-new.c b/src/lib/merchant_api_delete-private-products-PRODUCT_ID-new.c
@@ -0,0 +1,202 @@
+/*
+ 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_delete-private-products-PRODUCT_ID-new.c
+ * @brief Implementation of the DELETE /private/products/$PRODUCT_ID 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/delete-private-products-PRODUCT_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/products/$PRODUCT_ID operation.
+ */
+struct TALER_MERCHANT_DeletePrivateProductHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateProductCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Identifier of the product to delete.
+ */
+ char *product_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/products/$PRODUCT_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateProductHandle`
+ * @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_DeletePrivateProductHandle *dph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateProductResponse dpr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ dph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/products/$PRODUCT_ID DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dpr.hr.ec = TALER_JSON_get_error_code (json);
+ dpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dpr.hr.ec = TALER_JSON_get_error_code (json);
+ dpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ dpr.hr.ec = TALER_JSON_get_error_code (json);
+ dpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dpr.hr.ec = TALER_JSON_get_error_code (json);
+ dpr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/products/$PRODUCT_ID\n",
+ (unsigned int) response_code,
+ (int) dpr.hr.ec);
+ break;
+ }
+ dph->cb (dph->cb_cls,
+ &dpr);
+ TALER_MERCHANT_delete_private_product_cancel (dph);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateProductHandle *
+TALER_MERCHANT_delete_private_product_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *product_id)
+{
+ struct TALER_MERCHANT_DeletePrivateProductHandle *dph;
+
+ dph = GNUNET_new (struct TALER_MERCHANT_DeletePrivateProductHandle);
+ dph->ctx = ctx;
+ dph->base_url = GNUNET_strdup (url);
+ dph->product_id = GNUNET_strdup (product_id);
+ return dph;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_product_start (
+ struct TALER_MERCHANT_DeletePrivateProductHandle *dph,
+ TALER_MERCHANT_DeletePrivateProductCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ dph->cb = cb;
+ dph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/products/%s",
+ dph->product_id);
+ dph->url = TALER_url_join (dph->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == dph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (dph->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ dph->job = GNUNET_CURL_job_add (dph->ctx,
+ eh,
+ &handle_delete_product_finished,
+ dph);
+ if (NULL == dph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_product_cancel (
+ struct TALER_MERCHANT_DeletePrivateProductHandle *dph)
+{
+ if (NULL != dph->job)
+ {
+ GNUNET_CURL_job_cancel (dph->job);
+ dph->job = NULL;
+ }
+ GNUNET_free (dph->url);
+ GNUNET_free (dph->product_id);
+ GNUNET_free (dph->base_url);
+ GNUNET_free (dph);
+}
+
+
+/* end of merchant_api_delete-private-products-PRODUCT_ID-new.c */
diff --git a/src/lib/merchant_api_delete-private-templates-TEMPLATE_ID-new.c b/src/lib/merchant_api_delete-private-templates-TEMPLATE_ID-new.c
@@ -0,0 +1,202 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_delete-private-templates-TEMPLATE_ID-new.c
+ * @brief Implementation of the DELETE /private/templates/$TEMPLATE_ID 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/delete-private-templates-TEMPLATE_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/templates/$TEMPLATE_ID operation.
+ */
+struct TALER_MERCHANT_DeletePrivateTemplateHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateTemplateCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_TEMPLATE_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Identifier of the template to delete.
+ */
+ char *template_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/templates/$TEMPLATE_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateTemplateHandle`
+ * @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_DeletePrivateTemplateHandle *dth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateTemplateResponse dtr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ dth->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/templates/$TEMPLATE_ID DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/templates/$TEMPLATE_ID\n",
+ (unsigned int) response_code,
+ (int) dtr.hr.ec);
+ break;
+ }
+ dth->cb (dth->cb_cls,
+ &dtr);
+ TALER_MERCHANT_delete_private_template_cancel (dth);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateTemplateHandle *
+TALER_MERCHANT_delete_private_template_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *template_id)
+{
+ struct TALER_MERCHANT_DeletePrivateTemplateHandle *dth;
+
+ dth = GNUNET_new (struct TALER_MERCHANT_DeletePrivateTemplateHandle);
+ dth->ctx = ctx;
+ dth->base_url = GNUNET_strdup (url);
+ dth->template_id = GNUNET_strdup (template_id);
+ return dth;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_template_start (
+ struct TALER_MERCHANT_DeletePrivateTemplateHandle *dth,
+ TALER_MERCHANT_DeletePrivateTemplateCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_TEMPLATE_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ dth->cb = cb;
+ dth->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/templates/%s",
+ dth->template_id);
+ dth->url = TALER_url_join (dth->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == dth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (dth->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ dth->job = GNUNET_CURL_job_add (dth->ctx,
+ eh,
+ &handle_delete_template_finished,
+ dth);
+ if (NULL == dth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_template_cancel (
+ struct TALER_MERCHANT_DeletePrivateTemplateHandle *dth)
+{
+ if (NULL != dth->job)
+ {
+ GNUNET_CURL_job_cancel (dth->job);
+ dth->job = NULL;
+ }
+ GNUNET_free (dth->url);
+ GNUNET_free (dth->template_id);
+ GNUNET_free (dth->base_url);
+ GNUNET_free (dth);
+}
+
+
+/* end of merchant_api_delete-private-templates-TEMPLATE_ID-new.c */
diff --git a/src/lib/merchant_api_delete-private-tokens-SERIAL-new.c b/src/lib/merchant_api_delete-private-tokens-SERIAL-new.c
@@ -0,0 +1,198 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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_delete-private-tokens-SERIAL-new.c
+ * @brief Implementation of the DELETE /private/tokens/$SERIAL 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/delete-private-tokens-SERIAL-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/tokens/$SERIAL operation.
+ */
+struct TALER_MERCHANT_DeletePrivateTokenHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateTokenCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_TOKEN_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Identifier of the instance whose token to delete.
+ */
+ char *instance_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/tokens/$SERIAL request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateTokenHandle`
+ * @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_DeletePrivateTokenHandle *dth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateTokenResponse dtr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ dth->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got DELETE /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:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /instances/$ID/private/token\n",
+ (unsigned int) response_code,
+ (int) dtr.hr.ec);
+ break;
+ }
+ dth->cb (dth->cb_cls,
+ &dtr);
+ TALER_MERCHANT_delete_private_token_cancel (dth);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateTokenHandle *
+TALER_MERCHANT_delete_private_token_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *instance_id)
+{
+ struct TALER_MERCHANT_DeletePrivateTokenHandle *dth;
+
+ dth = GNUNET_new (struct TALER_MERCHANT_DeletePrivateTokenHandle);
+ dth->ctx = ctx;
+ dth->base_url = GNUNET_strdup (url);
+ dth->instance_id = GNUNET_strdup (instance_id);
+ return dth;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_token_start (
+ struct TALER_MERCHANT_DeletePrivateTokenHandle *dth,
+ TALER_MERCHANT_DeletePrivateTokenCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_TOKEN_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ dth->cb = cb;
+ dth->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "instances/%s/private/token",
+ dth->instance_id);
+ dth->url = TALER_url_join (dth->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == dth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (dth->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ dth->job = GNUNET_CURL_job_add (dth->ctx,
+ eh,
+ &handle_delete_token_finished,
+ dth);
+ if (NULL == dth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_token_cancel (
+ struct TALER_MERCHANT_DeletePrivateTokenHandle *dth)
+{
+ if (NULL != dth->job)
+ {
+ GNUNET_CURL_job_cancel (dth->job);
+ dth->job = NULL;
+ }
+ GNUNET_free (dth->url);
+ GNUNET_free (dth->instance_id);
+ GNUNET_free (dth->base_url);
+ GNUNET_free (dth);
+}
+
+
+/* end of merchant_api_delete-private-tokens-SERIAL-new.c */
diff --git a/src/lib/merchant_api_delete-private-transfers-TID-new.c b/src/lib/merchant_api_delete-private-transfers-TID-new.c
@@ -0,0 +1,201 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021-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_delete-private-transfers-TID-new.c
+ * @brief Implementation of the DELETE /private/transfers/$TID 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/delete-private-transfers-TID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/transfers/$TID operation.
+ */
+struct TALER_MERCHANT_DeletePrivateTransferHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateTransferCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_TRANSFER_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Serial number of the wire transfer to delete.
+ */
+ uint64_t wire_transfer_serial;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/transfers/$TID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateTransferHandle`
+ * @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_DeletePrivateTransferHandle *dth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateTransferResponse dtr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ dth->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/transfers/$TID DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dtr.hr.ec = TALER_JSON_get_error_code (json);
+ dtr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/transfers/$TID\n",
+ (unsigned int) response_code,
+ (int) dtr.hr.ec);
+ break;
+ }
+ dth->cb (dth->cb_cls,
+ &dtr);
+ TALER_MERCHANT_delete_private_transfer_cancel (dth);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateTransferHandle *
+TALER_MERCHANT_delete_private_transfer_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ uint64_t wire_transfer_serial)
+{
+ struct TALER_MERCHANT_DeletePrivateTransferHandle *dth;
+
+ dth = GNUNET_new (struct TALER_MERCHANT_DeletePrivateTransferHandle);
+ dth->ctx = ctx;
+ dth->base_url = GNUNET_strdup (url);
+ dth->wire_transfer_serial = wire_transfer_serial;
+ return dth;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_transfer_start (
+ struct TALER_MERCHANT_DeletePrivateTransferHandle *dth,
+ TALER_MERCHANT_DeletePrivateTransferCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_TRANSFER_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ dth->cb = cb;
+ dth->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/transfers/%llu",
+ (unsigned long long) dth->wire_transfer_serial);
+ dth->url = TALER_url_join (dth->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == dth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (dth->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ dth->job = GNUNET_CURL_job_add (dth->ctx,
+ eh,
+ &handle_delete_transfer_finished,
+ dth);
+ if (NULL == dth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_transfer_cancel (
+ struct TALER_MERCHANT_DeletePrivateTransferHandle *dth)
+{
+ if (NULL != dth->job)
+ {
+ GNUNET_CURL_job_cancel (dth->job);
+ dth->job = NULL;
+ }
+ GNUNET_free (dth->url);
+ GNUNET_free (dth->base_url);
+ GNUNET_free (dth);
+}
+
+
+/* end of merchant_api_delete-private-transfers-TID-new.c */
diff --git a/src/lib/merchant_api_delete-private-units-UNIT-new.c b/src/lib/merchant_api_delete-private-units-UNIT-new.c
@@ -0,0 +1,202 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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_delete-private-units-UNIT-new.c
+ * @brief Implementation of the DELETE /private/units/$UNIT 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/delete-private-units-UNIT-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/units/$UNIT operation.
+ */
+struct TALER_MERCHANT_DeletePrivateUnitHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateUnitCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Identifier of the unit to delete.
+ */
+ char *unit_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/units/$UNIT request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateUnitHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_unit_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_DeletePrivateUnitHandle *duh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateUnitResponse dur = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ duh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/units/$UNIT DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dur.hr.ec = TALER_JSON_get_error_code (json);
+ dur.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dur.hr.ec = TALER_JSON_get_error_code (json);
+ dur.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ dur.hr.ec = TALER_JSON_get_error_code (json);
+ dur.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dur.hr.ec = TALER_JSON_get_error_code (json);
+ dur.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/units/$UNIT\n",
+ (unsigned int) response_code,
+ (int) dur.hr.ec);
+ break;
+ }
+ duh->cb (duh->cb_cls,
+ &dur);
+ TALER_MERCHANT_delete_private_unit_cancel (duh);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateUnitHandle *
+TALER_MERCHANT_delete_private_unit_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *unit_id)
+{
+ struct TALER_MERCHANT_DeletePrivateUnitHandle *duh;
+
+ duh = GNUNET_new (struct TALER_MERCHANT_DeletePrivateUnitHandle);
+ duh->ctx = ctx;
+ duh->base_url = GNUNET_strdup (url);
+ duh->unit_id = GNUNET_strdup (unit_id);
+ return duh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_unit_start (
+ struct TALER_MERCHANT_DeletePrivateUnitHandle *duh,
+ TALER_MERCHANT_DeletePrivateUnitCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ duh->cb = cb;
+ duh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/units/%s",
+ duh->unit_id);
+ duh->url = TALER_url_join (duh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == duh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (duh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ duh->job = GNUNET_CURL_job_add (duh->ctx,
+ eh,
+ &handle_delete_unit_finished,
+ duh);
+ if (NULL == duh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_unit_cancel (
+ struct TALER_MERCHANT_DeletePrivateUnitHandle *duh)
+{
+ if (NULL != duh->job)
+ {
+ GNUNET_CURL_job_cancel (duh->job);
+ duh->job = NULL;
+ }
+ GNUNET_free (duh->url);
+ GNUNET_free (duh->unit_id);
+ GNUNET_free (duh->base_url);
+ GNUNET_free (duh);
+}
+
+
+/* end of merchant_api_delete-private-units-UNIT-new.c */
diff --git a/src/lib/merchant_api_delete-private-webhooks-WEBHOOK_ID-new.c b/src/lib/merchant_api_delete-private-webhooks-WEBHOOK_ID-new.c
@@ -0,0 +1,203 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_delete-private-webhooks-WEBHOOK_ID-new.c
+ * @brief Implementation of the DELETE /private/webhooks/$WEBHOOK_ID 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/delete-private-webhooks-WEBHOOK_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a DELETE /private/webhooks/$WEBHOOK_ID operation.
+ */
+struct TALER_MERCHANT_DeletePrivateWebhookHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DeletePrivateWebhookCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_DELETE_PRIVATE_WEBHOOK_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Identifier of the webhook to delete.
+ */
+ char *webhook_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP DELETE /private/webhooks/$WEBHOOK_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DeletePrivateWebhookHandle`
+ * @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_DeletePrivateWebhookHandle *dwh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DeletePrivateWebhookResponse dwr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ dwh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/webhooks/$WEBHOOK_ID DELETE response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ dwr.hr.ec = TALER_JSON_get_error_code (json);
+ dwr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ dwr.hr.ec = TALER_JSON_get_error_code (json);
+ dwr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ dwr.hr.ec = TALER_JSON_get_error_code (json);
+ dwr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dwr.hr.ec = TALER_JSON_get_error_code (json);
+ dwr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for DELETE /private/webhooks/$WEBHOOK_ID\n",
+ (unsigned int) response_code,
+ (int) dwr.hr.ec);
+ break;
+ }
+ dwh->cb (dwh->cb_cls,
+ &dwr);
+ TALER_MERCHANT_delete_private_webhook_cancel (dwh);
+}
+
+
+struct TALER_MERCHANT_DeletePrivateWebhookHandle *
+TALER_MERCHANT_delete_private_webhook_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *webhook_id)
+{
+ struct TALER_MERCHANT_DeletePrivateWebhookHandle *dwh;
+
+ dwh = GNUNET_new (struct TALER_MERCHANT_DeletePrivateWebhookHandle);
+ dwh->ctx = ctx;
+ dwh->base_url = GNUNET_strdup (url);
+ dwh->webhook_id = GNUNET_strdup (webhook_id);
+ return dwh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_delete_private_webhook_start (
+ struct TALER_MERCHANT_DeletePrivateWebhookHandle *dwh,
+ TALER_MERCHANT_DeletePrivateWebhookCallback cb,
+ TALER_MERCHANT_DELETE_PRIVATE_WEBHOOK_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ dwh->cb = cb;
+ dwh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/webhooks/%s",
+ dwh->webhook_id);
+ dwh->url = TALER_url_join (dwh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == dwh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (dwh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_DELETE));
+ dwh->job = GNUNET_CURL_job_add (dwh->ctx,
+ eh,
+ &handle_delete_webhook_finished,
+ dwh);
+ if (NULL == dwh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_delete_private_webhook_cancel (
+ struct TALER_MERCHANT_DeletePrivateWebhookHandle *dwh)
+{
+ if (NULL != dwh->job)
+ {
+ GNUNET_CURL_job_cancel (dwh->job);
+ dwh->job = NULL;
+ }
+ GNUNET_free (dwh->url);
+ GNUNET_free (dwh->base_url);
+ GNUNET_free (dwh->webhook_id);
+ GNUNET_free (dwh);
+}
+
+
+/* end of merchant_api_delete-private-webhooks-WEBHOOK_ID-new.c */
diff --git a/src/lib/merchant_api_get-config-new.c b/src/lib/merchant_api_get-config-new.c
@@ -0,0 +1,335 @@
+/*
+ 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-new.c
+ * @brief Implementation of the GET /config 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/get-config-new.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
+
+
+/**
+ * Handle for a GET /config operation.
+ */
+struct TALER_MERCHANT_GetConfigHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetConfigCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_CONFIG_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /config request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetConfigHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_config_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetConfigHandle *gch = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetConfigResponse cr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gch->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /config response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *jcs;
+ const json_t *exchanges = NULL;
+ struct TALER_MERCHANT_GetConfigExchangeInfo *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_GET_CONFIG_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_GET_CONFIG_VC_MATCH;
+ if (MERCHANT_PROTOCOL_CURRENT < pv.current)
+ {
+ cr.details.ok.compat |= TALER_MERCHANT_GET_CONFIG_VC_NEWER;
+ if (MERCHANT_PROTOCOL_CURRENT < pv.current - pv.age)
+ cr.details.ok.compat
+ |= TALER_MERCHANT_GET_CONFIG_VC_INCOMPATIBLE;
+ }
+ if (MERCHANT_PROTOCOL_CURRENT > pv.current)
+ {
+ cr.details.ok.compat |= TALER_MERCHANT_GET_CONFIG_VC_OLDER;
+ if (MERCHANT_PROTOCOL_CURRENT - MERCHANT_PROTOCOL_AGE > pv.current)
+ cr.details.ok.compat
+ |= TALER_MERCHANT_GET_CONFIG_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_array_size (exchanges);
+ if ( (num_eci > MAX_EXCHANGES) ||
+ (json_array_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_GetConfigExchangeInfo);
+ for (unsigned int i = 0; i<num_eci; i++)
+ {
+ struct TALER_MERCHANT_GetConfigExchangeInfo *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;
+ }
+ }
+ gch->cb (gch->cb_cls,
+ &cr);
+ GNUNET_free (eci);
+ TALER_CONFIG_free_currencies (nspec,
+ cspecs);
+ }
+ TALER_MERCHANT_get_config_cancel (gch);
+ return;
+ }
+ default:
+ 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;
+ }
+ gch->cb (gch->cb_cls,
+ &cr);
+ TALER_MERCHANT_get_config_cancel (gch);
+}
+
+
+struct TALER_MERCHANT_GetConfigHandle *
+TALER_MERCHANT_get_config_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetConfigHandle *gch;
+
+ gch = GNUNET_new (struct TALER_MERCHANT_GetConfigHandle);
+ gch->ctx = ctx;
+ gch->base_url = GNUNET_strdup (url);
+ return gch;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_config_start (
+ struct TALER_MERCHANT_GetConfigHandle *gch,
+ TALER_MERCHANT_GetConfigCallback cb,
+ TALER_MERCHANT_GET_CONFIG_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gch->cb = cb;
+ gch->cb_cls = cb_cls;
+ gch->url = TALER_url_join (gch->base_url,
+ "config",
+ NULL);
+ if (NULL == gch->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gch->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gch->job = GNUNET_CURL_job_add (gch->ctx,
+ eh,
+ &handle_get_config_finished,
+ gch);
+ if (NULL == gch->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_config_cancel (
+ struct TALER_MERCHANT_GetConfigHandle *gch)
+{
+ if (NULL != gch->job)
+ {
+ GNUNET_CURL_job_cancel (gch->job);
+ gch->job = NULL;
+ }
+ GNUNET_free (gch->url);
+ GNUNET_free (gch->base_url);
+ GNUNET_free (gch);
+}
+
+
+/* end of merchant_api_get-config-new.c */
diff --git a/src/lib/merchant_api_get-management-instances-INSTANCE-new.c b/src/lib/merchant_api_get-management-instances-INSTANCE-new.c
@@ -0,0 +1,256 @@
+/*
+ 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-management-instances-INSTANCE-new.c
+ * @brief Implementation of the GET /management/instances/$INSTANCE 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/get-management-instances-INSTANCE-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /management/instances/$INSTANCE operation.
+ */
+struct TALER_MERCHANT_GetManagementInstanceHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * Instance identifier to query.
+ */
+ char *instance_id;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetManagementInstanceCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_MANAGEMENT_INSTANCE_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /management/instances/$INSTANCE request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetManagementInstanceHandle`
+ * @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_GetManagementInstanceHandle *gmi = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetManagementInstanceResponse igr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gmi->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /management/instances/$INSTANCE 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;
+ gmi->cb (gmi->cb_cls,
+ &igr);
+ TALER_MERCHANT_get_management_instance_cancel (gmi);
+ return;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ igr.hr.ec = TALER_JSON_get_error_code (json);
+ igr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ igr.hr.ec = TALER_JSON_get_error_code (json);
+ igr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ 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;
+ }
+ gmi->cb (gmi->cb_cls,
+ &igr);
+ TALER_MERCHANT_get_management_instance_cancel (gmi);
+}
+
+
+struct TALER_MERCHANT_GetManagementInstanceHandle *
+TALER_MERCHANT_get_management_instance_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *instance_id)
+{
+ struct TALER_MERCHANT_GetManagementInstanceHandle *gmi;
+
+ gmi = GNUNET_new (struct TALER_MERCHANT_GetManagementInstanceHandle);
+ gmi->ctx = ctx;
+ gmi->base_url = GNUNET_strdup (url);
+ if (NULL != instance_id)
+ gmi->instance_id = GNUNET_strdup (instance_id);
+ return gmi;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_management_instance_start (
+ struct TALER_MERCHANT_GetManagementInstanceHandle *gmi,
+ TALER_MERCHANT_GetManagementInstanceCallback cb,
+ TALER_MERCHANT_GET_MANAGEMENT_INSTANCE_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gmi->cb = cb;
+ gmi->cb_cls = cb_cls;
+ if (NULL != gmi->instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "instances/%s/private",
+ gmi->instance_id);
+ gmi->url = TALER_url_join (gmi->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ gmi->url = TALER_url_join (gmi->base_url,
+ "private",
+ NULL);
+ }
+ if (NULL == gmi->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gmi->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gmi->job = GNUNET_CURL_job_add (gmi->ctx,
+ eh,
+ &handle_get_instance_finished,
+ gmi);
+ if (NULL == gmi->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_management_instance_cancel (
+ struct TALER_MERCHANT_GetManagementInstanceHandle *gmi)
+{
+ if (NULL != gmi->job)
+ {
+ GNUNET_CURL_job_cancel (gmi->job);
+ gmi->job = NULL;
+ }
+ GNUNET_free (gmi->url);
+ GNUNET_free (gmi->instance_id);
+ GNUNET_free (gmi->base_url);
+ GNUNET_free (gmi);
+}
+
+
+/* end of merchant_api_get-management-instances-INSTANCE-new.c */
diff --git a/src/lib/merchant_api_get-management-instances-new.c b/src/lib/merchant_api_get-management-instances-new.c
@@ -0,0 +1,277 @@
+/*
+ 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-management-instances-new.c
+ * @brief Implementation of the GET /management/instances 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/get-management-instances-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+/**
+ * Maximum number of instances permitted.
+ */
+#define MAX_INSTANCES 1024
+
+
+/**
+ * Handle for a GET /management/instances operation.
+ */
+struct TALER_MERCHANT_GetManagementInstancesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetManagementInstancesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_MANAGEMENT_INSTANCES_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Parse instance information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with instance data
+ * @param[in] igr partially filled response
+ * @param gimh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_instances (const json_t *ia,
+ struct TALER_MERCHANT_GetManagementInstancesResponse *igr,
+ struct TALER_MERCHANT_GetManagementInstancesHandle *gimh)
+{
+ 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_GetManagementInstancesInstanceInfo iis[
+ GNUNET_NZL (iis_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_GetManagementInstancesInstanceInfo *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;
+ }
+ }
+ }
+ igr->details.ok.iis_length = iis_len;
+ igr->details.ok.iis = iis;
+ gimh->cb (gimh->cb_cls,
+ igr);
+ gimh->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /management/instances request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetManagementInstancesHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_instances_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetManagementInstancesHandle *gimh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetManagementInstancesResponse igr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gimh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /management/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 (instances,
+ &igr,
+ gimh))
+ {
+ TALER_MERCHANT_get_management_instances_cancel (gimh);
+ 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:
+ 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;
+ }
+ gimh->cb (gimh->cb_cls,
+ &igr);
+ TALER_MERCHANT_get_management_instances_cancel (gimh);
+}
+
+
+struct TALER_MERCHANT_GetManagementInstancesHandle *
+TALER_MERCHANT_get_management_instances_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetManagementInstancesHandle *gimh;
+
+ gimh = GNUNET_new (struct TALER_MERCHANT_GetManagementInstancesHandle);
+ gimh->ctx = ctx;
+ gimh->base_url = GNUNET_strdup (url);
+ return gimh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_management_instances_start (
+ struct TALER_MERCHANT_GetManagementInstancesHandle *gimh,
+ TALER_MERCHANT_GetManagementInstancesCallback cb,
+ TALER_MERCHANT_GET_MANAGEMENT_INSTANCES_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gimh->cb = cb;
+ gimh->cb_cls = cb_cls;
+ gimh->url = TALER_url_join (gimh->base_url,
+ "management/instances",
+ NULL);
+ if (NULL == gimh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gimh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gimh->job = GNUNET_CURL_job_add (gimh->ctx,
+ eh,
+ &handle_get_instances_finished,
+ gimh);
+ if (NULL == gimh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_management_instances_cancel (
+ struct TALER_MERCHANT_GetManagementInstancesHandle *gimh)
+{
+ if (NULL != gimh->job)
+ {
+ GNUNET_CURL_job_cancel (gimh->job);
+ gimh->job = NULL;
+ }
+ GNUNET_free (gimh->url);
+ GNUNET_free (gimh->base_url);
+ GNUNET_free (gimh);
+}
+
+
+/* end of merchant_api_get-management-instances-new.c */
diff --git a/src/lib/merchant_api_get-orders-ORDER_ID-new.c b/src/lib/merchant_api_get-orders-ORDER_ID-new.c
@@ -0,0 +1,335 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018-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-orders-ORDER_ID-new.c
+ * @brief Implementation of the GET /orders/$ORDER_ID request (wallet-facing)
+ * @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/get-orders-ORDER_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /orders/$ORDER_ID operation (wallet-facing).
+ */
+struct TALER_MERCHANT_GetOrdersHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetOrdersCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_ORDERS_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Order ID.
+ */
+ char *order_id;
+
+ /**
+ * Hash of the contract terms (for authentication).
+ */
+ struct TALER_PrivateContractHashP h_contract;
+
+ /**
+ * Session ID for repurchase detection, or NULL.
+ */
+ char *session_id;
+
+ /**
+ * Long polling timeout.
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * Minimum refund amount to wait for, or NULL if unset.
+ */
+ struct TALER_Amount min_refund;
+
+ /**
+ * True if @e min_refund was set.
+ */
+ bool have_min_refund;
+
+ /**
+ * If true, wait until refund is confirmed obtained.
+ */
+ bool await_refund_obtained;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /orders/$ORDER_ID request (wallet-facing).
+ *
+ * @param cls the `struct TALER_MERCHANT_GetOrdersHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_order_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetOrdersHandle *oph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetOrdersResponse owgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ oph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /orders/$ORDER_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_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))
+ {
+ owgr.hr.http_status = 0;
+ owgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ oph->cb (oph->cb_cls,
+ &owgr);
+ TALER_MERCHANT_get_orders_cancel (oph);
+ return;
+ }
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ {
+ 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)
+ {
+ owgr.hr.http_status = 0;
+ owgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ oph->cb (oph->cb_cls,
+ &owgr);
+ TALER_MERCHANT_get_orders_cancel (oph);
+ return;
+ }
+ default:
+ owgr.hr.ec = TALER_JSON_get_error_code (json);
+ owgr.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) owgr.hr.ec);
+ break;
+ }
+ oph->cb (oph->cb_cls,
+ &owgr);
+ TALER_MERCHANT_get_orders_cancel (oph);
+}
+
+
+struct TALER_MERCHANT_GetOrdersHandle *
+TALER_MERCHANT_get_orders_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id,
+ const struct TALER_PrivateContractHashP *h_contract)
+{
+ struct TALER_MERCHANT_GetOrdersHandle *oph;
+
+ oph = GNUNET_new (struct TALER_MERCHANT_GetOrdersHandle);
+ oph->ctx = ctx;
+ oph->base_url = GNUNET_strdup (url);
+ oph->order_id = GNUNET_strdup (order_id);
+ oph->h_contract = *h_contract;
+ return oph;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_orders_set_options_ (
+ struct TALER_MERCHANT_GetOrdersHandle *oph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetOrdersOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ const struct TALER_MERCHANT_GetOrdersOptionValue *opt =
+ &options[i];
+
+ switch (opt->option)
+ {
+ case TALER_MERCHANT_GET_ORDERS_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_GET_ORDERS_OPTION_TIMEOUT:
+ oph->timeout = opt->details.timeout;
+ break;
+ case TALER_MERCHANT_GET_ORDERS_OPTION_SESSION_ID:
+ GNUNET_free (oph->session_id);
+ if (NULL != opt->details.session_id)
+ oph->session_id = GNUNET_strdup (opt->details.session_id);
+ break;
+ case TALER_MERCHANT_GET_ORDERS_OPTION_MIN_REFUND:
+ oph->min_refund = opt->details.min_refund;
+ oph->have_min_refund = true;
+ break;
+ case TALER_MERCHANT_GET_ORDERS_OPTION_AWAIT_REFUND_OBTAINED:
+ oph->await_refund_obtained = opt->details.await_refund_obtained;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_orders_start (
+ struct TALER_MERCHANT_GetOrdersHandle *oph,
+ TALER_MERCHANT_GetOrdersCallback cb,
+ TALER_MERCHANT_GET_ORDERS_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+ unsigned int tms;
+
+ oph->cb = cb;
+ oph->cb_cls = cb_cls;
+ tms = (unsigned int) (oph->timeout.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
+ {
+ struct GNUNET_CRYPTO_HashAsciiEncoded h_contract_s;
+ char *path;
+ char timeout_ms[32];
+
+ GNUNET_CRYPTO_hash_to_enc (&oph->h_contract.hash,
+ &h_contract_s);
+ GNUNET_snprintf (timeout_ms,
+ sizeof (timeout_ms),
+ "%u",
+ tms);
+ GNUNET_asprintf (&path,
+ "orders/%s",
+ oph->order_id);
+ oph->url = TALER_url_join (oph->base_url,
+ path,
+ "h_contract",
+ h_contract_s.encoding,
+ "session_id",
+ oph->session_id,
+ "timeout_ms",
+ (0 != tms)
+ ? timeout_ms
+ : NULL,
+ "refund",
+ oph->have_min_refund
+ ? TALER_amount2s (&oph->min_refund)
+ : NULL,
+ "await_refund_obtained",
+ oph->await_refund_obtained
+ ? "yes"
+ : NULL,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == oph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ if (0 != tms)
+ {
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT_MS,
+ (long) (tms + 100L)));
+ }
+ oph->job = GNUNET_CURL_job_add (oph->ctx,
+ eh,
+ &handle_get_order_finished,
+ oph);
+ if (NULL == oph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_orders_cancel (
+ struct TALER_MERCHANT_GetOrdersHandle *oph)
+{
+ if (NULL != oph->job)
+ {
+ GNUNET_CURL_job_cancel (oph->job);
+ oph->job = NULL;
+ }
+ GNUNET_free (oph->url);
+ GNUNET_free (oph->order_id);
+ GNUNET_free (oph->session_id);
+ GNUNET_free (oph->base_url);
+ GNUNET_free (oph);
+}
+
+
+/* end of merchant_api_get-orders-ORDER_ID-new.c */
diff --git a/src/lib/merchant_api_get-private-accounts-H_WIRE-new.c b/src/lib/merchant_api_get-private-accounts-H_WIRE-new.c
@@ -0,0 +1,238 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-private-accounts-H_WIRE-new.c
+ * @brief Implementation of the GET /private/accounts/$H_WIRE 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/get-private-accounts-H_WIRE-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /private/accounts/$H_WIRE operation.
+ */
+struct TALER_MERCHANT_GetPrivateAccountHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * Instance identifier.
+ */
+ char *instance_id;
+
+ /**
+ * Hash of the wire details identifying the account.
+ */
+ struct TALER_MerchantWireHashP h_wire;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateAccountCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_ACCOUNT_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/accounts/$H_WIRE request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateAccountHandle`
+ * @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_GetPrivateAccountHandle *gpa = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateAccountResponse agr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpa->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/accounts/$H_WIRE 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",
+ &agr.details.ok.ad.salt),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_web_url ("credit_facade_url",
+ &agr.details.ok.ad.credit_facade_url),
+ NULL),
+ TALER_JSON_spec_full_payto_uri ("payto_uri",
+ &agr.details.ok.ad.payto_uri),
+ GNUNET_JSON_spec_fixed_auto ("h_wire",
+ &agr.details.ok.ad.h_wire),
+ GNUNET_JSON_spec_bool ("active",
+ &agr.details.ok.ad.active),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ gpa->cb (gpa->cb_cls,
+ &agr);
+ TALER_MERCHANT_get_private_account_cancel (gpa);
+ return;
+ }
+ agr.hr.http_status = 0;
+ agr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ agr.hr.ec = TALER_JSON_get_error_code (json);
+ agr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ agr.hr.ec = TALER_JSON_get_error_code (json);
+ agr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ agr.hr.ec = TALER_JSON_get_error_code (json);
+ agr.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) agr.hr.ec);
+ break;
+ }
+ gpa->cb (gpa->cb_cls,
+ &agr);
+ TALER_MERCHANT_get_private_account_cancel (gpa);
+}
+
+
+struct TALER_MERCHANT_GetPrivateAccountHandle *
+TALER_MERCHANT_get_private_account_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *instance_id,
+ const struct TALER_MerchantWireHashP *h_wire)
+{
+ struct TALER_MERCHANT_GetPrivateAccountHandle *gpa;
+
+ gpa = GNUNET_new (struct TALER_MERCHANT_GetPrivateAccountHandle);
+ gpa->ctx = ctx;
+ gpa->base_url = GNUNET_strdup (url);
+ gpa->instance_id = GNUNET_strdup (instance_id);
+ gpa->h_wire = *h_wire;
+ return gpa;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_account_start (
+ struct TALER_MERCHANT_GetPrivateAccountHandle *gpa,
+ TALER_MERCHANT_GetPrivateAccountCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_ACCOUNT_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpa->cb = cb;
+ gpa->cb_cls = cb_cls;
+ {
+ char w_str[sizeof (gpa->h_wire) * 2];
+ char *path;
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (&gpa->h_wire,
+ sizeof (gpa->h_wire),
+ w_str,
+ sizeof (w_str));
+ *end = '\0';
+ GNUNET_asprintf (&path,
+ "private/accounts/%s",
+ w_str);
+ gpa->url = TALER_url_join (gpa->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == gpa->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpa->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpa->job = GNUNET_CURL_job_add (gpa->ctx,
+ eh,
+ &handle_get_account_finished,
+ gpa);
+ if (NULL == gpa->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_account_cancel (
+ struct TALER_MERCHANT_GetPrivateAccountHandle *gpa)
+{
+ if (NULL != gpa->job)
+ {
+ GNUNET_CURL_job_cancel (gpa->job);
+ gpa->job = NULL;
+ }
+ GNUNET_free (gpa->url);
+ GNUNET_free (gpa->instance_id);
+ GNUNET_free (gpa->base_url);
+ GNUNET_free (gpa);
+}
+
+
+/* end of merchant_api_get-private-accounts-H_WIRE-new.c */
diff --git a/src/lib/merchant_api_get-private-accounts-new.c b/src/lib/merchant_api_get-private-accounts-new.c
@@ -0,0 +1,264 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-private-accounts-new.c
+ * @brief Implementation of the GET /private/accounts 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/get-private-accounts-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+/**
+ * Maximum number of accounts permitted.
+ */
+#define MAX_ACCOUNTS 1024
+
+
+/**
+ * Handle for a GET /private/accounts operation.
+ */
+struct TALER_MERCHANT_GetPrivateAccountsHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateAccountsCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_ACCOUNTS_RESULT_CLOSURE *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 gah operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_accounts (const json_t *ia,
+ struct TALER_MERCHANT_GetPrivateAccountsResponse *tgr,
+ struct TALER_MERCHANT_GetPrivateAccountsHandle *gah)
+{
+ unsigned int accounts_len = (unsigned int) json_array_size (ia);
+
+ if ( (json_array_size (ia) != (size_t) accounts_len) ||
+ (accounts_len > MAX_ACCOUNTS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_GetPrivateAccountsAccountEntry accounts[
+ GNUNET_NZL (accounts_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_GetPrivateAccountsAccountEntry *ae =
+ &accounts[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_full_payto_uri ("payto_uri",
+ &ae->payto_uri),
+ GNUNET_JSON_spec_fixed_auto ("h_wire",
+ &ae->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 = accounts_len;
+ tgr->details.ok.accounts = accounts;
+ gah->cb (gah->cb_cls,
+ tgr);
+ gah->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/accounts request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateAccountsHandle`
+ * @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_GetPrivateAccountsHandle *gah = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateAccountsResponse tgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gah->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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,
+ gah))
+ {
+ TALER_MERCHANT_get_private_accounts_cancel (gah);
+ 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);
+ break;
+ default:
+ 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;
+ }
+ gah->cb (gah->cb_cls,
+ &tgr);
+ TALER_MERCHANT_get_private_accounts_cancel (gah);
+}
+
+
+struct TALER_MERCHANT_GetPrivateAccountsHandle *
+TALER_MERCHANT_get_private_accounts_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateAccountsHandle *gah;
+
+ gah = GNUNET_new (struct TALER_MERCHANT_GetPrivateAccountsHandle);
+ gah->ctx = ctx;
+ gah->base_url = GNUNET_strdup (url);
+ return gah;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_accounts_start (
+ struct TALER_MERCHANT_GetPrivateAccountsHandle *gah,
+ TALER_MERCHANT_GetPrivateAccountsCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_ACCOUNTS_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gah->cb = cb;
+ gah->cb_cls = cb_cls;
+ gah->url = TALER_url_join (gah->base_url,
+ "private/accounts",
+ NULL);
+ if (NULL == gah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gah->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gah->job = GNUNET_CURL_job_add (gah->ctx,
+ eh,
+ &handle_get_accounts_finished,
+ gah);
+ if (NULL == gah->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_accounts_cancel (
+ struct TALER_MERCHANT_GetPrivateAccountsHandle *gah)
+{
+ if (NULL != gah->job)
+ {
+ GNUNET_CURL_job_cancel (gah->job);
+ gah->job = NULL;
+ }
+ GNUNET_free (gah->url);
+ GNUNET_free (gah->base_url);
+ GNUNET_free (gah);
+}
+
+
+/* end of merchant_api_get-private-accounts-new.c */
diff --git a/src/lib/merchant_api_get-private-donau-new.c b/src/lib/merchant_api_get-private-donau-new.c
@@ -0,0 +1,302 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024-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-private-donau-new.c
+ * @brief Implementation of the GET /private/donau 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/get-private-donau-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum number of Donau instances permitted.
+ */
+#define MAX_DONAU_INSTANCES 1024
+
+
+/**
+ * Handle for a GET /private/donau operation.
+ */
+struct TALER_MERCHANT_GetPrivateDonauHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateDonauCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_DONAU_RESULT_CLOSURE *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[in] dgr partially filled response
+ * @param gpdh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_donau_instances (const json_t *ia,
+ struct TALER_MERCHANT_GetPrivateDonauResponse *dgr,
+ struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh)
+{
+ unsigned int instances_len = (unsigned int) json_array_size (ia);
+ struct DONAU_Keys *donau_keys_ptr = NULL;
+
+ if ( (json_array_size (ia) != (size_t) instances_len) ||
+ (instances_len > MAX_DONAU_INSTANCES) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_GetPrivateDonauDonauInstanceEntry instances[
+ GNUNET_NZL (instances_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_GetPrivateDonauDonauInstanceEntry *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;
+ }
+ }
+ dgr->details.ok.donau_instances_length = instances_len;
+ dgr->details.ok.donau_instances = instances;
+ gpdh->cb (gpdh->cb_cls,
+ dgr);
+ gpdh->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 GET /private/donau request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateDonauHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_donau_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateDonauResponse dgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpdh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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))
+ {
+ dgr.hr.http_status = 0;
+ dgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_donau_instances (donau_instances,
+ &dgr,
+ gpdh))
+ {
+ TALER_MERCHANT_get_private_donau_cancel (gpdh);
+ return;
+ }
+ dgr.hr.http_status = 0;
+ dgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ case MHD_HTTP_NOT_FOUND:
+ dgr.hr.ec = TALER_JSON_get_error_code (json);
+ dgr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ dgr.hr.ec = TALER_JSON_get_error_code (json);
+ dgr.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) dgr.hr.ec);
+ break;
+ }
+ gpdh->cb (gpdh->cb_cls,
+ &dgr);
+ TALER_MERCHANT_get_private_donau_cancel (gpdh);
+}
+
+
+struct TALER_MERCHANT_GetPrivateDonauHandle *
+TALER_MERCHANT_get_private_donau_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh;
+
+ gpdh = GNUNET_new (struct TALER_MERCHANT_GetPrivateDonauHandle);
+ gpdh->ctx = ctx;
+ gpdh->base_url = GNUNET_strdup (url);
+ return gpdh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_donau_start (
+ struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh,
+ TALER_MERCHANT_GetPrivateDonauCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_DONAU_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpdh->cb = cb;
+ gpdh->cb_cls = cb_cls;
+ gpdh->url = TALER_url_join (gpdh->base_url,
+ "private/donau",
+ NULL);
+ if (NULL == gpdh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpdh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpdh->job = GNUNET_CURL_job_add (gpdh->ctx,
+ eh,
+ &handle_get_donau_finished,
+ gpdh);
+ if (NULL == gpdh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_donau_cancel (
+ struct TALER_MERCHANT_GetPrivateDonauHandle *gpdh)
+{
+ if (NULL != gpdh->job)
+ {
+ GNUNET_CURL_job_cancel (gpdh->job);
+ gpdh->job = NULL;
+ }
+ GNUNET_free (gpdh->url);
+ GNUNET_free (gpdh->base_url);
+ GNUNET_free (gpdh);
+}
+
+
+/* end of merchant_api_get-private-donau-new.c */
diff --git a/src/lib/merchant_api_get-private-kyc-new.c b/src/lib/merchant_api_get-private-kyc-new.c
@@ -0,0 +1,541 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023-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-private-kyc-new.c
+ * @brief Implementation of the GET /private/kyc 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/get-private-kyc-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum length of the KYC arrays supported.
+ */
+#define MAX_KYC 1024
+
+
+/**
+ * Handle for a GET /private/kyc operation.
+ */
+struct TALER_MERCHANT_GetPrivateKycHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateKycCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_KYC_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Hash of the wire account to filter by, or NULL.
+ */
+ const struct TALER_MerchantWireHashP *h_wire;
+
+ /**
+ * Storage for the h_wire value (if set).
+ */
+ struct TALER_MerchantWireHashP h_wire_val;
+
+ /**
+ * True if @e h_wire was set.
+ */
+ bool have_h_wire;
+
+ /**
+ * Exchange URL filter, or NULL.
+ */
+ char *exchange_url;
+
+ /**
+ * Long-poll target.
+ */
+ enum TALER_EXCHANGE_KycLongPollTarget lpt;
+
+ /**
+ * Long polling timeout.
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * Instance ID for management mode, or NULL.
+ */
+ char *instance_id;
+};
+
+
+/**
+ * Parse @a jkyc 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_GetPrivateKycHandle *kyc,
+ struct TALER_MERCHANT_GetPrivateKycResponse *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);
+ return GNUNET_SYSERR;
+ }
+ num_limits += json_array_size (jlimits);
+ num_kycauths += json_array_size (jkycauths);
+ }
+
+ {
+ struct TALER_MERCHANT_GetPrivateKycRedirectDetail 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_GetPrivateKycRedirectDetail *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);
+ 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);
+ 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);
+ return GNUNET_SYSERR;
+ }
+ pos_kycauths++;
+ }
+ }
+ kr->details.ok.kycs = kycs;
+ kr->details.ok.kycs_length = num_kycs;
+ kyc->cb (kyc->cb_cls,
+ kr);
+ kyc->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/kyc request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateKycHandle`
+ * @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_GetPrivateKycHandle *kyc = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateKycResponse kr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ kyc->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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_get_private_kyc_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);
+ break;
+ case MHD_HTTP_SERVICE_UNAVAILABLE:
+ break;
+ default:
+ 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_get_private_kyc_cancel (kyc);
+}
+
+
+struct TALER_MERCHANT_GetPrivateKycHandle *
+TALER_MERCHANT_get_private_kyc_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateKycHandle *kyc;
+
+ kyc = GNUNET_new (struct TALER_MERCHANT_GetPrivateKycHandle);
+ kyc->ctx = ctx;
+ kyc->base_url = GNUNET_strdup (url);
+ kyc->lpt = TALER_EXCHANGE_KLPT_NONE;
+ return kyc;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_private_kyc_set_options_ (
+ struct TALER_MERCHANT_GetPrivateKycHandle *kyc,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetPrivateKycOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ const struct TALER_MERCHANT_GetPrivateKycOptionValue *opt =
+ &options[i];
+
+ switch (opt->option)
+ {
+ case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_H_WIRE:
+ if (NULL != opt->details.h_wire)
+ {
+ kyc->h_wire_val = *opt->details.h_wire;
+ kyc->h_wire = &kyc->h_wire_val;
+ kyc->have_h_wire = true;
+ }
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_EXCHANGE_URL:
+ GNUNET_free (kyc->exchange_url);
+ if (NULL != opt->details.exchange_url)
+ kyc->exchange_url = GNUNET_strdup (opt->details.exchange_url);
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_LPT:
+ kyc->lpt = opt->details.lpt;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_TIMEOUT:
+ kyc->timeout = opt->details.timeout;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_INSTANCE_ID:
+ GNUNET_free (kyc->instance_id);
+ if (NULL != opt->details.instance_id)
+ kyc->instance_id = GNUNET_strdup (opt->details.instance_id);
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_kyc_start (
+ struct TALER_MERCHANT_GetPrivateKycHandle *kyc,
+ TALER_MERCHANT_GetPrivateKycCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_KYC_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+ unsigned long long tms;
+ char timeout_ms[32];
+ char lpt_str[32];
+ char *base_path;
+
+ kyc->cb = cb;
+ kyc->cb_cls = cb_cls;
+
+ /* Build the base path depending on whether instance_id is set */
+ if (NULL != kyc->instance_id)
+ {
+ GNUNET_asprintf (&base_path,
+ "%smanagement/instances/%s/",
+ kyc->base_url,
+ kyc->instance_id);
+ }
+ else
+ {
+ GNUNET_asprintf (&base_path,
+ "%sprivate/",
+ kyc->base_url);
+ }
+
+ GNUNET_snprintf (lpt_str,
+ sizeof (lpt_str),
+ "%d",
+ (int) kyc->lpt);
+ tms = kyc->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 (
+ base_path,
+ "kyc",
+ "h_wire",
+ kyc->have_h_wire
+ ? GNUNET_h2s_full (&kyc->h_wire_val.hash)
+ : NULL,
+ "exchange_url",
+ kyc->exchange_url,
+ "timeout_ms",
+ GNUNET_TIME_relative_is_zero (kyc->timeout)
+ ? NULL
+ : timeout_ms,
+ "lpt",
+ TALER_EXCHANGE_KLPT_NONE == kyc->lpt
+ ? NULL
+ : lpt_str,
+ NULL);
+ GNUNET_free (base_path);
+ if (NULL == kyc->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (kyc->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ if (0 != tms)
+ {
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT_MS,
+ (long) (tms + 100L)));
+ }
+ kyc->job = GNUNET_CURL_job_add (kyc->ctx,
+ eh,
+ &handle_get_kyc_finished,
+ kyc);
+ if (NULL == kyc->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_kyc_cancel (
+ struct TALER_MERCHANT_GetPrivateKycHandle *kyc)
+{
+ if (NULL != kyc->job)
+ {
+ GNUNET_CURL_job_cancel (kyc->job);
+ kyc->job = NULL;
+ }
+ GNUNET_free (kyc->url);
+ GNUNET_free (kyc->exchange_url);
+ GNUNET_free (kyc->instance_id);
+ GNUNET_free (kyc->base_url);
+ GNUNET_free (kyc);
+}
+
+
+/* end of merchant_api_get-private-kyc-new.c */
diff --git a/src/lib/merchant_api_get-private-orders-ORDER_ID-new.c b/src/lib/merchant_api_get-private-orders-ORDER_ID-new.c
@@ -0,0 +1,573 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018-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-private-orders-ORDER_ID-new.c
+ * @brief Implementation of the GET /private/orders/$ORDER_ID 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/get-private-orders-ORDER_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.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
+
+
+/**
+ * Handle for a GET /private/orders/$ORDER_ID operation.
+ */
+struct TALER_MERCHANT_GetPrivateOrderHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateOrderCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_ORDER_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Order ID.
+ */
+ char *order_id;
+
+ /**
+ * Session ID for repurchase detection, or NULL.
+ */
+ char *session_id;
+
+ /**
+ * Long polling timeout.
+ */
+ struct GNUNET_TIME_Relative timeout;
+};
+
+
+/**
+ * Handle HTTP OK response for an unpaid order.
+ *
+ * @param oph handle for the request
+ * @param[in,out] osr HTTP response we got
+ */
+static void
+handle_unpaid (struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
+ struct TALER_MERCHANT_GetPrivateOrderResponse *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_INVALID_RESPONSE;
+ oph->cb (oph->cb_cls,
+ osr);
+ return;
+ }
+ osr->details.ok.status = TALER_MERCHANT_OSC_UNPAID;
+ oph->cb (oph->cb_cls,
+ osr);
+}
+
+
+/**
+ * Handle HTTP OK response for a claimed order.
+ *
+ * @param oph handle for the request
+ * @param[in,out] osr HTTP response we got
+ */
+static void
+handle_claimed (struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
+ struct TALER_MERCHANT_GetPrivateOrderResponse *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_INVALID_RESPONSE;
+ oph->cb (oph->cb_cls,
+ osr);
+ return;
+ }
+ osr->details.ok.status = TALER_MERCHANT_OSC_CLAIMED;
+ oph->cb (oph->cb_cls,
+ osr);
+}
+
+
+/**
+ * Handle HTTP OK response for a paid order.
+ *
+ * @param oph handle for the request
+ * @param[in,out] osr HTTP response we got
+ */
+static void
+handle_paid (struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
+ struct TALER_MERCHANT_GetPrivateOrderResponse *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),
+ 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_INVALID_RESPONSE;
+ oph->cb (oph->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_INVALID_RESPONSE;
+ oph->cb (oph->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_INVALID_RESPONSE;
+ oph->cb (oph->cb_cls,
+ osr);
+ return;
+ }
+ {
+ struct TALER_MERCHANT_WireTransfer wts[GNUNET_NZL (wts_len)];
+ struct TALER_MERCHANT_GetPrivateOrderRefundDetail 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_INVALID_RESPONSE;
+ oph->cb (oph->cb_cls,
+ osr);
+ return;
+ }
+ }
+
+ for (unsigned int i = 0; i<ref_len; i++)
+ {
+ struct TALER_MERCHANT_GetPrivateOrderRefundDetail *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_INVALID_RESPONSE;
+ oph->cb (oph->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;
+ oph->cb (oph->cb_cls,
+ osr);
+ }
+ }
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/orders/$ORDER_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateOrderHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_private_order_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetPrivateOrderHandle *oph = cls;
+ const json_t *json = response;
+ const char *order_status;
+ struct TALER_MERCHANT_GetPrivateOrderResponse osr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ oph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/orders/$ORDER_ID response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ /* see below */
+ break;
+ case MHD_HTTP_ACCEPTED:
+ oph->cb (oph->cb_cls,
+ &osr);
+ TALER_MERCHANT_get_private_order_cancel (oph);
+ return;
+ case MHD_HTTP_UNAUTHORIZED:
+ osr.hr.ec = TALER_JSON_get_error_code (json);
+ osr.hr.hint = TALER_JSON_get_error_hint (json);
+ oph->cb (oph->cb_cls,
+ &osr);
+ TALER_MERCHANT_get_private_order_cancel (oph);
+ return;
+ case MHD_HTTP_NOT_FOUND:
+ osr.hr.ec = TALER_JSON_get_error_code (json);
+ osr.hr.hint = TALER_JSON_get_error_hint (json);
+ oph->cb (oph->cb_cls,
+ &osr);
+ TALER_MERCHANT_get_private_order_cancel (oph);
+ return;
+ case MHD_HTTP_GATEWAY_TIMEOUT:
+ osr.hr.ec = TALER_JSON_get_error_code (json);
+ osr.hr.hint = TALER_JSON_get_error_hint (json);
+ oph->cb (oph->cb_cls,
+ &osr);
+ TALER_MERCHANT_get_private_order_cancel (oph);
+ 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 order status failed with HTTP status code %u/%d\n",
+ (unsigned int) response_code,
+ (int) osr.hr.ec);
+ GNUNET_break_op (0);
+ oph->cb (oph->cb_cls,
+ &osr);
+ TALER_MERCHANT_get_private_order_cancel (oph);
+ 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_INVALID_RESPONSE;
+ oph->cb (oph->cb_cls,
+ &osr);
+ TALER_MERCHANT_get_private_order_cancel (oph);
+ return;
+ }
+
+ if (0 == strcmp ("paid",
+ order_status))
+ {
+ handle_paid (oph,
+ &osr);
+ }
+ else if (0 == strcmp ("claimed",
+ order_status))
+ {
+ handle_claimed (oph,
+ &osr);
+ }
+ else if (0 == strcmp ("unpaid",
+ order_status))
+ {
+ handle_unpaid (oph,
+ &osr);
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ osr.hr.http_status = 0;
+ osr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ oph->cb (oph->cb_cls,
+ &osr);
+ }
+ TALER_MERCHANT_get_private_order_cancel (oph);
+}
+
+
+struct TALER_MERCHANT_GetPrivateOrderHandle *
+TALER_MERCHANT_get_private_order_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id)
+{
+ struct TALER_MERCHANT_GetPrivateOrderHandle *oph;
+
+ oph = GNUNET_new (struct TALER_MERCHANT_GetPrivateOrderHandle);
+ oph->ctx = ctx;
+ oph->base_url = GNUNET_strdup (url);
+ oph->order_id = GNUNET_strdup (order_id);
+ return oph;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_private_order_set_options_ (
+ struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetPrivateOrderOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ const struct TALER_MERCHANT_GetPrivateOrderOptionValue *opt =
+ &options[i];
+
+ switch (opt->option)
+ {
+ case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_SESSION_ID:
+ GNUNET_free (oph->session_id);
+ if (NULL != opt->details.session_id)
+ oph->session_id = GNUNET_strdup (opt->details.session_id);
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDER_OPTION_TIMEOUT:
+ oph->timeout = opt->details.timeout;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_order_start (
+ struct TALER_MERCHANT_GetPrivateOrderHandle *oph,
+ TALER_MERCHANT_GetPrivateOrderCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_ORDER_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+ unsigned int tms;
+
+ oph->cb = cb;
+ oph->cb_cls = cb_cls;
+ tms = (unsigned int) (oph->timeout.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
+ {
+ char *path;
+ char timeout_ms[32];
+
+ GNUNET_snprintf (timeout_ms,
+ sizeof (timeout_ms),
+ "%u",
+ tms);
+ GNUNET_asprintf (&path,
+ "private/orders/%s",
+ oph->order_id);
+ oph->url = TALER_url_join (oph->base_url,
+ path,
+ "session_id",
+ oph->session_id,
+ "timeout_ms",
+ (0 != tms)
+ ? timeout_ms
+ : NULL,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == oph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ if (0 != tms)
+ {
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT_MS,
+ (long) (tms + 100L)));
+ }
+ oph->job = GNUNET_CURL_job_add (oph->ctx,
+ eh,
+ &handle_get_private_order_finished,
+ oph);
+ if (NULL == oph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_order_cancel (
+ struct TALER_MERCHANT_GetPrivateOrderHandle *oph)
+{
+ if (NULL != oph->job)
+ {
+ GNUNET_CURL_job_cancel (oph->job);
+ oph->job = NULL;
+ }
+ GNUNET_free (oph->url);
+ GNUNET_free (oph->order_id);
+ GNUNET_free (oph->session_id);
+ GNUNET_free (oph->base_url);
+ GNUNET_free (oph);
+}
+
+
+/* end of merchant_api_get-private-orders-ORDER_ID-new.c */
diff --git a/src/lib/merchant_api_get-private-orders-new.c b/src/lib/merchant_api_get-private-orders-new.c
@@ -0,0 +1,522 @@
+/*
+ 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-private-orders-new.c
+ * @brief Implementation of the GET /private/orders 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/get-private-orders-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum number of orders we return.
+ */
+#define MAX_ORDERS 1024
+
+
+/**
+ * Handle for a GET /private/orders operation.
+ */
+struct TALER_MERCHANT_GetPrivateOrdersHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateOrdersCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_ORDERS_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Paid filter.
+ */
+ enum TALER_EXCHANGE_YesNoAll paid;
+
+ /**
+ * Refunded filter.
+ */
+ enum TALER_EXCHANGE_YesNoAll refunded;
+
+ /**
+ * Wired filter.
+ */
+ enum TALER_EXCHANGE_YesNoAll wired;
+
+ /**
+ * Date threshold for filtering.
+ */
+ struct GNUNET_TIME_Timestamp date;
+
+ /**
+ * Starting row for pagination.
+ */
+ uint64_t offset;
+
+ /**
+ * Limit on number of results.
+ */
+ int64_t limit;
+
+ /**
+ * Long polling timeout.
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * Session ID filter, or NULL.
+ */
+ char *session_id;
+
+ /**
+ * Fulfillment URL filter, or NULL.
+ */
+ char *fulfillment_url;
+
+ /**
+ * Summary text filter, or NULL.
+ */
+ char *summary_filter;
+
+ /**
+ * True if offset was explicitly set.
+ */
+ bool have_offset;
+
+ /**
+ * True if date was explicitly set.
+ */
+ bool have_date;
+};
+
+
+/**
+ * Parse order information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with order data
+ * @param[in,out] ogr response to fill
+ * @param oph operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_orders (const json_t *ia,
+ struct TALER_MERCHANT_GetPrivateOrdersResponse *ogr,
+ struct TALER_MERCHANT_GetPrivateOrdersHandle *oph)
+{
+ 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_GetPrivateOrdersOrderEntry 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_GetPrivateOrdersOrderEntry *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;
+ oph->cb (oph->cb_cls,
+ ogr);
+ oph->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/orders request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateOrdersHandle`
+ * @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_GetPrivateOrdersHandle *oph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateOrdersResponse ogr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ oph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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,
+ oph))
+ {
+ TALER_MERCHANT_get_private_orders_cancel (oph);
+ 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);
+ 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:
+ 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;
+ }
+ oph->cb (oph->cb_cls,
+ &ogr);
+ TALER_MERCHANT_get_private_orders_cancel (oph);
+}
+
+
+struct TALER_MERCHANT_GetPrivateOrdersHandle *
+TALER_MERCHANT_get_private_orders_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateOrdersHandle *oph;
+
+ oph = GNUNET_new (struct TALER_MERCHANT_GetPrivateOrdersHandle);
+ oph->ctx = ctx;
+ oph->base_url = GNUNET_strdup (url);
+ oph->paid = TALER_EXCHANGE_YNA_ALL;
+ oph->refunded = TALER_EXCHANGE_YNA_ALL;
+ oph->wired = TALER_EXCHANGE_YNA_ALL;
+ oph->date = GNUNET_TIME_UNIT_FOREVER_TS;
+ oph->offset = UINT64_MAX;
+ oph->limit = -20;
+ return oph;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_private_orders_set_options_ (
+ struct TALER_MERCHANT_GetPrivateOrdersHandle *oph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetPrivateOrdersOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ const struct TALER_MERCHANT_GetPrivateOrdersOptionValue *opt =
+ &options[i];
+
+ switch (opt->option)
+ {
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_PAID:
+ oph->paid = opt->details.paid;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_REFUNDED:
+ oph->refunded = opt->details.refunded;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_WIRED:
+ oph->wired = opt->details.wired;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_LIMIT:
+ oph->limit = opt->details.limit;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_OFFSET:
+ oph->offset = opt->details.offset;
+ oph->have_offset = true;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_DATE:
+ oph->date = opt->details.date;
+ oph->have_date = true;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_TIMEOUT:
+ oph->timeout = opt->details.timeout;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_SESSION_ID:
+ GNUNET_free (oph->session_id);
+ if (NULL != opt->details.session_id)
+ oph->session_id = GNUNET_strdup (opt->details.session_id);
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_FULFILLMENT_URL:
+ GNUNET_free (oph->fulfillment_url);
+ if (NULL != opt->details.fulfillment_url)
+ oph->fulfillment_url = GNUNET_strdup (opt->details.fulfillment_url);
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_ORDERS_OPTION_SUMMARY_FILTER:
+ GNUNET_free (oph->summary_filter);
+ if (NULL != opt->details.summary_filter)
+ oph->summary_filter = GNUNET_strdup (opt->details.summary_filter);
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_orders_start (
+ struct TALER_MERCHANT_GetPrivateOrdersHandle *oph,
+ TALER_MERCHANT_GetPrivateOrdersCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_ORDERS_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+ unsigned int tms;
+
+ oph->cb = cb;
+ oph->cb_cls = cb_cls;
+ tms = (unsigned int) (oph->timeout.rel_value_us
+ / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
+ {
+ char dstr[30];
+ char *fec = NULL;
+ char *sid = NULL;
+ char *sfilt = 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) oph->limit);
+ GNUNET_snprintf (cbuf,
+ sizeof (cbuf),
+ "%llu",
+ (unsigned long long) oph->offset);
+ if (NULL != oph->session_id)
+ (void) GNUNET_STRINGS_urlencode (strlen (oph->session_id),
+ oph->session_id,
+ &sid);
+ if (NULL != oph->fulfillment_url)
+ (void) GNUNET_STRINGS_urlencode (strlen (oph->fulfillment_url),
+ oph->fulfillment_url,
+ &fec);
+ if (NULL != oph->summary_filter)
+ (void) GNUNET_STRINGS_urlencode (strlen (oph->summary_filter),
+ oph->summary_filter,
+ &sfilt);
+ GNUNET_snprintf (dstr,
+ sizeof (dstr),
+ "%llu",
+ (unsigned long long) GNUNET_TIME_timestamp_to_s (
+ oph->date));
+ if (oph->limit > 0)
+ {
+ have_date = oph->have_date
+ && ! GNUNET_TIME_absolute_is_zero (oph->date.abs_time);
+ have_srow = oph->have_offset
+ && (0 != oph->offset);
+ }
+ else
+ {
+ have_date = oph->have_date
+ && ! GNUNET_TIME_absolute_is_never (oph->date.abs_time);
+ have_srow = oph->have_offset
+ && (UINT64_MAX != oph->offset);
+ }
+ oph->url = TALER_url_join (oph->base_url,
+ "private/orders",
+ "paid",
+ (TALER_EXCHANGE_YNA_ALL != oph->paid)
+ ? TALER_yna_to_string (oph->paid)
+ : NULL,
+ "refunded",
+ (TALER_EXCHANGE_YNA_ALL != oph->refunded)
+ ? TALER_yna_to_string (oph->refunded)
+ : NULL,
+ "wired",
+ (TALER_EXCHANGE_YNA_ALL != oph->wired)
+ ? TALER_yna_to_string (oph->wired)
+ : NULL,
+ "date_s",
+ have_date
+ ? dstr
+ : NULL,
+ "start",
+ have_srow
+ ? cbuf
+ : NULL,
+ "delta",
+ (-20 != oph->limit)
+ ? dbuf
+ : NULL,
+ "timeout_ms",
+ (0 != tms)
+ ? tbuf
+ : NULL,
+ "session_id",
+ sid,
+ "fulfillment_url",
+ fec,
+ "summary_filter",
+ sfilt,
+ NULL);
+ GNUNET_free (sid);
+ GNUNET_free (fec);
+ GNUNET_free (sfilt);
+ }
+ if (NULL == oph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ if (0 != tms)
+ {
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT_MS,
+ (long) (tms + 100L)));
+ }
+ oph->job = GNUNET_CURL_job_add (oph->ctx,
+ eh,
+ &handle_get_orders_finished,
+ oph);
+ if (NULL == oph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_orders_cancel (
+ struct TALER_MERCHANT_GetPrivateOrdersHandle *oph)
+{
+ if (NULL != oph->job)
+ {
+ GNUNET_CURL_job_cancel (oph->job);
+ oph->job = NULL;
+ }
+ GNUNET_free (oph->url);
+ GNUNET_free (oph->session_id);
+ GNUNET_free (oph->fulfillment_url);
+ GNUNET_free (oph->summary_filter);
+ GNUNET_free (oph->base_url);
+ GNUNET_free (oph);
+}
+
+
+/* end of merchant_api_get-private-orders-new.c */
diff --git a/src/lib/merchant_api_get-private-otp-devices-DEVICE_ID-new.c b/src/lib/merchant_api_get-private-otp-devices-DEVICE_ID-new.c
@@ -0,0 +1,231 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-private-otp-devices-DEVICE_ID-new.c
+ * @brief Implementation of the GET /private/otp-devices/$DEVICE_ID 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/get-private-otp-devices-DEVICE_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /private/otp-devices/$DEVICE_ID operation.
+ */
+struct TALER_MERCHANT_GetPrivateOtpDeviceHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * OTP device identifier.
+ */
+ char *otp_device_id;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateOtpDeviceCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_OTP_DEVICE_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/otp-devices/$DEVICE_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateOtpDeviceHandle`
+ * @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_GetPrivateOtpDeviceHandle *god = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateOtpDeviceResponse ogr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ god->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/otp-devices/$DEVICE_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",
+ &ogr.details.ok.otp_device_description),
+ GNUNET_JSON_spec_uint32 ("otp_algorithm",
+ &alg32),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint64 ("otp_ctr",
+ &ogr.details.ok.otp_ctr),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint64 ("otp_timestamp",
+ &ogr.details.ok.otp_timestamp_s),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("otp_code",
+ &ogr.details.ok.otp_code),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ ogr.details.ok.otp_alg =
+ (enum TALER_MerchantConfirmationAlgorithm) alg32;
+ god->cb (god->cb_cls,
+ &ogr);
+ TALER_MERCHANT_get_private_otp_device_cancel (god);
+ 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);
+ 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:
+ 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;
+ }
+ god->cb (god->cb_cls,
+ &ogr);
+ TALER_MERCHANT_get_private_otp_device_cancel (god);
+}
+
+
+struct TALER_MERCHANT_GetPrivateOtpDeviceHandle *
+TALER_MERCHANT_get_private_otp_device_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *otp_device_id)
+{
+ struct TALER_MERCHANT_GetPrivateOtpDeviceHandle *god;
+
+ god = GNUNET_new (struct TALER_MERCHANT_GetPrivateOtpDeviceHandle);
+ god->ctx = ctx;
+ god->base_url = GNUNET_strdup (url);
+ god->otp_device_id = GNUNET_strdup (otp_device_id);
+ return god;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_otp_device_start (
+ struct TALER_MERCHANT_GetPrivateOtpDeviceHandle *god,
+ TALER_MERCHANT_GetPrivateOtpDeviceCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_OTP_DEVICE_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ god->cb = cb;
+ god->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/otp-devices/%s",
+ god->otp_device_id);
+ god->url = TALER_url_join (god->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == god->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (god->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ god->job = GNUNET_CURL_job_add (god->ctx,
+ eh,
+ &handle_get_otp_device_finished,
+ god);
+ if (NULL == god->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_otp_device_cancel (
+ struct TALER_MERCHANT_GetPrivateOtpDeviceHandle *god)
+{
+ if (NULL != god->job)
+ {
+ GNUNET_CURL_job_cancel (god->job);
+ god->job = NULL;
+ }
+ GNUNET_free (god->url);
+ GNUNET_free (god->otp_device_id);
+ GNUNET_free (god->base_url);
+ GNUNET_free (god);
+}
+
+
+/* end of merchant_api_get-private-otp-devices-DEVICE_ID-new.c */
diff --git a/src/lib/merchant_api_get-private-otp-devices-new.c b/src/lib/merchant_api_get-private-otp-devices-new.c
@@ -0,0 +1,264 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-private-otp-devices-new.c
+ * @brief Implementation of the GET /private/otp-devices 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/get-private-otp-devices-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+/**
+ * Maximum number of OTP devices we return.
+ */
+#define MAX_OTP 1024
+
+
+/**
+ * Handle for a GET /private/otp-devices operation.
+ */
+struct TALER_MERCHANT_GetPrivateOtpDevicesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateOtpDevicesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_OTP_DEVICES_RESULT_CLOSURE *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] ogr partially filled response
+ * @param gpoh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_otp_devices (const json_t *ia,
+ struct TALER_MERCHANT_GetPrivateOtpDevicesResponse *ogr,
+ struct TALER_MERCHANT_GetPrivateOtpDevicesHandle *gpoh)
+{
+ 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_GetPrivateOtpDevicesOtpDeviceEntry otp[
+ GNUNET_NZL (otp_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_GetPrivateOtpDevicesOtpDeviceEntry *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;
+ }
+ }
+ ogr->details.ok.otp_devices_length = otp_len;
+ ogr->details.ok.otp_devices = otp;
+ gpoh->cb (gpoh->cb_cls,
+ ogr);
+ gpoh->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/otp-devices request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateOtpDevicesHandle`
+ * @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_GetPrivateOtpDevicesHandle *gpoh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateOtpDevicesResponse ogr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpoh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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))
+ {
+ ogr.hr.http_status = 0;
+ ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_otp_devices (otp_devices,
+ &ogr,
+ gpoh))
+ {
+ TALER_MERCHANT_get_private_otp_devices_cancel (gpoh);
+ 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);
+ break;
+ default:
+ 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;
+ }
+ gpoh->cb (gpoh->cb_cls,
+ &ogr);
+ TALER_MERCHANT_get_private_otp_devices_cancel (gpoh);
+}
+
+
+struct TALER_MERCHANT_GetPrivateOtpDevicesHandle *
+TALER_MERCHANT_get_private_otp_devices_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateOtpDevicesHandle *gpoh;
+
+ gpoh = GNUNET_new (struct TALER_MERCHANT_GetPrivateOtpDevicesHandle);
+ gpoh->ctx = ctx;
+ gpoh->base_url = GNUNET_strdup (url);
+ return gpoh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_otp_devices_start (
+ struct TALER_MERCHANT_GetPrivateOtpDevicesHandle *gpoh,
+ TALER_MERCHANT_GetPrivateOtpDevicesCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_OTP_DEVICES_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpoh->cb = cb;
+ gpoh->cb_cls = cb_cls;
+ gpoh->url = TALER_url_join (gpoh->base_url,
+ "private/otp-devices",
+ NULL);
+ if (NULL == gpoh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpoh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpoh->job = GNUNET_CURL_job_add (gpoh->ctx,
+ eh,
+ &handle_get_otp_devices_finished,
+ gpoh);
+ if (NULL == gpoh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_otp_devices_cancel (
+ struct TALER_MERCHANT_GetPrivateOtpDevicesHandle *gpoh)
+{
+ if (NULL != gpoh->job)
+ {
+ GNUNET_CURL_job_cancel (gpoh->job);
+ gpoh->job = NULL;
+ }
+ GNUNET_free (gpoh->url);
+ GNUNET_free (gpoh->base_url);
+ GNUNET_free (gpoh);
+}
+
+
+/* end of merchant_api_get-private-otp-devices-new.c */
diff --git a/src/lib/merchant_api_get-private-products-PRODUCT_ID-new.c b/src/lib/merchant_api_get-private-products-PRODUCT_ID-new.c
@@ -0,0 +1,270 @@
+/*
+ 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-private-products-PRODUCT_ID-new.c
+ * @brief Implementation of the GET /private/products/$PRODUCT_ID 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/get-private-products-PRODUCT_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /private/products/$PRODUCT_ID operation.
+ */
+struct TALER_MERCHANT_GetPrivateProductHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * Product identifier.
+ */
+ char *product_id;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateProductCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/products/$PRODUCT_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateProductHandle`
+ * @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_GetPrivateProductHandle *gpp = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateProductResponse pgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpp->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/products/$PRODUCT_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))
+ {
+ gpp->cb (gpp->cb_cls,
+ &pgr);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_get_private_product_cancel (gpp);
+ 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);
+ 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:
+ 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;
+ }
+ gpp->cb (gpp->cb_cls,
+ &pgr);
+ TALER_MERCHANT_get_private_product_cancel (gpp);
+}
+
+
+struct TALER_MERCHANT_GetPrivateProductHandle *
+TALER_MERCHANT_get_private_product_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *product_id)
+{
+ struct TALER_MERCHANT_GetPrivateProductHandle *gpp;
+
+ gpp = GNUNET_new (struct TALER_MERCHANT_GetPrivateProductHandle);
+ gpp->ctx = ctx;
+ gpp->base_url = GNUNET_strdup (url);
+ gpp->product_id = GNUNET_strdup (product_id);
+ return gpp;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_product_start (
+ struct TALER_MERCHANT_GetPrivateProductHandle *gpp,
+ TALER_MERCHANT_GetPrivateProductCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpp->cb = cb;
+ gpp->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/products/%s",
+ gpp->product_id);
+ gpp->url = TALER_url_join (gpp->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == gpp->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpp->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpp->job = GNUNET_CURL_job_add (gpp->ctx,
+ eh,
+ &handle_get_product_finished,
+ gpp);
+ if (NULL == gpp->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_product_cancel (
+ struct TALER_MERCHANT_GetPrivateProductHandle *gpp)
+{
+ if (NULL != gpp->job)
+ {
+ GNUNET_CURL_job_cancel (gpp->job);
+ gpp->job = NULL;
+ }
+ GNUNET_free (gpp->url);
+ GNUNET_free (gpp->product_id);
+ GNUNET_free (gpp->base_url);
+ GNUNET_free (gpp);
+}
+
+
+/* end of merchant_api_get-private-products-PRODUCT_ID-new.c */
diff --git a/src/lib/merchant_api_get-private-products-new.c b/src/lib/merchant_api_get-private-products-new.c
@@ -0,0 +1,265 @@
+/*
+ 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-private-products-new.c
+ * @brief Implementation of the GET /private/products 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/get-private-products-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum number of products we return.
+ */
+#define MAX_PRODUCTS 1024
+
+
+/**
+ * Handle for a GET /private/products operation.
+ */
+struct TALER_MERCHANT_GetPrivateProductsHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateProductsCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Parse product information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with product data
+ * @param[in] pgr partially filled response
+ * @param gpph operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_products (const json_t *ia,
+ struct TALER_MERCHANT_GetPrivateProductsResponse *pgr,
+ struct TALER_MERCHANT_GetPrivateProductsHandle *gpph)
+{
+ unsigned int ies_len = (unsigned int) 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_GetPrivateProductsInventoryEntry ies[
+ GNUNET_NZL (ies_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_GetPrivateProductsInventoryEntry *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);
+ return GNUNET_SYSERR;
+ }
+ }
+ pgr->details.ok.products_length = ies_len;
+ pgr->details.ok.products = ies;
+ gpph->cb (gpph->cb_cls,
+ pgr);
+ gpph->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/products request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateProductsHandle`
+ * @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_GetPrivateProductsHandle *gpph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateProductsResponse pgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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))
+ {
+ pgr.hr.http_status = 0;
+ pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_products (products,
+ &pgr,
+ gpph))
+ {
+ TALER_MERCHANT_get_private_products_cancel (gpph);
+ 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);
+ break;
+ default:
+ 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;
+ }
+ gpph->cb (gpph->cb_cls,
+ &pgr);
+ TALER_MERCHANT_get_private_products_cancel (gpph);
+}
+
+
+struct TALER_MERCHANT_GetPrivateProductsHandle *
+TALER_MERCHANT_get_private_products_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateProductsHandle *gpph;
+
+ gpph = GNUNET_new (struct TALER_MERCHANT_GetPrivateProductsHandle);
+ gpph->ctx = ctx;
+ gpph->base_url = GNUNET_strdup (url);
+ return gpph;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_products_start (
+ struct TALER_MERCHANT_GetPrivateProductsHandle *gpph,
+ TALER_MERCHANT_GetPrivateProductsCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_PRODUCTS_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpph->cb = cb;
+ gpph->cb_cls = cb_cls;
+ gpph->url = TALER_url_join (gpph->base_url,
+ "private/products",
+ NULL);
+ if (NULL == gpph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpph->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpph->job = GNUNET_CURL_job_add (gpph->ctx,
+ eh,
+ &handle_get_products_finished,
+ gpph);
+ if (NULL == gpph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_products_cancel (
+ struct TALER_MERCHANT_GetPrivateProductsHandle *gpph)
+{
+ if (NULL != gpph->job)
+ {
+ GNUNET_CURL_job_cancel (gpph->job);
+ gpph->job = NULL;
+ }
+ GNUNET_free (gpph->url);
+ GNUNET_free (gpph->base_url);
+ GNUNET_free (gpph);
+}
+
+
+/* end of merchant_api_get-private-products-new.c */
diff --git a/src/lib/merchant_api_get-private-statistics-amount-SLUG-new.c b/src/lib/merchant_api_get-private-statistics-amount-SLUG-new.c
@@ -0,0 +1,455 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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-private-statistics-amount-SLUG-new.c
+ * @brief Implementation of the GET /private/statistics-amount/$SLUG 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/get-private-statistics-amount-SLUG-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum number of statistics entries we return.
+ */
+#define MAX_STATISTICS 1024
+
+
+/**
+ * Handle for a GET /private/statistics-amount/$SLUG operation.
+ */
+struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateStatisticsAmountCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_STATISTICS_AMOUNT_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Statistics slug.
+ */
+ char *slug;
+
+ /**
+ * Aggregation mode.
+ */
+ enum TALER_MERCHANT_StatisticsType stype;
+
+ /**
+ * Whether stype was explicitly set.
+ */
+ bool have_stype;
+};
+
+
+/**
+ * Parse interval and bucket data from the JSON response.
+ *
+ * @param json overall JSON reply
+ * @param jbuckets JSON array with bucket data
+ * @param buckets_description human-readable description for buckets
+ * @param jintervals JSON array with interval data
+ * @param intervals_description human-readable description for intervals
+ * @param sah 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_GetPrivateStatisticsAmountHandle *sah)
+{
+ 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_buckets_len > MAX_STATISTICS) ||
+ (resp_intervals_len > MAX_STATISTICS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountByBucket resp_buckets[
+ GNUNET_NZL (resp_buckets_len)];
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountByInterval 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_GetPrivateStatisticsAmountByInterval *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);
+ return GNUNET_SYSERR;
+ }
+ amounts_len = json_array_size (amounts_arr);
+ {
+ struct TALER_Amount amt_arr[GNUNET_NZL (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;
+ }
+ }
+ }
+ }
+ json_array_foreach (jbuckets, index, value) {
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountByBucket *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;
+ break;
+ }
+ amounts_len = json_array_size (amounts_arr);
+ {
+ struct TALER_Amount amt_arr[GNUNET_NZL (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_GetPrivateStatisticsAmountResponse sagr = {
+ .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,
+ };
+ sah->cb (sah->cb_cls,
+ &sagr);
+ sah->cb = NULL;
+ }
+ return ret;
+ }
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/statistics-amount/$SLUG request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle`
+ * @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_GetPrivateStatisticsAmountHandle *sah = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountResponse sagr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ sah->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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))
+ {
+ sagr.hr.http_status = 0;
+ sagr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_intervals_and_buckets_amt (json,
+ buckets,
+ buckets_description,
+ intervals,
+ intervals_description,
+ sah))
+ {
+ TALER_MERCHANT_get_private_statistics_amount_cancel (sah);
+ return;
+ }
+ sagr.hr.http_status = 0;
+ sagr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ sagr.hr.ec = TALER_JSON_get_error_code (json);
+ sagr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ sagr.hr.ec = TALER_JSON_get_error_code (json);
+ sagr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ sagr.hr.ec = TALER_JSON_get_error_code (json);
+ sagr.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) sagr.hr.ec);
+ break;
+ }
+ sah->cb (sah->cb_cls,
+ &sagr);
+ TALER_MERCHANT_get_private_statistics_amount_cancel (sah);
+}
+
+
+struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle *
+TALER_MERCHANT_get_private_statistics_amount_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *slug)
+{
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle *sah;
+
+ sah = GNUNET_new (struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle);
+ sah->ctx = ctx;
+ sah->base_url = GNUNET_strdup (url);
+ sah->slug = GNUNET_strdup (slug);
+ sah->stype = TALER_MERCHANT_STATISTICS_ALL;
+ return sah;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_private_statistics_amount_set_options_ (
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle *sah,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetPrivateStatisticsAmountOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ const struct TALER_MERCHANT_GetPrivateStatisticsAmountOptionValue *opt =
+ &options[i];
+
+ switch (opt->option)
+ {
+ case TALER_MERCHANT_GET_PRIVATE_STATISTICS_AMOUNT_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_GET_PRIVATE_STATISTICS_AMOUNT_OPTION_TYPE:
+ sah->stype = opt->details.type;
+ sah->have_stype = true;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_statistics_amount_start (
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle *sah,
+ TALER_MERCHANT_GetPrivateStatisticsAmountCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_STATISTICS_AMOUNT_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ sah->cb = cb;
+ sah->cb_cls = cb_cls;
+ {
+ const char *filter = NULL;
+ char *path;
+
+ switch (sah->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",
+ sah->slug);
+ sah->url = TALER_url_join (sah->base_url,
+ path,
+ "by",
+ filter,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == sah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (sah->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ sah->job = GNUNET_CURL_job_add (sah->ctx,
+ eh,
+ &handle_get_statistics_amount_finished,
+ sah);
+ if (NULL == sah->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_statistics_amount_cancel (
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle *sah)
+{
+ if (NULL != sah->job)
+ {
+ GNUNET_CURL_job_cancel (sah->job);
+ sah->job = NULL;
+ }
+ GNUNET_free (sah->url);
+ GNUNET_free (sah->base_url);
+ GNUNET_free (sah->slug);
+ GNUNET_free (sah);
+}
+
+
+/* end of merchant_api_get-private-statistics-amount-SLUG-new.c */
diff --git a/src/lib/merchant_api_get-private-statistics-counter-SLUG-new.c b/src/lib/merchant_api_get-private-statistics-counter-SLUG-new.c
@@ -0,0 +1,400 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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-private-statistics-counter-SLUG-new.c
+ * @brief Implementation of the GET /private/statistics-counter/$SLUG 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/get-private-statistics-counter-SLUG-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+/**
+ * Maximum number of statistics we return.
+ */
+#define MAX_STATISTICS 1024
+
+
+/**
+ * Handle for a GET /private/statistics-counter/$SLUG operation.
+ */
+struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateStatisticsCounterCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_STATISTICS_COUNTER_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Statistics slug to retrieve.
+ */
+ char *slug;
+
+ /**
+ * Aggregation filter type, or -1 if not set.
+ */
+ enum TALER_MERCHANT_StatisticsType stype;
+
+ /**
+ * Whether the type option was set.
+ */
+ bool type_set;
+};
+
+
+/**
+ * Parse interval and bucket information from the JSON response.
+ *
+ * @param json overall JSON reply
+ * @param jbuckets JSON array with bucket data
+ * @param buckets_description human-readable description for the buckets
+ * @param jintervals JSON array with interval data
+ * @param intervals_description human-readable description for the intervals
+ * @param sch 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_GetPrivateStatisticsCounterHandle *sch)
+{
+ unsigned int resp_buckets_len = (unsigned int) json_array_size (jbuckets);
+ unsigned int resp_intervals_len = (unsigned int) json_array_size (jintervals);
+
+ if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) ||
+ (json_array_size (jintervals) != (size_t) resp_intervals_len) ||
+ (resp_buckets_len > MAX_STATISTICS) ||
+ (resp_intervals_len > MAX_STATISTICS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterByBucket resp_buckets[
+ GNUNET_NZL (resp_buckets_len)];
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterByInterval resp_intervals[
+ GNUNET_NZL (resp_intervals_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (jintervals, index, value) {
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterByInterval *ji
+ = &resp_intervals[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &ji->start_time),
+ GNUNET_JSON_spec_uint64 ("cumulative_counter",
+ &ji->cumulative_counter),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ json_array_foreach (jbuckets, index, value) {
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterByBucket *jb
+ = &resp_buckets[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &jb->start_time),
+ GNUNET_JSON_spec_timestamp ("end_time",
+ &jb->end_time),
+ GNUNET_JSON_spec_string ("range",
+ &jb->range),
+ GNUNET_JSON_spec_uint64 ("cumulative_counter",
+ &jb->cumulative_counter),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ {
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterResponse scgr = {
+ .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,
+ };
+ sch->cb (sch->cb_cls,
+ &scgr);
+ sch->cb = NULL;
+ }
+ return GNUNET_OK;
+ }
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/statistics-counter/$SLUG request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle`
+ * @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_GetPrivateStatisticsCounterHandle *sch = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterResponse res = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ sch->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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 = 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 (json,
+ buckets,
+ buckets_description,
+ intervals,
+ intervals_description,
+ sch))
+ {
+ TALER_MERCHANT_get_private_statistics_counter_cancel (sch);
+ 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);
+ 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:
+ 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;
+ }
+ sch->cb (sch->cb_cls,
+ &res);
+ TALER_MERCHANT_get_private_statistics_counter_cancel (sch);
+}
+
+
+struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle *
+TALER_MERCHANT_get_private_statistics_counter_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *slug)
+{
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle *sch;
+
+ sch = GNUNET_new (struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle);
+ sch->ctx = ctx;
+ sch->base_url = GNUNET_strdup (url);
+ sch->slug = GNUNET_strdup (slug);
+ sch->type_set = false;
+ return sch;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_private_statistics_counter_set_options_ (
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle *sch,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetPrivateStatisticsCounterOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_GET_PRIVATE_STATISTICS_COUNTER_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_GET_PRIVATE_STATISTICS_COUNTER_OPTION_TYPE:
+ sch->stype = options[i].details.type;
+ sch->type_set = true;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_statistics_counter_start (
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle *sch,
+ TALER_MERCHANT_GetPrivateStatisticsCounterCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_STATISTICS_COUNTER_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ sch->cb = cb;
+ sch->cb_cls = cb_cls;
+ {
+ const char *filter = NULL;
+ char *path;
+
+ if (sch->type_set)
+ {
+ switch (sch->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",
+ sch->slug);
+ sch->url = TALER_url_join (sch->base_url,
+ path,
+ "by",
+ filter,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == sch->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (sch->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ sch->job = GNUNET_CURL_job_add (sch->ctx,
+ eh,
+ &handle_get_statistics_counter_finished,
+ sch);
+ if (NULL == sch->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_statistics_counter_cancel (
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle *sch)
+{
+ if (NULL != sch->job)
+ {
+ GNUNET_CURL_job_cancel (sch->job);
+ sch->job = NULL;
+ }
+ GNUNET_free (sch->slug);
+ GNUNET_free (sch->url);
+ GNUNET_free (sch->base_url);
+ GNUNET_free (sch);
+}
+
+
+/* end of merchant_api_get-private-statistics-counter-SLUG-new.c */
diff --git a/src/lib/merchant_api_get-private-templates-TEMPLATE_ID-new.c b/src/lib/merchant_api_get-private-templates-TEMPLATE_ID-new.c
@@ -0,0 +1,222 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-private-templates-TEMPLATE_ID-new.c
+ * @brief Implementation of the GET /private/templates/$TEMPLATE_ID 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/get-private-templates-TEMPLATE_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /private/templates/$TEMPLATE_ID operation.
+ */
+struct TALER_MERCHANT_GetPrivateTemplateHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * Template identifier.
+ */
+ char *template_id;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateTemplateCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_TEMPLATE_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/templates/$TEMPLATE_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateTemplateHandle`
+ * @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_GetPrivateTemplateHandle *gpt = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateTemplateResponse tgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpt->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/templates/$TEMPLATE_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;
+ gpt->cb (gpt->cb_cls,
+ &tgr);
+ TALER_MERCHANT_get_private_template_cancel (gpt);
+ 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);
+ 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:
+ 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;
+ }
+ gpt->cb (gpt->cb_cls,
+ &tgr);
+ TALER_MERCHANT_get_private_template_cancel (gpt);
+}
+
+
+struct TALER_MERCHANT_GetPrivateTemplateHandle *
+TALER_MERCHANT_get_private_template_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *template_id)
+{
+ struct TALER_MERCHANT_GetPrivateTemplateHandle *gpt;
+
+ gpt = GNUNET_new (struct TALER_MERCHANT_GetPrivateTemplateHandle);
+ gpt->ctx = ctx;
+ gpt->base_url = GNUNET_strdup (url);
+ gpt->template_id = GNUNET_strdup (template_id);
+ return gpt;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_template_start (
+ struct TALER_MERCHANT_GetPrivateTemplateHandle *gpt,
+ TALER_MERCHANT_GetPrivateTemplateCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_TEMPLATE_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpt->cb = cb;
+ gpt->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/templates/%s",
+ gpt->template_id);
+ gpt->url = TALER_url_join (gpt->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == gpt->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpt->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpt->job = GNUNET_CURL_job_add (gpt->ctx,
+ eh,
+ &handle_get_template_finished,
+ gpt);
+ if (NULL == gpt->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_template_cancel (
+ struct TALER_MERCHANT_GetPrivateTemplateHandle *gpt)
+{
+ if (NULL != gpt->job)
+ {
+ GNUNET_CURL_job_cancel (gpt->job);
+ gpt->job = NULL;
+ }
+ GNUNET_free (gpt->url);
+ GNUNET_free (gpt->template_id);
+ GNUNET_free (gpt->base_url);
+ GNUNET_free (gpt);
+}
+
+
+/* end of merchant_api_get-private-templates-TEMPLATE_ID-new.c */
diff --git a/src/lib/merchant_api_get-private-templates-new.c b/src/lib/merchant_api_get-private-templates-new.c
@@ -0,0 +1,265 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-private-templates-new.c
+ * @brief Implementation of the GET /private/templates 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/get-private-templates-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum number of templates we return.
+ */
+#define MAX_TEMPLATES 1024
+
+
+/**
+ * Handle for a GET /private/templates operation.
+ */
+struct TALER_MERCHANT_GetPrivateTemplatesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateTemplatesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_TEMPLATES_RESULT_CLOSURE *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 gpth operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_templates (const json_t *ia,
+ struct TALER_MERCHANT_GetPrivateTemplatesResponse *tgr,
+ struct TALER_MERCHANT_GetPrivateTemplatesHandle *gpth)
+{
+ 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_GetPrivateTemplatesTemplateEntry tmpl[
+ GNUNET_NZL (tmpl_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_GetPrivateTemplatesTemplateEntry *ie =
+ &tmpl[index];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("template_id",
+ &ie->template_id),
+ GNUNET_JSON_spec_string ("template_description",
+ &ie->template_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.templates_length = tmpl_len;
+ tgr->details.ok.templates = tmpl;
+ gpth->cb (gpth->cb_cls,
+ tgr);
+ gpth->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/templates request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateTemplatesHandle`
+ * @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_GetPrivateTemplatesHandle *gpth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateTemplatesResponse tgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpth->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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,
+ gpth))
+ {
+ TALER_MERCHANT_get_private_templates_cancel (gpth);
+ 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);
+ break;
+ default:
+ 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;
+ }
+ gpth->cb (gpth->cb_cls,
+ &tgr);
+ TALER_MERCHANT_get_private_templates_cancel (gpth);
+}
+
+
+struct TALER_MERCHANT_GetPrivateTemplatesHandle *
+TALER_MERCHANT_get_private_templates_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateTemplatesHandle *gpth;
+
+ gpth = GNUNET_new (struct TALER_MERCHANT_GetPrivateTemplatesHandle);
+ gpth->ctx = ctx;
+ gpth->base_url = GNUNET_strdup (url);
+ return gpth;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_templates_start (
+ struct TALER_MERCHANT_GetPrivateTemplatesHandle *gpth,
+ TALER_MERCHANT_GetPrivateTemplatesCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_TEMPLATES_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpth->cb = cb;
+ gpth->cb_cls = cb_cls;
+ gpth->url = TALER_url_join (gpth->base_url,
+ "private/templates",
+ NULL);
+ if (NULL == gpth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpth->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpth->job = GNUNET_CURL_job_add (gpth->ctx,
+ eh,
+ &handle_get_templates_finished,
+ gpth);
+ if (NULL == gpth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_templates_cancel (
+ struct TALER_MERCHANT_GetPrivateTemplatesHandle *gpth)
+{
+ if (NULL != gpth->job)
+ {
+ GNUNET_CURL_job_cancel (gpth->job);
+ gpth->job = NULL;
+ }
+ GNUNET_free (gpth->url);
+ GNUNET_free (gpth->base_url);
+ GNUNET_free (gpth);
+}
+
+
+/* end of merchant_api_get-private-templates-new.c */
diff --git a/src/lib/merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG-new.c b/src/lib/merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG-new.c
@@ -0,0 +1,242 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023-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-private-tokenfamilies-TOKEN_FAMILY_SLUG-new.c
+ * @brief Implementation of the GET /private/tokenfamilies/$TOKEN_FAMILY_SLUG 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/get-private-tokenfamilies-TOKEN_FAMILY_SLUG-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /private/tokenfamilies/$TOKEN_FAMILY_SLUG operation.
+ */
+struct TALER_MERCHANT_GetPrivateTokenfamiliesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateTokenfamiliesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_TOKENFAMILIES_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Token family slug.
+ */
+ char *token_family_slug;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/tokenfamilies/$TOKEN_FAMILY_SLUG request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateTokenfamiliesHandle`
+ * @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_GetPrivateTokenfamiliesHandle *gptf = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateTokenfamiliesResponse tfgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gptf->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/tokenfamilies/$SLUG 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 ("slug",
+ &tfgr.details.ok.slug),
+ GNUNET_JSON_spec_string ("name",
+ &tfgr.details.ok.name),
+ GNUNET_JSON_spec_string ("description",
+ &tfgr.details.ok.description),
+ GNUNET_JSON_spec_object_const ("description_i18n",
+ &tfgr.details.ok.description_i18n),
+ GNUNET_JSON_spec_object_const ("extra_data",
+ &tfgr.details.ok.extra_data),
+ GNUNET_JSON_spec_timestamp ("valid_after",
+ &tfgr.details.ok.valid_after),
+ GNUNET_JSON_spec_timestamp ("valid_before",
+ &tfgr.details.ok.valid_before),
+ GNUNET_JSON_spec_relative_time ("duation",
+ &tfgr.details.ok.duration),
+ GNUNET_JSON_spec_relative_time ("validity_granularity",
+ &tfgr.details.ok.validity_granularity),
+ GNUNET_JSON_spec_relative_time ("start_offset",
+ &tfgr.details.ok.start_offset),
+ GNUNET_JSON_spec_string ("kind",
+ &tfgr.details.ok.kind),
+ GNUNET_JSON_spec_uint64 ("issued",
+ &tfgr.details.ok.issued),
+ GNUNET_JSON_spec_uint64 ("used",
+ &tfgr.details.ok.used),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ gptf->cb (gptf->cb_cls,
+ &tfgr);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_get_private_tokenfamilies_cancel (gptf);
+ return;
+ }
+ tfgr.hr.http_status = 0;
+ tfgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ tfgr.hr.ec = TALER_JSON_get_error_code (json);
+ tfgr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ tfgr.hr.ec = TALER_JSON_get_error_code (json);
+ tfgr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ /* unexpected response code */
+ tfgr.hr.ec = TALER_JSON_get_error_code (json);
+ tfgr.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) tfgr.hr.ec);
+ break;
+ }
+ gptf->cb (gptf->cb_cls,
+ &tfgr);
+ TALER_MERCHANT_get_private_tokenfamilies_cancel (gptf);
+}
+
+
+struct TALER_MERCHANT_GetPrivateTokenfamiliesHandle *
+TALER_MERCHANT_get_private_tokenfamilies_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *token_family_slug)
+{
+ struct TALER_MERCHANT_GetPrivateTokenfamiliesHandle *gptf;
+
+ gptf = GNUNET_new (struct TALER_MERCHANT_GetPrivateTokenfamiliesHandle);
+ gptf->ctx = ctx;
+ gptf->base_url = GNUNET_strdup (url);
+ gptf->token_family_slug = GNUNET_strdup (token_family_slug);
+ return gptf;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_tokenfamilies_start (
+ struct TALER_MERCHANT_GetPrivateTokenfamiliesHandle *gptf,
+ TALER_MERCHANT_GetPrivateTokenfamiliesCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_TOKENFAMILIES_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gptf->cb = cb;
+ gptf->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/tokenfamilies/%s",
+ gptf->token_family_slug);
+ gptf->url = TALER_url_join (gptf->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == gptf->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gptf->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gptf->job = GNUNET_CURL_job_add (gptf->ctx,
+ eh,
+ &handle_get_token_family_finished,
+ gptf);
+ if (NULL == gptf->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_tokenfamilies_cancel (
+ struct TALER_MERCHANT_GetPrivateTokenfamiliesHandle *gptf)
+{
+ if (NULL != gptf->job)
+ {
+ GNUNET_CURL_job_cancel (gptf->job);
+ gptf->job = NULL;
+ }
+ GNUNET_free (gptf->url);
+ GNUNET_free (gptf->base_url);
+ GNUNET_free (gptf->token_family_slug);
+ GNUNET_free (gptf);
+}
+
+
+/* end of merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG-new.c */
diff --git a/src/lib/merchant_api_get-private-transfers-new.c b/src/lib/merchant_api_get-private-transfers-new.c
@@ -0,0 +1,417 @@
+/*
+ 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-private-transfers-new.c
+ * @brief Implementation of the GET /private/transfers 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/get-private-transfers-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum number of transfers we return.
+ */
+#define MAX_TRANSFERS 1024
+
+
+/**
+ * Handle for a GET /private/transfers operation.
+ */
+struct TALER_MERCHANT_GetPrivateTransfersHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateTransfersCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_TRANSFERS_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Payto URI filter (URL-encoded), or NULL.
+ */
+ char *payto_uri_enc;
+
+ /**
+ * Before timestamp filter, or GNUNET_TIME_UNIT_FOREVER_TS.
+ */
+ struct GNUNET_TIME_Timestamp before;
+
+ /**
+ * After timestamp filter, or GNUNET_TIME_UNIT_ZERO_TS.
+ */
+ struct GNUNET_TIME_Timestamp after;
+
+ /**
+ * Limit on number of results (0 = default).
+ */
+ int64_t limit;
+
+ /**
+ * Offset for pagination.
+ */
+ uint64_t offset;
+
+ /**
+ * Expected filter.
+ */
+ enum TALER_EXCHANGE_YesNoAll expected;
+
+ /**
+ * True if offset was explicitly set.
+ */
+ bool have_offset;
+};
+
+
+/**
+ * Parse transfer information from @a transfers_arr.
+ *
+ * @param transfers_arr JSON array with transfer data
+ * @param[in,out] gtr response to fill
+ * @param gth operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_transfers (
+ const json_t *transfers_arr,
+ struct TALER_MERCHANT_GetPrivateTransfersResponse *gtr,
+ struct TALER_MERCHANT_GetPrivateTransfersHandle *gth)
+{
+ unsigned int tds_length = (unsigned int) json_array_size (transfers_arr);
+
+ if ( (json_array_size (transfers_arr) != (size_t) tds_length) ||
+ (tds_length > MAX_TRANSFERS) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MERCHANT_GetPrivateTransfersTransferData tds[
+ GNUNET_NZL (tds_length)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (transfers_arr, index, value) {
+ struct TALER_MERCHANT_GetPrivateTransfersTransferData *td = &tds[index];
+ 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 (value,
+ ispec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ gtr->details.ok.transfers_length = tds_length;
+ gtr->details.ok.transfers = tds;
+ gth->cb (gth->cb_cls,
+ gtr);
+ gth->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/transfers request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateTransfersHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_transfers_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetPrivateTransfersHandle *gth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateTransfersResponse gtr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gth->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/transfers response with status code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ 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))
+ {
+ gtr.hr.http_status = 0;
+ gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK ==
+ parse_transfers (transfers,
+ >r,
+ gth))
+ {
+ TALER_MERCHANT_get_private_transfers_cancel (gth);
+ return;
+ }
+ gtr.hr.http_status = 0;
+ gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ gtr.hr.ec = TALER_JSON_get_error_code (json);
+ gtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ gtr.hr.ec = TALER_JSON_get_error_code (json);
+ gtr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ gtr.hr.ec = TALER_JSON_get_error_code (json);
+ gtr.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) gtr.hr.ec);
+ break;
+ }
+ gth->cb (gth->cb_cls,
+ >r);
+ TALER_MERCHANT_get_private_transfers_cancel (gth);
+}
+
+
+struct TALER_MERCHANT_GetPrivateTransfersHandle *
+TALER_MERCHANT_get_private_transfers_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateTransfersHandle *gth;
+
+ gth = GNUNET_new (struct TALER_MERCHANT_GetPrivateTransfersHandle);
+ gth->ctx = ctx;
+ gth->base_url = GNUNET_strdup (url);
+ gth->expected = TALER_EXCHANGE_YNA_ALL;
+ gth->before = GNUNET_TIME_UNIT_FOREVER_TS;
+ gth->after = GNUNET_TIME_UNIT_ZERO_TS;
+ gth->offset = UINT64_MAX;
+ return gth;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_get_private_transfers_set_options_ (
+ struct TALER_MERCHANT_GetPrivateTransfersHandle *gth,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_GetPrivateTransfersOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ const struct TALER_MERCHANT_GetPrivateTransfersOptionValue *opt =
+ &options[i];
+
+ switch (opt->option)
+ {
+ case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_PAYTO_URI:
+ GNUNET_free (gth->payto_uri_enc);
+ gth->payto_uri_enc = TALER_urlencode (
+ opt->details.payto_uri.full_payto);
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_BEFORE:
+ gth->before = opt->details.before;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_AFTER:
+ gth->after = opt->details.after;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_LIMIT:
+ gth->limit = opt->details.limit;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_OFFSET:
+ gth->offset = opt->details.offset;
+ gth->have_offset = true;
+ break;
+ case TALER_MERCHANT_GET_PRIVATE_TRANSFERS_OPTION_EXPECTED:
+ gth->expected = opt->details.expected;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_transfers_start (
+ struct TALER_MERCHANT_GetPrivateTransfersHandle *gth,
+ TALER_MERCHANT_GetPrivateTransfersCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_TRANSFERS_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gth->cb = cb;
+ gth->cb_cls = cb_cls;
+ {
+ const char *expected_s = NULL;
+ char limit_s[30];
+ char offset_s[30];
+ char before_s[30];
+ char after_s[30];
+
+ if (TALER_EXCHANGE_YNA_ALL != gth->expected)
+ expected_s = TALER_yna_to_string (gth->expected);
+ GNUNET_snprintf (limit_s,
+ sizeof (limit_s),
+ "%lld",
+ (long long) gth->limit);
+ GNUNET_snprintf (offset_s,
+ sizeof (offset_s),
+ "%lld",
+ (unsigned long long) gth->offset);
+ GNUNET_snprintf (before_s,
+ sizeof (before_s),
+ "%llu",
+ (unsigned long long) GNUNET_TIME_timestamp_to_s (
+ gth->before));
+ GNUNET_snprintf (after_s,
+ sizeof (after_s),
+ "%llu",
+ (unsigned long long) GNUNET_TIME_timestamp_to_s (
+ gth->after));
+ gth->url = TALER_url_join (gth->base_url,
+ "private/transfers",
+ "payto_uri",
+ gth->payto_uri_enc,
+ "expected",
+ expected_s,
+ "limit",
+ 0 != gth->limit
+ ? limit_s
+ : NULL,
+ "offset",
+ (gth->have_offset &&
+ (UINT64_MAX != gth->offset))
+ ? offset_s
+ : NULL,
+ "before",
+ GNUNET_TIME_absolute_is_never (
+ gth->before.abs_time)
+ ? NULL
+ : before_s,
+ "after",
+ GNUNET_TIME_absolute_is_zero (
+ gth->after.abs_time)
+ ? NULL
+ : after_s,
+ NULL);
+ }
+ if (NULL == gth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gth->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gth->job = GNUNET_CURL_job_add (gth->ctx,
+ eh,
+ &handle_get_transfers_finished,
+ gth);
+ if (NULL == gth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_transfers_cancel (
+ struct TALER_MERCHANT_GetPrivateTransfersHandle *gth)
+{
+ if (NULL != gth->job)
+ {
+ GNUNET_CURL_job_cancel (gth->job);
+ gth->job = NULL;
+ }
+ GNUNET_free (gth->url);
+ GNUNET_free (gth->base_url);
+ GNUNET_free (gth->payto_uri_enc);
+ GNUNET_free (gth);
+}
+
+
+/* end of merchant_api_get-private-transfers-new.c */
diff --git a/src/lib/merchant_api_get-private-units-UNIT-new.c b/src/lib/merchant_api_get-private-units-UNIT-new.c
@@ -0,0 +1,274 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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-private-units-UNIT-new.c
+ * @brief Implementation of the GET /private/units/$UNIT 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/get-private-units-UNIT-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /private/units/$UNIT operation.
+ */
+struct TALER_MERCHANT_GetPrivateUnitHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateUnitCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Unit identifier.
+ */
+ char *unit_id;
+};
+
+
+/**
+ * Parse the JSON response into the unit entry.
+ *
+ * @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_GetPrivateUnitResponse *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;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/units/$UNIT request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateUnitHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_unit_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetPrivateUnitHandle *gpu = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateUnitResponse ugr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpu->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/units/$UNIT response with status code %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;
+ }
+ gpu->cb (gpu->cb_cls,
+ &ugr);
+ TALER_MERCHANT_get_private_unit_cancel (gpu);
+ 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;
+ default:
+ ugr.hr.ec = TALER_JSON_get_error_code (json);
+ ugr.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) ugr.hr.ec);
+ break;
+ }
+ gpu->cb (gpu->cb_cls,
+ &ugr);
+ TALER_MERCHANT_get_private_unit_cancel (gpu);
+}
+
+
+struct TALER_MERCHANT_GetPrivateUnitHandle *
+TALER_MERCHANT_get_private_unit_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *unit_id)
+{
+ struct TALER_MERCHANT_GetPrivateUnitHandle *gpu;
+
+ gpu = GNUNET_new (struct TALER_MERCHANT_GetPrivateUnitHandle);
+ gpu->ctx = ctx;
+ gpu->base_url = GNUNET_strdup (url);
+ gpu->unit_id = GNUNET_strdup (unit_id);
+ return gpu;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_unit_start (
+ struct TALER_MERCHANT_GetPrivateUnitHandle *gpu,
+ TALER_MERCHANT_GetPrivateUnitCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpu->cb = cb;
+ gpu->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/units/%s",
+ gpu->unit_id);
+ gpu->url = TALER_url_join (gpu->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == gpu->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpu->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpu->job = GNUNET_CURL_job_add (gpu->ctx,
+ eh,
+ &handle_get_unit_finished,
+ gpu);
+ if (NULL == gpu->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_unit_cancel (
+ struct TALER_MERCHANT_GetPrivateUnitHandle *gpu)
+{
+ if (NULL != gpu->job)
+ {
+ GNUNET_CURL_job_cancel (gpu->job);
+ gpu->job = NULL;
+ }
+ GNUNET_free (gpu->url);
+ GNUNET_free (gpu->base_url);
+ GNUNET_free (gpu->unit_id);
+ GNUNET_free (gpu);
+}
+
+
+/* end of merchant_api_get-private-units-UNIT-new.c */
diff --git a/src/lib/merchant_api_get-private-units-new.c b/src/lib/merchant_api_get-private-units-new.c
@@ -0,0 +1,345 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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-private-units-new.c
+ * @brief Implementation of the GET /private/units 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/get-private-units-new.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_GetPrivateUnitsHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateUnitsCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_UNITS_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the 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 gpuh 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_GetPrivateUnitsHandle *gpuh)
+{
+ 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_GetPrivateUnitsResponse ugr = {
+ .hr.http_status = MHD_HTTP_OK,
+ .hr.reply = json,
+ .details = {
+ .ok = {
+ .units = entries,
+ .units_length = (unsigned int) len
+ }
+
+
+ }
+
+
+ };
+
+ gpuh->cb (gpuh->cb_cls,
+ &ugr);
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/units request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateUnitsHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_units_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_GetPrivateUnitsHandle *gpuh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateUnitsResponse ugr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpuh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/units response with status code %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,
+ gpuh))
+ {
+ TALER_MERCHANT_get_private_units_cancel (gpuh);
+ 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;
+ }
+ gpuh->cb (gpuh->cb_cls,
+ &ugr);
+ TALER_MERCHANT_get_private_units_cancel (gpuh);
+}
+
+
+struct TALER_MERCHANT_GetPrivateUnitsHandle *
+TALER_MERCHANT_get_private_units_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateUnitsHandle *gpuh;
+
+ gpuh = GNUNET_new (struct TALER_MERCHANT_GetPrivateUnitsHandle);
+ gpuh->ctx = ctx;
+ gpuh->base_url = GNUNET_strdup (url);
+ return gpuh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_units_start (
+ struct TALER_MERCHANT_GetPrivateUnitsHandle *gpuh,
+ TALER_MERCHANT_GetPrivateUnitsCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_UNITS_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpuh->cb = cb;
+ gpuh->cb_cls = cb_cls;
+ gpuh->url = TALER_url_join (gpuh->base_url,
+ "private/units",
+ NULL);
+ if (NULL == gpuh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpuh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpuh->job = GNUNET_CURL_job_add (gpuh->ctx,
+ eh,
+ &handle_get_units_finished,
+ gpuh);
+ if (NULL == gpuh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_units_cancel (
+ struct TALER_MERCHANT_GetPrivateUnitsHandle *gpuh)
+{
+ if (NULL != gpuh->job)
+ {
+ GNUNET_CURL_job_cancel (gpuh->job);
+ gpuh->job = NULL;
+ }
+ GNUNET_free (gpuh->url);
+ GNUNET_free (gpuh->base_url);
+ GNUNET_free (gpuh);
+}
+
+
+/* end of merchant_api_get-private-units-new.c */
diff --git a/src/lib/merchant_api_get-private-webhooks-WEBHOOK_ID-new.c b/src/lib/merchant_api_get-private-webhooks-WEBHOOK_ID-new.c
@@ -0,0 +1,227 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-private-webhooks-WEBHOOK_ID-new.c
+ * @brief Implementation of the GET /private/webhooks/$WEBHOOK_ID request
+ * @author Priscilla Huang
+ * @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/get-private-webhooks-WEBHOOK_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /private/webhooks/$WEBHOOK_ID operation.
+ */
+struct TALER_MERCHANT_GetPrivateWebhookHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateWebhookCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_WEBHOOK_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Webhook identifier.
+ */
+ char *webhook_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/webhooks/$WEBHOOK_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateWebhookHandle`
+ * @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_GetPrivateWebhookHandle *gpw = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateWebhookResponse wgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpw->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/webhooks/$WEBHOOK_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 ("event_type",
+ &wgr.details.ok.event_type),
+ TALER_JSON_spec_web_url ("url",
+ &wgr.details.ok.url),
+ GNUNET_JSON_spec_string ("http_method",
+ &wgr.details.ok.http_method),
+ GNUNET_JSON_spec_string ("header_template",
+ &wgr.details.ok.header_template),
+ GNUNET_JSON_spec_string ("body_template",
+ &wgr.details.ok.body_template),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ gpw->cb (gpw->cb_cls,
+ &wgr);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_get_private_webhook_cancel (gpw);
+ return;
+ }
+ wgr.hr.http_status = 0;
+ wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ GNUNET_JSON_parse_free (spec);
+ 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;
+ case MHD_HTTP_NOT_FOUND:
+ wgr.hr.ec = TALER_JSON_get_error_code (json);
+ wgr.hr.hint = TALER_JSON_get_error_hint (json);
+ 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;
+ }
+ gpw->cb (gpw->cb_cls,
+ &wgr);
+ TALER_MERCHANT_get_private_webhook_cancel (gpw);
+}
+
+
+struct TALER_MERCHANT_GetPrivateWebhookHandle *
+TALER_MERCHANT_get_private_webhook_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *webhook_id)
+{
+ struct TALER_MERCHANT_GetPrivateWebhookHandle *gpw;
+
+ gpw = GNUNET_new (struct TALER_MERCHANT_GetPrivateWebhookHandle);
+ gpw->ctx = ctx;
+ gpw->base_url = GNUNET_strdup (url);
+ gpw->webhook_id = GNUNET_strdup (webhook_id);
+ return gpw;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_webhook_start (
+ struct TALER_MERCHANT_GetPrivateWebhookHandle *gpw,
+ TALER_MERCHANT_GetPrivateWebhookCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_WEBHOOK_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpw->cb = cb;
+ gpw->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/webhooks/%s",
+ gpw->webhook_id);
+ gpw->url = TALER_url_join (gpw->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == gpw->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpw->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpw->job = GNUNET_CURL_job_add (gpw->ctx,
+ eh,
+ &handle_get_webhook_finished,
+ gpw);
+ if (NULL == gpw->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_webhook_cancel (
+ struct TALER_MERCHANT_GetPrivateWebhookHandle *gpw)
+{
+ if (NULL != gpw->job)
+ {
+ GNUNET_CURL_job_cancel (gpw->job);
+ gpw->job = NULL;
+ }
+ GNUNET_free (gpw->url);
+ GNUNET_free (gpw->base_url);
+ GNUNET_free (gpw->webhook_id);
+ GNUNET_free (gpw);
+}
+
+
+/* end of merchant_api_get-private-webhooks-WEBHOOK_ID-new.c */
diff --git a/src/lib/merchant_api_get-private-webhooks-new.c b/src/lib/merchant_api_get-private-webhooks-new.c
@@ -0,0 +1,265 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-private-webhooks-new.c
+ * @brief Implementation of the GET /private/webhooks 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/get-private-webhooks-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Maximum number of webhooks permitted.
+ */
+#define MAX_WEBHOOKS 1024
+
+
+/**
+ * Handle for a GET /private/webhooks operation.
+ */
+struct TALER_MERCHANT_GetPrivateWebhooksHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetPrivateWebhooksCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRIVATE_WEBHOOKS_RESULT_CLOSURE *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 gpwh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_webhooks (const json_t *ia,
+ struct TALER_MERCHANT_GetPrivateWebhooksResponse *wgr,
+ struct TALER_MERCHANT_GetPrivateWebhooksHandle *gpwh)
+{
+ 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_GetPrivateWebhooksWebhookEntry whook[
+ GNUNET_NZL (whook_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (ia, index, value) {
+ struct TALER_MERCHANT_GetPrivateWebhooksWebhookEntry *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;
+ gpwh->cb (gpwh->cb_cls,
+ wgr);
+ gpwh->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /private/webhooks request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetPrivateWebhooksHandle`
+ * @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_GetPrivateWebhooksHandle *gpwh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetPrivateWebhooksResponse wgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpwh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /private/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,
+ gpwh))
+ {
+ TALER_MERCHANT_get_private_webhooks_cancel (gpwh);
+ 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;
+ }
+ gpwh->cb (gpwh->cb_cls,
+ &wgr);
+ TALER_MERCHANT_get_private_webhooks_cancel (gpwh);
+}
+
+
+struct TALER_MERCHANT_GetPrivateWebhooksHandle *
+TALER_MERCHANT_get_private_webhooks_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url)
+{
+ struct TALER_MERCHANT_GetPrivateWebhooksHandle *gpwh;
+
+ gpwh = GNUNET_new (struct TALER_MERCHANT_GetPrivateWebhooksHandle);
+ gpwh->ctx = ctx;
+ gpwh->base_url = GNUNET_strdup (url);
+ return gpwh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_private_webhooks_start (
+ struct TALER_MERCHANT_GetPrivateWebhooksHandle *gpwh,
+ TALER_MERCHANT_GetPrivateWebhooksCallback cb,
+ TALER_MERCHANT_GET_PRIVATE_WEBHOOKS_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpwh->cb = cb;
+ gpwh->cb_cls = cb_cls;
+ gpwh->url = TALER_url_join (gpwh->base_url,
+ "private/webhooks",
+ NULL);
+ if (NULL == gpwh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpwh->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpwh->job = GNUNET_CURL_job_add (gpwh->ctx,
+ eh,
+ &handle_get_webhooks_finished,
+ gpwh);
+ if (NULL == gpwh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_private_webhooks_cancel (
+ struct TALER_MERCHANT_GetPrivateWebhooksHandle *gpwh)
+{
+ if (NULL != gpwh->job)
+ {
+ GNUNET_CURL_job_cancel (gpwh->job);
+ gpwh->job = NULL;
+ }
+ GNUNET_free (gpwh->url);
+ GNUNET_free (gpwh->base_url);
+ GNUNET_free (gpwh);
+}
+
+
+/* end of merchant_api_get-private-webhooks-new.c */
diff --git a/src/lib/merchant_api_get-products-IMAGE_HASH-image-new.c b/src/lib/merchant_api_get-products-IMAGE_HASH-image-new.c
@@ -0,0 +1,212 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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-products-IMAGE_HASH-image-new.c
+ * @brief Implementation of the GET /products/$IMAGE_HASH/image 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/get-products-IMAGE_HASH-image-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /products/$IMAGE_HASH/image operation.
+ */
+struct TALER_MERCHANT_GetProductsImageHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetProductsImageCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_PRODUCTS_IMAGE_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Image hash identifier.
+ */
+ char *image_hash;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /products/$IMAGE_HASH/image request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetProductsImageHandle`
+ * @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_GetProductsImageHandle *gpi = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetProductsImageResponse pgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gpi->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /products/$IMAGE_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",
+ &pgr.details.ok.image),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK ==
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ gpi->cb (gpi->cb_cls,
+ &pgr);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_get_products_image_cancel (gpi);
+ return;
+ }
+ pgr.hr.http_status = 0;
+ pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_BAD_REQUEST:
+ pgr.hr.ec = TALER_JSON_get_error_code (json);
+ pgr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ 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;
+ }
+ gpi->cb (gpi->cb_cls,
+ &pgr);
+ TALER_MERCHANT_get_products_image_cancel (gpi);
+}
+
+
+struct TALER_MERCHANT_GetProductsImageHandle *
+TALER_MERCHANT_get_products_image_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *image_hash)
+{
+ struct TALER_MERCHANT_GetProductsImageHandle *gpi;
+
+ gpi = GNUNET_new (struct TALER_MERCHANT_GetProductsImageHandle);
+ gpi->ctx = ctx;
+ gpi->base_url = GNUNET_strdup (url);
+ gpi->image_hash = GNUNET_strdup (image_hash);
+ return gpi;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_products_image_start (
+ struct TALER_MERCHANT_GetProductsImageHandle *gpi,
+ TALER_MERCHANT_GetProductsImageCallback cb,
+ TALER_MERCHANT_GET_PRODUCTS_IMAGE_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gpi->cb = cb;
+ gpi->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "products/%s/image",
+ gpi->image_hash);
+ gpi->url = TALER_url_join (gpi->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == gpi->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gpi->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gpi->job = GNUNET_CURL_job_add (gpi->ctx,
+ eh,
+ &handle_get_product_image_finished,
+ gpi);
+ if (NULL == gpi->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_products_image_cancel (
+ struct TALER_MERCHANT_GetProductsImageHandle *gpi)
+{
+ if (NULL != gpi->job)
+ {
+ GNUNET_CURL_job_cancel (gpi->job);
+ gpi->job = NULL;
+ }
+ GNUNET_free (gpi->url);
+ GNUNET_free (gpi->base_url);
+ GNUNET_free (gpi->image_hash);
+ GNUNET_free (gpi);
+}
+
+
+/* end of merchant_api_get-products-IMAGE_HASH-image-new.c */
diff --git a/src/lib/merchant_api_get-templates-TEMPLATE_ID-new.c b/src/lib/merchant_api_get-templates-TEMPLATE_ID-new.c
@@ -0,0 +1,216 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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-templates-TEMPLATE_ID-new.c
+ * @brief Implementation of the GET /templates/$TEMPLATE_ID request (wallet-facing)
+ * @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/get-templates-TEMPLATE_ID-new.h>
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle for a GET /templates/$TEMPLATE_ID operation (wallet-facing).
+ */
+struct TALER_MERCHANT_GetTemplatesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_GetTemplatesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_GET_TEMPLATES_RESULT_CLOSURE *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Template identifier.
+ */
+ char *template_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /templates/$TEMPLATE_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_GetTemplatesHandle`
+ * @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_GetTemplatesHandle *gth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_GetTemplatesResponse wtgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ gth->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got /templates/$TEMPLATE_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))
+ {
+ wtgr.details.ok.template_contract = contract;
+ gth->cb (gth->cb_cls,
+ &wtgr);
+ TALER_MERCHANT_get_templates_cancel (gth);
+ return;
+ }
+ wtgr.hr.http_status = 0;
+ wtgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ case MHD_HTTP_UNAUTHORIZED:
+ wtgr.hr.ec = TALER_JSON_get_error_code (json);
+ wtgr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ wtgr.hr.ec = TALER_JSON_get_error_code (json);
+ wtgr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ wtgr.hr.ec = TALER_JSON_get_error_code (json);
+ wtgr.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) wtgr.hr.ec);
+ break;
+ }
+ gth->cb (gth->cb_cls,
+ &wtgr);
+ TALER_MERCHANT_get_templates_cancel (gth);
+}
+
+
+struct TALER_MERCHANT_GetTemplatesHandle *
+TALER_MERCHANT_get_templates_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *template_id)
+{
+ struct TALER_MERCHANT_GetTemplatesHandle *gth;
+
+ gth = GNUNET_new (struct TALER_MERCHANT_GetTemplatesHandle);
+ gth->ctx = ctx;
+ gth->base_url = GNUNET_strdup (url);
+ gth->template_id = GNUNET_strdup (template_id);
+ return gth;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_get_templates_start (
+ struct TALER_MERCHANT_GetTemplatesHandle *gth,
+ TALER_MERCHANT_GetTemplatesCallback cb,
+ TALER_MERCHANT_GET_TEMPLATES_RESULT_CLOSURE *cb_cls)
+{
+ CURL *eh;
+
+ gth->cb = cb;
+ gth->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "templates/%s",
+ gth->template_id);
+ gth->url = TALER_url_join (gth->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == gth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ eh = TALER_MERCHANT_curl_easy_get_ (gth->url);
+ if (NULL == eh)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ gth->job = GNUNET_CURL_job_add (gth->ctx,
+ eh,
+ &handle_get_template_finished,
+ gth);
+ if (NULL == gth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_get_templates_cancel (
+ struct TALER_MERCHANT_GetTemplatesHandle *gth)
+{
+ if (NULL != gth->job)
+ {
+ GNUNET_CURL_job_cancel (gth->job);
+ gth->job = NULL;
+ }
+ GNUNET_free (gth->url);
+ GNUNET_free (gth->base_url);
+ GNUNET_free (gth->template_id);
+ GNUNET_free (gth);
+}
+
+
+/* end of merchant_api_get-templates-TEMPLATE_ID-new.c */
diff --git a/src/lib/merchant_api_patch-management-instances-INSTANCE-new.c b/src/lib/merchant_api_patch-management-instances-INSTANCE-new.c
@@ -0,0 +1,313 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_patch-management-instances-INSTANCE-new.c
+ * @brief Implementation of the PATCH /management/instances/$INSTANCE 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/patch-management-instances-INSTANCE-new.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 /management/instances/$INSTANCE operation.
+ */
+struct TALER_MERCHANT_PatchManagementInstancesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PatchManagementInstancesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_PATCH_MANAGEMENT_INSTANCES_RESULT_CLOSURE *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;
+
+ /**
+ * Instance identifier.
+ */
+ char *instance_id;
+
+ /**
+ * Human-readable name for the instance.
+ */
+ char *name;
+
+ /**
+ * Address (JSON).
+ */
+ json_t *address;
+
+ /**
+ * Jurisdiction (JSON).
+ */
+ json_t *jurisdiction;
+
+ /**
+ * Whether to use STEFAN curves for fee calculations.
+ */
+ bool use_stefan;
+
+ /**
+ * Default wire transfer delay.
+ */
+ struct GNUNET_TIME_Relative default_wire_transfer_delay;
+
+ /**
+ * Default payment deadline.
+ */
+ struct GNUNET_TIME_Relative default_pay_delay;
+
+ /**
+ * Default refund deadline.
+ */
+ struct GNUNET_TIME_Relative default_refund_delay;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /management/instances/$INSTANCE request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PatchManagementInstancesHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_management_instances_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PatchManagementInstancesHandle *pmih = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PatchManagementInstancesResponse pmir = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pmih->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /management/instances/$INSTANCE completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ pmir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ pmir.hr.ec = TALER_JSON_get_error_code (json);
+ pmir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ pmir.hr.ec = TALER_JSON_get_error_code (json);
+ pmir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ pmir.hr.ec = TALER_JSON_get_error_code (json);
+ pmir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ pmir.hr.ec = TALER_JSON_get_error_code (json);
+ pmir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ pmir.hr.ec = TALER_JSON_get_error_code (json);
+ pmir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ pmir.hr.ec = TALER_JSON_get_error_code (json);
+ pmir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pmir.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) pmir.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ pmih->cb (pmih->cb_cls,
+ &pmir);
+ TALER_MERCHANT_patch_management_instances_cancel (pmih);
+}
+
+
+struct TALER_MERCHANT_PatchManagementInstancesHandle *
+TALER_MERCHANT_patch_management_instances_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *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)
+{
+ struct TALER_MERCHANT_PatchManagementInstancesHandle *pmih;
+
+ pmih = GNUNET_new (struct TALER_MERCHANT_PatchManagementInstancesHandle);
+ pmih->ctx = ctx;
+ pmih->base_url = GNUNET_strdup (url);
+ pmih->instance_id = GNUNET_strdup (instance_id);
+ pmih->name = GNUNET_strdup (name);
+ pmih->address = json_incref ((json_t *) address);
+ pmih->jurisdiction = json_incref ((json_t *) jurisdiction);
+ pmih->use_stefan = use_stefan;
+ pmih->default_wire_transfer_delay = default_wire_transfer_delay;
+ pmih->default_pay_delay = default_pay_delay;
+ pmih->default_refund_delay = default_refund_delay;
+ return pmih;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_patch_management_instances_start (
+ struct TALER_MERCHANT_PatchManagementInstancesHandle *pmih,
+ TALER_MERCHANT_PatchManagementInstancesCallback cb,
+ TALER_MERCHANT_PATCH_MANAGEMENT_INSTANCES_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ pmih->cb = cb;
+ pmih->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "management/instances/%s",
+ pmih->instance_id);
+ pmih->url = TALER_url_join (pmih->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pmih->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("name",
+ pmih->name),
+ GNUNET_JSON_pack_object_incref ("address",
+ pmih->address),
+ GNUNET_JSON_pack_object_incref ("jurisdiction",
+ pmih->jurisdiction),
+ GNUNET_JSON_pack_bool ("use_stefan",
+ pmih->use_stefan),
+ GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay",
+ pmih->default_wire_transfer_delay),
+ GNUNET_JSON_pack_time_rel ("default_pay_delay",
+ pmih->default_pay_delay),
+ GNUNET_JSON_pack_time_rel ("default_refund_delay",
+ pmih->default_refund_delay),
+ GNUNET_JSON_pack_time_rounder_interval (
+ "default_wire_transfer_rounding_interval",
+ GNUNET_TIME_RI_NONE)
+ );
+ eh = TALER_MERCHANT_curl_easy_get_ (pmih->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&pmih->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ pmih->job = GNUNET_CURL_job_add2 (pmih->ctx,
+ eh,
+ pmih->post_ctx.headers,
+ &handle_patch_management_instances_finished,
+ pmih);
+ if (NULL == pmih->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_patch_management_instances_cancel (
+ struct TALER_MERCHANT_PatchManagementInstancesHandle *pmih)
+{
+ if (NULL != pmih->job)
+ {
+ GNUNET_CURL_job_cancel (pmih->job);
+ pmih->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pmih->post_ctx);
+ json_decref (pmih->address);
+ json_decref (pmih->jurisdiction);
+ GNUNET_free (pmih->instance_id);
+ GNUNET_free (pmih->name);
+ GNUNET_free (pmih->url);
+ GNUNET_free (pmih->base_url);
+ GNUNET_free (pmih);
+}
+
+
+/* end of merchant_api_patch-management-instances-INSTANCE-new.c */
diff --git a/src/lib/merchant_api_patch-private-accounts-H_WIRE-new.c b/src/lib/merchant_api_patch-private-accounts-H_WIRE-new.c
@@ -0,0 +1,295 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_patch-private-accounts-H_WIRE-new.c
+ * @brief Implementation of the PATCH /private/accounts/$H_WIRE 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/patch-private-accounts-H_WIRE-new.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/accounts/$H_WIRE operation.
+ */
+struct TALER_MERCHANT_PatchPrivateAccountHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PatchPrivateAccountCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_RESULT_CLOSURE *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;
+
+ /**
+ * Hash of the wire details identifying the account.
+ */
+ struct TALER_MerchantWireHashP h_wire;
+
+ /**
+ * Optional credit facade URL.
+ */
+ const char *credit_facade_url;
+
+ /**
+ * Optional credit facade credentials (JSON).
+ */
+ const json_t *credit_facade_credentials;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /private/accounts/$H_WIRE request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PatchPrivateAccountHandle`
+ * @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_PatchPrivateAccountHandle *pah = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PatchPrivateAccountResponse par = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pah->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /private/accounts/$H_WIRE completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ par.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ par.hr.ec = TALER_JSON_get_error_code (json);
+ par.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_break_op (0);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ par.hr.ec = TALER_JSON_get_error_code (json);
+ par.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ par.hr.ec = TALER_JSON_get_error_code (json);
+ par.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ par.hr.ec = TALER_JSON_get_error_code (json);
+ par.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ par.hr.ec = TALER_JSON_get_error_code (json);
+ par.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ par.hr.ec = TALER_JSON_get_error_code (json);
+ par.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &par.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) par.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ pah->cb (pah->cb_cls,
+ &par);
+ TALER_MERCHANT_patch_private_account_cancel (pah);
+}
+
+
+struct TALER_MERCHANT_PatchPrivateAccountHandle *
+TALER_MERCHANT_patch_private_account_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const struct TALER_MerchantWireHashP *h_wire)
+{
+ struct TALER_MERCHANT_PatchPrivateAccountHandle *pah;
+
+ pah = GNUNET_new (struct TALER_MERCHANT_PatchPrivateAccountHandle);
+ pah->ctx = ctx;
+ pah->base_url = GNUNET_strdup (url);
+ pah->h_wire = *h_wire;
+ return pah;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_patch_private_account_set_options_ (
+ struct TALER_MERCHANT_PatchPrivateAccountHandle *pah,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PatchPrivateAccountOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_OPTION_CREDIT_FACADE_URL:
+ pah->credit_facade_url = options[i].details.credit_facade_url;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_OPTION_CREDIT_FACADE_CREDENTIALS:
+ pah->credit_facade_credentials
+ = options[i].details.credit_facade_credentials;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_patch_private_account_start (
+ struct TALER_MERCHANT_PatchPrivateAccountHandle *pah,
+ TALER_MERCHANT_PatchPrivateAccountCallback cb,
+ TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ pah->cb = cb;
+ pah->cb_cls = cb_cls;
+ {
+ char w_str[sizeof (pah->h_wire) * 2];
+ char *path;
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (&pah->h_wire,
+ sizeof (pah->h_wire),
+ w_str,
+ sizeof (w_str));
+ *end = '\0';
+ GNUNET_asprintf (&path,
+ "private/accounts/%s",
+ w_str);
+ pah->url = TALER_url_join (pah->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("credit_facade_url",
+ pah->credit_facade_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("credit_facade_credentials",
+ (json_t *) pah->credit_facade_credentials)
+ ));
+ eh = TALER_MERCHANT_curl_easy_get_ (pah->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&pah->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ pah->job = GNUNET_CURL_job_add2 (pah->ctx,
+ eh,
+ pah->post_ctx.headers,
+ &handle_patch_account_finished,
+ pah);
+ if (NULL == pah->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_patch_private_account_cancel (
+ struct TALER_MERCHANT_PatchPrivateAccountHandle *pah)
+{
+ if (NULL != pah->job)
+ {
+ GNUNET_CURL_job_cancel (pah->job);
+ pah->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pah->post_ctx);
+ GNUNET_free (pah->url);
+ GNUNET_free (pah->base_url);
+ GNUNET_free (pah);
+}
+
+
+/* end of merchant_api_patch-private-accounts-H_WIRE-new.c */
diff --git a/src/lib/merchant_api_patch-private-orders-ORDER_ID-forget-new.c b/src/lib/merchant_api_patch-private-orders-ORDER_ID-forget-new.c
@@ -0,0 +1,279 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_patch-private-orders-ORDER_ID-forget-new.c
+ * @brief Implementation of the PATCH /private/orders/$ORDER_ID/forget 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/patch-private-orders-ORDER_ID-forget-new.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/orders/$ORDER_ID/forget operation.
+ */
+struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PatchPrivateOrdersForgetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_PATCH_PRIVATE_ORDERS_FORGET_RESULT_CLOSURE *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;
+
+ /**
+ * Order identifier.
+ */
+ char *order_id;
+
+ /**
+ * Array of JSON pointer paths to redact.
+ */
+ char **fields;
+
+ /**
+ * Number of entries in @e fields.
+ */
+ unsigned int fields_length;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /private/orders/$ORDER_ID/forget request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_orders_forget_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle *ofh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PatchPrivateOrdersForgetResponse por = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ofh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /private/orders/$ORDER_ID/forget completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ por.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:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &por.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) por.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ofh->cb (ofh->cb_cls,
+ &por);
+ TALER_MERCHANT_patch_private_orders_forget_cancel (ofh);
+}
+
+
+struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle *
+TALER_MERCHANT_patch_private_orders_forget_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id,
+ unsigned int fields_length,
+ const char *fields[static fields_length])
+{
+ struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle *ofh;
+
+ ofh = GNUNET_new (struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle);
+ ofh->ctx = ctx;
+ ofh->base_url = GNUNET_strdup (url);
+ ofh->order_id = GNUNET_strdup (order_id);
+ ofh->fields_length = fields_length;
+ ofh->fields = GNUNET_new_array (fields_length,
+ char *);
+ for (unsigned int i = 0; i < fields_length; i++)
+ ofh->fields[i] = GNUNET_strdup (fields[i]);
+ return ofh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_patch_private_orders_forget_start (
+ struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle *ofh,
+ TALER_MERCHANT_PatchPrivateOrdersForgetCallback cb,
+ TALER_MERCHANT_PATCH_PRIVATE_ORDERS_FORGET_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_fields;
+ json_t *req_obj;
+ CURL *eh;
+
+ ofh->cb = cb;
+ ofh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/orders/%s/forget",
+ ofh->order_id);
+ ofh->url = TALER_url_join (ofh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == ofh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_fields = json_array ();
+ if (NULL == req_fields)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ for (unsigned int i = 0; i < ofh->fields_length; i++)
+ {
+ if (0 !=
+ json_array_append_new (req_fields,
+ json_string (ofh->fields[i])))
+ {
+ json_decref (req_fields);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ }
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("fields",
+ req_fields));
+ eh = TALER_MERCHANT_curl_easy_get_ (ofh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ofh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ ofh->job = GNUNET_CURL_job_add2 (ofh->ctx,
+ eh,
+ ofh->post_ctx.headers,
+ &handle_patch_orders_forget_finished,
+ ofh);
+ if (NULL == ofh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_patch_private_orders_forget_cancel (
+ struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle *ofh)
+{
+ if (NULL != ofh->job)
+ {
+ GNUNET_CURL_job_cancel (ofh->job);
+ ofh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ofh->post_ctx);
+ for (unsigned int i = 0; i < ofh->fields_length; i++)
+ GNUNET_free (ofh->fields[i]);
+ GNUNET_free (ofh->fields);
+ GNUNET_free (ofh->order_id);
+ GNUNET_free (ofh->url);
+ GNUNET_free (ofh->base_url);
+ GNUNET_free (ofh);
+}
+
+
+/* end of merchant_api_patch-private-orders-ORDER_ID-forget-new.c */
diff --git a/src/lib/merchant_api_patch-private-otp-devices-DEVICE_ID-new.c b/src/lib/merchant_api_patch-private-otp-devices-DEVICE_ID-new.c
@@ -0,0 +1,284 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_patch-private-otp-devices-DEVICE_ID-new.c
+ * @brief Implementation of the PATCH /private/otp-devices/$DEVICE_ID 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/patch-private-otp-devices-DEVICE_ID-new.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/otp-devices/$DEVICE_ID operation.
+ */
+struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PatchPrivateOtpDeviceCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_PATCH_PRIVATE_OTP_DEVICE_RESULT_CLOSURE *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;
+
+ /**
+ * OTP device identifier.
+ */
+ char *otp_device_id;
+
+ /**
+ * Human-readable description.
+ */
+ char *otp_device_description;
+
+ /**
+ * Base32-encoded OTP secret key, or NULL to keep unchanged.
+ */
+ char *otp_key;
+
+ /**
+ * OTP algorithm.
+ */
+ enum TALER_MerchantConfirmationAlgorithm mca;
+
+ /**
+ * Counter value (for HOTP).
+ */
+ uint64_t otp_ctr;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /private/otp-devices/$DEVICE_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle`
+ * @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_PatchPrivateOtpDeviceHandle *odh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PatchPrivateOtpDeviceResponse odr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ odh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /private/otp-devices/$DEVICE_ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ odr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ GNUNET_break_op (0);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &odr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) odr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ odh->cb (odh->cb_cls,
+ &odr);
+ TALER_MERCHANT_patch_private_otp_device_cancel (odh);
+}
+
+
+struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *
+TALER_MERCHANT_patch_private_otp_device_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *otp_device_id,
+ const char *otp_device_description,
+ const char *otp_key,
+ enum TALER_MerchantConfirmationAlgorithm mca,
+ uint64_t otp_ctr)
+{
+ struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *odh;
+
+ odh = GNUNET_new (struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle);
+ odh->ctx = ctx;
+ odh->base_url = GNUNET_strdup (url);
+ odh->otp_device_id = GNUNET_strdup (otp_device_id);
+ odh->otp_device_description = GNUNET_strdup (otp_device_description);
+ if (NULL != otp_key)
+ odh->otp_key = GNUNET_strdup (otp_key);
+ odh->mca = mca;
+ odh->otp_ctr = otp_ctr;
+ return odh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_patch_private_otp_device_start (
+ struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *odh,
+ TALER_MERCHANT_PatchPrivateOtpDeviceCallback cb,
+ TALER_MERCHANT_PATCH_PRIVATE_OTP_DEVICE_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ odh->cb = cb;
+ odh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/otp-devices/%s",
+ odh->otp_device_id);
+ odh->url = TALER_url_join (odh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == odh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("otp_device_description",
+ odh->otp_device_description),
+ GNUNET_JSON_pack_uint64 ("otp_algorithm",
+ (uint32_t) odh->mca),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_key",
+ odh->otp_key)),
+ GNUNET_JSON_pack_uint64 ("otp_ctr",
+ odh->otp_ctr));
+ eh = TALER_MERCHANT_curl_easy_get_ (odh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&odh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ odh->job = GNUNET_CURL_job_add2 (odh->ctx,
+ eh,
+ odh->post_ctx.headers,
+ &handle_patch_otp_device_finished,
+ odh);
+ if (NULL == odh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_patch_private_otp_device_cancel (
+ struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *odh)
+{
+ if (NULL != odh->job)
+ {
+ GNUNET_CURL_job_cancel (odh->job);
+ odh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&odh->post_ctx);
+ GNUNET_free (odh->otp_device_id);
+ GNUNET_free (odh->otp_device_description);
+ GNUNET_free (odh->otp_key);
+ GNUNET_free (odh->url);
+ GNUNET_free (odh->base_url);
+ GNUNET_free (odh);
+}
+
+
+/* end of merchant_api_patch-private-otp-devices-DEVICE_ID-new.c */
diff --git a/src/lib/merchant_api_patch-private-products-PRODUCT_ID-new.c b/src/lib/merchant_api_patch-private-products-PRODUCT_ID-new.c
@@ -0,0 +1,465 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_patch-private-products-PRODUCT_ID-new.c
+ * @brief Implementation of the PATCH /private/products/$PRODUCT_ID 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/patch-private-products-PRODUCT_ID-new.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/products/$PRODUCT_ID operation.
+ */
+struct TALER_MERCHANT_PatchPrivateProductHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PatchPrivateProductCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_PATCH_PRIVATE_PRODUCT_RESULT_CLOSURE *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;
+
+ /**
+ * Identifier of the product to update.
+ */
+ char *product_id;
+
+ /**
+ * New human-readable description.
+ */
+ char *description;
+
+ /**
+ * Internationalized descriptions (JSON).
+ */
+ json_t *description_i18n;
+
+ /**
+ * Unit of measurement.
+ */
+ char *unit;
+
+ /**
+ * New unit price.
+ */
+ struct TALER_Amount price;
+
+ /**
+ * New base64-encoded image.
+ */
+ char *image;
+
+ /**
+ * New tax information (JSON array).
+ */
+ json_t *taxes;
+
+ /**
+ * New total stock (-1 for unlimited).
+ */
+ int64_t total_stock;
+
+ /**
+ * Total units lost/expired.
+ */
+ uint64_t total_lost;
+
+ /**
+ * Storage location (JSON).
+ */
+ json_t *address;
+
+ /**
+ * Expected restock time.
+ */
+ struct GNUNET_TIME_Timestamp next_restock;
+
+ /**
+ * Optional unit prices array (for patch2 variant).
+ */
+ const struct TALER_Amount *unit_prices;
+
+ /**
+ * Number of prices in @e unit_prices.
+ */
+ size_t unit_price_len;
+
+ /**
+ * Fractional part of total stock.
+ */
+ uint32_t total_stock_frac;
+
+ /**
+ * Whether fractional quantities are allowed.
+ */
+ bool unit_allow_fraction;
+
+ /**
+ * Whether unit_allow_fraction was explicitly set.
+ */
+ bool have_unit_allow_fraction;
+
+ /**
+ * Precision level for fractions.
+ */
+ uint32_t unit_precision_level;
+
+ /**
+ * Whether unit_precision_level was explicitly set.
+ */
+ bool have_unit_precision_level;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /private/products/$PRODUCT_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PatchPrivateProductHandle`
+ * @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_PatchPrivateProductHandle *pph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PatchPrivateProductResponse ppr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /private/products/$PRODUCT_ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ ppr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.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:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.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,
+ &ppr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) ppr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ pph->cb (pph->cb_cls,
+ &ppr);
+ TALER_MERCHANT_patch_private_product_cancel (pph);
+}
+
+
+struct TALER_MERCHANT_PatchPrivateProductHandle *
+TALER_MERCHANT_patch_private_product_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *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)
+{
+ struct TALER_MERCHANT_PatchPrivateProductHandle *pph;
+
+ pph = GNUNET_new (struct TALER_MERCHANT_PatchPrivateProductHandle);
+ pph->ctx = ctx;
+ pph->base_url = GNUNET_strdup (url);
+ pph->product_id = GNUNET_strdup (product_id);
+ pph->description = GNUNET_strdup (description);
+ pph->description_i18n = json_incref ((json_t *) description_i18n);
+ pph->unit = GNUNET_strdup (unit);
+ pph->price = *price;
+ pph->image = GNUNET_strdup (image);
+ pph->taxes = json_incref ((json_t *) taxes);
+ pph->total_stock = total_stock;
+ pph->total_lost = total_lost;
+ pph->address = json_incref ((json_t *) address);
+ pph->next_restock = next_restock;
+ /* Default: single price, no fractions */
+ pph->unit_prices = NULL;
+ pph->unit_price_len = 0;
+ pph->total_stock_frac = 0;
+ pph->unit_allow_fraction = false;
+ pph->have_unit_allow_fraction = false;
+ pph->have_unit_precision_level = false;
+ return pph;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_patch_private_product_set_options_ (
+ struct TALER_MERCHANT_PatchPrivateProductHandle *pph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PatchPrivateProductOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_PATCH_PRIVATE_PRODUCT_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_PATCH_PRIVATE_PRODUCT_OPTION_UNIT_PRICES:
+ pph->unit_prices = options[i].details.unit_prices.unit_prices;
+ pph->unit_price_len = options[i].details.unit_prices.unit_price_len;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_PRODUCT_OPTION_TOTAL_STOCK_FRAC:
+ pph->total_stock_frac = options[i].details.total_stock_frac;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_PRODUCT_OPTION_UNIT_ALLOW_FRACTION:
+ pph->unit_allow_fraction = options[i].details.unit_allow_fraction;
+ pph->have_unit_allow_fraction = true;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_PRODUCT_OPTION_UNIT_PRECISION_LEVEL:
+ pph->unit_precision_level = options[i].details.unit_precision_level;
+ pph->have_unit_precision_level = true;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_patch_private_product_start (
+ struct TALER_MERCHANT_PatchPrivateProductHandle *pph,
+ TALER_MERCHANT_PatchPrivateProductCallback cb,
+ TALER_MERCHANT_PATCH_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+ const struct TALER_Amount *prices;
+ size_t price_len;
+ char unit_total_stock_buf[64];
+
+ pph->cb = cb;
+ pph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/products/%s",
+ pph->product_id);
+ pph->url = TALER_url_join (pph->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+
+ /* Use explicit unit_prices if set, otherwise fall back to single price */
+ if (NULL != pph->unit_prices)
+ {
+ prices = pph->unit_prices;
+ price_len = pph->unit_price_len;
+ }
+ else
+ {
+ prices = &pph->price;
+ price_len = 1;
+ }
+
+ TALER_MERCHANT_format_stock_string (pph->total_stock,
+ pph->total_stock_frac,
+ unit_total_stock_buf,
+ sizeof (unit_total_stock_buf));
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("product_name",
+ pph->description),
+ GNUNET_JSON_pack_string ("description",
+ pph->description),
+ GNUNET_JSON_pack_object_incref ("description_i18n",
+ (json_t *) pph->description_i18n),
+ GNUNET_JSON_pack_string ("unit",
+ pph->unit),
+ TALER_JSON_pack_amount_array ("unit_price",
+ price_len,
+ prices),
+ GNUNET_JSON_pack_string ("image",
+ pph->image),
+ GNUNET_JSON_pack_array_incref ("taxes",
+ (json_t *) pph->taxes),
+ GNUNET_JSON_pack_string ("unit_total_stock",
+ unit_total_stock_buf),
+ GNUNET_JSON_pack_uint64 ("total_lost",
+ pph->total_lost),
+ GNUNET_JSON_pack_object_incref ("address",
+ (json_t *) pph->address),
+ GNUNET_JSON_pack_timestamp ("next_restock",
+ pph->next_restock));
+ if (pph->have_unit_allow_fraction &&
+ pph->unit_allow_fraction)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_allow_fraction",
+ json_boolean (
+ pph->unit_allow_fraction)));
+ if (pph->have_unit_precision_level)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_precision_level",
+ json_integer (
+ pph->unit_precision_level)));
+ }
+ }
+ eh = TALER_MERCHANT_curl_easy_get_ (pph->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&pph->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ pph->job = GNUNET_CURL_job_add2 (pph->ctx,
+ eh,
+ pph->post_ctx.headers,
+ &handle_patch_product_finished,
+ pph);
+ if (NULL == pph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_patch_private_product_cancel (
+ struct TALER_MERCHANT_PatchPrivateProductHandle *pph)
+{
+ if (NULL != pph->job)
+ {
+ GNUNET_CURL_job_cancel (pph->job);
+ pph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pph->post_ctx);
+ json_decref (pph->description_i18n);
+ json_decref (pph->taxes);
+ json_decref (pph->address);
+ GNUNET_free (pph->url);
+ GNUNET_free (pph->base_url);
+ GNUNET_free (pph->product_id);
+ GNUNET_free (pph->description);
+ GNUNET_free (pph->unit);
+ GNUNET_free (pph->image);
+ GNUNET_free (pph);
+}
+
+
+/* end of merchant_api_patch-private-products-PRODUCT_ID-new.c */
diff --git a/src/lib/merchant_api_patch-private-templates-TEMPLATE_ID-new.c b/src/lib/merchant_api_patch-private-templates-TEMPLATE_ID-new.c
@@ -0,0 +1,282 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_patch-private-templates-TEMPLATE_ID-new.c
+ * @brief Implementation of the PATCH /private/templates/$TEMPLATE_ID 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/patch-private-templates-TEMPLATE_ID-new.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/templates/$TEMPLATE_ID operation.
+ */
+struct TALER_MERCHANT_PatchPrivateTemplateHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PatchPrivateTemplateCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_RESULT_CLOSURE *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;
+
+ /**
+ * Identifier of the template to update.
+ */
+ char *template_id;
+
+ /**
+ * New human-readable description.
+ */
+ char *template_description;
+
+ /**
+ * New OTP device ID, or NULL to remove association.
+ */
+ char *otp_id;
+
+ /**
+ * New template contract (JSON).
+ */
+ json_t *template_contract;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /private/templates/$TEMPLATE_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PatchPrivateTemplateHandle`
+ * @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_PatchPrivateTemplateHandle *tph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PatchPrivateTemplateResponse tpr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ tph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /private/templates/$TEMPLATE_ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ tpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ tpr.hr.ec = TALER_JSON_get_error_code (json);
+ tpr.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:
+ tpr.hr.ec = TALER_JSON_get_error_code (json);
+ tpr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ tpr.hr.ec = TALER_JSON_get_error_code (json);
+ tpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ tpr.hr.ec = TALER_JSON_get_error_code (json);
+ tpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ tpr.hr.ec = TALER_JSON_get_error_code (json);
+ tpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ tpr.hr.ec = TALER_JSON_get_error_code (json);
+ tpr.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,
+ &tpr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) tpr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ tph->cb (tph->cb_cls,
+ &tpr);
+ TALER_MERCHANT_patch_private_template_cancel (tph);
+}
+
+
+struct TALER_MERCHANT_PatchPrivateTemplateHandle *
+TALER_MERCHANT_patch_private_template_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *template_id,
+ const char *template_description,
+ const char *otp_id,
+ json_t *template_contract)
+{
+ struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph;
+
+ tph = GNUNET_new (struct TALER_MERCHANT_PatchPrivateTemplateHandle);
+ tph->ctx = ctx;
+ tph->base_url = GNUNET_strdup (url);
+ tph->template_id = GNUNET_strdup (template_id);
+ tph->template_description = GNUNET_strdup (template_description);
+ if (NULL != otp_id)
+ tph->otp_id = GNUNET_strdup (otp_id);
+ tph->template_contract = json_incref (template_contract);
+ return tph;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_patch_private_template_start (
+ struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph,
+ TALER_MERCHANT_PatchPrivateTemplateCallback cb,
+ TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ tph->cb = cb;
+ tph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/templates/%s",
+ tph->template_id);
+ tph->url = TALER_url_join (tph->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == tph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("template_description",
+ tph->template_description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_id",
+ tph->otp_id)),
+ GNUNET_JSON_pack_object_incref ("template_contract",
+ (json_t *) tph->template_contract));
+ eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&tph->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ tph->job = GNUNET_CURL_job_add2 (tph->ctx,
+ eh,
+ tph->post_ctx.headers,
+ &handle_patch_template_finished,
+ tph);
+ if (NULL == tph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_patch_private_template_cancel (
+ struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph)
+{
+ if (NULL != tph->job)
+ {
+ GNUNET_CURL_job_cancel (tph->job);
+ tph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&tph->post_ctx);
+ json_decref (tph->template_contract);
+ GNUNET_free (tph->url);
+ GNUNET_free (tph->base_url);
+ GNUNET_free (tph->template_id);
+ GNUNET_free (tph->template_description);
+ GNUNET_free (tph->otp_id);
+ GNUNET_free (tph);
+}
+
+
+/* end of merchant_api_patch-private-templates-TEMPLATE_ID-new.c */
diff --git a/src/lib/merchant_api_patch-private-units-UNIT-new.c b/src/lib/merchant_api_patch-private-units-UNIT-new.c
@@ -0,0 +1,378 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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_patch-private-units-UNIT-new.c
+ * @brief Implementation of the PATCH /private/units/$UNIT 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/patch-private-units-UNIT-new.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/$UNIT operation.
+ */
+struct TALER_MERCHANT_PatchPrivateUnitHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PatchPrivateUnitCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_PATCH_PRIVATE_UNIT_RESULT_CLOSURE *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;
+
+ /**
+ * Identifier of the unit to update.
+ */
+ char *unit_id;
+
+ /**
+ * Long name of the unit (optional).
+ */
+ const char *unit_name_long;
+
+ /**
+ * Short name (symbol) of the unit (optional).
+ */
+ const char *unit_name_short;
+
+ /**
+ * Internationalized long names (JSON, optional).
+ */
+ const json_t *unit_name_long_i18n;
+
+ /**
+ * Internationalized short names (JSON, optional).
+ */
+ const json_t *unit_name_short_i18n;
+
+ /**
+ * Whether fractional quantities are allowed.
+ */
+ bool unit_allow_fraction;
+
+ /**
+ * Whether unit_allow_fraction was explicitly set.
+ */
+ bool have_unit_allow_fraction;
+
+ /**
+ * Precision level for fractions.
+ */
+ uint32_t unit_precision_level;
+
+ /**
+ * Whether unit_precision_level was explicitly set.
+ */
+ bool have_unit_precision_level;
+
+ /**
+ * Active status of the unit.
+ */
+ bool unit_active;
+
+ /**
+ * Whether unit_active was explicitly set.
+ */
+ bool have_unit_active;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /private/units/$UNIT request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PatchPrivateUnitHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_patch_unit_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PatchPrivateUnitHandle *uph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PatchPrivateUnitResponse upr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ uph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /private/units/$UNIT 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:
+ upr.hr.ec = TALER_JSON_get_error_code (json);
+ upr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case 0:
+ upr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &upr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response %u/%d for PATCH /private/units\n",
+ (unsigned int) response_code,
+ (int) upr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ uph->cb (uph->cb_cls,
+ &upr);
+ TALER_MERCHANT_patch_private_unit_cancel (uph);
+}
+
+
+struct TALER_MERCHANT_PatchPrivateUnitHandle *
+TALER_MERCHANT_patch_private_unit_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *unit_id)
+{
+ struct TALER_MERCHANT_PatchPrivateUnitHandle *uph;
+
+ uph = GNUNET_new (struct TALER_MERCHANT_PatchPrivateUnitHandle);
+ uph->ctx = ctx;
+ uph->base_url = GNUNET_strdup (url);
+ uph->unit_id = GNUNET_strdup (unit_id);
+ return uph;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_patch_private_unit_set_options_ (
+ struct TALER_MERCHANT_PatchPrivateUnitHandle *uph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PatchPrivateUnitOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_PATCH_PRIVATE_UNIT_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_PATCH_PRIVATE_UNIT_OPTION_UNIT_NAME_LONG:
+ uph->unit_name_long = options[i].details.unit_name_long;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_UNIT_OPTION_UNIT_NAME_SHORT:
+ uph->unit_name_short = options[i].details.unit_name_short;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_UNIT_OPTION_UNIT_NAME_LONG_I18N:
+ uph->unit_name_long_i18n = options[i].details.unit_name_long_i18n;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_UNIT_OPTION_UNIT_NAME_SHORT_I18N:
+ uph->unit_name_short_i18n = options[i].details.unit_name_short_i18n;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_UNIT_OPTION_UNIT_ALLOW_FRACTION:
+ uph->unit_allow_fraction = options[i].details.unit_allow_fraction;
+ uph->have_unit_allow_fraction = true;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_UNIT_OPTION_UNIT_PRECISION_LEVEL:
+ uph->unit_precision_level = options[i].details.unit_precision_level;
+ uph->have_unit_precision_level = true;
+ break;
+ case TALER_MERCHANT_PATCH_PRIVATE_UNIT_OPTION_UNIT_ACTIVE:
+ uph->unit_active = options[i].details.unit_active;
+ uph->have_unit_active = true;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_patch_private_unit_start (
+ struct TALER_MERCHANT_PatchPrivateUnitHandle *uph,
+ TALER_MERCHANT_PatchPrivateUnitCallback cb,
+ TALER_MERCHANT_PATCH_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ uph->cb = cb;
+ uph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/units/%s",
+ uph->unit_id);
+ uph->url = TALER_url_join (uph->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == uph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+
+ /* Build JSON request body from options that were set */
+ req_obj = json_object ();
+ if (NULL == req_obj)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ if (NULL != uph->unit_name_long)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_name_long",
+ json_string (uph->unit_name_long)));
+ }
+ if (NULL != uph->unit_name_short)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_name_short",
+ json_string (uph->unit_name_short)));
+ }
+ if (NULL != uph->unit_name_long_i18n)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (
+ req_obj,
+ "unit_name_long_i18n",
+ json_incref ((json_t *) uph->unit_name_long_i18n)));
+ }
+ if (NULL != uph->unit_name_short_i18n)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (
+ req_obj,
+ "unit_name_short_i18n",
+ json_incref ((json_t *) uph->unit_name_short_i18n)));
+ }
+ if (uph->have_unit_allow_fraction)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_allow_fraction",
+ json_boolean (
+ uph->unit_allow_fraction)));
+ }
+ if (uph->have_unit_precision_level)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_precision_level",
+ json_integer (
+ (json_int_t)
+ uph->unit_precision_level)));
+ }
+ if (uph->have_unit_active)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_active",
+ json_boolean (uph->unit_active)));
+ }
+ eh = TALER_MERCHANT_curl_easy_get_ (uph->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&uph->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ uph->job = GNUNET_CURL_job_add2 (uph->ctx,
+ eh,
+ uph->post_ctx.headers,
+ &handle_patch_unit_finished,
+ uph);
+ if (NULL == uph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_patch_private_unit_cancel (
+ struct TALER_MERCHANT_PatchPrivateUnitHandle *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->base_url);
+ GNUNET_free (uph->unit_id);
+ GNUNET_free (uph);
+}
+
+
+/* end of merchant_api_patch-private-units-UNIT-new.c */
diff --git a/src/lib/merchant_api_patch-private-webhooks-WEBHOOK_ID-new.c b/src/lib/merchant_api_patch-private-webhooks-WEBHOOK_ID-new.c
@@ -0,0 +1,300 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_patch-private-webhooks-WEBHOOK_ID-new.c
+ * @brief Implementation of the PATCH /private/webhooks/$WEBHOOK_ID 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/patch-private-webhooks-WEBHOOK_ID-new.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/webhooks/$WEBHOOK_ID operation.
+ */
+struct TALER_MERCHANT_PatchPrivateWebhookHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PatchPrivateWebhookCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_PATCH_PRIVATE_WEBHOOK_RESULT_CLOSURE *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;
+
+ /**
+ * Identifier of the webhook to update.
+ */
+ char *webhook_id;
+
+ /**
+ * New event type.
+ */
+ char *event_type;
+
+ /**
+ * New notification URL template.
+ */
+ char *url_template;
+
+ /**
+ * New HTTP method.
+ */
+ char *http_method;
+
+ /**
+ * New header template.
+ */
+ char *header_template;
+
+ /**
+ * New body template.
+ */
+ char *body_template;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP PATCH /private/webhooks/$WEBHOOK_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PatchPrivateWebhookHandle`
+ * @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_PatchPrivateWebhookHandle *wph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PatchPrivateWebhookResponse wpr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ wph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "PATCH /private/webhooks/$WEBHOOK_ID completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ wpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.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:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ /* Nothing really to verify, merchant says we need to authenticate. */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.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,
+ &wpr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) wpr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ wph->cb (wph->cb_cls,
+ &wpr);
+ TALER_MERCHANT_patch_private_webhook_cancel (wph);
+}
+
+
+struct TALER_MERCHANT_PatchPrivateWebhookHandle *
+TALER_MERCHANT_patch_private_webhook_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *webhook_id,
+ const char *event_type,
+ const char *url_template,
+ const char *http_method,
+ const char *header_template,
+ const char *body_template)
+{
+ struct TALER_MERCHANT_PatchPrivateWebhookHandle *wph;
+
+ wph = GNUNET_new (struct TALER_MERCHANT_PatchPrivateWebhookHandle);
+ wph->ctx = ctx;
+ wph->base_url = GNUNET_strdup (url);
+ wph->webhook_id = GNUNET_strdup (webhook_id);
+ wph->event_type = GNUNET_strdup (event_type);
+ wph->url_template = GNUNET_strdup (url_template);
+ wph->http_method = GNUNET_strdup (http_method);
+ wph->header_template = GNUNET_strdup (header_template);
+ wph->body_template = GNUNET_strdup (body_template);
+ return wph;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_patch_private_webhook_start (
+ struct TALER_MERCHANT_PatchPrivateWebhookHandle *wph,
+ TALER_MERCHANT_PatchPrivateWebhookCallback cb,
+ TALER_MERCHANT_PATCH_PRIVATE_WEBHOOK_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ wph->cb = cb;
+ wph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/webhooks/%s",
+ wph->webhook_id);
+ wph->url = TALER_url_join (wph->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == wph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("event_type",
+ wph->event_type),
+ GNUNET_JSON_pack_string ("url",
+ wph->url_template),
+ GNUNET_JSON_pack_string ("http_method",
+ wph->http_method),
+ GNUNET_JSON_pack_string ("header_template",
+ wph->header_template),
+ GNUNET_JSON_pack_string ("body_template",
+ wph->body_template));
+ eh = TALER_MERCHANT_curl_easy_get_ (wph->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&wph->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_PATCH));
+ wph->job = GNUNET_CURL_job_add2 (wph->ctx,
+ eh,
+ wph->post_ctx.headers,
+ &handle_patch_webhook_finished,
+ wph);
+ if (NULL == wph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_patch_private_webhook_cancel (
+ struct TALER_MERCHANT_PatchPrivateWebhookHandle *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->base_url);
+ GNUNET_free (wph->webhook_id);
+ GNUNET_free (wph->event_type);
+ GNUNET_free (wph->url_template);
+ GNUNET_free (wph->http_method);
+ GNUNET_free (wph->header_template);
+ GNUNET_free (wph->body_template);
+ GNUNET_free (wph);
+}
+
+
+/* end of merchant_api_patch-private-webhooks-WEBHOOK_ID-new.c */
diff --git a/src/lib/merchant_api_pay_service.c b/src/lib/merchant_api_pay_service.c
@@ -1,1070 +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 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-new.c b/src/lib/merchant_api_post-management-instances-INSTANCE-auth-new.c
@@ -0,0 +1,254 @@
+/*
+ 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_post-management-instances-INSTANCE-auth-new.c
+ * @brief Implementation of the POST /management/instances/$INSTANCE/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/post-management-instances-INSTANCE-auth-new.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 /management/instances/$INSTANCE/auth operation.
+ */
+struct TALER_MERCHANT_PostManagementInstancesAuthHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostManagementInstancesAuthCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_AUTH_RESULT_CLOSURE *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;
+
+ /**
+ * Instance identifier.
+ */
+ char *instance_id;
+
+ /**
+ * New authentication password.
+ */
+ char *auth_password;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /management/instances/$INSTANCE/auth request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostManagementInstancesAuthHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_management_instances_auth_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostManagementInstancesAuthHandle *piah = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostManagementInstancesAuthResponse iar = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ piah->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "POST /management/instances/$INSTANCE/auth completed with "
+ "response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ iar.hr.ec = TALER_JSON_get_error_code (json);
+ iar.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ iar.hr.ec = TALER_JSON_get_error_code (json);
+ iar.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ iar.hr.ec = TALER_JSON_get_error_code (json);
+ iar.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) iar.hr.ec);
+ break;
+ }
+ piah->cb (piah->cb_cls,
+ &iar);
+ TALER_MERCHANT_post_management_instances_auth_cancel (piah);
+}
+
+
+struct TALER_MERCHANT_PostManagementInstancesAuthHandle *
+TALER_MERCHANT_post_management_instances_auth_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *instance_id,
+ const char *auth_password)
+{
+ struct TALER_MERCHANT_PostManagementInstancesAuthHandle *piah;
+
+ piah = GNUNET_new (struct TALER_MERCHANT_PostManagementInstancesAuthHandle);
+ piah->ctx = ctx;
+ piah->base_url = GNUNET_strdup (url);
+ if (NULL != instance_id)
+ piah->instance_id = GNUNET_strdup (instance_id);
+ if (NULL != auth_password)
+ piah->auth_password = GNUNET_strdup (auth_password);
+ return piah;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_management_instances_auth_start (
+ struct TALER_MERCHANT_PostManagementInstancesAuthHandle *piah,
+ TALER_MERCHANT_PostManagementInstancesAuthCallback cb,
+ TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_AUTH_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ piah->cb = cb;
+ piah->cb_cls = cb_cls;
+ if (NULL != piah->instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "management/instances/%s/auth",
+ piah->instance_id);
+ piah->url = TALER_url_join (piah->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ /* backend_url is already identifying the instance */
+ piah->url = TALER_url_join (piah->base_url,
+ "private/auth",
+ NULL);
+ }
+ if (NULL == piah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+
+ if (NULL == piah->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",
+ piah->auth_password));
+ }
+ eh = TALER_MERCHANT_curl_easy_get_ (piah->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&piah->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_POST));
+ piah->job = GNUNET_CURL_job_add2 (piah->ctx,
+ eh,
+ piah->post_ctx.headers,
+ &
+ handle_post_management_instances_auth_finished,
+ piah);
+ if (NULL == piah->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_management_instances_auth_cancel (
+ struct TALER_MERCHANT_PostManagementInstancesAuthHandle *piah)
+{
+ if (NULL != piah->job)
+ {
+ GNUNET_CURL_job_cancel (piah->job);
+ piah->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&piah->post_ctx);
+ GNUNET_free (piah->instance_id);
+ GNUNET_free (piah->auth_password);
+ GNUNET_free (piah->url);
+ GNUNET_free (piah->base_url);
+ GNUNET_free (piah);
+}
+
+
+/* end of merchant_api_post-management-instances-INSTANCE-auth-new.c */
diff --git a/src/lib/merchant_api_post-management-instances-new.c b/src/lib/merchant_api_post-management-instances-new.c
@@ -0,0 +1,333 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_post-management-instances-new.c
+ * @brief Implementation of the POST /management/instances 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/post-management-instances-new.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 /management/instances operation.
+ */
+struct TALER_MERCHANT_PostManagementInstancesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostManagementInstancesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_RESULT_CLOSURE *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;
+
+ /**
+ * Instance identifier.
+ */
+ char *instance_id;
+
+ /**
+ * Human-readable name.
+ */
+ char *name;
+
+ /**
+ * Address (JSON).
+ */
+ json_t *address;
+
+ /**
+ * Jurisdiction (JSON).
+ */
+ json_t *jurisdiction;
+
+ /**
+ * Whether to use the STEFAN curve.
+ */
+ bool use_stefan;
+
+ /**
+ * Default wire transfer delay.
+ */
+ struct GNUNET_TIME_Relative default_wire_transfer_delay;
+
+ /**
+ * Default payment deadline.
+ */
+ struct GNUNET_TIME_Relative default_pay_delay;
+
+ /**
+ * Default refund deadline.
+ */
+ struct GNUNET_TIME_Relative default_refund_delay;
+
+ /**
+ * Authentication password (or NULL).
+ */
+ char *auth_password;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /management/instances request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostManagementInstancesHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_management_instances_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostManagementInstancesHandle *pmih = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostManagementInstancesResponse mir = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pmih->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:
+ mir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ mir.hr.ec = TALER_JSON_get_error_code (json);
+ mir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ mir.hr.ec = TALER_JSON_get_error_code (json);
+ mir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ mir.hr.ec = TALER_JSON_get_error_code (json);
+ mir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ mir.hr.ec = TALER_JSON_get_error_code (json);
+ mir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ mir.hr.ec = TALER_JSON_get_error_code (json);
+ mir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ mir.hr.ec = TALER_JSON_get_error_code (json);
+ mir.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &mir.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) mir.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ pmih->cb (pmih->cb_cls,
+ &mir);
+ TALER_MERCHANT_post_management_instances_cancel (pmih);
+}
+
+
+struct TALER_MERCHANT_PostManagementInstancesHandle *
+TALER_MERCHANT_post_management_instances_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *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)
+{
+ struct TALER_MERCHANT_PostManagementInstancesHandle *pmih;
+
+ pmih = GNUNET_new (struct TALER_MERCHANT_PostManagementInstancesHandle);
+ pmih->ctx = ctx;
+ pmih->base_url = GNUNET_strdup (url);
+ pmih->instance_id = GNUNET_strdup (instance_id);
+ pmih->name = GNUNET_strdup (name);
+ pmih->address = json_incref ((json_t *) address);
+ pmih->jurisdiction = json_incref ((json_t *) jurisdiction);
+ pmih->use_stefan = use_stefan;
+ pmih->default_wire_transfer_delay = default_wire_transfer_delay;
+ pmih->default_pay_delay = default_pay_delay;
+ pmih->default_refund_delay = default_refund_delay;
+ if (NULL != auth_password)
+ pmih->auth_password = GNUNET_strdup (auth_password);
+ return pmih;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_management_instances_start (
+ struct TALER_MERCHANT_PostManagementInstancesHandle *pmih,
+ TALER_MERCHANT_PostManagementInstancesCallback cb,
+ TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ json_t *auth_obj;
+ CURL *eh;
+
+ pmih->cb = cb;
+ pmih->cb_cls = cb_cls;
+ pmih->url = TALER_url_join (pmih->base_url,
+ "management/instances",
+ NULL);
+ if (NULL == pmih->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ if (NULL != pmih->auth_password)
+ {
+ auth_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("method",
+ "token"),
+ GNUNET_JSON_pack_string ("password",
+ pmih->auth_password));
+ }
+ else
+ {
+ auth_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("method",
+ "external"));
+ }
+ if (NULL == auth_obj)
+ {
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("id",
+ pmih->instance_id),
+ GNUNET_JSON_pack_string ("name",
+ pmih->name),
+ GNUNET_JSON_pack_object_incref ("address",
+ pmih->address),
+ GNUNET_JSON_pack_object_incref ("jurisdiction",
+ pmih->jurisdiction),
+ GNUNET_JSON_pack_bool ("use_stefan",
+ pmih->use_stefan),
+ GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay",
+ pmih->default_wire_transfer_delay),
+ GNUNET_JSON_pack_time_rel ("default_pay_delay",
+ pmih->default_pay_delay),
+ GNUNET_JSON_pack_time_rel ("default_refund_delay",
+ pmih->default_refund_delay),
+ GNUNET_JSON_pack_time_rounder_interval (
+ "default_wire_transfer_rounding_interval",
+ GNUNET_TIME_RI_NONE),
+ GNUNET_JSON_pack_object_steal ("auth",
+ auth_obj));
+ eh = TALER_MERCHANT_curl_easy_get_ (pmih->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&pmih->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ pmih->job = GNUNET_CURL_job_add2 (pmih->ctx,
+ eh,
+ pmih->post_ctx.headers,
+ &handle_post_management_instances_finished,
+ pmih);
+ if (NULL == pmih->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_management_instances_cancel (
+ struct TALER_MERCHANT_PostManagementInstancesHandle *pmih)
+{
+ if (NULL != pmih->job)
+ {
+ GNUNET_CURL_job_cancel (pmih->job);
+ pmih->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pmih->post_ctx);
+ json_decref (pmih->address);
+ json_decref (pmih->jurisdiction);
+ GNUNET_free (pmih->instance_id);
+ GNUNET_free (pmih->name);
+ GNUNET_free (pmih->auth_password);
+ GNUNET_free (pmih->url);
+ GNUNET_free (pmih->base_url);
+ GNUNET_free (pmih);
+}
+
+
+/* end of merchant_api_post-management-instances-new.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-abort-new.c b/src/lib/merchant_api_post-orders-ORDER_ID-abort-new.c
@@ -0,0 +1,432 @@
+/*
+ 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_post-orders-ORDER_ID-abort-new.c
+ * @brief Implementation of the POST /orders/$ID/abort 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/post-orders-ORDER_ID-abort-new.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_signatures.h>
+
+
+/**
+ * Maximum number of refunds we return.
+ */
+#define MAX_REFUNDS 1024
+
+
+/**
+ * Handle for a POST /orders/$ORDER_ID/abort operation.
+ */
+struct TALER_MERCHANT_PostOrdersAbortHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostOrdersAbortCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_ORDERS_ABORT_RESULT_CLOSURE *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;
+
+ /**
+ * Order identifier.
+ */
+ char *order_id;
+
+ /**
+ * Public key of the merchant.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Hash of the contract terms.
+ */
+ struct TALER_PrivateContractHashP h_contract;
+
+ /**
+ * The coins we are aborting on.
+ */
+ struct TALER_MERCHANT_PostOrdersAbortCoin *coins;
+
+ /**
+ * Number of @e coins.
+ */
+ 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 poah 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_PostOrdersAbortHandle *poah,
+ struct TALER_MERCHANT_PostOrdersAbortResponse *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_PostOrdersAbortedCoin 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;
+ }
+ res[i].coin_pub = poah->coins[i].coin_pub;
+
+ if (GNUNET_OK !=
+ TALER_exchange_online_refund_confirmation_verify (
+ &poah->h_contract,
+ &poah->coins[i].coin_pub,
+ &poah->merchant_pub,
+ 0, /* transaction id */
+ &poah->coins[i].amount_with_fee,
+ &res[i].exchange_pub,
+ &res[i].exchange_sig))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+ ar->details.ok.num_aborts = num_refunds;
+ ar->details.ok.aborts = res;
+ poah->cb (poah->cb_cls,
+ ar);
+ poah->cb = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /orders/$ID/abort request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostOrdersAbortHandle`
+ * @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_PostOrdersAbortHandle *poah = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostOrdersAbortResponse ar = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ poah->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /orders/$ID/abort 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 (poah,
+ &ar,
+ json))
+ {
+ TALER_MERCHANT_post_orders_abort_cancel (poah);
+ 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);
+ 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);
+ break;
+ case MHD_HTTP_REQUEST_TIMEOUT:
+ ar.hr.ec = TALER_JSON_get_error_code (json);
+ ar.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_PRECONDITION_FAILED:
+ 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);
+ break;
+ case MHD_HTTP_BAD_GATEWAY:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &ar.hr);
+ break;
+ default:
+ 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;
+ }
+ poah->cb (poah->cb_cls,
+ &ar);
+ TALER_MERCHANT_post_orders_abort_cancel (poah);
+}
+
+
+struct TALER_MERCHANT_PostOrdersAbortHandle *
+TALER_MERCHANT_post_orders_abort_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *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_PostOrdersAbortCoin coins[static num_coins])
+{
+ struct TALER_MERCHANT_PostOrdersAbortHandle *poah;
+
+ poah = GNUNET_new (struct TALER_MERCHANT_PostOrdersAbortHandle);
+ poah->ctx = ctx;
+ poah->base_url = GNUNET_strdup (url);
+ poah->order_id = GNUNET_strdup (order_id);
+ poah->merchant_pub = *merchant_pub;
+ poah->h_contract = *h_contract;
+ poah->num_coins = num_coins;
+ poah->coins = GNUNET_new_array (num_coins,
+ struct TALER_MERCHANT_PostOrdersAbortCoin);
+ GNUNET_memcpy (poah->coins,
+ coins,
+ num_coins * sizeof (struct TALER_MERCHANT_PostOrdersAbortCoin))
+ ;
+ return poah;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_orders_abort_start (
+ struct TALER_MERCHANT_PostOrdersAbortHandle *poah,
+ TALER_MERCHANT_PostOrdersAbortCallback cb,
+ TALER_MERCHANT_POST_ORDERS_ABORT_RESULT_CLOSURE *cb_cls)
+{
+ json_t *abort_obj;
+ json_t *j_coins;
+ CURL *eh;
+
+ poah->cb = cb;
+ poah->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/abort",
+ poah->order_id);
+ poah->url = TALER_url_join (poah->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == poah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ j_coins = json_array ();
+ if (NULL == j_coins)
+ {
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ for (unsigned int i = 0; i<poah->num_coins; i++)
+ {
+ const struct TALER_MERCHANT_PostOrdersAbortCoin *ac = &poah->coins[i];
+ json_t *j_coin;
+
+ j_coin = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("coin_pub",
+ &ac->coin_pub),
+ 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 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ }
+ abort_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("coins",
+ j_coins),
+ GNUNET_JSON_pack_data_auto ("h_contract",
+ &poah->h_contract));
+ eh = TALER_MERCHANT_curl_easy_get_ (poah->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&poah->post_ctx,
+ eh,
+ abort_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (abort_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (abort_obj);
+ poah->job = GNUNET_CURL_job_add2 (poah->ctx,
+ eh,
+ poah->post_ctx.headers,
+ &handle_abort_finished,
+ poah);
+ if (NULL == poah->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_orders_abort_cancel (
+ struct TALER_MERCHANT_PostOrdersAbortHandle *poah)
+{
+ if (NULL != poah->job)
+ {
+ GNUNET_CURL_job_cancel (poah->job);
+ poah->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&poah->post_ctx);
+ GNUNET_free (poah->coins);
+ GNUNET_free (poah->order_id);
+ GNUNET_free (poah->url);
+ GNUNET_free (poah->base_url);
+ GNUNET_free (poah);
+}
+
+
+/* end of merchant_api_post-orders-ORDER_ID-abort-new.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-claim-new.c b/src/lib/merchant_api_post-orders-ORDER_ID-claim-new.c
@@ -0,0 +1,294 @@
+/*
+ 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_post-orders-ORDER_ID-claim-new.c
+ * @brief Implementation of the POST /orders/$ID/claim 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/post-orders-ORDER_ID-claim-new.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_signatures.h>
+
+
+/**
+ * Handle for a POST /orders/$ORDER_ID/claim operation.
+ */
+struct TALER_MERCHANT_PostOrdersClaimHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostOrdersClaimCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_ORDERS_CLAIM_RESULT_CLOSURE *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;
+
+ /**
+ * Order identifier.
+ */
+ char *order_id;
+
+ /**
+ * Wallet nonce for claiming.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey nonce;
+
+ /**
+ * Optional claim token.
+ */
+ const struct TALER_ClaimTokenP *token;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /orders/$ID/claim request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostOrdersClaimHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_order_claim_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostOrdersClaimHandle *poch = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostOrdersClaimResponse 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.merchant_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ poch->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /orders/$ID/claim completed with response code %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,
+ "Claim order failed with HTTP status code %u/%d\n",
+ (unsigned int) response_code,
+ (int) ocr.hr.ec);
+ poch->cb (poch->cb_cls,
+ &ocr);
+ TALER_MERCHANT_post_orders_claim_cancel (poch);
+ 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;
+ poch->cb (poch->cb_cls,
+ &ocr);
+ TALER_MERCHANT_post_orders_claim_cancel (poch);
+ 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);
+ poch->cb (poch->cb_cls,
+ &ocr);
+ TALER_MERCHANT_post_orders_claim_cancel (poch);
+ return;
+ }
+
+ poch->cb (poch->cb_cls,
+ &ocr);
+ GNUNET_JSON_parse_free (spec);
+ TALER_MERCHANT_post_orders_claim_cancel (poch);
+}
+
+
+struct TALER_MERCHANT_PostOrdersClaimHandle *
+TALER_MERCHANT_post_orders_claim_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *nonce)
+{
+ struct TALER_MERCHANT_PostOrdersClaimHandle *poch;
+
+ poch = GNUNET_new (struct TALER_MERCHANT_PostOrdersClaimHandle);
+ poch->ctx = ctx;
+ poch->base_url = GNUNET_strdup (url);
+ poch->order_id = GNUNET_strdup (order_id);
+ poch->nonce = *nonce;
+ return poch;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_orders_claim_set_options_ (
+ struct TALER_MERCHANT_PostOrdersClaimHandle *poch,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostOrdersClaimOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_ORDERS_CLAIM_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_ORDERS_CLAIM_OPTION_TOKEN:
+ poch->token = options[i].details.token;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_orders_claim_start (
+ struct TALER_MERCHANT_PostOrdersClaimHandle *poch,
+ TALER_MERCHANT_PostOrdersClaimCallback cb,
+ TALER_MERCHANT_POST_ORDERS_CLAIM_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ poch->cb = cb;
+ poch->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/claim",
+ poch->order_id);
+ poch->url = TALER_url_join (poch->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == poch->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("nonce",
+ &poch->nonce),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_data_auto ("token",
+ poch->token)));
+ eh = TALER_MERCHANT_curl_easy_get_ (poch->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&poch->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ poch->job = GNUNET_CURL_job_add2 (poch->ctx,
+ eh,
+ poch->post_ctx.headers,
+ &handle_post_order_claim_finished,
+ poch);
+ if (NULL == poch->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_orders_claim_cancel (
+ struct TALER_MERCHANT_PostOrdersClaimHandle *poch)
+{
+ if (NULL != poch->job)
+ {
+ GNUNET_CURL_job_cancel (poch->job);
+ poch->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&poch->post_ctx);
+ GNUNET_free (poch->order_id);
+ GNUNET_free (poch->url);
+ GNUNET_free (poch->base_url);
+ GNUNET_free (poch);
+}
+
+
+/* end of merchant_api_post-orders-ORDER_ID-claim-new.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-paid-new.c b/src/lib/merchant_api_post-orders-ORDER_ID-paid-new.c
@@ -0,0 +1,298 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_post-orders-ORDER_ID-paid-new.c
+ * @brief Implementation of the POST /orders/$ORDER_ID/paid 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/post-orders-ORDER_ID-paid-new.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_signatures.h>
+
+
+/**
+ * Handle for a POST /orders/$ORDER_ID/paid operation.
+ */
+struct TALER_MERCHANT_PostOrdersPaidHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostOrdersPaidCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_ORDERS_PAID_RESULT_CLOSURE *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;
+
+ /**
+ * Order identifier.
+ */
+ char *order_id;
+
+ /**
+ * Session identifier.
+ */
+ char *session_id;
+
+ /**
+ * Hash of the contract terms.
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ /**
+ * Merchant signature over the session data.
+ */
+ struct TALER_MerchantSignatureP merchant_sig;
+
+ /**
+ * Optional hash of wallet-specific data.
+ */
+ const struct GNUNET_HashCode *wallet_data_hash;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /orders/$ORDER_ID/paid request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostOrdersPaidHandle`
+ * @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_PostOrdersPaidHandle *poph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostOrdersPaidResponse opr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ poph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /orders/$ID/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:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ opr.hr.ec = TALER_JSON_get_error_code (json);
+ opr.hr.hint = TALER_JSON_get_error_hint (json);
+ 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);
+ break;
+ case MHD_HTTP_SERVICE_UNAVAILABLE:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &opr.hr);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &opr.hr);
+ 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;
+ }
+ poph->cb (poph->cb_cls,
+ &opr);
+ TALER_MERCHANT_post_orders_paid_cancel (poph);
+}
+
+
+struct TALER_MERCHANT_PostOrdersPaidHandle *
+TALER_MERCHANT_post_orders_paid_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id,
+ const char *session_id,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ const struct TALER_MerchantSignatureP *merchant_sig)
+{
+ struct TALER_MERCHANT_PostOrdersPaidHandle *poph;
+
+ poph = GNUNET_new (struct TALER_MERCHANT_PostOrdersPaidHandle);
+ poph->ctx = ctx;
+ poph->base_url = GNUNET_strdup (url);
+ poph->order_id = GNUNET_strdup (order_id);
+ poph->session_id = GNUNET_strdup (session_id);
+ poph->h_contract_terms = *h_contract_terms;
+ poph->merchant_sig = *merchant_sig;
+ return poph;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_orders_paid_set_options_ (
+ struct TALER_MERCHANT_PostOrdersPaidHandle *poph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostOrdersPaidOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_ORDERS_PAID_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_ORDERS_PAID_OPTION_WALLET_DATA_HASH:
+ poph->wallet_data_hash = options[i].details.wallet_data_hash;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_orders_paid_start (
+ struct TALER_MERCHANT_PostOrdersPaidHandle *poph,
+ TALER_MERCHANT_PostOrdersPaidCallback cb,
+ TALER_MERCHANT_POST_ORDERS_PAID_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ poph->cb = cb;
+ poph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/paid",
+ poph->order_id);
+ poph->url = TALER_url_join (poph->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == poph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("sig",
+ &poph->merchant_sig),
+ GNUNET_JSON_pack_data_auto ("h_contract",
+ &poph->h_contract_terms),
+ GNUNET_JSON_pack_string ("session_id",
+ poph->session_id));
+ eh = TALER_MERCHANT_curl_easy_get_ (poph->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&poph->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ poph->job = GNUNET_CURL_job_add2 (poph->ctx,
+ eh,
+ poph->post_ctx.headers,
+ &handle_paid_finished,
+ poph);
+ if (NULL == poph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_orders_paid_cancel (
+ struct TALER_MERCHANT_PostOrdersPaidHandle *poph)
+{
+ if (NULL != poph->job)
+ {
+ GNUNET_CURL_job_cancel (poph->job);
+ poph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&poph->post_ctx);
+ GNUNET_free (poph->order_id);
+ GNUNET_free (poph->session_id);
+ GNUNET_free (poph->url);
+ GNUNET_free (poph->base_url);
+ GNUNET_free (poph);
+}
+
+
+/* end of merchant_api_post-orders-ORDER_ID-paid-new.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-pay-new.c b/src/lib/merchant_api_post-orders-ORDER_ID-pay-new.c
@@ -0,0 +1,1020 @@
+/*
+ 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_post-orders-ORDER_ID-pay-new.c
+ * @brief Implementation of the POST /orders/$ORDER_ID/pay 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/post-orders-ORDER_ID-pay-new.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_signatures.h>
+
+
+/**
+ * Handle for a POST /orders/$ORDER_ID/pay operation.
+ */
+struct TALER_MERCHANT_PostOrdersPayHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostOrdersPayCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_ORDERS_PAY_RESULT_CLOSURE *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;
+
+ /**
+ * Order identifier.
+ */
+ char *order_id;
+
+ /**
+ * The coins we are paying with (frontend mode, already signed).
+ */
+ struct TALER_MERCHANT_PostOrdersPayPaidCoin *paid_coins;
+
+ /**
+ * Number of @e paid_coins.
+ */
+ unsigned int num_paid_coins;
+
+ /**
+ * Hash of the contract terms (wallet mode).
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ /**
+ * Public key of the merchant (wallet mode).
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Merchant signature (wallet mode).
+ */
+ struct TALER_MerchantSignatureP merchant_sig;
+
+ /**
+ * Total payment amount (wallet mode).
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Maximum fee (wallet mode).
+ */
+ struct TALER_Amount max_fee;
+
+ /**
+ * Contract timestamp (wallet mode).
+ */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /**
+ * Refund deadline (wallet mode).
+ */
+ struct GNUNET_TIME_Timestamp refund_deadline;
+
+ /**
+ * Payment deadline (wallet mode).
+ */
+ struct GNUNET_TIME_Timestamp pay_deadline;
+
+ /**
+ * Hash of merchant wire details (wallet mode).
+ */
+ struct TALER_MerchantWireHashP h_wire;
+
+ /**
+ * Choice index (wallet mode).
+ */
+ int choice_index;
+
+ /**
+ * Coins with private keys (wallet mode).
+ */
+ struct TALER_MERCHANT_PostOrdersPayCoin *coins;
+
+ /**
+ * Number of @e coins (wallet mode).
+ */
+ unsigned int num_coins;
+
+ /**
+ * Optional session identifier.
+ */
+ char *session_id;
+
+ /**
+ * Optional wallet data (JSON).
+ */
+ json_t *wallet_data;
+
+ /**
+ * Used tokens (public form, frontend mode).
+ */
+ struct TALER_MERCHANT_PostOrdersPayUsedToken *used_tokens;
+
+ /**
+ * Number of @e used_tokens.
+ */
+ unsigned int num_used_tokens;
+
+ /**
+ * Use tokens (private form, wallet mode).
+ */
+ struct TALER_MERCHANT_PostOrdersPayUseToken *use_tokens;
+
+ /**
+ * Number of @e use_tokens.
+ */
+ unsigned int num_use_tokens;
+
+ /**
+ * Output tokens (wallet mode).
+ */
+ struct TALER_MERCHANT_PostOrdersPayOutputToken *output_tokens;
+
+ /**
+ * Number of @e output_tokens.
+ */
+ unsigned int num_output_tokens;
+
+ /**
+ * Output tokens as JSON array (frontend mode).
+ */
+ json_t *output_tokens_json;
+
+ /**
+ * Set to true if this is the wallet mode (private keys available).
+ */
+ bool am_wallet;
+};
+
+
+/**
+ * Parse blindly signed output tokens from JSON response.
+ *
+ * @param token_sigs the JSON array with the token signatures, can be NULL
+ * @param[out] tokens where to store the parsed tokens
+ * @param[out] num_tokens where to store the length of the @a tokens array
+ * @return #GNUNET_YES on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_tokens (const json_t *token_sigs,
+ struct TALER_MERCHANT_PostOrdersPayOutputToken **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_PostOrdersPayOutputToken *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 POST /orders/$ORDER_ID/pay request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostOrdersPayHandle`
+ * @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_PostOrdersPayHandle *poph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostOrdersPayResponse pr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ poph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /orders/$ID/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 (poph->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 (&poph->h_contract_terms,
+ &poph->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;
+ 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);
+ break;
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ 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);
+ break;
+ case MHD_HTTP_REQUEST_TIMEOUT:
+ pr.hr.ec = TALER_JSON_get_error_code (json);
+ pr.hr.hint = TALER_JSON_get_error_hint (json);
+ 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);
+ break;
+ case MHD_HTTP_PRECONDITION_FAILED:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ 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;
+ poph->cb (poph->cb_cls,
+ &pr);
+ TALER_MERCHANT_post_orders_pay_cancel (poph);
+ return;
+ }
+ }
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ break;
+ case MHD_HTTP_BAD_GATEWAY:
+ 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);
+ break;
+ case MHD_HTTP_GATEWAY_TIMEOUT:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pr.hr);
+ 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;
+ }
+ poph->cb (poph->cb_cls,
+ &pr);
+ TALER_MERCHANT_post_orders_pay_cancel (poph);
+}
+
+
+struct TALER_MERCHANT_PostOrdersPayHandle *
+TALER_MERCHANT_post_orders_pay_frontend_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id,
+ unsigned int num_coins,
+ const struct TALER_MERCHANT_PostOrdersPayPaidCoin coins[static num_coins])
+{
+ struct TALER_MERCHANT_PostOrdersPayHandle *poph;
+
+ poph = GNUNET_new (struct TALER_MERCHANT_PostOrdersPayHandle);
+ poph->ctx = ctx;
+ poph->base_url = GNUNET_strdup (url);
+ poph->order_id = GNUNET_strdup (order_id);
+ poph->am_wallet = false;
+ poph->num_paid_coins = num_coins;
+ poph->paid_coins = GNUNET_new_array (
+ num_coins,
+ struct TALER_MERCHANT_PostOrdersPayPaidCoin);
+ for (unsigned int i = 0; i < num_coins; i++)
+ {
+ struct TALER_MERCHANT_PostOrdersPayPaidCoin *dst = &poph->paid_coins[i];
+ const struct TALER_MERCHANT_PostOrdersPayPaidCoin *src = &coins[i];
+
+ *dst = *src;
+ /* deep copy fields that need it */
+ TALER_denom_pub_copy (&dst->denom_pub,
+ &src->denom_pub);
+ TALER_denom_sig_copy (&dst->denom_sig,
+ &src->denom_sig);
+ dst->exchange_url = GNUNET_strdup (src->exchange_url);
+ }
+ return poph;
+}
+
+
+struct TALER_MERCHANT_PostOrdersPayHandle *
+TALER_MERCHANT_post_orders_pay_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_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,
+ unsigned int num_coins,
+ const struct TALER_MERCHANT_PostOrdersPayCoin coins[static num_coins])
+{
+ struct TALER_MERCHANT_PostOrdersPayHandle *poph;
+
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (amount,
+ max_fee))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ poph = GNUNET_new (struct TALER_MERCHANT_PostOrdersPayHandle);
+ poph->ctx = ctx;
+ poph->base_url = GNUNET_strdup (url);
+ poph->order_id = GNUNET_strdup (order_id);
+ poph->am_wallet = true;
+ poph->h_contract_terms = *h_contract_terms;
+ poph->choice_index = choice_index;
+ poph->amount = *amount;
+ poph->max_fee = *max_fee;
+ poph->merchant_pub = *merchant_pub;
+ poph->merchant_sig = *merchant_sig;
+ poph->timestamp = timestamp;
+ poph->refund_deadline = refund_deadline;
+ poph->pay_deadline = pay_deadline;
+ poph->h_wire = *h_wire;
+ poph->num_coins = num_coins;
+ poph->coins = GNUNET_new_array (num_coins,
+ struct TALER_MERCHANT_PostOrdersPayCoin);
+ for (unsigned int i = 0; i < num_coins; i++)
+ {
+ struct TALER_MERCHANT_PostOrdersPayCoin *dst = &poph->coins[i];
+ const struct TALER_MERCHANT_PostOrdersPayCoin *src = &coins[i];
+
+ *dst = *src;
+ TALER_denom_pub_copy (&dst->denom_pub,
+ &src->denom_pub);
+ TALER_denom_sig_copy (&dst->denom_sig,
+ &src->denom_sig);
+ dst->exchange_url = GNUNET_strdup (src->exchange_url);
+ }
+ return poph;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_orders_pay_set_options_ (
+ struct TALER_MERCHANT_PostOrdersPayHandle *poph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostOrdersPayOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_SESSION_ID:
+ GNUNET_free (poph->session_id);
+ if (NULL != options[i].details.session_id)
+ poph->session_id = GNUNET_strdup (options[i].details.session_id);
+ break;
+ case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_WALLET_DATA:
+ json_decref (poph->wallet_data);
+ poph->wallet_data = NULL;
+ if (NULL != options[i].details.wallet_data)
+ poph->wallet_data = json_incref (
+ (json_t *) options[i].details.wallet_data);
+ break;
+ case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_USED_TOKENS:
+ GNUNET_free (poph->used_tokens);
+ poph->num_used_tokens = options[i].details.used_tokens.num;
+ if (0 < poph->num_used_tokens)
+ {
+ poph->used_tokens = GNUNET_new_array (
+ poph->num_used_tokens,
+ struct TALER_MERCHANT_PostOrdersPayUsedToken);
+ GNUNET_memcpy (poph->used_tokens,
+ options[i].details.used_tokens.tokens,
+ poph->num_used_tokens
+ * sizeof (struct TALER_MERCHANT_PostOrdersPayUsedToken));
+ }
+ break;
+ case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_USE_TOKENS:
+ GNUNET_free (poph->use_tokens);
+ poph->num_use_tokens = options[i].details.use_tokens.num;
+ if (0 < poph->num_use_tokens)
+ {
+ poph->use_tokens = GNUNET_new_array (
+ poph->num_use_tokens,
+ struct TALER_MERCHANT_PostOrdersPayUseToken);
+ GNUNET_memcpy (poph->use_tokens,
+ options[i].details.use_tokens.tokens,
+ poph->num_use_tokens
+ * sizeof (struct TALER_MERCHANT_PostOrdersPayUseToken));
+ }
+ break;
+ case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_TOKENS:
+ GNUNET_free (poph->output_tokens);
+ poph->num_output_tokens = options[i].details.output_tokens.num;
+ if (0 < poph->num_output_tokens)
+ {
+ poph->output_tokens = GNUNET_new_array (
+ poph->num_output_tokens,
+ struct TALER_MERCHANT_PostOrdersPayOutputToken);
+ GNUNET_memcpy (
+ poph->output_tokens,
+ options[i].details.output_tokens.tokens,
+ poph->num_output_tokens
+ * sizeof (struct TALER_MERCHANT_PostOrdersPayOutputToken));
+ }
+ break;
+ case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_TOKENS_JSON:
+ json_decref (poph->output_tokens_json);
+ poph->output_tokens_json = NULL;
+ if (NULL != options[i].details.output_tokens_json)
+ poph->output_tokens_json = json_incref (
+ options[i].details.output_tokens_json);
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_orders_pay_start (
+ struct TALER_MERCHANT_PostOrdersPayHandle *poph,
+ TALER_MERCHANT_PostOrdersPayCallback cb,
+ TALER_MERCHANT_POST_ORDERS_PAY_RESULT_CLOSURE *cb_cls)
+{
+ json_t *pay_obj;
+ json_t *j_coins;
+ json_t *j_tokens = NULL;
+ json_t *j_output_tokens = NULL;
+ CURL *eh;
+
+ poph->cb = cb;
+ poph->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/pay",
+ poph->order_id);
+ poph->url = TALER_url_join (poph->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == poph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+
+ if (poph->am_wallet)
+ {
+ /* Wallet mode: sign coins and tokens, build wallet_data */
+ json_t *wallet_data = poph->wallet_data;
+ struct GNUNET_HashCode wallet_data_hash;
+
+ /* Build output token envelopes JSON if we have output tokens */
+ if (0 < poph->num_output_tokens)
+ {
+ j_output_tokens = json_array ();
+ GNUNET_assert (NULL != j_output_tokens);
+ for (unsigned int i = 0; i < poph->num_output_tokens; i++)
+ {
+ json_t *j_token_ev;
+ const struct TALER_MERCHANT_PostOrdersPayOutputToken *ev
+ = &poph->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 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ }
+ }
+ else if (NULL != poph->output_tokens_json)
+ {
+ j_output_tokens = json_incref (poph->output_tokens_json);
+ }
+
+ /* Build wallet_data if choice_index is valid */
+ if (0 <= poph->choice_index)
+ {
+ if (NULL == wallet_data)
+ {
+ wallet_data = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("choice_index",
+ poph->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);
+ }
+
+ if ( (0 < poph->num_use_tokens || 0 < poph->num_output_tokens
+ || NULL != poph->output_tokens_json)
+ && (0 > poph->choice_index) )
+ {
+ GNUNET_break (0);
+ json_decref (j_output_tokens);
+ if ( (NULL == poph->wallet_data) &&
+ (NULL != wallet_data) )
+ json_decref (wallet_data);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+
+ /* Sign coins */
+ j_coins = json_array ();
+ GNUNET_assert (NULL != j_coins);
+ for (unsigned int i = 0; i < poph->num_coins; i++)
+ {
+ const struct TALER_MERCHANT_PostOrdersPayCoin *coin = &poph->coins[i];
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_CoinSpendSignatureP coin_sig;
+ struct TALER_Amount fee;
+ struct TALER_DenominationHashP h_denom_pub;
+ json_t *j_coin;
+
+ if (0 >
+ TALER_amount_subtract (&fee,
+ &coin->amount_with_fee,
+ &coin->amount_without_fee))
+ {
+ GNUNET_break (0);
+ json_decref (j_coins);
+ json_decref (j_output_tokens);
+ if ( (NULL == poph->wallet_data) &&
+ (NULL != wallet_data) )
+ json_decref (wallet_data);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ TALER_denom_pub_hash (&coin->denom_pub,
+ &h_denom_pub);
+ TALER_wallet_deposit_sign (&coin->amount_with_fee,
+ &fee,
+ &poph->h_wire,
+ &poph->h_contract_terms,
+ (0 <= poph->choice_index)
+ ? &wallet_data_hash
+ : NULL,
+ GNUNET_is_zero (&coin->h_age_commitment)
+ ? NULL
+ : &coin->h_age_commitment,
+ NULL /* h_extensions */,
+ &h_denom_pub,
+ poph->timestamp,
+ &poph->merchant_pub,
+ poph->refund_deadline,
+ &coin->coin_priv,
+ &coin_sig);
+ GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
+ &coin_pub.eddsa_pub);
+ j_coin = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("contribution",
+ &coin->amount_with_fee),
+ GNUNET_JSON_pack_data_auto ("coin_pub",
+ &coin_pub),
+ GNUNET_JSON_pack_string ("exchange_url",
+ coin->exchange_url),
+ GNUNET_JSON_pack_data_auto ("h_denom",
+ &h_denom_pub),
+ TALER_JSON_pack_denom_sig ("ub_sig",
+ &coin->denom_sig),
+ GNUNET_JSON_pack_data_auto ("coin_sig",
+ &coin_sig));
+ if (0 !=
+ json_array_append_new (j_coins,
+ j_coin))
+ {
+ GNUNET_break (0);
+ json_decref (j_coins);
+ json_decref (j_output_tokens);
+ if ( (NULL == poph->wallet_data) &&
+ (NULL != wallet_data) )
+ json_decref (wallet_data);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ }
+
+ /* Sign use tokens */
+ if (0 < poph->num_use_tokens)
+ {
+ j_tokens = json_array ();
+ GNUNET_assert (NULL != j_tokens);
+ for (unsigned int i = 0; i < poph->num_use_tokens; i++)
+ {
+ const struct TALER_MERCHANT_PostOrdersPayUseToken *token
+ = &poph->use_tokens[i];
+ struct TALER_TokenUseSignatureP token_sig;
+ struct TALER_TokenUsePublicKeyP token_pub;
+ json_t *j_token;
+
+ TALER_wallet_token_use_sign (&poph->h_contract_terms,
+ &wallet_data_hash,
+ &token->token_priv,
+ &token_sig);
+ GNUNET_CRYPTO_eddsa_key_get_public (
+ &token->token_priv.private_key,
+ &token_pub.public_key);
+ j_token = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("token_sig",
+ &token_sig),
+ GNUNET_JSON_pack_data_auto ("token_pub",
+ &token_pub),
+ GNUNET_JSON_pack_data_auto (
+ "h_issue",
+ &token->issue_pub.public_key->pub_key_hash),
+ TALER_JSON_pack_token_issue_sig ("ub_sig",
+ &token->ub_sig));
+ if (0 !=
+ json_array_append_new (j_tokens,
+ j_token))
+ {
+ GNUNET_break (0);
+ json_decref (j_coins);
+ json_decref (j_tokens);
+ json_decref (j_output_tokens);
+ if ( (NULL == poph->wallet_data) &&
+ (NULL != wallet_data) )
+ json_decref (wallet_data);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ }
+ }
+
+ 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",
+ wallet_data)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("session_id",
+ poph->session_id)));
+ if ( (NULL == poph->wallet_data) &&
+ (NULL != wallet_data) )
+ json_decref (wallet_data);
+ }
+ else
+ {
+ /* Frontend mode: coins are already signed */
+ j_coins = json_array ();
+ GNUNET_assert (NULL != j_coins);
+ for (unsigned int i = 0; i < poph->num_paid_coins; i++)
+ {
+ const struct TALER_MERCHANT_PostOrdersPayPaidCoin *pc
+ = &poph->paid_coins[i];
+ struct TALER_DenominationHashP denom_hash;
+ json_t *j_coin;
+
+ TALER_denom_pub_hash (&pc->denom_pub,
+ &denom_hash);
+ 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 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ }
+
+ /* Build used tokens JSON (frontend mode) */
+ if (0 < poph->num_used_tokens)
+ {
+ j_tokens = json_array ();
+ GNUNET_assert (NULL != j_tokens);
+ for (unsigned int i = 0; i < poph->num_used_tokens; i++)
+ {
+ const struct TALER_MERCHANT_PostOrdersPayUsedToken *ut
+ = &poph->used_tokens[i];
+ json_t *j_token;
+
+ 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_coins);
+ json_decref (j_tokens);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ }
+ }
+
+ 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",
+ poph->wallet_data)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("session_id",
+ poph->session_id)));
+ }
+
+ eh = TALER_MERCHANT_curl_easy_get_ (poph->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&poph->post_ctx,
+ eh,
+ pay_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (pay_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (pay_obj);
+ poph->job = GNUNET_CURL_job_add2 (poph->ctx,
+ eh,
+ poph->post_ctx.headers,
+ &handle_pay_finished,
+ poph);
+ if (NULL == poph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_orders_pay_cancel (
+ struct TALER_MERCHANT_PostOrdersPayHandle *poph)
+{
+ if (NULL != poph->job)
+ {
+ GNUNET_CURL_job_cancel (poph->job);
+ poph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&poph->post_ctx);
+ if (NULL != poph->paid_coins)
+ {
+ for (unsigned int i = 0; i < poph->num_paid_coins; i++)
+ {
+ TALER_denom_pub_free (&poph->paid_coins[i].denom_pub);
+ TALER_denom_sig_free (&poph->paid_coins[i].denom_sig);
+ GNUNET_free (poph->paid_coins[i].exchange_url);
+ }
+ GNUNET_free (poph->paid_coins);
+ }
+ if (NULL != poph->coins)
+ {
+ for (unsigned int i = 0; i < poph->num_coins; i++)
+ {
+ TALER_denom_pub_free (&poph->coins[i].denom_pub);
+ TALER_denom_sig_free (&poph->coins[i].denom_sig);
+ GNUNET_free (poph->coins[i].exchange_url);
+ }
+ GNUNET_free (poph->coins);
+ }
+ GNUNET_free (poph->used_tokens);
+ GNUNET_free (poph->use_tokens);
+ GNUNET_free (poph->output_tokens);
+ json_decref (poph->output_tokens_json);
+ json_decref (poph->wallet_data);
+ GNUNET_free (poph->session_id);
+ GNUNET_free (poph->order_id);
+ GNUNET_free (poph->url);
+ GNUNET_free (poph->base_url);
+ GNUNET_free (poph);
+}
+
+
+/* end of merchant_api_post-orders-ORDER_ID-pay-new.c */
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-refund-new.c b/src/lib/merchant_api_post-orders-ORDER_ID-refund-new.c
@@ -0,0 +1,370 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_post-orders-ORDER_ID-refund-new.c
+ * @brief Implementation of the POST /orders/$ORDER_ID/refund 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/post-orders-ORDER_ID-refund-new.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_signatures.h>
+
+
+/**
+ * Maximum number of refunds we return.
+ */
+#define MAX_REFUNDS 1024
+
+
+/**
+ * Handle for a POST /orders/$ORDER_ID/refund operation.
+ */
+struct TALER_MERCHANT_PostOrdersRefundHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostOrdersRefundCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_ORDERS_REFUND_RESULT_CLOSURE *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;
+
+ /**
+ * Order identifier.
+ */
+ char *order_id;
+
+ /**
+ * Hash of the contract terms.
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /orders/$ORDER_ID/refund request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostOrdersRefundHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_refund_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostOrdersRefundHandle *porh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostOrdersRefundResponse orr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ porh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /orders/$ID/refund completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ orr.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[] = {
+ GNUNET_JSON_spec_array_const (
+ "refunds",
+ &refunds),
+ GNUNET_JSON_spec_fixed_auto (
+ "merchant_pub",
+ &orr.details.ok.merchant_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ orr.hr.http_status = 0;
+ break;
+ }
+ refund_len = (unsigned int) json_array_size (refunds);
+ if ( (json_array_size (refunds) != (size_t) refund_len) ||
+ (refund_len > MAX_REFUNDS) )
+ {
+ GNUNET_break (0);
+ orr.hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
+ orr.hr.http_status = 0;
+ break;
+ }
+ {
+ struct TALER_MERCHANT_PostOrdersRefundDetail rds[GNUNET_NZL (
+ refund_len)];
+
+ memset (rds,
+ 0,
+ sizeof (rds));
+ for (unsigned int i = 0; i < refund_len; i++)
+ {
+ struct TALER_MERCHANT_PostOrdersRefundDetail *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->exchange_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);
+ orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ orr.hr.http_status = 0;
+ goto finish;
+ }
+
+ rd->exchange_http_status = exchange_status;
+ rd->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->exchange_sig),
+ GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+ &rd->exchange_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jrefund,
+ rspec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ orr.hr.http_status = 0;
+ goto finish;
+ }
+ if (0 != strcmp ("success",
+ refund_status_type))
+ {
+ GNUNET_break_op (0);
+ orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ orr.hr.http_status = 0;
+ goto finish;
+ }
+ }
+ break;
+ default:
+ if (0 != strcmp ("failure",
+ refund_status_type))
+ {
+ GNUNET_break_op (0);
+ orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ orr.hr.http_status = 0;
+ goto finish;
+ }
+ }
+ }
+
+ orr.details.ok.refunds = rds;
+ orr.details.ok.num_refunds = refund_len;
+ porh->cb (porh->cb_cls,
+ &orr);
+ TALER_MERCHANT_post_orders_refund_cancel (porh);
+ return;
+ }
+ }
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_CONFLICT:
+ case MHD_HTTP_NOT_FOUND:
+ orr.hr.ec = TALER_JSON_get_error_code (json);
+ orr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ GNUNET_break_op (0);
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &orr.hr);
+ break;
+ }
+finish:
+ porh->cb (porh->cb_cls,
+ &orr);
+ TALER_MERCHANT_post_orders_refund_cancel (porh);
+}
+
+
+struct TALER_MERCHANT_PostOrdersRefundHandle *
+TALER_MERCHANT_post_orders_refund_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id,
+ const struct TALER_PrivateContractHashP *h_contract_terms)
+{
+ struct TALER_MERCHANT_PostOrdersRefundHandle *porh;
+
+ porh = GNUNET_new (struct TALER_MERCHANT_PostOrdersRefundHandle);
+ porh->ctx = ctx;
+ porh->base_url = GNUNET_strdup (url);
+ porh->order_id = GNUNET_strdup (order_id);
+ porh->h_contract_terms = *h_contract_terms;
+ return porh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_orders_refund_start (
+ struct TALER_MERCHANT_PostOrdersRefundHandle *porh,
+ TALER_MERCHANT_PostOrdersRefundCallback cb,
+ TALER_MERCHANT_POST_ORDERS_REFUND_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ porh->cb = cb;
+ porh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "orders/%s/refund",
+ porh->order_id);
+ porh->url = TALER_url_join (porh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == porh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_contract",
+ &porh->h_contract_terms));
+ eh = TALER_MERCHANT_curl_easy_get_ (porh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&porh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ porh->job = GNUNET_CURL_job_add2 (porh->ctx,
+ eh,
+ porh->post_ctx.headers,
+ &handle_refund_finished,
+ porh);
+ if (NULL == porh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_orders_refund_cancel (
+ struct TALER_MERCHANT_PostOrdersRefundHandle *porh)
+{
+ if (NULL != porh->job)
+ {
+ GNUNET_CURL_job_cancel (porh->job);
+ porh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&porh->post_ctx);
+ GNUNET_free (porh->order_id);
+ GNUNET_free (porh->url);
+ GNUNET_free (porh->base_url);
+ GNUNET_free (porh);
+}
+
+
+/* end of merchant_api_post-orders-ORDER_ID-refund-new.c */
diff --git a/src/lib/merchant_api_post-private-accounts-new.c b/src/lib/merchant_api_post-private-accounts-new.c
@@ -0,0 +1,298 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023-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_post-private-accounts-new.c
+ * @brief Implementation of the POST /private/accounts 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/post-private-accounts-new.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_PostPrivateAccountsHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateAccountsCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_RESULT_CLOSURE *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;
+
+ /**
+ * Payto URI to add.
+ */
+ struct TALER_FullPayto payto_uri;
+
+ /**
+ * Optional credit facade URL.
+ */
+ const char *credit_facade_url;
+
+ /**
+ * Optional credit facade credentials (JSON).
+ */
+ const json_t *credit_facade_credentials;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/accounts request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateAccountsHandle`
+ * @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_PostPrivateAccountsHandle *pah = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateAccountsResponse apr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pah->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/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);
+ 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);
+ 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);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &apr.hr);
+ 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;
+ }
+ pah->cb (pah->cb_cls,
+ &apr);
+ TALER_MERCHANT_post_private_accounts_cancel (pah);
+}
+
+
+struct TALER_MERCHANT_PostPrivateAccountsHandle *
+TALER_MERCHANT_post_private_accounts_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ struct TALER_FullPayto payto_uri)
+{
+ struct TALER_MERCHANT_PostPrivateAccountsHandle *pah;
+
+ pah = GNUNET_new (struct TALER_MERCHANT_PostPrivateAccountsHandle);
+ pah->ctx = ctx;
+ pah->base_url = GNUNET_strdup (url);
+ pah->payto_uri.full_payto = GNUNET_strdup (payto_uri.full_payto);
+ return pah;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_accounts_set_options_ (
+ struct TALER_MERCHANT_PostPrivateAccountsHandle *pah,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateAccountsOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_CREDIT_FACADE_URL:
+ pah->credit_facade_url = options[i].details.credit_facade_url;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_CREDIT_FACADE_CREDENTIALS:
+ pah->credit_facade_credentials
+ = options[i].details.credit_facade_credentials;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_accounts_start (
+ struct TALER_MERCHANT_PostPrivateAccountsHandle *pah,
+ TALER_MERCHANT_PostPrivateAccountsCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ pah->cb = cb;
+ pah->cb_cls = cb_cls;
+ pah->url = TALER_url_join (pah->base_url,
+ "private/accounts",
+ NULL);
+ if (NULL == pah->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ TALER_JSON_pack_full_payto (
+ "payto_uri",
+ pah->payto_uri),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string (
+ "credit_facade_url",
+ pah->credit_facade_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref (
+ "credit_facade_credentials",
+ (json_t *) pah->credit_facade_credentials))
+ );
+ eh = TALER_MERCHANT_curl_easy_get_ (pah->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&pah->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ pah->job = GNUNET_CURL_job_add2 (pah->ctx,
+ eh,
+ pah->post_ctx.headers,
+ &handle_post_account_finished,
+ pah);
+ if (NULL == pah->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_accounts_cancel (
+ struct TALER_MERCHANT_PostPrivateAccountsHandle *pah)
+{
+ if (NULL != pah->job)
+ {
+ GNUNET_CURL_job_cancel (pah->job);
+ pah->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pah->post_ctx);
+ GNUNET_free (pah->payto_uri.full_payto);
+ GNUNET_free (pah->url);
+ GNUNET_free (pah->base_url);
+ GNUNET_free (pah);
+}
+
+
+/* end of merchant_api_post-private-accounts-new.c */
diff --git a/src/lib/merchant_api_post-private-categories-new.c b/src/lib/merchant_api_post-private-categories-new.c
@@ -0,0 +1,286 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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_post-private-categories-new.c
+ * @brief Implementation of the POST /private/categories 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/post-private-categories-new.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_PostPrivateCategoriesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateCategoriesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_CATEGORIES_RESULT_CLOSURE *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;
+
+ /**
+ * Human-readable name of the category.
+ */
+ char *name;
+
+ /**
+ * Optional internationalized names (JSON).
+ */
+ const json_t *name_i18n;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/categories request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateCategoriesHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_categories_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateCategoriesResponse cpr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppch->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/categories completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint64 ("category_id",
+ &cpr.details.ok.category_id),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ cpr.hr.http_status = 0;
+ cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ cpr.hr.ec = TALER_JSON_get_error_code (json);
+ cpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ cpr.hr.ec = TALER_JSON_get_error_code (json);
+ cpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ cpr.hr.ec = TALER_JSON_get_error_code (json);
+ cpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ cpr.hr.ec = TALER_JSON_get_error_code (json);
+ cpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ cpr.hr.ec = TALER_JSON_get_error_code (json);
+ cpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ 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;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &cpr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) cpr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ppch->cb (ppch->cb_cls,
+ &cpr);
+ TALER_MERCHANT_post_private_categories_cancel (ppch);
+}
+
+
+struct TALER_MERCHANT_PostPrivateCategoriesHandle *
+TALER_MERCHANT_post_private_categories_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *name)
+{
+ struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch;
+
+ ppch = GNUNET_new (struct TALER_MERCHANT_PostPrivateCategoriesHandle);
+ ppch->ctx = ctx;
+ ppch->base_url = GNUNET_strdup (url);
+ ppch->name = GNUNET_strdup (name);
+ return ppch;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_categories_set_options_ (
+ struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateCategoriesOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_CATEGORIES_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_CATEGORIES_OPTION_NAME_I18N:
+ ppch->name_i18n = options[i].details.name_i18n;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_categories_start (
+ struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch,
+ TALER_MERCHANT_PostPrivateCategoriesCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_CATEGORIES_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ppch->cb = cb;
+ ppch->cb_cls = cb_cls;
+ ppch->url = TALER_url_join (ppch->base_url,
+ "private/categories",
+ NULL);
+ if (NULL == ppch->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "name",
+ ppch->name),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref (
+ "name_i18n",
+ (json_t *) ppch->name_i18n))
+ );
+ eh = TALER_MERCHANT_curl_easy_get_ (ppch->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppch->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ppch->job = GNUNET_CURL_job_add2 (ppch->ctx,
+ eh,
+ ppch->post_ctx.headers,
+ &handle_post_categories_finished,
+ ppch);
+ if (NULL == ppch->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_categories_cancel (
+ struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch)
+{
+ if (NULL != ppch->job)
+ {
+ GNUNET_CURL_job_cancel (ppch->job);
+ ppch->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppch->post_ctx);
+ GNUNET_free (ppch->name);
+ GNUNET_free (ppch->url);
+ GNUNET_free (ppch->base_url);
+ GNUNET_free (ppch);
+}
+
+
+/* end of merchant_api_post-private-categories-new.c */
diff --git a/src/lib/merchant_api_post-private-donau-new.c b/src/lib/merchant_api_post-private-donau-new.c
@@ -0,0 +1,256 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024-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_post-private-donau-new.c
+ * @brief Implementation of the POST /private/donau 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/post-private-donau-new.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/donau operation.
+ */
+struct TALER_MERCHANT_PostPrivateDonauHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateDonauCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_DONAU_RESULT_CLOSURE *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;
+
+ /**
+ * Charity information to register.
+ */
+ struct TALER_MERCHANT_Charity charity;
+
+ /**
+ * Charity URL (owned copy).
+ */
+ char *charity_url;
+
+ /**
+ * Optional authentication token.
+ */
+ const char *auth_token;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/donau request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateDonauHandle`
+ * @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_PostPrivateDonauHandle *ppdh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateDonauResponse pdr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppdh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/donau completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ pdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ pdr.hr.ec = TALER_JSON_get_error_code (json);
+ pdr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ pdr.hr.ec = TALER_JSON_get_error_code (json);
+ pdr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pdr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) pdr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ppdh->cb (ppdh->cb_cls,
+ &pdr);
+ TALER_MERCHANT_post_private_donau_cancel (ppdh);
+}
+
+
+struct TALER_MERCHANT_PostPrivateDonauHandle *
+TALER_MERCHANT_post_private_donau_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const struct TALER_MERCHANT_Charity *charity)
+{
+ struct TALER_MERCHANT_PostPrivateDonauHandle *ppdh;
+
+ ppdh = GNUNET_new (struct TALER_MERCHANT_PostPrivateDonauHandle);
+ ppdh->ctx = ctx;
+ ppdh->base_url = GNUNET_strdup (url);
+ ppdh->charity_url = GNUNET_strdup (charity->charity_url);
+ ppdh->charity.charity_url = ppdh->charity_url;
+ ppdh->charity.charity_id = charity->charity_id;
+ return ppdh;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_donau_set_options_ (
+ struct TALER_MERCHANT_PostPrivateDonauHandle *ppdh,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateDonauOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_DONAU_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_DONAU_OPTION_AUTH_TOKEN:
+ ppdh->auth_token = options[i].details.auth_token;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_donau_start (
+ struct TALER_MERCHANT_PostPrivateDonauHandle *ppdh,
+ TALER_MERCHANT_PostPrivateDonauCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_DONAU_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ppdh->cb = cb;
+ ppdh->cb_cls = cb_cls;
+ ppdh->url = TALER_url_join (ppdh->base_url,
+ "private/donau",
+ NULL);
+ if (NULL == ppdh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("donau_url",
+ ppdh->charity.charity_url),
+ GNUNET_JSON_pack_uint64 ("charity_id",
+ ppdh->charity.charity_id)
+ );
+ eh = TALER_MERCHANT_curl_easy_get_ (ppdh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppdh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ppdh->job = GNUNET_CURL_job_add2 (ppdh->ctx,
+ eh,
+ ppdh->post_ctx.headers,
+ &handle_post_donau_finished,
+ ppdh);
+ if (NULL == ppdh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_donau_cancel (
+ struct TALER_MERCHANT_PostPrivateDonauHandle *ppdh)
+{
+ if (NULL != ppdh->job)
+ {
+ GNUNET_CURL_job_cancel (ppdh->job);
+ ppdh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppdh->post_ctx);
+ GNUNET_free (ppdh->charity_url);
+ GNUNET_free (ppdh->url);
+ GNUNET_free (ppdh->base_url);
+ GNUNET_free (ppdh);
+}
+
+
+/* end of merchant_api_post-private-donau-new.c */
diff --git a/src/lib/merchant_api_post-private-orders-ORDER_ID-refund-new.c b/src/lib/merchant_api_post-private-orders-ORDER_ID-refund-new.c
@@ -0,0 +1,267 @@
+/*
+ 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_post-private-orders-ORDER_ID-refund-new.c
+ * @brief Implementation of the POST /private/orders/$ORDER_ID/refund 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/post-private-orders-ORDER_ID-refund-new.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/orders/$ORDER_ID/refund operation.
+ */
+struct TALER_MERCHANT_PostPrivateOrdersRefundHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateOrdersRefundCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_ORDERS_REFUND_RESULT_CLOSURE *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;
+
+ /**
+ * Identifier of the order to refund.
+ */
+ char *order_id;
+
+ /**
+ * Amount to refund.
+ */
+ struct TALER_Amount refund;
+
+ /**
+ * Human-readable reason for the refund.
+ */
+ char *reason;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/orders/$ORDER_ID/refund request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateOrdersRefundHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_refund_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostPrivateOrdersRefundHandle *porh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateOrdersRefundResponse rr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ porh->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);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_GONE:
+ rr.hr.ec = TALER_JSON_get_error_code (json);
+ rr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ GNUNET_break_op (0);
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &rr.hr);
+ break;
+ }
+ porh->cb (porh->cb_cls,
+ &rr);
+ TALER_MERCHANT_post_private_orders_refund_cancel (porh);
+}
+
+
+struct TALER_MERCHANT_PostPrivateOrdersRefundHandle *
+TALER_MERCHANT_post_private_orders_refund_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *order_id,
+ const struct TALER_Amount *refund,
+ const char *reason)
+{
+ struct TALER_MERCHANT_PostPrivateOrdersRefundHandle *porh;
+
+ porh = GNUNET_new (struct TALER_MERCHANT_PostPrivateOrdersRefundHandle);
+ porh->ctx = ctx;
+ porh->base_url = GNUNET_strdup (url);
+ porh->order_id = GNUNET_strdup (order_id);
+ porh->refund = *refund;
+ porh->reason = GNUNET_strdup (reason);
+ return porh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_orders_refund_start (
+ struct TALER_MERCHANT_PostPrivateOrdersRefundHandle *porh,
+ TALER_MERCHANT_PostPrivateOrdersRefundCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_ORDERS_REFUND_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ porh->cb = cb;
+ porh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/orders/%s/refund",
+ porh->order_id);
+ porh->url = TALER_url_join (porh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == porh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("refund",
+ &porh->refund),
+ GNUNET_JSON_pack_string ("reason",
+ porh->reason));
+ eh = TALER_MERCHANT_curl_easy_get_ (porh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&porh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ porh->job = GNUNET_CURL_job_add2 (porh->ctx,
+ eh,
+ porh->post_ctx.headers,
+ &handle_refund_finished,
+ porh);
+ if (NULL == porh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_orders_refund_cancel (
+ struct TALER_MERCHANT_PostPrivateOrdersRefundHandle *porh)
+{
+ if (NULL != porh->job)
+ {
+ GNUNET_CURL_job_cancel (porh->job);
+ porh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&porh->post_ctx);
+ GNUNET_free (porh->order_id);
+ GNUNET_free (porh->reason);
+ GNUNET_free (porh->url);
+ GNUNET_free (porh->base_url);
+ GNUNET_free (porh);
+}
+
+
+/* end of merchant_api_post-private-orders-ORDER_ID-refund-new.c */
diff --git a/src/lib/merchant_api_post-private-orders-new.c b/src/lib/merchant_api_post-private-orders-new.c
@@ -0,0 +1,510 @@
+/*
+ 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_post-private-orders-new.c
+ * @brief Implementation of the POST /private/orders 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/post-private-orders-new.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/orders operation.
+ */
+struct TALER_MERCHANT_PostPrivateOrdersHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateOrdersCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE *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;
+
+ /**
+ * Order contract terms (JSON).
+ */
+ json_t *order;
+
+ /**
+ * Optional refund delay.
+ */
+ struct GNUNET_TIME_Relative refund_delay;
+
+ /**
+ * Whether refund_delay was set.
+ */
+ bool refund_delay_set;
+
+ /**
+ * Optional payment target.
+ */
+ const char *payment_target;
+
+ /**
+ * Optional session ID.
+ */
+ const char *session_id;
+
+ /**
+ * Whether to create a claim token (default: true).
+ */
+ bool create_token;
+
+ /**
+ * Whether create_token was explicitly set.
+ */
+ bool create_token_set;
+
+ /**
+ * Optional OTP device ID.
+ */
+ const char *otp_id;
+
+ /**
+ * Optional inventory products.
+ */
+ const struct TALER_MERCHANT_PostPrivateOrdersInventoryProduct *
+ inventory_products;
+
+ /**
+ * Number of inventory products.
+ */
+ unsigned int num_inventory_products;
+
+ /**
+ * Optional lock UUIDs.
+ */
+ const char **lock_uuids;
+
+ /**
+ * Number of lock UUIDs.
+ */
+ unsigned int num_lock_uuids;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/orders request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateOrdersHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_orders_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostPrivateOrdersHandle *ppoh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateOrdersResponse por = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+ struct TALER_ClaimTokenP token;
+
+ ppoh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ por.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ bool no_token;
+ bool no_pay_deadline;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("order_id",
+ &por.details.ok.order_id),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto ("token",
+ &token),
+ &no_token),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp ("pay_deadline",
+ &por.details.ok.pay_deadline),
+ &no_pay_deadline),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ por.hr.http_status = 0;
+ por.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ break;
+ }
+ if (! no_token)
+ por.details.ok.token = &token;
+ if (no_pay_deadline)
+ por.details.ok.pay_deadline = GNUNET_TIME_UNIT_ZERO_TS;
+ break;
+ }
+ case MHD_HTTP_BAD_REQUEST:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_GONE:
+ {
+ bool rq_frac_missing;
+ bool aq_frac_missing;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string (
+ "product_id",
+ &por.details.gone.product_id),
+ GNUNET_JSON_spec_uint64 (
+ "requested_quantity",
+ &por.details.gone.requested_quantity),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 (
+ "requested_quantity_frac",
+ &por.details.gone.requested_quantity_frac),
+ &rq_frac_missing),
+ GNUNET_JSON_spec_uint64 (
+ "available_quantity",
+ &por.details.gone.available_quantity),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 (
+ "available_quantity_frac",
+ &por.details.gone.available_quantity_frac),
+ &aq_frac_missing),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp (
+ "restock_expected",
+ &por.details.gone.restock_expected),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ por.hr.http_status = 0;
+ por.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ }
+ else
+ {
+ if (rq_frac_missing)
+ por.details.gone.requested_quantity_frac = 0;
+ if (aq_frac_missing)
+ por.details.gone.available_quantity_frac = 0;
+ }
+ break;
+ }
+ case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ por.hr.ec = TALER_JSON_get_error_code (json);
+ por.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) por.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ppoh->cb (ppoh->cb_cls,
+ &por);
+ TALER_MERCHANT_post_private_orders_cancel (ppoh);
+}
+
+
+struct TALER_MERCHANT_PostPrivateOrdersHandle *
+TALER_MERCHANT_post_private_orders_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const json_t *order)
+{
+ struct TALER_MERCHANT_PostPrivateOrdersHandle *ppoh;
+
+ ppoh = GNUNET_new (struct TALER_MERCHANT_PostPrivateOrdersHandle);
+ ppoh->ctx = ctx;
+ ppoh->base_url = GNUNET_strdup (url);
+ ppoh->order = json_incref ((json_t *) order);
+ return ppoh;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_orders_set_options_ (
+ struct TALER_MERCHANT_PostPrivateOrdersHandle *ppoh,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateOrdersOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_REFUND_DELAY:
+ ppoh->refund_delay = options[i].details.refund_delay;
+ ppoh->refund_delay_set = true;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_PAYMENT_TARGET:
+ ppoh->payment_target = options[i].details.payment_target;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_SESSION_ID:
+ ppoh->session_id = options[i].details.session_id;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_CREATE_TOKEN:
+ ppoh->create_token = options[i].details.create_token;
+ ppoh->create_token_set = true;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_OTP_ID:
+ ppoh->otp_id = options[i].details.otp_id;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_INVENTORY_PRODUCTS:
+ ppoh->num_inventory_products
+ = options[i].details.inventory_products.num;
+ ppoh->inventory_products
+ = options[i].details.inventory_products.products;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_LOCK_UUIDS:
+ ppoh->num_lock_uuids = options[i].details.lock_uuids.num;
+ ppoh->lock_uuids = options[i].details.lock_uuids.uuids;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_orders_start (
+ struct TALER_MERCHANT_PostPrivateOrdersHandle *ppoh,
+ TALER_MERCHANT_PostPrivateOrdersCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req;
+ CURL *eh;
+
+ ppoh->cb = cb;
+ ppoh->cb_cls = cb_cls;
+ ppoh->url = TALER_url_join (ppoh->base_url,
+ "private/orders",
+ NULL);
+ if (NULL == ppoh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_object_incref ("order",
+ ppoh->order),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("session_id",
+ ppoh->session_id)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("payment_target",
+ ppoh->payment_target)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_id",
+ ppoh->otp_id)));
+ if (ppoh->refund_delay_set &&
+ (0 != ppoh->refund_delay.rel_value_us))
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req,
+ "refund_delay",
+ GNUNET_JSON_from_time_rel (
+ ppoh->refund_delay)));
+ }
+ if (0 != ppoh->num_inventory_products)
+ {
+ json_t *ipa = json_array ();
+
+ GNUNET_assert (NULL != ipa);
+ for (unsigned int i = 0; i < ppoh->num_inventory_products; i++)
+ {
+ json_t *ip;
+
+ if (ppoh->inventory_products[i].use_fractional_quantity)
+ {
+ char unit_quantity_buf[64];
+
+ TALER_MERCHANT_format_quantity_string (
+ ppoh->inventory_products[i].quantity,
+ ppoh->inventory_products[i].quantity_frac,
+ unit_quantity_buf,
+ sizeof (unit_quantity_buf));
+ ip = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("product_id",
+ ppoh->inventory_products[i].product_id),
+ GNUNET_JSON_pack_string ("unit_quantity",
+ unit_quantity_buf));
+ }
+ else
+ {
+ char unit_quantity_buf[64];
+
+ TALER_MERCHANT_format_quantity_string (
+ ppoh->inventory_products[i].quantity,
+ 0,
+ unit_quantity_buf,
+ sizeof (unit_quantity_buf));
+ ip = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("product_id",
+ ppoh->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 != ppoh->num_lock_uuids)
+ {
+ json_t *ua = json_array ();
+
+ GNUNET_assert (NULL != ua);
+ for (unsigned int i = 0; i < ppoh->num_lock_uuids; i++)
+ {
+ GNUNET_assert (0 ==
+ json_array_append_new (ua,
+ json_string (
+ ppoh->lock_uuids[i])));
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (req,
+ "lock_uuids",
+ ua));
+ }
+ if (ppoh->create_token_set &&
+ ! ppoh->create_token)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req,
+ "create_token",
+ json_boolean (ppoh->create_token)));
+ }
+ eh = TALER_MERCHANT_curl_easy_get_ (ppoh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppoh->post_ctx,
+ eh,
+ req)) )
+ {
+ GNUNET_break (0);
+ json_decref (req);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req);
+ ppoh->job = GNUNET_CURL_job_add2 (ppoh->ctx,
+ eh,
+ ppoh->post_ctx.headers,
+ &handle_post_orders_finished,
+ ppoh);
+ if (NULL == ppoh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_orders_cancel (
+ struct TALER_MERCHANT_PostPrivateOrdersHandle *ppoh)
+{
+ if (NULL != ppoh->job)
+ {
+ GNUNET_CURL_job_cancel (ppoh->job);
+ ppoh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppoh->post_ctx);
+ json_decref (ppoh->order);
+ GNUNET_free (ppoh->url);
+ GNUNET_free (ppoh->base_url);
+ GNUNET_free (ppoh);
+}
+
+
+/* end of merchant_api_post-private-orders-new.c */
diff --git a/src/lib/merchant_api_post-private-otp-devices-new.c b/src/lib/merchant_api_post-private-otp-devices-new.c
@@ -0,0 +1,273 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_post-private-otp-devices-new.c
+ * @brief Implementation of the POST /private/otp-devices 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/post-private-otp-devices-new.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/otp-devices operation.
+ */
+struct TALER_MERCHANT_PostPrivateOtpDevicesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateOtpDevicesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_OTP_DEVICES_RESULT_CLOSURE *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;
+
+ /**
+ * OTP device identifier.
+ */
+ char *otp_device_id;
+
+ /**
+ * Human-readable description.
+ */
+ char *otp_device_description;
+
+ /**
+ * Base32-encoded OTP secret key (or NULL).
+ */
+ char *otp_key;
+
+ /**
+ * OTP algorithm to use.
+ */
+ enum TALER_MerchantConfirmationAlgorithm otp_algorithm;
+
+ /**
+ * Initial counter value (for HOTP).
+ */
+ uint64_t otp_ctr;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/otp-devices request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateOtpDevicesHandle`
+ * @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_PostPrivateOtpDevicesHandle *ppoh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateOtpDevicesResponse odr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppoh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/otp-devices completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ odr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ odr.hr.ec = TALER_JSON_get_error_code (json);
+ odr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &odr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) odr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ppoh->cb (ppoh->cb_cls,
+ &odr);
+ TALER_MERCHANT_post_private_otp_devices_cancel (ppoh);
+}
+
+
+struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *
+TALER_MERCHANT_post_private_otp_devices_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *otp_device_id,
+ const char *otp_device_description,
+ const char *otp_key,
+ enum TALER_MerchantConfirmationAlgorithm otp_algorithm,
+ uint64_t otp_ctr)
+{
+ struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *ppoh;
+
+ ppoh = GNUNET_new (struct TALER_MERCHANT_PostPrivateOtpDevicesHandle);
+ ppoh->ctx = ctx;
+ ppoh->base_url = GNUNET_strdup (url);
+ ppoh->otp_device_id = GNUNET_strdup (otp_device_id);
+ ppoh->otp_device_description = GNUNET_strdup (otp_device_description);
+ if (NULL != otp_key)
+ ppoh->otp_key = GNUNET_strdup (otp_key);
+ ppoh->otp_algorithm = otp_algorithm;
+ ppoh->otp_ctr = otp_ctr;
+ return ppoh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_otp_devices_start (
+ struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *ppoh,
+ TALER_MERCHANT_PostPrivateOtpDevicesCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_OTP_DEVICES_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ppoh->cb = cb;
+ ppoh->cb_cls = cb_cls;
+ ppoh->url = TALER_url_join (ppoh->base_url,
+ "private/otp-devices",
+ NULL);
+ if (NULL == ppoh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("otp_device_id",
+ ppoh->otp_device_id),
+ GNUNET_JSON_pack_string ("otp_device_description",
+ ppoh->otp_device_description),
+ GNUNET_JSON_pack_uint64 ("otp_algorithm",
+ (uint32_t) ppoh->otp_algorithm),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_key",
+ ppoh->otp_key)),
+ GNUNET_JSON_pack_uint64 ("otp_ctr",
+ ppoh->otp_ctr));
+ eh = TALER_MERCHANT_curl_easy_get_ (ppoh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppoh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ppoh->job = GNUNET_CURL_job_add2 (ppoh->ctx,
+ eh,
+ ppoh->post_ctx.headers,
+ &handle_post_otp_devices_finished,
+ ppoh);
+ if (NULL == ppoh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_otp_devices_cancel (
+ struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *ppoh)
+{
+ if (NULL != ppoh->job)
+ {
+ GNUNET_CURL_job_cancel (ppoh->job);
+ ppoh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppoh->post_ctx);
+ GNUNET_free (ppoh->otp_device_id);
+ GNUNET_free (ppoh->otp_device_description);
+ GNUNET_free (ppoh->otp_key);
+ GNUNET_free (ppoh->url);
+ GNUNET_free (ppoh->base_url);
+ GNUNET_free (ppoh);
+}
+
+
+/* end of merchant_api_post-private-otp-devices-new.c */
diff --git a/src/lib/merchant_api_post-private-products-PRODUCT_ID-lock-new.c b/src/lib/merchant_api_post-private-products-PRODUCT_ID-lock-new.c
@@ -0,0 +1,314 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_post-private-products-PRODUCT_ID-lock-new.c
+ * @brief Implementation of the POST /private/products/$PRODUCT_ID/lock 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/post-private-products-PRODUCT_ID-lock-new.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/products/$PRODUCT_ID/lock operation.
+ */
+struct TALER_MERCHANT_PostPrivateProductsLockHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateProductsLockCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_RESULT_CLOSURE *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;
+
+ /**
+ * Product identifier.
+ */
+ char *product_id;
+
+ /**
+ * Lock UUID.
+ */
+ char *uuid;
+
+ /**
+ * How long to lock the inventory.
+ */
+ struct GNUNET_TIME_Relative duration;
+
+ /**
+ * Number of units to lock.
+ */
+ uint64_t quantity;
+
+ /**
+ * Fractional part of the quantity.
+ */
+ uint32_t quantity_frac;
+
+ /**
+ * Whether to use fractional quantity.
+ */
+ bool use_fractional_quantity;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/products/$PRODUCT_ID/lock request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateProductsLockHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_products_lock_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateProductsLockResponse plr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ pplh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/products/$PRODUCT_ID/lock completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ plr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ plr.hr.ec = TALER_JSON_get_error_code (json);
+ plr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ GNUNET_break_op (0);
+ plr.hr.ec = TALER_JSON_get_error_code (json);
+ plr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ plr.hr.ec = TALER_JSON_get_error_code (json);
+ plr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ plr.hr.ec = TALER_JSON_get_error_code (json);
+ plr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_GONE:
+ plr.hr.ec = TALER_JSON_get_error_code (json);
+ plr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ plr.hr.ec = TALER_JSON_get_error_code (json);
+ plr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &plr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) plr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ pplh->cb (pplh->cb_cls,
+ &plr);
+ TALER_MERCHANT_post_private_products_lock_cancel (pplh);
+}
+
+
+struct TALER_MERCHANT_PostPrivateProductsLockHandle *
+TALER_MERCHANT_post_private_products_lock_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *product_id,
+ const char *uuid,
+ struct GNUNET_TIME_Relative duration,
+ uint64_t quantity)
+{
+ struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh;
+
+ pplh = GNUNET_new (struct TALER_MERCHANT_PostPrivateProductsLockHandle);
+ pplh->ctx = ctx;
+ pplh->base_url = GNUNET_strdup (url);
+ pplh->product_id = GNUNET_strdup (product_id);
+ pplh->uuid = GNUNET_strdup (uuid);
+ pplh->duration = duration;
+ pplh->quantity = quantity;
+ return pplh;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_products_lock_set_options_ (
+ struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateProductsLockOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_OPTION_QUANTITY_FRAC:
+ pplh->quantity_frac = options[i].details.quantity_frac;
+ break;
+ case
+ TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_OPTION_USE_FRACTIONAL_QUANTITY:
+ pplh->use_fractional_quantity
+ = options[i].details.use_fractional_quantity;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_products_lock_start (
+ struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh,
+ TALER_MERCHANT_PostPrivateProductsLockCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+ char unit_quantity_buf[64];
+
+ pplh->cb = cb;
+ pplh->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "private/products/%s/lock",
+ pplh->product_id);
+ pplh->url = TALER_url_join (pplh->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pplh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ GNUNET_assert ( (0 == pplh->quantity_frac) ||
+ pplh->use_fractional_quantity);
+ TALER_MERCHANT_format_quantity_string (pplh->quantity,
+ pplh->quantity_frac,
+ unit_quantity_buf,
+ sizeof (unit_quantity_buf));
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("lock_uuid",
+ pplh->uuid),
+ GNUNET_JSON_pack_time_rel ("duration",
+ pplh->duration),
+ GNUNET_JSON_pack_string ("unit_quantity",
+ unit_quantity_buf));
+ eh = TALER_MERCHANT_curl_easy_get_ (pplh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&pplh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ pplh->job = GNUNET_CURL_job_add2 (pplh->ctx,
+ eh,
+ pplh->post_ctx.headers,
+ &handle_post_products_lock_finished,
+ pplh);
+ if (NULL == pplh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_products_lock_cancel (
+ struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh)
+{
+ if (NULL != pplh->job)
+ {
+ GNUNET_CURL_job_cancel (pplh->job);
+ pplh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pplh->post_ctx);
+ GNUNET_free (pplh->product_id);
+ GNUNET_free (pplh->uuid);
+ GNUNET_free (pplh->url);
+ GNUNET_free (pplh->base_url);
+ GNUNET_free (pplh);
+}
+
+
+/* end of merchant_api_post-private-products-PRODUCT_ID-lock-new.c */
diff --git a/src/lib/merchant_api_post-private-products-new.c b/src/lib/merchant_api_post-private-products-new.c
@@ -0,0 +1,498 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_post-private-products-new.c
+ * @brief Implementation of the POST /private/products 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/post-private-products-new.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/products operation.
+ */
+struct TALER_MERCHANT_PostPrivateProductsHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateProductsCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_PRODUCTS_RESULT_CLOSURE *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;
+
+ /**
+ * Product identifier.
+ */
+ char *product_id;
+
+ /**
+ * Human-readable description.
+ */
+ char *description;
+
+ /**
+ * Unit of measurement.
+ */
+ char *unit;
+
+ /**
+ * Unit price.
+ */
+ struct TALER_Amount price;
+
+ /**
+ * Product image (base64-encoded or empty).
+ */
+ char *image;
+
+ /**
+ * Total stock (-1 for unlimited).
+ */
+ int64_t total_stock;
+
+ /**
+ * Optional internationalized descriptions (JSON).
+ */
+ const json_t *description_i18n;
+
+ /**
+ * Optional tax information (JSON array).
+ */
+ const json_t *taxes;
+
+ /**
+ * Optional storage location (JSON).
+ */
+ const json_t *address;
+
+ /**
+ * Optional expected restock time.
+ */
+ struct GNUNET_TIME_Timestamp next_restock;
+
+ /**
+ * Whether next_restock has been set.
+ */
+ bool have_next_restock;
+
+ /**
+ * Optional minimum age requirement.
+ */
+ uint32_t minimum_age;
+
+ /**
+ * Whether minimum_age has been set.
+ */
+ bool have_minimum_age;
+
+ /**
+ * Optional category IDs.
+ */
+ const uint64_t *cats;
+
+ /**
+ * Number of category IDs.
+ */
+ unsigned int num_cats;
+
+ /**
+ * Optional additional unit prices.
+ */
+ const struct TALER_Amount *unit_prices;
+
+ /**
+ * Number of additional unit prices.
+ */
+ size_t unit_prices_len;
+
+ /**
+ * Fractional part of total stock.
+ */
+ uint32_t total_stock_frac;
+
+ /**
+ * Whether fractional quantities are allowed.
+ */
+ bool unit_allow_fraction;
+
+ /**
+ * Whether unit_allow_fraction has been set.
+ */
+ bool have_unit_allow_fraction;
+
+ /**
+ * Precision level for fractions.
+ */
+ uint32_t unit_precision_level;
+
+ /**
+ * Whether unit_precision_level has been set.
+ */
+ bool have_unit_precision_level;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/products request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateProductsHandle`
+ * @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_PostPrivateProductsHandle *ppph = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateProductsResponse ppr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppph->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/products completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ ppr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ ppr.hr.ec = TALER_JSON_get_error_code (json);
+ ppr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &ppr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) ppr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ppph->cb (ppph->cb_cls,
+ &ppr);
+ TALER_MERCHANT_post_private_products_cancel (ppph);
+}
+
+
+struct TALER_MERCHANT_PostPrivateProductsHandle *
+TALER_MERCHANT_post_private_products_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *product_id,
+ const char *description,
+ const char *unit,
+ const struct TALER_Amount *price,
+ const char *image,
+ int64_t total_stock)
+{
+ struct TALER_MERCHANT_PostPrivateProductsHandle *ppph;
+
+ ppph = GNUNET_new (struct TALER_MERCHANT_PostPrivateProductsHandle);
+ ppph->ctx = ctx;
+ ppph->base_url = GNUNET_strdup (url);
+ ppph->product_id = GNUNET_strdup (product_id);
+ ppph->description = GNUNET_strdup (description);
+ ppph->unit = GNUNET_strdup (unit);
+ ppph->price = *price;
+ ppph->image = GNUNET_strdup (image);
+ ppph->total_stock = total_stock;
+ return ppph;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_products_set_options_ (
+ struct TALER_MERCHANT_PostPrivateProductsHandle *ppph,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateProductsOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_I18N:
+ ppph->description_i18n = options[i].details.description_i18n;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_TAXES:
+ ppph->taxes = options[i].details.taxes;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_ADDRESS:
+ ppph->address = options[i].details.address;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_NEXT_RESTOCK:
+ ppph->next_restock = options[i].details.next_restock;
+ ppph->have_next_restock = true;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_MINIMUM_AGE:
+ ppph->minimum_age = options[i].details.minimum_age;
+ ppph->have_minimum_age = true;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_CATEGORIES:
+ ppph->num_cats = options[i].details.categories.num;
+ ppph->cats = options[i].details.categories.cats;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_UNIT_PRICES:
+ ppph->unit_prices = options[i].details.unit_prices.prices;
+ ppph->unit_prices_len = options[i].details.unit_prices.len;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_TOTAL_STOCK_FRAC:
+ ppph->total_stock_frac = options[i].details.total_stock_frac;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_UNIT_ALLOW_FRACTION:
+ ppph->unit_allow_fraction = options[i].details.unit_allow_fraction;
+ ppph->have_unit_allow_fraction = true;
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_OPTION_UNIT_PRECISION_LEVEL:
+ ppph->unit_precision_level = options[i].details.unit_precision_level;
+ ppph->have_unit_precision_level = true;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_products_start (
+ struct TALER_MERCHANT_PostPrivateProductsHandle *ppph,
+ TALER_MERCHANT_PostPrivateProductsCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_PRODUCTS_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ json_t *categories;
+ CURL *eh;
+ const struct TALER_Amount *unit_prices;
+ size_t unit_price_len;
+ char unit_total_stock_buf[64];
+
+ ppph->cb = cb;
+ ppph->cb_cls = cb_cls;
+ ppph->url = TALER_url_join (ppph->base_url,
+ "private/products",
+ NULL);
+ if (NULL == ppph->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+
+ TALER_MERCHANT_format_stock_string (ppph->total_stock,
+ ppph->total_stock_frac,
+ unit_total_stock_buf,
+ sizeof (unit_total_stock_buf));
+
+ if (0 == ppph->num_cats)
+ {
+ categories = NULL;
+ }
+ else
+ {
+ categories = json_array ();
+ GNUNET_assert (NULL != categories);
+ for (unsigned int i = 0; i < ppph->num_cats; i++)
+ GNUNET_assert (0 ==
+ json_array_append_new (categories,
+ json_integer (ppph->cats[i])));
+ }
+
+ if ( (NULL != ppph->unit_prices) &&
+ (0 < ppph->unit_prices_len) )
+ {
+ unit_prices = ppph->unit_prices;
+ unit_price_len = ppph->unit_prices_len;
+ }
+ else
+ {
+ unit_prices = &ppph->price;
+ unit_price_len = 1;
+ }
+
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("product_id",
+ ppph->product_id),
+ GNUNET_JSON_pack_string ("product_name",
+ ppph->description),
+ GNUNET_JSON_pack_string ("description",
+ ppph->description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("description_i18n",
+ (json_t *) ppph->description_i18n)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_steal ("categories",
+ categories)),
+ GNUNET_JSON_pack_string ("unit",
+ ppph->unit),
+ TALER_JSON_pack_amount_array ("unit_price",
+ unit_price_len,
+ unit_prices),
+ GNUNET_JSON_pack_string ("image",
+ ppph->image),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_incref ("taxes",
+ (json_t *) ppph->taxes)),
+ GNUNET_JSON_pack_string ("unit_total_stock",
+ unit_total_stock_buf),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("address",
+ (json_t *) ppph->address)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp ("next_restock",
+ ppph->have_next_restock
+ ? ppph->next_restock
+ : GNUNET_TIME_UNIT_ZERO_TS)));
+ if (ppph->have_minimum_age)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "minimum_age",
+ json_integer (
+ ppph->minimum_age)));
+ }
+ if (ppph->have_unit_allow_fraction &&
+ ppph->unit_allow_fraction)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_allow_fraction",
+ json_true ()));
+ if (ppph->have_unit_precision_level)
+ {
+ GNUNET_assert (0 ==
+ json_object_set_new (req_obj,
+ "unit_precision_level",
+ json_integer (
+ ppph->unit_precision_level)));
+ }
+ }
+ eh = TALER_MERCHANT_curl_easy_get_ (ppph->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppph->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ppph->job = GNUNET_CURL_job_add2 (ppph->ctx,
+ eh,
+ ppph->post_ctx.headers,
+ &handle_post_products_finished,
+ ppph);
+ if (NULL == ppph->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_products_cancel (
+ struct TALER_MERCHANT_PostPrivateProductsHandle *ppph)
+{
+ if (NULL != ppph->job)
+ {
+ GNUNET_CURL_job_cancel (ppph->job);
+ ppph->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppph->post_ctx);
+ GNUNET_free (ppph->product_id);
+ GNUNET_free (ppph->description);
+ GNUNET_free (ppph->unit);
+ GNUNET_free (ppph->image);
+ GNUNET_free (ppph->url);
+ GNUNET_free (ppph->base_url);
+ GNUNET_free (ppph);
+}
+
+
+/* end of merchant_api_post-private-products-new.c */
diff --git a/src/lib/merchant_api_post-private-templates-new.c b/src/lib/merchant_api_post-private-templates-new.c
@@ -0,0 +1,287 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_post-private-templates-new.c
+ * @brief Implementation of the POST /private/templates 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/post-private-templates-new.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/templates operation.
+ */
+struct TALER_MERCHANT_PostPrivateTemplatesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateTemplatesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_TEMPLATES_RESULT_CLOSURE *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;
+
+ /**
+ * Template identifier.
+ */
+ char *template_id;
+
+ /**
+ * Human-readable description.
+ */
+ char *template_description;
+
+ /**
+ * Template contract (JSON).
+ */
+ json_t *template_contract;
+
+ /**
+ * Optional OTP device ID.
+ */
+ char *otp_id;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/templates request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateTemplatesHandle`
+ * @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_PostPrivateTemplatesHandle *ppth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateTemplatesResponse ptr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppth->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/templates completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ GNUNET_break_op (0);
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &ptr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) ptr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ppth->cb (ppth->cb_cls,
+ &ptr);
+ TALER_MERCHANT_post_private_templates_cancel (ppth);
+}
+
+
+struct TALER_MERCHANT_PostPrivateTemplatesHandle *
+TALER_MERCHANT_post_private_templates_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *template_id,
+ const char *template_description,
+ const json_t *template_contract)
+{
+ struct TALER_MERCHANT_PostPrivateTemplatesHandle *ppth;
+
+ ppth = GNUNET_new (struct TALER_MERCHANT_PostPrivateTemplatesHandle);
+ ppth->ctx = ctx;
+ ppth->base_url = GNUNET_strdup (url);
+ ppth->template_id = GNUNET_strdup (template_id);
+ ppth->template_description = GNUNET_strdup (template_description);
+ ppth->template_contract = json_incref ((json_t *) template_contract);
+ return ppth;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_templates_set_options_ (
+ struct TALER_MERCHANT_PostPrivateTemplatesHandle *ppth,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateTemplatesOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_TEMPLATES_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_TEMPLATES_OPTION_OTP_ID:
+ ppth->otp_id = GNUNET_strdup (options[i].details.otp_id);
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_templates_start (
+ struct TALER_MERCHANT_PostPrivateTemplatesHandle *ppth,
+ TALER_MERCHANT_PostPrivateTemplatesCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_TEMPLATES_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ppth->cb = cb;
+ ppth->cb_cls = cb_cls;
+ ppth->url = TALER_url_join (ppth->base_url,
+ "private/templates",
+ NULL);
+ if (NULL == ppth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("template_id",
+ ppth->template_id),
+ GNUNET_JSON_pack_string ("template_description",
+ ppth->template_description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("otp_id",
+ ppth->otp_id)),
+ GNUNET_JSON_pack_object_incref ("template_contract",
+ ppth->template_contract));
+ eh = TALER_MERCHANT_curl_easy_get_ (ppth->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppth->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ppth->job = GNUNET_CURL_job_add2 (ppth->ctx,
+ eh,
+ ppth->post_ctx.headers,
+ &handle_post_templates_finished,
+ ppth);
+ if (NULL == ppth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_templates_cancel (
+ struct TALER_MERCHANT_PostPrivateTemplatesHandle *ppth)
+{
+ if (NULL != ppth->job)
+ {
+ GNUNET_CURL_job_cancel (ppth->job);
+ ppth->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppth->post_ctx);
+ json_decref (ppth->template_contract);
+ GNUNET_free (ppth->template_id);
+ GNUNET_free (ppth->template_description);
+ GNUNET_free (ppth->otp_id);
+ GNUNET_free (ppth->url);
+ GNUNET_free (ppth->base_url);
+ GNUNET_free (ppth);
+}
+
+
+/* end of merchant_api_post-private-templates-new.c */
diff --git a/src/lib/merchant_api_post-private-token-new.c b/src/lib/merchant_api_post-private-token-new.c
@@ -0,0 +1,258 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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_post-private-token-new.c
+ * @brief Implementation of the POST /private/token 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/post-private-token-new.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/token operation.
+ */
+struct TALER_MERCHANT_PostPrivateTokenHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateTokenCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_TOKEN_RESULT_CLOSURE *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;
+
+ /**
+ * Instance identifier (or NULL).
+ */
+ char *instance_id;
+
+ /**
+ * Scope for the token.
+ */
+ char *scope;
+
+ /**
+ * How long the token should be valid.
+ */
+ struct GNUNET_TIME_Relative duration;
+
+ /**
+ * Whether the token may be refreshed.
+ */
+ bool refreshable;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/token request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateTokenHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_token_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostPrivateTokenHandle *ppth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateTokenResponse ptr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppth->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "POST /private/token completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.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) ptr.hr.ec);
+ break;
+ }
+ ppth->cb (ppth->cb_cls,
+ &ptr);
+ TALER_MERCHANT_post_private_token_cancel (ppth);
+}
+
+
+struct TALER_MERCHANT_PostPrivateTokenHandle *
+TALER_MERCHANT_post_private_token_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *instance_id,
+ const char *scope,
+ struct GNUNET_TIME_Relative duration,
+ bool refreshable)
+{
+ struct TALER_MERCHANT_PostPrivateTokenHandle *ppth;
+
+ ppth = GNUNET_new (struct TALER_MERCHANT_PostPrivateTokenHandle);
+ ppth->ctx = ctx;
+ ppth->base_url = GNUNET_strdup (url);
+ if (NULL != instance_id)
+ ppth->instance_id = GNUNET_strdup (instance_id);
+ ppth->scope = GNUNET_strdup (scope);
+ ppth->duration = duration;
+ ppth->refreshable = refreshable;
+ return ppth;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_token_start (
+ struct TALER_MERCHANT_PostPrivateTokenHandle *ppth,
+ TALER_MERCHANT_PostPrivateTokenCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_TOKEN_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ppth->cb = cb;
+ ppth->cb_cls = cb_cls;
+ if (NULL != ppth->instance_id)
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "instances/%s/private/token",
+ ppth->instance_id);
+ ppth->url = TALER_url_join (ppth->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ else
+ {
+ ppth->url = TALER_url_join (ppth->base_url,
+ "private/token",
+ NULL);
+ }
+ if (NULL == ppth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_time_rel ("duration",
+ ppth->duration),
+ GNUNET_JSON_pack_bool ("refreshable",
+ ppth->refreshable),
+ GNUNET_JSON_pack_string ("scope",
+ ppth->scope));
+ eh = TALER_MERCHANT_curl_easy_get_ (ppth->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppth->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_CUSTOMREQUEST,
+ MHD_HTTP_METHOD_POST));
+ ppth->job = GNUNET_CURL_job_add2 (ppth->ctx,
+ eh,
+ ppth->post_ctx.headers,
+ &handle_post_token_finished,
+ ppth);
+ if (NULL == ppth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_token_cancel (
+ struct TALER_MERCHANT_PostPrivateTokenHandle *ppth)
+{
+ if (NULL != ppth->job)
+ {
+ GNUNET_CURL_job_cancel (ppth->job);
+ ppth->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppth->post_ctx);
+ GNUNET_free (ppth->instance_id);
+ GNUNET_free (ppth->scope);
+ GNUNET_free (ppth->url);
+ GNUNET_free (ppth->base_url);
+ GNUNET_free (ppth);
+}
+
+
+/* end of merchant_api_post-private-token-new.c */
diff --git a/src/lib/merchant_api_post-private-tokenfamilies-new.c b/src/lib/merchant_api_post-private-tokenfamilies-new.c
@@ -0,0 +1,357 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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_post-private-tokenfamilies-new.c
+ * @brief Implementation of the POST /private/tokenfamilies 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/post-private-tokenfamilies-new.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/tokenfamilies operation.
+ */
+struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateTokenfamiliesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_TOKENFAMILIES_RESULT_CLOSURE *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;
+
+ /**
+ * URL-safe slug identifier.
+ */
+ char *slug;
+
+ /**
+ * Human-readable name.
+ */
+ char *name;
+
+ /**
+ * Human-readable description.
+ */
+ char *description;
+
+ /**
+ * Start of validity period.
+ */
+ struct GNUNET_TIME_Timestamp valid_after;
+
+ /**
+ * End of validity period.
+ */
+ struct GNUNET_TIME_Timestamp valid_before;
+
+ /**
+ * Duration of individual token validity.
+ */
+ struct GNUNET_TIME_Relative duration;
+
+ /**
+ * Granularity for validity alignment.
+ */
+ struct GNUNET_TIME_Relative validity_granularity;
+
+ /**
+ * Offset from purchase to start of validity.
+ */
+ struct GNUNET_TIME_Relative start_offset;
+
+ /**
+ * Kind of token family.
+ */
+ char *kind;
+
+ /**
+ * Optional internationalized descriptions (JSON).
+ */
+ json_t *description_i18n;
+
+ /**
+ * Optional extra data (JSON).
+ */
+ json_t *extra_data;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/tokenfamilies request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_tokenfamilies_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle *ptfh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateTokenfamiliesResponse tfr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ptfh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/tokenfamilies completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ tfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ GNUNET_break_op (0);
+ tfr.hr.ec = TALER_JSON_get_error_code (json);
+ tfr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ tfr.hr.ec = TALER_JSON_get_error_code (json);
+ tfr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ tfr.hr.ec = TALER_JSON_get_error_code (json);
+ tfr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ tfr.hr.ec = TALER_JSON_get_error_code (json);
+ tfr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ tfr.hr.ec = TALER_JSON_get_error_code (json);
+ tfr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ tfr.hr.ec = TALER_JSON_get_error_code (json);
+ tfr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &tfr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) tfr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ptfh->cb (ptfh->cb_cls,
+ &tfr);
+ TALER_MERCHANT_post_private_tokenfamilies_cancel (ptfh);
+}
+
+
+struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle *
+TALER_MERCHANT_post_private_tokenfamilies_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *slug,
+ const char *name,
+ const char *description,
+ 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)
+{
+ struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle *ptfh;
+
+ ptfh = GNUNET_new (struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle);
+ ptfh->ctx = ctx;
+ ptfh->base_url = GNUNET_strdup (url);
+ ptfh->slug = GNUNET_strdup (slug);
+ ptfh->name = GNUNET_strdup (name);
+ ptfh->description = GNUNET_strdup (description);
+ ptfh->valid_after = valid_after;
+ ptfh->valid_before = valid_before;
+ ptfh->duration = duration;
+ ptfh->validity_granularity = validity_granularity;
+ ptfh->start_offset = start_offset;
+ ptfh->kind = GNUNET_strdup (kind);
+ return ptfh;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_tokenfamilies_set_options_ (
+ struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle *ptfh,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateTokenfamiliesOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_TOKENFAMILIES_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_TOKENFAMILIES_OPTION_DESCRIPTION_I18N:
+ ptfh->description_i18n
+ = json_incref ((json_t *) options[i].details.description_i18n);
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_TOKENFAMILIES_OPTION_EXTRA_DATA:
+ ptfh->extra_data
+ = json_incref ((json_t *) options[i].details.extra_data);
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_tokenfamilies_start (
+ struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle *ptfh,
+ TALER_MERCHANT_PostPrivateTokenfamiliesCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_TOKENFAMILIES_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ptfh->cb = cb;
+ ptfh->cb_cls = cb_cls;
+ ptfh->url = TALER_url_join (ptfh->base_url,
+ "private/tokenfamilies",
+ NULL);
+ if (NULL == ptfh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("slug",
+ ptfh->slug),
+ GNUNET_JSON_pack_string ("name",
+ ptfh->name),
+ GNUNET_JSON_pack_string ("description",
+ ptfh->description),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("description_i18n",
+ ptfh->description_i18n)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("extra_data",
+ ptfh->extra_data)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp ("valid_after",
+ ptfh->valid_after)),
+ GNUNET_JSON_pack_timestamp ("valid_before",
+ ptfh->valid_before),
+ GNUNET_JSON_pack_time_rel ("duration",
+ ptfh->duration),
+ GNUNET_JSON_pack_time_rel ("validity_granularity",
+ ptfh->validity_granularity),
+ GNUNET_JSON_pack_time_rel ("start_offset",
+ ptfh->start_offset),
+ GNUNET_JSON_pack_string ("kind",
+ ptfh->kind));
+ eh = TALER_MERCHANT_curl_easy_get_ (ptfh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ptfh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ptfh->job = GNUNET_CURL_job_add2 (ptfh->ctx,
+ eh,
+ ptfh->post_ctx.headers,
+ &handle_post_tokenfamilies_finished,
+ ptfh);
+ if (NULL == ptfh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_tokenfamilies_cancel (
+ struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle *ptfh)
+{
+ if (NULL != ptfh->job)
+ {
+ GNUNET_CURL_job_cancel (ptfh->job);
+ ptfh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ptfh->post_ctx);
+ json_decref (ptfh->description_i18n);
+ json_decref (ptfh->extra_data);
+ GNUNET_free (ptfh->slug);
+ GNUNET_free (ptfh->name);
+ GNUNET_free (ptfh->description);
+ GNUNET_free (ptfh->kind);
+ GNUNET_free (ptfh->url);
+ GNUNET_free (ptfh->base_url);
+ GNUNET_free (ptfh);
+}
+
+
+/* end of merchant_api_post-private-tokenfamilies-new.c */
diff --git a/src/lib/merchant_api_post-private-transfers-new.c b/src/lib/merchant_api_post-private-transfers-new.c
@@ -0,0 +1,286 @@
+/*
+ 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_post-private-transfers-new.c
+ * @brief Implementation of the POST /private/transfers 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/post-private-transfers-new.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/transfers operation.
+ */
+struct TALER_MERCHANT_PostPrivateTransfersHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateTransfersCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_TRANSFERS_RESULT_CLOSURE *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;
+
+ /**
+ * Amount credited in this transfer.
+ */
+ struct TALER_Amount credit_amount;
+
+ /**
+ * Wire transfer identifier.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Payto URI of the merchant account.
+ */
+ struct TALER_FullPayto payto_uri;
+
+ /**
+ * Base URL of the exchange.
+ */
+ char *exchange_url;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/transfers request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateTransfersHandle`
+ * @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_PostPrivateTransfersHandle *ppth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateTransfersResponse ptr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppth->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 (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Did not find any data\n");
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_BAD_GATEWAY:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ {
+ 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 (json,
+ 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:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ GNUNET_break_op (0);
+ TALER_MERCHANT_parse_error_details_ (json,
+ 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;
+ }
+ ppth->cb (ppth->cb_cls,
+ &ptr);
+ TALER_MERCHANT_post_private_transfers_cancel (ppth);
+}
+
+
+struct TALER_MERCHANT_PostPrivateTransfersHandle *
+TALER_MERCHANT_post_private_transfers_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const struct TALER_Amount *credit_amount,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ struct TALER_FullPayto payto_uri,
+ const char *exchange_url)
+{
+ struct TALER_MERCHANT_PostPrivateTransfersHandle *ppth;
+
+ ppth = GNUNET_new (struct TALER_MERCHANT_PostPrivateTransfersHandle);
+ ppth->ctx = ctx;
+ ppth->base_url = GNUNET_strdup (url);
+ ppth->credit_amount = *credit_amount;
+ ppth->wtid = *wtid;
+ ppth->payto_uri.full_payto = GNUNET_strdup (payto_uri.full_payto);
+ ppth->exchange_url = GNUNET_strdup (exchange_url);
+ return ppth;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_transfers_start (
+ struct TALER_MERCHANT_PostPrivateTransfersHandle *ppth,
+ TALER_MERCHANT_PostPrivateTransfersCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_TRANSFERS_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ppth->cb = cb;
+ ppth->cb_cls = cb_cls;
+ ppth->url = TALER_url_join (ppth->base_url,
+ "private/transfers",
+ NULL);
+ if (NULL == ppth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("credit_amount",
+ &ppth->credit_amount),
+ GNUNET_JSON_pack_data_auto ("wtid",
+ &ppth->wtid),
+ TALER_JSON_pack_full_payto ("payto_uri",
+ ppth->payto_uri),
+ GNUNET_JSON_pack_string ("exchange_url",
+ ppth->exchange_url));
+ eh = TALER_MERCHANT_curl_easy_get_ (ppth->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppth->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ppth->job = GNUNET_CURL_job_add2 (ppth->ctx,
+ eh,
+ ppth->post_ctx.headers,
+ &handle_post_transfers_finished,
+ ppth);
+ if (NULL == ppth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_transfers_cancel (
+ struct TALER_MERCHANT_PostPrivateTransfersHandle *ppth)
+{
+ if (NULL != ppth->job)
+ {
+ GNUNET_CURL_job_cancel (ppth->job);
+ ppth->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppth->post_ctx);
+ GNUNET_free (ppth->payto_uri.full_payto);
+ GNUNET_free (ppth->exchange_url);
+ GNUNET_free (ppth->url);
+ GNUNET_free (ppth->base_url);
+ GNUNET_free (ppth);
+}
+
+
+/* end of merchant_api_post-private-transfers-new.c */
diff --git a/src/lib/merchant_api_post-private-units-new.c b/src/lib/merchant_api_post-private-units-new.c
@@ -0,0 +1,312 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025-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_post-private-units-new.c
+ * @brief Implementation of the POST /private/units 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/post-private-units-new.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_PostPrivateUnitsHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateUnitsCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_UNITS_RESULT_CLOSURE *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;
+
+ /**
+ * Unit identifier.
+ */
+ char *unit_id;
+
+ /**
+ * Long human-readable name.
+ */
+ char *unit_name_long;
+
+ /**
+ * Short symbol for the unit.
+ */
+ char *unit_name_short;
+
+ /**
+ * Whether fractional quantities are allowed.
+ */
+ bool unit_allow_fraction;
+
+ /**
+ * Precision level for fractional quantities.
+ */
+ uint32_t unit_precision_level;
+
+ /**
+ * Whether the unit is active.
+ */
+ bool unit_active;
+
+ /**
+ * Optional internationalized long names (JSON).
+ */
+ json_t *unit_name_long_i18n;
+
+ /**
+ * Optional internationalized short names (JSON).
+ */
+ json_t *unit_name_short_i18n;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/units request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateUnitsHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_post_units_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateUnitsResponse pur = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppuh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/units completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ pur.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ 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:
+ pur.hr.ec = TALER_JSON_get_error_code (json);
+ pur.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &pur.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) pur.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ppuh->cb (ppuh->cb_cls,
+ &pur);
+ TALER_MERCHANT_post_private_units_cancel (ppuh);
+}
+
+
+struct TALER_MERCHANT_PostPrivateUnitsHandle *
+TALER_MERCHANT_post_private_units_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *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)
+{
+ struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh;
+
+ ppuh = GNUNET_new (struct TALER_MERCHANT_PostPrivateUnitsHandle);
+ ppuh->ctx = ctx;
+ ppuh->base_url = GNUNET_strdup (url);
+ ppuh->unit_id = GNUNET_strdup (unit_id);
+ ppuh->unit_name_long = GNUNET_strdup (unit_name_long);
+ ppuh->unit_name_short = GNUNET_strdup (unit_name_short);
+ ppuh->unit_allow_fraction = unit_allow_fraction;
+ ppuh->unit_precision_level = unit_precision_level;
+ ppuh->unit_active = unit_active;
+ return ppuh;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_private_units_set_options_ (
+ struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostPrivateUnitsOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_NAME_LONG_I18N:
+ ppuh->unit_name_long_i18n
+ = json_incref ((json_t *) options[i].details.unit_name_long_i18n);
+ break;
+ case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_NAME_SHORT_I18N:
+ ppuh->unit_name_short_i18n
+ = json_incref ((json_t *) options[i].details.unit_name_short_i18n);
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_units_start (
+ struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh,
+ TALER_MERCHANT_PostPrivateUnitsCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_UNITS_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ppuh->cb = cb;
+ ppuh->cb_cls = cb_cls;
+ ppuh->url = TALER_url_join (ppuh->base_url,
+ "private/units",
+ NULL);
+ if (NULL == ppuh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("unit",
+ ppuh->unit_id),
+ GNUNET_JSON_pack_string ("unit_name_long",
+ ppuh->unit_name_long),
+ GNUNET_JSON_pack_string ("unit_name_short",
+ ppuh->unit_name_short),
+ GNUNET_JSON_pack_bool ("unit_allow_fraction",
+ ppuh->unit_allow_fraction),
+ GNUNET_JSON_pack_uint64 ("unit_precision_level",
+ (uint64_t) ppuh->unit_precision_level),
+ GNUNET_JSON_pack_bool ("unit_active",
+ ppuh->unit_active),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("unit_name_long_i18n",
+ ppuh->unit_name_long_i18n)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("unit_name_short_i18n",
+ ppuh->unit_name_short_i18n)));
+ eh = TALER_MERCHANT_curl_easy_get_ (ppuh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppuh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ppuh->job = GNUNET_CURL_job_add2 (ppuh->ctx,
+ eh,
+ ppuh->post_ctx.headers,
+ &handle_post_units_finished,
+ ppuh);
+ if (NULL == ppuh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_units_cancel (
+ struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh)
+{
+ if (NULL != ppuh->job)
+ {
+ GNUNET_CURL_job_cancel (ppuh->job);
+ ppuh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppuh->post_ctx);
+ json_decref (ppuh->unit_name_long_i18n);
+ json_decref (ppuh->unit_name_short_i18n);
+ GNUNET_free (ppuh->unit_id);
+ GNUNET_free (ppuh->unit_name_long);
+ GNUNET_free (ppuh->unit_name_short);
+ GNUNET_free (ppuh->url);
+ GNUNET_free (ppuh->base_url);
+ GNUNET_free (ppuh);
+}
+
+
+/* end of merchant_api_post-private-units-new.c */
diff --git a/src/lib/merchant_api_post-private-webhooks-new.c b/src/lib/merchant_api_post-private-webhooks-new.c
@@ -0,0 +1,283 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_post-private-webhooks-new.c
+ * @brief Implementation of the POST /private/webhooks 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/post-private-webhooks-new.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/webhooks operation.
+ */
+struct TALER_MERCHANT_PostPrivateWebhooksHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostPrivateWebhooksCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_PRIVATE_WEBHOOKS_RESULT_CLOSURE *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;
+
+ /**
+ * Webhook identifier.
+ */
+ char *webhook_id;
+
+ /**
+ * Event type that triggers this webhook.
+ */
+ char *event_type;
+
+ /**
+ * URL to send notifications to.
+ */
+ char *notification_url;
+
+ /**
+ * HTTP method to use for notifications.
+ */
+ char *http_method;
+
+ /**
+ * Template for HTTP request headers.
+ */
+ char *header_template;
+
+ /**
+ * Template for the HTTP request body.
+ */
+ char *body_template;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /private/webhooks request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostPrivateWebhooksHandle`
+ * @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_PostPrivateWebhooksHandle *ppwh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostPrivateWebhooksResponse wpr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+
+ ppwh->job = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "POST /private/webhooks completed with response code %u\n",
+ (unsigned int) response_code);
+ switch (response_code)
+ {
+ case 0:
+ wpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ wpr.hr.ec = TALER_JSON_get_error_code (json);
+ wpr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &wpr.hr);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) wpr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ ppwh->cb (ppwh->cb_cls,
+ &wpr);
+ TALER_MERCHANT_post_private_webhooks_cancel (ppwh);
+}
+
+
+struct TALER_MERCHANT_PostPrivateWebhooksHandle *
+TALER_MERCHANT_post_private_webhooks_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *webhook_id,
+ const char *event_type,
+ const char *notification_url,
+ const char *http_method,
+ const char *header_template,
+ const char *body_template)
+{
+ struct TALER_MERCHANT_PostPrivateWebhooksHandle *ppwh;
+
+ ppwh = GNUNET_new (struct TALER_MERCHANT_PostPrivateWebhooksHandle);
+ ppwh->ctx = ctx;
+ ppwh->base_url = GNUNET_strdup (url);
+ ppwh->webhook_id = GNUNET_strdup (webhook_id);
+ ppwh->event_type = GNUNET_strdup (event_type);
+ ppwh->notification_url = GNUNET_strdup (notification_url);
+ ppwh->http_method = GNUNET_strdup (http_method);
+ ppwh->header_template = GNUNET_strdup (header_template);
+ ppwh->body_template = GNUNET_strdup (body_template);
+ return ppwh;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_private_webhooks_start (
+ struct TALER_MERCHANT_PostPrivateWebhooksHandle *ppwh,
+ TALER_MERCHANT_PostPrivateWebhooksCallback cb,
+ TALER_MERCHANT_POST_PRIVATE_WEBHOOKS_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ ppwh->cb = cb;
+ ppwh->cb_cls = cb_cls;
+ ppwh->url = TALER_url_join (ppwh->base_url,
+ "private/webhooks",
+ NULL);
+ if (NULL == ppwh->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("webhook_id",
+ ppwh->webhook_id),
+ GNUNET_JSON_pack_string ("event_type",
+ ppwh->event_type),
+ GNUNET_JSON_pack_string ("url",
+ ppwh->notification_url),
+ GNUNET_JSON_pack_string ("http_method",
+ ppwh->http_method),
+ GNUNET_JSON_pack_string ("header_template",
+ ppwh->header_template),
+ GNUNET_JSON_pack_string ("body_template",
+ ppwh->body_template));
+ eh = TALER_MERCHANT_curl_easy_get_ (ppwh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&ppwh->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ ppwh->job = GNUNET_CURL_job_add2 (ppwh->ctx,
+ eh,
+ ppwh->post_ctx.headers,
+ &handle_post_webhooks_finished,
+ ppwh);
+ if (NULL == ppwh->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_private_webhooks_cancel (
+ struct TALER_MERCHANT_PostPrivateWebhooksHandle *ppwh)
+{
+ if (NULL != ppwh->job)
+ {
+ GNUNET_CURL_job_cancel (ppwh->job);
+ ppwh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&ppwh->post_ctx);
+ GNUNET_free (ppwh->webhook_id);
+ GNUNET_free (ppwh->event_type);
+ GNUNET_free (ppwh->notification_url);
+ GNUNET_free (ppwh->http_method);
+ GNUNET_free (ppwh->header_template);
+ GNUNET_free (ppwh->body_template);
+ GNUNET_free (ppwh->url);
+ GNUNET_free (ppwh->base_url);
+ GNUNET_free (ppwh);
+}
+
+
+/* end of merchant_api_post-private-webhooks-new.c */
diff --git a/src/lib/merchant_api_post-templates-TEMPLATE_ID-new.c b/src/lib/merchant_api_post-templates-TEMPLATE_ID-new.c
@@ -0,0 +1,330 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022-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_post-templates-TEMPLATE_ID-new.c
+ * @brief Implementation of the POST /templates/$TEMPLATE_ID 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/post-templates-TEMPLATE_ID-new.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 /templates/$TEMPLATE_ID operation.
+ */
+struct TALER_MERCHANT_PostTemplatesHandle
+{
+ /**
+ * Base URL of the merchant backend.
+ */
+ char *base_url;
+
+ /**
+ * The full URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_PostTemplatesCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ TALER_MERCHANT_POST_TEMPLATES_RESULT_CLOSURE *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;
+
+ /**
+ * Template identifier.
+ */
+ char *template_id;
+
+ /**
+ * Optional summary string.
+ */
+ const char *summary;
+
+ /**
+ * Optional amount.
+ */
+ const struct TALER_Amount *amount;
+
+ /**
+ * Optional detailed contract customization (JSON).
+ */
+ const json_t *details;
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /templates/$TEMPLATE_ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_PostTemplatesHandle`
+ * @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_PostTemplatesHandle *pth = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_PostTemplatesResponse ptr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
+ };
+ struct TALER_ClaimTokenP token;
+
+ pth->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ bool no_token;
+ bool no_pay_deadline;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("order_id",
+ &ptr.details.ok.order_id),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto ("token",
+ &token),
+ &no_token),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp ("pay_deadline",
+ &ptr.details.ok.pay_deadline),
+ &no_pay_deadline),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ptr.hr.http_status = 0;
+ ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (! no_token)
+ ptr.details.ok.token = &token;
+ if (no_pay_deadline)
+ ptr.details.ok.pay_deadline = GNUNET_TIME_UNIT_ZERO_TS;
+ break;
+ }
+ case MHD_HTTP_BAD_REQUEST:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_CONFLICT:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.hr.hint = TALER_JSON_get_error_hint (json);
+ break;
+ default:
+ ptr.hr.ec = TALER_JSON_get_error_code (json);
+ ptr.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) ptr.hr.ec);
+ GNUNET_break_op (0);
+ break;
+ }
+ pth->cb (pth->cb_cls,
+ &ptr);
+ TALER_MERCHANT_post_templates_cancel (pth);
+}
+
+
+struct TALER_MERCHANT_PostTemplatesHandle *
+TALER_MERCHANT_post_templates_create (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *template_id)
+{
+ struct TALER_MERCHANT_PostTemplatesHandle *pth;
+
+ pth = GNUNET_new (struct TALER_MERCHANT_PostTemplatesHandle);
+ pth->ctx = ctx;
+ pth->base_url = GNUNET_strdup (url);
+ pth->template_id = GNUNET_strdup (template_id);
+ return pth;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MERCHANT_post_templates_set_options_ (
+ struct TALER_MERCHANT_PostTemplatesHandle *pth,
+ unsigned int num_options,
+ const struct TALER_MERCHANT_PostTemplatesOptionValue *options)
+{
+ for (unsigned int i = 0; i < num_options; i++)
+ {
+ switch (options[i].option)
+ {
+ case TALER_MERCHANT_POST_TEMPLATES_OPTION_END:
+ return GNUNET_OK;
+ case TALER_MERCHANT_POST_TEMPLATES_OPTION_SUMMARY:
+ pth->summary = options[i].details.summary;
+ break;
+ case TALER_MERCHANT_POST_TEMPLATES_OPTION_AMOUNT:
+ pth->amount = options[i].details.amount;
+ break;
+ case TALER_MERCHANT_POST_TEMPLATES_OPTION_DETAILS:
+ pth->details = options[i].details.details;
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+enum TALER_ErrorCode
+TALER_MERCHANT_post_templates_start (
+ struct TALER_MERCHANT_PostTemplatesHandle *pth,
+ TALER_MERCHANT_PostTemplatesCallback cb,
+ TALER_MERCHANT_POST_TEMPLATES_RESULT_CLOSURE *cb_cls)
+{
+ json_t *req_obj;
+ CURL *eh;
+
+ pth->cb = cb;
+ pth->cb_cls = cb_cls;
+ {
+ char *path;
+
+ GNUNET_asprintf (&path,
+ "templates/%s",
+ pth->template_id);
+ pth->url = TALER_url_join (pth->base_url,
+ path,
+ NULL);
+ GNUNET_free (path);
+ }
+ if (NULL == pth->url)
+ return TALER_EC_GENERIC_CONFIGURATION_INVALID;
+ if (NULL != pth->details)
+ {
+ /* If detailed contract customization is provided, send it directly. */
+ req_obj = json_incref ((json_t *) pth->details);
+ }
+ else
+ {
+ /* Build fixed-order request with optional summary and amount. */
+ req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("template_type",
+ "fixed-order"),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("summary",
+ pth->summary)),
+ GNUNET_JSON_pack_allow_null (
+ TALER_JSON_pack_amount ("amount",
+ pth->amount)));
+ }
+ eh = TALER_MERCHANT_curl_easy_get_ (pth->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&pth->post_ctx,
+ eh,
+ req_obj)) )
+ {
+ GNUNET_break (0);
+ json_decref (req_obj);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ }
+ json_decref (req_obj);
+ pth->job = GNUNET_CURL_job_add2 (pth->ctx,
+ eh,
+ pth->post_ctx.headers,
+ &handle_post_templates_finished,
+ pth);
+ if (NULL == pth->job)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return TALER_EC_NONE;
+}
+
+
+void
+TALER_MERCHANT_post_templates_cancel (
+ struct TALER_MERCHANT_PostTemplatesHandle *pth)
+{
+ if (NULL != pth->job)
+ {
+ GNUNET_CURL_job_cancel (pth->job);
+ pth->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&pth->post_ctx);
+ GNUNET_free (pth->template_id);
+ GNUNET_free (pth->url);
+ GNUNET_free (pth->base_url);
+ GNUNET_free (pth);
+}
+
+
+/* end of merchant_api_post-templates-TEMPLATE_ID-new.c */
diff --git a/src/testing/testing_api_cmd_abort_order.c b/src/testing/testing_api_cmd_abort_order.c
@@ -27,6 +27,7 @@
#include <taler/taler_signatures.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-orders-ORDER_ID-abort-new.h>
/**
* State for a " abort" CMD.
@@ -47,7 +48,7 @@ struct AbortState
/**
* Handle to a "abort" operation.
*/
- struct TALER_MERCHANT_OrderAbortHandle *oah;
+ struct TALER_MERCHANT_PostOrdersAbortHandle *oah;
/**
* Interpreter state.
@@ -57,7 +58,7 @@ struct AbortState
/**
* The actual abort/refund data.
*/
- struct TALER_MERCHANT_AbortedCoin *acs;
+ struct TALER_MERCHANT_PostOrdersAbortedCoin *acs;
/**
* Expected HTTP response code.
@@ -86,7 +87,7 @@ struct AbortState
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-build_coins (struct TALER_MERCHANT_AbortCoin **ac,
+build_coins (struct TALER_MERCHANT_PostOrdersAbortCoin **ac,
unsigned int *nac,
char *coins,
struct TALER_TESTING_Interpreter *is)
@@ -97,7 +98,7 @@ build_coins (struct TALER_MERCHANT_AbortCoin **ac,
{
char *ctok;
unsigned int ci;
- struct TALER_MERCHANT_AbortCoin *icoin;
+ struct TALER_MERCHANT_PostOrdersAbortCoin *icoin;
/* Token syntax is "LABEL[/NUMBER]" */
ctok = strchr (token, '/');
@@ -168,7 +169,7 @@ build_coins (struct TALER_MERCHANT_AbortCoin **ac,
*/
static void
abort_cb (void *cls,
- const struct TALER_MERCHANT_AbortResponse *ar)
+ const struct TALER_MERCHANT_PostOrdersAbortResponse *ar)
{
struct AbortState *as = cls;
@@ -191,11 +192,11 @@ abort_cb (void *cls,
ar->details.ok.num_aborts);
as->acs_length = ar->details.ok.num_aborts;
as->acs = GNUNET_new_array (as->acs_length,
- struct TALER_MERCHANT_AbortedCoin);
+ struct TALER_MERCHANT_PostOrdersAbortedCoin);
GNUNET_memcpy (as->acs,
ar->details.ok.aborts,
as->acs_length
- * sizeof (struct TALER_MERCHANT_AbortedCoin));
+ * sizeof (struct TALER_MERCHANT_PostOrdersAbortedCoin));
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Successful pay-abort (HTTP status: %u)\n",
@@ -227,7 +228,7 @@ abort_run (void *cls,
struct TALER_Amount total_amount;
const char *error_name;
unsigned int error_line;
- struct TALER_MERCHANT_AbortCoin *abort_coins;
+ struct TALER_MERCHANT_PostOrdersAbortCoin *abort_coins;
unsigned int nabort_coins;
char *cr;
@@ -313,21 +314,26 @@ abort_run (void *cls,
TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
&h_proposal))
TALER_TESTING_FAIL (is);
- as->oah = TALER_MERCHANT_order_abort (TALER_TESTING_interpreter_get_context (
- is),
- as->merchant_url,
- order_id,
- &merchant_pub,
- h_proposal,
- nabort_coins,
- abort_coins,
- &abort_cb,
- as);
+ as->oah = TALER_MERCHANT_post_orders_abort_create (
+ TALER_TESTING_interpreter_get_context (is),
+ as->merchant_url,
+ order_id,
+ &merchant_pub,
+ h_proposal,
+ nabort_coins,
+ abort_coins);
GNUNET_array_grow (abort_coins,
nabort_coins,
0);
- if (NULL == as->oah)
- TALER_TESTING_FAIL (is);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_orders_abort_start (
+ as->oah,
+ &abort_cb,
+ as);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -349,7 +355,7 @@ abort_cleanup (void *cls,
"Command `%s' did not complete.\n",
TALER_TESTING_interpreter_get_current_label (
as->is));
- TALER_MERCHANT_order_abort_cancel (as->oah);
+ TALER_MERCHANT_post_orders_abort_cancel (as->oah);
}
GNUNET_array_grow (as->acs,
as->acs_length,
diff --git a/src/testing/testing_api_cmd_claim_order.c b/src/testing/testing_api_cmd_claim_order.c
@@ -27,6 +27,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-orders-ORDER_ID-claim-new.h>
/**
* State for a "order claim" CMD. Not used by
@@ -72,7 +73,7 @@ struct OrderClaimState
/**
* /order/claim operation handle.
*/
- struct TALER_MERCHANT_OrderClaimHandle *och;
+ struct TALER_MERCHANT_PostOrdersClaimHandle *och;
/**
* Reference to a order operation. Will offer the
@@ -106,7 +107,7 @@ order_claim_cleanup (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command '%s' did not complete\n",
cmd->label);
- TALER_MERCHANT_order_claim_cancel (pls->och);
+ TALER_MERCHANT_post_orders_claim_cancel (pls->och);
pls->och = NULL;
}
if (NULL != pls->contract_terms)
@@ -127,7 +128,7 @@ order_claim_cleanup (void *cls,
*/
static void
order_claim_cb (void *cls,
- const struct TALER_MERCHANT_OrderClaimResponse *ocr)
+ const struct TALER_MERCHANT_PostOrdersClaimResponse *ocr)
{
struct OrderClaimState *pls = cls;
@@ -139,7 +140,7 @@ order_claim_cb (void *cls,
pls->contract_terms
= json_incref ((json_t *) ocr->details.ok.contract_terms);
pls->contract_terms_hash = ocr->details.ok.h_contract_terms;
- pls->merchant_sig = ocr->details.ok.sig;
+ pls->merchant_sig = ocr->details.ok.merchant_sig;
{
const char *error_name;
unsigned int error_line;
@@ -217,16 +218,24 @@ order_claim_run (void *cls,
&order_id))
TALER_TESTING_FAIL (is);
}
- pls->och = TALER_MERCHANT_order_claim (TALER_TESTING_interpreter_get_context (
- is),
- pls->merchant_url,
- order_id,
- nonce,
- claim_token,
- &order_claim_cb,
- pls);
- if (NULL == pls->och)
- TALER_TESTING_FAIL (is);
+ pls->och = TALER_MERCHANT_post_orders_claim_create (
+ TALER_TESTING_interpreter_get_context (is),
+ pls->merchant_url,
+ order_id,
+ nonce);
+ if (NULL != claim_token)
+ TALER_MERCHANT_post_orders_claim_set_options (
+ pls->och,
+ TALER_MERCHANT_post_orders_claim_option_token (claim_token));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_orders_claim_start (
+ pls->och,
+ &order_claim_cb,
+ pls);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
diff --git a/src/testing/testing_api_cmd_config.c b/src/testing/testing_api_cmd_config.c
@@ -26,8 +26,8 @@
#include "taler/platform.h"
#include <taler/taler_exchange_service.h>
#include <taler/taler_testing_lib.h>
-#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-config-new.h>
/**
@@ -38,7 +38,7 @@ struct ConfigState
/**
* Operation handle for a GET /public/config request.
*/
- struct TALER_MERCHANT_ConfigGetHandle *vgh;
+ struct TALER_MERCHANT_GetConfigHandle *vgh;
/**
* Base URL of the merchant serving the request.
@@ -74,7 +74,7 @@ config_cleanup (void *cls,
if (NULL != cs->vgh)
{
TALER_LOG_WARNING ("config operation did not complete\n");
- TALER_MERCHANT_config_get_cancel (cs->vgh);
+ TALER_MERCHANT_get_config_cancel (cs->vgh);
}
GNUNET_free (cs);
}
@@ -88,7 +88,7 @@ config_cleanup (void *cls,
*/
static void
config_cb (void *cls,
- const struct TALER_MERCHANT_ConfigResponse *cr)
+ const struct TALER_MERCHANT_GetConfigResponse *cr)
{
struct ConfigState *cs = cls;
@@ -97,7 +97,7 @@ config_cb (void *cls,
TALER_TESTING_FAIL (cs->is);
if (MHD_HTTP_OK == cr->hr.http_status)
{
- if (TALER_MERCHANT_VC_MATCH != cr->details.ok.compat)
+ if (TALER_MERCHANT_GET_CONFIG_VC_MATCH != cr->details.ok.compat)
TALER_TESTING_FAIL (cs->is);
}
TALER_TESTING_interpreter_next (cs->is);
@@ -119,12 +119,17 @@ config_run (void *cls,
struct ConfigState *cs = cls;
cs->is = is;
- cs->vgh = TALER_MERCHANT_config_get (TALER_TESTING_interpreter_get_context (
- is),
- cs->merchant_url,
- &config_cb,
- cs);
- GNUNET_assert (NULL != cs->vgh);
+ cs->vgh = TALER_MERCHANT_get_config_create (
+ TALER_TESTING_interpreter_get_context (is),
+ cs->merchant_url);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_config_start (cs->vgh,
+ &config_cb,
+ cs);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
diff --git a/src/testing/testing_api_cmd_delete_account.c b/src/testing/testing_api_cmd_delete_account.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-accounts-H_WIRE-new.h>
/**
@@ -37,7 +38,7 @@ struct DeleteAccountState
/**
* Handle for a "DELETE account" request.
*/
- struct TALER_MERCHANT_AccountDeleteHandle *adh;
+ struct TALER_MERCHANT_DeletePrivateAccountHandle *adh;
/**
* The interpreter state.
@@ -70,7 +71,8 @@ struct DeleteAccountState
*/
static void
delete_account_cb (void *cls,
- const struct TALER_MERCHANT_AccountDeleteResponse *adr)
+ const struct TALER_MERCHANT_DeletePrivateAccountResponse *adr
+ )
{
struct DeleteAccountState *das = cls;
@@ -154,13 +156,18 @@ delete_account_run (void *cls,
return;
}
GNUNET_assert (NULL != h_wire);
- das->adh = TALER_MERCHANT_account_delete (
+ das->adh = TALER_MERCHANT_delete_private_account_create (
TALER_TESTING_interpreter_get_context (is),
merchant_url,
- h_wire,
- &delete_account_cb,
- das);
- GNUNET_assert (NULL != das->adh);
+ h_wire);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_account_start (das->adh,
+ &delete_account_cb,
+ das);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -181,7 +188,7 @@ delete_account_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /accounts/$ID operation did not complete\n");
- TALER_MERCHANT_account_delete_cancel (das->adh);
+ TALER_MERCHANT_delete_private_account_cancel (das->adh);
}
GNUNET_free (das);
}
diff --git a/src/testing/testing_api_cmd_delete_donau_instances.c b/src/testing/testing_api_cmd_delete_donau_instances.c
@@ -25,6 +25,7 @@
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
#include "taler/taler_merchant_donau.h"
+#include <taler/taler-merchant/delete-private-donau-DONAU_SERIAL-new.h>
/**
* State for DELETE /donau/$charity_id testing command.
@@ -34,7 +35,7 @@ struct DeleteDonauState
/**
* Handle for a DELETE /donau request.
*/
- struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh;
+ struct TALER_MERCHANT_DeletePrivateDonauHandle *ddh;
/**
* The interpreter state.
@@ -65,24 +66,24 @@ struct DeleteDonauState
*/
static void
delete_donau_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_DeletePrivateDonauResponse *ddr)
{
struct DeleteDonauState *dds = cls;
dds->ddh = NULL;
- if (dds->expected_http_status != hr->http_status)
+ if (dds->expected_http_status != ddr->hr.http_status)
{
TALER_TESTING_unexpected_status_with_body (
dds->is,
- hr->http_status,
+ ddr->hr.http_status,
dds->expected_http_status,
- hr->reply);
+ ddr->hr.reply);
TALER_TESTING_interpreter_fail (dds->is);
return;
}
- switch (hr->http_status)
+ switch (ddr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -99,7 +100,7 @@ delete_donau_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u\n",
- hr->http_status);
+ ddr->hr.http_status);
}
TALER_TESTING_interpreter_next (dds->is);
@@ -121,19 +122,17 @@ delete_donau_run (void *cls,
struct DeleteDonauState *dds = cls;
dds->is = is;
- dds->ddh = TALER_MERCHANT_donau_instance_delete (
+ dds->ddh = TALER_MERCHANT_delete_private_donau_create (
TALER_TESTING_interpreter_get_context (is),
dds->url,
- dds->charity_id,
- &delete_donau_cb,
- dds);
-
- if (NULL == dds->ddh)
+ dds->charity_id);
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to initiate DELETE /donau/$charity_id\n");
- TALER_TESTING_interpreter_fail (is);
- return;
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_donau_start (dds->ddh,
+ &delete_donau_cb,
+ dds);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
}
@@ -154,7 +153,7 @@ delete_donau_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /donau/$charity_id operation did not complete\n");
- TALER_MERCHANT_donau_instance_delete_cancel (dds->ddh);
+ TALER_MERCHANT_delete_private_donau_cancel (dds->ddh);
}
GNUNET_free (dds);
}
diff --git a/src/testing/testing_api_cmd_delete_instance.c b/src/testing/testing_api_cmd_delete_instance.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-management-instances-INSTANCE-new.h>
/**
@@ -37,7 +38,7 @@ struct DeleteInstanceState
/**
* Handle for a "DELETE instance" request.
*/
- struct TALER_MERCHANT_InstanceDeleteHandle *igh;
+ struct TALER_MERCHANT_DeleteManagementInstanceHandle *igh;
/**
* The interpreter state.
@@ -75,22 +76,23 @@ struct DeleteInstanceState
*/
static void
delete_instance_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct
+ TALER_MERCHANT_DeleteManagementInstanceResponse *dir)
{
struct DeleteInstanceState *dis = cls;
dis->igh = NULL;
- if (dis->http_status != hr->http_status)
+ if (dis->http_status != dir->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ dir->hr.http_status,
+ (int) dir->hr.ec,
TALER_TESTING_interpreter_get_current_label (dis->is));
TALER_TESTING_interpreter_fail (dis->is);
return;
}
- switch (hr->http_status)
+ switch (dir->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -101,7 +103,7 @@ delete_instance_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %d for DELETE instance.\n",
- hr->http_status);
+ dir->hr.http_status);
}
TALER_TESTING_interpreter_next (dis->is);
}
@@ -122,21 +124,22 @@ delete_instance_run (void *cls,
struct DeleteInstanceState *dis = cls;
dis->is = is;
+ dis->igh = TALER_MERCHANT_delete_management_instance_create (
+ TALER_TESTING_interpreter_get_context (is),
+ dis->merchant_url,
+ dis->instance_id);
if (dis->purge)
- dis->igh = TALER_MERCHANT_instance_purge (
- TALER_TESTING_interpreter_get_context (is),
- dis->merchant_url,
- dis->instance_id,
- &delete_instance_cb,
- dis);
- else
- dis->igh = TALER_MERCHANT_instance_delete (
- TALER_TESTING_interpreter_get_context (is),
- dis->merchant_url,
- dis->instance_id,
- &delete_instance_cb,
- dis);
- GNUNET_assert (NULL != dis->igh);
+ TALER_MERCHANT_delete_management_instance_set_options (
+ dis->igh,
+ { .option = TALER_MERCHANT_DELETE_MANAGEMENT_INSTANCE_OPTION_PURGE });
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_management_instance_start (dis->igh,
+ &delete_instance_cb,
+ dis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -157,7 +160,7 @@ delete_instance_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /instances/$ID operation did not complete\n");
- TALER_MERCHANT_instance_delete_cancel (dis->igh);
+ TALER_MERCHANT_delete_management_instance_cancel (dis->igh);
}
GNUNET_free (dis);
}
diff --git a/src/testing/testing_api_cmd_delete_order.c b/src/testing/testing_api_cmd_delete_order.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-orders-ORDER_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct DeleteOrderState
/**
* Handle for a "DELETE order" request.
*/
- struct TALER_MERCHANT_OrderDeleteHandle *odh;
+ struct TALER_MERCHANT_DeletePrivateOrderHandle *odh;
/**
* The interpreter state.
@@ -70,22 +71,22 @@ struct DeleteOrderState
*/
static void
delete_order_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_DeletePrivateOrderResponse *dor)
{
struct DeleteOrderState *dos = cls;
dos->odh = NULL;
- if (dos->http_status != hr->http_status)
+ if (dos->http_status != dor->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ dor->hr.http_status,
+ (int) dor->hr.ec,
TALER_TESTING_interpreter_get_current_label (dos->is));
TALER_TESTING_interpreter_fail (dos->is);
return;
}
- switch (hr->http_status)
+ switch (dor->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -99,7 +100,7 @@ delete_order_cb (void *cls,
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for DELETE order.\n",
- hr->http_status);
+ dor->hr.http_status);
}
TALER_TESTING_interpreter_next (dos->is);
}
@@ -121,14 +122,18 @@ delete_order_run (void *cls,
struct DeleteOrderState *dos = cls;
dos->is = is;
- dos->odh = TALER_MERCHANT_order_delete (
+ dos->odh = TALER_MERCHANT_delete_private_order_create (
TALER_TESTING_interpreter_get_context (is),
dos->merchant_url,
- dos->order_id,
- false, /* FIXME: support testing force... */
- &delete_order_cb,
- dos);
- GNUNET_assert (NULL != dos->odh);
+ dos->order_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_order_start (dos->odh,
+ &delete_order_cb,
+ dos);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -149,7 +154,7 @@ delete_order_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /orders/$ORDER_ID operation did not complete\n");
- TALER_MERCHANT_order_delete_cancel (dos->odh);
+ TALER_MERCHANT_delete_private_order_cancel (dos->odh);
}
GNUNET_free (dos);
}
diff --git a/src/testing/testing_api_cmd_delete_otp_device.c b/src/testing/testing_api_cmd_delete_otp_device.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-otp-devices-DEVICE_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct DeleteOtpDeviceState
/**
* Handle for a "DELETE otp_device" request.
*/
- struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh;
+ struct TALER_MERCHANT_DeletePrivateOtpDeviceHandle *tdh;
/**
* The interpreter state.
@@ -70,22 +71,23 @@ struct DeleteOtpDeviceState
*/
static void
delete_otp_device_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct
+ TALER_MERCHANT_DeletePrivateOtpDeviceResponse *dor)
{
struct DeleteOtpDeviceState *dis = cls;
dis->tdh = NULL;
- if (dis->http_status != hr->http_status)
+ if (dis->http_status != dor->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ dor->hr.http_status,
+ (int) dor->hr.ec,
TALER_TESTING_interpreter_get_current_label (dis->is));
TALER_TESTING_interpreter_fail (dis->is);
return;
}
- switch (hr->http_status)
+ switch (dor->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -98,7 +100,7 @@ delete_otp_device_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for DELETE otp_device.\n",
- hr->http_status);
+ dor->hr.http_status);
}
TALER_TESTING_interpreter_next (dis->is);
}
@@ -120,13 +122,18 @@ delete_otp_device_run (void *cls,
struct DeleteOtpDeviceState *dis = cls;
dis->is = is;
- dis->tdh = TALER_MERCHANT_otp_device_delete (
+ dis->tdh = TALER_MERCHANT_delete_private_otp_device_create (
TALER_TESTING_interpreter_get_context (is),
dis->merchant_url,
- dis->otp_device_id,
- &delete_otp_device_cb,
- dis);
- GNUNET_assert (NULL != dis->tdh);
+ dis->otp_device_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_otp_device_start (dis->tdh,
+ &delete_otp_device_cb,
+ dis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -147,7 +154,7 @@ delete_otp_device_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /otp-devices/$ID operation did not complete\n");
- TALER_MERCHANT_otp_device_delete_cancel (dis->tdh);
+ TALER_MERCHANT_delete_private_otp_device_cancel (dis->tdh);
}
GNUNET_free (dis);
}
diff --git a/src/testing/testing_api_cmd_delete_product.c b/src/testing/testing_api_cmd_delete_product.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-products-PRODUCT_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct DeleteProductState
/**
* Handle for a "DELETE product" request.
*/
- struct TALER_MERCHANT_ProductDeleteHandle *pdh;
+ struct TALER_MERCHANT_DeletePrivateProductHandle *pdh;
/**
* The interpreter state.
@@ -70,22 +71,23 @@ struct DeleteProductState
*/
static void
delete_product_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_DeletePrivateProductResponse *dpr
+ )
{
struct DeleteProductState *dis = cls;
dis->pdh = NULL;
- if (dis->http_status != hr->http_status)
+ if (dis->http_status != dpr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ dpr->hr.http_status,
+ (int) dpr->hr.ec,
TALER_TESTING_interpreter_get_current_label (dis->is));
TALER_TESTING_interpreter_fail (dis->is);
return;
}
- switch (hr->http_status)
+ switch (dpr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -98,7 +100,7 @@ delete_product_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for DELETE product.\n",
- hr->http_status);
+ dpr->hr.http_status);
}
TALER_TESTING_interpreter_next (dis->is);
}
@@ -120,13 +122,18 @@ delete_product_run (void *cls,
struct DeleteProductState *dis = cls;
dis->is = is;
- dis->pdh = TALER_MERCHANT_product_delete (
+ dis->pdh = TALER_MERCHANT_delete_private_product_create (
TALER_TESTING_interpreter_get_context (is),
dis->merchant_url,
- dis->product_id,
- &delete_product_cb,
- dis);
- GNUNET_assert (NULL != dis->pdh);
+ dis->product_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_product_start (dis->pdh,
+ &delete_product_cb,
+ dis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -147,7 +154,7 @@ delete_product_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /products/$ID operation did not complete\n");
- TALER_MERCHANT_product_delete_cancel (dis->pdh);
+ TALER_MERCHANT_delete_private_product_cancel (dis->pdh);
}
GNUNET_free (dis);
}
diff --git a/src/testing/testing_api_cmd_delete_template.c b/src/testing/testing_api_cmd_delete_template.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-templates-TEMPLATE_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct DeleteTemplateState
/**
* Handle for a "DELETE template" request.
*/
- struct TALER_MERCHANT_TemplateDeleteHandle *tdh;
+ struct TALER_MERCHANT_DeletePrivateTemplateHandle *tdh;
/**
* The interpreter state.
@@ -70,20 +71,21 @@ struct DeleteTemplateState
*/
static void
delete_template_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_DeletePrivateTemplateResponse *
+ dtr)
{
struct DeleteTemplateState *dis = cls;
dis->tdh = NULL;
- if (dis->http_status != hr->http_status)
+ if (dis->http_status != dtr->hr.http_status)
{
TALER_TESTING_unexpected_status_with_body (dis->is,
- hr->http_status,
+ dtr->hr.http_status,
dis->http_status,
- hr->reply);
+ dtr->hr.reply);
return;
}
- switch (hr->http_status)
+ switch (dtr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -96,7 +98,7 @@ delete_template_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for DELETE template.\n",
- hr->http_status);
+ dtr->hr.http_status);
}
TALER_TESTING_interpreter_next (dis->is);
}
@@ -118,13 +120,18 @@ delete_template_run (void *cls,
struct DeleteTemplateState *dis = cls;
dis->is = is;
- dis->tdh = TALER_MERCHANT_template_delete (
+ dis->tdh = TALER_MERCHANT_delete_private_template_create (
TALER_TESTING_interpreter_get_context (is),
dis->merchant_url,
- dis->template_id,
- &delete_template_cb,
- dis);
- GNUNET_assert (NULL != dis->tdh);
+ dis->template_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_template_start (dis->tdh,
+ &delete_template_cb,
+ dis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -145,7 +152,7 @@ delete_template_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /templates/$ID operation did not complete\n");
- TALER_MERCHANT_template_delete_cancel (dis->tdh);
+ TALER_MERCHANT_delete_private_template_cancel (dis->tdh);
}
GNUNET_free (dis);
}
diff --git a/src/testing/testing_api_cmd_delete_transfer.c b/src/testing/testing_api_cmd_delete_transfer.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-transfers-TID-new.h>
/**
@@ -37,7 +38,7 @@ struct DeleteTransferState
/**
* Handle for a "DELETE transfer" request.
*/
- struct TALER_MERCHANT_TransferDeleteHandle *tdh;
+ struct TALER_MERCHANT_DeletePrivateTransferHandle *tdh;
/**
* The interpreter state.
@@ -70,22 +71,23 @@ struct DeleteTransferState
*/
static void
delete_transfer_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_DeletePrivateTransferResponse *
+ dtr)
{
struct DeleteTransferState *dts = cls;
dts->tdh = NULL;
- if (dts->http_status != hr->http_status)
+ if (dts->http_status != dtr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ dtr->hr.http_status,
+ (int) dtr->hr.ec,
TALER_TESTING_interpreter_get_current_label (dts->is));
TALER_TESTING_interpreter_fail (dts->is);
return;
}
- switch (hr->http_status)
+ switch (dtr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -99,7 +101,7 @@ delete_transfer_cb (void *cls,
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for DELETE transfer.\n",
- hr->http_status);
+ dtr->hr.http_status);
}
TALER_TESTING_interpreter_next (dts->is);
}
@@ -145,13 +147,18 @@ delete_transfer_run (void *cls,
TALER_TESTING_interpreter_fail (dts->is);
return;
}
- dts->tdh = TALER_MERCHANT_transfer_delete (
+ dts->tdh = TALER_MERCHANT_delete_private_transfer_create (
TALER_TESTING_interpreter_get_context (is),
dts->merchant_url,
- *tid,
- &delete_transfer_cb,
- dts);
- GNUNET_assert (NULL != dts->tdh);
+ *tid);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_transfer_start (dts->tdh,
+ &delete_transfer_cb,
+ dts);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -172,7 +179,7 @@ delete_transfer_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /transfers/$TRANSFER_ID operation did not complete\n");
- TALER_MERCHANT_transfer_delete_cancel (dts->tdh);
+ TALER_MERCHANT_delete_private_transfer_cancel (dts->tdh);
}
GNUNET_free (dts);
}
diff --git a/src/testing/testing_api_cmd_delete_unit.c b/src/testing/testing_api_cmd_delete_unit.c
@@ -25,6 +25,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-units-UNIT-new.h>
/**
@@ -35,7 +36,7 @@ struct DeleteUnitState
/**
* In-flight request handle.
*/
- struct TALER_MERCHANT_UnitDeleteHandle *udh;
+ struct TALER_MERCHANT_DeletePrivateUnitHandle *udh;
/**
* Interpreter context.
@@ -64,17 +65,17 @@ struct DeleteUnitState
*/
static void
delete_unit_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_DeletePrivateUnitResponse *dur)
{
struct DeleteUnitState *dus = cls;
dus->udh = NULL;
- if (dus->http_status != hr->http_status)
+ if (dus->http_status != dur->hr.http_status)
{
TALER_TESTING_unexpected_status_with_body (dus->is,
- hr->http_status,
+ dur->hr.http_status,
dus->http_status,
- hr->reply);
+ dur->hr.reply);
return;
}
TALER_TESTING_interpreter_next (dus->is);
@@ -92,16 +93,17 @@ delete_unit_run (void *cls,
struct DeleteUnitState *dus = cls;
dus->is = is;
- dus->udh = TALER_MERCHANT_unit_delete (
+ dus->udh = TALER_MERCHANT_delete_private_unit_create (
TALER_TESTING_interpreter_get_context (is),
dus->merchant_url,
- dus->unit_id,
- &delete_unit_cb,
- dus);
- if (NULL == dus->udh)
+ dus->unit_id);
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_unit_start (dus->udh,
+ &delete_unit_cb,
+ dus);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
}
@@ -139,7 +141,7 @@ delete_unit_cleanup (void *cls,
if (NULL != dus->udh)
{
- TALER_MERCHANT_unit_delete_cancel (dus->udh);
+ TALER_MERCHANT_delete_private_unit_cancel (dus->udh);
dus->udh = NULL;
}
GNUNET_free (dus);
diff --git a/src/testing/testing_api_cmd_delete_webhook.c b/src/testing/testing_api_cmd_delete_webhook.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-webhooks-WEBHOOK_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct DeleteWebhookState
/**
* Handle for a "DELETE webhook" request.
*/
- struct TALER_MERCHANT_WebhookDeleteHandle *wdh;
+ struct TALER_MERCHANT_DeletePrivateWebhookHandle *wdh;
/**
* The interpreter state.
@@ -70,22 +71,23 @@ struct DeleteWebhookState
*/
static void
delete_webhook_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_DeletePrivateWebhookResponse *dwr
+ )
{
struct DeleteWebhookState *dis = cls;
dis->wdh = NULL;
- if (dis->http_status != hr->http_status)
+ if (dis->http_status != dwr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ dwr->hr.http_status,
+ (int) dwr->hr.ec,
TALER_TESTING_interpreter_get_current_label (dis->is));
TALER_TESTING_interpreter_fail (dis->is);
return;
}
- switch (hr->http_status)
+ switch (dwr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -98,7 +100,7 @@ delete_webhook_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for DELETE webhook.\n",
- hr->http_status);
+ dwr->hr.http_status);
}
TALER_TESTING_interpreter_next (dis->is);
}
@@ -120,13 +122,18 @@ delete_webhook_run (void *cls,
struct DeleteWebhookState *dis = cls;
dis->is = is;
- dis->wdh = TALER_MERCHANT_webhook_delete (
+ dis->wdh = TALER_MERCHANT_delete_private_webhook_create (
TALER_TESTING_interpreter_get_context (is),
dis->merchant_url,
- dis->webhook_id,
- &delete_webhook_cb,
- dis);
- GNUNET_assert (NULL != dis->wdh);
+ dis->webhook_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_webhook_start (dis->wdh,
+ &delete_webhook_cb,
+ dis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -147,7 +154,7 @@ delete_webhook_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"DELETE /webhooks/$ID operation did not complete\n");
- TALER_MERCHANT_webhook_delete_cancel (dis->wdh);
+ TALER_MERCHANT_delete_private_webhook_cancel (dis->wdh);
}
GNUNET_free (dis);
}
diff --git a/src/testing/testing_api_cmd_forget_order.c b/src/testing/testing_api_cmd_forget_order.c
@@ -27,6 +27,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/patch-private-orders-ORDER_ID-forget-new.h>
/**
@@ -52,7 +53,7 @@ struct OrderForgetState
/**
* PATCH /orders/$ORDER_ID/forget operation handle.
*/
- struct TALER_MERCHANT_OrderForgetHandle *ofh;
+ struct TALER_MERCHANT_PatchPrivateOrdersForgetHandle *ofh;
/**
* Reference to a order operation.
@@ -95,7 +96,7 @@ order_forget_cleanup (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command '%s' did not complete\n",
cmd->label);
- TALER_MERCHANT_order_forget_cancel (ofs->ofh);
+ TALER_MERCHANT_patch_private_orders_forget_cancel (ofs->ofh);
ofs->ofh = NULL;
}
GNUNET_array_grow (ofs->paths,
@@ -114,9 +115,11 @@ order_forget_cleanup (void *cls,
*/
static void
order_forget_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PatchPrivateOrdersForgetResponse *
+ result)
{
struct OrderForgetState *ofs = cls;
+ const struct TALER_MERCHANT_HttpResponse *hr = &result->hr;
ofs->ofh = NULL;
if (ofs->http_status != hr->http_status)
@@ -165,15 +168,22 @@ order_forget_run (void *cls,
&order_id))
TALER_TESTING_FAIL (is);
}
- ofs->ofh = TALER_MERCHANT_order_forget (
+ ofs->ofh = TALER_MERCHANT_patch_private_orders_forget_create (
TALER_TESTING_interpreter_get_context (is),
ofs->merchant_url,
order_id,
ofs->paths_length,
- ofs->paths,
- &order_forget_cb,
- ofs);
+ ofs->paths);
GNUNET_assert (NULL != ofs->ofh);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_patch_private_orders_forget_start (
+ ofs->ofh,
+ &order_forget_cb,
+ ofs);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
diff --git a/src/testing/testing_api_cmd_get_donau_instances.c b/src/testing/testing_api_cmd_get_donau_instances.c
@@ -28,6 +28,7 @@
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
#include "taler/taler_merchant_donau.h"
+#include <taler/taler-merchant/get-private-donau-new.h>
/**
* State of a "GET donau instances" CMD.
@@ -37,7 +38,7 @@ struct GetDonauInstancesState
/**
* Handle for a "GET donau instances" request.
*/
- struct TALER_MERCHANT_DonauInstanceGetHandle *dgh;
+ struct TALER_MERCHANT_GetPrivateDonauHandle *dgh;
/**
* The interpreter state.
@@ -69,7 +70,7 @@ struct GetDonauInstancesState
static void
get_donau_instances_cb (void *cls,
const struct
- TALER_MERCHANT_DonauInstanceGetResponse *response)
+ TALER_MERCHANT_GetPrivateDonauResponse *response)
{
struct GetDonauInstancesState *gis = cls;
@@ -117,13 +118,18 @@ get_donau_instances_run (void *cls,
struct GetDonauInstancesState *gis = cls;
gis->is = is;
- gis->dgh = TALER_MERCHANT_donau_instances_get (
+ gis->dgh = TALER_MERCHANT_get_private_donau_create (
TALER_TESTING_interpreter_get_context (is),
- gis->url,
- &get_donau_instances_cb,
- gis);
+ gis->url);
+ {
+ enum TALER_ErrorCode ec;
- GNUNET_assert (NULL != gis->dgh);
+ ec = TALER_MERCHANT_get_private_donau_start (
+ gis->dgh,
+ &get_donau_instances_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -144,7 +150,7 @@ get_donau_instances_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /donau instances operation did not complete\n");
- TALER_MERCHANT_donau_instances_get_cancel (gis->dgh);
+ TALER_MERCHANT_get_private_donau_cancel (gis->dgh);
}
GNUNET_free (gis);
diff --git a/src/testing/testing_api_cmd_get_instance.c b/src/testing/testing_api_cmd_get_instance.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-management-instances-INSTANCE-new.h>
/**
@@ -37,7 +38,7 @@ struct GetInstanceState
/**
* Handle for a "GET instance" request.
*/
- struct TALER_MERCHANT_InstanceGetHandle *igh;
+ struct TALER_MERCHANT_GetManagementInstanceHandle *igh;
/**
* The interpreter state.
@@ -80,7 +81,7 @@ struct GetInstanceState
*/
static void
get_instance_cb (void *cls,
- const struct TALER_MERCHANT_InstanceGetResponse *igr)
+ const struct TALER_MERCHANT_GetManagementInstanceResponse *igr)
{
struct GetInstanceState *gis = cls;
const struct TALER_TESTING_Command *instance_cmd;
@@ -104,7 +105,7 @@ get_instance_cb (void *cls,
{
case MHD_HTTP_OK:
{
- const struct TALER_MERCHANT_InstanceDetails *details =
+ const struct TALER_MERCHANT_GetManagementInstanceDetails *details =
&igr->details.ok.details;
gis->account_pub.merchant_pub
@@ -237,13 +238,19 @@ get_instance_run (void *cls,
struct GetInstanceState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_instance_get (
+ gis->igh = TALER_MERCHANT_get_management_instance_create (
TALER_TESTING_interpreter_get_context (is),
gis->merchant_url,
- gis->instance_id,
- &get_instance_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->instance_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_management_instance_start (
+ gis->igh,
+ &get_instance_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -264,7 +271,7 @@ get_instance_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /instances/$ID operation did not complete\n");
- TALER_MERCHANT_instance_get_cancel (gis->igh);
+ TALER_MERCHANT_get_management_instance_cancel (gis->igh);
}
GNUNET_free (gis);
}
diff --git a/src/testing/testing_api_cmd_get_instances.c b/src/testing/testing_api_cmd_get_instances.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-management-instances-new.h>
/**
@@ -37,7 +38,7 @@ struct GetInstancesState
/**
* Handle for a "GET instance" request.
*/
- struct TALER_MERCHANT_InstancesGetHandle *igh;
+ struct TALER_MERCHANT_GetManagementInstancesHandle *igh;
/**
* The interpreter state.
@@ -75,7 +76,8 @@ struct GetInstancesState
*/
static void
get_instances_cb (void *cls,
- const struct TALER_MERCHANT_InstancesGetResponse *igr)
+ const struct TALER_MERCHANT_GetManagementInstancesResponse *
+ igr)
{
const struct TALER_MERCHANT_HttpResponse *hr = &igr->hr;
struct GetInstancesState *gis = cls;
@@ -97,7 +99,7 @@ get_instances_cb (void *cls,
{
unsigned int iis_length
= igr->details.ok.iis_length;
- const struct TALER_MERCHANT_InstanceInformation *iis
+ const struct TALER_MERCHANT_GetManagementInstancesInstanceInfo *iis
= igr->details.ok.iis;
if (iis_length != gis->instances_length)
@@ -188,12 +190,18 @@ get_instances_run (void *cls,
struct GetInstancesState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_instances_get (
+ gis->igh = TALER_MERCHANT_get_management_instances_create (
TALER_TESTING_interpreter_get_context (is),
- gis->merchant_url,
- &get_instances_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->merchant_url);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_management_instances_start (
+ gis->igh,
+ &get_instances_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -214,7 +222,7 @@ get_instances_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /instances operation did not complete\n");
- TALER_MERCHANT_instances_get_cancel (gis->igh);
+ TALER_MERCHANT_get_management_instances_cancel (gis->igh);
}
GNUNET_array_grow (gis->instances,
gis->instances_length,
diff --git a/src/testing/testing_api_cmd_get_orders.c b/src/testing/testing_api_cmd_get_orders.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-orders-new.h>
/**
@@ -37,7 +38,7 @@ struct GetOrdersState
/**
* Handle for a "GET orders" request.
*/
- struct TALER_MERCHANT_OrdersGetHandle *ogh;
+ struct TALER_MERCHANT_GetPrivateOrdersHandle *ogh;
/**
* The interpreter state.
@@ -75,7 +76,7 @@ struct GetOrdersState
*/
static void
get_orders_cb (void *cls,
- const struct TALER_MERCHANT_OrdersGetResponse *ogr)
+ const struct TALER_MERCHANT_GetPrivateOrdersResponse *ogr)
{
struct GetOrdersState *gos = cls;
@@ -102,7 +103,7 @@ get_orders_cb (void *cls,
}
for (unsigned int i = 0; i < ogr->details.ok.orders_length; ++i)
{
- const struct TALER_MERCHANT_OrderEntry *order =
+ const struct TALER_MERCHANT_GetPrivateOrdersOrderEntry *order =
&ogr->details.ok.orders[i];
const struct TALER_TESTING_Command *order_cmd;
@@ -203,12 +204,18 @@ get_orders_run (void *cls,
struct GetOrdersState *gos = cls;
gos->is = is;
- gos->ogh = TALER_MERCHANT_orders_get (TALER_TESTING_interpreter_get_context (
- is),
- gos->merchant_url,
- &get_orders_cb,
- gos);
+ gos->ogh = TALER_MERCHANT_get_private_orders_create (
+ TALER_TESTING_interpreter_get_context (is),
+ gos->merchant_url);
GNUNET_assert (NULL != gos->ogh);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_orders_start (gos->ogh,
+ &get_orders_cb,
+ gos);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -229,7 +236,7 @@ get_orders_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /orders operation did not complete\n");
- TALER_MERCHANT_orders_get_cancel (gos->ogh);
+ TALER_MERCHANT_get_private_orders_cancel (gos->ogh);
}
GNUNET_array_grow (gos->orders,
gos->orders_length,
@@ -309,7 +316,7 @@ struct MerchantPollOrdersStartState
/**
* The handle to the current GET /private/orders request.
*/
- struct TALER_MERCHANT_OrdersGetHandle *ogh;
+ struct TALER_MERCHANT_GetPrivateOrdersHandle *ogh;
/**
* The interpreter state.
@@ -400,7 +407,7 @@ conclude_task (void *cls)
static void
merchant_poll_orders_cb (
void *cls,
- const struct TALER_MERCHANT_OrdersGetResponse *ogr)
+ const struct TALER_MERCHANT_GetPrivateOrdersResponse *ogr)
{
struct MerchantPollOrdersStartState *pos = cls;
@@ -454,19 +461,32 @@ merchant_poll_orders_start_run (void *cls,
GNUNET_TIME_relative_add (pos->timeout,
GNUNET_TIME_UNIT_SECONDS));
pos->is = is;
- pos->ogh = TALER_MERCHANT_orders_get2 (TALER_TESTING_interpreter_get_context (
- is),
- pos->merchant_url,
- TALER_EXCHANGE_YNA_ALL,
- TALER_EXCHANGE_YNA_ALL,
- TALER_EXCHANGE_YNA_ALL,
- GNUNET_TIME_UNIT_ZERO_TS,
- 1,
- 2,
- pos->timeout,
- &merchant_poll_orders_cb,
- pos);
+ pos->ogh = TALER_MERCHANT_get_private_orders_create (
+ TALER_TESTING_interpreter_get_context (is),
+ pos->merchant_url);
GNUNET_assert (NULL != pos->ogh);
+ TALER_MERCHANT_get_private_orders_set_options (
+ pos->ogh,
+ TALER_MERCHANT_get_private_orders_option_paid (
+ TALER_EXCHANGE_YNA_ALL),
+ TALER_MERCHANT_get_private_orders_option_refunded (
+ TALER_EXCHANGE_YNA_ALL),
+ TALER_MERCHANT_get_private_orders_option_wired (
+ TALER_EXCHANGE_YNA_ALL),
+ TALER_MERCHANT_get_private_orders_option_date (
+ GNUNET_TIME_UNIT_ZERO_TS),
+ TALER_MERCHANT_get_private_orders_option_offset (1),
+ TALER_MERCHANT_get_private_orders_option_limit (2),
+ TALER_MERCHANT_get_private_orders_option_timeout (
+ pos->timeout));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_orders_start (pos->ogh,
+ &merchant_poll_orders_cb,
+ pos);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
/* We CONTINUE to run the interpreter while the long-polled command
completes asynchronously! */
TALER_TESTING_interpreter_next (pos->is);
@@ -492,7 +512,7 @@ merchant_poll_orders_start_cleanup (void *cls,
"Command `%s' was not terminated\n",
TALER_TESTING_interpreter_get_current_label (
pos->is));
- TALER_MERCHANT_orders_get_cancel (pos->ogh);
+ TALER_MERCHANT_get_private_orders_cancel (pos->ogh);
}
GNUNET_free (pos);
}
diff --git a/src/testing/testing_api_cmd_get_otp_device.c b/src/testing/testing_api_cmd_get_otp_device.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-otp-devices-DEVICE_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct GetOtpDeviceState
/**
* Handle for a "GET /otp-device/$ID" request.
*/
- struct TALER_MERCHANT_OtpDeviceGetHandle *igh;
+ struct TALER_MERCHANT_GetPrivateOtpDeviceHandle *igh;
/**
* The interpreter state.
@@ -75,7 +76,7 @@ struct GetOtpDeviceState
*/
static void
get_otp_device_cb (void *cls,
- const struct TALER_MERCHANT_OtpDeviceGetResponse *tgr)
+ const struct TALER_MERCHANT_GetPrivateOtpDeviceResponse *tgr)
{
struct GetOtpDeviceState *gis = cls;
const struct TALER_TESTING_Command *otp_device_cmd;
@@ -143,13 +144,19 @@ get_otp_device_run (void *cls,
struct GetOtpDeviceState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_otp_device_get (
+ gis->igh = TALER_MERCHANT_get_private_otp_device_create (
TALER_TESTING_interpreter_get_context (is),
gis->merchant_url,
- gis->otp_device_id,
- &get_otp_device_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->otp_device_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_otp_device_start (
+ gis->igh,
+ &get_otp_device_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -170,7 +177,7 @@ get_otp_device_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /otp-devices/$ID operation did not complete\n");
- TALER_MERCHANT_otp_device_get_cancel (gis->igh);
+ TALER_MERCHANT_get_private_otp_device_cancel (gis->igh);
}
GNUNET_free (gis);
}
diff --git a/src/testing/testing_api_cmd_get_otp_devices.c b/src/testing/testing_api_cmd_get_otp_devices.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-otp-devices-new.h>
/**
@@ -37,7 +38,7 @@ struct GetOtpDevicesState
/**
* Handle for a "GET /otp-devices" request.
*/
- struct TALER_MERCHANT_OtpDevicesGetHandle *igh;
+ struct TALER_MERCHANT_GetPrivateOtpDevicesHandle *igh;
/**
* The interpreter state.
@@ -75,7 +76,8 @@ struct GetOtpDevicesState
*/
static void
get_otp_devices_cb (void *cls,
- const struct TALER_MERCHANT_OtpDevicesGetResponse *tgr)
+ const struct TALER_MERCHANT_GetPrivateOtpDevicesResponse *
+ tgr)
{
struct GetOtpDevicesState *gis = cls;
@@ -163,12 +165,18 @@ get_otp_devices_run (void *cls,
struct GetOtpDevicesState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_otp_devices_get (
+ gis->igh = TALER_MERCHANT_get_private_otp_devices_create (
TALER_TESTING_interpreter_get_context (is),
- gis->merchant_url,
- &get_otp_devices_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->merchant_url);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_otp_devices_start (
+ gis->igh,
+ &get_otp_devices_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -189,7 +197,7 @@ get_otp_devices_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /otp-devices operation did not complete\n");
- TALER_MERCHANT_otp_devices_get_cancel (gis->igh);
+ TALER_MERCHANT_get_private_otp_devices_cancel (gis->igh);
}
GNUNET_array_grow (gis->otp_devices,
gis->otp_devices_length,
diff --git a/src/testing/testing_api_cmd_get_product.c b/src/testing/testing_api_cmd_get_product.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-products-PRODUCT_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct GetProductState
/**
* Handle for a "GET product" request.
*/
- struct TALER_MERCHANT_ProductGetHandle *igh;
+ struct TALER_MERCHANT_GetPrivateProductHandle *igh;
/**
* The interpreter state.
@@ -80,7 +81,7 @@ struct GetProductState
*/
static void
get_product_cb (void *cls,
- const struct TALER_MERCHANT_ProductGetResponse *pgr)
+ const struct TALER_MERCHANT_GetPrivateProductResponse *pgr)
{
struct GetProductState *gis = cls;
const struct TALER_TESTING_Command *product_cmd;
@@ -391,13 +392,19 @@ get_product_run (void *cls,
struct GetProductState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_product_get (TALER_TESTING_interpreter_get_context (
- is),
- gis->merchant_url,
- gis->product_id,
- &get_product_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->igh = TALER_MERCHANT_get_private_product_create (
+ TALER_TESTING_interpreter_get_context (is),
+ gis->merchant_url,
+ gis->product_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_product_start (
+ gis->igh,
+ &get_product_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -418,7 +425,7 @@ get_product_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /products/$ID operation did not complete\n");
- TALER_MERCHANT_product_get_cancel (gis->igh);
+ TALER_MERCHANT_get_private_product_cancel (gis->igh);
}
GNUNET_free (gis);
}
diff --git a/src/testing/testing_api_cmd_get_product_image.c b/src/testing/testing_api_cmd_get_product_image.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-products-IMAGE_HASH-image-new.h>
/**
@@ -36,7 +37,7 @@ struct GetProductImageState
/**
* Handle for a "GET product image" request.
*/
- struct TALER_MERCHANT_ProductImageGetHandle *pigh;
+ struct TALER_MERCHANT_GetProductsImageHandle *pigh;
/**
* The interpreter state.
@@ -78,7 +79,7 @@ struct GetProductImageState
*/
static void
get_product_image_cb (void *cls,
- const struct TALER_MERCHANT_ProductImageGetResponse *pir)
+ const struct TALER_MERCHANT_GetProductsImageResponse *pir)
{
struct GetProductImageState *gis = cls;
@@ -156,13 +157,19 @@ get_product_image_run (void *cls,
gis->expected_image = GNUNET_strdup (product_image);
}
gis->pigh
- = TALER_MERCHANT_product_image_get (
+ = TALER_MERCHANT_get_products_image_create (
TALER_TESTING_interpreter_get_context (is),
gis->merchant_url,
- gis->image_hash,
- &get_product_image_cb,
- gis);
- GNUNET_assert (NULL != gis->pigh);
+ gis->image_hash);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_products_image_start (
+ gis->pigh,
+ &get_product_image_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -183,7 +190,7 @@ get_product_image_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /products/$HASH/image operation did not complete\n");
- TALER_MERCHANT_product_image_get_cancel (gis->pigh);
+ TALER_MERCHANT_get_products_image_cancel (gis->pigh);
}
GNUNET_free (gis->expected_image);
GNUNET_free (gis);
diff --git a/src/testing/testing_api_cmd_get_products.c b/src/testing/testing_api_cmd_get_products.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-products-new.h>
/**
@@ -37,7 +38,7 @@ struct GetProductsState
/**
* Handle for a "GET product" request.
*/
- struct TALER_MERCHANT_ProductsGetHandle *igh;
+ struct TALER_MERCHANT_GetPrivateProductsHandle *igh;
/**
* The interpreter state.
@@ -75,29 +76,28 @@ struct GetProductsState
*/
static void
get_products_cb (void *cls,
- const struct TALER_MERCHANT_GetProductsResponse *gpr)
+ const struct TALER_MERCHANT_GetPrivateProductsResponse *gpr)
{
struct GetProductsState *gis = cls;
- const struct TALER_MERCHANT_HttpResponse *hr = &gpr->hr;
gis->igh = NULL;
- if (gis->http_status != hr->http_status)
+ if (gis->http_status != gpr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ gpr->hr.http_status,
+ (int) gpr->hr.ec,
TALER_TESTING_interpreter_get_current_label (gis->is));
TALER_TESTING_interpreter_fail (gis->is);
return;
}
- switch (hr->http_status)
+ switch (gpr->hr.http_status)
{
case MHD_HTTP_OK:
{
unsigned int products_length
= gpr->details.ok.products_length;
- const struct TALER_MERCHANT_InventoryEntry *products
+ const struct TALER_MERCHANT_GetPrivateProductsInventoryEntry *products
= gpr->details.ok.products;
if (products_length != gis->products_length)
@@ -147,8 +147,8 @@ get_products_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u (%d).\n",
- hr->http_status,
- hr->ec);
+ gpr->hr.http_status,
+ gpr->hr.ec);
}
TALER_TESTING_interpreter_next (gis->is);
}
@@ -170,12 +170,17 @@ get_products_run (void *cls,
struct GetProductsState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_products_get (
+ gis->igh = TALER_MERCHANT_get_private_products_create (
TALER_TESTING_interpreter_get_context (is),
- gis->merchant_url,
- &get_products_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->merchant_url);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_products_start (gis->igh,
+ &get_products_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -196,7 +201,7 @@ get_products_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /products operation did not complete\n");
- TALER_MERCHANT_products_get_cancel (gis->igh);
+ TALER_MERCHANT_get_private_products_cancel (gis->igh);
}
GNUNET_array_grow (gis->products,
gis->products_length,
diff --git a/src/testing/testing_api_cmd_get_statisticsamount.c b/src/testing/testing_api_cmd_get_statisticsamount.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-statistics-amount-SLUG-new.h>
/**
@@ -37,7 +38,7 @@ struct GetStatisticsAmountState
/**
* Handle for a "GET statistics-amount" request.
*/
- struct TALER_MERCHANT_StatisticsAmountGetHandle *scgh;
+ struct TALER_MERCHANT_GetPrivateStatisticsAmountHandle *scgh;
/**
* The interpreter state.
@@ -81,7 +82,8 @@ struct GetStatisticsAmountState
static void
get_statisticsamount_cb (void *cls,
const struct
- TALER_MERCHANT_StatisticsAmountGetResponse *scgr)
+ TALER_MERCHANT_GetPrivateStatisticsAmountResponse *scgr
+ )
{
struct GetStatisticsAmountState *scs = cls;
const struct TALER_MERCHANT_HttpResponse *hr = &scgr->hr;
@@ -152,14 +154,23 @@ get_statisticsamount_run (void *cls,
struct GetStatisticsAmountState *scs = cls;
scs->is = is;
- scs->scgh = TALER_MERCHANT_statistic_amount_get (
+ scs->scgh = TALER_MERCHANT_get_private_statistics_amount_create (
TALER_TESTING_interpreter_get_context (is),
scs->merchant_url,
- scs->slug,
- TALER_MERCHANT_STATISTICS_ALL,
- &get_statisticsamount_cb,
- scs);
- GNUNET_assert (NULL != scs->scgh);
+ scs->slug);
+ TALER_MERCHANT_get_private_statistics_amount_set_options (
+ scs->scgh,
+ TALER_MERCHANT_get_private_statistics_amount_option_type (
+ TALER_MERCHANT_STATISTICS_ALL));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_statistics_amount_start (
+ scs->scgh,
+ &get_statisticsamount_cb,
+ scs);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -180,7 +191,7 @@ get_statisticsamount_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /statistics-amount operation did not complete\n");
- TALER_MERCHANT_statistic_amount_get_cancel (scs->scgh);
+ TALER_MERCHANT_get_private_statistics_amount_cancel (scs->scgh);
}
GNUNET_free (scs);
}
diff --git a/src/testing/testing_api_cmd_get_statisticscounter.c b/src/testing/testing_api_cmd_get_statisticscounter.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-statistics-counter-SLUG-new.h>
/**
@@ -37,7 +38,7 @@ struct GetStatisticsCounterState
/**
* Handle for a "GET statistics-counter" request.
*/
- struct TALER_MERCHANT_StatisticsCounterGetHandle *scgh;
+ struct TALER_MERCHANT_GetPrivateStatisticsCounterHandle *scgh;
/**
* The interpreter state.
@@ -81,7 +82,8 @@ struct GetStatisticsCounterState
static void
get_statisticscounter_cb (void *cls,
const struct
- TALER_MERCHANT_StatisticsCounterGetResponse *scgr)
+ TALER_MERCHANT_GetPrivateStatisticsCounterResponse *
+ scgr)
{
struct GetStatisticsCounterState *scs = cls;
const struct TALER_MERCHANT_HttpResponse *hr = &scgr->hr;
@@ -152,14 +154,23 @@ get_statisticscounter_run (void *cls,
struct GetStatisticsCounterState *scs = cls;
scs->is = is;
- scs->scgh = TALER_MERCHANT_statistic_counter_get (
+ scs->scgh = TALER_MERCHANT_get_private_statistics_counter_create (
TALER_TESTING_interpreter_get_context (is),
scs->merchant_url,
- scs->slug,
- TALER_MERCHANT_STATISTICS_ALL,
- &get_statisticscounter_cb,
- scs);
- GNUNET_assert (NULL != scs->scgh);
+ scs->slug);
+ TALER_MERCHANT_get_private_statistics_counter_set_options (
+ scs->scgh,
+ TALER_MERCHANT_get_private_statistics_counter_option_type (
+ TALER_MERCHANT_STATISTICS_ALL));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_statistics_counter_start (
+ scs->scgh,
+ &get_statisticscounter_cb,
+ scs);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -180,7 +191,7 @@ get_statisticscounter_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /statistics-counter operation did not complete\n");
- TALER_MERCHANT_statistic_counter_get_cancel (scs->scgh);
+ TALER_MERCHANT_get_private_statistics_counter_cancel (scs->scgh);
}
GNUNET_free (scs);
}
diff --git a/src/testing/testing_api_cmd_get_template.c b/src/testing/testing_api_cmd_get_template.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-templates-TEMPLATE_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct GetTemplateState
/**
* Handle for a "GET template" request.
*/
- struct TALER_MERCHANT_TemplateGetHandle *igh;
+ struct TALER_MERCHANT_GetPrivateTemplateHandle *igh;
/**
* The interpreter state.
@@ -75,7 +76,7 @@ struct GetTemplateState
*/
static void
get_template_cb (void *cls,
- const struct TALER_MERCHANT_TemplateGetResponse *tgr)
+ const struct TALER_MERCHANT_GetPrivateTemplateResponse *tgr)
{
struct GetTemplateState *gis = cls;
const struct TALER_TESTING_Command *template_cmd;
@@ -180,13 +181,19 @@ get_template_run (void *cls,
struct GetTemplateState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_template_get (
+ gis->igh = TALER_MERCHANT_get_private_template_create (
TALER_TESTING_interpreter_get_context (is),
gis->merchant_url,
- gis->template_id,
- &get_template_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->template_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_template_start (
+ gis->igh,
+ &get_template_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -207,7 +214,7 @@ get_template_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /templates/$ID operation did not complete\n");
- TALER_MERCHANT_template_get_cancel (gis->igh);
+ TALER_MERCHANT_get_private_template_cancel (gis->igh);
}
GNUNET_free (gis);
}
diff --git a/src/testing/testing_api_cmd_get_templates.c b/src/testing/testing_api_cmd_get_templates.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-templates-new.h>
/**
@@ -37,7 +38,7 @@ struct GetTemplatesState
/**
* Handle for a "GET template" request.
*/
- struct TALER_MERCHANT_TemplatesGetHandle *igh;
+ struct TALER_MERCHANT_GetPrivateTemplatesHandle *igh;
/**
* The interpreter state.
@@ -75,7 +76,7 @@ struct GetTemplatesState
*/
static void
get_templates_cb (void *cls,
- const struct TALER_MERCHANT_TemplatesGetResponse *tgr)
+ const struct TALER_MERCHANT_GetPrivateTemplatesResponse *tgr)
{
struct GetTemplatesState *gis = cls;
@@ -163,12 +164,17 @@ get_templates_run (void *cls,
struct GetTemplatesState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_templates_get (
+ gis->igh = TALER_MERCHANT_get_private_templates_create (
TALER_TESTING_interpreter_get_context (is),
- gis->merchant_url,
- &get_templates_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->merchant_url);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_templates_start (gis->igh,
+ &get_templates_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -189,7 +195,7 @@ get_templates_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /templates operation did not complete\n");
- TALER_MERCHANT_templates_get_cancel (gis->igh);
+ TALER_MERCHANT_get_private_templates_cancel (gis->igh);
}
GNUNET_array_grow (gis->templates,
gis->templates_length,
diff --git a/src/testing/testing_api_cmd_get_transfers.c b/src/testing/testing_api_cmd_get_transfers.c
@@ -27,6 +27,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-transfers-new.h>
/**
@@ -38,7 +39,7 @@ struct GetTransfersState
/**
* Handle for a "get transfer" request.
*/
- struct TALER_MERCHANT_GetTransfersHandle *gth;
+ struct TALER_MERCHANT_GetPrivateTransfersHandle *gth;
/**
* The interpreter state.
@@ -88,7 +89,7 @@ struct GetTransfersState
static void
get_transfers_cb (
void *cls,
- const struct TALER_MERCHANT_GetTransfersResponse *gtr)
+ const struct TALER_MERCHANT_GetPrivateTransfersResponse *gtr)
{
struct GetTransfersState *gts = cls;
@@ -117,7 +118,7 @@ get_transfers_cb (
}
for (unsigned int i = 0; i < gtr->details.ok.transfers_length; ++i)
{
- const struct TALER_MERCHANT_TransferData *transfer
+ const struct TALER_MERCHANT_GetPrivateTransfersTransferData *transfer
= >r->details.ok.transfers[i];
const struct TALER_TESTING_Command *transfer_cmd;
@@ -252,18 +253,29 @@ get_transfers_run (void *cls,
struct GetTransfersState *gts = cls;
gts->is = is;
- gts->gth = TALER_MERCHANT_transfers_get (
+ gts->gth = TALER_MERCHANT_get_private_transfers_create (
TALER_TESTING_interpreter_get_context (is),
- gts->merchant_url,
- gts->payto_uri,
- GNUNET_TIME_UNIT_FOREVER_TS,
- GNUNET_TIME_UNIT_ZERO_TS,
- INT64_MAX,
- 0,
- TALER_EXCHANGE_YNA_ALL,
- &get_transfers_cb,
- gts);
- GNUNET_assert (NULL != gts->gth);
+ gts->merchant_url);
+ TALER_MERCHANT_get_private_transfers_set_options (
+ gts->gth,
+ TALER_MERCHANT_get_private_transfers_option_payto_uri (gts->payto_uri),
+ TALER_MERCHANT_get_private_transfers_option_before (
+ GNUNET_TIME_UNIT_FOREVER_TS),
+ TALER_MERCHANT_get_private_transfers_option_after (GNUNET_TIME_UNIT_ZERO_TS)
+ ,
+ TALER_MERCHANT_get_private_transfers_option_limit (INT64_MAX),
+ TALER_MERCHANT_get_private_transfers_option_offset (0),
+ TALER_MERCHANT_get_private_transfers_option_expected (TALER_EXCHANGE_YNA_ALL
+ ));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_transfers_start (
+ gts->gth,
+ &get_transfers_cb,
+ gts);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -284,7 +296,7 @@ get_transfers_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /transfer operation did not complete\n");
- TALER_MERCHANT_transfers_get_cancel (gts->gth);
+ TALER_MERCHANT_get_private_transfers_cancel (gts->gth);
}
GNUNET_array_grow (gts->transfers,
gts->transfers_length,
diff --git a/src/testing/testing_api_cmd_get_unit.c b/src/testing/testing_api_cmd_get_unit.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-units-UNIT-new.h>
/**
@@ -36,7 +37,7 @@ struct GetUnitState
/**
* In-flight request handle.
*/
- struct TALER_MERCHANT_UnitGetHandle *ugh;
+ struct TALER_MERCHANT_GetPrivateUnitHandle *ugh;
/**
* Interpreter context.
@@ -237,7 +238,7 @@ unit_matches_reference (const struct TALER_MERCHANT_UnitEntry *entry,
*/
static void
get_unit_cb (void *cls,
- const struct TALER_MERCHANT_UnitGetResponse *ugr)
+ const struct TALER_MERCHANT_GetPrivateUnitResponse *ugr)
{
struct GetUnitState *gug = cls;
@@ -289,16 +290,18 @@ get_unit_run (void *cls,
struct GetUnitState *gug = cls;
gug->is = is;
- gug->ugh = TALER_MERCHANT_unit_get (
+ gug->ugh = TALER_MERCHANT_get_private_unit_create (
TALER_TESTING_interpreter_get_context (is),
gug->merchant_url,
- gug->unit_id,
- &get_unit_cb,
- gug);
- if (NULL == gug->ugh)
+ gug->unit_id);
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_unit_start (
+ gug->ugh,
+ &get_unit_cb,
+ gug);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
}
@@ -314,7 +317,7 @@ get_unit_cleanup (void *cls,
if (NULL != gug->ugh)
{
- TALER_MERCHANT_unit_get_cancel (gug->ugh);
+ TALER_MERCHANT_get_private_unit_cancel (gug->ugh);
gug->ugh = NULL;
}
GNUNET_free (gug);
diff --git a/src/testing/testing_api_cmd_get_units.c b/src/testing/testing_api_cmd_get_units.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-units-new.h>
/**
@@ -36,7 +37,7 @@ struct GetUnitsState
/**
* In-flight request handle.
*/
- struct TALER_MERCHANT_UnitsGetHandle *ugh;
+ struct TALER_MERCHANT_GetPrivateUnitsHandle *ugh;
/**
* Interpreter context.
@@ -217,7 +218,7 @@ check_unit_matches (const struct TALER_MERCHANT_UnitEntry *entry,
*/
static void
get_units_cb (void *cls,
- const struct TALER_MERCHANT_UnitsGetResponse *ugr)
+ const struct TALER_MERCHANT_GetPrivateUnitsResponse *ugr)
{
struct GetUnitsState *gus = cls;
@@ -288,15 +289,17 @@ get_units_run (void *cls,
struct GetUnitsState *gus = cls;
gus->is = is;
- gus->ugh = TALER_MERCHANT_units_get (
+ gus->ugh = TALER_MERCHANT_get_private_units_create (
TALER_TESTING_interpreter_get_context (is),
- gus->merchant_url,
- &get_units_cb,
- gus);
- if (NULL == gus->ugh)
+ gus->merchant_url);
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_units_start (
+ gus->ugh,
+ &get_units_cb,
+ gus);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
}
@@ -312,7 +315,7 @@ get_units_cleanup (void *cls,
if (NULL != gus->ugh)
{
- TALER_MERCHANT_units_get_cancel (gus->ugh);
+ TALER_MERCHANT_get_private_units_cancel (gus->ugh);
gus->ugh = NULL;
}
GNUNET_array_grow (gus->references,
diff --git a/src/testing/testing_api_cmd_get_webhook.c b/src/testing/testing_api_cmd_get_webhook.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-webhooks-WEBHOOK_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct GetWebhookState
/**
* Handle for a "GET webhook" request.
*/
- struct TALER_MERCHANT_WebhookGetHandle *igh;
+ struct TALER_MERCHANT_GetPrivateWebhookHandle *igh;
/**
* The interpreter state.
@@ -71,37 +72,27 @@ struct GetWebhookState
* Callback for a /get/webhooks/$ID operation.
*
* @param cls closure for this function
- * @param hr HTTP response details
- * @param event_type event of the webhook
- * @param url use by the customer
- * @param http_method method use by the merchant
- * @param header_template of the webhook
- * @param body_template of the webhook
+ * @param wgr response details
*/
static void
get_webhook_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const char *event_type,
- const char *url,
- const char *http_method,
- const char *header_template,
- const char *body_template)
+ const struct TALER_MERCHANT_GetPrivateWebhookResponse *wgr)
{
struct GetWebhookState *gis = cls;
const struct TALER_TESTING_Command *webhook_cmd;
gis->igh = NULL;
- if (gis->http_status != hr->http_status)
+ if (gis->http_status != wgr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ wgr->hr.http_status,
+ (int) wgr->hr.ec,
TALER_TESTING_interpreter_get_current_label (gis->is));
TALER_TESTING_interpreter_fail (gis->is);
return;
}
- switch (hr->http_status)
+ switch (wgr->hr.http_status)
{
case MHD_HTTP_OK:
{
@@ -114,7 +105,7 @@ get_webhook_cb (void *cls,
TALER_TESTING_get_trait_event_type (webhook_cmd,
&expected_event_type))
TALER_TESTING_interpreter_fail (gis->is);
- if (0 != strcmp (event_type,
+ if (0 != strcmp (wgr->details.ok.event_type,
expected_event_type))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -130,7 +121,7 @@ get_webhook_cb (void *cls,
TALER_TESTING_get_trait_url (webhook_cmd,
&expected_url))
TALER_TESTING_interpreter_fail (gis->is);
- if (0 != strcmp (url,
+ if (0 != strcmp (wgr->details.ok.url,
expected_url))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -146,7 +137,7 @@ get_webhook_cb (void *cls,
TALER_TESTING_get_trait_http_method (webhook_cmd,
&expected_http_method))
TALER_TESTING_interpreter_fail (gis->is);
- if (0 != strcmp (http_method,
+ if (0 != strcmp (wgr->details.ok.http_method,
expected_http_method))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -162,10 +153,14 @@ get_webhook_cb (void *cls,
TALER_TESTING_get_trait_header_template (webhook_cmd,
&expected_header_template))
TALER_TESTING_interpreter_fail (gis->is);
- if ( ( (NULL == header_template) && (NULL != expected_header_template)) ||
- ( (NULL != header_template) && (NULL == expected_header_template)) ||
- ( (NULL != header_template) &&
- (0 != strcmp (header_template,
+ if ( ( (NULL == wgr->details.ok.header_template) && (NULL !=
+ expected_header_template))
+ ||
+ ( (NULL != wgr->details.ok.header_template) && (NULL ==
+ expected_header_template))
+ ||
+ ( (NULL != wgr->details.ok.header_template) &&
+ (0 != strcmp (wgr->details.ok.header_template,
expected_header_template)) ) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -181,10 +176,14 @@ get_webhook_cb (void *cls,
TALER_TESTING_get_trait_body_template (webhook_cmd,
&expected_body_template))
TALER_TESTING_interpreter_fail (gis->is);
- if ( ( (NULL == body_template) && (NULL != expected_body_template)) ||
- ( (NULL != body_template) && (NULL == expected_body_template)) ||
- ( (NULL != body_template) &&
- (0 != strcmp (body_template,
+ if ( ( (NULL == wgr->details.ok.body_template) && (NULL !=
+ expected_body_template)
+ ) ||
+ ( (NULL != wgr->details.ok.body_template) && (NULL ==
+ expected_body_template)
+ ) ||
+ ( (NULL != wgr->details.ok.body_template) &&
+ (0 != strcmp (wgr->details.ok.body_template,
expected_body_template)) ) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -222,13 +221,19 @@ get_webhook_run (void *cls,
struct GetWebhookState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_webhook_get (TALER_TESTING_interpreter_get_context (
- is),
- gis->merchant_url,
- gis->webhook_id,
- &get_webhook_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->igh = TALER_MERCHANT_get_private_webhook_create (
+ TALER_TESTING_interpreter_get_context (is),
+ gis->merchant_url,
+ gis->webhook_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_webhook_start (
+ gis->igh,
+ &get_webhook_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -249,7 +254,7 @@ get_webhook_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /webhooks/$ID operation did not complete\n");
- TALER_MERCHANT_webhook_get_cancel (gis->igh);
+ TALER_MERCHANT_get_private_webhook_cancel (gis->igh);
}
GNUNET_free (gis);
}
diff --git a/src/testing/testing_api_cmd_get_webhooks.c b/src/testing/testing_api_cmd_get_webhooks.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-webhooks-new.h>
/**
@@ -37,7 +38,7 @@ struct GetWebhooksState
/**
* Handle for a "GET webhook" request.
*/
- struct TALER_MERCHANT_WebhooksGetHandle *igh;
+ struct TALER_MERCHANT_GetPrivateWebhooksHandle *igh;
/**
* The interpreter state.
@@ -75,7 +76,7 @@ struct GetWebhooksState
*/
static void
get_webhooks_cb (void *cls,
- const struct TALER_MERCHANT_WebhooksGetResponse *wgr)
+ const struct TALER_MERCHANT_GetPrivateWebhooksResponse *wgr)
{
struct GetWebhooksState *gis = cls;
@@ -162,12 +163,18 @@ get_webhooks_run (void *cls,
struct GetWebhooksState *gis = cls;
gis->is = is;
- gis->igh = TALER_MERCHANT_webhooks_get (
+ gis->igh = TALER_MERCHANT_get_private_webhooks_create (
TALER_TESTING_interpreter_get_context (is),
- gis->merchant_url,
- &get_webhooks_cb,
- gis);
- GNUNET_assert (NULL != gis->igh);
+ gis->merchant_url);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_webhooks_start (
+ gis->igh,
+ &get_webhooks_cb,
+ gis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -188,7 +195,7 @@ get_webhooks_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /webhooks operation did not complete\n");
- TALER_MERCHANT_webhooks_get_cancel (gis->igh);
+ TALER_MERCHANT_get_private_webhooks_cancel (gis->igh);
}
GNUNET_array_grow (gis->webhooks,
gis->webhooks_length,
diff --git a/src/testing/testing_api_cmd_instance_auth.c b/src/testing/testing_api_cmd_instance_auth.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-management-instances-INSTANCE-auth-new.h>
/**
@@ -37,7 +38,7 @@ struct AuthInstanceState
/**
* Handle for a "POST auth" request.
*/
- struct TALER_MERCHANT_InstanceAuthPostHandle *iaph;
+ struct TALER_MERCHANT_PostManagementInstancesAuthHandle *iaph;
/**
* The interpreter state.
@@ -75,22 +76,23 @@ struct AuthInstanceState
*/
static void
auth_instance_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct
+ TALER_MERCHANT_PostManagementInstancesAuthResponse *iar)
{
struct AuthInstanceState *ais = cls;
ais->iaph = NULL;
- if (ais->http_status != hr->http_status)
+ if (ais->http_status != iar->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ iar->hr.http_status,
+ (int) iar->hr.ec,
TALER_TESTING_interpreter_get_current_label (ais->is));
TALER_TESTING_interpreter_fail (ais->is);
return;
}
- switch (hr->http_status)
+ switch (iar->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -102,8 +104,8 @@ auth_instance_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u (%d) returned from /private/auth operation.\n",
- hr->http_status,
- hr->ec);
+ iar->hr.http_status,
+ iar->hr.ec);
}
TALER_TESTING_interpreter_next (ais->is);
}
@@ -125,14 +127,20 @@ auth_instance_run (void *cls,
struct AuthInstanceState *ais = cls;
ais->is = is;
- ais->iaph = TALER_MERCHANT_instance_auth_post (
+ ais->iaph = TALER_MERCHANT_post_management_instances_auth_create (
TALER_TESTING_interpreter_get_context (is),
ais->merchant_url,
ais->instance_id,
- ais->auth_token,
- &auth_instance_cb,
- ais);
- GNUNET_assert (NULL != ais->iaph);
+ ais->auth_token);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_management_instances_auth_start (
+ ais->iaph,
+ &auth_instance_cb,
+ ais);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -153,7 +161,7 @@ auth_instance_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /instance/$ID/auth operation did not complete\n");
- TALER_MERCHANT_instance_auth_post_cancel (ais->iaph);
+ TALER_MERCHANT_post_management_instances_auth_cancel (ais->iaph);
}
GNUNET_free (ais);
}
diff --git a/src/testing/testing_api_cmd_instance_token.c b/src/testing/testing_api_cmd_instance_token.c
@@ -26,6 +26,8 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/delete-private-tokens-SERIAL-new.h>
+#include <taler/taler-merchant/post-private-token-new.h>
/**
@@ -37,12 +39,12 @@ struct TokenInstanceState
/**
* Handle for a "POST token" request.
*/
- struct TALER_MERCHANT_InstanceTokenPostHandle *itph;
+ struct TALER_MERCHANT_PostPrivateTokenHandle *itph;
/**
* Handle for a "DELETE token" request.
*/
- struct TALER_MERCHANT_InstanceTokenDeleteHandle *itdh;
+ struct TALER_MERCHANT_DeletePrivateTokenHandle *itdh;
/**
* The interpreter state.
@@ -95,11 +97,12 @@ struct TokenInstanceState
* Callback for a POST /instances/$ID/private/token operation.
*
* @param cls closure for this function
- * @param hr response being processed
+ * @param ptr response being processed
*/
static void
-token_instance_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+token_instance_post_cb (void *cls,
+ const struct TALER_MERCHANT_PostPrivateTokenResponse *
+ ptr)
{
struct TokenInstanceState *tis = cls;
const char *scope;
@@ -108,23 +111,19 @@ token_instance_cb (void *cls,
const char *error_name;
unsigned int error_line;
-
tis->itph = NULL;
- if (tis->http_status != hr->http_status)
+ if (tis->http_status != ptr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ ptr->hr.http_status,
+ (int) ptr->hr.ec,
TALER_TESTING_interpreter_get_current_label (tis->is));
TALER_TESTING_interpreter_fail (tis->is);
return;
}
- switch (hr->http_status)
+ switch (ptr->hr.http_status)
{
- case MHD_HTTP_NO_CONTENT:
- GNUNET_assert (GNUNET_YES == tis->is_delete);
- break;
case MHD_HTTP_OK:
{
/* Get token */
@@ -140,16 +139,15 @@ token_instance_cb (void *cls,
GNUNET_JSON_spec_end ()
};
- GNUNET_assert (GNUNET_NO == tis->is_delete);
if (GNUNET_OK !=
- GNUNET_JSON_parse (hr->reply,
+ GNUNET_JSON_parse (ptr->hr.reply,
spec,
&error_name,
&error_line))
{
char *js;
- js = json_dumps (hr->reply,
+ js = json_dumps (ptr->hr.reply,
JSON_INDENT (1));
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Parser failed on %s:%u for input `%s'\n",
@@ -169,11 +167,49 @@ token_instance_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u (%d) returned from /private/token operation.\n",
- hr->http_status,
- hr->ec);
+ ptr->hr.http_status,
+ ptr->hr.ec);
}
+ TALER_TESTING_interpreter_next (tis->is);
+}
+/**
+ * Callback for a DELETE /private/tokens/$SERIAL operation.
+ *
+ * @param cls closure for this function
+ * @param dtr response being processed
+ */
+static void
+token_instance_delete_cb (
+ void *cls,
+ const struct TALER_MERCHANT_DeletePrivateTokenResponse *dtr)
+{
+ struct TokenInstanceState *tis = cls;
+
+ tis->itdh = NULL;
+ if (tis->http_status != dtr->hr.http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u (%d) to command %s\n",
+ dtr->hr.http_status,
+ (int) dtr->hr.ec,
+ TALER_TESTING_interpreter_get_current_label (tis->is));
+ TALER_TESTING_interpreter_fail (tis->is);
+ return;
+ }
+ switch (dtr->hr.http_status)
+ {
+ case MHD_HTTP_NO_CONTENT:
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unhandled HTTP status %u (%d) returned from DELETE /private/tokens operation.\n",
+ dtr->hr.http_status,
+ dtr->hr.ec);
+ }
TALER_TESTING_interpreter_next (tis->is);
}
@@ -235,23 +271,40 @@ token_instance_run (void *cls,
tis->is = is;
if (GNUNET_NO == tis->is_delete)
- tis->itph = TALER_MERCHANT_instance_token_post (
+ {
+ tis->itph = TALER_MERCHANT_post_private_token_create (
TALER_TESTING_interpreter_get_context (is),
tis->merchant_url,
tis->instance_id,
tis->scope,
tis->duration,
- tis->refreshable,
- &token_instance_cb,
- tis);
+ tis->refreshable);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_token_start (
+ tis->itph,
+ &token_instance_post_cb,
+ tis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
+ }
else
- tis->itdh = TALER_MERCHANT_instance_token_delete (
+ {
+ tis->itdh = TALER_MERCHANT_delete_private_token_create (
TALER_TESTING_interpreter_get_context (is),
tis->merchant_url,
- tis->instance_id,
- &token_instance_cb,
- tis);
- GNUNET_assert ((NULL != tis->itph) || (NULL != tis->itdh));
+ tis->instance_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_delete_private_token_start (
+ tis->itdh,
+ &token_instance_delete_cb,
+ tis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
+ }
}
@@ -271,12 +324,14 @@ token_instance_cleanup (void *cls,
if (NULL != tis->itph)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "%s /instance/$ID/token operation did not complete\n",
- (GNUNET_NO == tis->is_delete) ? "DELETE" : "POST");
- if (GNUNET_NO == tis->is_delete)
- TALER_MERCHANT_instance_token_post_cancel (tis->itph);
- else
- TALER_MERCHANT_instance_token_delete_cancel (tis->itdh);
+ "POST /instance/$ID/token operation did not complete\n");
+ TALER_MERCHANT_post_private_token_cancel (tis->itph);
+ }
+ if (NULL != tis->itdh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "DELETE /instance/$ID/token operation did not complete\n");
+ TALER_MERCHANT_delete_private_token_cancel (tis->itdh);
}
GNUNET_free (tis);
}
diff --git a/src/testing/testing_api_cmd_kyc_get.c b/src/testing/testing_api_cmd_kyc_get.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-kyc-new.h>
/**
@@ -36,7 +37,7 @@ struct KycGetState
/**
* Operation handle for a GET /private/kyc GET request.
*/
- struct TALER_MERCHANT_KycGetHandle *kgh;
+ struct TALER_MERCHANT_GetPrivateKycHandle *kgh;
/**
* Base URL of the merchant serving the request.
@@ -114,7 +115,7 @@ kyc_get_cleanup (void *cls,
if (NULL != cs->kgh)
{
TALER_LOG_WARNING ("/kyc GET operation did not complete\n");
- TALER_MERCHANT_kyc_get_cancel (cs->kgh);
+ TALER_MERCHANT_get_private_kyc_cancel (cs->kgh);
}
GNUNET_free (cs);
}
@@ -128,7 +129,7 @@ kyc_get_cleanup (void *cls,
*/
static void
kyc_get_cb (void *cls,
- const struct TALER_MERCHANT_KycResponse *kr)
+ const struct TALER_MERCHANT_GetPrivateKycResponse *kr)
{
struct KycGetState *cs = cls;
@@ -213,33 +214,40 @@ kyc_get_run (void *cls,
TALER_TESTING_FAIL (cs->is);
}
}
- if (NULL == cs->instance_id)
- cs->kgh = TALER_MERCHANT_kyc_get (
- TALER_TESTING_interpreter_get_context (is),
- cs->merchant_url,
- h_wire,
- cs->exchange_url,
- cs->lpt,
- TALER_EXCHANGE_KLPT_NONE == cs->lpt
- ? GNUNET_TIME_UNIT_ZERO
- : GNUNET_TIME_UNIT_MINUTES,
- &kyc_get_cb,
- cs);
- else
- cs->kgh = TALER_MERCHANT_management_kyc_get (
- TALER_TESTING_interpreter_get_context (is),
- cs->merchant_url,
- cs->instance_id,
- h_wire,
- cs->exchange_url,
- cs->lpt,
- TALER_EXCHANGE_KLPT_NONE == cs->lpt
- ? GNUNET_TIME_UNIT_ZERO
- : GNUNET_TIME_UNIT_MINUTES,
- &kyc_get_cb,
- cs);
-
+ cs->kgh = TALER_MERCHANT_get_private_kyc_create (
+ TALER_TESTING_interpreter_get_context (is),
+ cs->merchant_url);
GNUNET_assert (NULL != cs->kgh);
+ if (NULL != cs->instance_id)
+ TALER_MERCHANT_get_private_kyc_set_options (
+ cs->kgh,
+ TALER_MERCHANT_get_private_kyc_option_instance_id (
+ cs->instance_id));
+ if (NULL != h_wire)
+ TALER_MERCHANT_get_private_kyc_set_options (
+ cs->kgh,
+ TALER_MERCHANT_get_private_kyc_option_h_wire (h_wire));
+ if (NULL != cs->exchange_url)
+ TALER_MERCHANT_get_private_kyc_set_options (
+ cs->kgh,
+ TALER_MERCHANT_get_private_kyc_option_exchange_url (
+ cs->exchange_url));
+ if (TALER_EXCHANGE_KLPT_NONE != cs->lpt)
+ {
+ TALER_MERCHANT_get_private_kyc_set_options (
+ cs->kgh,
+ TALER_MERCHANT_get_private_kyc_option_lpt (cs->lpt),
+ TALER_MERCHANT_get_private_kyc_option_timeout (
+ GNUNET_TIME_UNIT_MINUTES));
+ }
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_kyc_start (cs->kgh,
+ &kyc_get_cb,
+ cs);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
diff --git a/src/testing/testing_api_cmd_lock_product.c b/src/testing/testing_api_cmd_lock_product.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-products-PRODUCT_ID-lock-new.h>
/**
@@ -37,7 +38,7 @@ struct LockProductState
/**
* Handle for a "GET product" request.
*/
- struct TALER_MERCHANT_ProductLockHandle *iph;
+ struct TALER_MERCHANT_PostPrivateProductsLockHandle *iph;
/**
* The interpreter state.
@@ -96,22 +97,23 @@ struct LockProductState
*/
static void
lock_product_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PostPrivateProductsLockResponse *
+ plr)
{
struct LockProductState *pis = cls;
pis->iph = NULL;
- if (pis->http_status != hr->http_status)
+ if (pis->http_status != plr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ plr->hr.http_status,
+ (int) plr->hr.ec,
TALER_TESTING_interpreter_get_current_label (pis->is));
TALER_TESTING_interpreter_fail (pis->is);
return;
}
- switch (hr->http_status)
+ switch (plr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -124,7 +126,7 @@ lock_product_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for lock product.\n",
- hr->http_status);
+ plr->hr.http_status);
}
TALER_TESTING_interpreter_next (pis->is);
}
@@ -146,33 +148,28 @@ lock_product_run (void *cls,
struct LockProductState *pis = cls;
pis->is = is;
+ pis->iph = TALER_MERCHANT_post_private_products_lock_create (
+ TALER_TESTING_interpreter_get_context (is),
+ pis->merchant_url,
+ pis->product_id,
+ pis->uuid,
+ pis->duration,
+ pis->quantity);
if (pis->use_fractional_quantity)
+ TALER_MERCHANT_post_private_products_lock_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_lock_option_quantity_frac (
+ pis->quantity_frac),
+ TALER_MERCHANT_post_private_products_lock_option_use_frac_quantity ());
{
- pis->iph = TALER_MERCHANT_product_lock2 (
- TALER_TESTING_interpreter_get_context (is),
- pis->merchant_url,
- pis->product_id,
- pis->uuid,
- pis->duration,
- pis->quantity,
- pis->quantity_frac,
- true,
- &lock_product_cb,
- pis);
- }
- else
- {
- pis->iph = TALER_MERCHANT_product_lock (
- TALER_TESTING_interpreter_get_context (is),
- pis->merchant_url,
- pis->product_id,
- pis->uuid,
- pis->duration,
- pis->quantity,
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_products_lock_start (
+ pis->iph,
&lock_product_cb,
pis);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
- GNUNET_assert (NULL != pis->iph);
}
@@ -193,7 +190,7 @@ lock_product_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /product/$ID/lock operation did not complete\n");
- TALER_MERCHANT_product_lock_cancel (pis->iph);
+ TALER_MERCHANT_post_private_products_lock_cancel (pis->iph);
}
GNUNET_free (pis->uuid);
GNUNET_free (pis);
diff --git a/src/testing/testing_api_cmd_merchant_get_order.c b/src/testing/testing_api_cmd_merchant_get_order.c
@@ -24,8 +24,8 @@
#include "taler/platform.h"
#include <taler/taler_exchange_service.h>
#include <taler/taler_testing_lib.h>
-#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-private-orders-ORDER_ID-new.h>
/**
@@ -46,7 +46,7 @@ struct MerchantGetOrderState
/**
* The handle to the current GET /private/orders/$ORDER_ID request.
*/
- struct TALER_MERCHANT_OrderMerchantGetHandle *ogh;
+ struct TALER_MERCHANT_GetPrivateOrderHandle *ogh;
/**
* The interpreter state.
@@ -157,7 +157,7 @@ apply_forget (void *cls,
static void
merchant_get_order_cb (
void *cls,
- const struct TALER_MERCHANT_OrderStatusResponse *osr)
+ const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
{
struct MerchantGetOrderState *gos = cls;
@@ -602,14 +602,27 @@ merchant_get_order_run (void *cls,
TALER_TESTING_FAIL (is);
gos->is = is;
- gos->ogh = TALER_MERCHANT_merchant_order_get (
+ gos->ogh = TALER_MERCHANT_get_private_order_create (
TALER_TESTING_interpreter_get_context (is),
gos->merchant_url,
- order_id,
- gos->session_id,
- GNUNET_TIME_UNIT_ZERO,
- &merchant_get_order_cb,
- gos);
+ order_id);
+ GNUNET_assert (NULL != gos->ogh);
+ if (NULL != gos->session_id)
+ {
+ TALER_MERCHANT_get_private_order_set_options (
+ gos->ogh,
+ TALER_MERCHANT_get_private_order_option_session_id (
+ gos->session_id));
+ }
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_order_start (
+ gos->ogh,
+ &merchant_get_order_cb,
+ gos);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -629,7 +642,7 @@ merchant_get_order_cleanup (void *cls,
if (NULL != gos->ogh)
{
TALER_LOG_WARNING ("Get order operation did not complete\n");
- TALER_MERCHANT_merchant_order_get_cancel (gos->ogh);
+ TALER_MERCHANT_get_private_order_cancel (gos->ogh);
}
GNUNET_array_grow (gos->transfers,
gos->transfers_length,
@@ -847,7 +860,7 @@ struct MerchantPollOrderStartState
/**
* The handle to the current GET /private/orders/$ORDER_ID request.
*/
- struct TALER_MERCHANT_OrderMerchantGetHandle *ogh;
+ struct TALER_MERCHANT_GetPrivateOrderHandle *ogh;
/**
* The interpreter state.
@@ -940,7 +953,7 @@ conclude_task (void *cls)
static void
merchant_poll_order_cb (
void *cls,
- const struct TALER_MERCHANT_OrderStatusResponse *osr)
+ const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
{
struct MerchantPollOrderStartState *pos = cls;
const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
@@ -987,15 +1000,27 @@ merchant_poll_order_start_run (void *cls,
= GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout),
GNUNET_TIME_UNIT_SECONDS);
pos->is = is;
- pos->ogh = TALER_MERCHANT_merchant_order_get (
+ pos->ogh = TALER_MERCHANT_get_private_order_create (
TALER_TESTING_interpreter_get_context (is),
pos->merchant_url,
- pos->order_id,
- NULL,
- pos->timeout,
- &merchant_poll_order_cb,
- pos);
+ pos->order_id);
GNUNET_assert (NULL != pos->ogh);
+ if (pos->timeout.rel_value_us > 0)
+ {
+ TALER_MERCHANT_get_private_order_set_options (
+ pos->ogh,
+ TALER_MERCHANT_get_private_order_option_timeout (
+ pos->timeout));
+ }
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_private_order_start (
+ pos->ogh,
+ &merchant_poll_order_cb,
+ pos);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
/* We CONTINUE to run the interpreter while the long-polled command
completes asynchronously! */
TALER_TESTING_interpreter_next (pos->is);
@@ -1021,7 +1046,7 @@ merchant_poll_order_start_cleanup (void *cls,
"Command `%s' was not terminated\n",
TALER_TESTING_interpreter_get_current_label (
pos->is));
- TALER_MERCHANT_merchant_order_get_cancel (pos->ogh);
+ TALER_MERCHANT_get_private_order_cancel (pos->ogh);
}
GNUNET_free (pos);
}
diff --git a/src/testing/testing_api_cmd_patch_instance.c b/src/testing/testing_api_cmd_patch_instance.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/patch-management-instances-INSTANCE-new.h>
/**
@@ -37,7 +38,7 @@ struct PatchInstanceState
/**
* Handle for a "PATCH /instance/$ID" request.
*/
- struct TALER_MERCHANT_InstancePatchHandle *iph;
+ struct TALER_MERCHANT_PatchManagementInstancesHandle *iph;
/**
* The interpreter state.
@@ -100,9 +101,11 @@ struct PatchInstanceState
*/
static void
patch_instance_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct
+ TALER_MERCHANT_PatchManagementInstancesResponse *result)
{
struct PatchInstanceState *pis = cls;
+ const struct TALER_MERCHANT_HttpResponse *hr = &result->hr;
pis->iph = NULL;
if (pis->http_status != hr->http_status)
@@ -153,7 +156,7 @@ patch_instance_run (void *cls,
struct PatchInstanceState *pis = cls;
pis->is = is;
- pis->iph = TALER_MERCHANT_instance_patch (
+ pis->iph = TALER_MERCHANT_patch_management_instances_create (
TALER_TESTING_interpreter_get_context (is),
pis->merchant_url,
pis->instance_id,
@@ -164,10 +167,17 @@ patch_instance_run (void *cls,
pis->default_wire_transfer_delay,
pis->default_pay_delay,
GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS,
- 15),
- &patch_instance_cb,
- pis);
+ 15));
GNUNET_assert (NULL != pis->iph);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_patch_management_instances_start (
+ pis->iph,
+ &patch_instance_cb,
+ pis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -223,7 +233,7 @@ patch_instance_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"PATCH /instance/$ID operation did not complete\n");
- TALER_MERCHANT_instance_patch_cancel (pis->iph);
+ TALER_MERCHANT_patch_management_instances_cancel (pis->iph);
}
json_decref (pis->jurisdiction);
json_decref (pis->address);
diff --git a/src/testing/testing_api_cmd_patch_otp_device.c b/src/testing/testing_api_cmd_patch_otp_device.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/patch-private-otp-devices-DEVICE_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct PatchOtpDeviceState
/**
* Handle for a "GET otp_device" request.
*/
- struct TALER_MERCHANT_OtpDevicePatchHandle *iph;
+ struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *iph;
/**
* The interpreter state.
@@ -90,9 +91,11 @@ struct PatchOtpDeviceState
*/
static void
patch_otp_device_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PatchPrivateOtpDeviceResponse *
+ result)
{
struct PatchOtpDeviceState *pis = cls;
+ const struct TALER_MERCHANT_HttpResponse *hr = &result->hr;
pis->iph = NULL;
if (pis->http_status != hr->http_status)
@@ -142,17 +145,24 @@ patch_otp_device_run (void *cls,
struct PatchOtpDeviceState *pis = cls;
pis->is = is;
- pis->iph = TALER_MERCHANT_otp_device_patch (
+ pis->iph = TALER_MERCHANT_patch_private_otp_device_create (
TALER_TESTING_interpreter_get_context (is),
pis->merchant_url,
pis->otp_device_id,
pis->otp_device_description,
pis->otp_key,
pis->otp_alg,
- pis->otp_ctr,
- &patch_otp_device_cb,
- pis);
+ pis->otp_ctr);
GNUNET_assert (NULL != pis->iph);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_patch_private_otp_device_start (
+ pis->iph,
+ &patch_otp_device_cb,
+ pis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -206,7 +216,7 @@ patch_otp_device_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"PATCH /otp-devices/$ID operation did not complete\n");
- TALER_MERCHANT_otp_device_patch_cancel (pis->iph);
+ TALER_MERCHANT_patch_private_otp_device_cancel (pis->iph);
}
GNUNET_free (pis->otp_key);
GNUNET_free (pis);
diff --git a/src/testing/testing_api_cmd_patch_product.c b/src/testing/testing_api_cmd_patch_product.c
@@ -26,7 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
-#include "merchant_api_common.h"
+#include <taler/taler-merchant/patch-private-products-PRODUCT_ID-new.h>
/**
@@ -38,7 +38,7 @@ struct PatchProductState
/**
* Handle for a "GET product" request.
*/
- struct TALER_MERCHANT_ProductPatchHandle *iph;
+ struct TALER_MERCHANT_PatchPrivateProductHandle *iph;
/**
* The interpreter state.
@@ -245,9 +245,11 @@ default_precision_from_unit (const char *unit)
*/
static void
patch_product_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PatchPrivateProductResponse *
+ result)
{
struct PatchProductState *pis = cls;
+ const struct TALER_MERCHANT_HttpResponse *hr = &result->hr;
pis->iph = NULL;
if (pis->http_status != hr->http_status)
@@ -297,51 +299,58 @@ patch_product_run (void *cls,
struct PatchProductState *pis = cls;
pis->is = is;
+ pis->iph = TALER_MERCHANT_patch_private_product_create (
+ TALER_TESTING_interpreter_get_context (is),
+ pis->merchant_url,
+ pis->product_id,
+ pis->description,
+ pis->description_i18n,
+ pis->unit,
+ &pis->price,
+ pis->image,
+ pis->taxes,
+ pis->total_stock,
+ pis->total_lost,
+ pis->address,
+ pis->next_restock);
+ GNUNET_assert (NULL != pis->iph);
if (pis->use_fractional)
{
- pis->iph = TALER_MERCHANT_product_patch2 (
- TALER_TESTING_interpreter_get_context (is),
- pis->merchant_url,
- pis->product_id,
- pis->description,
- pis->description_i18n,
- pis->unit,
- pis->unit_prices,
- pis->unit_prices_len,
- pis->image,
- pis->taxes,
- pis->total_stock,
- pis->total_stock_frac,
- pis->unit_allow_fraction,
- pis->use_fractional
- ? &pis->unit_precision_level
- : NULL,
- pis->total_lost,
- pis->address,
- pis->next_restock,
- &patch_product_cb,
- pis);
+ if (pis->use_unit_price_array)
+ {
+ TALER_MERCHANT_patch_private_product_set_options (
+ pis->iph,
+ TALER_MERCHANT_patch_private_product_option_unit_prices (
+ pis->unit_prices,
+ pis->unit_prices_len),
+ TALER_MERCHANT_patch_private_product_option_total_stock_frac (
+ pis->total_stock_frac),
+ TALER_MERCHANT_patch_private_product_option_unit_allow_fraction (
+ pis->unit_allow_fraction),
+ TALER_MERCHANT_patch_private_product_option_unit_precision_level (
+ pis->unit_precision_level));
+ }
+ else
+ {
+ TALER_MERCHANT_patch_private_product_set_options (
+ pis->iph,
+ TALER_MERCHANT_patch_private_product_option_total_stock_frac (
+ pis->total_stock_frac),
+ TALER_MERCHANT_patch_private_product_option_unit_allow_fraction (
+ pis->unit_allow_fraction),
+ TALER_MERCHANT_patch_private_product_option_unit_precision_level (
+ pis->unit_precision_level));
+ }
}
- else
{
- pis->iph = TALER_MERCHANT_product_patch (
- TALER_TESTING_interpreter_get_context (is),
- pis->merchant_url,
- pis->product_id,
- pis->description,
- pis->description_i18n,
- pis->unit,
- &pis->price,
- pis->image,
- pis->taxes,
- pis->total_stock,
- pis->total_lost,
- pis->address,
- pis->next_restock,
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_patch_private_product_start (
+ pis->iph,
&patch_product_cb,
pis);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
- GNUNET_assert (NULL != pis->iph);
}
@@ -407,7 +416,7 @@ patch_product_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"PATCH /products/$ID operation did not complete\n");
- TALER_MERCHANT_product_patch_cancel (pis->iph);
+ TALER_MERCHANT_patch_private_product_cancel (pis->iph);
}
if (pis->owns_unit_prices)
GNUNET_free (pis->unit_prices);
diff --git a/src/testing/testing_api_cmd_patch_template.c b/src/testing/testing_api_cmd_patch_template.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/patch-private-templates-TEMPLATE_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct PatchTemplateState
/**
* Handle for a "GET template" request.
*/
- struct TALER_MERCHANT_TemplatePatchHandle *iph;
+ struct TALER_MERCHANT_PatchPrivateTemplateHandle *iph;
/**
* The interpreter state.
@@ -85,9 +86,11 @@ struct PatchTemplateState
*/
static void
patch_template_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PatchPrivateTemplateResponse *
+ result)
{
struct PatchTemplateState *pis = cls;
+ const struct TALER_MERCHANT_HttpResponse *hr = &result->hr;
pis->iph = NULL;
if (pis->http_status != hr->http_status)
@@ -137,16 +140,23 @@ patch_template_run (void *cls,
struct PatchTemplateState *pis = cls;
pis->is = is;
- pis->iph = TALER_MERCHANT_template_patch (
+ pis->iph = TALER_MERCHANT_patch_private_template_create (
TALER_TESTING_interpreter_get_context (is),
pis->merchant_url,
pis->template_id,
pis->template_description,
pis->otp_id,
- pis->template_contract,
- &patch_template_cb,
- pis);
+ pis->template_contract);
GNUNET_assert (NULL != pis->iph);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_patch_private_template_start (
+ pis->iph,
+ &patch_template_cb,
+ pis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -199,7 +209,7 @@ patch_template_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"PATCH /templates/$ID operation did not complete\n");
- TALER_MERCHANT_template_patch_cancel (pis->iph);
+ TALER_MERCHANT_patch_private_template_cancel (pis->iph);
}
GNUNET_free (pis->otp_id);
json_decref (pis->template_contract);
diff --git a/src/testing/testing_api_cmd_patch_unit.c b/src/testing/testing_api_cmd_patch_unit.c
@@ -25,6 +25,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/patch-private-units-UNIT-new.h>
/**
@@ -35,7 +36,7 @@ struct PatchUnitState
/**
* In-flight request handle.
*/
- struct TALER_MERCHANT_UnitPatchHandle *uph;
+ struct TALER_MERCHANT_PatchPrivateUnitHandle *uph;
/**
* Interpreter context.
@@ -114,9 +115,10 @@ struct PatchUnitState
*/
static void
patch_unit_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PatchPrivateUnitResponse *result)
{
struct PatchUnitState *pus = cls;
+ const struct TALER_MERCHANT_HttpResponse *hr = &result->hr;
pus->uph = NULL;
if (pus->http_status != hr->http_status)
@@ -140,34 +142,64 @@ patch_unit_run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct PatchUnitState *pus = cls;
- const bool *allow_ptr = pus->have_unit_allow_fraction
- ? &pus->unit_allow_fraction
- : NULL;
- const uint32_t *precision_ptr = pus->have_unit_precision_level
- ? &pus->unit_precision_level
- : NULL;
- const bool *active_ptr = pus->have_unit_active
- ? &pus->unit_active
- : NULL;
pus->is = is;
- pus->uph = TALER_MERCHANT_unit_patch (
+ pus->uph = TALER_MERCHANT_patch_private_unit_create (
TALER_TESTING_interpreter_get_context (is),
pus->merchant_url,
- pus->unit_id,
- pus->unit_name_long,
- pus->unit_name_short,
- pus->unit_name_long_i18n,
- pus->unit_name_short_i18n,
- allow_ptr,
- precision_ptr,
- active_ptr,
- &patch_unit_cb,
- pus);
+ pus->unit_id);
if (NULL == pus->uph)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ /* Set all optional fields via set_options.
+ We build the options dynamically based on which fields are present. */
+ {
+ struct TALER_MERCHANT_PatchPrivateUnitOptionValue
+ opts[8];
+ unsigned int n = 0;
+
+ if (NULL != pus->unit_name_long)
+ opts[n++] = TALER_MERCHANT_patch_private_unit_option_unit_name_long (
+ pus->unit_name_long);
+ if (NULL != pus->unit_name_short)
+ opts[n++] = TALER_MERCHANT_patch_private_unit_option_unit_name_short (
+ pus->unit_name_short);
+ if (NULL != pus->unit_name_long_i18n)
+ opts[n++] =
+ TALER_MERCHANT_patch_private_unit_option_unit_name_long_i18n (
+ pus->unit_name_long_i18n);
+ if (NULL != pus->unit_name_short_i18n)
+ opts[n++] =
+ TALER_MERCHANT_patch_private_unit_option_unit_name_short_i18n (
+ pus->unit_name_short_i18n);
+ if (pus->have_unit_allow_fraction)
+ opts[n++] =
+ TALER_MERCHANT_patch_private_unit_option_unit_allow_fraction (
+ pus->unit_allow_fraction);
+ if (pus->have_unit_precision_level)
+ opts[n++] =
+ TALER_MERCHANT_patch_private_unit_option_unit_precision_level (
+ pus->unit_precision_level);
+ if (pus->have_unit_active)
+ opts[n++] = TALER_MERCHANT_patch_private_unit_option_unit_active (
+ pus->unit_active);
+ opts[n++] = TALER_MERCHANT_patch_private_unit_option_end_ ();
+ TALER_MERCHANT_patch_private_unit_set_options_ (
+ pus->uph,
+ n,
+ opts);
+ }
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_patch_private_unit_start (
+ pus->uph,
+ &patch_unit_cb,
+ pus);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
}
@@ -221,7 +253,7 @@ patch_unit_cleanup (void *cls,
if (NULL != pus->uph)
{
- TALER_MERCHANT_unit_patch_cancel (pus->uph);
+ TALER_MERCHANT_patch_private_unit_cancel (pus->uph);
pus->uph = NULL;
}
if (NULL != pus->unit_name_long_i18n)
diff --git a/src/testing/testing_api_cmd_patch_webhook.c b/src/testing/testing_api_cmd_patch_webhook.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/patch-private-webhooks-WEBHOOK_ID-new.h>
/**
@@ -37,7 +38,7 @@ struct PatchWebhookState
/**
* Handle for a "GET webhook" request.
*/
- struct TALER_MERCHANT_WebhookPatchHandle *iph;
+ struct TALER_MERCHANT_PatchPrivateWebhookHandle *iph;
/**
* The interpreter state.
@@ -95,9 +96,11 @@ struct PatchWebhookState
*/
static void
patch_webhook_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PatchPrivateWebhookResponse *
+ result)
{
struct PatchWebhookState *pis = cls;
+ const struct TALER_MERCHANT_HttpResponse *hr = &result->hr;
pis->iph = NULL;
if (pis->http_status != hr->http_status)
@@ -147,7 +150,7 @@ patch_webhook_run (void *cls,
struct PatchWebhookState *pis = cls;
pis->is = is;
- pis->iph = TALER_MERCHANT_webhook_patch (
+ pis->iph = TALER_MERCHANT_patch_private_webhook_create (
TALER_TESTING_interpreter_get_context (is),
pis->merchant_url,
pis->webhook_id,
@@ -155,10 +158,17 @@ patch_webhook_run (void *cls,
pis->url,
pis->http_method,
pis->header_template,
- pis->body_template,
- &patch_webhook_cb,
- pis);
+ pis->body_template);
GNUNET_assert (NULL != pis->iph);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_patch_private_webhook_start (
+ pis->iph,
+ &patch_webhook_cb,
+ pis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -213,7 +223,7 @@ patch_webhook_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"PATCH /webhooks/$ID operation did not complete\n");
- TALER_MERCHANT_webhook_patch_cancel (pis->iph);
+ TALER_MERCHANT_patch_private_webhook_cancel (pis->iph);
}
GNUNET_free (pis);
}
diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c
@@ -33,8 +33,8 @@
#include <taler/taler_testing_lib.h>
#include <taler/taler_signatures.h>
#include "taler/taler_merchant_service.h"
-#include "taler/taler_merchant_pay_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-orders-ORDER_ID-pay-new.h>
#ifdef HAVE_DONAU_DONAU_SERVICE_H
#include <donau/donau_service.h>
@@ -469,7 +469,7 @@ struct PayState
/**
* Handle to the pay operation.
*/
- struct TALER_MERCHANT_OrderPayHandle *oph;
+ struct TALER_MERCHANT_PostOrdersPayHandle *oph;
/**
* Signature from the merchant, set on success.
@@ -614,7 +614,7 @@ find_token_public_key (const json_t *token_families,
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-build_coins (struct TALER_MERCHANT_PayCoin **pc,
+build_coins (struct TALER_MERCHANT_PostOrdersPayCoin **pc,
unsigned int *npc,
char *coins,
struct TALER_TESTING_Interpreter *is,
@@ -638,7 +638,7 @@ build_coins (struct TALER_MERCHANT_PayCoin **pc,
const struct TALER_TESTING_Command *coin_cmd;
char *ctok;
unsigned int ci;
- struct TALER_MERCHANT_PayCoin *icoin;
+ struct TALER_MERCHANT_PostOrdersPayCoin *icoin;
const struct TALER_EXCHANGE_DenomPublicKey *dpk;
const char *exchange_url;
@@ -706,7 +706,8 @@ build_coins (struct TALER_MERCHANT_PayCoin **pc,
icoin->denom_sig = *denom_sig;
icoin->denom_value = *denom_value;
icoin->amount_with_fee = *denom_value;
- icoin->h_age_commitment = h_age_commitment;
+ if (NULL != h_age_commitment)
+ icoin->h_age_commitment = *h_age_commitment;
}
GNUNET_assert (NULL != (dpk =
TALER_TESTING_find_pk (keys,
@@ -720,7 +721,7 @@ build_coins (struct TALER_MERCHANT_PayCoin **pc,
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_exchange_url (coin_cmd,
&exchange_url));
- icoin->exchange_url = exchange_url;
+ icoin->exchange_url = (char *) exchange_url;
}
return GNUNET_OK;
@@ -739,7 +740,7 @@ build_coins (struct TALER_MERCHANT_PayCoin **pc,
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-build_tokens (struct TALER_MERCHANT_UseToken **tokens,
+build_tokens (struct TALER_MERCHANT_PostOrdersPayUseToken **tokens,
unsigned int *tokens_num,
char *pay_references,
struct TALER_TESTING_Interpreter *is)
@@ -753,7 +754,7 @@ build_tokens (struct TALER_MERCHANT_UseToken **tokens,
const struct TALER_TESTING_Command *pay_cmd;
char *slash;
unsigned int index;
- struct TALER_MERCHANT_UseToken *token;
+ struct TALER_MERCHANT_PostOrdersPayUseToken *token;
/* Reference syntax is "LABEL[/NUMBER]" */
slash = strchr (ref, '/');
@@ -825,7 +826,7 @@ build_tokens (struct TALER_MERCHANT_UseToken **tokens,
*/
static void
pay_cb (void *cls,
- const struct TALER_MERCHANT_PayResponse *pr)
+ const struct TALER_MERCHANT_PostOrdersPayResponse *pr)
{
struct PayState *ps = cls;
@@ -945,11 +946,11 @@ pay_run (void *cls,
struct TALER_Amount max_fee;
const char *error_name = NULL;
unsigned int error_line = 0;
- struct TALER_MERCHANT_PayCoin *pay_coins;
+ struct TALER_MERCHANT_PostOrdersPayCoin *pay_coins;
unsigned int npay_coins;
- struct TALER_MERCHANT_UseToken *use_tokens = NULL;
+ struct TALER_MERCHANT_PostOrdersPayUseToken *use_tokens = NULL;
unsigned int len_use_tokens = 0;
- struct TALER_MERCHANT_OutputToken *output_tokens = NULL;
+ struct TALER_MERCHANT_PostOrdersPayOutputToken *output_tokens = NULL;
unsigned int len_output_tokens = 0;
const struct TALER_MerchantSignatureP *merchant_sig;
const enum TALER_MerchantConfirmationAlgorithm *alg_ptr;
@@ -1401,61 +1402,68 @@ pay_run (void *cls,
{
struct GNUNET_CURL_Context *ctx =
TALER_TESTING_interpreter_get_context (is);
- struct TALER_MERCHANT_OrderPayOption opts[32];
- size_t oi = 0;
- ps->oph = TALER_MERCHANT_order_pay_create (ctx,
- &pay_cb,
- ps);
+ ps->oph = TALER_MERCHANT_post_orders_pay_create (ctx,
+ ps->merchant_url,
+ order_id,
+ h_proposal,
+ ps->choice_index,
+ &ps->total_amount,
+ &max_fee,
+ &merchant_pub,
+ merchant_sig,
+ timestamp,
+ refund_deadline,
+ pay_deadline,
+ &h_wire,
+ npay_coins,
+ pay_coins);
if (NULL == ps->oph)
TALER_TESTING_FAIL (is);
-
-#define ADD(_opt) opts[oi++] = (_opt)
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL (ps->merchant_url));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_ORDER_ID (order_id));
if (NULL != ps->session_id)
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID (ps->session_id));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_H_CONTRACT (h_proposal));
- if (ps->choice_index >= 0)
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_CHOICE_INDEX (ps->choice_index));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT (&ps->total_amount));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE (&max_fee));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_PUB (&merchant_pub));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_TIMESTAMP (timestamp));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_REFUND_DEADLINE (refund_deadline));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_PAY_DEADLINE (pay_deadline));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_H_WIRE (&h_wire));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_COINS (npay_coins,
- pay_coins));
+ GNUNET_assert (
+ GNUNET_OK ==
+ TALER_MERCHANT_post_orders_pay_set_options (
+ ps->oph,
+ TALER_MERCHANT_post_orders_pay_option_session_id (
+ ps->session_id)));
+
if (len_use_tokens > 0)
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_INPUT_TOKENS (len_use_tokens,
- use_tokens));
+ GNUNET_assert (
+ GNUNET_OK ==
+ TALER_MERCHANT_post_orders_pay_set_options (
+ ps->oph,
+ TALER_MERCHANT_post_orders_pay_option_use_tokens (
+ len_use_tokens,
+ use_tokens)));
if (len_output_tokens > 0)
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_OUTPUT_TOKENS (len_output_tokens,
- output_tokens));
-
+ GNUNET_assert (
+ GNUNET_OK ==
+ TALER_MERCHANT_post_orders_pay_set_options (
+ ps->oph,
+ TALER_MERCHANT_post_orders_pay_option_output_tokens (
+ len_output_tokens,
+ output_tokens)));
+
+#if 1
#ifdef HAVE_DONAU_DONAU_SERVICE_H
if (ps->donau_data.charity_reference)
{
ADD (
- TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_URL (ps->donau_data.donau_url));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_YEAR (ps->donau_data.year));
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_BUDIS (
+ TALER_MERCHANT_POST_ORDERS_PAY_OPTION_DONAU_URL (ps->donau_data.
+ donau_url));
+ ADD (TALER_MERCHANT_POST_ORDERS_PAY_OPTION_DONAU_YEAR (ps->donau_data.year
+ ));
+ ADD (TALER_MERCHANT_POST_ORDERS_PAY_OPTION_DONAU_BUDIS (
ps->donau_data.budis_json));
}
#endif
- ADD (TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE ());
-#undef ADD
-
- if (TALER_MERCHANT_OPOEC_OK !=
- TALER_MERCHANT_order_pay_set_options (ps->oph,
- opts,
- oi))
- TALER_TESTING_FAIL (is);
-
- if (TALER_MERCHANT_OPOEC_OK !=
- TALER_MERCHANT_order_pay_start (ps->oph))
+#endif
+ if (TALER_EC_NONE !=
+ TALER_MERCHANT_post_orders_pay_start (ps->oph,
+ &pay_cb,
+ ps))
TALER_TESTING_FAIL (is);
}
@@ -1491,7 +1499,7 @@ pay_cleanup (void *cls,
"Command `%s' did not complete.\n",
TALER_TESTING_interpreter_get_current_label (
ps->is));
- TALER_MERCHANT_order_pay_cancel1 (ps->oph);
+ TALER_MERCHANT_post_orders_pay_cancel (ps->oph);
}
GNUNET_free (ps);
diff --git a/src/testing/testing_api_cmd_post_account.c b/src/testing/testing_api_cmd_post_account.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-accounts-new.h>
/**
@@ -37,7 +38,7 @@ struct PostAccountState
/**
* Handle for a "GET product" request.
*/
- struct TALER_MERCHANT_AccountsPostHandle *aph;
+ struct TALER_MERCHANT_PostPrivateAccountsHandle *aph;
/**
* The interpreter state.
@@ -85,7 +86,7 @@ struct PostAccountState
*/
static void
post_account_cb (void *cls,
- const struct TALER_MERCHANT_AccountsPostResponse *apr)
+ const struct TALER_MERCHANT_PostPrivateAccountsResponse *apr)
{
struct PostAccountState *pas = cls;
@@ -139,15 +140,29 @@ post_account_run (void *cls,
struct PostAccountState *pas = cls;
pas->is = is;
- pas->aph = TALER_MERCHANT_accounts_post (
+ pas->aph = TALER_MERCHANT_post_private_accounts_create (
TALER_TESTING_interpreter_get_context (is),
pas->merchant_url,
- pas->payto_uri,
- pas->credit_facade_url,
- pas->credit_facade_credentials,
- &post_account_cb,
- pas);
- GNUNET_assert (NULL != pas->aph);
+ pas->payto_uri);
+ if (NULL != pas->credit_facade_url)
+ TALER_MERCHANT_post_private_accounts_set_options (
+ pas->aph,
+ TALER_MERCHANT_post_private_accounts_option_credit_facade_url (
+ pas->credit_facade_url));
+ if (NULL != pas->credit_facade_credentials)
+ TALER_MERCHANT_post_private_accounts_set_options (
+ pas->aph,
+ TALER_MERCHANT_post_private_accounts_option_credit_facade_credentials (
+ pas->credit_facade_credentials));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_accounts_start (
+ pas->aph,
+ &post_account_cb,
+ pas);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -206,7 +221,7 @@ post_account_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /account operation did not complete\n");
- TALER_MERCHANT_accounts_post_cancel (pas->aph);
+ TALER_MERCHANT_post_private_accounts_cancel (pas->aph);
}
GNUNET_free (pas->payto_uri.full_payto);
GNUNET_free (pas->credit_facade_url);
diff --git a/src/testing/testing_api_cmd_post_categories.c b/src/testing/testing_api_cmd_post_categories.c
@@ -24,6 +24,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-categories-new.h>
/**
* State of a "POST /private/categories" CMD.
@@ -33,7 +34,7 @@ struct PostCategoriesState
/**
* Handle for a "POST /private/categories" request.
*/
- struct TALER_MERCHANT_CategoriesPostHandle *cph;
+ struct TALER_MERCHANT_PostPrivateCategoriesHandle *cph;
/**
* The interpreter state.
@@ -79,7 +80,8 @@ struct PostCategoriesState
*/
static void
post_categories_cb (void *cls,
- const struct TALER_MERCHANT_CategoriesPostResponse *cpr)
+ const struct TALER_MERCHANT_PostPrivateCategoriesResponse *
+ cpr)
{
struct PostCategoriesState *pcs = cls;
@@ -96,7 +98,7 @@ post_categories_cb (void *cls,
}
if (MHD_HTTP_OK == cpr->hr.http_status)
{
- pcs->category_id = cpr->category_id;
+ pcs->category_id = cpr->details.ok.category_id;
if ( (0 != pcs->expected_category_id) &&
(pcs->expected_category_id != pcs->category_id) )
{
@@ -127,14 +129,25 @@ post_categories_run (void *cls,
struct PostCategoriesState *pcs = cls;
pcs->is = is;
- pcs->cph = TALER_MERCHANT_categories_post (
+ pcs->cph = TALER_MERCHANT_post_private_categories_create (
TALER_TESTING_interpreter_get_context (is),
pcs->merchant_url,
- pcs->name,
- pcs->name_i18n,
- &post_categories_cb,
- pcs);
- GNUNET_assert (NULL != pcs->cph);
+ pcs->name);
+ if (NULL != pcs->name_i18n)
+ {
+ TALER_MERCHANT_post_private_categories_set_options (
+ pcs->cph,
+ TALER_MERCHANT_post_private_categories_option_name_i18n (
+ pcs->name_i18n));
+ }
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_categories_start (pcs->cph,
+ &post_categories_cb,
+ pcs);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -183,7 +196,7 @@ post_categories_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /private/categories operation did not complete\n");
- TALER_MERCHANT_categories_post_cancel (pcs->cph);
+ TALER_MERCHANT_post_private_categories_cancel (pcs->cph);
}
json_decref (pcs->name_i18n);
GNUNET_free (pcs);
diff --git a/src/testing/testing_api_cmd_post_donau_instances.c b/src/testing/testing_api_cmd_post_donau_instances.c
@@ -30,6 +30,7 @@
#include "taler/taler_merchant_testing_lib.h"
#include "taler/taler_merchant_donau.h"
#include <donau/donau_testing_lib.h>
+#include <taler/taler-merchant/post-private-donau-new.h>
/**
@@ -40,7 +41,7 @@ struct PostDonauState
/**
* Handle for a "POST donau" request.
*/
- struct TALER_MERCHANT_DonauInstancePostHandle *dph;
+ struct TALER_MERCHANT_PostPrivateDonauHandle *dph;
/**
* The interpreter state.
@@ -81,30 +82,30 @@ struct PostDonauState
*/
static void
post_donau_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PostPrivateDonauResponse *pdr)
{
struct PostDonauState *pds = cls;
pds->dph = NULL;
- if (pds->http_status != hr->http_status)
+ if (pds->http_status != pdr->hr.http_status)
{
TALER_TESTING_unexpected_status_with_body (
pds->is,
- hr->http_status,
+ pdr->hr.http_status,
pds->http_status,
- hr->reply);
+ pdr->hr.reply);
TALER_TESTING_interpreter_fail (pds->is);
return;
}
- switch (hr->http_status)
+ switch (pdr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
case MHD_HTTP_BAD_REQUEST:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /donau returned BAD REQUEST: %s\n",
- json_dumps (hr->reply, JSON_INDENT (2)));
+ json_dumps (pdr->hr.reply, JSON_INDENT (2)));
break;
case MHD_HTTP_UNAUTHORIZED:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -117,7 +118,7 @@ post_donau_cb (void *cls,
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for POST /donau\n",
- hr->http_status);
+ pdr->hr.http_status);
}
TALER_TESTING_interpreter_next (pds->is);
}
@@ -139,19 +140,24 @@ post_donau_run (void *cls,
pds->is = is;
pds->charity.charity_url = TALER_TESTING_get_donau_url (is);
- pds->dph = TALER_MERCHANT_donau_instances_post (
+ pds->dph = TALER_MERCHANT_post_private_donau_create (
TALER_TESTING_interpreter_get_context (is),
pds->merchant_url,
- &pds->charity,
- pds->auth_token,
- &post_donau_cb,
- pds);
-
- if (NULL == pds->dph)
+ &pds->charity);
+ if (NULL != pds->auth_token)
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (pds->is);
- return;
+ TALER_MERCHANT_post_private_donau_set_options (
+ pds->dph,
+ TALER_MERCHANT_post_private_donau_option_auth_token (
+ pds->auth_token));
+ }
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_donau_start (pds->dph,
+ &post_donau_cb,
+ pds);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
}
@@ -173,7 +179,7 @@ post_donau_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /donau operation did not complete\n");
- TALER_MERCHANT_donau_instances_post_cancel (pds->dph);
+ TALER_MERCHANT_post_private_donau_cancel (pds->dph);
}
GNUNET_free (pds);
}
diff --git a/src/testing/testing_api_cmd_post_instances.c b/src/testing/testing_api_cmd_post_instances.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-management-instances-new.h>
/**
@@ -37,7 +38,7 @@ struct PostInstancesState
/**
* Handle for a "POST instance" request.
*/
- struct TALER_MERCHANT_InstancesPostHandle *iph;
+ struct TALER_MERCHANT_PostManagementInstancesHandle *iph;
/**
* The interpreter state.
@@ -106,22 +107,22 @@ struct PostInstancesState
static void
post_instances_cb (
void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PostManagementInstancesResponse *mir)
{
struct PostInstancesState *pis = cls;
pis->iph = NULL;
- if (pis->http_status != hr->http_status)
+ if (pis->http_status != mir->hr.http_status)
{
TALER_TESTING_unexpected_status_with_body (
pis->is,
- hr->http_status,
+ mir->hr.http_status,
pis->http_status,
- hr->reply);
+ mir->hr.reply);
TALER_TESTING_interpreter_fail (pis->is);
return;
}
- switch (hr->http_status)
+ switch (mir->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -139,7 +140,7 @@ post_instances_cb (
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for POST instances.\n",
- hr->http_status);
+ mir->hr.http_status);
}
TALER_TESTING_interpreter_next (pis->is);
}
@@ -162,7 +163,7 @@ post_instances_run (
struct PostInstancesState *pis = cls;
pis->is = is;
- pis->iph = TALER_MERCHANT_instances_post (
+ pis->iph = TALER_MERCHANT_post_management_instances_create (
TALER_TESTING_interpreter_get_context (is),
pis->merchant_url,
pis->instance_id,
@@ -174,14 +175,20 @@ post_instances_run (
pis->default_pay_delay,
GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS,
15),
- pis->auth_token,
- &post_instances_cb,
- pis);
- if (NULL == pis->iph)
+ pis->auth_token);
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (pis->is);
- return;
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_management_instances_start (
+ pis->iph,
+ &post_instances_cb,
+ pis);
+ if (TALER_EC_NONE != ec)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (pis->is);
+ return;
+ }
}
}
@@ -238,7 +245,7 @@ post_instances_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /instances operation did not complete\n");
- TALER_MERCHANT_instances_post_cancel (pis->iph);
+ TALER_MERCHANT_post_management_instances_cancel (pis->iph);
}
json_decref (pis->address);
json_decref (pis->jurisdiction);
diff --git a/src/testing/testing_api_cmd_post_orders.c b/src/testing/testing_api_cmd_post_orders.c
@@ -35,6 +35,8 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-orders-new.h>
+#include <taler/taler-merchant/post-orders-ORDER_ID-claim-new.h>
/**
* State for a "POST /orders" CMD.
@@ -80,14 +82,14 @@ struct OrdersState
/**
* The /orders operation handle.
*/
- struct TALER_MERCHANT_PostOrdersHandle *po;
+ struct TALER_MERCHANT_PostPrivateOrdersHandle *po;
/**
* The (initial) POST /orders/$ID/claim operation handle.
* The logic is such that after an order creation,
* we immediately claim the order.
*/
- struct TALER_MERCHANT_OrderClaimHandle *och;
+ struct TALER_MERCHANT_PostOrdersClaimHandle *och;
/**
* The nonce.
@@ -197,7 +199,7 @@ orders_traits (void *cls,
*/
static void
orders_claim_cb (void *cls,
- const struct TALER_MERCHANT_OrderClaimResponse *ocr)
+ const struct TALER_MERCHANT_PostOrdersClaimResponse *ocr)
{
struct OrdersState *ps = cls;
const char *error_name;
@@ -224,8 +226,10 @@ orders_claim_cb (void *cls,
}
ps->contract_terms = json_deep_copy (
(json_t *) ocr->details.ok.contract_terms);
- ps->h_contract_terms = ocr->details.ok.h_contract_terms;
- ps->merchant_sig = ocr->details.ok.sig;
+ GNUNET_assert (GNUNET_OK ==
+ TALER_JSON_contract_hash (ps->contract_terms,
+ &ps->h_contract_terms));
+ ps->merchant_sig = ocr->details.ok.merchant_sig;
if (GNUNET_OK !=
GNUNET_JSON_parse (ps->contract_terms,
spec,
@@ -260,7 +264,7 @@ orders_claim_cb (void *cls,
*/
static void
order_cb (void *cls,
- const struct TALER_MERCHANT_PostOrdersReply *por)
+ const struct TALER_MERCHANT_PostPrivateOrdersResponse *por)
{
struct OrdersState *ps = cls;
@@ -357,16 +361,25 @@ order_cb (void *cls,
TALER_TESTING_interpreter_next (ps->is);
return;
}
- if (NULL ==
- (ps->och = TALER_MERCHANT_order_claim (
- TALER_TESTING_interpreter_get_context (ps->is),
- ps->merchant_url,
- ps->order_id,
- &ps->nonce,
- &ps->claim_token,
- &orders_claim_cb,
- ps)))
+ ps->och = TALER_MERCHANT_post_orders_claim_create (
+ TALER_TESTING_interpreter_get_context (ps->is),
+ ps->merchant_url,
+ ps->order_id,
+ &ps->nonce);
+ if (NULL == ps->och)
TALER_TESTING_FAIL (ps->is);
+ TALER_MERCHANT_post_orders_claim_set_options (
+ ps->och,
+ TALER_MERCHANT_post_orders_claim_option_token (
+ &ps->claim_token));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_orders_claim_start (ps->och,
+ &orders_claim_cb,
+ ps);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -404,14 +417,19 @@ orders_run (void *cls,
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&ps->nonce,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
- ps->po = TALER_MERCHANT_orders_post (TALER_TESTING_interpreter_get_context (
- is),
- ps->merchant_url,
- ps->order_terms,
- GNUNET_TIME_UNIT_ZERO,
- &order_cb,
- ps);
+ ps->po = TALER_MERCHANT_post_private_orders_create (
+ TALER_TESTING_interpreter_get_context (is),
+ ps->merchant_url,
+ ps->order_terms);
GNUNET_assert (NULL != ps->po);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_orders_start (ps->po,
+ &order_cb,
+ ps);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -432,7 +450,7 @@ orders_run2 (void *cls,
char *products_string = GNUNET_strdup (ps->products);
char *locks_string = GNUNET_strdup (ps->locks);
char *token;
- struct TALER_MERCHANT_InventoryProduct *products = NULL;
+ struct TALER_MERCHANT_PostPrivateOrdersInventoryProduct *products = NULL;
unsigned int products_length = 0;
const char **locks = NULL;
unsigned int locks_length = 0;
@@ -492,7 +510,7 @@ orders_run2 (void *cls,
token = strtok (NULL, ";"))
{
char *ctok;
- struct TALER_MERCHANT_InventoryProduct pd;
+ struct TALER_MERCHANT_PostPrivateOrdersInventoryProduct pd;
double quantity_double = 0.0;
/* Token syntax is "[product_id]/[quantity]" */
@@ -586,20 +604,38 @@ orders_run2 (void *cls,
locks_length,
uuid);
}
- ps->po = TALER_MERCHANT_orders_post2 (
- TALER_TESTING_interpreter_get_context (
- is),
+ ps->po = TALER_MERCHANT_post_private_orders_create (
+ TALER_TESTING_interpreter_get_context (is),
ps->merchant_url,
- order,
- GNUNET_TIME_UNIT_ZERO,
- ps->payment_target,
- products_length,
- products,
- locks_length,
- locks,
- ps->make_claim_token,
- &order_cb,
- ps);
+ order);
+ GNUNET_assert (NULL != ps->po);
+ if (NULL != ps->payment_target)
+ TALER_MERCHANT_post_private_orders_set_options (
+ ps->po,
+ TALER_MERCHANT_post_private_orders_option_payment_target (
+ ps->payment_target));
+ if (0 < products_length)
+ TALER_MERCHANT_post_private_orders_set_options (
+ ps->po,
+ TALER_MERCHANT_post_private_orders_option_inventory_products (
+ products_length, products));
+ if (0 < locks_length)
+ TALER_MERCHANT_post_private_orders_set_options (
+ ps->po,
+ TALER_MERCHANT_post_private_orders_option_lock_uuids (
+ locks_length, locks));
+ TALER_MERCHANT_post_private_orders_set_options (
+ ps->po,
+ TALER_MERCHANT_post_private_orders_option_create_token (
+ ps->make_claim_token));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_orders_start (ps->po,
+ &order_cb,
+ ps);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
GNUNET_free (products_string);
GNUNET_free (locks_string);
GNUNET_array_grow (products,
@@ -608,7 +644,6 @@ orders_run2 (void *cls,
GNUNET_array_grow (locks,
locks_length,
0);
- GNUNET_assert (NULL != ps->po);
}
@@ -647,14 +682,19 @@ orders_run3 (void *cls,
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&ps->nonce,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
- ps->po = TALER_MERCHANT_orders_post (TALER_TESTING_interpreter_get_context (
- is),
- ps->merchant_url,
- ps->order_terms,
- GNUNET_TIME_UNIT_ZERO,
- &order_cb,
- ps);
+ ps->po = TALER_MERCHANT_post_private_orders_create (
+ TALER_TESTING_interpreter_get_context (is),
+ ps->merchant_url,
+ ps->order_terms);
GNUNET_assert (NULL != ps->po);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_orders_start (ps->po,
+ &order_cb,
+ ps);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -676,7 +716,7 @@ orders_cleanup (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command '%s' did not complete (orders put)\n",
cmd->label);
- TALER_MERCHANT_orders_post_cancel (ps->po);
+ TALER_MERCHANT_post_private_orders_cancel (ps->po);
ps->po = NULL;
}
@@ -685,7 +725,7 @@ orders_cleanup (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command '%s' did not complete (orders lookup)\n",
cmd->label);
- TALER_MERCHANT_order_claim_cancel (ps->och);
+ TALER_MERCHANT_post_orders_claim_cancel (ps->och);
ps->och = NULL;
}
diff --git a/src/testing/testing_api_cmd_post_orders_paid.c b/src/testing/testing_api_cmd_post_orders_paid.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-orders-ORDER_ID-paid-new.h>
/**
@@ -37,7 +38,7 @@ struct PostOrdersPaidState
/**
* Handle for a "POST /paid" request.
*/
- struct TALER_MERCHANT_OrderPaidHandle *oph;
+ struct TALER_MERCHANT_PostOrdersPaidHandle *oph;
/**
* The interpreter state.
@@ -75,7 +76,7 @@ struct PostOrdersPaidState
*/
static void
paid_cb (void *cls,
- const struct TALER_MERCHANT_OrderPaidResponse *opr)
+ const struct TALER_MERCHANT_PostOrdersPaidResponse *opr)
{
struct PostOrdersPaidState *ops = cls;
@@ -179,18 +180,22 @@ paid_run (void *cls,
&h_contract_terms))
TALER_TESTING_FAIL (is);
- ops->oph = TALER_MERCHANT_order_paid (TALER_TESTING_interpreter_get_context (
- is),
- ops->merchant_url,
- order_id,
- ops->session_id,
- h_contract_terms,
- NULL,
- merchant_sig,
- &paid_cb,
- ops);
- if (NULL == ops->oph)
- TALER_TESTING_FAIL (is);
+ ops->oph = TALER_MERCHANT_post_orders_paid_create (
+ TALER_TESTING_interpreter_get_context (is),
+ ops->merchant_url,
+ order_id,
+ ops->session_id,
+ h_contract_terms,
+ merchant_sig);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_orders_paid_start (
+ ops->oph,
+ &paid_cb,
+ ops);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -212,7 +217,7 @@ paid_cleanup (void *cls,
"Command `%s' did not complete.\n",
TALER_TESTING_interpreter_get_current_label (
ops->is));
- TALER_MERCHANT_order_paid_cancel (ops->oph);
+ TALER_MERCHANT_post_orders_paid_cancel (ops->oph);
}
GNUNET_free (ops);
}
diff --git a/src/testing/testing_api_cmd_post_otp_devices.c b/src/testing/testing_api_cmd_post_otp_devices.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-otp-devices-new.h>
/**
@@ -37,7 +38,7 @@ struct PostOtpDevicesState
/**
* Handle for a "GET otp_device" request.
*/
- struct TALER_MERCHANT_OtpDevicesPostHandle *iph;
+ struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *iph;
/**
* The interpreter state.
@@ -90,22 +91,23 @@ struct PostOtpDevicesState
*/
static void
post_otp_devices_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PostPrivateOtpDevicesResponse *
+ odr)
{
struct PostOtpDevicesState *tis = cls;
tis->iph = NULL;
- if (tis->http_status != hr->http_status)
+ if (tis->http_status != odr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ odr->hr.http_status,
+ (int) odr->hr.ec,
TALER_TESTING_interpreter_get_current_label (tis->is));
TALER_TESTING_interpreter_fail (tis->is);
return;
}
- switch (hr->http_status)
+ switch (odr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -121,7 +123,7 @@ post_otp_devices_cb (void *cls,
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for POST /otp-devices.\n",
- hr->http_status);
+ odr->hr.http_status);
}
TALER_TESTING_interpreter_next (tis->is);
}
@@ -143,21 +145,27 @@ post_otp_devices_run (void *cls,
struct PostOtpDevicesState *tis = cls;
tis->is = is;
- tis->iph = TALER_MERCHANT_otp_devices_post (
+ tis->iph = TALER_MERCHANT_post_private_otp_devices_create (
TALER_TESTING_interpreter_get_context (is),
tis->merchant_url,
tis->otp_device_id,
tis->otp_device_description,
tis->otp_key,
tis->otp_alg,
- tis->otp_ctr,
- &post_otp_devices_cb,
- tis);
- if (NULL == tis->iph)
+ tis->otp_ctr);
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (tis->is);
- return;
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_otp_devices_start (
+ tis->iph,
+ &post_otp_devices_cb,
+ tis);
+ if (TALER_EC_NONE != ec)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tis->is);
+ return;
+ }
}
}
@@ -212,7 +220,7 @@ post_otp_devices_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /otp-devices operation did not complete\n");
- TALER_MERCHANT_otp_devices_post_cancel (tis->iph);
+ TALER_MERCHANT_post_private_otp_devices_cancel (tis->iph);
}
GNUNET_free (tis->otp_key);
GNUNET_free (tis);
diff --git a/src/testing/testing_api_cmd_post_products.c b/src/testing/testing_api_cmd_post_products.c
@@ -24,9 +24,8 @@
#include "taler/platform.h"
#include <taler/taler_exchange_service.h>
#include <taler/taler_testing_lib.h>
-#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
-#include "merchant_api_common.h"
+#include <taler/taler-merchant/post-private-products-new.h>
/**
@@ -38,7 +37,7 @@ struct PostProductsState
/**
* Handle for a "POST /products" request.
*/
- struct TALER_MERCHANT_ProductsPostHandle *iph;
+ struct TALER_MERCHANT_PostPrivateProductsHandle *iph;
/**
* The interpreter state.
@@ -251,22 +250,22 @@ default_precision_from_unit (const char *unit)
*/
static void
post_products_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PostPrivateProductsResponse *ppr)
{
struct PostProductsState *pis = cls;
pis->iph = NULL;
- if (pis->http_status != hr->http_status)
+ if (pis->http_status != ppr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ ppr->hr.http_status,
+ (int) ppr->hr.ec,
TALER_TESTING_interpreter_get_current_label (pis->is));
TALER_TESTING_interpreter_fail (pis->is);
return;
}
- switch (hr->http_status)
+ switch (ppr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -282,7 +281,7 @@ post_products_cb (void *cls,
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for POST /products.\n",
- hr->http_status);
+ ppr->hr.http_status);
}
TALER_TESTING_interpreter_next (pis->is);
}
@@ -304,55 +303,76 @@ post_products_run (void *cls,
struct PostProductsState *pis = cls;
pis->is = is;
- if (pis->use_fractional ||
- pis->use_unit_price_array ||
- (0 < pis->num_cats))
+ pis->iph = TALER_MERCHANT_post_private_products_create (
+ TALER_TESTING_interpreter_get_context (is),
+ pis->merchant_url,
+ pis->product_id,
+ pis->description,
+ pis->unit,
+ &pis->price,
+ pis->image,
+ pis->total_stock);
+ if (NULL != pis->description_i18n)
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_description_i18n (
+ pis->description_i18n));
+ if (NULL != pis->taxes)
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_taxes (
+ pis->taxes));
+ if (NULL != pis->address)
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_address (
+ pis->address));
+ if (GNUNET_TIME_absolute_is_zero (pis->next_restock.abs_time) == 0)
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_next_restock (
+ pis->next_restock));
+ if (0 < pis->minimum_age)
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_minimum_age (
+ pis->minimum_age));
+ if (0 < pis->num_cats)
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_categories (
+ pis->num_cats,
+ pis->cats));
+ if (pis->use_unit_price_array)
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_unit_prices (
+ pis->unit_prices,
+ pis->unit_prices_len));
+ if (pis->use_fractional)
{
- pis->iph = TALER_MERCHANT_products_post4 (
- TALER_TESTING_interpreter_get_context (is),
- pis->merchant_url,
- pis->product_id,
- pis->description,
- pis->description_i18n,
- pis->unit,
- pis->unit_prices,
- pis->unit_prices_len,
- pis->image,
- pis->taxes,
- pis->total_stock,
- pis->total_stock_frac,
- pis->unit_allow_fraction,
- pis->use_fractional
- ? &pis->unit_precision_level
- : NULL,
- pis->address,
- pis->next_restock,
- pis->minimum_age,
- pis->num_cats,
- pis->cats,
- &post_products_cb,
- pis);
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_total_stock_frac (
+ pis->total_stock_frac));
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_unit_allow_fraction (
+ pis->unit_allow_fraction));
+ TALER_MERCHANT_post_private_products_set_options (
+ pis->iph,
+ TALER_MERCHANT_post_private_products_option_unit_precision_level (
+ pis->unit_precision_level));
}
- else
{
- pis->iph = TALER_MERCHANT_products_post2 (
- TALER_TESTING_interpreter_get_context (is),
- pis->merchant_url,
- pis->product_id,
- pis->description,
- pis->description_i18n,
- pis->unit,
- &pis->price,
- pis->image,
- pis->taxes,
- pis->total_stock,
- pis->address,
- pis->next_restock,
- pis->minimum_age,
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_products_start (
+ pis->iph,
&post_products_cb,
pis);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
- GNUNET_assert (NULL != pis->iph);
}
@@ -418,7 +438,7 @@ post_products_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /products operation did not complete\n");
- TALER_MERCHANT_products_post_cancel (pis->iph);
+ TALER_MERCHANT_post_private_products_cancel (pis->iph);
}
if (pis->owns_unit_prices)
GNUNET_free (pis->unit_prices);
diff --git a/src/testing/testing_api_cmd_post_templates.c b/src/testing/testing_api_cmd_post_templates.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-templates-new.h>
/**
@@ -37,7 +38,7 @@ struct PostTemplatesState
/**
* Handle for a "GET template" request.
*/
- struct TALER_MERCHANT_TemplatesPostHandle *iph;
+ struct TALER_MERCHANT_PostPrivateTemplatesHandle *iph;
/**
* The interpreter state.
@@ -85,20 +86,21 @@ struct PostTemplatesState
*/
static void
post_templates_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PostPrivateTemplatesResponse *ptr
+ )
{
struct PostTemplatesState *tis = cls;
tis->iph = NULL;
- if (tis->http_status != hr->http_status)
+ if (tis->http_status != ptr->hr.http_status)
{
TALER_TESTING_unexpected_status_with_body (tis->is,
- hr->http_status,
+ ptr->hr.http_status,
tis->http_status,
- hr->reply);
+ ptr->hr.reply);
return;
}
- switch (hr->http_status)
+ switch (ptr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -114,7 +116,7 @@ post_templates_cb (void *cls,
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for POST /templates.\n",
- hr->http_status);
+ ptr->hr.http_status);
}
TALER_TESTING_interpreter_next (tis->is);
}
@@ -136,20 +138,30 @@ post_templates_run (void *cls,
struct PostTemplatesState *tis = cls;
tis->is = is;
- tis->iph = TALER_MERCHANT_templates_post (
+ tis->iph = TALER_MERCHANT_post_private_templates_create (
TALER_TESTING_interpreter_get_context (is),
tis->merchant_url,
tis->template_id,
tis->template_description,
- tis->otp_id,
- tis->template_contract,
- &post_templates_cb,
- tis);
- if (NULL == tis->iph)
+ tis->template_contract);
+ if (NULL != tis->otp_id)
+ TALER_MERCHANT_post_private_templates_set_options (
+ tis->iph,
+ TALER_MERCHANT_post_private_templates_option_otp_id (
+ tis->otp_id));
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (tis->is);
- return;
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_templates_start (
+ tis->iph,
+ &post_templates_cb,
+ tis);
+ if (TALER_EC_NONE != ec)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tis->is);
+ return;
+ }
}
}
@@ -203,7 +215,7 @@ post_templates_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /templates operation did not complete\n");
- TALER_MERCHANT_templates_post_cancel (tis->iph);
+ TALER_MERCHANT_post_private_templates_cancel (tis->iph);
}
GNUNET_free (tis->otp_id);
json_decref (tis->template_contract);
diff --git a/src/testing/testing_api_cmd_post_tokenfamilies.c b/src/testing/testing_api_cmd_post_tokenfamilies.c
@@ -28,6 +28,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-tokenfamilies-new.h>
/**
@@ -44,7 +45,7 @@ struct PostTokenFamiliesState
/**
* Handle for a "POST /tokenfamilies" request.
*/
- struct TALER_MERCHANT_TokenFamiliesPostHandle *handle;
+ struct TALER_MERCHANT_PostPrivateTokenfamiliesHandle *handle;
/**
* The interpreter state.
@@ -111,22 +112,23 @@ struct PostTokenFamiliesState
*/
static void
post_tokenfamilies_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct
+ TALER_MERCHANT_PostPrivateTokenfamiliesResponse *tfr)
{
struct PostTokenFamiliesState *state = cls;
state->handle = NULL;
- if (state->http_status != hr->http_status)
+ if (state->http_status != tfr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ tfr->hr.http_status,
+ (int) tfr->hr.ec,
TALER_TESTING_interpreter_get_current_label (state->is));
TALER_TESTING_interpreter_fail (state->is);
return;
}
- switch (hr->http_status)
+ switch (tfr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -140,7 +142,7 @@ post_tokenfamilies_cb (void *cls,
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for POST /tokenfamilies.\n",
- hr->http_status);
+ tfr->hr.http_status);
}
TALER_TESTING_interpreter_next (state->is);
}
@@ -162,23 +164,34 @@ post_tokenfamilies_run (void *cls,
struct PostTokenFamiliesState *state = cls;
state->is = is;
- state->handle = TALER_MERCHANT_token_families_post (
+ state->handle = TALER_MERCHANT_post_private_tokenfamilies_create (
TALER_TESTING_interpreter_get_context (is),
state->merchant_url,
state->slug,
state->name,
state->description,
- state->description_i18n,
- NULL, /* extra data */
state->valid_after,
state->valid_before,
state->duration,
state->rounding,
GNUNET_TIME_UNIT_ZERO, /* start_offset */
- state->kind,
- &post_tokenfamilies_cb,
- state);
- GNUNET_assert (NULL != state->handle);
+ state->kind);
+ if (NULL != state->description_i18n)
+ {
+ TALER_MERCHANT_post_private_tokenfamilies_set_options (
+ state->handle,
+ TALER_MERCHANT_post_private_tokenfamilies_option_description_i18n (
+ state->description_i18n));
+ }
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_tokenfamilies_start (
+ state->handle,
+ &post_tokenfamilies_cb,
+ state);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -234,7 +247,7 @@ post_tokenfamilies_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /tokenfamilies operation did not complete\n");
- TALER_MERCHANT_token_families_post_cancel (state->handle);
+ TALER_MERCHANT_post_private_tokenfamilies_cancel (state->handle);
}
json_decref (state->description_i18n);
GNUNET_free (state);
diff --git a/src/testing/testing_api_cmd_post_transfers.c b/src/testing/testing_api_cmd_post_transfers.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-transfers-new.h>
/**
@@ -37,7 +38,7 @@ struct PostTransfersState
/**
* Handle for a "POST /transfers" request.
*/
- struct TALER_MERCHANT_PostTransfersHandle *pth;
+ struct TALER_MERCHANT_PostPrivateTransfersHandle *pth;
/**
* Handle for a "GET" bank account history request.
@@ -126,7 +127,7 @@ struct PostTransfersState
*/
static void
transfers_cb (void *cls,
- const struct TALER_MERCHANT_PostTransfersResponse *ptr)
+ const struct TALER_MERCHANT_PostPrivateTransfersResponse *ptr)
{
struct PostTransfersState *pts = cls;
@@ -213,16 +214,22 @@ post_transfers_run2 (void *cls,
struct PostTransfersState *pts = cls;
pts->is = is;
- pts->pth = TALER_MERCHANT_transfers_post (
+ pts->pth = TALER_MERCHANT_post_private_transfers_create (
TALER_TESTING_interpreter_get_context (pts->is),
pts->merchant_url,
&pts->credit_amount,
&pts->wtid,
pts->credit_account,
- pts->exchange_url,
- &transfers_cb,
- pts);
- GNUNET_assert (NULL != pts->pth);
+ pts->exchange_url);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_transfers_start (
+ pts->pth,
+ &transfers_cb,
+ pts);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -278,16 +285,22 @@ debit_cb (
pts->payto_uri.full_payto,
pts->exchange_url,
TALER_B2S (&pts->wtid));
- pts->pth = TALER_MERCHANT_transfers_post (
+ pts->pth = TALER_MERCHANT_post_private_transfers_create (
TALER_TESTING_interpreter_get_context (pts->is),
pts->merchant_url,
&pts->credit_amount,
&pts->wtid,
pts->credit_account,
- pts->exchange_url,
- &transfers_cb,
- pts);
- GNUNET_assert (NULL != pts->pth);
+ pts->exchange_url);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_transfers_start (
+ pts->pth,
+ &transfers_cb,
+ pts);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
break;
}
if (NULL == pts->pth)
@@ -348,7 +361,7 @@ post_transfers_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /transfers operation did not complete\n");
- TALER_MERCHANT_transfers_post_cancel (pts->pth);
+ TALER_MERCHANT_post_private_transfers_cancel (pts->pth);
}
if (NULL != pts->dhh)
{
diff --git a/src/testing/testing_api_cmd_post_units.c b/src/testing/testing_api_cmd_post_units.c
@@ -25,6 +25,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-units-new.h>
/**
@@ -35,7 +36,7 @@ struct PostUnitState
/**
* In-flight request handle.
*/
- struct TALER_MERCHANT_UnitsPostHandle *uph;
+ struct TALER_MERCHANT_PostPrivateUnitsHandle *uph;
/**
* Interpreter context.
@@ -99,17 +100,17 @@ struct PostUnitState
*/
static void
post_unit_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PostPrivateUnitsResponse *pur)
{
struct PostUnitState *pus = cls;
pus->uph = NULL;
- if (pus->http_status != hr->http_status)
+ if (pus->http_status != pur->hr.http_status)
{
TALER_TESTING_unexpected_status_with_body (pus->is,
- hr->http_status,
+ pur->hr.http_status,
pus->http_status,
- hr->reply);
+ pur->hr.reply);
return;
}
TALER_TESTING_interpreter_next (pus->is);
@@ -127,7 +128,7 @@ post_unit_run (void *cls,
struct PostUnitState *pus = cls;
pus->is = is;
- pus->uph = TALER_MERCHANT_units_post (
+ pus->uph = TALER_MERCHANT_post_private_units_create (
TALER_TESTING_interpreter_get_context (is),
pus->merchant_url,
pus->unit_id,
@@ -135,15 +136,29 @@ post_unit_run (void *cls,
pus->unit_name_short,
pus->unit_allow_fraction,
pus->unit_precision_level,
- pus->unit_active,
- pus->unit_name_long_i18n,
- pus->unit_name_short_i18n,
- &post_unit_cb,
- pus);
- if (NULL == pus->uph)
+ pus->unit_active);
+ if (NULL != pus->unit_name_long_i18n)
+ TALER_MERCHANT_post_private_units_set_options (
+ pus->uph,
+ TALER_MERCHANT_post_private_units_option_unit_name_long_i18n (
+ pus->unit_name_long_i18n));
+ if (NULL != pus->unit_name_short_i18n)
+ TALER_MERCHANT_post_private_units_set_options (
+ pus->uph,
+ TALER_MERCHANT_post_private_units_option_unit_name_short_i18n (
+ pus->unit_name_short_i18n));
{
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (is);
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_units_start (
+ pus->uph,
+ &post_unit_cb,
+ pus);
+ if (TALER_EC_NONE != ec)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ }
}
}
@@ -188,7 +203,7 @@ post_unit_cleanup (void *cls,
if (NULL != pus->uph)
{
- TALER_MERCHANT_units_post_cancel (pus->uph);
+ TALER_MERCHANT_post_private_units_cancel (pus->uph);
pus->uph = NULL;
}
if (NULL != pus->unit_name_long_i18n)
diff --git a/src/testing/testing_api_cmd_post_using_templates.c b/src/testing/testing_api_cmd_post_using_templates.c
@@ -26,6 +26,8 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-templates-TEMPLATE_ID-new.h>
+#include <taler/taler-merchant/post-orders-ORDER_ID-claim-new.h>
/**
@@ -35,9 +37,9 @@ struct PostUsingTemplatesState
{
/**
- * Handle for a "GET using-template" request.
+ * Handle for a "POST /templates/$TEMPLATE_ID" request.
*/
- struct TALER_MERCHANT_UsingTemplatesPostHandle *iph;
+ struct TALER_MERCHANT_PostTemplatesHandle *iph;
/**
* The interpreter state.
@@ -49,7 +51,7 @@ struct PostUsingTemplatesState
* The logic is such that after an order creation,
* we immediately claim the order.
*/
- struct TALER_MERCHANT_OrderClaimHandle *och;
+ struct TALER_MERCHANT_PostOrdersClaimHandle *och;
/**
* Base URL of the merchant serving the request.
@@ -169,7 +171,7 @@ struct PostUsingTemplatesState
*/
static void
using_claim_cb (void *cls,
- const struct TALER_MERCHANT_OrderClaimResponse *ocr)
+ const struct TALER_MERCHANT_PostOrdersClaimResponse *ocr)
{
struct PostUsingTemplatesState *tis = cls;
const char *error_name;
@@ -196,8 +198,14 @@ using_claim_cb (void *cls,
}
tis->contract_terms = json_deep_copy (
(json_t *) ocr->details.ok.contract_terms);
- tis->h_contract_terms = ocr->details.ok.h_contract_terms;
- tis->merchant_sig = ocr->details.ok.sig;
+ if (GNUNET_OK !=
+ TALER_JSON_contract_hash (tis->contract_terms,
+ &tis->h_contract_terms))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_FAIL (tis->is);
+ }
+ tis->merchant_sig = ocr->details.ok.merchant_sig;
if (GNUNET_OK !=
GNUNET_JSON_parse (tis->contract_terms,
spec,
@@ -230,7 +238,7 @@ using_claim_cb (void *cls,
*/
static void
post_using_templates_cb (void *cls,
- const struct TALER_MERCHANT_PostOrdersReply *por)
+ const struct TALER_MERCHANT_PostTemplatesResponse *por)
{
struct PostUsingTemplatesState *tis = cls;
@@ -331,16 +339,25 @@ post_using_templates_cb (void *cls,
TALER_TESTING_interpreter_next (tis->is);
return;
}
- if (NULL ==
- (tis->och = TALER_MERCHANT_order_claim (
- TALER_TESTING_interpreter_get_context (tis->is),
- tis->merchant_url,
- tis->order_id,
- &tis->nonce,
- &tis->claim_token,
- &using_claim_cb,
- tis)))
+ tis->och = TALER_MERCHANT_post_orders_claim_create (
+ TALER_TESTING_interpreter_get_context (tis->is),
+ tis->merchant_url,
+ tis->order_id,
+ &tis->nonce);
+ if (NULL == tis->och)
TALER_TESTING_FAIL (tis->is);
+ TALER_MERCHANT_post_orders_claim_set_options (
+ tis->och,
+ TALER_MERCHANT_post_orders_claim_option_token (
+ &tis->claim_token));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_orders_claim_start (tis->och,
+ &using_claim_cb,
+ tis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -384,30 +401,39 @@ post_using_templates_run (void *cls,
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&tis->nonce,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ tis->iph = TALER_MERCHANT_post_templates_create (
+ TALER_TESTING_interpreter_get_context (is),
+ tis->merchant_url,
+ template_id);
+ GNUNET_assert (NULL != tis->iph);
if (NULL != tis->details)
{
- tis->iph = TALER_MERCHANT_using_templates_post2 (
- TALER_TESTING_interpreter_get_context (is),
- tis->merchant_url,
- template_id,
- tis->details,
- &post_using_templates_cb,
- tis);
+ TALER_MERCHANT_post_templates_set_options (
+ tis->iph,
+ TALER_MERCHANT_post_templates_option_details (
+ tis->details));
}
else
{
- tis->iph = TALER_MERCHANT_using_templates_post (
- TALER_TESTING_interpreter_get_context (is),
- tis->merchant_url,
- template_id,
- tis->summary,
- TALER_amount_is_valid (&tis->amount)
- ? &tis->amount
- : NULL,
- &post_using_templates_cb,
- tis);
+ if (NULL != tis->summary)
+ TALER_MERCHANT_post_templates_set_options (
+ tis->iph,
+ TALER_MERCHANT_post_templates_option_summary (
+ tis->summary));
+ if (TALER_amount_is_valid (&tis->amount))
+ TALER_MERCHANT_post_templates_set_options (
+ tis->iph,
+ TALER_MERCHANT_post_templates_option_amount (
+ &tis->amount));
+ }
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_templates_start (tis->iph,
+ &post_using_templates_cb,
+ tis);
+ GNUNET_assert (TALER_EC_NONE == ec);
}
- GNUNET_assert (NULL != tis->iph);
}
@@ -467,7 +493,7 @@ post_using_templates_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /using-templates operation did not complete\n");
- TALER_MERCHANT_using_templates_post_cancel (tis->iph);
+ TALER_MERCHANT_post_templates_cancel (tis->iph);
}
json_decref (tis->order_terms);
json_decref (tis->contract_terms);
diff --git a/src/testing/testing_api_cmd_post_webhooks.c b/src/testing/testing_api_cmd_post_webhooks.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-webhooks-new.h>
/**
@@ -37,7 +38,7 @@ struct PostWebhooksState
/**
* Handle for a "GET webhook" request.
*/
- struct TALER_MERCHANT_WebhooksPostHandle *iph;
+ struct TALER_MERCHANT_PostPrivateWebhooksHandle *iph;
/**
* The interpreter state.
@@ -95,22 +96,22 @@ struct PostWebhooksState
*/
static void
post_webhooks_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr)
+ const struct TALER_MERCHANT_PostPrivateWebhooksResponse *wpr)
{
struct PostWebhooksState *wis = cls;
wis->iph = NULL;
- if (wis->http_status != hr->http_status)
+ if (wis->http_status != wpr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ wpr->hr.http_status,
+ (int) wpr->hr.ec,
TALER_TESTING_interpreter_get_current_label (wis->is));
TALER_TESTING_interpreter_fail (wis->is);
return;
}
- switch (hr->http_status)
+ switch (wpr->hr.http_status)
{
case MHD_HTTP_NO_CONTENT:
break;
@@ -126,7 +127,7 @@ post_webhooks_cb (void *cls,
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for POST /templates.\n",
- hr->http_status);
+ wpr->hr.http_status);
}
TALER_TESTING_interpreter_next (wis->is);
}
@@ -148,7 +149,7 @@ post_webhooks_run (void *cls,
struct PostWebhooksState *wis = cls;
wis->is = is;
- wis->iph = TALER_MERCHANT_webhooks_post (
+ wis->iph = TALER_MERCHANT_post_private_webhooks_create (
TALER_TESTING_interpreter_get_context (is),
wis->merchant_url,
wis->webhook_id,
@@ -156,10 +157,16 @@ post_webhooks_run (void *cls,
wis->url,
wis->http_method,
wis->header_template,
- wis->body_template,
- &post_webhooks_cb,
- wis);
- GNUNET_assert (NULL != wis->iph);
+ wis->body_template);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_webhooks_start (
+ wis->iph,
+ &post_webhooks_cb,
+ wis);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -214,7 +221,7 @@ post_webhooks_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"POST /webhooks operation did not complete\n");
- TALER_MERCHANT_webhooks_post_cancel (wis->iph);
+ TALER_MERCHANT_post_private_webhooks_cancel (wis->iph);
}
GNUNET_free (wis);
}
diff --git a/src/testing/testing_api_cmd_refund_order.c b/src/testing/testing_api_cmd_refund_order.c
@@ -27,6 +27,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-private-orders-ORDER_ID-refund-new.h>
/**
@@ -37,7 +38,7 @@ struct RefundState
/**
* Operation handle for a POST /orders/$ID/refund request.
*/
- struct TALER_MERCHANT_OrderRefundHandle *orh;
+ struct TALER_MERCHANT_PostPrivateOrdersRefundHandle *orh;
/**
* Base URL of the merchant serving the request.
@@ -80,7 +81,7 @@ struct RefundState
*/
static void
refund_cb (void *cls,
- const struct TALER_MERCHANT_RefundResponse *rr)
+ const struct TALER_MERCHANT_PostPrivateOrdersRefundResponse *rr)
{
struct RefundState *ris = cls;
@@ -165,16 +166,21 @@ refund_increase_run (void *cls,
struct RefundState *ris = cls;
ris->is = is;
- ris->orh = TALER_MERCHANT_post_order_refund (
+ ris->orh = TALER_MERCHANT_post_private_orders_refund_create (
TALER_TESTING_interpreter_get_context (is),
ris->merchant_url,
ris->order_id,
&ris->refund_amount,
- ris->reason,
- &refund_cb,
- ris);
- if (NULL == ris->orh)
- TALER_TESTING_FAIL (is);
+ ris->reason);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_private_orders_refund_start (
+ ris->orh,
+ &refund_cb,
+ ris);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -224,7 +230,7 @@ refund_increase_cleanup (void *cls,
if (NULL != ris->orh)
{
TALER_LOG_WARNING ("Refund operation did not complete\n");
- TALER_MERCHANT_post_order_refund_cancel (ris->orh);
+ TALER_MERCHANT_post_private_orders_refund_cancel (ris->orh);
}
GNUNET_free (ris);
}
diff --git a/src/testing/testing_api_cmd_wallet_get_order.c b/src/testing/testing_api_cmd_wallet_get_order.c
@@ -26,6 +26,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-orders-ORDER_ID-new.h>
/**
@@ -46,7 +47,7 @@ struct WalletGetOrderState
/**
* The handle to the current GET /orders/$ORDER_ID request.
*/
- struct TALER_MERCHANT_OrderWalletGetHandle *ogh;
+ struct TALER_MERCHANT_GetOrdersHandle *ogh;
/**
* The interpreter state.
@@ -96,7 +97,7 @@ struct WalletGetOrderState
static void
wallet_get_order_cb (
void *cls,
- const struct TALER_MERCHANT_OrderWalletGetResponse *owgr)
+ const struct TALER_MERCHANT_GetOrdersResponse *owgr)
{
struct WalletGetOrderState *gos = cls;
const struct TALER_MERCHANT_HttpResponse *hr = &owgr->hr;
@@ -274,17 +275,24 @@ wallet_get_order_run (void *cls,
TALER_TESTING_FAIL (is);
gos->is = is;
- gos->ogh = TALER_MERCHANT_wallet_order_get (
+ gos->ogh = TALER_MERCHANT_get_orders_create (
TALER_TESTING_interpreter_get_context (is),
gos->merchant_url,
order_id,
- h_contract,
- GNUNET_TIME_UNIT_ZERO,
- gos->session_id,
- NULL,
- false,
- &wallet_get_order_cb,
- gos);
+ h_contract);
+ if (NULL != gos->session_id)
+ TALER_MERCHANT_get_orders_set_options (
+ gos->ogh,
+ TALER_MERCHANT_get_orders_option_session_id (gos->session_id));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_orders_start (
+ gos->ogh,
+ &wallet_get_order_cb,
+ gos);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -304,7 +312,7 @@ wallet_get_order_cleanup (void *cls,
if (NULL != gos->ogh)
{
TALER_LOG_WARNING ("Get order operation did not complete\n");
- TALER_MERCHANT_wallet_order_get_cancel (gos->ogh);
+ TALER_MERCHANT_get_orders_cancel (gos->ogh);
}
GNUNET_free (gos);
}
@@ -427,7 +435,7 @@ struct WalletPollOrderStartState
/**
* The handle to the current GET /orders/$ORDER_ID request.
*/
- struct TALER_MERCHANT_OrderWalletGetHandle *ogh;
+ struct TALER_MERCHANT_GetOrdersHandle *ogh;
/**
* The interpreter state.
@@ -599,7 +607,7 @@ conclude_task (void *cls)
static void
wallet_poll_order_cb (
void *cls,
- const struct TALER_MERCHANT_OrderWalletGetResponse *owgr)
+ const struct TALER_MERCHANT_GetOrdersResponse *owgr)
{
struct WalletPollOrderStartState *pos = cls;
const struct TALER_MERCHANT_HttpResponse *hr = &owgr->hr;
@@ -674,20 +682,28 @@ wallet_poll_order_start_run (void *cls,
= GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout),
GNUNET_TIME_UNIT_SECONDS);
pos->is = is;
- pos->ogh = TALER_MERCHANT_wallet_order_get (
+ pos->ogh = TALER_MERCHANT_get_orders_create (
TALER_TESTING_interpreter_get_context (is),
pos->merchant_url,
order_id,
- h_contract,
- pos->timeout,
- pos->session_id,
- pos->wait_for_refund
- ? &pos->refund_threshold
- : NULL,
- false, /* await_refund_obtained */
- &wallet_poll_order_cb,
- pos);
- GNUNET_assert (NULL != pos->ogh);
+ h_contract);
+ TALER_MERCHANT_get_orders_set_options (
+ pos->ogh,
+ TALER_MERCHANT_get_orders_option_timeout (pos->timeout),
+ TALER_MERCHANT_get_orders_option_session_id (pos->session_id));
+ if (pos->wait_for_refund)
+ TALER_MERCHANT_get_orders_set_options (
+ pos->ogh,
+ TALER_MERCHANT_get_orders_option_min_refund (pos->refund_threshold));
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_orders_start (
+ pos->ogh,
+ &wallet_poll_order_cb,
+ pos);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
/* We CONTINUE to run the interpreter while the long-polled command
completes asynchronously! */
TALER_TESTING_interpreter_next (pos->is);
@@ -713,7 +729,7 @@ wallet_poll_order_start_cleanup (void *cls,
"Command `%s' was not terminated\n",
TALER_TESTING_interpreter_get_current_label (
pos->is));
- TALER_MERCHANT_wallet_order_get_cancel (pos->ogh);
+ TALER_MERCHANT_get_orders_cancel (pos->ogh);
}
GNUNET_free (pos->already_paid_order_id);
GNUNET_free (pos);
diff --git a/src/testing/testing_api_cmd_wallet_get_template.c b/src/testing/testing_api_cmd_wallet_get_template.c
@@ -24,6 +24,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/get-templates-TEMPLATE_ID-new.h>
/**
* State of a "GET template" wallet CMD.
@@ -33,7 +34,7 @@ struct WalletGetTemplateState
/**
* Handle for a "GET template" request.
*/
- struct TALER_MERCHANT_WalletTemplateGetHandle *igh;
+ struct TALER_MERCHANT_GetTemplatesHandle *igh;
/**
* The interpreter state.
@@ -132,7 +133,7 @@ product_id_matches (const json_t *product,
static void
wallet_get_template_cb (void *cls,
const struct
- TALER_MERCHANT_WalletTemplateGetResponse *tgr)
+ TALER_MERCHANT_GetTemplatesResponse *tgr)
{
struct WalletGetTemplateState *wgs = cls;
@@ -364,13 +365,19 @@ wallet_get_template_run (void *cls,
struct WalletGetTemplateState *wgs = cls;
wgs->is = is;
- wgs->igh = TALER_MERCHANT_wallet_template_get (
+ wgs->igh = TALER_MERCHANT_get_templates_create (
TALER_TESTING_interpreter_get_context (is),
wgs->merchant_url,
- wgs->template_id,
- &wallet_get_template_cb,
- wgs);
- GNUNET_assert (NULL != wgs->igh);
+ wgs->template_id);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_get_templates_start (
+ wgs->igh,
+ &wallet_get_template_cb,
+ wgs);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -391,7 +398,7 @@ wallet_get_template_cleanup (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"GET /templates/$ID operation did not complete\n");
- TALER_MERCHANT_wallet_template_get_cancel (wgs->igh);
+ TALER_MERCHANT_get_templates_cancel (wgs->igh);
}
json_decref (wgs->expected_unit_name_short_i18n);
GNUNET_free (wgs);
diff --git a/src/testing/testing_api_cmd_wallet_post_orders_refund.c b/src/testing/testing_api_cmd_wallet_post_orders_refund.c
@@ -27,6 +27,7 @@
#include <taler/taler_testing_lib.h>
#include "taler/taler_merchant_service.h"
#include "taler/taler_merchant_testing_lib.h"
+#include <taler/taler-merchant/post-orders-ORDER_ID-refund-new.h>
/**
@@ -37,7 +38,7 @@ struct WalletRefundState
/**
* Operation handle for a (public) POST /orders/$ID/refund request.
*/
- struct TALER_MERCHANT_WalletOrderRefundHandle *orh;
+ struct TALER_MERCHANT_PostOrdersRefundHandle *orh;
/**
* Base URL of the merchant serving the request.
@@ -81,7 +82,7 @@ struct WalletRefundState
static void
refund_cb (
void *cls,
- const struct TALER_MERCHANT_WalletRefundResponse *wrr)
+ const struct TALER_MERCHANT_PostOrdersRefundResponse *wrr)
{
struct WalletRefundState *wrs = cls;
@@ -100,15 +101,15 @@ refund_cb (
case MHD_HTTP_OK:
{
struct TALER_Amount refunded_total;
- if (wrr->details.ok.refunds_length > 0)
+ if (wrr->details.ok.num_refunds > 0)
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (
wrr->details.ok.refunds[0].refund_amount.currency,
&refunded_total));
- for (unsigned int i = 0; i < wrr->details.ok.refunds_length; ++i)
+ for (unsigned int i = 0; i < wrr->details.ok.num_refunds; ++i)
{
- const struct TALER_MERCHANT_RefundDetail *refund
- = &wrr->details.ok.refunds[wrr->details.ok.refunds_length - 1 - i];
+ const struct TALER_MERCHANT_PostOrdersRefundDetail *refund
+ = &wrr->details.ok.refunds[wrr->details.ok.num_refunds - 1 - i];
const struct TALER_TESTING_Command *refund_cmd;
const struct TALER_Amount *expected_amount;
@@ -216,15 +217,20 @@ obtain_refunds_run (void *cls,
}
wrs->is = is;
- wrs->orh = TALER_MERCHANT_wallet_post_order_refund (
+ wrs->orh = TALER_MERCHANT_post_orders_refund_create (
TALER_TESTING_interpreter_get_context (is),
wrs->merchant_url,
order_id,
- h_contract_terms,
- &refund_cb,
- wrs);
- if (NULL == wrs->orh)
- TALER_TESTING_FAIL (is);
+ h_contract_terms);
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = TALER_MERCHANT_post_orders_refund_start (
+ wrs->orh,
+ &refund_cb,
+ wrs);
+ GNUNET_assert (TALER_EC_NONE == ec);
+ }
}
@@ -244,7 +250,7 @@ obtain_refunds_cleanup (void *cls,
if (NULL != wrs->orh)
{
TALER_LOG_WARNING ("Refund operation did not complete\n");
- TALER_MERCHANT_wallet_post_order_refund_cancel (wrs->orh);
+ TALER_MERCHANT_post_orders_refund_cancel (wrs->orh);
}
GNUNET_array_grow (wrs->refunds,
wrs->refunds_length,