merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

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:
Msrc/include/taler/taler-merchant/common.h | 39+++++++++++++++++++++++++++++++++++++++
Msrc/include/taler/taler-merchant/get-management-instances-INSTANCE-new.h | 3++-
Msrc/include/taler/taler-merchant/get-private-donau-new.h | 1+
Msrc/include/taler/taler-merchant/post-management-instances-INSTANCE-auth-new.h | 2+-
Msrc/include/taler/taler-merchant/post-orders-ORDER_ID-claim-new.h | 5+++++
Msrc/include/taler/taler-merchant/post-orders-ORDER_ID-pay-new.h | 11++++++-----
Msrc/include/taler/taler-merchant/post-orders-ORDER_ID-refund.h | 12++++++------
Msrc/include/taler/taler-merchant/post-private-products-PRODUCT_ID-lock-new.h | 8+++-----
Msrc/include/taler/taler-merchant/post-private-transfers.h | 39---------------------------------------
Msrc/include/taler/taler_merchant_service.h | 129++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/include/taler/taler_merchant_testing_lib.h | 3++-
Msrc/lib/Makefile.am | 135+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/lib/merchant_api_common.c | 4++--
Msrc/lib/merchant_api_common.h | 2+-
Asrc/lib/merchant_api_delete-management-instances-INSTANCE-new.c | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-accounts-H_WIRE-new.c | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-donau-DONAU_SERIAL-new.c | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-orders-ORDER_ID-new.c | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-otp-devices-DEVICE_ID-new.c | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-products-PRODUCT_ID-new.c | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-templates-TEMPLATE_ID-new.c | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-tokens-SERIAL-new.c | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-transfers-TID-new.c | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-units-UNIT-new.c | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-webhooks-WEBHOOK_ID-new.c | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-config-new.c | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-management-instances-INSTANCE-new.c | 256+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-management-instances-new.c | 277+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-orders-ORDER_ID-new.c | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-accounts-H_WIRE-new.c | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-accounts-new.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-donau-new.c | 302++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-kyc-new.c | 541+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-orders-ORDER_ID-new.c | 573+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-orders-new.c | 522+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-otp-devices-DEVICE_ID-new.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-otp-devices-new.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-products-PRODUCT_ID-new.c | 270+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-products-new.c | 265+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-statistics-amount-SLUG-new.c | 455+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-statistics-counter-SLUG-new.c | 400+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-templates-TEMPLATE_ID-new.c | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-templates-new.c | 265+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG-new.c | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-transfers-new.c | 417+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-units-UNIT-new.c | 274+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-units-new.c | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-webhooks-WEBHOOK_ID-new.c | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-webhooks-new.c | 265+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-products-IMAGE_HASH-image-new.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-templates-TEMPLATE_ID-new.c | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-management-instances-INSTANCE-new.c | 313+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-accounts-H_WIRE-new.c | 295+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-orders-ORDER_ID-forget-new.c | 279+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-otp-devices-DEVICE_ID-new.c | 284+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-products-PRODUCT_ID-new.c | 465+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-templates-TEMPLATE_ID-new.c | 282+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-units-UNIT-new.c | 378+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-webhooks-WEBHOOK_ID-new.c | 300+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/merchant_api_pay_service.c | 1070-------------------------------------------------------------------------------
Asrc/lib/merchant_api_post-management-instances-INSTANCE-auth-new.c | 254+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-management-instances-new.c | 333+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-abort-new.c | 432+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-claim-new.c | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-paid-new.c | 298+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-pay-new.c | 1020+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-refund-new.c | 370+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-accounts-new.c | 298+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-categories-new.c | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-donau-new.c | 256+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-orders-ORDER_ID-refund-new.c | 267+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-orders-new.c | 510+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-otp-devices-new.c | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-products-PRODUCT_ID-lock-new.c | 314+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-products-new.c | 498+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-templates-new.c | 287+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-token-new.c | 258+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-tokenfamilies-new.c | 357+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-transfers-new.c | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-units-new.c | 312+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-webhooks-new.c | 283+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-templates-TEMPLATE_ID-new.c | 330+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testing/testing_api_cmd_abort_order.c | 48+++++++++++++++++++++++++++---------------------
Msrc/testing/testing_api_cmd_claim_order.c | 37+++++++++++++++++++++++--------------
Msrc/testing/testing_api_cmd_config.c | 27++++++++++++++++-----------
Msrc/testing/testing_api_cmd_delete_account.c | 23+++++++++++++++--------
Msrc/testing/testing_api_cmd_delete_donau_instances.c | 35+++++++++++++++++------------------
Msrc/testing/testing_api_cmd_delete_instance.c | 47+++++++++++++++++++++++++----------------------
Msrc/testing/testing_api_cmd_delete_order.c | 33+++++++++++++++++++--------------
Msrc/testing/testing_api_cmd_delete_otp_device.c | 33++++++++++++++++++++-------------
Msrc/testing/testing_api_cmd_delete_product.c | 33++++++++++++++++++++-------------
Msrc/testing/testing_api_cmd_delete_template.c | 33++++++++++++++++++++-------------
Msrc/testing/testing_api_cmd_delete_transfer.c | 33++++++++++++++++++++-------------
Msrc/testing/testing_api_cmd_delete_unit.c | 28+++++++++++++++-------------
Msrc/testing/testing_api_cmd_delete_webhook.c | 33++++++++++++++++++++-------------
Msrc/testing/testing_api_cmd_forget_order.c | 24+++++++++++++++++-------
Msrc/testing/testing_api_cmd_get_donau_instances.c | 22++++++++++++++--------
Msrc/testing/testing_api_cmd_get_instance.c | 25++++++++++++++++---------
Msrc/testing/testing_api_cmd_get_instances.c | 26+++++++++++++++++---------
Msrc/testing/testing_api_cmd_get_orders.c | 68++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/testing/testing_api_cmd_get_otp_device.c | 23+++++++++++++++--------
Msrc/testing/testing_api_cmd_get_otp_devices.c | 24++++++++++++++++--------
Msrc/testing/testing_api_cmd_get_product.c | 27+++++++++++++++++----------
Msrc/testing/testing_api_cmd_get_product_image.c | 23+++++++++++++++--------
Msrc/testing/testing_api_cmd_get_products.c | 37+++++++++++++++++++++----------------
Msrc/testing/testing_api_cmd_get_statisticsamount.c | 29++++++++++++++++++++---------
Msrc/testing/testing_api_cmd_get_statisticscounter.c | 29++++++++++++++++++++---------
Msrc/testing/testing_api_cmd_get_template.c | 23+++++++++++++++--------
Msrc/testing/testing_api_cmd_get_templates.c | 22++++++++++++++--------
Msrc/testing/testing_api_cmd_get_transfers.c | 42+++++++++++++++++++++++++++---------------
Msrc/testing/testing_api_cmd_get_unit.c | 23+++++++++++++----------
Msrc/testing/testing_api_cmd_get_units.c | 23+++++++++++++----------
Msrc/testing/testing_api_cmd_get_webhook.c | 77+++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/testing/testing_api_cmd_get_webhooks.c | 23+++++++++++++++--------
Msrc/testing/testing_api_cmd_instance_auth.c | 36++++++++++++++++++++++--------------
Msrc/testing/testing_api_cmd_instance_token.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/testing/testing_api_cmd_kyc_get.c | 66+++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/testing/testing_api_cmd_lock_product.c | 57+++++++++++++++++++++++++++------------------------------
Msrc/testing/testing_api_cmd_merchant_get_order.c | 63++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/testing/testing_api_cmd_patch_instance.c | 24+++++++++++++++++-------
Msrc/testing/testing_api_cmd_patch_otp_device.c | 24+++++++++++++++++-------
Msrc/testing/testing_api_cmd_patch_product.c | 93+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/testing/testing_api_cmd_patch_template.c | 24+++++++++++++++++-------
Msrc/testing/testing_api_cmd_patch_unit.c | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/testing/testing_api_cmd_patch_webhook.c | 24+++++++++++++++++-------
Msrc/testing/testing_api_cmd_pay_order.c | 116++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/testing/testing_api_cmd_post_account.c | 35+++++++++++++++++++++++++----------
Msrc/testing/testing_api_cmd_post_categories.c | 33+++++++++++++++++++++++----------
Msrc/testing/testing_api_cmd_post_donau_instances.c | 44+++++++++++++++++++++++++-------------------
Msrc/testing/testing_api_cmd_post_instances.c | 39+++++++++++++++++++++++----------------
Msrc/testing/testing_api_cmd_post_orders.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/testing/testing_api_cmd_post_orders_paid.c | 35++++++++++++++++++++---------------
Msrc/testing/testing_api_cmd_post_otp_devices.c | 40++++++++++++++++++++++++----------------
Msrc/testing/testing_api_cmd_post_products.c | 126++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/testing/testing_api_cmd_post_templates.c | 46+++++++++++++++++++++++++++++-----------------
Msrc/testing/testing_api_cmd_post_tokenfamilies.c | 43++++++++++++++++++++++++++++---------------
Msrc/testing/testing_api_cmd_post_transfers.c | 39++++++++++++++++++++++++++-------------
Msrc/testing/testing_api_cmd_post_units.c | 45++++++++++++++++++++++++++++++---------------
Msrc/testing/testing_api_cmd_post_using_templates.c | 96++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/testing/testing_api_cmd_post_webhooks.c | 33++++++++++++++++++++-------------
Msrc/testing/testing_api_cmd_refund_order.c | 24+++++++++++++++---------
Msrc/testing/testing_api_cmd_wallet_get_order.c | 66+++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/testing/testing_api_cmd_wallet_get_template.c | 23+++++++++++++++--------
Msrc/testing/testing_api_cmd_wallet_post_orders_refund.c | 32+++++++++++++++++++-------------
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, + &gtr, + 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, + &gtr); + 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 = &gtr->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,