merchant

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

commit b29d3a74d6945731e105beb9cf6d0524d2ec8d84
parent 6bba22954678b9ece5ff59a6854cf248cafe01e6
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed,  4 Mar 2026 13:50:59 +0100

rename merchant_api_ files for better consistency, ensure MFA codes are 0-padded to 4+4 digits

Diffstat:
Msrc/backend/taler-merchant-httpd_mfa.c | 3++-
Msrc/backend/taler-merchant-httpd_post-challenge-ID-confirm.c | 2+-
Msrc/backend/taler-merchant-httpd_post-challenge-ID.c | 20+++++++++-----------
Msrc/lib/Makefile.am | 134++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/lib/merchant_api_curl_defaults.c | 2+-
Asrc/lib/merchant_api_delete-management-instances-INSTANCE.c | 236+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-accounts-H_WIRE.c | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-donau-DONAU_SERIAL.c | 199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-orders-ORDER_ID.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-otp-devices-DEVICE_ID.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-products-PRODUCT_ID.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-templates-TEMPLATE_ID.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-tokens-SERIAL.c | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-transfers-TID.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-units-UNIT.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_delete-private-webhooks-WEBHOOK_ID.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/merchant_api_delete_account.c | 185-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_donau_instance.c | 199-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_instance.c | 236-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_instance_token.c | 178-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_order.c | 177-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_otp_device.c | 184-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_product.c | 184-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_template.c | 184-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_transfer.c | 184-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_unit.c | 177-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_delete_webhook.c | 184-------------------------------------------------------------------------------
Asrc/lib/merchant_api_get-config.c | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-management-instances-INSTANCE.c | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-management-instances.c | 263+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-orders-ORDER_ID.c | 306+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-accounts-H_WIRE.c | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-accounts.c | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-donau.c | 299+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-kyc.c | 515+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-orders-ORDER_ID.c | 538+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-orders.c | 442+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-otp-devices-DEVICE_ID.c | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-otp-devices.c | 248+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-products-PRODUCT_ID.c | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-products.c | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-statistics-amount-SLUG.c | 408+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-statistics-counter-SLUG.c | 353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-templates-TEMPLATE_ID.c | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-templates.c | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.c | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-transfers.c | 314+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-units-UNIT.c | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-units.c | 329+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-webhooks-WEBHOOK_ID.c | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-private-webhooks.c | 246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-products-IMAGE_HASH-image.c | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_get-templates-TEMPLATE_ID.c | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/merchant_api_get_account.c | 211-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_accounts.c | 247-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_config.c | 320-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_donau_instance.c | 299-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_instance.c | 235-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_instances.c | 263-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_kyc.c | 515-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_orders.c | 442-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_otp_device.c | 210-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_otp_devices.c | 248-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_product.c | 249-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_product_image.c | 189-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_products.c | 262-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_statistics.c | 717-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_template.c | 201-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_templates.c | 247-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_tokenfamily.c | 218-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_transfers.c | 314-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_unit.c | 249-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_units.c | 329-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_webhook.c | 221-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_get_webhooks.c | 246-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_lock_product.c | 279-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_merchant_get_order.c | 538-------------------------------------------------------------------------------
Asrc/lib/merchant_api_patch-management-instances-INSTANCE.c | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-accounts-H_WIRE.c | 254+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-orders-ORDER_ID-forget.c | 254+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-otp-devices-DEVICE_ID.c | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-products-PRODUCT_ID.c | 346+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-templates-TEMPLATE_ID.c | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-units-UNIT.c | 291++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_patch-private-webhooks-WEBHOOK_ID.c | 254+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/merchant_api_patch_account.c | 254-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_patch_instance.c | 271-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_patch_order_forget.c | 254-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_patch_otp_device.c | 252-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_patch_product.c | 346-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_patch_template.c | 249-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_patch_unit.c | 291------------------------------------------------------------------------------
Dsrc/lib/merchant_api_patch_webhook.c | 254-------------------------------------------------------------------------------
Asrc/lib/merchant_api_pay_service.c | 1070+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-management-instances-INSTANCE-auth.c | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-management-instances.c | 280+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-abort.c | 436+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-claim.c | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-paid.c | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-pay.c | 790+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-orders-ORDER_ID-refund.c | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-accounts.c | 250+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-categories.c | 223+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-donau.c | 237+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-orders-ORDER_ID-refund.c | 246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-orders.c | 297+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-otp-devices.c | 237+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-products-PRODUCT_ID-lock.c | 279+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-products.c | 433+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-templates.c | 495+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-token.c | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-tokenfamilies.c | 257+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-transfers.c | 256+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-units.c | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-private-webhooks.c | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/merchant_api_post-templates-TEMPLATE_ID.c | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/merchant_api_post_account.c | 250-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_categories.c | 223-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_donau_instance.c | 237-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_instance_auth.c | 227-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_instance_token.c | 235-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_instances.c | 280-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_order_abort.c | 436-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_order_claim.c | 242-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_order_paid.c | 271-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_order_pay.c | 790-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_order_refund.c | 246-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_orders.c | 297-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_otp_devices.c | 237-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_products.c | 433-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_templates.c | 495-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_tokenfamilies.c | 257-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_transfers.c | 256-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_units.c | 218-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_using_templates.c | 233-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_post_webhooks.c | 240-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_wallet_get_order.c | 306-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_wallet_get_template.c | 195-------------------------------------------------------------------------------
Dsrc/lib/merchant_api_wallet_post_order_refund.c | 345-------------------------------------------------------------------------------
139 files changed, 19636 insertions(+), 18521 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_mfa.c b/src/backend/taler-merchant-httpd_mfa.c @@ -260,10 +260,11 @@ mfa_challenge_start ( GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, 1000 * 1000 * 100); /* Note: if this is changed, the code in + taler-merchant-httpd_post-challenge-ID.c and taler-merchant-httpd_post-challenge-ID-confirm.c must possibly also be updated! */ GNUNET_asprintf (&code, - "%llu-%llu", + "%04llu-%04llu", challenge_num / 10000, challenge_num % 10000); qs = TMH_db->create_mfa_challenge (TMH_db->cls, diff --git a/src/backend/taler-merchant-httpd_post-challenge-ID-confirm.c b/src/backend/taler-merchant-httpd_post-challenge-ID-confirm.c @@ -84,7 +84,7 @@ TMH_post_challenge_ID_confirm (const struct TMH_RequestHandler *rh, { /* inject hyphen */ GNUNET_asprintf (&xtan, - "%llu-%llu", + "%04llu-%04llu", challenge_num / 10000, challenge_num % 10000); diff --git a/src/backend/taler-merchant-httpd_post-challenge-ID.c b/src/backend/taler-merchant-httpd_post-challenge-ID.c @@ -375,7 +375,15 @@ static void phase_send_challenge (struct MfaState *mfa) { const char *prog = NULL; - + unsigned long long challenge_num; + + challenge_num = (unsigned long long) + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, + 1000 * 1000 * 100); + GNUNET_asprintf (&mfa->code, + "%04llu-%04llu", + challenge_num / 10000, + challenge_num % 10000); switch (mfa->channel) { case TALER_MERCHANT_MFA_CHANNEL_NONE: @@ -386,11 +394,6 @@ phase_send_challenge (struct MfaState *mfa) = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS); mfa->retransmission_date = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS); - GNUNET_asprintf (&mfa->code, - "%llu", - (unsigned long long) - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, - 100000000)); prog = TMH_helper_sms; break; case TALER_MERCHANT_MFA_CHANNEL_EMAIL: @@ -398,11 +401,6 @@ phase_send_challenge (struct MfaState *mfa) = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS); mfa->retransmission_date = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS); - GNUNET_asprintf (&mfa->code, - "%llu", - (unsigned long long) - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, - 100000000)); prog = TMH_helper_email; break; case TALER_MERCHANT_MFA_CHANNEL_TOTP: diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -16,69 +16,71 @@ libtalermerchant_la_LDFLAGS = \ libtalermerchant_la_SOURCES = \ merchant_api_curl_defaults.c merchant_api_curl_defaults.h \ merchant_api_common.c merchant_api_common.h \ - merchant_api_delete_account.c \ - merchant_api_delete_instance.c \ - merchant_api_delete_instance_token.c \ - merchant_api_delete_order.c \ - merchant_api_delete_otp_device.c \ - merchant_api_delete_product.c \ - merchant_api_delete_template.c \ - merchant_api_delete_unit.c \ - merchant_api_delete_transfer.c \ - merchant_api_delete_webhook.c \ - merchant_api_get_account.c \ - merchant_api_get_accounts.c \ - merchant_api_get_config.c \ - merchant_api_get_instance.c \ - merchant_api_get_instances.c \ - merchant_api_get_kyc.c \ - merchant_api_get_orders.c \ - merchant_api_get_otp_device.c \ - merchant_api_get_otp_devices.c \ - merchant_api_get_product.c \ - merchant_api_get_product_image.c \ - merchant_api_get_products.c \ - merchant_api_get_statistics.c \ - merchant_api_get_transfers.c \ - merchant_api_get_template.c \ - merchant_api_get_unit.c \ - merchant_api_get_units.c \ - merchant_api_get_templates.c \ - merchant_api_get_webhook.c \ - merchant_api_get_webhooks.c \ - merchant_api_lock_product.c \ - merchant_api_merchant_get_order.c \ - merchant_api_patch_account.c \ - merchant_api_patch_instance.c \ - merchant_api_patch_order_forget.c \ - merchant_api_patch_otp_device.c \ - merchant_api_patch_product.c \ - merchant_api_patch_template.c \ - merchant_api_patch_unit.c \ - merchant_api_patch_webhook.c \ - merchant_api_post_account.c \ - merchant_api_post_instance_auth.c \ - merchant_api_post_instance_token.c \ - merchant_api_post_instances.c \ - merchant_api_post_orders.c \ - merchant_api_post_order_abort.c \ - merchant_api_post_order_claim.c \ - merchant_api_post_order_paid.c \ - merchant_api_post_order_pay.c \ - merchant_api_post_order_refund.c \ - merchant_api_post_otp_devices.c \ - merchant_api_post_categories.c \ - merchant_api_post_products.c \ - merchant_api_post_units.c \ - merchant_api_post_transfers.c \ - merchant_api_post_templates.c \ - merchant_api_post_tokenfamilies.c \ - merchant_api_post_using_templates.c \ - merchant_api_post_webhooks.c \ - merchant_api_wallet_get_order.c \ - merchant_api_wallet_get_template.c \ - merchant_api_wallet_post_order_refund.c \ - taler_merchant_pay_service.c + merchant_api_delete-management-instances-INSTANCE.c \ + merchant_api_delete-private-accounts-H_WIRE.c \ + merchant_api_delete-private-orders-ORDER_ID.c \ + merchant_api_delete-private-otp-devices-DEVICE_ID.c \ + merchant_api_delete-private-products-PRODUCT_ID.c \ + merchant_api_delete-private-templates-TEMPLATE_ID.c \ + merchant_api_delete-private-tokens-SERIAL.c \ + merchant_api_delete-private-transfers-TID.c \ + merchant_api_delete-private-units-UNIT.c \ + merchant_api_delete-private-webhooks-WEBHOOK_ID.c \ + merchant_api_get-config.c \ + merchant_api_get-management-instances.c \ + merchant_api_get-management-instances-INSTANCE.c \ + merchant_api_get-orders-ORDER_ID.c \ + merchant_api_get-private-accounts.c \ + merchant_api_get-private-accounts-H_WIRE.c \ + merchant_api_get-private-kyc.c \ + merchant_api_get-private-orders.c \ + merchant_api_get-private-orders-ORDER_ID.c \ + merchant_api_get-private-otp-devices.c \ + merchant_api_get-private-otp-devices-DEVICE_ID.c \ + merchant_api_get-private-products.c \ + merchant_api_get-private-products-PRODUCT_ID.c \ + merchant_api_get-private-statistics-amount-SLUG.c \ + merchant_api_get-private-statistics-counter-SLUG.c \ + merchant_api_get-private-templates.c \ + merchant_api_get-private-templates-TEMPLATE_ID.c \ + merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.c \ + merchant_api_get-private-transfers.c \ + merchant_api_get-private-units.c \ + merchant_api_get-private-units-UNIT.c \ + merchant_api_get-private-webhooks.c \ + merchant_api_get-private-webhooks-WEBHOOK_ID.c \ + merchant_api_get-products-IMAGE_HASH-image.c \ + merchant_api_get-templates-TEMPLATE_ID.c \ + merchant_api_patch-management-instances-INSTANCE.c \ + merchant_api_patch-private-accounts-H_WIRE.c \ + merchant_api_patch-private-orders-ORDER_ID-forget.c \ + merchant_api_patch-private-otp-devices-DEVICE_ID.c \ + merchant_api_patch-private-products-PRODUCT_ID.c \ + merchant_api_patch-private-templates-TEMPLATE_ID.c \ + merchant_api_patch-private-units-UNIT.c \ + merchant_api_patch-private-webhooks-WEBHOOK_ID.c \ + merchant_api_pay_service.c \ + merchant_api_post-management-instances.c \ + merchant_api_post-management-instances-INSTANCE-auth.c \ + merchant_api_post-orders-ORDER_ID-abort.c \ + merchant_api_post-orders-ORDER_ID-claim.c \ + merchant_api_post-orders-ORDER_ID-paid.c \ + merchant_api_post-orders-ORDER_ID-pay.c \ + merchant_api_post-orders-ORDER_ID-refund.c \ + merchant_api_post-private-accounts.c \ + merchant_api_post-private-categories.c \ + merchant_api_post-private-orders.c \ + merchant_api_post-private-orders-ORDER_ID-refund.c \ + merchant_api_post-private-otp-devices.c \ + merchant_api_post-private-products.c \ + merchant_api_post-private-products-PRODUCT_ID-lock.c \ + merchant_api_post-private-templates.c \ + merchant_api_post-private-token.c \ + merchant_api_post-private-tokenfamilies.c \ + merchant_api_post-private-transfers.c \ + merchant_api_post-private-units.c \ + merchant_api_post-private-webhooks.c \ + merchant_api_post-templates-TEMPLATE_ID.c libtalermerchant_la_LIBADD = \ -ltalerexchange \ @@ -95,9 +97,9 @@ libtalermerchant_la_LIBADD = \ if HAVE_DONAU libtalermerchant_la_SOURCES += \ - merchant_api_get_donau_instance.c \ - merchant_api_post_donau_instance.c \ - merchant_api_delete_donau_instance.c + merchant_api_get-private-donau.c \ + merchant_api_post-private-donau.c \ + merchant_api_delete-private-donau-DONAU_SERIAL.c libtalermerchant_la_LIBADD += \ -ldonau diff --git a/src/lib/merchant_api_curl_defaults.c b/src/lib/merchant_api_curl_defaults.c @@ -15,7 +15,7 @@ <http://www.gnu.org/licenses/> */ /** - * @file lib/merchant_api_curl_defaults.c + * @file merchant_api_curl_defaults.c * @brief curl easy handle defaults * @author Florian Dold */ diff --git a/src/lib/merchant_api_delete-management-instances-INSTANCE.c b/src/lib/merchant_api_delete-management-instances-INSTANCE.c @@ -0,0 +1,236 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018, 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-management-instances-INSTANCE.c + * @brief Implementation of the DELETE /instance/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /instances/$ID operation. + */ +struct TALER_MERCHANT_InstanceDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_InstanceDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /instances/$ID request. + * + * @param cls the `struct TALER_MERCHANT_InstanceDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_instance_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_InstanceDeleteHandle *idh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + idh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /instances/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for DELETE /instance/ID\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + idh->cb (idh->cb_cls, + &hr); + TALER_MERCHANT_instance_delete_cancel (idh); +} + + +/** + * Delete the private key of an instance of a backend, thereby disabling the + * instance for future requests. Will preserve the other instance data + * (i.e. for taxation). + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param instance_id which instance should be deleted + * @param purge purge instead of just deleting + * @param cb function to call with the + * backend's return + * @param cb_cls closure for @a config_cb + * @return the instances handle; NULL upon error + */ +static struct TALER_MERCHANT_InstanceDeleteHandle * +instance_delete (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + bool purge, + TALER_MERCHANT_InstanceDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_InstanceDeleteHandle *idh; + + idh = GNUNET_new (struct TALER_MERCHANT_InstanceDeleteHandle); + idh->ctx = ctx; + idh->cb = cb; + idh->cb_cls = cb_cls; + if (NULL != instance_id) + { + char *path; + + GNUNET_asprintf (&path, + "management/instances/%s", + instance_id); + idh->url = TALER_url_join (backend_url, + path, + "purge", + (purge) ? "yes" : NULL, + NULL); + GNUNET_free (path); + } + else + { + /* backend_url is already identifying the instance */ + idh->url = TALER_url_join (backend_url, + "private", + "purge", + (purge) ? "yes" : NULL, + NULL); + } + if (NULL == idh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (idh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + idh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (idh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + idh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_instance_finished, + idh); + } + return idh; +} + + +struct TALER_MERCHANT_InstanceDeleteHandle * +TALER_MERCHANT_instance_delete (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + TALER_MERCHANT_InstanceDeleteCallback cb, + void *cb_cls) +{ + return instance_delete (ctx, + backend_url, + instance_id, + false, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_InstanceDeleteHandle * +TALER_MERCHANT_instance_purge (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + TALER_MERCHANT_InstanceDeleteCallback cb, + void *cb_cls) +{ + return instance_delete (ctx, + backend_url, + instance_id, + true, + cb, + cb_cls); +} + + +void +TALER_MERCHANT_instance_delete_cancel ( + struct TALER_MERCHANT_InstanceDeleteHandle *idh) +{ + if (NULL != idh->job) + GNUNET_CURL_job_cancel (idh->job); + GNUNET_free (idh->url); + GNUNET_free (idh); +} diff --git a/src/lib/merchant_api_delete-private-accounts-H_WIRE.c b/src/lib/merchant_api_delete-private-accounts-H_WIRE.c @@ -0,0 +1,185 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-accounts-H_WIRE.c + * @brief Implementation of the DELETE /private/account/$H_WIRE request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP DELETE /accounts/$H_WIRE request. + * + * @param cls the `struct TALER_MERCHANT_AccountDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountDeleteHandle *adh = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountDeleteResponse adr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + adh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /accounts/$H_WIRE response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + break; + default: + /* unexpected response code */ + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for DELETE /account/ID\n", + (unsigned int) response_code, + (int) adr.hr.ec); + break; + } + adh->cb (adh->cb_cls, + &adr); + TALER_MERCHANT_account_delete_cancel (adh); +} + + +struct TALER_MERCHANT_AccountDeleteHandle * +TALER_MERCHANT_account_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MerchantWireHashP *h_wire, + TALER_MERCHANT_AccountDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountDeleteHandle *adh; + + adh = GNUNET_new (struct TALER_MERCHANT_AccountDeleteHandle); + adh->ctx = ctx; + adh->cb = cb; + adh->cb_cls = cb_cls; + { + char h_wire_str[sizeof (*h_wire) * 2]; + char *path; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_wire, + sizeof (*h_wire), + h_wire_str, + sizeof (h_wire_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "private/account/%s", + h_wire_str); + adh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == adh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (adh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + adh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (adh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + adh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_account_finished, + adh); + } + return adh; +} + + +void +TALER_MERCHANT_account_delete_cancel ( + struct TALER_MERCHANT_AccountDeleteHandle *adh) +{ + if (NULL != adh->job) + GNUNET_CURL_job_cancel (adh->job); + GNUNET_free (adh->url); + GNUNET_free (adh); +} diff --git a/src/lib/merchant_api_delete-private-donau-DONAU_SERIAL.c b/src/lib/merchant_api_delete-private-donau-DONAU_SERIAL.c @@ -0,0 +1,198 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * @file merchant_api_delete-private-donau-DONAU_SERIAL.c + * @brief Implementation of the DELETE /donau/$charity_id request of the merchant's HTTP API + * @author Bohdan Potuzhnyi + * @author Vlada Svirsh + */ + +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +/* DONAU RELATED IMPORTS */ +#include "taler/taler_merchant_donau.h" +#include <donau/donau_service.h> + + +/** + * Handle for a DELETE /donau/$charity_id operation. + */ +struct TALER_MERCHANT_DonauInstanceDeleteHandle +{ + /** + * The URL for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_DonauInstanceDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + +/** + * Function called when we're done processing the + * HTTP DELETE /donau/$charity_id request. + * + * @param cls the struct TALER_MERCHANT_DonauInstanceDeleteHandle + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_donau_instance_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + ddh->job = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /donau/$charity_id response with status code %u\n", + (unsigned int) response_code); + + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* Unexpected response */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for DELETE /donau/$charity_id\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + ddh->cb (ddh->cb_cls, + &hr); + TALER_MERCHANT_donau_instance_delete_cancel (ddh); +} + + +/** + * Initiates the DELETE /donau/$charity_id operation. + * + * @param ctx CURL context + * @param backend_url Base URL for the backend + * @param charity_id The ID of the charity to delete + * @param cb Callback function to handle the response + * @param cb_cls Closure for @a cb + * @return the handle for the operation, or NULL on error + */ +struct TALER_MERCHANT_DonauInstanceDeleteHandle * +TALER_MERCHANT_donau_instance_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + uint64_t charity_id, + TALER_MERCHANT_DonauInstanceDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh; + char *charity_id_str; + CURL *eh; + + GNUNET_asprintf (&charity_id_str, + "private/donau/%ld", + charity_id); + if (NULL == charity_id_str) + return NULL; + + ddh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceDeleteHandle); + ddh->ctx = ctx; + ddh->cb = cb; + ddh->cb_cls = cb_cls; + + ddh->url = TALER_url_join (backend_url, + charity_id_str, + NULL); + GNUNET_free (charity_id_str); + + if (NULL == ddh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (ddh); + return NULL; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + ddh->url); + + eh = TALER_MERCHANT_curl_easy_get_ (ddh->url); + GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + "DELETE")); + ddh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_donau_instance_finished, + ddh); + + return ddh; +} + + +/** + * Cancel the DELETE /donau/$charity_id operation. + * + * @param ddh Handle for the operation to cancel. + */ +void +TALER_MERCHANT_donau_instance_delete_cancel ( + struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh) +{ + if (NULL != ddh->job) + GNUNET_CURL_job_cancel (ddh->job); + + GNUNET_free (ddh->url); + GNUNET_free (ddh); +} +\ No newline at end of file diff --git a/src/lib/merchant_api_delete-private-orders-ORDER_ID.c b/src/lib/merchant_api_delete-private-orders-ORDER_ID.c @@ -0,0 +1,177 @@ +/* + This file is part of TALER + Copyright (C) 2020-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-orders-ORDER_ID.c + * @brief Implementation of the DELETE /orders/$ORDER_ID request of the merchant's HTTP API + * @author Jonathan Buchanan + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Handle for a DELETE /orders/$ID operation. + */ +struct TALER_MERCHANT_OrderDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OrderDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP DELETE /orders/$ORDER_ID request. + * + * @param cls the `struct TALER_MERCHANT_OrderDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_order_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderDeleteHandle *odh = cls; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = NULL, + }; + + odh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /orders/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (response); + hr.hint = TALER_JSON_get_error_hint (response); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + break; + case MHD_HTTP_CONFLICT: + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + (unsigned int) response_code); + break; + } + odh->cb (odh->cb_cls, + &hr); + TALER_MERCHANT_order_delete_cancel (odh); +} + + +struct TALER_MERCHANT_OrderDeleteHandle * +TALER_MERCHANT_order_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *order_id, + bool force, + TALER_MERCHANT_OrderDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OrderDeleteHandle *odh; + + odh = GNUNET_new (struct TALER_MERCHANT_OrderDeleteHandle); + odh->ctx = ctx; + odh->cb = cb; + odh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/orders/%s%s", + order_id, + force + ? "?force=yes" + : ""); + + odh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == odh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request url.\n"); + GNUNET_free (odh); + return NULL; + } + + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (odh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + odh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_order_finished, + odh); + } + return odh; +} + + +void +TALER_MERCHANT_order_delete_cancel ( + struct TALER_MERCHANT_OrderDeleteHandle *odh) +{ + if (NULL != odh->job) + GNUNET_CURL_job_cancel (odh->job); + GNUNET_free (odh->url); + GNUNET_free (odh); +} diff --git a/src/lib/merchant_api_delete-private-otp-devices-DEVICE_ID.c b/src/lib/merchant_api_delete-private-otp-devices-DEVICE_ID.c @@ -0,0 +1,184 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-otp-devices-DEVICE_ID.c + * @brief Implementation of the DELETE /otp-devices/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDeviceDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDeviceDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDeviceDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tdh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got DELETE /otp-devices/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + tdh->cb (tdh->cb_cls, + &hr); + TALER_MERCHANT_otp_device_delete_cancel (tdh); +} + + +struct TALER_MERCHANT_OtpDeviceDeleteHandle * +TALER_MERCHANT_otp_device_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + TALER_MERCHANT_OtpDeviceDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh; + + tdh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceDeleteHandle); + tdh->ctx = ctx; + tdh->cb = cb; + tdh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tdh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tdh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tdh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tdh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + tdh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_otp_device_finished, + tdh); + } + return tdh; +} + + +void +TALER_MERCHANT_otp_device_delete_cancel ( + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh) +{ + if (NULL != tdh->job) + GNUNET_CURL_job_cancel (tdh->job); + GNUNET_free (tdh->url); + GNUNET_free (tdh); +} diff --git a/src/lib/merchant_api_delete-private-products-PRODUCT_ID.c b/src/lib/merchant_api_delete-private-products-PRODUCT_ID.c @@ -0,0 +1,184 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018, 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-products-PRODUCT_ID.c + * @brief Implementation of the DELETE /product/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /products/$ID operation. + */ +struct TALER_MERCHANT_ProductDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ProductDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /products/$ID request. + * + * @param cls the `struct TALER_MERCHANT_ProductDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_product_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_ProductDeleteHandle *pdh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + pdh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /products/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + pdh->cb (pdh->cb_cls, + &hr); + TALER_MERCHANT_product_delete_cancel (pdh); +} + + +struct TALER_MERCHANT_ProductDeleteHandle * +TALER_MERCHANT_product_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + TALER_MERCHANT_ProductDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_ProductDeleteHandle *pdh; + + pdh = GNUNET_new (struct TALER_MERCHANT_ProductDeleteHandle); + pdh->ctx = ctx; + pdh->cb = cb; + pdh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/products/%s", + product_id); + pdh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == pdh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (pdh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + pdh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (pdh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + pdh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_product_finished, + pdh); + } + return pdh; +} + + +void +TALER_MERCHANT_product_delete_cancel ( + struct TALER_MERCHANT_ProductDeleteHandle *pdh) +{ + if (NULL != pdh->job) + GNUNET_CURL_job_cancel (pdh->job); + GNUNET_free (pdh->url); + GNUNET_free (pdh); +} diff --git a/src/lib/merchant_api_delete-private-templates-TEMPLATE_ID.c b/src/lib/merchant_api_delete-private-templates-TEMPLATE_ID.c @@ -0,0 +1,184 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-templates-TEMPLATE_ID.c + * @brief Implementation of the DELETE /templates/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /templates/$ID operation. + */ +struct TALER_MERCHANT_TemplateDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TemplateDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /templates/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TemplateDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_template_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TemplateDeleteHandle *tdh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tdh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /templates/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + tdh->cb (tdh->cb_cls, + &hr); + TALER_MERCHANT_template_delete_cancel (tdh); +} + + +struct TALER_MERCHANT_TemplateDeleteHandle * +TALER_MERCHANT_template_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + TALER_MERCHANT_TemplateDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TemplateDeleteHandle *tdh; + + tdh = GNUNET_new (struct TALER_MERCHANT_TemplateDeleteHandle); + tdh->ctx = ctx; + tdh->cb = cb; + tdh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/templates/%s", + template_id); + tdh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tdh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tdh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tdh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + tdh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_template_finished, + tdh); + } + return tdh; +} + + +void +TALER_MERCHANT_template_delete_cancel ( + struct TALER_MERCHANT_TemplateDeleteHandle *tdh) +{ + if (NULL != tdh->job) + GNUNET_CURL_job_cancel (tdh->job); + GNUNET_free (tdh->url); + GNUNET_free (tdh); +} diff --git a/src/lib/merchant_api_delete-private-tokens-SERIAL.c b/src/lib/merchant_api_delete-private-tokens-SERIAL.c @@ -0,0 +1,178 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-tokens-SERIAL.c + * @brief Implementation of the DELETE /instance/$ID/private/token request of the merchant's HTTP API + * @author Martin Schanzenbach + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /instance/$ID/private/token operation. + */ +struct TALER_MERCHANT_InstanceTokenDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_InstanceTokenDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP DELETE /instance/$ID/private/token request. + * + * @param cls the `struct TALER_MERCHANT_TokenDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_token_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse tdr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tdh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /instances/$ID/private/token response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + tdr.ec = TALER_JSON_get_error_code (json); + tdr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + break; + default: + /* unexpected response code */ + tdr.ec = TALER_JSON_get_error_code (json); + tdr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for DELETE /instance/$ID/private/token\n", + (unsigned int) response_code, + (int) tdr.ec); + break; + } + tdh->cb (tdh->cb_cls, + &tdr); + TALER_MERCHANT_instance_token_delete_cancel (tdh); +} + + +struct TALER_MERCHANT_InstanceTokenDeleteHandle * +TALER_MERCHANT_instance_token_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + TALER_MERCHANT_InstanceTokenDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh; + + tdh = GNUNET_new (struct TALER_MERCHANT_InstanceTokenDeleteHandle); + tdh->ctx = ctx; + tdh->cb = cb; + tdh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "instances/%s/private/token", + instance_id); + tdh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tdh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tdh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tdh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + tdh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_token_finished, + tdh); + } + return tdh; +} + + +void +TALER_MERCHANT_instance_token_delete_cancel ( + struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh) +{ + if (NULL != tdh->job) + GNUNET_CURL_job_cancel (tdh->job); + GNUNET_free (tdh->url); + GNUNET_free (tdh); +} diff --git a/src/lib/merchant_api_delete-private-transfers-TID.c b/src/lib/merchant_api_delete-private-transfers-TID.c @@ -0,0 +1,184 @@ +/* + This file is part of TALER + Copyright (C) 2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-transfers-TID.c + * @brief Implementation of the DELETE /transfer/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /transfers/$ID operation. + */ +struct TALER_MERCHANT_TransferDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TransferDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /transfers/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TransferDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_transfer_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TransferDeleteHandle *tdh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tdh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /transfers/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + tdh->cb (tdh->cb_cls, + &hr); + TALER_MERCHANT_transfer_delete_cancel (tdh); +} + + +struct TALER_MERCHANT_TransferDeleteHandle * +TALER_MERCHANT_transfer_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + uint64_t wire_transfer_serial, + TALER_MERCHANT_TransferDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TransferDeleteHandle *tdh; + + tdh = GNUNET_new (struct TALER_MERCHANT_TransferDeleteHandle); + tdh->ctx = ctx; + tdh->cb = cb; + tdh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/transfers/%llu", + (unsigned long long) wire_transfer_serial); + tdh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tdh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tdh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tdh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + tdh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_transfer_finished, + tdh); + } + return tdh; +} + + +void +TALER_MERCHANT_transfer_delete_cancel ( + struct TALER_MERCHANT_TransferDeleteHandle *tdh) +{ + if (NULL != tdh->job) + GNUNET_CURL_job_cancel (tdh->job); + GNUNET_free (tdh->url); + GNUNET_free (tdh); +} diff --git a/src/lib/merchant_api_delete-private-units-UNIT.c b/src/lib/merchant_api_delete-private-units-UNIT.c @@ -0,0 +1,177 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-units-UNIT.c + * @brief Implementation of DELETE /private/units/$ID + * @author Bohdan Potuzhnyi + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> + + +/** + * Handle for a DELETE /private/units/$ID operation. + */ +struct TALER_MERCHANT_UnitDeleteHandle +{ + /** + * Fully qualified request URL. + */ + char *url; + + /** + * In-flight CURL job. + */ + struct GNUNET_CURL_Job *job; + + /** + * Completion callback. + */ + TALER_MERCHANT_UnitDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Called when the HTTP request finishes. + * + * @param cls operation handle + * @param response_code HTTP status (0 on failure) + * @param response parsed JSON reply (NULL if unavailable) + */ +static void +handle_delete_unit_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_UnitDeleteHandle *udh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + udh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "DELETE /private/units finished with status %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + case MHD_HTTP_UNAUTHORIZED: + case MHD_HTTP_FORBIDDEN: + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_CONFLICT: + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response %u/%d for DELETE /private/units\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + udh->cb (udh->cb_cls, + &hr); + TALER_MERCHANT_unit_delete_cancel (udh); +} + + +struct TALER_MERCHANT_UnitDeleteHandle * +TALER_MERCHANT_unit_delete (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *unit_id, + TALER_MERCHANT_UnitDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_UnitDeleteHandle *udh; + CURL *eh; + char *path; + + GNUNET_asprintf (&path, + "private/units/%s", + unit_id); + udh = GNUNET_new (struct TALER_MERCHANT_UnitDeleteHandle); + udh->ctx = ctx; + udh->cb = cb; + udh->cb_cls = cb_cls; + udh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + if (NULL == udh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to build /private/units/%s URL\n", + unit_id); + GNUNET_free (udh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting DELETE on '%s'\n", + udh->url); + eh = TALER_MERCHANT_curl_easy_get_ (udh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + udh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_unit_finished, + udh); + return udh; +} + + +void +TALER_MERCHANT_unit_delete_cancel (struct TALER_MERCHANT_UnitDeleteHandle *udh) +{ + if (NULL != udh->job) + GNUNET_CURL_job_cancel (udh->job); + GNUNET_free (udh->url); + GNUNET_free (udh); +} + + +/* end of merchant_api_delete_unit.c */ diff --git a/src/lib/merchant_api_delete-private-webhooks-WEBHOOK_ID.c b/src/lib/merchant_api_delete-private-webhooks-WEBHOOK_ID.c @@ -0,0 +1,184 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete-private-webhooks-WEBHOOK_ID.c + * @brief Implementation of the DELETE /webhooks/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /webhooks/$ID operation. + */ +struct TALER_MERCHANT_WebhookDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_WebhookDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /webhooks/$ID request. + * + * @param cls the `struct TALER_MERCHANT_WebhookDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_webhook_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WebhookDeleteHandle *wdh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + wdh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /webhooks/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + wdh->cb (wdh->cb_cls, + &hr); + TALER_MERCHANT_webhook_delete_cancel (wdh); +} + + +struct TALER_MERCHANT_WebhookDeleteHandle * +TALER_MERCHANT_webhook_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *webhook_id, + TALER_MERCHANT_WebhookDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WebhookDeleteHandle *wdh; + + wdh = GNUNET_new (struct TALER_MERCHANT_WebhookDeleteHandle); + wdh->ctx = ctx; + wdh->cb = cb; + wdh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/webhooks/%s", + webhook_id); + wdh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == wdh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (wdh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + wdh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (wdh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + wdh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_webhook_finished, + wdh); + } + return wdh; +} + + +void +TALER_MERCHANT_webhook_delete_cancel ( + struct TALER_MERCHANT_WebhookDeleteHandle *wdh) +{ + if (NULL != wdh->job) + GNUNET_CURL_job_cancel (wdh->job); + GNUNET_free (wdh->url); + GNUNET_free (wdh); +} diff --git a/src/lib/merchant_api_delete_account.c b/src/lib/merchant_api_delete_account.c @@ -1,185 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_account.c - * @brief Implementation of the DELETE /private/account/$H_WIRE request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /accounts/$ID operation. - */ -struct TALER_MERCHANT_AccountDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_AccountDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP DELETE /accounts/$H_WIRE request. - * - * @param cls the `struct TALER_MERCHANT_AccountDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_account_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_AccountDeleteHandle *adh = cls; - const json_t *json = response; - struct TALER_MERCHANT_AccountDeleteResponse adr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - adh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /accounts/$H_WIRE response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - break; - default: - /* unexpected response code */ - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for DELETE /account/ID\n", - (unsigned int) response_code, - (int) adr.hr.ec); - break; - } - adh->cb (adh->cb_cls, - &adr); - TALER_MERCHANT_account_delete_cancel (adh); -} - - -struct TALER_MERCHANT_AccountDeleteHandle * -TALER_MERCHANT_account_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_MerchantWireHashP *h_wire, - TALER_MERCHANT_AccountDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_AccountDeleteHandle *adh; - - adh = GNUNET_new (struct TALER_MERCHANT_AccountDeleteHandle); - adh->ctx = ctx; - adh->cb = cb; - adh->cb_cls = cb_cls; - { - char h_wire_str[sizeof (*h_wire) * 2]; - char *path; - char *end; - - end = GNUNET_STRINGS_data_to_string (h_wire, - sizeof (*h_wire), - h_wire_str, - sizeof (h_wire_str)); - *end = '\0'; - GNUNET_asprintf (&path, - "private/account/%s", - h_wire_str); - adh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == adh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (adh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - adh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (adh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - adh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_account_finished, - adh); - } - return adh; -} - - -void -TALER_MERCHANT_account_delete_cancel ( - struct TALER_MERCHANT_AccountDeleteHandle *adh) -{ - if (NULL != adh->job) - GNUNET_CURL_job_cancel (adh->job); - GNUNET_free (adh->url); - GNUNET_free (adh); -} diff --git a/src/lib/merchant_api_delete_donau_instance.c b/src/lib/merchant_api_delete_donau_instance.c @@ -1,198 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2024 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * @file merchant_api_delete_donau_instance.c - * @brief Implementation of the DELETE /donau/$charity_id request of the merchant's HTTP API - * @author Bohdan Potuzhnyi - * @author Vlada Svirsh - */ - -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -/* DONAU RELATED IMPORTS */ -#include "taler/taler_merchant_donau.h" -#include <donau/donau_service.h> - - -/** - * Handle for a DELETE /donau/$charity_id operation. - */ -struct TALER_MERCHANT_DonauInstanceDeleteHandle -{ - /** - * The URL for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_DonauInstanceDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - -/** - * Function called when we're done processing the - * HTTP DELETE /donau/$charity_id request. - * - * @param cls the struct TALER_MERCHANT_DonauInstanceDeleteHandle - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_donau_instance_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - ddh->job = NULL; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /donau/$charity_id response with status code %u\n", - (unsigned int) response_code); - - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_NOT_FOUND: - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* Unexpected response */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for DELETE /donau/$charity_id\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - ddh->cb (ddh->cb_cls, - &hr); - TALER_MERCHANT_donau_instance_delete_cancel (ddh); -} - - -/** - * Initiates the DELETE /donau/$charity_id operation. - * - * @param ctx CURL context - * @param backend_url Base URL for the backend - * @param charity_id The ID of the charity to delete - * @param cb Callback function to handle the response - * @param cb_cls Closure for @a cb - * @return the handle for the operation, or NULL on error - */ -struct TALER_MERCHANT_DonauInstanceDeleteHandle * -TALER_MERCHANT_donau_instance_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - uint64_t charity_id, - TALER_MERCHANT_DonauInstanceDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh; - char *charity_id_str; - CURL *eh; - - GNUNET_asprintf (&charity_id_str, - "private/donau/%ld", - charity_id); - if (NULL == charity_id_str) - return NULL; - - ddh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceDeleteHandle); - ddh->ctx = ctx; - ddh->cb = cb; - ddh->cb_cls = cb_cls; - - ddh->url = TALER_url_join (backend_url, - charity_id_str, - NULL); - GNUNET_free (charity_id_str); - - if (NULL == ddh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (ddh); - return NULL; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - ddh->url); - - eh = TALER_MERCHANT_curl_easy_get_ (ddh->url); - GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - "DELETE")); - ddh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_donau_instance_finished, - ddh); - - return ddh; -} - - -/** - * Cancel the DELETE /donau/$charity_id operation. - * - * @param ddh Handle for the operation to cancel. - */ -void -TALER_MERCHANT_donau_instance_delete_cancel ( - struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh) -{ - if (NULL != ddh->job) - GNUNET_CURL_job_cancel (ddh->job); - - GNUNET_free (ddh->url); - GNUNET_free (ddh); -} -\ No newline at end of file diff --git a/src/lib/merchant_api_delete_instance.c b/src/lib/merchant_api_delete_instance.c @@ -1,236 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018, 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_instance.c - * @brief Implementation of the DELETE /instance/$ID request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /instances/$ID operation. - */ -struct TALER_MERCHANT_InstanceDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_InstanceDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /instances/$ID request. - * - * @param cls the `struct TALER_MERCHANT_InstanceDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_instance_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_InstanceDeleteHandle *idh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - idh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /instances/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for DELETE /instance/ID\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - idh->cb (idh->cb_cls, - &hr); - TALER_MERCHANT_instance_delete_cancel (idh); -} - - -/** - * Delete the private key of an instance of a backend, thereby disabling the - * instance for future requests. Will preserve the other instance data - * (i.e. for taxation). - * - * @param ctx the context - * @param backend_url HTTP base URL for the backend - * @param instance_id which instance should be deleted - * @param purge purge instead of just deleting - * @param cb function to call with the - * backend's return - * @param cb_cls closure for @a config_cb - * @return the instances handle; NULL upon error - */ -static struct TALER_MERCHANT_InstanceDeleteHandle * -instance_delete (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - bool purge, - TALER_MERCHANT_InstanceDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_InstanceDeleteHandle *idh; - - idh = GNUNET_new (struct TALER_MERCHANT_InstanceDeleteHandle); - idh->ctx = ctx; - idh->cb = cb; - idh->cb_cls = cb_cls; - if (NULL != instance_id) - { - char *path; - - GNUNET_asprintf (&path, - "management/instances/%s", - instance_id); - idh->url = TALER_url_join (backend_url, - path, - "purge", - (purge) ? "yes" : NULL, - NULL); - GNUNET_free (path); - } - else - { - /* backend_url is already identifying the instance */ - idh->url = TALER_url_join (backend_url, - "private", - "purge", - (purge) ? "yes" : NULL, - NULL); - } - if (NULL == idh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (idh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - idh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (idh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - idh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_instance_finished, - idh); - } - return idh; -} - - -struct TALER_MERCHANT_InstanceDeleteHandle * -TALER_MERCHANT_instance_delete (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - TALER_MERCHANT_InstanceDeleteCallback cb, - void *cb_cls) -{ - return instance_delete (ctx, - backend_url, - instance_id, - false, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_InstanceDeleteHandle * -TALER_MERCHANT_instance_purge (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - TALER_MERCHANT_InstanceDeleteCallback cb, - void *cb_cls) -{ - return instance_delete (ctx, - backend_url, - instance_id, - true, - cb, - cb_cls); -} - - -void -TALER_MERCHANT_instance_delete_cancel ( - struct TALER_MERCHANT_InstanceDeleteHandle *idh) -{ - if (NULL != idh->job) - GNUNET_CURL_job_cancel (idh->job); - GNUNET_free (idh->url); - GNUNET_free (idh); -} diff --git a/src/lib/merchant_api_delete_instance_token.c b/src/lib/merchant_api_delete_instance_token.c @@ -1,178 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_instance_token.c - * @brief Implementation of the DELETE /instance/$ID/private/token request of the merchant's HTTP API - * @author Martin Schanzenbach - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /instance/$ID/private/token operation. - */ -struct TALER_MERCHANT_InstanceTokenDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_InstanceTokenDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP DELETE /instance/$ID/private/token request. - * - * @param cls the `struct TALER_MERCHANT_TokenDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_token_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse tdr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tdh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /instances/$ID/private/token response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - tdr.ec = TALER_JSON_get_error_code (json); - tdr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - break; - default: - /* unexpected response code */ - tdr.ec = TALER_JSON_get_error_code (json); - tdr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for DELETE /instance/$ID/private/token\n", - (unsigned int) response_code, - (int) tdr.ec); - break; - } - tdh->cb (tdh->cb_cls, - &tdr); - TALER_MERCHANT_instance_token_delete_cancel (tdh); -} - - -struct TALER_MERCHANT_InstanceTokenDeleteHandle * -TALER_MERCHANT_instance_token_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - TALER_MERCHANT_InstanceTokenDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh; - - tdh = GNUNET_new (struct TALER_MERCHANT_InstanceTokenDeleteHandle); - tdh->ctx = ctx; - tdh->cb = cb; - tdh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "instances/%s/private/token", - instance_id); - tdh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tdh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tdh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tdh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - tdh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_token_finished, - tdh); - } - return tdh; -} - - -void -TALER_MERCHANT_instance_token_delete_cancel ( - struct TALER_MERCHANT_InstanceTokenDeleteHandle *tdh) -{ - if (NULL != tdh->job) - GNUNET_CURL_job_cancel (tdh->job); - GNUNET_free (tdh->url); - GNUNET_free (tdh); -} diff --git a/src/lib/merchant_api_delete_order.c b/src/lib/merchant_api_delete_order.c @@ -1,177 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_order.c - * @brief Implementation of the DELETE /orders/$ORDER_ID request of the merchant's HTTP API - * @author Jonathan Buchanan - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - -/** - * Handle for a DELETE /orders/$ID operation. - */ -struct TALER_MERCHANT_OrderDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OrderDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP DELETE /orders/$ORDER_ID request. - * - * @param cls the `struct TALER_MERCHANT_OrderDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_order_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderDeleteHandle *odh = cls; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = NULL, - }; - - odh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /orders/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (response); - hr.hint = TALER_JSON_get_error_hint (response); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - break; - case MHD_HTTP_CONFLICT: - break; - default: - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u\n", - (unsigned int) response_code); - break; - } - odh->cb (odh->cb_cls, - &hr); - TALER_MERCHANT_order_delete_cancel (odh); -} - - -struct TALER_MERCHANT_OrderDeleteHandle * -TALER_MERCHANT_order_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *order_id, - bool force, - TALER_MERCHANT_OrderDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OrderDeleteHandle *odh; - - odh = GNUNET_new (struct TALER_MERCHANT_OrderDeleteHandle); - odh->ctx = ctx; - odh->cb = cb; - odh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/orders/%s%s", - order_id, - force - ? "?force=yes" - : ""); - - odh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == odh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request url.\n"); - GNUNET_free (odh); - return NULL; - } - - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (odh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - odh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_order_finished, - odh); - } - return odh; -} - - -void -TALER_MERCHANT_order_delete_cancel ( - struct TALER_MERCHANT_OrderDeleteHandle *odh) -{ - if (NULL != odh->job) - GNUNET_CURL_job_cancel (odh->job); - GNUNET_free (odh->url); - GNUNET_free (odh); -} diff --git a/src/lib/merchant_api_delete_otp_device.c b/src/lib/merchant_api_delete_otp_device.c @@ -1,184 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_otp_device.c - * @brief Implementation of the DELETE /otp-devices/$ID request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /otp-devices/$ID operation. - */ -struct TALER_MERCHANT_OtpDeviceDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OtpDeviceDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /otp-devices/$ID request. - * - * @param cls the `struct TALER_MERCHANT_OtpDeviceDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_otp_device_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tdh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got DELETE /otp-devices/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - tdh->cb (tdh->cb_cls, - &hr); - TALER_MERCHANT_otp_device_delete_cancel (tdh); -} - - -struct TALER_MERCHANT_OtpDeviceDeleteHandle * -TALER_MERCHANT_otp_device_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *otp_device_id, - TALER_MERCHANT_OtpDeviceDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh; - - tdh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceDeleteHandle); - tdh->ctx = ctx; - tdh->cb = cb; - tdh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/otp-devices/%s", - otp_device_id); - tdh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tdh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tdh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tdh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - tdh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_otp_device_finished, - tdh); - } - return tdh; -} - - -void -TALER_MERCHANT_otp_device_delete_cancel ( - struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh) -{ - if (NULL != tdh->job) - GNUNET_CURL_job_cancel (tdh->job); - GNUNET_free (tdh->url); - GNUNET_free (tdh); -} diff --git a/src/lib/merchant_api_delete_product.c b/src/lib/merchant_api_delete_product.c @@ -1,184 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018, 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_product.c - * @brief Implementation of the DELETE /product/$ID request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /products/$ID operation. - */ -struct TALER_MERCHANT_ProductDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ProductDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /products/$ID request. - * - * @param cls the `struct TALER_MERCHANT_ProductDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_product_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ProductDeleteHandle *pdh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - pdh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /products/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - pdh->cb (pdh->cb_cls, - &hr); - TALER_MERCHANT_product_delete_cancel (pdh); -} - - -struct TALER_MERCHANT_ProductDeleteHandle * -TALER_MERCHANT_product_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - TALER_MERCHANT_ProductDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ProductDeleteHandle *pdh; - - pdh = GNUNET_new (struct TALER_MERCHANT_ProductDeleteHandle); - pdh->ctx = ctx; - pdh->cb = cb; - pdh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/products/%s", - product_id); - pdh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == pdh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (pdh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - pdh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (pdh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - pdh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_product_finished, - pdh); - } - return pdh; -} - - -void -TALER_MERCHANT_product_delete_cancel ( - struct TALER_MERCHANT_ProductDeleteHandle *pdh) -{ - if (NULL != pdh->job) - GNUNET_CURL_job_cancel (pdh->job); - GNUNET_free (pdh->url); - GNUNET_free (pdh); -} diff --git a/src/lib/merchant_api_delete_template.c b/src/lib/merchant_api_delete_template.c @@ -1,184 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_template.c - * @brief Implementation of the DELETE /templates/$ID request of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /templates/$ID operation. - */ -struct TALER_MERCHANT_TemplateDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TemplateDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /templates/$ID request. - * - * @param cls the `struct TALER_MERCHANT_TemplateDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_template_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TemplateDeleteHandle *tdh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tdh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /templates/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - tdh->cb (tdh->cb_cls, - &hr); - TALER_MERCHANT_template_delete_cancel (tdh); -} - - -struct TALER_MERCHANT_TemplateDeleteHandle * -TALER_MERCHANT_template_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *template_id, - TALER_MERCHANT_TemplateDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TemplateDeleteHandle *tdh; - - tdh = GNUNET_new (struct TALER_MERCHANT_TemplateDeleteHandle); - tdh->ctx = ctx; - tdh->cb = cb; - tdh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/templates/%s", - template_id); - tdh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tdh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tdh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tdh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - tdh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_template_finished, - tdh); - } - return tdh; -} - - -void -TALER_MERCHANT_template_delete_cancel ( - struct TALER_MERCHANT_TemplateDeleteHandle *tdh) -{ - if (NULL != tdh->job) - GNUNET_CURL_job_cancel (tdh->job); - GNUNET_free (tdh->url); - GNUNET_free (tdh); -} diff --git a/src/lib/merchant_api_delete_transfer.c b/src/lib/merchant_api_delete_transfer.c @@ -1,184 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_transfer.c - * @brief Implementation of the DELETE /transfer/$ID request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /transfers/$ID operation. - */ -struct TALER_MERCHANT_TransferDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TransferDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /transfers/$ID request. - * - * @param cls the `struct TALER_MERCHANT_TransferDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_transfer_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TransferDeleteHandle *tdh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tdh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /transfers/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - tdh->cb (tdh->cb_cls, - &hr); - TALER_MERCHANT_transfer_delete_cancel (tdh); -} - - -struct TALER_MERCHANT_TransferDeleteHandle * -TALER_MERCHANT_transfer_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - uint64_t wire_transfer_serial, - TALER_MERCHANT_TransferDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TransferDeleteHandle *tdh; - - tdh = GNUNET_new (struct TALER_MERCHANT_TransferDeleteHandle); - tdh->ctx = ctx; - tdh->cb = cb; - tdh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/transfers/%llu", - (unsigned long long) wire_transfer_serial); - tdh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tdh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tdh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tdh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - tdh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_transfer_finished, - tdh); - } - return tdh; -} - - -void -TALER_MERCHANT_transfer_delete_cancel ( - struct TALER_MERCHANT_TransferDeleteHandle *tdh) -{ - if (NULL != tdh->job) - GNUNET_CURL_job_cancel (tdh->job); - GNUNET_free (tdh->url); - GNUNET_free (tdh); -} diff --git a/src/lib/merchant_api_delete_unit.c b/src/lib/merchant_api_delete_unit.c @@ -1,177 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_unit.c - * @brief Implementation of DELETE /private/units/$ID - * @author Bohdan Potuzhnyi - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> - - -/** - * Handle for a DELETE /private/units/$ID operation. - */ -struct TALER_MERCHANT_UnitDeleteHandle -{ - /** - * Fully qualified request URL. - */ - char *url; - - /** - * In-flight CURL job. - */ - struct GNUNET_CURL_Job *job; - - /** - * Completion callback. - */ - TALER_MERCHANT_UnitDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Called when the HTTP request finishes. - * - * @param cls operation handle - * @param response_code HTTP status (0 on failure) - * @param response parsed JSON reply (NULL if unavailable) - */ -static void -handle_delete_unit_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_UnitDeleteHandle *udh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - udh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "DELETE /private/units finished with status %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - case MHD_HTTP_UNAUTHORIZED: - case MHD_HTTP_FORBIDDEN: - case MHD_HTTP_NOT_FOUND: - case MHD_HTTP_CONFLICT: - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response %u/%d for DELETE /private/units\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - udh->cb (udh->cb_cls, - &hr); - TALER_MERCHANT_unit_delete_cancel (udh); -} - - -struct TALER_MERCHANT_UnitDeleteHandle * -TALER_MERCHANT_unit_delete (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *unit_id, - TALER_MERCHANT_UnitDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_UnitDeleteHandle *udh; - CURL *eh; - char *path; - - GNUNET_asprintf (&path, - "private/units/%s", - unit_id); - udh = GNUNET_new (struct TALER_MERCHANT_UnitDeleteHandle); - udh->ctx = ctx; - udh->cb = cb; - udh->cb_cls = cb_cls; - udh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - if (NULL == udh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to build /private/units/%s URL\n", - unit_id); - GNUNET_free (udh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting DELETE on '%s'\n", - udh->url); - eh = TALER_MERCHANT_curl_easy_get_ (udh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - udh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_unit_finished, - udh); - return udh; -} - - -void -TALER_MERCHANT_unit_delete_cancel (struct TALER_MERCHANT_UnitDeleteHandle *udh) -{ - if (NULL != udh->job) - GNUNET_CURL_job_cancel (udh->job); - GNUNET_free (udh->url); - GNUNET_free (udh); -} - - -/* end of merchant_api_delete_unit.c */ diff --git a/src/lib/merchant_api_delete_webhook.c b/src/lib/merchant_api_delete_webhook.c @@ -1,184 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_delete_template.c - * @brief Implementation of the DELETE /webhooks/$ID request of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a DELETE /webhooks/$ID operation. - */ -struct TALER_MERCHANT_WebhookDeleteHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_WebhookDeleteCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /webhooks/$ID request. - * - * @param cls the `struct TALER_MERCHANT_WebhookDeleteHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_delete_webhook_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_WebhookDeleteHandle *wdh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - wdh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /webhooks/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - wdh->cb (wdh->cb_cls, - &hr); - TALER_MERCHANT_webhook_delete_cancel (wdh); -} - - -struct TALER_MERCHANT_WebhookDeleteHandle * -TALER_MERCHANT_webhook_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *webhook_id, - TALER_MERCHANT_WebhookDeleteCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_WebhookDeleteHandle *wdh; - - wdh = GNUNET_new (struct TALER_MERCHANT_WebhookDeleteHandle); - wdh->ctx = ctx; - wdh->cb = cb; - wdh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/webhooks/%s", - webhook_id); - wdh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == wdh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (wdh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - wdh->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (wdh->url); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_DELETE)); - wdh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_delete_webhook_finished, - wdh); - } - return wdh; -} - - -void -TALER_MERCHANT_webhook_delete_cancel ( - struct TALER_MERCHANT_WebhookDeleteHandle *wdh) -{ - if (NULL != wdh->job) - GNUNET_CURL_job_cancel (wdh->job); - GNUNET_free (wdh->url); - GNUNET_free (wdh); -} diff --git a/src/lib/merchant_api_get-config.c b/src/lib/merchant_api_get-config.c @@ -0,0 +1,320 @@ +/* + This file is part of TALER + Copyright (C) 2014-2026 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-config.c + * @brief Implementation of the /config request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Which version of the Taler protocol is implemented + * by this library? Used to determine compatibility. + */ +#define MERCHANT_PROTOCOL_CURRENT 27 + +/** + * How many configs are we backwards-compatible with? + */ +#define MERCHANT_PROTOCOL_AGE 3 + +/** + * How many exchanges do we allow at most per merchant? + */ +#define MAX_EXCHANGES 1024 + +/** + * How many currency specs do we allow at most per merchant? + */ +#define MAX_CURRENCIES 1024 + +/** + * @brief A handle for /config operations + */ +struct TALER_MERCHANT_ConfigGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ConfigCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP /config request. + * + * @param cls the `struct TALER_MERCHANT_ConfigGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_config_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_ConfigGetHandle *vgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_ConfigResponse cr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /config response with status code %u\n", + (unsigned int) response_code); + + vgh->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *jcs; + const json_t *exchanges = NULL; + struct TALER_MERCHANT_ExchangeConfigInfo *eci = NULL; + unsigned int num_eci = 0; + unsigned int nspec; + struct TALER_JSON_ProtocolVersion pv; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_object_const ("currencies", + &jcs), + GNUNET_JSON_spec_array_const ("exchanges", + &exchanges), + GNUNET_JSON_spec_string ("currency", + &cr.details.ok.ci.currency), + TALER_JSON_spec_version ("version", + &pv), + GNUNET_JSON_spec_string ("version", + &cr.details.ok.ci.version), + GNUNET_JSON_spec_end () + }; + + cr.details.ok.compat = TALER_MERCHANT_VC_PROTOCOL_ERROR; + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + cr.details.ok.compat = TALER_MERCHANT_VC_MATCH; + if (MERCHANT_PROTOCOL_CURRENT < pv.current) + { + cr.details.ok.compat |= TALER_MERCHANT_VC_NEWER; + if (MERCHANT_PROTOCOL_CURRENT < pv.current - pv.age) + cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE; + } + if (MERCHANT_PROTOCOL_CURRENT > pv.current) + { + cr.details.ok.compat |= TALER_MERCHANT_VC_OLDER; + if (MERCHANT_PROTOCOL_CURRENT - MERCHANT_PROTOCOL_AGE > pv.current) + cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE; + } + + nspec = (unsigned int) json_object_size (jcs); + if ( (nspec > MAX_CURRENCIES) || + (json_object_size (jcs) != (size_t) nspec) ) + { + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (NULL != exchanges) + { + num_eci = (unsigned int) json_object_size (exchanges); + if ( (num_eci > MAX_EXCHANGES) || + (json_object_size (exchanges) != (size_t) num_eci) ) + { + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + eci = GNUNET_new_array (num_eci, + struct TALER_MERCHANT_ExchangeConfigInfo); + for (unsigned int i = 0; i<num_eci; i++) + { + struct TALER_MERCHANT_ExchangeConfigInfo *ei = &eci[i]; + const json_t *ej = json_array_get (exchanges, + i); + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_string ("currency", + &ei->currency), + GNUNET_JSON_spec_string ("base_url", + &ei->base_url), + GNUNET_JSON_spec_fixed_auto ("master_pub", + &ei->master_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (ej, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_free (eci); + break; + } + } + } + { + struct TALER_CurrencySpecification *cspecs; + unsigned int off = 0; + json_t *obj; + const char *curr; + + cspecs = GNUNET_new_array (nspec, + struct TALER_CurrencySpecification); + cr.details.ok.num_cspecs = nspec; + cr.details.ok.cspecs = cspecs; + cr.details.ok.num_exchanges = (unsigned int) num_eci; + cr.details.ok.exchanges = eci; + json_object_foreach ((json_t *) jcs, curr, obj) + { + struct TALER_CurrencySpecification *cs = &cspecs[off++]; + struct GNUNET_JSON_Specification cspec[] = { + TALER_JSON_spec_currency_specification (curr, + curr, + cs), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (jcs, + cspec, + NULL, NULL)) + { + GNUNET_break_op (0); + cr.hr.http_status = 0; + cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_free (eci); + TALER_CONFIG_free_currencies (off - 1, + cspecs); + break; + } + } + vgh->cb (vgh->cb_cls, + &cr); + GNUNET_free (eci); + TALER_CONFIG_free_currencies (nspec, + cspecs); + } + TALER_MERCHANT_config_get_cancel (vgh); + return; + } + default: + /* unexpected response code */ + cr.hr.ec = TALER_JSON_get_error_code (json); + cr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) cr.hr.ec); + break; + } + vgh->cb (vgh->cb_cls, + &cr); + TALER_MERCHANT_config_get_cancel (vgh); +} + + +struct TALER_MERCHANT_ConfigGetHandle * +TALER_MERCHANT_config_get (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_ConfigCallback config_cb, + void *config_cb_cls) +{ + struct TALER_MERCHANT_ConfigGetHandle *vgh; + CURL *eh; + + vgh = GNUNET_new (struct TALER_MERCHANT_ConfigGetHandle); + vgh->ctx = ctx; + vgh->cb = config_cb; + vgh->cb_cls = config_cb_cls; + vgh->url = TALER_url_join (backend_url, + "config", + NULL); + if (NULL == vgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (vgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + vgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (vgh->url); + vgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_config_finished, + vgh); + return vgh; +} + + +void +TALER_MERCHANT_config_get_cancel (struct TALER_MERCHANT_ConfigGetHandle *vgh) +{ + if (NULL != vgh->job) + { + GNUNET_CURL_job_cancel (vgh->job); + vgh->job = NULL; + } + GNUNET_free (vgh->url); + GNUNET_free (vgh); +} + + +/* end of merchant_api_config_get.c */ diff --git a/src/lib/merchant_api_get-management-instances-INSTANCE.c b/src/lib/merchant_api_get-management-instances-INSTANCE.c @@ -0,0 +1,235 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-management-instances-INSTANCE.c + * @brief Implementation of the GET /instance/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_kyclogic_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /instances/$ID operation. + */ +struct TALER_MERCHANT_InstanceGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_InstanceGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /instances/$ID request. + * + * @param cls the `struct TALER_MERCHANT_InstanceGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_instance_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_InstanceGetHandle *igh = cls; + const json_t *json = response; + struct TALER_MERCHANT_InstanceGetResponse igr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + igh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /instances/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *address; + const json_t *jurisdiction; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ( + "name", + &igr.details.ok.details.name), + GNUNET_JSON_spec_fixed_auto ( + "merchant_pub", + &igr.details.ok.details.merchant_pub), + GNUNET_JSON_spec_object_const ( + "address", + &address), + GNUNET_JSON_spec_object_const ( + "jurisdiction", + &jurisdiction), + GNUNET_JSON_spec_bool ( + "use_stefan", + &igr.details.ok.details.use_stefan), + GNUNET_JSON_spec_relative_time ( + "default_wire_transfer_delay", + &igr.details.ok.details.default_wire_transfer_delay), + GNUNET_JSON_spec_relative_time ( + "default_pay_delay", + &igr.details.ok.details.default_pay_delay), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_relative_time ( + "default_refund_delay", + &igr.details.ok.details.default_refund_delay), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_time_rounder_interval ( + "default_wire_transfer_rounding_interval", + &igr.details.ok.details.default_wire_transfer_rounding_interval), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + igr.details.ok.details.address = address; + igr.details.ok.details.jurisdiction = jurisdiction; + igh->cb (igh->cb_cls, + &igr); + TALER_MERCHANT_instance_get_cancel (igh); + return; + } + case MHD_HTTP_UNAUTHORIZED: + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + /* instance does not exist */ + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) igr.hr.ec); + break; + } + igh->cb (igh->cb_cls, + &igr); + TALER_MERCHANT_instance_get_cancel (igh); +} + + +struct TALER_MERCHANT_InstanceGetHandle * +TALER_MERCHANT_instance_get (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + TALER_MERCHANT_InstanceGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_InstanceGetHandle *igh; + CURL *eh; + + igh = GNUNET_new (struct TALER_MERCHANT_InstanceGetHandle); + igh->ctx = ctx; + igh->cb = cb; + igh->cb_cls = cb_cls; + if (NULL != instance_id) + { + char *path; + + GNUNET_asprintf (&path, + "instances/%s/private", + instance_id); + igh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + else + { + igh->url = TALER_url_join (backend_url, + "private", + NULL); + } + if (NULL == igh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (igh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + igh->url); + eh = TALER_MERCHANT_curl_easy_get_ (igh->url); + igh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_instance_finished, + igh); + return igh; +} + + +void +TALER_MERCHANT_instance_get_cancel ( + struct TALER_MERCHANT_InstanceGetHandle *igh) +{ + if (NULL != igh->job) + GNUNET_CURL_job_cancel (igh->job); + GNUNET_free (igh->url); + GNUNET_free (igh); +} diff --git a/src/lib/merchant_api_get-management-instances.c b/src/lib/merchant_api_get-management-instances.c @@ -0,0 +1,263 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-management-instances.c + * @brief Implementation of the GET /instances request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Maximum number of instances permitted. + */ +#define MAX_INSTANCES 1024 + +/** + * Handle for a GET /instances operation. + */ +struct TALER_MERCHANT_InstancesGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_InstancesGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse instance information from @a ia. + * + * @param json overall reply body + * @param ia JSON array (or NULL!) with instance data + * @param igh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_instances (const json_t *json, + const json_t *ia, + struct TALER_MERCHANT_InstancesGetHandle *igh) +{ + unsigned int iis_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) iis_len) || + (iis_len > MAX_INSTANCES) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_InstanceInformation iis[GNUNET_NZL (iis_len)]; + size_t index; + json_t *value; + struct TALER_MERCHANT_InstancesGetResponse igr = { + .hr.http_status = MHD_HTTP_OK, + .hr.reply = json, + .details.ok.iis_length = iis_len, + .details.ok.iis = iis + }; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_InstanceInformation *ii = &iis[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("name", + &ii->name), + GNUNET_JSON_spec_string ("id", + &ii->id), + GNUNET_JSON_spec_fixed_auto ("merchant_pub", + &ii->merchant_pub), + GNUNET_JSON_spec_array_const ("payment_targets", + &ii->payment_targets), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (size_t i = 0; i<json_array_size (ii->payment_targets); i++) + { + if (! json_is_string (json_array_get (ii->payment_targets, + i))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + } /* for all instances */ + igh->cb (igh->cb_cls, + &igr); + igh->cb = NULL; /* just to be sure */ + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /instances request. + * + * @param cls the `struct TALER_MERCHANT_InstancesGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_instances_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_InstancesGetHandle *igh = cls; + const json_t *json = response; + struct TALER_MERCHANT_InstancesGetResponse igr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + igh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /instances response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *instances; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("instances", + &instances), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_instances (json, + instances, + igh)) + { + TALER_MERCHANT_instances_get_cancel (igh); + return; + } + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) igr.hr.ec); + break; + } + igh->cb (igh->cb_cls, + &igr); + TALER_MERCHANT_instances_get_cancel (igh); +} + + +struct TALER_MERCHANT_InstancesGetHandle * +TALER_MERCHANT_instances_get (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_InstancesGetCallback instances_cb, + void *instances_cb_cls) +{ + struct TALER_MERCHANT_InstancesGetHandle *igh; + CURL *eh; + + igh = GNUNET_new (struct TALER_MERCHANT_InstancesGetHandle); + igh->ctx = ctx; + igh->cb = instances_cb; + igh->cb_cls = instances_cb_cls; + igh->url = TALER_url_join (backend_url, + "management/instances", + NULL); + if (NULL == igh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (igh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + igh->url); + eh = TALER_MERCHANT_curl_easy_get_ (igh->url); + igh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_instances_finished, + igh); + return igh; +} + + +void +TALER_MERCHANT_instances_get_cancel ( + struct TALER_MERCHANT_InstancesGetHandle *igh) +{ + if (NULL != igh->job) + GNUNET_CURL_job_cancel (igh->job); + GNUNET_free (igh->url); + GNUNET_free (igh); +} diff --git a/src/lib/merchant_api_get-orders-ORDER_ID.c b/src/lib/merchant_api_get-orders-ORDER_ID.c @@ -0,0 +1,306 @@ +/* + This file is part of TALER + Copyright (C) 2018, 2020, 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-orders-ORDER_ID.c + * @brief Implementation of the GET /orders/$ID request + * @author Christian Grothoff + * @author Marcello Stanisci + * @author Florian Dold + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * @brief A GET /orders/$ID handle + */ +struct TALER_MERCHANT_OrderWalletGetHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OrderWalletGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Convenience function to call the callback in @a owgh with an error code of + * @a ec and the exchange body being set to @a reply. + * + * @param owgh handle providing callback + * @param ec error code to return to application + * @param reply JSON reply we got from the exchange, can be NULL + */ +static void +cb_failure (struct TALER_MERCHANT_OrderWalletGetHandle *owgh, + enum TALER_ErrorCode ec, + const json_t *reply) +{ + struct TALER_MERCHANT_OrderWalletGetResponse owgr = { + .hr.ec = ec, + .hr.reply = reply + }; + + owgh->cb (owgh->cb_cls, + &owgr); +} + + +/** + * Function called when we're done processing the GET /check-payment request. + * + * @param cls the `struct TALER_MERCHANT_OrderWalletGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, should be NULL + */ +static void +handle_wallet_get_order_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderWalletGetHandle *owgh = cls; + const json_t *json = response; + + owgh->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + { + struct TALER_MERCHANT_OrderWalletGetResponse owgr = { + .hr.reply = json, + .hr.http_status = MHD_HTTP_OK + }; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_bool ("refunded", + &owgr.details.ok.refunded), + GNUNET_JSON_spec_bool ("refund_pending", + &owgr.details.ok.refund_pending), + TALER_JSON_spec_amount_any ("refund_amount", + &owgr.details.ok.refund_amount), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + cb_failure (owgh, + TALER_EC_GENERIC_REPLY_MALFORMED, + json); + TALER_MERCHANT_wallet_order_get_cancel (owgh); + return; + } + owgh->cb (owgh->cb_cls, + &owgr); + GNUNET_JSON_parse_free (spec); + break; + } + case MHD_HTTP_PAYMENT_REQUIRED: + { + struct TALER_MERCHANT_OrderWalletGetResponse owgr = { + .hr.reply = json, + .hr.http_status = MHD_HTTP_PAYMENT_REQUIRED + }; + + /* Status is: unpaid */ + owgr.details.payment_required.taler_pay_uri + = json_string_value (json_object_get (json, + "taler_pay_uri")); + owgr.details.payment_required.already_paid_order_id + = json_string_value (json_object_get (json, + "already_paid_order_id")); + if (NULL == owgr.details.payment_required.taler_pay_uri) + { + GNUNET_break_op (0); + cb_failure (owgh, + TALER_EC_GENERIC_REPLY_MALFORMED, + json); + break; + } + owgh->cb (owgh->cb_cls, + &owgr); + break; + } + default: + { + struct TALER_MERCHANT_OrderWalletGetResponse owgr = { + .hr.reply = json, + .hr.http_status = response_code + }; + + TALER_MERCHANT_parse_error_details_ (response, + response_code, + &owgr.hr); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Checking order status failed with HTTP status code %u/%d\n", + (unsigned int) response_code, + (int) owgr.hr.ec); + GNUNET_break_op (0); + owgh->cb (owgh->cb_cls, + &owgr); + break; + } + } + TALER_MERCHANT_wallet_order_get_cancel (owgh); +} + + +struct TALER_MERCHANT_OrderWalletGetHandle * +TALER_MERCHANT_wallet_order_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *order_id, + const struct TALER_PrivateContractHashP *h_contract, + struct GNUNET_TIME_Relative timeout, + const char *session_id, + const struct TALER_Amount *min_refund, + bool await_refund_obtained, + TALER_MERCHANT_OrderWalletGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OrderWalletGetHandle *owgh; + unsigned int tms; + + GNUNET_assert (NULL != backend_url); + GNUNET_assert (NULL != order_id); + owgh = GNUNET_new (struct TALER_MERCHANT_OrderWalletGetHandle); + owgh->ctx = ctx; + owgh->cb = cb; + owgh->cb_cls = cb_cls; + tms = (unsigned int) (timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); + { + char timeout_ms[32]; + struct GNUNET_CRYPTO_HashAsciiEncoded h_contract_s; + char *path; + + GNUNET_CRYPTO_hash_to_enc (&h_contract->hash, + &h_contract_s); + GNUNET_snprintf (timeout_ms, + sizeof (timeout_ms), + "%u", + tms); + GNUNET_asprintf (&path, + "orders/%s", + order_id); + owgh->url = TALER_url_join (backend_url, + path, + "h_contract", + h_contract_s.encoding, + "session_id", + session_id, + "timeout_ms", + (0 != tms) + ? timeout_ms + : NULL, + "refund", + (NULL != min_refund) + ? TALER_amount2s (min_refund) + : NULL, + "await_refund_obtained", + await_refund_obtained + ? "yes" + : NULL, + NULL); + GNUNET_free (path); + } + if (NULL == owgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (owgh); + return NULL; + } + + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (owgh->url); + if (0 != tms) + { + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 100L))); + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Checking order status at %s\n", + owgh->url); + if (NULL == (owgh->job = + GNUNET_CURL_job_add (ctx, + eh, + &handle_wallet_get_order_finished, + owgh))) + { + GNUNET_break (0); + GNUNET_free (owgh->url); + GNUNET_free (owgh); + return NULL; + } + } + return owgh; +} + + +void +TALER_MERCHANT_wallet_order_get_cancel ( + struct TALER_MERCHANT_OrderWalletGetHandle *owgh) +{ + if (NULL != owgh->job) + { + GNUNET_CURL_job_cancel (owgh->job); + owgh->job = NULL; + } + GNUNET_free (owgh->url); + GNUNET_free (owgh); +} + + +/* end of merchant_api_wallet_get_order.c */ diff --git a/src/lib/merchant_api_get-private-accounts-H_WIRE.c b/src/lib/merchant_api_get-private-accounts-H_WIRE.c @@ -0,0 +1,211 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-accounts-H_WIRE.c + * @brief Implementation of the GET /accounts/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /accounts/$ID request. + * + * @param cls the `struct TALER_MERCHANT_AccountGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /accounts/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("salt", + &tgr.details.ok.ad.salt), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_web_url ("credit_facade_url", + &tgr.details.ok.ad.credit_facade_url), + NULL), + TALER_JSON_spec_full_payto_uri ("payto_uri", + &tgr.details.ok.ad.payto_uri), + GNUNET_JSON_spec_fixed_auto ("h_wire", + &tgr.details.ok.ad.h_wire), + GNUNET_JSON_spec_bool ("active", + &tgr.details.ok.ad.active), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_account_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_account_get_cancel (tgh); +} + + +struct TALER_MERCHANT_AccountGetHandle * +TALER_MERCHANT_account_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const struct TALER_MerchantWireHashP *h_wire, + TALER_MERCHANT_AccountGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_AccountGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char w_str[sizeof (*h_wire) * 2]; + char *path; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_wire, + sizeof (*h_wire), + w_str, + sizeof (w_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "private/accounts/%s", + w_str); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_account_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_account_get_cancel ( + struct TALER_MERCHANT_AccountGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get-private-accounts.c b/src/lib/merchant_api_get-private-accounts.c @@ -0,0 +1,247 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-accounts.c + * @brief Implementation of the GET /accounts request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Maximum number of accounts permitted. + */ +#define MAX_ACCOUNTS 1024 + +/** + * Handle for a GET /accounts operation. + */ +struct TALER_MERCHANT_AccountsGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountsGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse account information from @a ia. + * + * @param ia JSON array (or NULL!) with account data + * @param[in] tgr partially filled response + * @param tgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_accounts (const json_t *ia, + struct TALER_MERCHANT_AccountsGetResponse *tgr, + struct TALER_MERCHANT_AccountsGetHandle *tgh) +{ + unsigned int tmpl_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) tmpl_len) || + (tmpl_len > MAX_ACCOUNTS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_AccountEntry tmpl[GNUNET_NZL (tmpl_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_AccountEntry *ie = &tmpl[index]; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_full_payto_uri ("payto_uri", + &ie->payto_uri), + GNUNET_JSON_spec_fixed_auto ("h_wire", + &ie->h_wire), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.accounts_length = tmpl_len; + tgr->details.ok.accounts = tmpl; + tgh->cb (tgh->cb_cls, + tgr); + tgh->cb = NULL; /* just to be sure */ + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /accounts request. + * + * @param cls the `struct TALER_MERCHANT_AccountsGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_accounts_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountsGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountsGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /accounts response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *accounts; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("accounts", + &accounts), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_accounts (accounts, + &tgr, + tgh)) + { + TALER_MERCHANT_accounts_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_accounts_get_cancel (tgh); +} + + +struct TALER_MERCHANT_AccountsGetHandle * +TALER_MERCHANT_accounts_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_AccountsGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountsGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_AccountsGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + tgh->url = TALER_url_join (backend_url, + "private/accounts", + NULL); + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_accounts_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_accounts_get_cancel ( + struct TALER_MERCHANT_AccountsGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get-private-donau.c b/src/lib/merchant_api_get-private-donau.c @@ -0,0 +1,299 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file merchant_api_get-private-donau.c + * @brief Implementation of the GET /donau request of the merchant's HTTP API + * @author Bohdan Potuzhnyi + * @author Vlada Svirsh + */ + +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +/* DONAU RELATED IMPORTS */ +#include "taler/taler_merchant_donau.h" +#include <donau/donau_service.h> + +/** + * Handle for a GET /donau operation. + */ +struct TALER_MERCHANT_DonauInstanceGetHandle +{ + /** + * The URL for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_DonauInstanceGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + +/** + * Parse Donau instance information from @a ia. + * + * @param ia JSON array (or NULL!) with Donau instance data + * @param igr response to fill + * @param dgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_donau_instances (const json_t *ia, + struct TALER_MERCHANT_DonauInstanceGetResponse *igr, + struct TALER_MERCHANT_DonauInstanceGetHandle *dgh) +{ + unsigned int instances_len = (unsigned int) json_array_size (ia); + struct TALER_MERCHANT_DonauInstanceEntry + instances[GNUNET_NZL (instances_len)]; + size_t index; + json_t *value; + struct DONAU_Keys *donau_keys_ptr = NULL; + + if ((json_array_size (ia) != (size_t) instances_len)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + json_array_foreach (ia, + index, + value) + { + struct TALER_MERCHANT_DonauInstanceEntry *instance = &instances[index]; + const json_t *donau_keys_json = NULL; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint64 ("donau_instance_serial", + &instance->donau_instance_serial), + GNUNET_JSON_spec_string ("donau_url", + &instance->donau_url), + GNUNET_JSON_spec_string ("charity_name", + &instance->charity_name), + GNUNET_JSON_spec_fixed_auto ("charity_pub_key", + &instance->charity_pub_key), + GNUNET_JSON_spec_uint64 ("charity_id", + &instance->charity_id), + TALER_JSON_spec_amount_any ("charity_max_per_year", + &instance->charity_max_per_year), + TALER_JSON_spec_amount_any ("charity_receipts_to_date", + &instance->charity_receipts_to_date), + GNUNET_JSON_spec_int64 ("current_year", + &instance->current_year), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("donau_keys_json", + &donau_keys_json), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* Parse the Donau keys */ + if (NULL != donau_keys_json) + { + donau_keys_ptr = DONAU_keys_from_json (donau_keys_json); + if (NULL == donau_keys_ptr) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to convert donau keys from JSON\n"); + return GNUNET_SYSERR; + } + instance->donau_keys = donau_keys_ptr; + } + } + + igr->details.ok.donau_instances_length = instances_len; + igr->details.ok.donau_instances = instances; + dgh->cb (dgh->cb_cls, + igr); + dgh->cb = NULL; + if (NULL != donau_keys_ptr) + { + DONAU_keys_decref (donau_keys_ptr); + donau_keys_ptr= NULL; + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /donau request. + * + * @param cls the `struct TALER_MERCHANT_DonauInstanceGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_donau_instances_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_DonauInstanceGetHandle *dgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_DonauInstanceGetResponse igr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + dgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /donau response with status code %u\n", + (unsigned int) response_code); + + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *donau_instances; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("donau_instances", + &donau_instances), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, + NULL)) + { + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + + if (GNUNET_OK == + parse_donau_instances (donau_instances, + &igr, + dgh)) + { + TALER_MERCHANT_donau_instances_get_cancel (dgh); + return; + } + + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + + case MHD_HTTP_UNAUTHORIZED: + case MHD_HTTP_NOT_FOUND: + default: + igr.hr.ec = TALER_JSON_get_error_code (json); + igr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) igr.hr.ec); + break; + } + + dgh->cb (dgh->cb_cls, + &igr); + TALER_MERCHANT_donau_instances_get_cancel (dgh); +} + + +/** + * Initiate the GET /donau request. + * + * @param ctx CURL context + * @param backend_url base URL for the backend + * @param cb callback function to handle the response + * @param cb_cls closure for the callback function + * @return the handle for the operation, or NULL on error + */ +struct TALER_MERCHANT_DonauInstanceGetHandle * +TALER_MERCHANT_donau_instances_get (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_DonauInstanceGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_DonauInstanceGetHandle *dgh; + CURL *eh; + + dgh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceGetHandle); + dgh->ctx = ctx; + dgh->cb = cb; + dgh->cb_cls = cb_cls; + dgh->url = TALER_url_join (backend_url, + "private/donau", + NULL); + if (NULL == dgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (dgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + dgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (dgh->url); + dgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_donau_instances_finished, + dgh); + + return dgh; +} + + +/** + * Cancel the GET /donau instances operation. + * + * @param dgh request to cancel. + */ +void +TALER_MERCHANT_donau_instances_get_cancel ( + struct TALER_MERCHANT_DonauInstanceGetHandle *dgh) +{ + if (NULL != dgh->job) + GNUNET_CURL_job_cancel (dgh->job); + GNUNET_free (dgh->url); + GNUNET_free (dgh); +} diff --git a/src/lib/merchant_api_get-private-kyc.c b/src/lib/merchant_api_get-private-kyc.c @@ -0,0 +1,515 @@ +/* + This file is part of TALER + Copyright (C) 2023--2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-kyc.c + * @brief Implementation of the GET /kyc request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Maximum length of the KYC arrays supported. + */ +#define MAX_KYC 1024 + +/** + * Handle for a GET /kyc operation. + */ +struct TALER_MERCHANT_KycGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_KycGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse @a kyc response and call the continuation on success. + * + * @param kyc operation handle + * @param[in,out] kr response details + * @param jkyc array from the reply + * @return #GNUNET_OK on success (callback was called) + */ +static enum GNUNET_GenericReturnValue +parse_kyc (struct TALER_MERCHANT_KycGetHandle *kyc, + struct TALER_MERCHANT_KycResponse *kr, + const json_t *jkyc) +{ + unsigned int num_kycs = (unsigned int) json_array_size (jkyc); + unsigned int num_limits = 0; + unsigned int num_kycauths = 0; + unsigned int pos_limits = 0; + unsigned int pos_kycauths = 0; + + if ( (json_array_size (jkyc) != (size_t) num_kycs) || + (num_kycs > MAX_KYC) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + for (unsigned int i = 0; i<num_kycs; i++) + { + const json_t *jlimits = NULL; + const json_t *jkycauths = NULL; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ( + "limits", + &jlimits), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ( + "payto_kycauths", + &jkycauths), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json_array_get (jkyc, + i), + spec, + NULL, NULL)) + { + GNUNET_break (0); + json_dumpf (json_array_get (jkyc, + i), + stderr, + JSON_INDENT (2)); + return GNUNET_SYSERR; + } + num_limits += json_array_size (jlimits); + num_kycauths += json_array_size (jkycauths); + } + + + { + struct TALER_MERCHANT_AccountKycRedirectDetail kycs[ + GNUNET_NZL (num_kycs)]; + struct TALER_EXCHANGE_AccountLimit limits[ + GNUNET_NZL (num_limits)]; + struct TALER_FullPayto payto_kycauths[ + GNUNET_NZL (num_kycauths)]; + + memset (kycs, + 0, + sizeof (kycs)); + for (unsigned int i = 0; i<num_kycs; i++) + { + struct TALER_MERCHANT_AccountKycRedirectDetail *rd + = &kycs[i]; + const json_t *jlimits = NULL; + const json_t *jkycauths = NULL; + uint32_t hs; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_full_payto_uri ( + "payto_uri", + &rd->payto_uri), + TALER_JSON_spec_web_url ( + "exchange_url", + &rd->exchange_url), + GNUNET_JSON_spec_uint32 ( + "exchange_http_status", + &hs), + GNUNET_JSON_spec_bool ( + "no_keys", + &rd->no_keys), + GNUNET_JSON_spec_bool ( + "auth_conflict", + &rd->auth_conflict), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_ec ( + "exchange_code", + &rd->exchange_code), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ( + "access_token", + &rd->access_token), + &rd->no_access_token), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ( + "limits", + &jlimits), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ( + "payto_kycauths", + &jkycauths), + NULL), + GNUNET_JSON_spec_end () + }; + size_t j; + json_t *jlimit; + json_t *jkycauth; + + if (GNUNET_OK != + GNUNET_JSON_parse (json_array_get (jkyc, + i), + spec, + NULL, NULL)) + { + GNUNET_break (0); + json_dumpf (json_array_get (jkyc, + i), + stderr, + JSON_INDENT (2)); + return GNUNET_SYSERR; + } + rd->exchange_http_status = (unsigned int) hs; + rd->limits = &limits[pos_limits]; + rd->limits_length = json_array_size (jlimits); + json_array_foreach (jlimits, j, jlimit) + { + struct TALER_EXCHANGE_AccountLimit *limit + = &limits[pos_limits]; + struct GNUNET_JSON_Specification jspec[] = { + TALER_JSON_spec_kycte ( + "operation_type", + &limit->operation_type), + GNUNET_JSON_spec_relative_time ( + "timeframe", + &limit->timeframe), + TALER_JSON_spec_amount_any ( + "threshold", + &limit->threshold), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_bool ( + "soft_limit", + &limit->soft_limit), + NULL), + GNUNET_JSON_spec_end () + }; + + GNUNET_assert (pos_limits < num_limits); + limit->soft_limit = false; + if (GNUNET_OK != + GNUNET_JSON_parse (jlimit, + jspec, + NULL, NULL)) + { + GNUNET_break (0); + json_dumpf (json_array_get (jkyc, + i), + stderr, + JSON_INDENT (2)); + return GNUNET_SYSERR; + } + pos_limits++; + } + rd->payto_kycauths = &payto_kycauths[pos_kycauths]; + rd->pkycauth_length = json_array_size (jkycauths); + json_array_foreach (jkycauths, j, jkycauth) + { + GNUNET_assert (pos_kycauths < num_kycauths); + payto_kycauths[pos_kycauths].full_payto + = (char *) json_string_value (jkycauth); + if (NULL == payto_kycauths[pos_kycauths].full_payto) + { + GNUNET_break (0); + json_dumpf (json_array_get (jkyc, + i), + stderr, + JSON_INDENT (2)); + return GNUNET_SYSERR; + } + pos_kycauths++; + } + } + kr->details.ok.kycs = kycs; + kr->details.ok.kycs_length = num_kycs; + kyc->cb (kyc->cb_cls, + kr); + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /kyc request. + * + * @param cls the `struct TALER_MERCHANT_KycGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_kyc_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_KycGetHandle *kyc = cls; + const json_t *json = response; + struct TALER_MERCHANT_KycResponse kr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + kyc->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /kyc response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *jkyc; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("kyc_data", + &jkyc), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + kr.hr.http_status = 0; + kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK != + parse_kyc (kyc, + &kr, + jkyc)) + { + kr.hr.http_status = 0; + kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + /* parse_kyc called the continuation already */ + TALER_MERCHANT_kyc_get_cancel (kyc); + return; + } + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + kr.hr.ec = TALER_JSON_get_error_code (json); + kr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_SERVICE_UNAVAILABLE: + break; + default: + /* unexpected response code */ + kr.hr.ec = TALER_JSON_get_error_code (json); + kr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) kr.hr.ec); + break; + } + kyc->cb (kyc->cb_cls, + &kr); + TALER_MERCHANT_kyc_get_cancel (kyc); +} + + +/** + * Issue a GET KYC request to the backend. + * Returns KYC status of bank accounts. + * + * @param ctx execution context + * @param[in] url URL to use for the request, consumed! + * @param h_wire which bank account to query, NULL for all + * @param exchange_url which exchange to query, NULL for all + * @param lpt target for long polling + * @param timeout how long to wait for a reply + * @param cb function to call with the result + * @param cb_cls closure for @a cb + * @return handle for this operation, NULL upon errors + */ +static struct TALER_MERCHANT_KycGetHandle * +kyc_get (struct GNUNET_CURL_Context *ctx, + char *url, + const struct TALER_MerchantWireHashP *h_wire, + const char *exchange_url, + enum TALER_EXCHANGE_KycLongPollTarget lpt, + struct GNUNET_TIME_Relative timeout, + TALER_MERCHANT_KycGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_KycGetHandle *kyc; + CURL *eh; + char timeout_ms[32]; + char lpt_str[32]; + unsigned long long tms; + + kyc = GNUNET_new (struct TALER_MERCHANT_KycGetHandle); + kyc->ctx = ctx; + kyc->cb = cb; + kyc->cb_cls = cb_cls; + GNUNET_snprintf (lpt_str, + sizeof (lpt_str), + "%d", + (int) lpt); + tms = timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; + GNUNET_snprintf (timeout_ms, + sizeof (timeout_ms), + "%llu", + tms); + kyc->url + = TALER_url_join ( + url, + "kyc", + "h_wire", + NULL == h_wire + ? NULL + : GNUNET_h2s_full (&h_wire->hash), + "exchange_url", + NULL == exchange_url + ? NULL + : exchange_url, + "timeout_ms", + GNUNET_TIME_relative_is_zero (timeout) + ? NULL + : timeout_ms, + "lpt", + TALER_EXCHANGE_KLPT_NONE == lpt + ? NULL + : lpt_str, + NULL); + GNUNET_free (url); + if (NULL == kyc->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (kyc); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + kyc->url); + eh = TALER_MERCHANT_curl_easy_get_ (kyc->url); + if (0 != tms) + { + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 100L))); + } + kyc->job + = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_kyc_finished, + kyc); + return kyc; +} + + +struct TALER_MERCHANT_KycGetHandle * +TALER_MERCHANT_kyc_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MerchantWireHashP *h_wire, + const char *exchange_url, + enum TALER_EXCHANGE_KycLongPollTarget lpt, + struct GNUNET_TIME_Relative timeout, + TALER_MERCHANT_KycGetCallback cb, + void *cb_cls) +{ + char *url; + + GNUNET_asprintf (&url, + "%sprivate/", + backend_url); + return kyc_get (ctx, + url, /* consumed! */ + h_wire, + exchange_url, + lpt, + timeout, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_KycGetHandle * +TALER_MERCHANT_management_kyc_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const struct TALER_MerchantWireHashP *h_wire, + const char *exchange_url, + enum TALER_EXCHANGE_KycLongPollTarget lpt, + struct GNUNET_TIME_Relative timeout, + TALER_MERCHANT_KycGetCallback cb, + void *cb_cls) +{ + char *url; + + GNUNET_asprintf (&url, + "%smanagement/instances/%s/", + backend_url, + instance_id); + return kyc_get (ctx, + url, /* consumed! */ + h_wire, + exchange_url, + lpt, + timeout, + cb, + cb_cls); +} + + +void +TALER_MERCHANT_kyc_get_cancel ( + struct TALER_MERCHANT_KycGetHandle *kyc) +{ + if (NULL != kyc->job) + GNUNET_CURL_job_cancel (kyc->job); + GNUNET_free (kyc->url); + GNUNET_free (kyc); +} diff --git a/src/lib/merchant_api_get-private-orders-ORDER_ID.c b/src/lib/merchant_api_get-private-orders-ORDER_ID.c @@ -0,0 +1,538 @@ +/* + This file is part of TALER + Copyright (C) 2018, 2019, 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-orders-ORDER_ID.c + * @brief Implementation of the GET /private/orders/$ORDER request + * @author Christian Grothoff + * @author Marcello Stanisci + * @author Florian Dold + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Maximum number of refund details we return. + */ +#define MAX_REFUND_DETAILS 1024 + +/** + * Maximum number of wire details we return. + */ +#define MAX_WIRE_DETAILS 1024 + + +/** + * @brief A GET /private/orders/$ORDER handle + */ +struct TALER_MERCHANT_OrderMerchantGetHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OrderMerchantGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the GET /private/orders/$ORDER + * request and we got an HTTP status of OK and the order was unpaid. Parse + * the response and call the callback. + * + * @param omgh handle for the request + * @param[in,out] osr HTTP response we got + */ +static void +handle_unpaid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, + struct TALER_MERCHANT_OrderStatusResponse *osr) +{ + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount_any ( + "total_amount", + &osr->details.ok.details.unpaid.contract_amount), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ( + "already_paid_order_id", + &osr->details.ok.details.unpaid.already_paid_order_id), + NULL), + GNUNET_JSON_spec_string ( + "taler_pay_uri", + &osr->details.ok.details.unpaid.taler_pay_uri), + GNUNET_JSON_spec_string ( + "summary", + &osr->details.ok.details.unpaid.summary), + GNUNET_JSON_spec_timestamp ( + "creation_time", + &osr->details.ok.details.unpaid.creation_time), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (osr->hr.reply, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + osr); + return; + } + osr->details.ok.status = TALER_MERCHANT_OSC_UNPAID; + omgh->cb (omgh->cb_cls, + osr); +} + + +/** + * Function called when we're done processing the GET /private/orders/$ORDER + * request and we got an HTTP status of OK and the order was claimed but not + * paid. Parse the response and call the callback. + * + * @param omgh handle for the request + * @param[in,out] osr HTTP response we got + */ +static void +handle_claimed (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, + struct TALER_MERCHANT_OrderStatusResponse *osr) +{ + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_object_const ( + "contract_terms", + &osr->details.ok.details.claimed.contract_terms), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (osr->hr.reply, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + osr); + return; + } + osr->details.ok.status = TALER_MERCHANT_OSC_CLAIMED; + omgh->cb (omgh->cb_cls, + osr); +} + + +/** + * Function called when we're done processing the GET /private/orders/$ORDER + * request and we got an HTTP status of OK and the order was paid. Parse + * the response and call the callback. + * + * @param omgh handle for the request + * @param[in,out] osr HTTP response we got + */ +static void +handle_paid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, + struct TALER_MERCHANT_OrderStatusResponse *osr) +{ + uint32_t hc32; + const json_t *wire_details; + const json_t *refund_details; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_bool ("refunded", + &osr->details.ok.details.paid.refunded), + GNUNET_JSON_spec_bool ("refund_pending", + &osr->details.ok.details.paid.refund_pending), + GNUNET_JSON_spec_bool ("wired", + &osr->details.ok.details.paid.wired), + TALER_JSON_spec_amount_any ("deposit_total", + &osr->details.ok.details.paid.deposit_total), + TALER_JSON_spec_ec ("exchange_code", + &osr->details.ok.details.paid.exchange_ec), + GNUNET_JSON_spec_uint32 ("exchange_http_status", + &hc32), + TALER_JSON_spec_amount_any ("refund_amount", + &osr->details.ok.details.paid.refund_amount), + GNUNET_JSON_spec_object_const ( + "contract_terms", + &osr->details.ok.details.paid.contract_terms), + GNUNET_JSON_spec_array_const ("wire_details", + &wire_details), + GNUNET_JSON_spec_array_const ("refund_details", + &refund_details), + /* Only available since **v14** */ + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_timestamp ("last_payment", + &osr->details.ok.details.paid.last_payment), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (osr->hr.reply, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + osr); + return; + } + osr->details.ok.status = TALER_MERCHANT_OSC_PAID; + + osr->details.ok.details.paid.exchange_hc = (unsigned int) hc32; + { + unsigned int wts_len = (unsigned int) json_array_size (wire_details); + unsigned int ref_len = (unsigned int) json_array_size (refund_details); + + if ( (json_array_size (wire_details) != (size_t) wts_len) || + (wts_len > MAX_WIRE_DETAILS) ) + { + GNUNET_break (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; + omgh->cb (omgh->cb_cls, + osr); + return; + } + if ( (json_array_size (refund_details) != (size_t) ref_len) || + (ref_len > MAX_REFUND_DETAILS) ) + { + GNUNET_break (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; + omgh->cb (omgh->cb_cls, + osr); + return; + } + { + struct TALER_MERCHANT_WireTransfer wts[GNUNET_NZL (wts_len)]; + struct TALER_MERCHANT_RefundOrderDetail ref[GNUNET_NZL (ref_len)]; + + for (unsigned int i = 0; i<wts_len; i++) + { + struct TALER_MERCHANT_WireTransfer *wt = &wts[i]; + const json_t *w = json_array_get (wire_details, + i); + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_web_url ("exchange_url", + &wt->exchange_url), + GNUNET_JSON_spec_fixed_auto ("wtid", + &wt->wtid), + GNUNET_JSON_spec_timestamp ("execution_time", + &wt->execution_time), + TALER_JSON_spec_amount_any ("amount", + &wt->total_amount), + GNUNET_JSON_spec_bool ("confirmed", + &wt->confirmed), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (w, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + osr); + return; + } + } + + for (unsigned int i = 0; i<ref_len; i++) + { + struct TALER_MERCHANT_RefundOrderDetail *ro = &ref[i]; + const json_t *w = json_array_get (refund_details, + i); + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_amount_any ("amount", + &ro->refund_amount), + GNUNET_JSON_spec_string ("reason", + &ro->reason), + GNUNET_JSON_spec_timestamp ("timestamp", + &ro->refund_time), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (w, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + osr->hr.http_status = 0; + osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + osr); + return; + } + } + + osr->details.ok.details.paid.wts = wts; + osr->details.ok.details.paid.wts_len = wts_len; + osr->details.ok.details.paid.refunds = ref; + osr->details.ok.details.paid.refunds_len = ref_len; + omgh->cb (omgh->cb_cls, + osr); + } + } +} + + +/** + * Function called when we're done processing the GET /private/orders/$ORDER + * request. + * + * @param cls the `struct TALER_MERCHANT_OrderMerchantGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_merchant_order_get_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderMerchantGetHandle *omgh = cls; + const json_t *json = response; + const char *order_status; + struct TALER_MERCHANT_OrderStatusResponse osr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + omgh->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + /* see below */ + break; + case MHD_HTTP_ACCEPTED: + /* see below */ + omgh->cb (omgh->cb_cls, + &osr); + TALER_MERCHANT_merchant_order_get_cancel (omgh); + return; + case MHD_HTTP_UNAUTHORIZED: + osr.hr.ec = TALER_JSON_get_error_code (json); + osr.hr.hint = TALER_JSON_get_error_hint (json); + omgh->cb (omgh->cb_cls, + &osr); + TALER_MERCHANT_merchant_order_get_cancel (omgh); + return; + case MHD_HTTP_NOT_FOUND: + osr.hr.ec = TALER_JSON_get_error_code (json); + osr.hr.hint = TALER_JSON_get_error_hint (json); + omgh->cb (omgh->cb_cls, + &osr); + TALER_MERCHANT_merchant_order_get_cancel (omgh); + return; + case MHD_HTTP_GATEWAY_TIMEOUT: + osr.hr.ec = TALER_JSON_get_error_code (json); + osr.hr.hint = TALER_JSON_get_error_hint (json); + omgh->cb (omgh->cb_cls, + &osr); + TALER_MERCHANT_merchant_order_get_cancel (omgh); + return; + default: + osr.hr.ec = TALER_JSON_get_error_code (json); + osr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Polling payment failed with HTTP status code %u/%d\n", + (unsigned int) response_code, + (int) osr.hr.ec); + GNUNET_break_op (0); + omgh->cb (omgh->cb_cls, + &osr); + TALER_MERCHANT_merchant_order_get_cancel (omgh); + return; + } + + order_status = json_string_value (json_object_get (json, + "order_status")); + + if (NULL == order_status) + { + GNUNET_break_op (0); + osr.hr.http_status = 0; + osr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + &osr); + TALER_MERCHANT_merchant_order_get_cancel (omgh); + return; + } + + if (0 == strcmp ("paid", + order_status)) + { + handle_paid (omgh, + &osr); + } + else if (0 == strcmp ("claimed", + order_status)) + { + handle_claimed (omgh, + &osr); + } + else if (0 == strcmp ("unpaid", + order_status)) + { + handle_unpaid (omgh, + &osr); + } + else + { + GNUNET_break_op (0); + osr.hr.http_status = 0; + osr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + omgh->cb (omgh->cb_cls, + &osr); + } + TALER_MERCHANT_merchant_order_get_cancel (omgh); +} + + +struct TALER_MERCHANT_OrderMerchantGetHandle * +TALER_MERCHANT_merchant_order_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *order_id, + const char *session_id, + struct GNUNET_TIME_Relative timeout, + TALER_MERCHANT_OrderMerchantGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OrderMerchantGetHandle *omgh; + unsigned int tms; + + tms = (unsigned int) (timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); + omgh = GNUNET_new (struct TALER_MERCHANT_OrderMerchantGetHandle); + omgh->ctx = ctx; + omgh->cb = cb; + omgh->cb_cls = cb_cls; + { + char *path; + char timeout_ms[32]; + + GNUNET_snprintf (timeout_ms, + sizeof (timeout_ms), + "%u", + tms); + GNUNET_asprintf (&path, + "private/orders/%s", + order_id); + omgh->url = TALER_url_join (backend_url, + path, + "session_id", session_id, + "timeout_ms", (0 != tms) ? timeout_ms : NULL, + NULL); + GNUNET_free (path); + } + if (NULL == omgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (omgh); + return NULL; + } + + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (omgh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (omgh->url); + GNUNET_free (omgh); + return NULL; + } + if (0 != tms) + { + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 100L))); + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Getting order status from %s\n", + omgh->url); + if (NULL == (omgh->job = + GNUNET_CURL_job_add (ctx, + eh, + &handle_merchant_order_get_finished, + omgh))) + { + GNUNET_break (0); + GNUNET_free (omgh->url); + GNUNET_free (omgh); + return NULL; + } + } + return omgh; +} + + +void +TALER_MERCHANT_merchant_order_get_cancel ( + struct TALER_MERCHANT_OrderMerchantGetHandle *omgh) +{ + if (NULL != omgh->job) + { + GNUNET_CURL_job_cancel (omgh->job); + omgh->job = NULL; + } + GNUNET_free (omgh->url); + GNUNET_free (omgh); +} + + +/* end of merchant_api_merchant_get_order.c */ diff --git a/src/lib/merchant_api_get-private-orders.c b/src/lib/merchant_api_get-private-orders.c @@ -0,0 +1,442 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-orders.c + * @brief Implementation of the GET /private/orders request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Maximum number of orders we return. + */ +#define MAX_ORDERS 1024 + +/** + * Handle for a GET /orders operation. + */ +struct TALER_MERCHANT_OrdersGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OrdersGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse order information from @a ia. + * + * @param ia JSON array (or NULL!) with order data + * @param[in] ogr response to fill + * @param ogh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_orders (const json_t *ia, + struct TALER_MERCHANT_OrdersGetResponse *ogr, + struct TALER_MERCHANT_OrdersGetHandle *ogh) +{ + unsigned int oes_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) oes_len) || + (oes_len > MAX_ORDERS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_OrderEntry oes[GNUNET_NZL (oes_len)]; + size_t index; + json_t *value; + + memset (oes, + 0, + sizeof (oes)); + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_OrderEntry *ie = &oes[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("order_id", + &ie->order_id), + GNUNET_JSON_spec_timestamp ("timestamp", + &ie->timestamp), + GNUNET_JSON_spec_uint64 ("row_id", + &ie->order_serial), + TALER_JSON_spec_amount_any ("amount", + &ie->amount), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_amount_any ("refund_amount", + &ie->refund_amount), + NULL), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_amount_any ("pending_refund_amount", + &ie->pending_refund_amount), + NULL), + GNUNET_JSON_spec_string ("summary", + &ie->summary), + GNUNET_JSON_spec_bool ("refundable", + &ie->refundable), + GNUNET_JSON_spec_bool ("paid", + &ie->paid), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + ogr->details.ok.orders_length = oes_len; + ogr->details.ok.orders = oes; + ogh->cb (ogh->cb_cls, + ogr); + ogh->cb = NULL; /* just to be sure */ + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /orders request. + * + * @param cls the `struct TALER_MERCHANT_OrdersGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_orders_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrdersGetHandle *ogh = cls; + const json_t *json = response; + struct TALER_MERCHANT_OrdersGetResponse ogr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + ogh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /orders response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *orders; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("orders", + &orders), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + ogr.hr.http_status = 0; + ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_orders (orders, + &ogr, + ogh)) + { + TALER_MERCHANT_orders_get_cancel (ogh); + return; + } + ogr.hr.http_status = 0; + ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + ogr.hr.ec = TALER_JSON_get_error_code (json); + ogr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + ogr.hr.ec = TALER_JSON_get_error_code (json); + ogr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + ogr.hr.ec = TALER_JSON_get_error_code (json); + ogr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) ogr.hr.ec); + break; + } + ogh->cb (ogh->cb_cls, + &ogr); + TALER_MERCHANT_orders_get_cancel (ogh); +} + + +struct TALER_MERCHANT_OrdersGetHandle * +TALER_MERCHANT_orders_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_OrdersGetCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_orders_get2 (ctx, + backend_url, + TALER_EXCHANGE_YNA_ALL, + TALER_EXCHANGE_YNA_ALL, + TALER_EXCHANGE_YNA_ALL, + GNUNET_TIME_UNIT_FOREVER_TS, + UINT64_MAX, + -20, /* default is most recent 20 entries */ + GNUNET_TIME_UNIT_ZERO, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_OrdersGetHandle * +TALER_MERCHANT_orders_get2 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + enum TALER_EXCHANGE_YesNoAll paid, + enum TALER_EXCHANGE_YesNoAll refunded, + enum TALER_EXCHANGE_YesNoAll wired, + struct GNUNET_TIME_Timestamp date, + uint64_t start_row, + int64_t delta, + struct GNUNET_TIME_Relative timeout, + TALER_MERCHANT_OrdersGetCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_orders_get3 ( + ctx, + backend_url, + paid, + refunded, + wired, + NULL, + NULL, + date, + start_row, + delta, + timeout, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_OrdersGetHandle * +TALER_MERCHANT_orders_get3 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + enum TALER_EXCHANGE_YesNoAll paid, + enum TALER_EXCHANGE_YesNoAll refunded, + enum TALER_EXCHANGE_YesNoAll wired, + const char *session_id, + const char *fulfillment_url, + struct GNUNET_TIME_Timestamp date, + uint64_t start_row, + int64_t delta, + struct GNUNET_TIME_Relative timeout, + TALER_MERCHANT_OrdersGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OrdersGetHandle *ogh; + CURL *eh; + unsigned int tms = timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; + + GNUNET_assert (NULL != backend_url); + if ( (delta > MAX_ORDERS) || + (delta < -MAX_ORDERS) ) + { + GNUNET_break (0); + return NULL; + } + if (0 == delta) + { + GNUNET_break (0); + return NULL; + } + ogh = GNUNET_new (struct TALER_MERCHANT_OrdersGetHandle); + ogh->ctx = ctx; + ogh->cb = cb; + ogh->cb_cls = cb_cls; + + /* build ogh->url with the various optional arguments */ + { + char dstr[30]; + char *fec = NULL; + char *sid = NULL; + bool have_date; + bool have_srow; + char cbuf[30]; + char dbuf[30]; + char tbuf[30]; + + GNUNET_snprintf (tbuf, + sizeof (tbuf), + "%u", + tms); + GNUNET_snprintf (dbuf, + sizeof (dbuf), + "%lld", + (long long) delta); + GNUNET_snprintf (cbuf, + sizeof (cbuf), + "%llu", + (unsigned long long) start_row); + if (NULL != session_id) + (void) GNUNET_STRINGS_urlencode (strlen (session_id), + session_id, + &sid); + if (NULL != fulfillment_url) + (void) GNUNET_STRINGS_urlencode (strlen (fulfillment_url), + fulfillment_url, + &fec); + GNUNET_snprintf (dstr, + sizeof (dstr), + "%llu", + (unsigned long long) GNUNET_TIME_timestamp_to_s (date)); + if (delta > 0) + { + have_date = ! GNUNET_TIME_absolute_is_zero (date.abs_time); + have_srow = (0 != start_row); + } + else + { + have_date = ! GNUNET_TIME_absolute_is_never (date.abs_time); + have_srow = (UINT64_MAX != start_row); + } + ogh->url = TALER_url_join (backend_url, + "private/orders", + "paid", + (TALER_EXCHANGE_YNA_ALL != paid) + ? TALER_yna_to_string (paid) + : NULL, + "refunded", + (TALER_EXCHANGE_YNA_ALL != refunded) + ? TALER_yna_to_string (refunded) + : NULL, + "wired", + (TALER_EXCHANGE_YNA_ALL != wired) + ? TALER_yna_to_string (wired) + : NULL, + "date_s", + (have_date) + ? dstr + : NULL, + "start", + (have_srow) + ? cbuf + : NULL, + "delta", + (-20 != delta) + ? dbuf + : NULL, + "timeout_ms", + (0 != tms) + ? tbuf + : NULL, + "session_id", + sid, + "fulfillment_url", + fec, + NULL); + GNUNET_free (sid); + GNUNET_free (fec); + } + if (NULL == ogh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (ogh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + ogh->url); + eh = TALER_MERCHANT_curl_easy_get_ (ogh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (ogh->url); + GNUNET_free (ogh); + return NULL; + } + if (0 != tms) + { + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT_MS, + (long) (tms + 100L))); + } + ogh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_orders_finished, + ogh); + return ogh; +} + + +void +TALER_MERCHANT_orders_get_cancel ( + struct TALER_MERCHANT_OrdersGetHandle *ogh) +{ + if (NULL != ogh->job) + GNUNET_CURL_job_cancel (ogh->job); + GNUNET_free (ogh->url); + GNUNET_free (ogh); +} diff --git a/src/lib/merchant_api_get-private-otp-devices-DEVICE_ID.c b/src/lib/merchant_api_get-private-otp-devices-DEVICE_ID.c @@ -0,0 +1,210 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-otp-devices-DEVICE_ID.c + * @brief Implementation of the GET /otp-devices/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDeviceGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDeviceGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDeviceGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_OtpDeviceGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /otp-devices/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + uint32_t alg32; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("otp_device_description", + &tgr.details.ok.otp_device_description), + GNUNET_JSON_spec_uint32 ("otp_algorithm", + &alg32), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("otp_ctr", + &tgr.details.ok.otp_ctr), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("otp_timestamp", + &tgr.details.ok.otp_timestamp_s), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("otp_code", + &tgr.details.ok.otp_code), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.details.ok.otp_alg = + (enum TALER_MerchantConfirmationAlgorithm) alg32; + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_device_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_device_get_cancel (tgh); +} + + +struct TALER_MERCHANT_OtpDeviceGetHandle * +TALER_MERCHANT_otp_device_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + TALER_MERCHANT_OtpDeviceGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_otp_device_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_otp_device_get_cancel ( + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get-private-otp-devices.c b/src/lib/merchant_api_get-private-otp-devices.c @@ -0,0 +1,248 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-otp-devices.c + * @brief Implementation of the GET /otp-devices request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Maximum number of OTP devices we return. + */ +#define MAX_OTP 1024 + + +/** + * Handle for a GET /otp-devices operation. + */ +struct TALER_MERCHANT_OtpDevicesGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicesGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse OTP device information from @a ia. + * + * @param ia JSON array (or NULL!) with otp_device data + * @param[in] tgr partially filled response + * @param tgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_otp_devices (const json_t *ia, + struct TALER_MERCHANT_OtpDevicesGetResponse *tgr, + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh) +{ + unsigned int otp_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) otp_len) || + (otp_len > MAX_OTP) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_OtpDeviceEntry otp[GNUNET_NZL (otp_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_OtpDeviceEntry *ie = &otp[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("otp_device_id", + &ie->otp_device_id), + GNUNET_JSON_spec_string ("device_description", + &ie->otp_device_description), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.otp_devices_length = otp_len; + tgr->details.ok.otp_devices = otp; + tgh->cb (tgh->cb_cls, + tgr); + tgh->cb = NULL; /* just to be sure */ + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /otp-devices request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicesGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_otp_devices_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_OtpDevicesGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /otp-devices response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *otp_devices; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("otp_devices", + &otp_devices), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_otp_devices (otp_devices, + &tgr, + tgh)) + { + TALER_MERCHANT_otp_devices_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_devices_get_cancel (tgh); +} + + +struct TALER_MERCHANT_OtpDevicesGetHandle * +TALER_MERCHANT_otp_devices_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_OtpDevicesGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_OtpDevicesGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + tgh->url = TALER_url_join (backend_url, + "private/otp-devices", + NULL); + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_otp_devices_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_otp_devices_get_cancel ( + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get-private-products-PRODUCT_ID.c b/src/lib/merchant_api_get-private-products-PRODUCT_ID.c @@ -0,0 +1,249 @@ +/* + This file is part of TALER + Copyright (C) 2014--2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-products-PRODUCT_ID.c + * @brief Implementation of the GET /product/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /products/$ID operation. + */ +struct TALER_MERCHANT_ProductGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ProductGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /products/$ID request. + * + * @param cls the `struct TALER_MERCHANT_ProductGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_product_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_ProductGetHandle *pgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_ProductGetResponse pgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + pgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /products/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ( + "product_name", + &pgr.details.ok.product_name), + GNUNET_JSON_spec_string ( + "description", + &pgr.details.ok.description), + GNUNET_JSON_spec_object_const ( + "description_i18n", + &pgr.details.ok.description_i18n), + GNUNET_JSON_spec_string ( + "unit", + &pgr.details.ok.unit), + TALER_JSON_spec_amount_any_array ( + "unit_price", + &pgr.details.ok.unit_price_len, + (struct TALER_Amount **) &pgr.details.ok.unit_price), + TALER_JSON_spec_amount_any ( + "price", + &pgr.details.ok.price), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ( + "image", + &pgr.details.ok.image), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ( + "taxes", + &pgr.details.ok.taxes), + NULL), + GNUNET_JSON_spec_int64 ( + "total_stock", + &pgr.details.ok.total_stock), + GNUNET_JSON_spec_string ( + "unit_total_stock", + &pgr.details.ok.unit_total_stock), + GNUNET_JSON_spec_bool ( + "unit_allow_fraction", + &pgr.details.ok.unit_allow_fraction), + GNUNET_JSON_spec_uint32 ( + "unit_precision_level", + &pgr.details.ok.unit_precision_level), + GNUNET_JSON_spec_uint64 ( + "total_sold", + &pgr.details.ok.total_sold), + GNUNET_JSON_spec_uint64 ( + "total_lost", + &pgr.details.ok.total_lost), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ( + "address", + &pgr.details.ok.location), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_timestamp ( + "next_restock", + &pgr.details.ok.next_restock), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + pgh->cb (pgh->cb_cls, + &pgr); + GNUNET_JSON_parse_free (spec); + TALER_MERCHANT_product_get_cancel (pgh); + return; + } + pgr.hr.http_status = 0; + pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + pgr.hr.ec = TALER_JSON_get_error_code (json); + pgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + pgr.hr.ec = TALER_JSON_get_error_code (json); + pgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + pgr.hr.ec = TALER_JSON_get_error_code (json); + pgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) pgr.hr.ec); + break; + } + pgh->cb (pgh->cb_cls, + &pgr); + TALER_MERCHANT_product_get_cancel (pgh); +} + + +struct TALER_MERCHANT_ProductGetHandle * +TALER_MERCHANT_product_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + TALER_MERCHANT_ProductGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_ProductGetHandle *pgh; + CURL *eh; + + pgh = GNUNET_new (struct TALER_MERCHANT_ProductGetHandle); + pgh->ctx = ctx; + pgh->cb = cb; + pgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/products/%s", + product_id); + pgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == pgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (pgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + pgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (pgh->url); + pgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_product_finished, + pgh); + return pgh; +} + + +void +TALER_MERCHANT_product_get_cancel ( + struct TALER_MERCHANT_ProductGetHandle *pgh) +{ + if (NULL != pgh->job) + GNUNET_CURL_job_cancel (pgh->job); + GNUNET_free (pgh->url); + GNUNET_free (pgh); +} diff --git a/src/lib/merchant_api_get-private-products.c b/src/lib/merchant_api_get-private-products.c @@ -0,0 +1,262 @@ +/* + This file is part of TALER + Copyright (C) 2014-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-products.c + * @brief Implementation of the GET /products request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Maximum number of products we return. + */ +#define MAX_PRODUCTS 1024 + + +/** + * Handle for a GET /products operation. + */ +struct TALER_MERCHANT_ProductsGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ProductsGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse product information from @a ia. + * + * @param json overall JSON reply + * @param ia JSON array (or NULL!) with product data + * @param pgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_products (const json_t *json, + const json_t *ia, + struct TALER_MERCHANT_ProductsGetHandle *pgh) +{ + unsigned int ies_len = json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) ies_len) || + (ies_len > MAX_PRODUCTS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_InventoryEntry ies[GNUNET_NZL (ies_len)]; + size_t index; + json_t *value; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_OK; + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_InventoryEntry *ie = &ies[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("product_id", + &ie->product_id), + GNUNET_JSON_spec_uint64 ("product_serial", + &ie->product_serial), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ret = GNUNET_SYSERR; + continue; + } + if (GNUNET_SYSERR == ret) + break; + } + if (GNUNET_OK == ret) + { + struct TALER_MERCHANT_GetProductsResponse gpr = { + .hr.http_status = MHD_HTTP_OK, + .hr.reply = json, + .details.ok.products_length = ies_len, + .details.ok.products = ies + }; + + pgh->cb (pgh->cb_cls, + &gpr); + pgh->cb = NULL; /* just to be sure */ + } + return ret; + } +} + + +/** + * Function called when we're done processing the + * HTTP /products request. + * + * @param cls the `struct TALER_MERCHANT_ProductsGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_products_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_ProductsGetHandle *pgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_GetProductsResponse gpr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + pgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /products response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *products; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("products", + &products), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + gpr.hr.http_status = 0; + gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_products (json, + products, + pgh)) + { + TALER_MERCHANT_products_get_cancel (pgh); + return; + } + gpr.hr.http_status = 0; + gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + gpr.hr.ec = TALER_JSON_get_error_code (json); + gpr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + gpr.hr.ec = TALER_JSON_get_error_code (json); + gpr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) gpr.hr.ec); + break; + } + pgh->cb (pgh->cb_cls, + &gpr); + TALER_MERCHANT_products_get_cancel (pgh); +} + + +struct TALER_MERCHANT_ProductsGetHandle * +TALER_MERCHANT_products_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_ProductsGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_ProductsGetHandle *pgh; + CURL *eh; + + pgh = GNUNET_new (struct TALER_MERCHANT_ProductsGetHandle); + pgh->ctx = ctx; + pgh->cb = cb; + pgh->cb_cls = cb_cls; + pgh->url = TALER_url_join (backend_url, + "private/products", + NULL); + if (NULL == pgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (pgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + pgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (pgh->url); + pgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_products_finished, + pgh); + return pgh; +} + + +void +TALER_MERCHANT_products_get_cancel ( + struct TALER_MERCHANT_ProductsGetHandle *pgh) +{ + if (NULL != pgh->job) + GNUNET_CURL_job_cancel (pgh->job); + GNUNET_free (pgh->url); + GNUNET_free (pgh); +} diff --git a/src/lib/merchant_api_get-private-statistics-amount-SLUG.c b/src/lib/merchant_api_get-private-statistics-amount-SLUG.c @@ -0,0 +1,408 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-statistics-amount-SLUG.c + * @brief Implementation of the GET /private/statistics-amount/$SLUG request of the merchant's HTTP API + * @author Martin Schanzenbach + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Maximum number of statistics we return + */ +#define MAX_STATISTICS 1024 + +/** + * Handle for a GET /statistics-amount/$SLUG operation. + */ +struct TALER_MERCHANT_StatisticsAmountGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_StatisticsAmountGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse interval information from buckets and intervals. + * + * @param json overall JSON reply + * @param jbuckets JSON array (or NULL!) with bucket data + * @param buckets_description human-readable description for the buckets + * @param jintervals JSON array (or NULL!) with bucket data + * @param intervals_description human-readable description for the intervals + * @param sgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_intervals_and_buckets_amt ( + const json_t *json, + const json_t *jbuckets, + const char *buckets_description, + const json_t *jintervals, + const char *intervals_description, + struct TALER_MERCHANT_StatisticsAmountGetHandle *sgh + ) +{ + unsigned int resp_buckets_len = json_array_size (jbuckets); + unsigned int resp_intervals_len = json_array_size (jintervals); + + if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) || + (json_array_size (jintervals) != (size_t) resp_intervals_len) || + (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_StatisticAmountByBucket resp_buckets[ + GNUNET_NZL (resp_buckets_len)]; + struct TALER_MERCHANT_StatisticAmountByInterval resp_intervals[ + GNUNET_NZL (resp_intervals_len)]; + size_t index; + json_t *value; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_OK; + json_array_foreach (jintervals, index, value) { + struct TALER_MERCHANT_StatisticAmountByInterval *jinterval + = &resp_intervals[index]; + const json_t *amounts_arr; + size_t amounts_len; + + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("start_time", + &jinterval->start_time), + GNUNET_JSON_spec_array_const ("cumulative_amounts", + &amounts_arr), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ret = GNUNET_SYSERR; + continue; + } + if (GNUNET_SYSERR == ret) + break; + amounts_len = json_array_size (amounts_arr); + { + struct TALER_Amount amt_arr[amounts_len]; + size_t aindex; + json_t *avalue; + + jinterval->cumulative_amount_len = amounts_len; + jinterval->cumulative_amounts = amt_arr; + json_array_foreach (amounts_arr, aindex, avalue) { + if (! json_is_string (avalue)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_string_to_amount (json_string_value (avalue), + &amt_arr[aindex])) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + } + } + ret = GNUNET_OK; + json_array_foreach (jbuckets, index, value) { + struct TALER_MERCHANT_StatisticAmountByBucket *jbucket + = &resp_buckets[index]; + const json_t *amounts_arr; + size_t amounts_len; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("start_time", + &jbucket->start_time), + GNUNET_JSON_spec_timestamp ("end_time", + &jbucket->end_time), + GNUNET_JSON_spec_string ("range", + &jbucket->range), + GNUNET_JSON_spec_array_const ("cumulative_amounts", + &amounts_arr), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ret = GNUNET_SYSERR; + continue; + } + if (GNUNET_SYSERR == ret) + break; + amounts_len = json_array_size (amounts_arr); + if (0 > amounts_len) + { + GNUNET_break_op (0); + ret = GNUNET_SYSERR; + break; + } + { + struct TALER_Amount amt_arr[amounts_len]; + size_t aindex; + json_t *avalue; + jbucket->cumulative_amount_len = amounts_len; + jbucket->cumulative_amounts = amt_arr; + json_array_foreach (amounts_arr, aindex, avalue) { + if (! json_is_string (avalue)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_string_to_amount (json_string_value (avalue), + &amt_arr[aindex])) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + } + } + if (GNUNET_OK == ret) + { + struct TALER_MERCHANT_StatisticsAmountGetResponse gsr = { + .hr.http_status = MHD_HTTP_OK, + .hr.reply = json, + .details.ok.buckets_length = resp_buckets_len, + .details.ok.buckets = resp_buckets, + .details.ok.buckets_description = buckets_description, + .details.ok.intervals_length = resp_intervals_len, + .details.ok.intervals = resp_intervals, + .details.ok.intervals_description = intervals_description, + }; + sgh->cb (sgh->cb_cls, + &gsr); + sgh->cb = NULL; /* just to be sure */ + } + return ret; + } +} + + +/** + * Function called when we're done processing the + * HTTP GET /statistics-amount/$SLUG request. + * + * @param cls the `struct TALER_MERCHANT_StatisticsAmountGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_statistics_amount_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_StatisticsAmountGetHandle *handle = cls; + const json_t *json = response; + struct TALER_MERCHANT_StatisticsAmountGetResponse res = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + handle->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /statistics-amount/$SLUG response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *buckets; + const json_t *intervals; + const char *buckets_description = NULL; + const char *intervals_description = NULL; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("buckets", + &buckets), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("buckets_description", + &buckets_description), + NULL), + GNUNET_JSON_spec_array_const ("intervals", + &intervals), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("intervals_description", + &intervals_description), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + res.hr.http_status = 0; + res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_intervals_and_buckets_amt (json, + buckets, + buckets_description, + intervals, + intervals_description, + handle)) + { + TALER_MERCHANT_statistic_amount_get_cancel (handle); + return; + } + res.hr.http_status = 0; + res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) res.hr.ec); + break; + } +} + + +struct TALER_MERCHANT_StatisticsAmountGetHandle * +TALER_MERCHANT_statistic_amount_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *slug, + enum TALER_MERCHANT_StatisticsType stype, + TALER_MERCHANT_StatisticsAmountGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_StatisticsAmountGetHandle *handle; + CURL *eh; + + handle = GNUNET_new (struct TALER_MERCHANT_StatisticsAmountGetHandle); + handle->ctx = ctx; + handle->cb = cb; + handle->cb_cls = cb_cls; + { + const char *filter = NULL; + char *path; + + switch (stype) + { + case TALER_MERCHANT_STATISTICS_BY_BUCKET: + filter = "bucket"; + break; + case TALER_MERCHANT_STATISTICS_BY_INTERVAL: + filter = "interval"; + break; + case TALER_MERCHANT_STATISTICS_ALL: + filter = NULL; + break; + } + GNUNET_asprintf (&path, + "private/statistics-amount/%s", + slug); + handle->url = TALER_url_join (backend_url, + path, + "by", + filter, + NULL); + GNUNET_free (path); + } + if (NULL == handle->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (handle); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + handle->url); + eh = TALER_MERCHANT_curl_easy_get_ (handle->url); + handle->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_statistics_amount_finished, + handle); + return handle; +} + + +void +TALER_MERCHANT_statistic_amount_get_cancel ( + struct TALER_MERCHANT_StatisticsAmountGetHandle *handle) +{ + if (NULL != handle->job) + GNUNET_CURL_job_cancel (handle->job); + GNUNET_free (handle->url); + GNUNET_free (handle); +} + + +/* end of merchant_api_get-private-statistics-amount-SLUG.c */ diff --git a/src/lib/merchant_api_get-private-statistics-counter-SLUG.c b/src/lib/merchant_api_get-private-statistics-counter-SLUG.c @@ -0,0 +1,353 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-statistics-counter-SLUG.c + * @brief Implementation of the GET /private/statistics-counter/$SLUG request of the merchant's HTTP API + * @author Martin Schanzenbach + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + +/** + * Maximum number of statistics we return + */ +#define MAX_STATISTICS 1024 + +/** + * Handle for a GET /statistics-counter/$SLUG operation. + */ +struct TALER_MERCHANT_StatisticsCounterGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_StatisticsCounterGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse interval information from @a ia. + * + * @param json overall JSON reply + * @param jbuckets JSON array (or NULL!) with bucket data + * @param buckets_description human-readable description for the buckets + * @param jintervals JSON array (or NULL!) with bucket data + * @param intervals_description human-readable description for the intervals + * @param scgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_intervals_and_buckets ( + const json_t *json, + const json_t *jbuckets, + const char *buckets_description, + const json_t *jintervals, + const char *intervals_description, + struct TALER_MERCHANT_StatisticsCounterGetHandle *scgh) +{ + unsigned int resp_buckets_len = json_array_size (jbuckets); + unsigned int resp_intervals_len = json_array_size (jintervals); + + if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) || + (json_array_size (jintervals) != (size_t) resp_intervals_len) || + (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_StatisticCounterByBucket resp_buckets[ + GNUNET_NZL (resp_buckets_len)]; + struct TALER_MERCHANT_StatisticCounterByInterval resp_intervals[ + GNUNET_NZL (resp_intervals_len)]; + size_t index; + json_t *value; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_OK; + json_array_foreach (jintervals, index, value) { + struct TALER_MERCHANT_StatisticCounterByInterval *jinterval + = &resp_intervals[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("start_time", + &jinterval->start_time), + GNUNET_JSON_spec_uint64 ("cumulative_counter", + &jinterval->cumulative_counter), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ret = GNUNET_SYSERR; + continue; + } + if (GNUNET_SYSERR == ret) + break; + } + ret = GNUNET_OK; + json_array_foreach (jbuckets, index, value) { + struct TALER_MERCHANT_StatisticCounterByBucket *jbucket = &resp_buckets[ + index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("start_time", + &jbucket->start_time), + GNUNET_JSON_spec_timestamp ("end_time", + &jbucket->end_time), + GNUNET_JSON_spec_string ("range", + &jbucket->range), + GNUNET_JSON_spec_uint64 ("cumulative_counter", + &jbucket->cumulative_counter), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ret = GNUNET_SYSERR; + continue; + } + if (GNUNET_SYSERR == ret) + break; + } + if (GNUNET_OK == ret) + { + struct TALER_MERCHANT_StatisticsCounterGetResponse gsr = { + .hr.http_status = MHD_HTTP_OK, + .hr.reply = json, + .details.ok.buckets_length = resp_buckets_len, + .details.ok.buckets = resp_buckets, + .details.ok.buckets_description = buckets_description, + .details.ok.intervals_length = resp_intervals_len, + .details.ok.intervals = resp_intervals, + .details.ok.intervals_description = intervals_description, + }; + scgh->cb (scgh->cb_cls, + &gsr); + scgh->cb = NULL; /* just to be sure */ + } + return ret; + } +} + + +/** + * Function called when we're done processing the + * HTTP GET /statistics-counter/$SLUG request. + * + * @param cls the `struct TALER_MERCHANT_StatisticsCounterGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_statistics_counter_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_StatisticsCounterGetHandle *handle = cls; + const json_t *json = response; + struct TALER_MERCHANT_StatisticsCounterGetResponse res = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + handle->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /statistics-counter/$SLUG response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *buckets; + const json_t *intervals; + const char *buckets_description; + const char *intervals_description; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("buckets", + &buckets), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("buckets_description", + &buckets_description), + NULL), + GNUNET_JSON_spec_array_const ("intervals", + &intervals), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("intervals_description", + &intervals_description), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + res.hr.http_status = 0; + res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "%s\n", json_dumps (json, JSON_INDENT (1))); + if (GNUNET_OK == + parse_intervals_and_buckets (json, + buckets, + buckets_description, + intervals, + intervals_description, + handle)) + { + TALER_MERCHANT_statistic_counter_get_cancel (handle); + return; + } + res.hr.http_status = 0; + res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) res.hr.ec); + break; + } +} + + +struct TALER_MERCHANT_StatisticsCounterGetHandle * +TALER_MERCHANT_statistic_counter_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *slug, + enum TALER_MERCHANT_StatisticsType stype, + TALER_MERCHANT_StatisticsCounterGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_StatisticsCounterGetHandle *handle; + CURL *eh; + + handle = GNUNET_new (struct TALER_MERCHANT_StatisticsCounterGetHandle); + handle->ctx = ctx; + handle->cb = cb; + handle->cb_cls = cb_cls; + { + const char *filter = NULL; + char *path; + + switch (stype) + { + case TALER_MERCHANT_STATISTICS_BY_BUCKET: + filter = "bucket"; + break; + case TALER_MERCHANT_STATISTICS_BY_INTERVAL: + filter = "interval"; + break; + case TALER_MERCHANT_STATISTICS_ALL: + filter = NULL; + break; + } + GNUNET_asprintf (&path, + "private/statistics-counter/%s", + slug); + handle->url = TALER_url_join (backend_url, + path, + "by", + filter, + NULL); + GNUNET_free (path); + } + if (NULL == handle->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (handle); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + handle->url); + eh = TALER_MERCHANT_curl_easy_get_ (handle->url); + handle->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_statistics_counter_finished, + handle); + return handle; +} + + +void +TALER_MERCHANT_statistic_counter_get_cancel ( + struct TALER_MERCHANT_StatisticsCounterGetHandle *handle) +{ + if (NULL != handle->job) + GNUNET_CURL_job_cancel (handle->job); + GNUNET_free (handle->url); + GNUNET_free (handle); +} + + +/* end of merchant_api_get-private-statistics-counter-SLUG.c */ diff --git a/src/lib/merchant_api_get-private-templates-TEMPLATE_ID.c b/src/lib/merchant_api_get-private-templates-TEMPLATE_ID.c @@ -0,0 +1,201 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-templates-TEMPLATE_ID.c + * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /templates/$ID operation. + */ +struct TALER_MERCHANT_TemplateGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TemplateGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /templates/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TemplateGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_template_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TemplateGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_TemplateGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /templates/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *contract; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("template_description", + &tgr.details.ok.template_description), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("otp_id", + &tgr.details.ok.otp_id), + NULL), + GNUNET_JSON_spec_object_const ("template_contract", + &contract), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.details.ok.template_contract = contract; + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_template_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_template_get_cancel (tgh); +} + + +struct TALER_MERCHANT_TemplateGetHandle * +TALER_MERCHANT_template_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + TALER_MERCHANT_TemplateGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TemplateGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_TemplateGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/templates/%s", + template_id); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_template_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_template_get_cancel ( + struct TALER_MERCHANT_TemplateGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get-private-templates.c b/src/lib/merchant_api_get-private-templates.c @@ -0,0 +1,247 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-templates.c + * @brief Implementation of the GET /templates request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Maximum number of templates we return. + */ +#define MAX_TEMPLATES 1024 + + +/** + * Handle for a GET /templates operation. + */ +struct TALER_MERCHANT_TemplatesGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TemplatesGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse template information from @a ia. + * + * @param ia JSON array (or NULL!) with template data + * @param[in] tgr partially filled response + * @param tgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_templates (const json_t *ia, + struct TALER_MERCHANT_TemplatesGetResponse *tgr, + struct TALER_MERCHANT_TemplatesGetHandle *tgh) +{ + unsigned int tmpl_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) tmpl_len) || + (tmpl_len > MAX_TEMPLATES) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_TemplateEntry tmpl[GNUNET_NZL (tmpl_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_TemplateEntry *ie = &tmpl[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("template_id", + &ie->template_id), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.templates_length = tmpl_len; + tgr->details.ok.templates = tmpl; + tgh->cb (tgh->cb_cls, + tgr); + tgh->cb = NULL; /* just to be sure */ + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /templates request. + * + * @param cls the `struct TALER_MERCHANT_TemplatesGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_templates_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TemplatesGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_TemplatesGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /templates response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *templates; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("templates", + &templates), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_templates (templates, + &tgr, + tgh)) + { + TALER_MERCHANT_templates_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_templates_get_cancel (tgh); +} + + +struct TALER_MERCHANT_TemplatesGetHandle * +TALER_MERCHANT_templates_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_TemplatesGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TemplatesGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_TemplatesGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + tgh->url = TALER_url_join (backend_url, + "private/templates", + NULL); + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_templates_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_templates_get_cancel ( + struct TALER_MERCHANT_TemplatesGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.c b/src/lib/merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.c @@ -0,0 +1,218 @@ +/* + This file is part of TALER + Copyright (C) 2023-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.c + * @brief Implementation of the GET /tokenfamily/$ID request of the merchant's HTTP API + * @author Christian Blättler + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /tokenfamilies/$SLUG operation. + */ +struct TALER_MERCHANT_TokenFamilyGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TokenFamilyGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /tokenfamilies/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TokenFamilyGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_token_family_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TokenFamilyGetHandle *handle = cls; + const json_t *json = response; + struct TALER_MERCHANT_TokenFamilyGetResponse res = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + handle->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /tokenfamilies/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + // Parse token family response + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("slug", + &res.details.ok.slug), + GNUNET_JSON_spec_string ("name", + &res.details.ok.name), + GNUNET_JSON_spec_string ("description", + &res.details.ok.description), + GNUNET_JSON_spec_object_const ("description_i18n", + &res.details.ok.description_i18n), + GNUNET_JSON_spec_object_const ("extra_data", + &res.details.ok.extra_data), + GNUNET_JSON_spec_timestamp ("valid_after", + &res.details.ok.valid_after), + GNUNET_JSON_spec_timestamp ("valid_before", + &res.details.ok.valid_before), + GNUNET_JSON_spec_relative_time ("duation", + &res.details.ok.duration), + GNUNET_JSON_spec_relative_time ("validity_granularity", + &res.details.ok.validity_granularity), + GNUNET_JSON_spec_relative_time ("start_offset", + &res.details.ok.start_offset), + GNUNET_JSON_spec_string ("kind", + &res.details.ok.kind), + GNUNET_JSON_spec_uint64 ("issued", + &res.details.ok.issued), + GNUNET_JSON_spec_uint64 ("used", + &res.details.ok.used), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + handle->cb (handle->cb_cls, + &res); + GNUNET_JSON_parse_free (spec); + TALER_MERCHANT_token_family_get_cancel (handle); + return; + } + res.hr.http_status = 0; + res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + res.hr.ec = TALER_JSON_get_error_code (json); + res.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) res.hr.ec); + break; + } +} + + +struct TALER_MERCHANT_TokenFamilyGetHandle * +TALER_MERCHANT_token_family_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *token_family_slug, + TALER_MERCHANT_TokenFamilyGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TokenFamilyGetHandle *handle; + CURL *eh; + + handle = GNUNET_new (struct TALER_MERCHANT_TokenFamilyGetHandle); + handle->ctx = ctx; + handle->cb = cb; + handle->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/tokenfamilies/%s", + token_family_slug); + handle->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == handle->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (handle); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + handle->url); + eh = TALER_MERCHANT_curl_easy_get_ (handle->url); + handle->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_token_family_finished, + handle); + return handle; +} + + +void +TALER_MERCHANT_token_family_get_cancel ( + struct TALER_MERCHANT_TokenFamilyGetHandle *handle) +{ + if (NULL != handle->job) + GNUNET_CURL_job_cancel (handle->job); + GNUNET_free (handle->url); + GNUNET_free (handle); +} diff --git a/src/lib/merchant_api_get-private-transfers.c b/src/lib/merchant_api_get-private-transfers.c @@ -0,0 +1,314 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-transfers.c + * @brief Implementation of the GET /transfers request of the merchant's HTTP API + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * @brief A Handle for tracking wire transfers. + */ +struct TALER_MERCHANT_GetTransfersHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_GetTransfersCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP GET /transfers request. + * + * @param cls the `struct TALER_MERCHANT_GetTransfersHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_transfers_get_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_GetTransfersHandle *gth = cls; + const json_t *json = response; + struct TALER_MERCHANT_GetTransfersResponse gtr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + gth->job = NULL; + switch (response_code) + { + case 0: + gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + const json_t *transfers; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("transfers", + &transfers), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + gtr.hr.http_status = 0; + gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + + { + size_t tds_length; + struct TALER_MERCHANT_TransferData *tds; + json_t *transfer; + size_t i; + bool ok; + + tds_length = json_array_size (transfers); + tds = GNUNET_new_array (tds_length, + struct TALER_MERCHANT_TransferData); + ok = true; + json_array_foreach (transfers, i, transfer) { + struct TALER_MERCHANT_TransferData *td = &tds[i]; + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_amount_any ("credit_amount", + &td->credit_amount), + GNUNET_JSON_spec_fixed_auto ("wtid", + &td->wtid), + TALER_JSON_spec_full_payto_uri ("payto_uri", + &td->payto_uri), + TALER_JSON_spec_web_url ("exchange_url", + &td->exchange_url), + GNUNET_JSON_spec_uint64 ("transfer_serial_id", + &td->credit_serial), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_timestamp ("execution_time", + &td->execution_time), + NULL), + GNUNET_JSON_spec_bool ("expected", + &td->expected), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (transfer, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + ok = false; + break; + } + } + + if (! ok) + { + GNUNET_break_op (0); + GNUNET_free (tds); + gtr.hr.http_status = 0; + gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + gtr.details.ok.transfers = tds; + gtr.details.ok.transfers_length = tds_length; + gth->cb (gth->cb_cls, + &gtr); + GNUNET_free (tds); + TALER_MERCHANT_transfers_get_cancel (gth); + return; + } + } + case MHD_HTTP_UNAUTHORIZED: + gtr.hr.ec = TALER_JSON_get_error_code (json); + gtr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + gtr.hr.ec = TALER_JSON_get_error_code (json); + gtr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + gtr.hr.ec = TALER_JSON_get_error_code (json); + gtr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &gtr.hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) gtr.hr.ec); + gtr.hr.http_status = 0; + break; + } + gth->cb (gth->cb_cls, + &gtr); + TALER_MERCHANT_transfers_get_cancel (gth); +} + + +struct TALER_MERCHANT_GetTransfersHandle * +TALER_MERCHANT_transfers_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + struct TALER_FullPayto payto_uri, + const struct GNUNET_TIME_Timestamp before, + const struct GNUNET_TIME_Timestamp after, + int64_t limit, + uint64_t offset, + enum TALER_EXCHANGE_YesNoAll expected, + TALER_MERCHANT_GetTransfersCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_GetTransfersHandle *gth; + CURL *eh; + const char *expected_s = NULL; + char limit_s[30]; + char offset_s[30]; + char before_s[30]; + char after_s[30]; + + gth = GNUNET_new (struct TALER_MERCHANT_GetTransfersHandle); + gth->ctx = ctx; + gth->cb = cb; + gth->cb_cls = cb_cls; + expected_s = TALER_yna_to_string (expected); + GNUNET_snprintf (limit_s, + sizeof (limit_s), + "%lld", + (long long) limit); + GNUNET_snprintf (offset_s, + sizeof (offset_s), + "%lld", + (unsigned long long) offset); + GNUNET_snprintf (before_s, + sizeof (before_s), + "%llu", + (unsigned long long) GNUNET_TIME_timestamp_to_s (before)); + GNUNET_snprintf (after_s, + sizeof (after_s), + "%llu", + (unsigned long long) GNUNET_TIME_timestamp_to_s (after)); + { + char *enc_payto = TALER_urlencode (payto_uri.full_payto); + + gth->url = TALER_url_join (backend_url, + "private/transfers", + "payto_uri", + enc_payto, + "expected", + (TALER_EXCHANGE_YNA_ALL != expected) + ? expected_s + : NULL, + "limit", + 0 != limit + ? limit_s + : NULL, + "offset", + ((0 != offset) && (UINT64_MAX != offset)) + ? offset_s + : NULL, + "before", + GNUNET_TIME_absolute_is_never (before.abs_time) + ? NULL + : before_s, + "after", + GNUNET_TIME_absolute_is_zero (after.abs_time) + ? NULL + : after_s, + NULL); + GNUNET_free (enc_payto); + } + if (NULL == gth->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (gth); + return NULL; + } + eh = TALER_MERCHANT_curl_easy_get_ (gth->url); + gth->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_transfers_get_finished, + gth); + return gth; +} + + +void +TALER_MERCHANT_transfers_get_cancel ( + struct TALER_MERCHANT_GetTransfersHandle *gth) +{ + if (NULL != gth->job) + { + GNUNET_CURL_job_cancel (gth->job); + gth->job = NULL; + } + GNUNET_free (gth->url); + GNUNET_free (gth); +} + + +/* end of merchant_api_get_transfers.c */ diff --git a/src/lib/merchant_api_get-private-units-UNIT.c b/src/lib/merchant_api_get-private-units-UNIT.c @@ -0,0 +1,249 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-units-UNIT.c + * @brief Implementation of GET /private/units/$ID + * @author Bohdan Potuzhnyi + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> + + +/** + * Handle for a GET /private/units/$ID operation. + */ +struct TALER_MERCHANT_UnitGetHandle +{ + /** + * Fully qualified request URL. + */ + char *url; + + /** + * In-flight job handle. + */ + struct GNUNET_CURL_Job *job; + + /** + * Callback to invoke with the response. + */ + TALER_MERCHANT_UnitGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Parse the JSON response into @a ugr. + * + * @param json full JSON reply + * @param ugr response descriptor to populate + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_unit (const json_t *json, + struct TALER_MERCHANT_UnitGetResponse *ugr) +{ + struct TALER_MERCHANT_UnitEntry *entry = &ugr->details.ok.unit; + const char *unit; + const char *unit_name_long; + const char *unit_name_short; + const json_t *unit_name_long_i18n = NULL; + const json_t *unit_name_short_i18n = NULL; + bool unit_allow_fraction; + bool unit_active; + bool unit_builtin; + uint32_t unit_precision_level; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("unit", + &unit), + GNUNET_JSON_spec_string ("unit_name_long", + &unit_name_long), + GNUNET_JSON_spec_string ("unit_name_short", + &unit_name_short), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("unit_name_long_i18n", + &unit_name_long_i18n), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("unit_name_short_i18n", + &unit_name_short_i18n), + NULL), + GNUNET_JSON_spec_bool ("unit_allow_fraction", + &unit_allow_fraction), + GNUNET_JSON_spec_uint32 ("unit_precision_level", + &unit_precision_level), + GNUNET_JSON_spec_bool ("unit_active", + &unit_active), + GNUNET_JSON_spec_bool ("unit_builtin", + &unit_builtin), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return GNUNET_SYSERR; + } + GNUNET_JSON_parse_free (spec); + entry->unit = unit; + entry->unit_name_long = unit_name_long; + entry->unit_name_short = unit_name_short; + entry->unit_name_long_i18n = unit_name_long_i18n; + entry->unit_name_short_i18n = unit_name_short_i18n; + entry->unit_allow_fraction = unit_allow_fraction; + entry->unit_precision_level = unit_precision_level; + entry->unit_active = unit_active; + entry->unit_builtin = unit_builtin; + return GNUNET_OK; +} + + +/** + * Called once the HTTP request completes. + * + * @param cls operation handle + * @param response_code HTTP status (0 on client-side errors) + * @param response parsed JSON reply (NULL if parsing failed) + */ +static void +handle_get_unit_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_UnitGetHandle *ugh = cls; + const json_t *json = response; + struct TALER_MERCHANT_UnitGetResponse ugr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + ugh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "GET /private/units/$ID finished with status %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + if (GNUNET_OK != + parse_unit (json, + &ugr)) + { + ugr.hr.http_status = 0; + ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + ugh->cb (ugh->cb_cls, + &ugr); + TALER_MERCHANT_unit_get_cancel (ugh); + return; + case MHD_HTTP_UNAUTHORIZED: + case MHD_HTTP_FORBIDDEN: + case MHD_HTTP_NOT_FOUND: + ugr.hr.ec = TALER_JSON_get_error_code (json); + ugr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case 0: + ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + default: + ugr.hr.ec = TALER_JSON_get_error_code (json); + ugr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unexpected response %u/%d for GET /private/units/$ID\n", + (unsigned int) response_code, + (int) ugr.hr.ec); + break; + } + ugh->cb (ugh->cb_cls, + &ugr); + TALER_MERCHANT_unit_get_cancel (ugh); +} + + +struct TALER_MERCHANT_UnitGetHandle * +TALER_MERCHANT_unit_get (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *unit_id, + TALER_MERCHANT_UnitGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_UnitGetHandle *ugh; + CURL *eh; + char *path; + + GNUNET_asprintf (&path, + "private/units/%s", + unit_id); + ugh = GNUNET_new (struct TALER_MERCHANT_UnitGetHandle); + ugh->ctx = ctx; + ugh->cb = cb; + ugh->cb_cls = cb_cls; + ugh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + if (NULL == ugh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to build /private/units/%s URL\n", + unit_id); + GNUNET_free (ugh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + ugh->url); + eh = TALER_MERCHANT_curl_easy_get_ (ugh->url); + ugh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_unit_finished, + ugh); + return ugh; +} + + +void +TALER_MERCHANT_unit_get_cancel (struct TALER_MERCHANT_UnitGetHandle *ugh) +{ + if (NULL != ugh->job) + GNUNET_CURL_job_cancel (ugh->job); + GNUNET_free (ugh->url); + GNUNET_free (ugh); +} + + +/* end of merchant_api_get_unit.c */ diff --git a/src/lib/merchant_api_get-private-units.c b/src/lib/merchant_api_get-private-units.c @@ -0,0 +1,329 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License along + with TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-units.c + * @brief Implementation of GET /private/units + * @author Bohdan Potuzhnyi + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> + + +/** + * Maximum number of units returned in a single response. + */ +#define MAX_UNITS 1024 + + +/** + * Handle for a GET /private/units operation. + */ +struct TALER_MERCHANT_UnitsGetHandle +{ + /** + * Fully qualified request URL. + */ + char *url; + + /** + * In-flight job handle. + */ + struct GNUNET_CURL_Job *job; + + /** + * Callback to invoke with the outcome. + */ + TALER_MERCHANT_UnitsGetCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Parse an individual unit entry from @a value. + * + * @param value JSON object describing the unit + * @param[out] ue set to the parsed values + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_unit_entry (const json_t *value, + struct TALER_MERCHANT_UnitEntry *ue) +{ + const char *unit; + const char *unit_name_long; + const char *unit_name_short; + const json_t *unit_name_long_i18n = NULL; + const json_t *unit_name_short_i18n = NULL; + bool unit_allow_fraction; + bool unit_active; + bool unit_builtin; + uint32_t unit_precision_level; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("unit", + &unit), + GNUNET_JSON_spec_string ("unit_name_long", + &unit_name_long), + GNUNET_JSON_spec_string ("unit_name_short", + &unit_name_short), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("unit_name_long_i18n", + &unit_name_long_i18n), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("unit_name_short_i18n", + &unit_name_short_i18n), + NULL), + GNUNET_JSON_spec_bool ("unit_allow_fraction", + &unit_allow_fraction), + GNUNET_JSON_spec_uint32 ("unit_precision_level", + &unit_precision_level), + GNUNET_JSON_spec_bool ("unit_active", + &unit_active), + GNUNET_JSON_spec_bool ("unit_builtin", + &unit_builtin), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return GNUNET_SYSERR; + } + GNUNET_JSON_parse_free (spec); + ue->unit = unit; + ue->unit_name_long = unit_name_long; + ue->unit_name_short = unit_name_short; + ue->unit_name_long_i18n = unit_name_long_i18n; + ue->unit_name_short_i18n = unit_name_short_i18n; + ue->unit_allow_fraction = unit_allow_fraction; + ue->unit_precision_level = unit_precision_level; + ue->unit_active = unit_active; + ue->unit_builtin = unit_builtin; + return GNUNET_OK; +} + + +/** + * Parse the list of units from @a units and call the callback. + * + * @param json complete response JSON + * @param units array of units + * @param ugh ongoing operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_units (const json_t *json, + const json_t *units, + struct TALER_MERCHANT_UnitsGetHandle *ugh) +{ + size_t len; + + len = json_array_size (units); + if (len > MAX_UNITS) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + { + struct TALER_MERCHANT_UnitEntry entries[GNUNET_NZL (len)]; + size_t idx; + json_t *value; + + json_array_foreach (units, idx, value) { + if (GNUNET_OK != + parse_unit_entry (value, + &entries[idx])) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + { + struct TALER_MERCHANT_UnitsGetResponse ugr = { + .hr.http_status = MHD_HTTP_OK, + .hr.reply = json, + .details = { + .ok = { + .units = entries, + .units_length = (unsigned int) len + } + + + } + + + }; + + ugh->cb (ugh->cb_cls, + &ugr); + } + } + return GNUNET_OK; +} + + +/** + * Called when the HTTP transfer finishes. + * + * @param cls closure, the operation handle + * @param response_code HTTP status (0 on network errors) + * @param response parsed JSON body (NULL if parsing failed) + */ +static void +handle_get_units_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_UnitsGetHandle *ugh = cls; + const json_t *json = response; + struct TALER_MERCHANT_UnitsGetResponse ugr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + ugh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "GET /private/units finished with status %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *units; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("units", + &units), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + ugr.hr.http_status = 0; + ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_units (json, + units, + ugh)) + { + TALER_MERCHANT_units_get_cancel (ugh); + return; + } + ugr.hr.http_status = 0; + ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + case MHD_HTTP_FORBIDDEN: + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_CONFLICT: + ugr.hr.ec = TALER_JSON_get_error_code (json); + ugr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case 0: + ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + default: + ugr.hr.ec = TALER_JSON_get_error_code (json); + ugr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unexpected response code %u/%d for GET /private/units\n", + (unsigned int) response_code, + (int) ugr.hr.ec); + break; + } + ugh->cb (ugh->cb_cls, + &ugr); + TALER_MERCHANT_units_get_cancel (ugh); +} + + +struct TALER_MERCHANT_UnitsGetHandle * +TALER_MERCHANT_units_get (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_UnitsGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_UnitsGetHandle *ugh; + CURL *eh; + + ugh = GNUNET_new (struct TALER_MERCHANT_UnitsGetHandle); + ugh->ctx = ctx; + ugh->cb = cb; + ugh->cb_cls = cb_cls; + ugh->url = TALER_url_join (backend_url, + "private/units", + NULL); + if (NULL == ugh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to build /private/units URL\n"); + GNUNET_free (ugh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + ugh->url); + eh = TALER_MERCHANT_curl_easy_get_ (ugh->url); + ugh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_units_finished, + ugh); + return ugh; +} + + +void +TALER_MERCHANT_units_get_cancel (struct TALER_MERCHANT_UnitsGetHandle *ugh) +{ + if (NULL != ugh->job) + GNUNET_CURL_job_cancel (ugh->job); + GNUNET_free (ugh->url); + GNUNET_free (ugh); +} + + +/* end of merchant_api_get_units.c */ diff --git a/src/lib/merchant_api_get-private-webhooks-WEBHOOK_ID.c b/src/lib/merchant_api_get-private-webhooks-WEBHOOK_ID.c @@ -0,0 +1,221 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-webhooks-WEBHOOK_ID.c + * @brief Implementation of the GET /webhooks/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /webhooks/$ID operation. + */ +struct TALER_MERCHANT_WebhookGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_WebhookGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /webhooks/$ID request. + * + * @param cls the `struct TALER_MERCHANT_WebhookGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_webhook_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WebhookGetHandle *wgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + wgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /webhooks/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const char *event_type; + const char *url; + const char *http_method; + const char *header_template; + const char *body_template; + bool rst_ok = true; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("event_type", + &event_type), + TALER_JSON_spec_web_url ("url", + &url), + GNUNET_JSON_spec_string ("http_method", + &http_method), + GNUNET_JSON_spec_string ("header_template", + &header_template), + GNUNET_JSON_spec_string ("body_template", + &body_template), + GNUNET_JSON_spec_end () + }; + + + if ( (rst_ok) && + (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) ) + { + wgh->cb (wgh->cb_cls, + &hr, + event_type, + url, + http_method, + header_template, + body_template); + GNUNET_JSON_parse_free (spec); + TALER_MERCHANT_webhook_get_cancel (wgh); + return; + } + hr.http_status = 0; + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_JSON_parse_free (spec); + break; + } + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + wgh->cb (wgh->cb_cls, + &hr, + NULL, + NULL, + NULL, + NULL, + NULL); + TALER_MERCHANT_webhook_get_cancel (wgh); +} + + +struct TALER_MERCHANT_WebhookGetHandle * +TALER_MERCHANT_webhook_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *webhook_id, + TALER_MERCHANT_WebhookGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WebhookGetHandle *wgh; + CURL *eh; + + wgh = GNUNET_new (struct TALER_MERCHANT_WebhookGetHandle); + wgh->ctx = ctx; + wgh->cb = cb; + wgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/webhooks/%s", + webhook_id); + wgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == wgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (wgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + wgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (wgh->url); + wgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_webhook_finished, + wgh); + return wgh; +} + + +void +TALER_MERCHANT_webhook_get_cancel ( + struct TALER_MERCHANT_WebhookGetHandle *wgh) +{ + if (NULL != wgh->job) + GNUNET_CURL_job_cancel (wgh->job); + GNUNET_free (wgh->url); + GNUNET_free (wgh); +} diff --git a/src/lib/merchant_api_get-private-webhooks.c b/src/lib/merchant_api_get-private-webhooks.c @@ -0,0 +1,246 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-private-webhooks.c + * @brief Implementation of the GET /webhooks request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Maximum number of webhooks we return. + */ +#define MAX_WEBHOOKS 1024 + +/** + * Handle for a GET /webhooks operation. + */ +struct TALER_MERCHANT_WebhooksGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_WebhooksGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse webhook information from @a ia. + * + * @param ia JSON array (or NULL!) with webhook data + * @param[in] wgr partially filled webhook response + * @param wgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_webhooks (const json_t *ia, + struct TALER_MERCHANT_WebhooksGetResponse *wgr, + struct TALER_MERCHANT_WebhooksGetHandle *wgh) +{ + unsigned int whook_len = (unsigned int) json_array_size (ia); + + if ( (json_array_size (ia) != (size_t) whook_len) || + (whook_len > MAX_WEBHOOKS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_WebhookEntry whook[GNUNET_NZL (whook_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_WebhookEntry *ie = &whook[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("webhook_id", + &ie->webhook_id), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + wgr->details.ok.webhooks_length = whook_len; + wgr->details.ok.webhooks = whook; + wgh->cb (wgh->cb_cls, + wgr); + wgh->cb = NULL; /* just to be sure */ + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /webhooks request. + * + * @param cls the `struct TALER_MERCHANT_WebhooksGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_webhooks_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WebhooksGetHandle *wgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_WebhooksGetResponse wgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + wgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /webhooks response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *webhooks; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("webhooks", + &webhooks), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + wgr.hr.http_status = 0; + wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_webhooks (webhooks, + &wgr, + wgh)) + { + TALER_MERCHANT_webhooks_get_cancel (wgh); + return; + } + wgr.hr.http_status = 0; + wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + wgr.hr.ec = TALER_JSON_get_error_code (json); + wgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + wgr.hr.ec = TALER_JSON_get_error_code (json); + wgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) wgr.hr.ec); + break; + } + wgh->cb (wgh->cb_cls, + &wgr); + TALER_MERCHANT_webhooks_get_cancel (wgh); +} + + +struct TALER_MERCHANT_WebhooksGetHandle * +TALER_MERCHANT_webhooks_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_WebhooksGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WebhooksGetHandle *wgh; + CURL *eh; + + wgh = GNUNET_new (struct TALER_MERCHANT_WebhooksGetHandle); + wgh->ctx = ctx; + wgh->cb = cb; + wgh->cb_cls = cb_cls; + wgh->url = TALER_url_join (backend_url, + "private/webhooks", + NULL); + if (NULL == wgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (wgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + wgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (wgh->url); + wgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_webhooks_finished, + wgh); + return wgh; +} + + +void +TALER_MERCHANT_webhooks_get_cancel ( + struct TALER_MERCHANT_WebhooksGetHandle *wgh) +{ + if (NULL != wgh->job) + GNUNET_CURL_job_cancel (wgh->job); + GNUNET_free (wgh->url); + GNUNET_free (wgh); +} diff --git a/src/lib/merchant_api_get-products-IMAGE_HASH-image.c b/src/lib/merchant_api_get-products-IMAGE_HASH-image.c @@ -0,0 +1,188 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-products-IMAGE_HASH-image.c + * @brief Implementation of the GET /products/$HASH/image request + * @author Bohdan Potuzhnyi + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include <taler/taler_error_codes.h> +#include <taler/taler_json_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" + + +/** + * Handle for a GET /products/$HASH/image operation. + */ +struct TALER_MERCHANT_ProductImageGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ProductImageGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP GET /products/$HASH/image request. + * + * @param cls the `struct TALER_MERCHANT_ProductImageGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_product_image_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_ProductImageGetHandle *pigh = cls; + const json_t *json = response; + struct TALER_MERCHANT_ProductImageGetResponse pir = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + pigh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /products/$HASH/image response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("image", + &pir.details.ok.image), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + pigh->cb (pigh->cb_cls, + &pir); + GNUNET_JSON_parse_free (spec); + TALER_MERCHANT_product_image_get_cancel (pigh); + return; + } + pir.hr.http_status = 0; + pir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_BAD_REQUEST: + pir.hr.ec = TALER_JSON_get_error_code (json); + pir.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + pir.hr.ec = TALER_JSON_get_error_code (json); + pir.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) pir.hr.ec); + break; + } + pigh->cb (pigh->cb_cls, + &pir); + TALER_MERCHANT_product_image_get_cancel (pigh); +} + + +struct TALER_MERCHANT_ProductImageGetHandle * +TALER_MERCHANT_product_image_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *image_hash, + TALER_MERCHANT_ProductImageGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_ProductImageGetHandle *pigh; + CURL *eh; + + pigh = GNUNET_new (struct TALER_MERCHANT_ProductImageGetHandle); + pigh->ctx = ctx; + pigh->cb = cb; + pigh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "products/%s/image", + image_hash); + pigh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == pigh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (pigh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + pigh->url); + eh = TALER_MERCHANT_curl_easy_get_ (pigh->url); + pigh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_product_image_finished, + pigh); + return pigh; +} + + +void +TALER_MERCHANT_product_image_get_cancel ( + struct TALER_MERCHANT_ProductImageGetHandle *pigh) +{ + if (NULL != pigh->job) + GNUNET_CURL_job_cancel (pigh->job); + GNUNET_free (pigh->url); + GNUNET_free (pigh); +} +\ No newline at end of file diff --git a/src/lib/merchant_api_get-templates-TEMPLATE_ID.c b/src/lib/merchant_api_get-templates-TEMPLATE_ID.c @@ -0,0 +1,195 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get-templates-TEMPLATE_ID.c + * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /templates/$ID operation. + */ +struct TALER_MERCHANT_WalletTemplateGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_WalletTemplateGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /templates/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TemplateGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_template_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_WalletTemplateGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /templates/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *contract; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_object_const ("template_contract", + &contract), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.details.ok.template_contract = contract; + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_wallet_template_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_wallet_template_get_cancel (tgh); +} + + +struct TALER_MERCHANT_WalletTemplateGetHandle * +TALER_MERCHANT_wallet_template_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + TALER_MERCHANT_WalletTemplateGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_WalletTemplateGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "templates/%s", + template_id); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_template_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_wallet_template_get_cancel ( + struct TALER_MERCHANT_WalletTemplateGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_account.c b/src/lib/merchant_api_get_account.c @@ -1,211 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_account.c - * @brief Implementation of the GET /accounts/$ID request of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /accounts/$ID operation. - */ -struct TALER_MERCHANT_AccountGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_AccountGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /accounts/$ID request. - * - * @param cls the `struct TALER_MERCHANT_AccountGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_account_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_AccountGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_AccountGetResponse tgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /accounts/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("salt", - &tgr.details.ok.ad.salt), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_web_url ("credit_facade_url", - &tgr.details.ok.ad.credit_facade_url), - NULL), - TALER_JSON_spec_full_payto_uri ("payto_uri", - &tgr.details.ok.ad.payto_uri), - GNUNET_JSON_spec_fixed_auto ("h_wire", - &tgr.details.ok.ad.h_wire), - GNUNET_JSON_spec_bool ("active", - &tgr.details.ok.ad.active), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_account_get_cancel (tgh); - return; - } - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_account_get_cancel (tgh); -} - - -struct TALER_MERCHANT_AccountGetHandle * -TALER_MERCHANT_account_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - const struct TALER_MerchantWireHashP *h_wire, - TALER_MERCHANT_AccountGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_AccountGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_AccountGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - { - char w_str[sizeof (*h_wire) * 2]; - char *path; - char *end; - - end = GNUNET_STRINGS_data_to_string (h_wire, - sizeof (*h_wire), - w_str, - sizeof (w_str)); - *end = '\0'; - GNUNET_asprintf (&path, - "private/accounts/%s", - w_str); - tgh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_account_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_account_get_cancel ( - struct TALER_MERCHANT_AccountGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_get_accounts.c b/src/lib/merchant_api_get_accounts.c @@ -1,247 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_accounts.c - * @brief Implementation of the GET /accounts request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - -/** - * Maximum number of accounts permitted. - */ -#define MAX_ACCOUNTS 1024 - -/** - * Handle for a GET /accounts operation. - */ -struct TALER_MERCHANT_AccountsGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_AccountsGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse account information from @a ia. - * - * @param ia JSON array (or NULL!) with account data - * @param[in] tgr partially filled response - * @param tgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_accounts (const json_t *ia, - struct TALER_MERCHANT_AccountsGetResponse *tgr, - struct TALER_MERCHANT_AccountsGetHandle *tgh) -{ - unsigned int tmpl_len = (unsigned int) json_array_size (ia); - - if ( (json_array_size (ia) != (size_t) tmpl_len) || - (tmpl_len > MAX_ACCOUNTS) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_AccountEntry tmpl[GNUNET_NZL (tmpl_len)]; - size_t index; - json_t *value; - - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_AccountEntry *ie = &tmpl[index]; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_full_payto_uri ("payto_uri", - &ie->payto_uri), - GNUNET_JSON_spec_fixed_auto ("h_wire", - &ie->h_wire), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - tgr->details.ok.accounts_length = tmpl_len; - tgr->details.ok.accounts = tmpl; - tgh->cb (tgh->cb_cls, - tgr); - tgh->cb = NULL; /* just to be sure */ - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /accounts request. - * - * @param cls the `struct TALER_MERCHANT_AccountsGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_accounts_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_AccountsGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_AccountsGetResponse tgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /accounts response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *accounts; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("accounts", - &accounts), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_accounts (accounts, - &tgr, - tgh)) - { - TALER_MERCHANT_accounts_get_cancel (tgh); - return; - } - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_accounts_get_cancel (tgh); -} - - -struct TALER_MERCHANT_AccountsGetHandle * -TALER_MERCHANT_accounts_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_AccountsGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_AccountsGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_AccountsGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - tgh->url = TALER_url_join (backend_url, - "private/accounts", - NULL); - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_accounts_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_accounts_get_cancel ( - struct TALER_MERCHANT_AccountsGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_get_config.c b/src/lib/merchant_api_get_config.c @@ -1,320 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2026 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_config.c - * @brief Implementation of the /config request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - -/** - * Which version of the Taler protocol is implemented - * by this library? Used to determine compatibility. - */ -#define MERCHANT_PROTOCOL_CURRENT 27 - -/** - * How many configs are we backwards-compatible with? - */ -#define MERCHANT_PROTOCOL_AGE 3 - -/** - * How many exchanges do we allow at most per merchant? - */ -#define MAX_EXCHANGES 1024 - -/** - * How many currency specs do we allow at most per merchant? - */ -#define MAX_CURRENCIES 1024 - -/** - * @brief A handle for /config operations - */ -struct TALER_MERCHANT_ConfigGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ConfigCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP /config request. - * - * @param cls the `struct TALER_MERCHANT_ConfigGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_config_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ConfigGetHandle *vgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_ConfigResponse cr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /config response with status code %u\n", - (unsigned int) response_code); - - vgh->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *jcs; - const json_t *exchanges = NULL; - struct TALER_MERCHANT_ExchangeConfigInfo *eci = NULL; - unsigned int num_eci = 0; - unsigned int nspec; - struct TALER_JSON_ProtocolVersion pv; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_object_const ("currencies", - &jcs), - GNUNET_JSON_spec_array_const ("exchanges", - &exchanges), - GNUNET_JSON_spec_string ("currency", - &cr.details.ok.ci.currency), - TALER_JSON_spec_version ("version", - &pv), - GNUNET_JSON_spec_string ("version", - &cr.details.ok.ci.version), - GNUNET_JSON_spec_end () - }; - - cr.details.ok.compat = TALER_MERCHANT_VC_PROTOCOL_ERROR; - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - cr.hr.http_status = 0; - cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - cr.details.ok.compat = TALER_MERCHANT_VC_MATCH; - if (MERCHANT_PROTOCOL_CURRENT < pv.current) - { - cr.details.ok.compat |= TALER_MERCHANT_VC_NEWER; - if (MERCHANT_PROTOCOL_CURRENT < pv.current - pv.age) - cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE; - } - if (MERCHANT_PROTOCOL_CURRENT > pv.current) - { - cr.details.ok.compat |= TALER_MERCHANT_VC_OLDER; - if (MERCHANT_PROTOCOL_CURRENT - MERCHANT_PROTOCOL_AGE > pv.current) - cr.details.ok.compat |= TALER_MERCHANT_VC_INCOMPATIBLE; - } - - nspec = (unsigned int) json_object_size (jcs); - if ( (nspec > MAX_CURRENCIES) || - (json_object_size (jcs) != (size_t) nspec) ) - { - GNUNET_break_op (0); - cr.hr.http_status = 0; - cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (NULL != exchanges) - { - num_eci = (unsigned int) json_object_size (exchanges); - if ( (num_eci > MAX_EXCHANGES) || - (json_object_size (exchanges) != (size_t) num_eci) ) - { - GNUNET_break_op (0); - cr.hr.http_status = 0; - cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - eci = GNUNET_new_array (num_eci, - struct TALER_MERCHANT_ExchangeConfigInfo); - for (unsigned int i = 0; i<num_eci; i++) - { - struct TALER_MERCHANT_ExchangeConfigInfo *ei = &eci[i]; - const json_t *ej = json_array_get (exchanges, - i); - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("currency", - &ei->currency), - GNUNET_JSON_spec_string ("base_url", - &ei->base_url), - GNUNET_JSON_spec_fixed_auto ("master_pub", - &ei->master_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (ej, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - cr.hr.http_status = 0; - cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_free (eci); - break; - } - } - } - { - struct TALER_CurrencySpecification *cspecs; - unsigned int off = 0; - json_t *obj; - const char *curr; - - cspecs = GNUNET_new_array (nspec, - struct TALER_CurrencySpecification); - cr.details.ok.num_cspecs = nspec; - cr.details.ok.cspecs = cspecs; - cr.details.ok.num_exchanges = (unsigned int) num_eci; - cr.details.ok.exchanges = eci; - json_object_foreach ((json_t *) jcs, curr, obj) - { - struct TALER_CurrencySpecification *cs = &cspecs[off++]; - struct GNUNET_JSON_Specification cspec[] = { - TALER_JSON_spec_currency_specification (curr, - curr, - cs), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (jcs, - cspec, - NULL, NULL)) - { - GNUNET_break_op (0); - cr.hr.http_status = 0; - cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_free (eci); - TALER_CONFIG_free_currencies (off - 1, - cspecs); - break; - } - } - vgh->cb (vgh->cb_cls, - &cr); - GNUNET_free (eci); - TALER_CONFIG_free_currencies (nspec, - cspecs); - } - TALER_MERCHANT_config_get_cancel (vgh); - return; - } - default: - /* unexpected response code */ - cr.hr.ec = TALER_JSON_get_error_code (json); - cr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) cr.hr.ec); - break; - } - vgh->cb (vgh->cb_cls, - &cr); - TALER_MERCHANT_config_get_cancel (vgh); -} - - -struct TALER_MERCHANT_ConfigGetHandle * -TALER_MERCHANT_config_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_ConfigCallback config_cb, - void *config_cb_cls) -{ - struct TALER_MERCHANT_ConfigGetHandle *vgh; - CURL *eh; - - vgh = GNUNET_new (struct TALER_MERCHANT_ConfigGetHandle); - vgh->ctx = ctx; - vgh->cb = config_cb; - vgh->cb_cls = config_cb_cls; - vgh->url = TALER_url_join (backend_url, - "config", - NULL); - if (NULL == vgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (vgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - vgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (vgh->url); - vgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_config_finished, - vgh); - return vgh; -} - - -void -TALER_MERCHANT_config_get_cancel (struct TALER_MERCHANT_ConfigGetHandle *vgh) -{ - if (NULL != vgh->job) - { - GNUNET_CURL_job_cancel (vgh->job); - vgh->job = NULL; - } - GNUNET_free (vgh->url); - GNUNET_free (vgh); -} - - -/* end of merchant_api_config_get.c */ diff --git a/src/lib/merchant_api_get_donau_instance.c b/src/lib/merchant_api_get_donau_instance.c @@ -1,299 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2024 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> - */ -/** - * @file merchant_api_get_donau_instance.c - * @brief Implementation of the GET /donau request of the merchant's HTTP API - * @author Bohdan Potuzhnyi - * @author Vlada Svirsh - */ - -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -/* DONAU RELATED IMPORTS */ -#include "taler/taler_merchant_donau.h" -#include <donau/donau_service.h> - -/** - * Handle for a GET /donau operation. - */ -struct TALER_MERCHANT_DonauInstanceGetHandle -{ - /** - * The URL for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_DonauInstanceGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - -/** - * Parse Donau instance information from @a ia. - * - * @param ia JSON array (or NULL!) with Donau instance data - * @param igr response to fill - * @param dgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_donau_instances (const json_t *ia, - struct TALER_MERCHANT_DonauInstanceGetResponse *igr, - struct TALER_MERCHANT_DonauInstanceGetHandle *dgh) -{ - unsigned int instances_len = (unsigned int) json_array_size (ia); - struct TALER_MERCHANT_DonauInstanceEntry - instances[GNUNET_NZL (instances_len)]; - size_t index; - json_t *value; - struct DONAU_Keys *donau_keys_ptr = NULL; - - if ((json_array_size (ia) != (size_t) instances_len)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - json_array_foreach (ia, - index, - value) - { - struct TALER_MERCHANT_DonauInstanceEntry *instance = &instances[index]; - const json_t *donau_keys_json = NULL; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint64 ("donau_instance_serial", - &instance->donau_instance_serial), - GNUNET_JSON_spec_string ("donau_url", - &instance->donau_url), - GNUNET_JSON_spec_string ("charity_name", - &instance->charity_name), - GNUNET_JSON_spec_fixed_auto ("charity_pub_key", - &instance->charity_pub_key), - GNUNET_JSON_spec_uint64 ("charity_id", - &instance->charity_id), - TALER_JSON_spec_amount_any ("charity_max_per_year", - &instance->charity_max_per_year), - TALER_JSON_spec_amount_any ("charity_receipts_to_date", - &instance->charity_receipts_to_date), - GNUNET_JSON_spec_int64 ("current_year", - &instance->current_year), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("donau_keys_json", - &donau_keys_json), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - /* Parse the Donau keys */ - if (NULL != donau_keys_json) - { - donau_keys_ptr = DONAU_keys_from_json (donau_keys_json); - if (NULL == donau_keys_ptr) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to convert donau keys from JSON\n"); - return GNUNET_SYSERR; - } - instance->donau_keys = donau_keys_ptr; - } - } - - igr->details.ok.donau_instances_length = instances_len; - igr->details.ok.donau_instances = instances; - dgh->cb (dgh->cb_cls, - igr); - dgh->cb = NULL; - if (NULL != donau_keys_ptr) - { - DONAU_keys_decref (donau_keys_ptr); - donau_keys_ptr= NULL; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /donau request. - * - * @param cls the `struct TALER_MERCHANT_DonauInstanceGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_donau_instances_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_DonauInstanceGetHandle *dgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_DonauInstanceGetResponse igr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - dgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /donau response with status code %u\n", - (unsigned int) response_code); - - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *donau_instances; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("donau_instances", - &donau_instances), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, - NULL)) - { - igr.hr.http_status = 0; - igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - - if (GNUNET_OK == - parse_donau_instances (donau_instances, - &igr, - dgh)) - { - TALER_MERCHANT_donau_instances_get_cancel (dgh); - return; - } - - igr.hr.http_status = 0; - igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - - case MHD_HTTP_UNAUTHORIZED: - case MHD_HTTP_NOT_FOUND: - default: - igr.hr.ec = TALER_JSON_get_error_code (json); - igr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) igr.hr.ec); - break; - } - - dgh->cb (dgh->cb_cls, - &igr); - TALER_MERCHANT_donau_instances_get_cancel (dgh); -} - - -/** - * Initiate the GET /donau request. - * - * @param ctx CURL context - * @param backend_url base URL for the backend - * @param cb callback function to handle the response - * @param cb_cls closure for the callback function - * @return the handle for the operation, or NULL on error - */ -struct TALER_MERCHANT_DonauInstanceGetHandle * -TALER_MERCHANT_donau_instances_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_DonauInstanceGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_DonauInstanceGetHandle *dgh; - CURL *eh; - - dgh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceGetHandle); - dgh->ctx = ctx; - dgh->cb = cb; - dgh->cb_cls = cb_cls; - dgh->url = TALER_url_join (backend_url, - "private/donau", - NULL); - if (NULL == dgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (dgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - dgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (dgh->url); - dgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_donau_instances_finished, - dgh); - - return dgh; -} - - -/** - * Cancel the GET /donau instances operation. - * - * @param dgh request to cancel. - */ -void -TALER_MERCHANT_donau_instances_get_cancel ( - struct TALER_MERCHANT_DonauInstanceGetHandle *dgh) -{ - if (NULL != dgh->job) - GNUNET_CURL_job_cancel (dgh->job); - GNUNET_free (dgh->url); - GNUNET_free (dgh); -} diff --git a/src/lib/merchant_api_get_instance.c b/src/lib/merchant_api_get_instance.c @@ -1,235 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_instance.c - * @brief Implementation of the GET /instance/$ID request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_kyclogic_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /instances/$ID operation. - */ -struct TALER_MERCHANT_InstanceGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_InstanceGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /instances/$ID request. - * - * @param cls the `struct TALER_MERCHANT_InstanceGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_instance_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_InstanceGetHandle *igh = cls; - const json_t *json = response; - struct TALER_MERCHANT_InstanceGetResponse igr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - igh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /instances/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *address; - const json_t *jurisdiction; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ( - "name", - &igr.details.ok.details.name), - GNUNET_JSON_spec_fixed_auto ( - "merchant_pub", - &igr.details.ok.details.merchant_pub), - GNUNET_JSON_spec_object_const ( - "address", - &address), - GNUNET_JSON_spec_object_const ( - "jurisdiction", - &jurisdiction), - GNUNET_JSON_spec_bool ( - "use_stefan", - &igr.details.ok.details.use_stefan), - GNUNET_JSON_spec_relative_time ( - "default_wire_transfer_delay", - &igr.details.ok.details.default_wire_transfer_delay), - GNUNET_JSON_spec_relative_time ( - "default_pay_delay", - &igr.details.ok.details.default_pay_delay), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_relative_time ( - "default_refund_delay", - &igr.details.ok.details.default_refund_delay), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_time_rounder_interval ( - "default_wire_transfer_rounding_interval", - &igr.details.ok.details.default_wire_transfer_rounding_interval), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - igr.hr.http_status = 0; - igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - igr.details.ok.details.address = address; - igr.details.ok.details.jurisdiction = jurisdiction; - igh->cb (igh->cb_cls, - &igr); - TALER_MERCHANT_instance_get_cancel (igh); - return; - } - case MHD_HTTP_UNAUTHORIZED: - igr.hr.ec = TALER_JSON_get_error_code (json); - igr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* instance does not exist */ - igr.hr.ec = TALER_JSON_get_error_code (json); - igr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - igr.hr.ec = TALER_JSON_get_error_code (json); - igr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) igr.hr.ec); - break; - } - igh->cb (igh->cb_cls, - &igr); - TALER_MERCHANT_instance_get_cancel (igh); -} - - -struct TALER_MERCHANT_InstanceGetHandle * -TALER_MERCHANT_instance_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - TALER_MERCHANT_InstanceGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_InstanceGetHandle *igh; - CURL *eh; - - igh = GNUNET_new (struct TALER_MERCHANT_InstanceGetHandle); - igh->ctx = ctx; - igh->cb = cb; - igh->cb_cls = cb_cls; - if (NULL != instance_id) - { - char *path; - - GNUNET_asprintf (&path, - "instances/%s/private", - instance_id); - igh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - else - { - igh->url = TALER_url_join (backend_url, - "private", - NULL); - } - if (NULL == igh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (igh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - igh->url); - eh = TALER_MERCHANT_curl_easy_get_ (igh->url); - igh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_instance_finished, - igh); - return igh; -} - - -void -TALER_MERCHANT_instance_get_cancel ( - struct TALER_MERCHANT_InstanceGetHandle *igh) -{ - if (NULL != igh->job) - GNUNET_CURL_job_cancel (igh->job); - GNUNET_free (igh->url); - GNUNET_free (igh); -} diff --git a/src/lib/merchant_api_get_instances.c b/src/lib/merchant_api_get_instances.c @@ -1,263 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_instances.c - * @brief Implementation of the GET /instances request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Maximum number of instances permitted. - */ -#define MAX_INSTANCES 1024 - -/** - * Handle for a GET /instances operation. - */ -struct TALER_MERCHANT_InstancesGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_InstancesGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse instance information from @a ia. - * - * @param json overall reply body - * @param ia JSON array (or NULL!) with instance data - * @param igh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_instances (const json_t *json, - const json_t *ia, - struct TALER_MERCHANT_InstancesGetHandle *igh) -{ - unsigned int iis_len = (unsigned int) json_array_size (ia); - - if ( (json_array_size (ia) != (size_t) iis_len) || - (iis_len > MAX_INSTANCES) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_InstanceInformation iis[GNUNET_NZL (iis_len)]; - size_t index; - json_t *value; - struct TALER_MERCHANT_InstancesGetResponse igr = { - .hr.http_status = MHD_HTTP_OK, - .hr.reply = json, - .details.ok.iis_length = iis_len, - .details.ok.iis = iis - }; - - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_InstanceInformation *ii = &iis[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("name", - &ii->name), - GNUNET_JSON_spec_string ("id", - &ii->id), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &ii->merchant_pub), - GNUNET_JSON_spec_array_const ("payment_targets", - &ii->payment_targets), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - for (size_t i = 0; i<json_array_size (ii->payment_targets); i++) - { - if (! json_is_string (json_array_get (ii->payment_targets, - i))) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - } /* for all instances */ - igh->cb (igh->cb_cls, - &igr); - igh->cb = NULL; /* just to be sure */ - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /instances request. - * - * @param cls the `struct TALER_MERCHANT_InstancesGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_instances_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_InstancesGetHandle *igh = cls; - const json_t *json = response; - struct TALER_MERCHANT_InstancesGetResponse igr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - igh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /instances response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *instances; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("instances", - &instances), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - igr.hr.http_status = 0; - igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_instances (json, - instances, - igh)) - { - TALER_MERCHANT_instances_get_cancel (igh); - return; - } - igr.hr.http_status = 0; - igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - igr.hr.ec = TALER_JSON_get_error_code (json); - igr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - igr.hr.ec = TALER_JSON_get_error_code (json); - igr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) igr.hr.ec); - break; - } - igh->cb (igh->cb_cls, - &igr); - TALER_MERCHANT_instances_get_cancel (igh); -} - - -struct TALER_MERCHANT_InstancesGetHandle * -TALER_MERCHANT_instances_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_InstancesGetCallback instances_cb, - void *instances_cb_cls) -{ - struct TALER_MERCHANT_InstancesGetHandle *igh; - CURL *eh; - - igh = GNUNET_new (struct TALER_MERCHANT_InstancesGetHandle); - igh->ctx = ctx; - igh->cb = instances_cb; - igh->cb_cls = instances_cb_cls; - igh->url = TALER_url_join (backend_url, - "management/instances", - NULL); - if (NULL == igh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (igh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - igh->url); - eh = TALER_MERCHANT_curl_easy_get_ (igh->url); - igh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_instances_finished, - igh); - return igh; -} - - -void -TALER_MERCHANT_instances_get_cancel ( - struct TALER_MERCHANT_InstancesGetHandle *igh) -{ - if (NULL != igh->job) - GNUNET_CURL_job_cancel (igh->job); - GNUNET_free (igh->url); - GNUNET_free (igh); -} diff --git a/src/lib/merchant_api_get_kyc.c b/src/lib/merchant_api_get_kyc.c @@ -1,515 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023--2024 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_kyc.c - * @brief Implementation of the GET /kyc request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Maximum length of the KYC arrays supported. - */ -#define MAX_KYC 1024 - -/** - * Handle for a GET /kyc operation. - */ -struct TALER_MERCHANT_KycGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_KycGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse @a kyc response and call the continuation on success. - * - * @param kyc operation handle - * @param[in,out] kr response details - * @param jkyc array from the reply - * @return #GNUNET_OK on success (callback was called) - */ -static enum GNUNET_GenericReturnValue -parse_kyc (struct TALER_MERCHANT_KycGetHandle *kyc, - struct TALER_MERCHANT_KycResponse *kr, - const json_t *jkyc) -{ - unsigned int num_kycs = (unsigned int) json_array_size (jkyc); - unsigned int num_limits = 0; - unsigned int num_kycauths = 0; - unsigned int pos_limits = 0; - unsigned int pos_kycauths = 0; - - if ( (json_array_size (jkyc) != (size_t) num_kycs) || - (num_kycs > MAX_KYC) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - for (unsigned int i = 0; i<num_kycs; i++) - { - const json_t *jlimits = NULL; - const json_t *jkycauths = NULL; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ( - "limits", - &jlimits), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ( - "payto_kycauths", - &jkycauths), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json_array_get (jkyc, - i), - spec, - NULL, NULL)) - { - GNUNET_break (0); - json_dumpf (json_array_get (jkyc, - i), - stderr, - JSON_INDENT (2)); - return GNUNET_SYSERR; - } - num_limits += json_array_size (jlimits); - num_kycauths += json_array_size (jkycauths); - } - - - { - struct TALER_MERCHANT_AccountKycRedirectDetail kycs[ - GNUNET_NZL (num_kycs)]; - struct TALER_EXCHANGE_AccountLimit limits[ - GNUNET_NZL (num_limits)]; - struct TALER_FullPayto payto_kycauths[ - GNUNET_NZL (num_kycauths)]; - - memset (kycs, - 0, - sizeof (kycs)); - for (unsigned int i = 0; i<num_kycs; i++) - { - struct TALER_MERCHANT_AccountKycRedirectDetail *rd - = &kycs[i]; - const json_t *jlimits = NULL; - const json_t *jkycauths = NULL; - uint32_t hs; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_full_payto_uri ( - "payto_uri", - &rd->payto_uri), - TALER_JSON_spec_web_url ( - "exchange_url", - &rd->exchange_url), - GNUNET_JSON_spec_uint32 ( - "exchange_http_status", - &hs), - GNUNET_JSON_spec_bool ( - "no_keys", - &rd->no_keys), - GNUNET_JSON_spec_bool ( - "auth_conflict", - &rd->auth_conflict), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_ec ( - "exchange_code", - &rd->exchange_code), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ( - "access_token", - &rd->access_token), - &rd->no_access_token), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ( - "limits", - &jlimits), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ( - "payto_kycauths", - &jkycauths), - NULL), - GNUNET_JSON_spec_end () - }; - size_t j; - json_t *jlimit; - json_t *jkycauth; - - if (GNUNET_OK != - GNUNET_JSON_parse (json_array_get (jkyc, - i), - spec, - NULL, NULL)) - { - GNUNET_break (0); - json_dumpf (json_array_get (jkyc, - i), - stderr, - JSON_INDENT (2)); - return GNUNET_SYSERR; - } - rd->exchange_http_status = (unsigned int) hs; - rd->limits = &limits[pos_limits]; - rd->limits_length = json_array_size (jlimits); - json_array_foreach (jlimits, j, jlimit) - { - struct TALER_EXCHANGE_AccountLimit *limit - = &limits[pos_limits]; - struct GNUNET_JSON_Specification jspec[] = { - TALER_JSON_spec_kycte ( - "operation_type", - &limit->operation_type), - GNUNET_JSON_spec_relative_time ( - "timeframe", - &limit->timeframe), - TALER_JSON_spec_amount_any ( - "threshold", - &limit->threshold), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_bool ( - "soft_limit", - &limit->soft_limit), - NULL), - GNUNET_JSON_spec_end () - }; - - GNUNET_assert (pos_limits < num_limits); - limit->soft_limit = false; - if (GNUNET_OK != - GNUNET_JSON_parse (jlimit, - jspec, - NULL, NULL)) - { - GNUNET_break (0); - json_dumpf (json_array_get (jkyc, - i), - stderr, - JSON_INDENT (2)); - return GNUNET_SYSERR; - } - pos_limits++; - } - rd->payto_kycauths = &payto_kycauths[pos_kycauths]; - rd->pkycauth_length = json_array_size (jkycauths); - json_array_foreach (jkycauths, j, jkycauth) - { - GNUNET_assert (pos_kycauths < num_kycauths); - payto_kycauths[pos_kycauths].full_payto - = (char *) json_string_value (jkycauth); - if (NULL == payto_kycauths[pos_kycauths].full_payto) - { - GNUNET_break (0); - json_dumpf (json_array_get (jkyc, - i), - stderr, - JSON_INDENT (2)); - return GNUNET_SYSERR; - } - pos_kycauths++; - } - } - kr->details.ok.kycs = kycs; - kr->details.ok.kycs_length = num_kycs; - kyc->cb (kyc->cb_cls, - kr); - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /kyc request. - * - * @param cls the `struct TALER_MERCHANT_KycGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_kyc_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_KycGetHandle *kyc = cls; - const json_t *json = response; - struct TALER_MERCHANT_KycResponse kr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - kyc->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /kyc response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *jkyc; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("kyc_data", - &jkyc), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - kr.hr.http_status = 0; - kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK != - parse_kyc (kyc, - &kr, - jkyc)) - { - kr.hr.http_status = 0; - kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - /* parse_kyc called the continuation already */ - TALER_MERCHANT_kyc_get_cancel (kyc); - return; - } - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - kr.hr.ec = TALER_JSON_get_error_code (json); - kr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_SERVICE_UNAVAILABLE: - break; - default: - /* unexpected response code */ - kr.hr.ec = TALER_JSON_get_error_code (json); - kr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) kr.hr.ec); - break; - } - kyc->cb (kyc->cb_cls, - &kr); - TALER_MERCHANT_kyc_get_cancel (kyc); -} - - -/** - * Issue a GET KYC request to the backend. - * Returns KYC status of bank accounts. - * - * @param ctx execution context - * @param[in] url URL to use for the request, consumed! - * @param h_wire which bank account to query, NULL for all - * @param exchange_url which exchange to query, NULL for all - * @param lpt target for long polling - * @param timeout how long to wait for a reply - * @param cb function to call with the result - * @param cb_cls closure for @a cb - * @return handle for this operation, NULL upon errors - */ -static struct TALER_MERCHANT_KycGetHandle * -kyc_get (struct GNUNET_CURL_Context *ctx, - char *url, - const struct TALER_MerchantWireHashP *h_wire, - const char *exchange_url, - enum TALER_EXCHANGE_KycLongPollTarget lpt, - struct GNUNET_TIME_Relative timeout, - TALER_MERCHANT_KycGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_KycGetHandle *kyc; - CURL *eh; - char timeout_ms[32]; - char lpt_str[32]; - unsigned long long tms; - - kyc = GNUNET_new (struct TALER_MERCHANT_KycGetHandle); - kyc->ctx = ctx; - kyc->cb = cb; - kyc->cb_cls = cb_cls; - GNUNET_snprintf (lpt_str, - sizeof (lpt_str), - "%d", - (int) lpt); - tms = timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; - GNUNET_snprintf (timeout_ms, - sizeof (timeout_ms), - "%llu", - tms); - kyc->url - = TALER_url_join ( - url, - "kyc", - "h_wire", - NULL == h_wire - ? NULL - : GNUNET_h2s_full (&h_wire->hash), - "exchange_url", - NULL == exchange_url - ? NULL - : exchange_url, - "timeout_ms", - GNUNET_TIME_relative_is_zero (timeout) - ? NULL - : timeout_ms, - "lpt", - TALER_EXCHANGE_KLPT_NONE == lpt - ? NULL - : lpt_str, - NULL); - GNUNET_free (url); - if (NULL == kyc->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (kyc); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - kyc->url); - eh = TALER_MERCHANT_curl_easy_get_ (kyc->url); - if (0 != tms) - { - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TIMEOUT_MS, - (long) (tms + 100L))); - } - kyc->job - = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_kyc_finished, - kyc); - return kyc; -} - - -struct TALER_MERCHANT_KycGetHandle * -TALER_MERCHANT_kyc_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_MerchantWireHashP *h_wire, - const char *exchange_url, - enum TALER_EXCHANGE_KycLongPollTarget lpt, - struct GNUNET_TIME_Relative timeout, - TALER_MERCHANT_KycGetCallback cb, - void *cb_cls) -{ - char *url; - - GNUNET_asprintf (&url, - "%sprivate/", - backend_url); - return kyc_get (ctx, - url, /* consumed! */ - h_wire, - exchange_url, - lpt, - timeout, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_KycGetHandle * -TALER_MERCHANT_management_kyc_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - const struct TALER_MerchantWireHashP *h_wire, - const char *exchange_url, - enum TALER_EXCHANGE_KycLongPollTarget lpt, - struct GNUNET_TIME_Relative timeout, - TALER_MERCHANT_KycGetCallback cb, - void *cb_cls) -{ - char *url; - - GNUNET_asprintf (&url, - "%smanagement/instances/%s/", - backend_url, - instance_id); - return kyc_get (ctx, - url, /* consumed! */ - h_wire, - exchange_url, - lpt, - timeout, - cb, - cb_cls); -} - - -void -TALER_MERCHANT_kyc_get_cancel ( - struct TALER_MERCHANT_KycGetHandle *kyc) -{ - if (NULL != kyc->job) - GNUNET_CURL_job_cancel (kyc->job); - GNUNET_free (kyc->url); - GNUNET_free (kyc); -} diff --git a/src/lib/merchant_api_get_orders.c b/src/lib/merchant_api_get_orders.c @@ -1,442 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_orders.c - * @brief Implementation of the GET /private/orders request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - -/** - * Maximum number of orders we return. - */ -#define MAX_ORDERS 1024 - -/** - * Handle for a GET /orders operation. - */ -struct TALER_MERCHANT_OrdersGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OrdersGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse order information from @a ia. - * - * @param ia JSON array (or NULL!) with order data - * @param[in] ogr response to fill - * @param ogh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_orders (const json_t *ia, - struct TALER_MERCHANT_OrdersGetResponse *ogr, - struct TALER_MERCHANT_OrdersGetHandle *ogh) -{ - unsigned int oes_len = (unsigned int) json_array_size (ia); - - if ( (json_array_size (ia) != (size_t) oes_len) || - (oes_len > MAX_ORDERS) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_OrderEntry oes[GNUNET_NZL (oes_len)]; - size_t index; - json_t *value; - - memset (oes, - 0, - sizeof (oes)); - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_OrderEntry *ie = &oes[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("order_id", - &ie->order_id), - GNUNET_JSON_spec_timestamp ("timestamp", - &ie->timestamp), - GNUNET_JSON_spec_uint64 ("row_id", - &ie->order_serial), - TALER_JSON_spec_amount_any ("amount", - &ie->amount), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_amount_any ("refund_amount", - &ie->refund_amount), - NULL), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_amount_any ("pending_refund_amount", - &ie->pending_refund_amount), - NULL), - GNUNET_JSON_spec_string ("summary", - &ie->summary), - GNUNET_JSON_spec_bool ("refundable", - &ie->refundable), - GNUNET_JSON_spec_bool ("paid", - &ie->paid), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - ogr->details.ok.orders_length = oes_len; - ogr->details.ok.orders = oes; - ogh->cb (ogh->cb_cls, - ogr); - ogh->cb = NULL; /* just to be sure */ - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /orders request. - * - * @param cls the `struct TALER_MERCHANT_OrdersGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_orders_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrdersGetHandle *ogh = cls; - const json_t *json = response; - struct TALER_MERCHANT_OrdersGetResponse ogr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - ogh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /orders response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *orders; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("orders", - &orders), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - ogr.hr.http_status = 0; - ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_orders (orders, - &ogr, - ogh)) - { - TALER_MERCHANT_orders_get_cancel (ogh); - return; - } - ogr.hr.http_status = 0; - ogr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - ogr.hr.ec = TALER_JSON_get_error_code (json); - ogr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - ogr.hr.ec = TALER_JSON_get_error_code (json); - ogr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - ogr.hr.ec = TALER_JSON_get_error_code (json); - ogr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) ogr.hr.ec); - break; - } - ogh->cb (ogh->cb_cls, - &ogr); - TALER_MERCHANT_orders_get_cancel (ogh); -} - - -struct TALER_MERCHANT_OrdersGetHandle * -TALER_MERCHANT_orders_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_OrdersGetCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_orders_get2 (ctx, - backend_url, - TALER_EXCHANGE_YNA_ALL, - TALER_EXCHANGE_YNA_ALL, - TALER_EXCHANGE_YNA_ALL, - GNUNET_TIME_UNIT_FOREVER_TS, - UINT64_MAX, - -20, /* default is most recent 20 entries */ - GNUNET_TIME_UNIT_ZERO, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_OrdersGetHandle * -TALER_MERCHANT_orders_get2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - enum TALER_EXCHANGE_YesNoAll paid, - enum TALER_EXCHANGE_YesNoAll refunded, - enum TALER_EXCHANGE_YesNoAll wired, - struct GNUNET_TIME_Timestamp date, - uint64_t start_row, - int64_t delta, - struct GNUNET_TIME_Relative timeout, - TALER_MERCHANT_OrdersGetCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_orders_get3 ( - ctx, - backend_url, - paid, - refunded, - wired, - NULL, - NULL, - date, - start_row, - delta, - timeout, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_OrdersGetHandle * -TALER_MERCHANT_orders_get3 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - enum TALER_EXCHANGE_YesNoAll paid, - enum TALER_EXCHANGE_YesNoAll refunded, - enum TALER_EXCHANGE_YesNoAll wired, - const char *session_id, - const char *fulfillment_url, - struct GNUNET_TIME_Timestamp date, - uint64_t start_row, - int64_t delta, - struct GNUNET_TIME_Relative timeout, - TALER_MERCHANT_OrdersGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OrdersGetHandle *ogh; - CURL *eh; - unsigned int tms = timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; - - GNUNET_assert (NULL != backend_url); - if ( (delta > MAX_ORDERS) || - (delta < -MAX_ORDERS) ) - { - GNUNET_break (0); - return NULL; - } - if (0 == delta) - { - GNUNET_break (0); - return NULL; - } - ogh = GNUNET_new (struct TALER_MERCHANT_OrdersGetHandle); - ogh->ctx = ctx; - ogh->cb = cb; - ogh->cb_cls = cb_cls; - - /* build ogh->url with the various optional arguments */ - { - char dstr[30]; - char *fec = NULL; - char *sid = NULL; - bool have_date; - bool have_srow; - char cbuf[30]; - char dbuf[30]; - char tbuf[30]; - - GNUNET_snprintf (tbuf, - sizeof (tbuf), - "%u", - tms); - GNUNET_snprintf (dbuf, - sizeof (dbuf), - "%lld", - (long long) delta); - GNUNET_snprintf (cbuf, - sizeof (cbuf), - "%llu", - (unsigned long long) start_row); - if (NULL != session_id) - (void) GNUNET_STRINGS_urlencode (strlen (session_id), - session_id, - &sid); - if (NULL != fulfillment_url) - (void) GNUNET_STRINGS_urlencode (strlen (fulfillment_url), - fulfillment_url, - &fec); - GNUNET_snprintf (dstr, - sizeof (dstr), - "%llu", - (unsigned long long) GNUNET_TIME_timestamp_to_s (date)); - if (delta > 0) - { - have_date = ! GNUNET_TIME_absolute_is_zero (date.abs_time); - have_srow = (0 != start_row); - } - else - { - have_date = ! GNUNET_TIME_absolute_is_never (date.abs_time); - have_srow = (UINT64_MAX != start_row); - } - ogh->url = TALER_url_join (backend_url, - "private/orders", - "paid", - (TALER_EXCHANGE_YNA_ALL != paid) - ? TALER_yna_to_string (paid) - : NULL, - "refunded", - (TALER_EXCHANGE_YNA_ALL != refunded) - ? TALER_yna_to_string (refunded) - : NULL, - "wired", - (TALER_EXCHANGE_YNA_ALL != wired) - ? TALER_yna_to_string (wired) - : NULL, - "date_s", - (have_date) - ? dstr - : NULL, - "start", - (have_srow) - ? cbuf - : NULL, - "delta", - (-20 != delta) - ? dbuf - : NULL, - "timeout_ms", - (0 != tms) - ? tbuf - : NULL, - "session_id", - sid, - "fulfillment_url", - fec, - NULL); - GNUNET_free (sid); - GNUNET_free (fec); - } - if (NULL == ogh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (ogh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - ogh->url); - eh = TALER_MERCHANT_curl_easy_get_ (ogh->url); - if (NULL == eh) - { - GNUNET_break (0); - GNUNET_free (ogh->url); - GNUNET_free (ogh); - return NULL; - } - if (0 != tms) - { - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TIMEOUT_MS, - (long) (tms + 100L))); - } - ogh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_orders_finished, - ogh); - return ogh; -} - - -void -TALER_MERCHANT_orders_get_cancel ( - struct TALER_MERCHANT_OrdersGetHandle *ogh) -{ - if (NULL != ogh->job) - GNUNET_CURL_job_cancel (ogh->job); - GNUNET_free (ogh->url); - GNUNET_free (ogh); -} diff --git a/src/lib/merchant_api_get_otp_device.c b/src/lib/merchant_api_get_otp_device.c @@ -1,210 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_otp_device.c - * @brief Implementation of the GET /otp-devices/$ID request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /otp-devices/$ID operation. - */ -struct TALER_MERCHANT_OtpDeviceGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OtpDeviceGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /otp-devices/$ID request. - * - * @param cls the `struct TALER_MERCHANT_OtpDeviceGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_otp_device_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OtpDeviceGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_OtpDeviceGetResponse tgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /otp-devices/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - uint32_t alg32; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("otp_device_description", - &tgr.details.ok.otp_device_description), - GNUNET_JSON_spec_uint32 ("otp_algorithm", - &alg32), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint64 ("otp_ctr", - &tgr.details.ok.otp_ctr), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint64 ("otp_timestamp", - &tgr.details.ok.otp_timestamp_s), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("otp_code", - &tgr.details.ok.otp_code), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tgr.details.ok.otp_alg = - (enum TALER_MerchantConfirmationAlgorithm) alg32; - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_otp_device_get_cancel (tgh); - return; - } - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_otp_device_get_cancel (tgh); -} - - -struct TALER_MERCHANT_OtpDeviceGetHandle * -TALER_MERCHANT_otp_device_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *otp_device_id, - TALER_MERCHANT_OtpDeviceGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OtpDeviceGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/otp-devices/%s", - otp_device_id); - tgh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_otp_device_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_otp_device_get_cancel ( - struct TALER_MERCHANT_OtpDeviceGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_get_otp_devices.c b/src/lib/merchant_api_get_otp_devices.c @@ -1,248 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_otp_devices.c - * @brief Implementation of the GET /otp-devices request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - -/** - * Maximum number of OTP devices we return. - */ -#define MAX_OTP 1024 - - -/** - * Handle for a GET /otp-devices operation. - */ -struct TALER_MERCHANT_OtpDevicesGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OtpDevicesGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse OTP device information from @a ia. - * - * @param ia JSON array (or NULL!) with otp_device data - * @param[in] tgr partially filled response - * @param tgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_otp_devices (const json_t *ia, - struct TALER_MERCHANT_OtpDevicesGetResponse *tgr, - struct TALER_MERCHANT_OtpDevicesGetHandle *tgh) -{ - unsigned int otp_len = (unsigned int) json_array_size (ia); - - if ( (json_array_size (ia) != (size_t) otp_len) || - (otp_len > MAX_OTP) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_OtpDeviceEntry otp[GNUNET_NZL (otp_len)]; - size_t index; - json_t *value; - - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_OtpDeviceEntry *ie = &otp[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("otp_device_id", - &ie->otp_device_id), - GNUNET_JSON_spec_string ("device_description", - &ie->otp_device_description), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - tgr->details.ok.otp_devices_length = otp_len; - tgr->details.ok.otp_devices = otp; - tgh->cb (tgh->cb_cls, - tgr); - tgh->cb = NULL; /* just to be sure */ - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /otp-devices request. - * - * @param cls the `struct TALER_MERCHANT_OtpDevicesGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_otp_devices_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OtpDevicesGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_OtpDevicesGetResponse tgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /otp-devices response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *otp_devices; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("otp_devices", - &otp_devices), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_otp_devices (otp_devices, - &tgr, - tgh)) - { - TALER_MERCHANT_otp_devices_get_cancel (tgh); - return; - } - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_otp_devices_get_cancel (tgh); -} - - -struct TALER_MERCHANT_OtpDevicesGetHandle * -TALER_MERCHANT_otp_devices_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_OtpDevicesGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OtpDevicesGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_OtpDevicesGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - tgh->url = TALER_url_join (backend_url, - "private/otp-devices", - NULL); - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_otp_devices_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_otp_devices_get_cancel ( - struct TALER_MERCHANT_OtpDevicesGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_get_product.c b/src/lib/merchant_api_get_product.c @@ -1,249 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014--2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_product.c - * @brief Implementation of the GET /product/$ID request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /products/$ID operation. - */ -struct TALER_MERCHANT_ProductGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ProductGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /products/$ID request. - * - * @param cls the `struct TALER_MERCHANT_ProductGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_product_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ProductGetHandle *pgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_ProductGetResponse pgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - pgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /products/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ( - "product_name", - &pgr.details.ok.product_name), - GNUNET_JSON_spec_string ( - "description", - &pgr.details.ok.description), - GNUNET_JSON_spec_object_const ( - "description_i18n", - &pgr.details.ok.description_i18n), - GNUNET_JSON_spec_string ( - "unit", - &pgr.details.ok.unit), - TALER_JSON_spec_amount_any_array ( - "unit_price", - &pgr.details.ok.unit_price_len, - (struct TALER_Amount **) &pgr.details.ok.unit_price), - TALER_JSON_spec_amount_any ( - "price", - &pgr.details.ok.price), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ( - "image", - &pgr.details.ok.image), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ( - "taxes", - &pgr.details.ok.taxes), - NULL), - GNUNET_JSON_spec_int64 ( - "total_stock", - &pgr.details.ok.total_stock), - GNUNET_JSON_spec_string ( - "unit_total_stock", - &pgr.details.ok.unit_total_stock), - GNUNET_JSON_spec_bool ( - "unit_allow_fraction", - &pgr.details.ok.unit_allow_fraction), - GNUNET_JSON_spec_uint32 ( - "unit_precision_level", - &pgr.details.ok.unit_precision_level), - GNUNET_JSON_spec_uint64 ( - "total_sold", - &pgr.details.ok.total_sold), - GNUNET_JSON_spec_uint64 ( - "total_lost", - &pgr.details.ok.total_lost), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ( - "address", - &pgr.details.ok.location), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ( - "next_restock", - &pgr.details.ok.next_restock), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - pgh->cb (pgh->cb_cls, - &pgr); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_product_get_cancel (pgh); - return; - } - pgr.hr.http_status = 0; - pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - pgr.hr.ec = TALER_JSON_get_error_code (json); - pgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - pgr.hr.ec = TALER_JSON_get_error_code (json); - pgr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - pgr.hr.ec = TALER_JSON_get_error_code (json); - pgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) pgr.hr.ec); - break; - } - pgh->cb (pgh->cb_cls, - &pgr); - TALER_MERCHANT_product_get_cancel (pgh); -} - - -struct TALER_MERCHANT_ProductGetHandle * -TALER_MERCHANT_product_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - TALER_MERCHANT_ProductGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ProductGetHandle *pgh; - CURL *eh; - - pgh = GNUNET_new (struct TALER_MERCHANT_ProductGetHandle); - pgh->ctx = ctx; - pgh->cb = cb; - pgh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/products/%s", - product_id); - pgh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == pgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (pgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - pgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (pgh->url); - pgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_product_finished, - pgh); - return pgh; -} - - -void -TALER_MERCHANT_product_get_cancel ( - struct TALER_MERCHANT_ProductGetHandle *pgh) -{ - if (NULL != pgh->job) - GNUNET_CURL_job_cancel (pgh->job); - GNUNET_free (pgh->url); - GNUNET_free (pgh); -} diff --git a/src/lib/merchant_api_get_product_image.c b/src/lib/merchant_api_get_product_image.c @@ -1,188 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_product_image.c - * @brief Implementation of the GET /products/$HASH/image request - * @author Bohdan Potuzhnyi - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include <taler/taler_error_codes.h> -#include <taler/taler_json_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" - - -/** - * Handle for a GET /products/$HASH/image operation. - */ -struct TALER_MERCHANT_ProductImageGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ProductImageGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP GET /products/$HASH/image request. - * - * @param cls the `struct TALER_MERCHANT_ProductImageGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_product_image_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ProductImageGetHandle *pigh = cls; - const json_t *json = response; - struct TALER_MERCHANT_ProductImageGetResponse pir = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - pigh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /products/$HASH/image response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("image", - &pir.details.ok.image), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - pigh->cb (pigh->cb_cls, - &pir); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_product_image_get_cancel (pigh); - return; - } - pir.hr.http_status = 0; - pir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_NOT_FOUND: - case MHD_HTTP_BAD_REQUEST: - pir.hr.ec = TALER_JSON_get_error_code (json); - pir.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - pir.hr.ec = TALER_JSON_get_error_code (json); - pir.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) pir.hr.ec); - break; - } - pigh->cb (pigh->cb_cls, - &pir); - TALER_MERCHANT_product_image_get_cancel (pigh); -} - - -struct TALER_MERCHANT_ProductImageGetHandle * -TALER_MERCHANT_product_image_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *image_hash, - TALER_MERCHANT_ProductImageGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ProductImageGetHandle *pigh; - CURL *eh; - - pigh = GNUNET_new (struct TALER_MERCHANT_ProductImageGetHandle); - pigh->ctx = ctx; - pigh->cb = cb; - pigh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "products/%s/image", - image_hash); - pigh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == pigh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (pigh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - pigh->url); - eh = TALER_MERCHANT_curl_easy_get_ (pigh->url); - pigh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_product_image_finished, - pigh); - return pigh; -} - - -void -TALER_MERCHANT_product_image_get_cancel ( - struct TALER_MERCHANT_ProductImageGetHandle *pigh) -{ - if (NULL != pigh->job) - GNUNET_CURL_job_cancel (pigh->job); - GNUNET_free (pigh->url); - GNUNET_free (pigh); -} -\ No newline at end of file diff --git a/src/lib/merchant_api_get_products.c b/src/lib/merchant_api_get_products.c @@ -1,262 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2024 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_products.c - * @brief Implementation of the GET /products request of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Maximum number of products we return. - */ -#define MAX_PRODUCTS 1024 - - -/** - * Handle for a GET /products operation. - */ -struct TALER_MERCHANT_ProductsGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ProductsGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse product information from @a ia. - * - * @param json overall JSON reply - * @param ia JSON array (or NULL!) with product data - * @param pgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_products (const json_t *json, - const json_t *ia, - struct TALER_MERCHANT_ProductsGetHandle *pgh) -{ - unsigned int ies_len = json_array_size (ia); - - if ( (json_array_size (ia) != (size_t) ies_len) || - (ies_len > MAX_PRODUCTS) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_InventoryEntry ies[GNUNET_NZL (ies_len)]; - size_t index; - json_t *value; - enum GNUNET_GenericReturnValue ret; - - ret = GNUNET_OK; - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_InventoryEntry *ie = &ies[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("product_id", - &ie->product_id), - GNUNET_JSON_spec_uint64 ("product_serial", - &ie->product_serial), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; - } - if (GNUNET_OK == ret) - { - struct TALER_MERCHANT_GetProductsResponse gpr = { - .hr.http_status = MHD_HTTP_OK, - .hr.reply = json, - .details.ok.products_length = ies_len, - .details.ok.products = ies - }; - - pgh->cb (pgh->cb_cls, - &gpr); - pgh->cb = NULL; /* just to be sure */ - } - return ret; - } -} - - -/** - * Function called when we're done processing the - * HTTP /products request. - * - * @param cls the `struct TALER_MERCHANT_ProductsGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_products_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ProductsGetHandle *pgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_GetProductsResponse gpr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - pgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /products response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *products; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("products", - &products), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - gpr.hr.http_status = 0; - gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_products (json, - products, - pgh)) - { - TALER_MERCHANT_products_get_cancel (pgh); - return; - } - gpr.hr.http_status = 0; - gpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - gpr.hr.ec = TALER_JSON_get_error_code (json); - gpr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - gpr.hr.ec = TALER_JSON_get_error_code (json); - gpr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) gpr.hr.ec); - break; - } - pgh->cb (pgh->cb_cls, - &gpr); - TALER_MERCHANT_products_get_cancel (pgh); -} - - -struct TALER_MERCHANT_ProductsGetHandle * -TALER_MERCHANT_products_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_ProductsGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ProductsGetHandle *pgh; - CURL *eh; - - pgh = GNUNET_new (struct TALER_MERCHANT_ProductsGetHandle); - pgh->ctx = ctx; - pgh->cb = cb; - pgh->cb_cls = cb_cls; - pgh->url = TALER_url_join (backend_url, - "private/products", - NULL); - if (NULL == pgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (pgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - pgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (pgh->url); - pgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_products_finished, - pgh); - return pgh; -} - - -void -TALER_MERCHANT_products_get_cancel ( - struct TALER_MERCHANT_ProductsGetHandle *pgh) -{ - if (NULL != pgh->job) - GNUNET_CURL_job_cancel (pgh->job); - GNUNET_free (pgh->url); - GNUNET_free (pgh); -} diff --git a/src/lib/merchant_api_get_statistics.c b/src/lib/merchant_api_get_statistics.c @@ -1,717 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_statistics.c - * @brief Implementation of the GET /statistics-[counter,amount]/$SLUG request of the merchant's HTTP API - * @author Martin Schanzenbach - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <gnunet/gnunet_common.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - -/** - * Maximum number of statistics we return - */ -#define MAX_STATISTICS 1024 - -/** - * Handle for a GET /statistics-amount/$SLUG operation. - */ -struct TALER_MERCHANT_StatisticsAmountGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_StatisticsAmountGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - -/** - * Handle for a GET /statistics-counter/$SLUG operation. - */ -struct TALER_MERCHANT_StatisticsCounterGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_StatisticsCounterGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse interval information from buckets and intervals. - * - * @param json overall JSON reply - * @param jbuckets JSON array (or NULL!) with bucket data - * @param buckets_description human-readable description for the buckets - * @param jintervals JSON array (or NULL!) with bucket data - * @param intervals_description human-readable description for the intervals - * @param sgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_intervals_and_buckets_amt ( - const json_t *json, - const json_t *jbuckets, - const char *buckets_description, - const json_t *jintervals, - const char *intervals_description, - struct TALER_MERCHANT_StatisticsAmountGetHandle *sgh - ) -{ - unsigned int resp_buckets_len = json_array_size (jbuckets); - unsigned int resp_intervals_len = json_array_size (jintervals); - - if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) || - (json_array_size (jintervals) != (size_t) resp_intervals_len) || - (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_StatisticAmountByBucket resp_buckets[ - GNUNET_NZL (resp_buckets_len)]; - struct TALER_MERCHANT_StatisticAmountByInterval resp_intervals[ - GNUNET_NZL (resp_intervals_len)]; - size_t index; - json_t *value; - enum GNUNET_GenericReturnValue ret; - - ret = GNUNET_OK; - json_array_foreach (jintervals, index, value) { - struct TALER_MERCHANT_StatisticAmountByInterval *jinterval - = &resp_intervals[index]; - const json_t *amounts_arr; - size_t amounts_len; - - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("start_time", - &jinterval->start_time), - GNUNET_JSON_spec_array_const ("cumulative_amounts", - &amounts_arr), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; - amounts_len = json_array_size (amounts_arr); - { - struct TALER_Amount amt_arr[amounts_len]; - size_t aindex; - json_t *avalue; - - jinterval->cumulative_amount_len = amounts_len; - jinterval->cumulative_amounts = amt_arr; - json_array_foreach (amounts_arr, aindex, avalue) { - if (! json_is_string (avalue)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_string_to_amount (json_string_value (avalue), - &amt_arr[aindex])) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - } - } - ret = GNUNET_OK; - json_array_foreach (jbuckets, index, value) { - struct TALER_MERCHANT_StatisticAmountByBucket *jbucket - = &resp_buckets[index]; - const json_t *amounts_arr; - size_t amounts_len; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("start_time", - &jbucket->start_time), - GNUNET_JSON_spec_timestamp ("end_time", - &jbucket->end_time), - GNUNET_JSON_spec_string ("range", - &jbucket->range), - GNUNET_JSON_spec_array_const ("cumulative_amounts", - &amounts_arr), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; - amounts_len = json_array_size (amounts_arr); - if (0 > amounts_len) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - break; - } - { - struct TALER_Amount amt_arr[amounts_len]; - size_t aindex; - json_t *avalue; - jbucket->cumulative_amount_len = amounts_len; - jbucket->cumulative_amounts = amt_arr; - json_array_foreach (amounts_arr, aindex, avalue) { - if (! json_is_string (avalue)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_string_to_amount (json_string_value (avalue), - &amt_arr[aindex])) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - } - } - if (GNUNET_OK == ret) - { - struct TALER_MERCHANT_StatisticsAmountGetResponse gsr = { - .hr.http_status = MHD_HTTP_OK, - .hr.reply = json, - .details.ok.buckets_length = resp_buckets_len, - .details.ok.buckets = resp_buckets, - .details.ok.buckets_description = buckets_description, - .details.ok.intervals_length = resp_intervals_len, - .details.ok.intervals = resp_intervals, - .details.ok.intervals_description = intervals_description, - }; - sgh->cb (sgh->cb_cls, - &gsr); - sgh->cb = NULL; /* just to be sure */ - } - return ret; - } -} - - -/** - * Function called when we're done processing the - * HTTP GET /statistics-amount/$SLUG request. - * - * @param cls the `struct TALER_MERCHANT_StatisticsAmountGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_statistics_amount_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_StatisticsAmountGetHandle *handle = cls; - const json_t *json = response; - struct TALER_MERCHANT_StatisticsAmountGetResponse res = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - handle->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /statistics-amount/$SLUG response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *buckets; - const json_t *intervals; - const char *buckets_description = NULL; - const char *intervals_description = NULL; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("buckets", - &buckets), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("buckets_description", - &buckets_description), - NULL), - GNUNET_JSON_spec_array_const ("intervals", - &intervals), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("intervals_description", - &intervals_description), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - res.hr.http_status = 0; - res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_intervals_and_buckets_amt (json, - buckets, - buckets_description, - intervals, - intervals_description, - handle)) - { - TALER_MERCHANT_statistic_amount_get_cancel (handle); - return; - } - res.hr.http_status = 0; - res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) res.hr.ec); - break; - } -} - - -/** - * Parse interval information from @a ia. - * - * @param json overall JSON reply - * @param jbuckets JSON array (or NULL!) with bucket data - * @param buckets_description human-readable description for the buckets - * @param jintervals JSON array (or NULL!) with bucket data - * @param intervals_description human-readable description for the intervals - * @param scgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_intervals_and_buckets ( - const json_t *json, - const json_t *jbuckets, - const char *buckets_description, - const json_t *jintervals, - const char *intervals_description, - struct TALER_MERCHANT_StatisticsCounterGetHandle *scgh) -{ - unsigned int resp_buckets_len = json_array_size (jbuckets); - unsigned int resp_intervals_len = json_array_size (jintervals); - - if ( (json_array_size (jbuckets) != (size_t) resp_buckets_len) || - (json_array_size (jintervals) != (size_t) resp_intervals_len) || - (resp_intervals_len = resp_buckets_len > MAX_STATISTICS) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_StatisticCounterByBucket resp_buckets[ - GNUNET_NZL (resp_buckets_len)]; - struct TALER_MERCHANT_StatisticCounterByInterval resp_intervals[ - GNUNET_NZL (resp_intervals_len)]; - size_t index; - json_t *value; - enum GNUNET_GenericReturnValue ret; - - ret = GNUNET_OK; - json_array_foreach (jintervals, index, value) { - struct TALER_MERCHANT_StatisticCounterByInterval *jinterval - = &resp_intervals[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("start_time", - &jinterval->start_time), - GNUNET_JSON_spec_uint64 ("cumulative_counter", - &jinterval->cumulative_counter), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; - } - ret = GNUNET_OK; - json_array_foreach (jbuckets, index, value) { - struct TALER_MERCHANT_StatisticCounterByBucket *jbucket = &resp_buckets[ - index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("start_time", - &jbucket->start_time), - GNUNET_JSON_spec_timestamp ("end_time", - &jbucket->end_time), - GNUNET_JSON_spec_string ("range", - &jbucket->range), - GNUNET_JSON_spec_uint64 ("cumulative_counter", - &jbucket->cumulative_counter), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - continue; - } - if (GNUNET_SYSERR == ret) - break; - } - if (GNUNET_OK == ret) - { - struct TALER_MERCHANT_StatisticsCounterGetResponse gsr = { - .hr.http_status = MHD_HTTP_OK, - .hr.reply = json, - .details.ok.buckets_length = resp_buckets_len, - .details.ok.buckets = resp_buckets, - .details.ok.buckets_description = buckets_description, - .details.ok.intervals_length = resp_intervals_len, - .details.ok.intervals = resp_intervals, - .details.ok.intervals_description = intervals_description, - }; - scgh->cb (scgh->cb_cls, - &gsr); - scgh->cb = NULL; /* just to be sure */ - } - return ret; - } -} - - -/** - * Function called when we're done processing the - * HTTP GET /statistics-counter/$SLUG request. - * - * @param cls the `struct TALER_MERCHANT_StatisticsCounterGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_statistics_counter_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_StatisticsCounterGetHandle *handle = cls; - const json_t *json = response; - struct TALER_MERCHANT_StatisticsCounterGetResponse res = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - handle->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /statistics-counter/$SLUG response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *buckets; - const json_t *intervals; - const char *buckets_description; - const char *intervals_description; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("buckets", - &buckets), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("buckets_description", - &buckets_description), - NULL), - GNUNET_JSON_spec_array_const ("intervals", - &intervals), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("intervals_description", - &intervals_description), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - res.hr.http_status = 0; - res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "%s\n", json_dumps (json, JSON_INDENT (1))); - if (GNUNET_OK == - parse_intervals_and_buckets (json, - buckets, - buckets_description, - intervals, - intervals_description, - handle)) - { - TALER_MERCHANT_statistic_counter_get_cancel (handle); - return; - } - res.hr.http_status = 0; - res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) res.hr.ec); - break; - } -} - - -struct TALER_MERCHANT_StatisticsCounterGetHandle * -TALER_MERCHANT_statistic_counter_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *slug, - enum TALER_MERCHANT_StatisticsType stype, - TALER_MERCHANT_StatisticsCounterGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_StatisticsCounterGetHandle *handle; - CURL *eh; - - handle = GNUNET_new (struct TALER_MERCHANT_StatisticsCounterGetHandle); - handle->ctx = ctx; - handle->cb = cb; - handle->cb_cls = cb_cls; - { - const char *filter = NULL; - char *path; - - switch (stype) - { - case TALER_MERCHANT_STATISTICS_BY_BUCKET: - filter = "bucket"; - break; - case TALER_MERCHANT_STATISTICS_BY_INTERVAL: - filter = "interval"; - break; - case TALER_MERCHANT_STATISTICS_ALL: - filter = NULL; - break; - } - GNUNET_asprintf (&path, - "private/statistics-counter/%s", - slug); - handle->url = TALER_url_join (backend_url, - path, - "by", - filter, - NULL); - GNUNET_free (path); - } - if (NULL == handle->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (handle); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - handle->url); - eh = TALER_MERCHANT_curl_easy_get_ (handle->url); - handle->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_statistics_counter_finished, - handle); - return handle; -} - - -void -TALER_MERCHANT_statistic_counter_get_cancel ( - struct TALER_MERCHANT_StatisticsCounterGetHandle *handle) -{ - if (NULL != handle->job) - GNUNET_CURL_job_cancel (handle->job); - GNUNET_free (handle->url); - GNUNET_free (handle); -} - - -struct TALER_MERCHANT_StatisticsAmountGetHandle * -TALER_MERCHANT_statistic_amount_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *slug, - enum TALER_MERCHANT_StatisticsType stype, - TALER_MERCHANT_StatisticsAmountGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_StatisticsAmountGetHandle *handle; - CURL *eh; - - handle = GNUNET_new (struct TALER_MERCHANT_StatisticsAmountGetHandle); - handle->ctx = ctx; - handle->cb = cb; - handle->cb_cls = cb_cls; - { - const char *filter = NULL; - char *path; - - switch (stype) - { - case TALER_MERCHANT_STATISTICS_BY_BUCKET: - filter = "bucket"; - break; - case TALER_MERCHANT_STATISTICS_BY_INTERVAL: - filter = "interval"; - break; - case TALER_MERCHANT_STATISTICS_ALL: - filter = NULL; - break; - } - GNUNET_asprintf (&path, - "private/statistics-amount/%s", - slug); - handle->url = TALER_url_join (backend_url, - path, - "by", - filter, - NULL); - GNUNET_free (path); - } - if (NULL == handle->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (handle); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - handle->url); - eh = TALER_MERCHANT_curl_easy_get_ (handle->url); - handle->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_statistics_amount_finished, - handle); - return handle; -} - - -void -TALER_MERCHANT_statistic_amount_get_cancel ( - struct TALER_MERCHANT_StatisticsAmountGetHandle *handle) -{ - if (NULL != handle->job) - GNUNET_CURL_job_cancel (handle->job); - GNUNET_free (handle->url); - GNUNET_free (handle); -} diff --git a/src/lib/merchant_api_get_template.c b/src/lib/merchant_api_get_template.c @@ -1,201 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_template.c - * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /templates/$ID operation. - */ -struct TALER_MERCHANT_TemplateGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TemplateGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /templates/$ID request. - * - * @param cls the `struct TALER_MERCHANT_TemplateGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_template_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TemplateGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_TemplateGetResponse tgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /templates/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *contract; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("template_description", - &tgr.details.ok.template_description), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("otp_id", - &tgr.details.ok.otp_id), - NULL), - GNUNET_JSON_spec_object_const ("template_contract", - &contract), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tgr.details.ok.template_contract = contract; - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_template_get_cancel (tgh); - return; - } - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_template_get_cancel (tgh); -} - - -struct TALER_MERCHANT_TemplateGetHandle * -TALER_MERCHANT_template_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *template_id, - TALER_MERCHANT_TemplateGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TemplateGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_TemplateGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/templates/%s", - template_id); - tgh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_template_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_template_get_cancel ( - struct TALER_MERCHANT_TemplateGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_get_templates.c b/src/lib/merchant_api_get_templates.c @@ -1,247 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_templates.c - * @brief Implementation of the GET /templates request of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Maximum number of templates we return. - */ -#define MAX_TEMPLATES 1024 - - -/** - * Handle for a GET /templates operation. - */ -struct TALER_MERCHANT_TemplatesGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TemplatesGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse template information from @a ia. - * - * @param ia JSON array (or NULL!) with template data - * @param[in] tgr partially filled response - * @param tgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_templates (const json_t *ia, - struct TALER_MERCHANT_TemplatesGetResponse *tgr, - struct TALER_MERCHANT_TemplatesGetHandle *tgh) -{ - unsigned int tmpl_len = (unsigned int) json_array_size (ia); - - if ( (json_array_size (ia) != (size_t) tmpl_len) || - (tmpl_len > MAX_TEMPLATES) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_TemplateEntry tmpl[GNUNET_NZL (tmpl_len)]; - size_t index; - json_t *value; - - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_TemplateEntry *ie = &tmpl[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("template_id", - &ie->template_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - tgr->details.ok.templates_length = tmpl_len; - tgr->details.ok.templates = tmpl; - tgh->cb (tgh->cb_cls, - tgr); - tgh->cb = NULL; /* just to be sure */ - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /templates request. - * - * @param cls the `struct TALER_MERCHANT_TemplatesGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_templates_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TemplatesGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_TemplatesGetResponse tgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /templates response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *templates; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("templates", - &templates), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_templates (templates, - &tgr, - tgh)) - { - TALER_MERCHANT_templates_get_cancel (tgh); - return; - } - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_templates_get_cancel (tgh); -} - - -struct TALER_MERCHANT_TemplatesGetHandle * -TALER_MERCHANT_templates_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_TemplatesGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TemplatesGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_TemplatesGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - tgh->url = TALER_url_join (backend_url, - "private/templates", - NULL); - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_templates_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_templates_get_cancel ( - struct TALER_MERCHANT_TemplatesGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_get_tokenfamily.c b/src/lib/merchant_api_get_tokenfamily.c @@ -1,218 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023-2024 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_tokenfamily.c - * @brief Implementation of the GET /tokenfamily/$ID request of the merchant's HTTP API - * @author Christian Blättler - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <gnunet/gnunet_common.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /tokenfamilies/$SLUG operation. - */ -struct TALER_MERCHANT_TokenFamilyGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TokenFamilyGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /tokenfamilies/$ID request. - * - * @param cls the `struct TALER_MERCHANT_TokenFamilyGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_token_family_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TokenFamilyGetHandle *handle = cls; - const json_t *json = response; - struct TALER_MERCHANT_TokenFamilyGetResponse res = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - handle->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /tokenfamilies/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - // Parse token family response - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("slug", - &res.details.ok.slug), - GNUNET_JSON_spec_string ("name", - &res.details.ok.name), - GNUNET_JSON_spec_string ("description", - &res.details.ok.description), - GNUNET_JSON_spec_object_const ("description_i18n", - &res.details.ok.description_i18n), - GNUNET_JSON_spec_object_const ("extra_data", - &res.details.ok.extra_data), - GNUNET_JSON_spec_timestamp ("valid_after", - &res.details.ok.valid_after), - GNUNET_JSON_spec_timestamp ("valid_before", - &res.details.ok.valid_before), - GNUNET_JSON_spec_relative_time ("duation", - &res.details.ok.duration), - GNUNET_JSON_spec_relative_time ("validity_granularity", - &res.details.ok.validity_granularity), - GNUNET_JSON_spec_relative_time ("start_offset", - &res.details.ok.start_offset), - GNUNET_JSON_spec_string ("kind", - &res.details.ok.kind), - GNUNET_JSON_spec_uint64 ("issued", - &res.details.ok.issued), - GNUNET_JSON_spec_uint64 ("used", - &res.details.ok.used), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - handle->cb (handle->cb_cls, - &res); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_token_family_get_cancel (handle); - return; - } - res.hr.http_status = 0; - res.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - res.hr.ec = TALER_JSON_get_error_code (json); - res.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) res.hr.ec); - break; - } -} - - -struct TALER_MERCHANT_TokenFamilyGetHandle * -TALER_MERCHANT_token_family_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *token_family_slug, - TALER_MERCHANT_TokenFamilyGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TokenFamilyGetHandle *handle; - CURL *eh; - - handle = GNUNET_new (struct TALER_MERCHANT_TokenFamilyGetHandle); - handle->ctx = ctx; - handle->cb = cb; - handle->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/tokenfamilies/%s", - token_family_slug); - handle->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == handle->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (handle); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - handle->url); - eh = TALER_MERCHANT_curl_easy_get_ (handle->url); - handle->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_token_family_finished, - handle); - return handle; -} - - -void -TALER_MERCHANT_token_family_get_cancel ( - struct TALER_MERCHANT_TokenFamilyGetHandle *handle) -{ - if (NULL != handle->job) - GNUNET_CURL_job_cancel (handle->job); - GNUNET_free (handle->url); - GNUNET_free (handle); -} diff --git a/src/lib/merchant_api_get_transfers.c b/src/lib/merchant_api_get_transfers.c @@ -1,314 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_transfers.c - * @brief Implementation of the GET /transfers request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * @brief A Handle for tracking wire transfers. - */ -struct TALER_MERCHANT_GetTransfersHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_GetTransfersCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP GET /transfers request. - * - * @param cls the `struct TALER_MERCHANT_GetTransfersHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_transfers_get_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_GetTransfersHandle *gth = cls; - const json_t *json = response; - struct TALER_MERCHANT_GetTransfersResponse gtr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - gth->job = NULL; - switch (response_code) - { - case 0: - gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - const json_t *transfers; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("transfers", - &transfers), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - gtr.hr.http_status = 0; - gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - - { - size_t tds_length; - struct TALER_MERCHANT_TransferData *tds; - json_t *transfer; - size_t i; - bool ok; - - tds_length = json_array_size (transfers); - tds = GNUNET_new_array (tds_length, - struct TALER_MERCHANT_TransferData); - ok = true; - json_array_foreach (transfers, i, transfer) { - struct TALER_MERCHANT_TransferData *td = &tds[i]; - struct GNUNET_JSON_Specification ispec[] = { - TALER_JSON_spec_amount_any ("credit_amount", - &td->credit_amount), - GNUNET_JSON_spec_fixed_auto ("wtid", - &td->wtid), - TALER_JSON_spec_full_payto_uri ("payto_uri", - &td->payto_uri), - TALER_JSON_spec_web_url ("exchange_url", - &td->exchange_url), - GNUNET_JSON_spec_uint64 ("transfer_serial_id", - &td->credit_serial), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("execution_time", - &td->execution_time), - NULL), - GNUNET_JSON_spec_bool ("expected", - &td->expected), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (transfer, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - ok = false; - break; - } - } - - if (! ok) - { - GNUNET_break_op (0); - GNUNET_free (tds); - gtr.hr.http_status = 0; - gtr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - gtr.details.ok.transfers = tds; - gtr.details.ok.transfers_length = tds_length; - gth->cb (gth->cb_cls, - &gtr); - GNUNET_free (tds); - TALER_MERCHANT_transfers_get_cancel (gth); - return; - } - } - case MHD_HTTP_UNAUTHORIZED: - gtr.hr.ec = TALER_JSON_get_error_code (json); - gtr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - gtr.hr.ec = TALER_JSON_get_error_code (json); - gtr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - gtr.hr.ec = TALER_JSON_get_error_code (json); - gtr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &gtr.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) gtr.hr.ec); - gtr.hr.http_status = 0; - break; - } - gth->cb (gth->cb_cls, - &gtr); - TALER_MERCHANT_transfers_get_cancel (gth); -} - - -struct TALER_MERCHANT_GetTransfersHandle * -TALER_MERCHANT_transfers_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - struct TALER_FullPayto payto_uri, - const struct GNUNET_TIME_Timestamp before, - const struct GNUNET_TIME_Timestamp after, - int64_t limit, - uint64_t offset, - enum TALER_EXCHANGE_YesNoAll expected, - TALER_MERCHANT_GetTransfersCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_GetTransfersHandle *gth; - CURL *eh; - const char *expected_s = NULL; - char limit_s[30]; - char offset_s[30]; - char before_s[30]; - char after_s[30]; - - gth = GNUNET_new (struct TALER_MERCHANT_GetTransfersHandle); - gth->ctx = ctx; - gth->cb = cb; - gth->cb_cls = cb_cls; - expected_s = TALER_yna_to_string (expected); - GNUNET_snprintf (limit_s, - sizeof (limit_s), - "%lld", - (long long) limit); - GNUNET_snprintf (offset_s, - sizeof (offset_s), - "%lld", - (unsigned long long) offset); - GNUNET_snprintf (before_s, - sizeof (before_s), - "%llu", - (unsigned long long) GNUNET_TIME_timestamp_to_s (before)); - GNUNET_snprintf (after_s, - sizeof (after_s), - "%llu", - (unsigned long long) GNUNET_TIME_timestamp_to_s (after)); - { - char *enc_payto = TALER_urlencode (payto_uri.full_payto); - - gth->url = TALER_url_join (backend_url, - "private/transfers", - "payto_uri", - enc_payto, - "expected", - (TALER_EXCHANGE_YNA_ALL != expected) - ? expected_s - : NULL, - "limit", - 0 != limit - ? limit_s - : NULL, - "offset", - ((0 != offset) && (UINT64_MAX != offset)) - ? offset_s - : NULL, - "before", - GNUNET_TIME_absolute_is_never (before.abs_time) - ? NULL - : before_s, - "after", - GNUNET_TIME_absolute_is_zero (after.abs_time) - ? NULL - : after_s, - NULL); - GNUNET_free (enc_payto); - } - if (NULL == gth->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (gth); - return NULL; - } - eh = TALER_MERCHANT_curl_easy_get_ (gth->url); - gth->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_transfers_get_finished, - gth); - return gth; -} - - -void -TALER_MERCHANT_transfers_get_cancel ( - struct TALER_MERCHANT_GetTransfersHandle *gth) -{ - if (NULL != gth->job) - { - GNUNET_CURL_job_cancel (gth->job); - gth->job = NULL; - } - GNUNET_free (gth->url); - GNUNET_free (gth); -} - - -/* end of merchant_api_get_transfers.c */ diff --git a/src/lib/merchant_api_get_unit.c b/src/lib/merchant_api_get_unit.c @@ -1,249 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_unit.c - * @brief Implementation of GET /private/units/$ID - * @author Bohdan Potuzhnyi - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> - - -/** - * Handle for a GET /private/units/$ID operation. - */ -struct TALER_MERCHANT_UnitGetHandle -{ - /** - * Fully qualified request URL. - */ - char *url; - - /** - * In-flight job handle. - */ - struct GNUNET_CURL_Job *job; - - /** - * Callback to invoke with the response. - */ - TALER_MERCHANT_UnitGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Parse the JSON response into @a ugr. - * - * @param json full JSON reply - * @param ugr response descriptor to populate - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_unit (const json_t *json, - struct TALER_MERCHANT_UnitGetResponse *ugr) -{ - struct TALER_MERCHANT_UnitEntry *entry = &ugr->details.ok.unit; - const char *unit; - const char *unit_name_long; - const char *unit_name_short; - const json_t *unit_name_long_i18n = NULL; - const json_t *unit_name_short_i18n = NULL; - bool unit_allow_fraction; - bool unit_active; - bool unit_builtin; - uint32_t unit_precision_level; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("unit", - &unit), - GNUNET_JSON_spec_string ("unit_name_long", - &unit_name_long), - GNUNET_JSON_spec_string ("unit_name_short", - &unit_name_short), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("unit_name_long_i18n", - &unit_name_long_i18n), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("unit_name_short_i18n", - &unit_name_short_i18n), - NULL), - GNUNET_JSON_spec_bool ("unit_allow_fraction", - &unit_allow_fraction), - GNUNET_JSON_spec_uint32 ("unit_precision_level", - &unit_precision_level), - GNUNET_JSON_spec_bool ("unit_active", - &unit_active), - GNUNET_JSON_spec_bool ("unit_builtin", - &unit_builtin), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - GNUNET_JSON_parse_free (spec); - entry->unit = unit; - entry->unit_name_long = unit_name_long; - entry->unit_name_short = unit_name_short; - entry->unit_name_long_i18n = unit_name_long_i18n; - entry->unit_name_short_i18n = unit_name_short_i18n; - entry->unit_allow_fraction = unit_allow_fraction; - entry->unit_precision_level = unit_precision_level; - entry->unit_active = unit_active; - entry->unit_builtin = unit_builtin; - return GNUNET_OK; -} - - -/** - * Called once the HTTP request completes. - * - * @param cls operation handle - * @param response_code HTTP status (0 on client-side errors) - * @param response parsed JSON reply (NULL if parsing failed) - */ -static void -handle_get_unit_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_UnitGetHandle *ugh = cls; - const json_t *json = response; - struct TALER_MERCHANT_UnitGetResponse ugr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - ugh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "GET /private/units/$ID finished with status %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - if (GNUNET_OK != - parse_unit (json, - &ugr)) - { - ugr.hr.http_status = 0; - ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - ugh->cb (ugh->cb_cls, - &ugr); - TALER_MERCHANT_unit_get_cancel (ugh); - return; - case MHD_HTTP_UNAUTHORIZED: - case MHD_HTTP_FORBIDDEN: - case MHD_HTTP_NOT_FOUND: - ugr.hr.ec = TALER_JSON_get_error_code (json); - ugr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case 0: - ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - default: - ugr.hr.ec = TALER_JSON_get_error_code (json); - ugr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unexpected response %u/%d for GET /private/units/$ID\n", - (unsigned int) response_code, - (int) ugr.hr.ec); - break; - } - ugh->cb (ugh->cb_cls, - &ugr); - TALER_MERCHANT_unit_get_cancel (ugh); -} - - -struct TALER_MERCHANT_UnitGetHandle * -TALER_MERCHANT_unit_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *unit_id, - TALER_MERCHANT_UnitGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_UnitGetHandle *ugh; - CURL *eh; - char *path; - - GNUNET_asprintf (&path, - "private/units/%s", - unit_id); - ugh = GNUNET_new (struct TALER_MERCHANT_UnitGetHandle); - ugh->ctx = ctx; - ugh->cb = cb; - ugh->cb_cls = cb_cls; - ugh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - if (NULL == ugh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to build /private/units/%s URL\n", - unit_id); - GNUNET_free (ugh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - ugh->url); - eh = TALER_MERCHANT_curl_easy_get_ (ugh->url); - ugh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_unit_finished, - ugh); - return ugh; -} - - -void -TALER_MERCHANT_unit_get_cancel (struct TALER_MERCHANT_UnitGetHandle *ugh) -{ - if (NULL != ugh->job) - GNUNET_CURL_job_cancel (ugh->job); - GNUNET_free (ugh->url); - GNUNET_free (ugh); -} - - -/* end of merchant_api_get_unit.c */ diff --git a/src/lib/merchant_api_get_units.c b/src/lib/merchant_api_get_units.c @@ -1,329 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free - Software Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - - You should have received a copy of the GNU Lesser General Public License along - with TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_units.c - * @brief Implementation of GET /private/units - * @author Bohdan Potuzhnyi - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> - - -/** - * Maximum number of units returned in a single response. - */ -#define MAX_UNITS 1024 - - -/** - * Handle for a GET /private/units operation. - */ -struct TALER_MERCHANT_UnitsGetHandle -{ - /** - * Fully qualified request URL. - */ - char *url; - - /** - * In-flight job handle. - */ - struct GNUNET_CURL_Job *job; - - /** - * Callback to invoke with the outcome. - */ - TALER_MERCHANT_UnitsGetCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Parse an individual unit entry from @a value. - * - * @param value JSON object describing the unit - * @param[out] ue set to the parsed values - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_unit_entry (const json_t *value, - struct TALER_MERCHANT_UnitEntry *ue) -{ - const char *unit; - const char *unit_name_long; - const char *unit_name_short; - const json_t *unit_name_long_i18n = NULL; - const json_t *unit_name_short_i18n = NULL; - bool unit_allow_fraction; - bool unit_active; - bool unit_builtin; - uint32_t unit_precision_level; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("unit", - &unit), - GNUNET_JSON_spec_string ("unit_name_long", - &unit_name_long), - GNUNET_JSON_spec_string ("unit_name_short", - &unit_name_short), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("unit_name_long_i18n", - &unit_name_long_i18n), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("unit_name_short_i18n", - &unit_name_short_i18n), - NULL), - GNUNET_JSON_spec_bool ("unit_allow_fraction", - &unit_allow_fraction), - GNUNET_JSON_spec_uint32 ("unit_precision_level", - &unit_precision_level), - GNUNET_JSON_spec_bool ("unit_active", - &unit_active), - GNUNET_JSON_spec_bool ("unit_builtin", - &unit_builtin), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - GNUNET_JSON_parse_free (spec); - ue->unit = unit; - ue->unit_name_long = unit_name_long; - ue->unit_name_short = unit_name_short; - ue->unit_name_long_i18n = unit_name_long_i18n; - ue->unit_name_short_i18n = unit_name_short_i18n; - ue->unit_allow_fraction = unit_allow_fraction; - ue->unit_precision_level = unit_precision_level; - ue->unit_active = unit_active; - ue->unit_builtin = unit_builtin; - return GNUNET_OK; -} - - -/** - * Parse the list of units from @a units and call the callback. - * - * @param json complete response JSON - * @param units array of units - * @param ugh ongoing operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_units (const json_t *json, - const json_t *units, - struct TALER_MERCHANT_UnitsGetHandle *ugh) -{ - size_t len; - - len = json_array_size (units); - if (len > MAX_UNITS) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - { - struct TALER_MERCHANT_UnitEntry entries[GNUNET_NZL (len)]; - size_t idx; - json_t *value; - - json_array_foreach (units, idx, value) { - if (GNUNET_OK != - parse_unit_entry (value, - &entries[idx])) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - { - struct TALER_MERCHANT_UnitsGetResponse ugr = { - .hr.http_status = MHD_HTTP_OK, - .hr.reply = json, - .details = { - .ok = { - .units = entries, - .units_length = (unsigned int) len - } - - - } - - - }; - - ugh->cb (ugh->cb_cls, - &ugr); - } - } - return GNUNET_OK; -} - - -/** - * Called when the HTTP transfer finishes. - * - * @param cls closure, the operation handle - * @param response_code HTTP status (0 on network errors) - * @param response parsed JSON body (NULL if parsing failed) - */ -static void -handle_get_units_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_UnitsGetHandle *ugh = cls; - const json_t *json = response; - struct TALER_MERCHANT_UnitsGetResponse ugr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - ugh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "GET /private/units finished with status %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *units; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("units", - &units), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - ugr.hr.http_status = 0; - ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_units (json, - units, - ugh)) - { - TALER_MERCHANT_units_get_cancel (ugh); - return; - } - ugr.hr.http_status = 0; - ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - case MHD_HTTP_FORBIDDEN: - case MHD_HTTP_NOT_FOUND: - case MHD_HTTP_CONFLICT: - ugr.hr.ec = TALER_JSON_get_error_code (json); - ugr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case 0: - ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - default: - ugr.hr.ec = TALER_JSON_get_error_code (json); - ugr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unexpected response code %u/%d for GET /private/units\n", - (unsigned int) response_code, - (int) ugr.hr.ec); - break; - } - ugh->cb (ugh->cb_cls, - &ugr); - TALER_MERCHANT_units_get_cancel (ugh); -} - - -struct TALER_MERCHANT_UnitsGetHandle * -TALER_MERCHANT_units_get (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_UnitsGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_UnitsGetHandle *ugh; - CURL *eh; - - ugh = GNUNET_new (struct TALER_MERCHANT_UnitsGetHandle); - ugh->ctx = ctx; - ugh->cb = cb; - ugh->cb_cls = cb_cls; - ugh->url = TALER_url_join (backend_url, - "private/units", - NULL); - if (NULL == ugh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to build /private/units URL\n"); - GNUNET_free (ugh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - ugh->url); - eh = TALER_MERCHANT_curl_easy_get_ (ugh->url); - ugh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_units_finished, - ugh); - return ugh; -} - - -void -TALER_MERCHANT_units_get_cancel (struct TALER_MERCHANT_UnitsGetHandle *ugh) -{ - if (NULL != ugh->job) - GNUNET_CURL_job_cancel (ugh->job); - GNUNET_free (ugh->url); - GNUNET_free (ugh); -} - - -/* end of merchant_api_get_units.c */ diff --git a/src/lib/merchant_api_get_webhook.c b/src/lib/merchant_api_get_webhook.c @@ -1,221 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_webhook.c - * @brief Implementation of the GET /webhooks/$ID request of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /webhooks/$ID operation. - */ -struct TALER_MERCHANT_WebhookGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_WebhookGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /webhooks/$ID request. - * - * @param cls the `struct TALER_MERCHANT_WebhookGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_webhook_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_WebhookGetHandle *wgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - wgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /webhooks/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const char *event_type; - const char *url; - const char *http_method; - const char *header_template; - const char *body_template; - bool rst_ok = true; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("event_type", - &event_type), - TALER_JSON_spec_web_url ("url", - &url), - GNUNET_JSON_spec_string ("http_method", - &http_method), - GNUNET_JSON_spec_string ("header_template", - &header_template), - GNUNET_JSON_spec_string ("body_template", - &body_template), - GNUNET_JSON_spec_end () - }; - - - if ( (rst_ok) && - (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) ) - { - wgh->cb (wgh->cb_cls, - &hr, - event_type, - url, - http_method, - header_template, - body_template); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_webhook_get_cancel (wgh); - return; - } - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_JSON_parse_free (spec); - break; - } - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - wgh->cb (wgh->cb_cls, - &hr, - NULL, - NULL, - NULL, - NULL, - NULL); - TALER_MERCHANT_webhook_get_cancel (wgh); -} - - -struct TALER_MERCHANT_WebhookGetHandle * -TALER_MERCHANT_webhook_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *webhook_id, - TALER_MERCHANT_WebhookGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_WebhookGetHandle *wgh; - CURL *eh; - - wgh = GNUNET_new (struct TALER_MERCHANT_WebhookGetHandle); - wgh->ctx = ctx; - wgh->cb = cb; - wgh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/webhooks/%s", - webhook_id); - wgh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == wgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (wgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - wgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (wgh->url); - wgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_webhook_finished, - wgh); - return wgh; -} - - -void -TALER_MERCHANT_webhook_get_cancel ( - struct TALER_MERCHANT_WebhookGetHandle *wgh) -{ - if (NULL != wgh->job) - GNUNET_CURL_job_cancel (wgh->job); - GNUNET_free (wgh->url); - GNUNET_free (wgh); -} diff --git a/src/lib/merchant_api_get_webhooks.c b/src/lib/merchant_api_get_webhooks.c @@ -1,246 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_get_webhooks.c - * @brief Implementation of the GET /webhooks request of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Maximum number of webhooks we return. - */ -#define MAX_WEBHOOKS 1024 - -/** - * Handle for a GET /webhooks operation. - */ -struct TALER_MERCHANT_WebhooksGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_WebhooksGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Parse webhook information from @a ia. - * - * @param ia JSON array (or NULL!) with webhook data - * @param[in] wgr partially filled webhook response - * @param wgh operation handle - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_webhooks (const json_t *ia, - struct TALER_MERCHANT_WebhooksGetResponse *wgr, - struct TALER_MERCHANT_WebhooksGetHandle *wgh) -{ - unsigned int whook_len = (unsigned int) json_array_size (ia); - - if ( (json_array_size (ia) != (size_t) whook_len) || - (whook_len > MAX_WEBHOOKS) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_MERCHANT_WebhookEntry whook[GNUNET_NZL (whook_len)]; - size_t index; - json_t *value; - - json_array_foreach (ia, index, value) { - struct TALER_MERCHANT_WebhookEntry *ie = &whook[index]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("webhook_id", - &ie->webhook_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - wgr->details.ok.webhooks_length = whook_len; - wgr->details.ok.webhooks = whook; - wgh->cb (wgh->cb_cls, - wgr); - wgh->cb = NULL; /* just to be sure */ - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /webhooks request. - * - * @param cls the `struct TALER_MERCHANT_WebhooksGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_webhooks_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_WebhooksGetHandle *wgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_WebhooksGetResponse wgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - wgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /webhooks response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *webhooks; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("webhooks", - &webhooks), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - wgr.hr.http_status = 0; - wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - if (GNUNET_OK == - parse_webhooks (webhooks, - &wgr, - wgh)) - { - TALER_MERCHANT_webhooks_get_cancel (wgh); - return; - } - wgr.hr.http_status = 0; - wgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - wgr.hr.ec = TALER_JSON_get_error_code (json); - wgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - wgr.hr.ec = TALER_JSON_get_error_code (json); - wgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) wgr.hr.ec); - break; - } - wgh->cb (wgh->cb_cls, - &wgr); - TALER_MERCHANT_webhooks_get_cancel (wgh); -} - - -struct TALER_MERCHANT_WebhooksGetHandle * -TALER_MERCHANT_webhooks_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - TALER_MERCHANT_WebhooksGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_WebhooksGetHandle *wgh; - CURL *eh; - - wgh = GNUNET_new (struct TALER_MERCHANT_WebhooksGetHandle); - wgh->ctx = ctx; - wgh->cb = cb; - wgh->cb_cls = cb_cls; - wgh->url = TALER_url_join (backend_url, - "private/webhooks", - NULL); - if (NULL == wgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (wgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - wgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (wgh->url); - wgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_webhooks_finished, - wgh); - return wgh; -} - - -void -TALER_MERCHANT_webhooks_get_cancel ( - struct TALER_MERCHANT_WebhooksGetHandle *wgh) -{ - if (NULL != wgh->job) - GNUNET_CURL_job_cancel (wgh->job); - GNUNET_free (wgh->url); - GNUNET_free (wgh); -} diff --git a/src/lib/merchant_api_lock_product.c b/src/lib/merchant_api_lock_product.c @@ -1,279 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_lock_product.c - * @brief Implementation of the POST /products/$ID/lock request - * of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /products/$ID/lock operation. - */ -struct TALER_MERCHANT_ProductLockHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ProductLockCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP POST /products/$ID/lock request. - * - * @param cls the `struct TALER_MERCHANT_ProductLockHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_lock_product_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ProductLockHandle *plh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - plh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "LOCK /products/$ID completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_break_op (0); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_GONE: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - plh->cb (plh->cb_cls, - &hr); - TALER_MERCHANT_product_lock_cancel (plh); -} - - -struct TALER_MERCHANT_ProductLockHandle * -TALER_MERCHANT_product_lock2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - const char *uuid, - struct GNUNET_TIME_Relative duration, - uint64_t quantity, - uint32_t quantity_frac, - bool use_fractional_quantity, - TALER_MERCHANT_ProductLockCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ProductLockHandle *plh; - json_t *req_obj; - char unit_quantity_buf[64]; - - TALER_MERCHANT_format_quantity_string (quantity, - quantity_frac, - unit_quantity_buf, - sizeof (unit_quantity_buf)); - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("lock_uuid", - uuid), - GNUNET_JSON_pack_time_rel ("duration", - duration), - GNUNET_JSON_pack_string ("unit_quantity", - unit_quantity_buf)); - (void) use_fractional_quantity; - GNUNET_assert ( (0 == quantity_frac) || use_fractional_quantity); - plh = GNUNET_new (struct TALER_MERCHANT_ProductLockHandle); - plh->ctx = ctx; - plh->cb = cb; - plh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/products/%s/lock", - product_id); - plh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == plh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (plh); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (plh->url); - if (GNUNET_OK != - TALER_curl_easy_post (&plh->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (plh->url); - GNUNET_free (plh); - return NULL; - } - json_decref (req_obj); - plh->job = GNUNET_CURL_job_add2 (ctx, - eh, - plh->post_ctx.headers, - &handle_lock_product_finished, - plh); - } - return plh; -} - - -void -TALER_MERCHANT_product_lock_cancel ( - struct TALER_MERCHANT_ProductLockHandle *plh) -{ - if (NULL != plh->job) - { - GNUNET_CURL_job_cancel (plh->job); - plh->job = NULL; - } - TALER_curl_easy_post_finished (&plh->post_ctx); - GNUNET_free (plh->url); - GNUNET_free (plh); -} - - -struct TALER_MERCHANT_ProductLockHandle * -TALER_MERCHANT_product_lock ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - const char *uuid, - struct GNUNET_TIME_Relative duration, - uint32_t quantity, - TALER_MERCHANT_ProductLockCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_product_lock2 (ctx, - backend_url, - product_id, - uuid, - duration, - quantity, - 0, - false, - cb, - cb_cls); -} - - -/* end of merchant_api_lock_product.c */ diff --git a/src/lib/merchant_api_merchant_get_order.c b/src/lib/merchant_api_merchant_get_order.c @@ -1,538 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018, 2019, 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_merchant_get_order.c - * @brief Implementation of the GET /private/orders/$ORDER request - * @author Christian Grothoff - * @author Marcello Stanisci - * @author Florian Dold - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Maximum number of refund details we return. - */ -#define MAX_REFUND_DETAILS 1024 - -/** - * Maximum number of wire details we return. - */ -#define MAX_WIRE_DETAILS 1024 - - -/** - * @brief A GET /private/orders/$ORDER handle - */ -struct TALER_MERCHANT_OrderMerchantGetHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OrderMerchantGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the GET /private/orders/$ORDER - * request and we got an HTTP status of OK and the order was unpaid. Parse - * the response and call the callback. - * - * @param omgh handle for the request - * @param[in,out] osr HTTP response we got - */ -static void -handle_unpaid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, - struct TALER_MERCHANT_OrderStatusResponse *osr) -{ - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ( - "total_amount", - &osr->details.ok.details.unpaid.contract_amount), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ( - "already_paid_order_id", - &osr->details.ok.details.unpaid.already_paid_order_id), - NULL), - GNUNET_JSON_spec_string ( - "taler_pay_uri", - &osr->details.ok.details.unpaid.taler_pay_uri), - GNUNET_JSON_spec_string ( - "summary", - &osr->details.ok.details.unpaid.summary), - GNUNET_JSON_spec_timestamp ( - "creation_time", - &osr->details.ok.details.unpaid.creation_time), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (osr->hr.reply, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - return; - } - osr->details.ok.status = TALER_MERCHANT_OSC_UNPAID; - omgh->cb (omgh->cb_cls, - osr); -} - - -/** - * Function called when we're done processing the GET /private/orders/$ORDER - * request and we got an HTTP status of OK and the order was claimed but not - * paid. Parse the response and call the callback. - * - * @param omgh handle for the request - * @param[in,out] osr HTTP response we got - */ -static void -handle_claimed (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, - struct TALER_MERCHANT_OrderStatusResponse *osr) -{ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_object_const ( - "contract_terms", - &osr->details.ok.details.claimed.contract_terms), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (osr->hr.reply, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - return; - } - osr->details.ok.status = TALER_MERCHANT_OSC_CLAIMED; - omgh->cb (omgh->cb_cls, - osr); -} - - -/** - * Function called when we're done processing the GET /private/orders/$ORDER - * request and we got an HTTP status of OK and the order was paid. Parse - * the response and call the callback. - * - * @param omgh handle for the request - * @param[in,out] osr HTTP response we got - */ -static void -handle_paid (struct TALER_MERCHANT_OrderMerchantGetHandle *omgh, - struct TALER_MERCHANT_OrderStatusResponse *osr) -{ - uint32_t hc32; - const json_t *wire_details; - const json_t *refund_details; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_bool ("refunded", - &osr->details.ok.details.paid.refunded), - GNUNET_JSON_spec_bool ("refund_pending", - &osr->details.ok.details.paid.refund_pending), - GNUNET_JSON_spec_bool ("wired", - &osr->details.ok.details.paid.wired), - TALER_JSON_spec_amount_any ("deposit_total", - &osr->details.ok.details.paid.deposit_total), - TALER_JSON_spec_ec ("exchange_code", - &osr->details.ok.details.paid.exchange_ec), - GNUNET_JSON_spec_uint32 ("exchange_http_status", - &hc32), - TALER_JSON_spec_amount_any ("refund_amount", - &osr->details.ok.details.paid.refund_amount), - GNUNET_JSON_spec_object_const ( - "contract_terms", - &osr->details.ok.details.paid.contract_terms), - GNUNET_JSON_spec_array_const ("wire_details", - &wire_details), - GNUNET_JSON_spec_array_const ("refund_details", - &refund_details), - /* Only available since **v14** */ - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("last_payment", - &osr->details.ok.details.paid.last_payment), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (osr->hr.reply, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - return; - } - osr->details.ok.status = TALER_MERCHANT_OSC_PAID; - - osr->details.ok.details.paid.exchange_hc = (unsigned int) hc32; - { - unsigned int wts_len = (unsigned int) json_array_size (wire_details); - unsigned int ref_len = (unsigned int) json_array_size (refund_details); - - if ( (json_array_size (wire_details) != (size_t) wts_len) || - (wts_len > MAX_WIRE_DETAILS) ) - { - GNUNET_break (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; - omgh->cb (omgh->cb_cls, - osr); - return; - } - if ( (json_array_size (refund_details) != (size_t) ref_len) || - (ref_len > MAX_REFUND_DETAILS) ) - { - GNUNET_break (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; - omgh->cb (omgh->cb_cls, - osr); - return; - } - { - struct TALER_MERCHANT_WireTransfer wts[GNUNET_NZL (wts_len)]; - struct TALER_MERCHANT_RefundOrderDetail ref[GNUNET_NZL (ref_len)]; - - for (unsigned int i = 0; i<wts_len; i++) - { - struct TALER_MERCHANT_WireTransfer *wt = &wts[i]; - const json_t *w = json_array_get (wire_details, - i); - struct GNUNET_JSON_Specification ispec[] = { - TALER_JSON_spec_web_url ("exchange_url", - &wt->exchange_url), - GNUNET_JSON_spec_fixed_auto ("wtid", - &wt->wtid), - GNUNET_JSON_spec_timestamp ("execution_time", - &wt->execution_time), - TALER_JSON_spec_amount_any ("amount", - &wt->total_amount), - GNUNET_JSON_spec_bool ("confirmed", - &wt->confirmed), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (w, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - return; - } - } - - for (unsigned int i = 0; i<ref_len; i++) - { - struct TALER_MERCHANT_RefundOrderDetail *ro = &ref[i]; - const json_t *w = json_array_get (refund_details, - i); - struct GNUNET_JSON_Specification ispec[] = { - TALER_JSON_spec_amount_any ("amount", - &ro->refund_amount), - GNUNET_JSON_spec_string ("reason", - &ro->reason), - GNUNET_JSON_spec_timestamp ("timestamp", - &ro->refund_time), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (w, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - osr->hr.http_status = 0; - osr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - osr); - return; - } - } - - osr->details.ok.details.paid.wts = wts; - osr->details.ok.details.paid.wts_len = wts_len; - osr->details.ok.details.paid.refunds = ref; - osr->details.ok.details.paid.refunds_len = ref_len; - omgh->cb (omgh->cb_cls, - osr); - } - } -} - - -/** - * Function called when we're done processing the GET /private/orders/$ORDER - * request. - * - * @param cls the `struct TALER_MERCHANT_OrderMerchantGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_merchant_order_get_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderMerchantGetHandle *omgh = cls; - const json_t *json = response; - const char *order_status; - struct TALER_MERCHANT_OrderStatusResponse osr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - omgh->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - /* see below */ - break; - case MHD_HTTP_ACCEPTED: - /* see below */ - omgh->cb (omgh->cb_cls, - &osr); - TALER_MERCHANT_merchant_order_get_cancel (omgh); - return; - case MHD_HTTP_UNAUTHORIZED: - osr.hr.ec = TALER_JSON_get_error_code (json); - osr.hr.hint = TALER_JSON_get_error_hint (json); - omgh->cb (omgh->cb_cls, - &osr); - TALER_MERCHANT_merchant_order_get_cancel (omgh); - return; - case MHD_HTTP_NOT_FOUND: - osr.hr.ec = TALER_JSON_get_error_code (json); - osr.hr.hint = TALER_JSON_get_error_hint (json); - omgh->cb (omgh->cb_cls, - &osr); - TALER_MERCHANT_merchant_order_get_cancel (omgh); - return; - case MHD_HTTP_GATEWAY_TIMEOUT: - osr.hr.ec = TALER_JSON_get_error_code (json); - osr.hr.hint = TALER_JSON_get_error_hint (json); - omgh->cb (omgh->cb_cls, - &osr); - TALER_MERCHANT_merchant_order_get_cancel (omgh); - return; - default: - osr.hr.ec = TALER_JSON_get_error_code (json); - osr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Polling payment failed with HTTP status code %u/%d\n", - (unsigned int) response_code, - (int) osr.hr.ec); - GNUNET_break_op (0); - omgh->cb (omgh->cb_cls, - &osr); - TALER_MERCHANT_merchant_order_get_cancel (omgh); - return; - } - - order_status = json_string_value (json_object_get (json, - "order_status")); - - if (NULL == order_status) - { - GNUNET_break_op (0); - osr.hr.http_status = 0; - osr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - &osr); - TALER_MERCHANT_merchant_order_get_cancel (omgh); - return; - } - - if (0 == strcmp ("paid", - order_status)) - { - handle_paid (omgh, - &osr); - } - else if (0 == strcmp ("claimed", - order_status)) - { - handle_claimed (omgh, - &osr); - } - else if (0 == strcmp ("unpaid", - order_status)) - { - handle_unpaid (omgh, - &osr); - } - else - { - GNUNET_break_op (0); - osr.hr.http_status = 0; - osr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - omgh->cb (omgh->cb_cls, - &osr); - } - TALER_MERCHANT_merchant_order_get_cancel (omgh); -} - - -struct TALER_MERCHANT_OrderMerchantGetHandle * -TALER_MERCHANT_merchant_order_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *order_id, - const char *session_id, - struct GNUNET_TIME_Relative timeout, - TALER_MERCHANT_OrderMerchantGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OrderMerchantGetHandle *omgh; - unsigned int tms; - - tms = (unsigned int) (timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); - omgh = GNUNET_new (struct TALER_MERCHANT_OrderMerchantGetHandle); - omgh->ctx = ctx; - omgh->cb = cb; - omgh->cb_cls = cb_cls; - { - char *path; - char timeout_ms[32]; - - GNUNET_snprintf (timeout_ms, - sizeof (timeout_ms), - "%u", - tms); - GNUNET_asprintf (&path, - "private/orders/%s", - order_id); - omgh->url = TALER_url_join (backend_url, - path, - "session_id", session_id, - "timeout_ms", (0 != tms) ? timeout_ms : NULL, - NULL); - GNUNET_free (path); - } - if (NULL == omgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (omgh); - return NULL; - } - - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (omgh->url); - if (NULL == eh) - { - GNUNET_break (0); - GNUNET_free (omgh->url); - GNUNET_free (omgh); - return NULL; - } - if (0 != tms) - { - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TIMEOUT_MS, - (long) (tms + 100L))); - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Getting order status from %s\n", - omgh->url); - if (NULL == (omgh->job = - GNUNET_CURL_job_add (ctx, - eh, - &handle_merchant_order_get_finished, - omgh))) - { - GNUNET_break (0); - GNUNET_free (omgh->url); - GNUNET_free (omgh); - return NULL; - } - } - return omgh; -} - - -void -TALER_MERCHANT_merchant_order_get_cancel ( - struct TALER_MERCHANT_OrderMerchantGetHandle *omgh) -{ - if (NULL != omgh->job) - { - GNUNET_CURL_job_cancel (omgh->job); - omgh->job = NULL; - } - GNUNET_free (omgh->url); - GNUNET_free (omgh); -} - - -/* end of merchant_api_merchant_get_order.c */ diff --git a/src/lib/merchant_api_patch-management-instances-INSTANCE.c b/src/lib/merchant_api_patch-management-instances-INSTANCE.c @@ -0,0 +1,271 @@ +/* + This file is part of TALER + Copyright (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch-management-instances-INSTANCE.c + * @brief Implementation of the PATCH /instances/$ID request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_kyclogic_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /instances/$ID operation. + */ +struct TALER_MERCHANT_InstancePatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_InstancePatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /instances/$ID request. + * + * @param cls the `struct TALER_MERCHANT_InstancePatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_instance_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_InstancePatchHandle *iph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + iph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /instances/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + iph->cb (iph->cb_cls, + &hr); + TALER_MERCHANT_instance_patch_cancel (iph); +} + + +struct TALER_MERCHANT_InstancePatchHandle * +TALER_MERCHANT_instance_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const char *name, + const json_t *address, + const json_t *jurisdiction, + bool use_stefan, + struct GNUNET_TIME_Relative default_wire_transfer_delay, + struct GNUNET_TIME_Relative default_pay_delay, + struct GNUNET_TIME_Relative default_refund_delay, + TALER_MERCHANT_InstancePatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_InstancePatchHandle *iph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("name", + name), + GNUNET_JSON_pack_object_incref ("address", + (json_t *) address), + GNUNET_JSON_pack_object_incref ("jurisdiction", + (json_t *) jurisdiction), + GNUNET_JSON_pack_bool ("use_stefan", + use_stefan), + GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay", + default_wire_transfer_delay), + GNUNET_JSON_pack_time_rel ("default_pay_delay", + default_pay_delay), + GNUNET_JSON_pack_time_rel ("default_refund_delay", + default_refund_delay), + /* FIXME: add eventually to arguments when we transform the API... */ + GNUNET_JSON_pack_time_rounder_interval ( + "default_wire_transfer_rounding_interval", + GNUNET_TIME_RI_NONE) + ); + iph = GNUNET_new (struct TALER_MERCHANT_InstancePatchHandle); + iph->ctx = ctx; + iph->cb = cb; + iph->cb_cls = cb_cls; + if (NULL != instance_id) + { + char *path; + + GNUNET_asprintf (&path, + "management/instances/%s", + instance_id); + iph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + else + { + /* backend_url is already identifying the instance */ + iph->url = TALER_url_join (backend_url, + "private", + NULL); + } + if (NULL == iph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (iph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (iph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&iph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (iph->url); + GNUNET_free (iph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + iph->job = GNUNET_CURL_job_add2 (ctx, + eh, + iph->post_ctx.headers, + &handle_patch_instance_finished, + iph); + } + return iph; +} + + +void +TALER_MERCHANT_instance_patch_cancel ( + struct TALER_MERCHANT_InstancePatchHandle *iph) +{ + if (NULL != iph->job) + { + GNUNET_CURL_job_cancel (iph->job); + iph->job = NULL; + } + TALER_curl_easy_post_finished (&iph->post_ctx); + GNUNET_free (iph->url); + GNUNET_free (iph); +} + + +/* end of merchant_api_patch_instance.c */ diff --git a/src/lib/merchant_api_patch-private-accounts-H_WIRE.c b/src/lib/merchant_api_patch-private-accounts-H_WIRE.c @@ -0,0 +1,254 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch-private-accounts-H_WIRE.c + * @brief Implementation of the PATCH /accounts/$ID request + * of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountPatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountPatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /accounts/$ID request. + * + * @param cls the `struct TALER_MERCHANT_AccountPatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountPatchHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /accounts/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_account_patch_cancel (tph); +} + + +struct TALER_MERCHANT_AccountPatchHandle * +TALER_MERCHANT_account_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MerchantWireHashP *h_wire, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + TALER_MERCHANT_AccountPatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountPatchHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("credit_facade_url", + credit_facade_url)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("credit_facade_credentials", + (json_t *) credit_facade_credentials))); + tph = GNUNET_new (struct TALER_MERCHANT_AccountPatchHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + { + char w_str[sizeof (*h_wire) * 2]; + char *path; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_wire, + sizeof (*h_wire), + w_str, + sizeof (w_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "private/accounts/%s", + w_str); + tph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_patch_account_finished, + tph); + } + return tph; +} + + +void +TALER_MERCHANT_account_patch_cancel ( + struct TALER_MERCHANT_AccountPatchHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_patch_account.c */ diff --git a/src/lib/merchant_api_patch-private-orders-ORDER_ID-forget.c b/src/lib/merchant_api_patch-private-orders-ORDER_ID-forget.c @@ -0,0 +1,254 @@ +/* + This file is part of TALER + Copyright (C) 2020, 2021, 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch-private-orders-ORDER_ID-forget.c + * @brief Implementation of the PATCH /orders/$ID/forget request + * of the merchant's HTTP API + * @author Jonathan Buchanan + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /orders/$ORDER_ID/forget operation. + */ +struct TALER_MERCHANT_OrderForgetHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ForgetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /orders/$ORDER_ID/forget request. + * + * @param cls the `struct TALER_MERCHANT_OrderForgetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_forget_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderForgetHandle *ofh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + ofh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /orders/$ORDER_ID/forget completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + /* fields were NOW forgotten */ + break; + case MHD_HTTP_NO_CONTENT: + /* fields were already forgotten before */ + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + ofh->cb (ofh->cb_cls, + &hr); + TALER_MERCHANT_order_forget_cancel (ofh); +} + + +struct TALER_MERCHANT_OrderForgetHandle * +TALER_MERCHANT_order_forget ( + struct GNUNET_CURL_Context *ctx, + const char *merchant_url, + const char *order_id, + unsigned int fields_length, + const char *fields[static fields_length], + TALER_MERCHANT_ForgetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OrderForgetHandle *ofh; + json_t *req_fields; + json_t *req_obj; + + req_fields = json_array (); + if (NULL == req_fields) + { + GNUNET_break (0); + return NULL; + } + for (unsigned int i = 0; i<fields_length; i++) + { + if (0 != + json_array_append_new (req_fields, + json_string (fields[i]))) + { + GNUNET_break (0); + json_decref (req_fields); + return NULL; + } + } + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("fields", + req_fields)); + ofh = GNUNET_new (struct TALER_MERCHANT_OrderForgetHandle); + ofh->ctx = ctx; + ofh->cb = cb; + ofh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/orders/%s/forget", + order_id); + ofh->url = TALER_url_join (merchant_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == ofh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (ofh); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (ofh->url); + if (GNUNET_OK != + TALER_curl_easy_post (&ofh->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (ofh); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + ofh->job = GNUNET_CURL_job_add2 (ctx, + eh, + ofh->post_ctx.headers, + &handle_forget_finished, + ofh); + } + return ofh; +} + + +void +TALER_MERCHANT_order_forget_cancel ( + struct TALER_MERCHANT_OrderForgetHandle *ofh) +{ + if (NULL != ofh->job) + { + GNUNET_CURL_job_cancel (ofh->job); + ofh->job = NULL; + } + TALER_curl_easy_post_finished (&ofh->post_ctx); + GNUNET_free (ofh->url); + GNUNET_free (ofh); +} + + +/* end of merchant_api_patch_order_forget.c */ diff --git a/src/lib/merchant_api_patch-private-otp-devices-DEVICE_ID.c b/src/lib/merchant_api_patch-private-otp-devices-DEVICE_ID.c @@ -0,0 +1,252 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch-private-otp-devices-DEVICE_ID.c + * @brief Implementation of the PATCH /otp-devices/$ID request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDevicePatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicePatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicePatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicePatchHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /otp-devices/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_otp_device_patch_cancel (tph); +} + + +struct TALER_MERCHANT_OtpDevicePatchHandle * +TALER_MERCHANT_otp_device_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + const char *otp_device_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm mca, + uint64_t otp_ctr, + TALER_MERCHANT_OtpDevicePatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicePatchHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("otp_device_description", + otp_device_description), + GNUNET_JSON_pack_uint64 ("otp_algorithm", + (uint32_t) mca), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_key", + otp_key)), + GNUNET_JSON_pack_uint64 ("otp_ctr", + otp_ctr)); + tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicePatchHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_patch_otp_device_finished, + tph); + } + return tph; +} + + +void +TALER_MERCHANT_otp_device_patch_cancel ( + struct TALER_MERCHANT_OtpDevicePatchHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_patch_otp_device.c */ diff --git a/src/lib/merchant_api_patch-private-products-PRODUCT_ID.c b/src/lib/merchant_api_patch-private-products-PRODUCT_ID.c @@ -0,0 +1,346 @@ +/* + This file is part of TALER + Copyright (C) 2020, 2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch-private-products-PRODUCT_ID.c + * @brief Implementation of the PATCH /products/$ID request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /products/$ID operation. + */ +struct TALER_MERCHANT_ProductPatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ProductPatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /products/$ID request. + * + * @param cls the `struct TALER_MERCHANT_ProductPatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_product_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_ProductPatchHandle *pph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + pph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /products/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + pph->cb (pph->cb_cls, + &hr); + TALER_MERCHANT_product_patch_cancel (pph); +} + + +struct TALER_MERCHANT_ProductPatchHandle * +TALER_MERCHANT_product_patch2 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *description, + const json_t *description_i18n, + const char *unit, + const struct TALER_Amount *unit_prices, + size_t unit_price_len, + const char *image, + const json_t *taxes, + int64_t total_stock, + uint32_t total_stock_frac, + bool unit_allow_fraction, + const uint32_t *unit_precision_level, + uint64_t total_lost, + const json_t *address, + struct GNUNET_TIME_Timestamp next_restock, + TALER_MERCHANT_ProductPatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_ProductPatchHandle *pph; + json_t *req_obj; + char unit_total_stock_buf[64]; + + TALER_MERCHANT_format_stock_string (total_stock, + total_stock_frac, + unit_total_stock_buf, + sizeof (unit_total_stock_buf)); + + { + req_obj = GNUNET_JSON_PACK ( + /* FIXME: once we move to the new-style API, + allow applications to set the product name properly! */ + GNUNET_JSON_pack_string ("product_name", + description), + GNUNET_JSON_pack_string ("description", + description), + GNUNET_JSON_pack_object_incref ("description_i18n", + (json_t *) description_i18n), + GNUNET_JSON_pack_string ("unit", + unit), + TALER_JSON_pack_amount_array ("unit_price", + unit_price_len, + unit_prices), + GNUNET_JSON_pack_string ("image", + image), + GNUNET_JSON_pack_array_incref ("taxes", + (json_t *) taxes), + GNUNET_JSON_pack_string ("unit_total_stock", + unit_total_stock_buf), + GNUNET_JSON_pack_bool ("unit_allow_fraction", + unit_allow_fraction), + GNUNET_JSON_pack_uint64 ("total_lost", + total_lost), + GNUNET_JSON_pack_object_incref ("address", + (json_t *) address), + GNUNET_JSON_pack_timestamp ("next_restock", + next_restock)); + } + if (NULL != unit_precision_level) + { + GNUNET_assert (0 == + json_object_set_new (req_obj, + "unit_precision_level", + json_integer ( + *unit_precision_level))); + } + if (! unit_allow_fraction) + { + GNUNET_assert (0 == + json_object_del (req_obj, + "unit_allow_fraction")); + if (NULL != unit_precision_level) + GNUNET_assert (0 == + json_object_del (req_obj, + "unit_precision_level")); + } + pph = GNUNET_new (struct TALER_MERCHANT_ProductPatchHandle); + pph->ctx = ctx; + pph->cb = cb; + pph->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/products/%s", + product_id); + pph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == pph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (pph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (pph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&pph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (pph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + pph->job = GNUNET_CURL_job_add2 (ctx, + eh, + pph->post_ctx.headers, + &handle_patch_product_finished, + pph); + } + return pph; +} + + +struct TALER_MERCHANT_ProductPatchHandle * +TALER_MERCHANT_product_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *description, + const json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + const char *image, + const json_t *taxes, + int64_t total_stock, + uint64_t total_lost, + const json_t *address, + struct GNUNET_TIME_Timestamp next_restock, + TALER_MERCHANT_ProductPatchCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_product_patch2 (ctx, + backend_url, + product_id, + description, + description_i18n, + unit, + price, + 1, + image, + taxes, + total_stock, + 0, + false, + NULL, + total_lost, + address, + next_restock, + cb, + cb_cls); +} + + +void +TALER_MERCHANT_product_patch_cancel ( + struct TALER_MERCHANT_ProductPatchHandle *pph) +{ + if (NULL != pph->job) + { + GNUNET_CURL_job_cancel (pph->job); + pph->job = NULL; + } + TALER_curl_easy_post_finished (&pph->post_ctx); + GNUNET_free (pph->url); + GNUNET_free (pph); +} + + +/* end of merchant_api_patch_product.c */ diff --git a/src/lib/merchant_api_patch-private-templates-TEMPLATE_ID.c b/src/lib/merchant_api_patch-private-templates-TEMPLATE_ID.c @@ -0,0 +1,249 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch-private-templates-TEMPLATE_ID.c + * @brief Implementation of the PATCH /templates/$ID request + * of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /templates/$ID operation. + */ +struct TALER_MERCHANT_TemplatePatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TemplatePatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /templates/$ID request. + * + * @param cls the `struct TALER_MERCHANT_TemplatePatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_template_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TemplatePatchHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /templates/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_template_patch_cancel (tph); +} + + +struct TALER_MERCHANT_TemplatePatchHandle * +TALER_MERCHANT_template_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + const char *template_description, + const char *otp_id, + json_t *template_contract, + TALER_MERCHANT_TemplatePatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TemplatePatchHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("template_description", + template_description), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_id", + otp_id)), + GNUNET_JSON_pack_object_incref ("template_contract", + (json_t *) template_contract)); + tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/templates/%s", + template_id); + tph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_patch_template_finished, + tph); + } + return tph; +} + + +void +TALER_MERCHANT_template_patch_cancel ( + struct TALER_MERCHANT_TemplatePatchHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_patch_template.c */ diff --git a/src/lib/merchant_api_patch-private-units-UNIT.c b/src/lib/merchant_api_patch-private-units-UNIT.c @@ -0,0 +1,291 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch-private-units-UNIT.c + * @brief Implementation of PATCH /private/units/$ID + * @author Bohdan Potuzhnyi + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /private/units/$ID operation. + */ +struct TALER_MERCHANT_UnitPatchHandle +{ + /** + * Fully qualified request URL. + */ + char *url; + + /** + * In-flight CURL job. + */ + struct GNUNET_CURL_Job *job; + + /** + * Completion callback. + */ + TALER_MERCHANT_UnitPatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Keeps POST body and headers alive. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Called when the HTTP transfer finishes. + * + * @param cls operation handle + * @param response_code HTTP status (0 on failure) + * @param response parsed JSON reply (NULL if unavailable) + */ +static void +handle_patch_unit_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_UnitPatchHandle *uph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + uph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /private/units completed with status %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + case MHD_HTTP_UNAUTHORIZED: + case MHD_HTTP_FORBIDDEN: + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_CONFLICT: + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response %u/%d for PATCH /private/units\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + uph->cb (uph->cb_cls, + &hr); + TALER_MERCHANT_unit_patch_cancel (uph); +} + + +struct TALER_MERCHANT_UnitPatchHandle * +TALER_MERCHANT_unit_patch (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *unit_id, + const char *unit_name_long, + const char *unit_name_short, + const json_t *unit_name_long_i18n, + const json_t *unit_name_short_i18n, + const bool *unit_allow_fraction, + const uint32_t *unit_precision_level, + const bool *unit_active, + TALER_MERCHANT_UnitPatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_UnitPatchHandle *uph; + json_t *req_obj; + char *path; + + req_obj = json_object (); + if (NULL == req_obj) + return NULL; + if (NULL != unit_name_long) + { + if (0 != json_object_set_new (req_obj, + "unit_name_long", + json_string (unit_name_long))) + { + json_decref (req_obj); + return NULL; + } + } + if (NULL != unit_name_short) + { + if (0 != json_object_set_new (req_obj, + "unit_name_short", + json_string (unit_name_short))) + { + json_decref (req_obj); + return NULL; + } + } + if (NULL != unit_name_long_i18n) + { + if (0 != json_object_set_new (req_obj, + "unit_name_long_i18n", + json_incref ((json_t *) unit_name_long_i18n))) + { + json_decref (req_obj); + return NULL; + } + } + if (NULL != unit_name_short_i18n) + { + if (0 != json_object_set_new (req_obj, + "unit_name_short_i18n", + json_incref ( + (json_t *) unit_name_short_i18n))) + { + json_decref (req_obj); + return NULL; + } + } + if (NULL != unit_allow_fraction) + { + if (0 != json_object_set_new (req_obj, + "unit_allow_fraction", + json_boolean (*unit_allow_fraction))) + { + json_decref (req_obj); + return NULL; + } + } + if (NULL != unit_precision_level) + { + if (0 != json_object_set_new (req_obj, + "unit_precision_level", + json_integer ( + (json_int_t) *unit_precision_level))) + { + json_decref (req_obj); + return NULL; + } + } + if (NULL != unit_active) + { + if (0 != json_object_set_new (req_obj, + "unit_active", + json_boolean (*unit_active))) + { + json_decref (req_obj); + return NULL; + } + } + if (0 == json_object_size (req_obj)) + { + json_decref (req_obj); + GNUNET_break (0); + return NULL; + } + + GNUNET_asprintf (&path, + "private/units/%s", + unit_id); + uph = GNUNET_new (struct TALER_MERCHANT_UnitPatchHandle); + uph->ctx = ctx; + uph->cb = cb; + uph->cb_cls = cb_cls; + uph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + if (NULL == uph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to build /private/units/%s URL\n", + unit_id); + json_decref (req_obj); + GNUNET_free (uph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (uph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&uph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (uph->url); + GNUNET_free (uph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + uph->job = GNUNET_CURL_job_add2 (ctx, + eh, + uph->post_ctx.headers, + &handle_patch_unit_finished, + uph); + } + return uph; +} + + +void +TALER_MERCHANT_unit_patch_cancel (struct TALER_MERCHANT_UnitPatchHandle *uph) +{ + if (NULL != uph->job) + { + GNUNET_CURL_job_cancel (uph->job); + uph->job = NULL; + } + TALER_curl_easy_post_finished (&uph->post_ctx); + GNUNET_free (uph->url); + GNUNET_free (uph); +} + + +/* end of merchant_api_patch_unit.c */ diff --git a/src/lib/merchant_api_patch-private-webhooks-WEBHOOK_ID.c b/src/lib/merchant_api_patch-private-webhooks-WEBHOOK_ID.c @@ -0,0 +1,254 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch-private-webhooks-WEBHOOK_ID.c + * @brief Implementation of the PATCH /webhooks/$ID request + * of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /webhooks/$ID operation. + */ +struct TALER_MERCHANT_WebhookPatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_WebhookPatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /webhooks/$ID request. + * + * @param cls the `struct TALER_MERCHANT_WebhookPatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_webhook_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WebhookPatchHandle *wph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + wph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /webhooks/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + wph->cb (wph->cb_cls, + &hr); + TALER_MERCHANT_webhook_patch_cancel (wph); +} + + +struct TALER_MERCHANT_WebhookPatchHandle * +TALER_MERCHANT_webhook_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *webhook_id, + const char *event_type, + const char *url, + const char *http_method, + const char *header_template, + const char *body_template, + TALER_MERCHANT_WebhookPatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WebhookPatchHandle *wph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("event_type", + event_type), + GNUNET_JSON_pack_string ("url", + url), + GNUNET_JSON_pack_string ("http_method", + http_method), + GNUNET_JSON_pack_string ("header_template", + header_template), + GNUNET_JSON_pack_string ("body_template", + body_template)); + wph = GNUNET_new (struct TALER_MERCHANT_WebhookPatchHandle); + wph->ctx = ctx; + wph->cb = cb; + wph->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/webhooks/%s", + webhook_id); + wph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == wph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (wph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (wph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&wph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (wph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + wph->job = GNUNET_CURL_job_add2 (ctx, + eh, + wph->post_ctx.headers, + &handle_patch_webhook_finished, + wph); + } + return wph; +} + + +void +TALER_MERCHANT_webhook_patch_cancel ( + struct TALER_MERCHANT_WebhookPatchHandle *wph) +{ + if (NULL != wph->job) + { + GNUNET_CURL_job_cancel (wph->job); + wph->job = NULL; + } + TALER_curl_easy_post_finished (&wph->post_ctx); + GNUNET_free (wph->url); + GNUNET_free (wph); +} + + +/* end of merchant_api_patch_webhook.c */ diff --git a/src/lib/merchant_api_patch_account.c b/src/lib/merchant_api_patch_account.c @@ -1,254 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_patch_account.c - * @brief Implementation of the PATCH /accounts/$ID request - * of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a PATCH /accounts/$ID operation. - */ -struct TALER_MERCHANT_AccountPatchHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_AccountPatchCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP PATCH /accounts/$ID request. - * - * @param cls the `struct TALER_MERCHANT_AccountPatchHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_patch_account_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_AccountPatchHandle *tph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "PATCH /accounts/$ID completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_break_op (0); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - tph->cb (tph->cb_cls, - &hr); - TALER_MERCHANT_account_patch_cancel (tph); -} - - -struct TALER_MERCHANT_AccountPatchHandle * -TALER_MERCHANT_account_patch ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_MerchantWireHashP *h_wire, - const char *credit_facade_url, - const json_t *credit_facade_credentials, - TALER_MERCHANT_AccountPatchCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_AccountPatchHandle *tph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("credit_facade_url", - credit_facade_url)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("credit_facade_credentials", - (json_t *) credit_facade_credentials))); - tph = GNUNET_new (struct TALER_MERCHANT_AccountPatchHandle); - tph->ctx = ctx; - tph->cb = cb; - tph->cb_cls = cb_cls; - { - char w_str[sizeof (*h_wire) * 2]; - char *path; - char *end; - - end = GNUNET_STRINGS_data_to_string (h_wire, - sizeof (*h_wire), - w_str, - sizeof (w_str)); - *end = '\0'; - GNUNET_asprintf (&path, - "private/accounts/%s", - w_str); - tph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (tph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&tph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (tph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_PATCH)); - tph->job = GNUNET_CURL_job_add2 (ctx, - eh, - tph->post_ctx.headers, - &handle_patch_account_finished, - tph); - } - return tph; -} - - -void -TALER_MERCHANT_account_patch_cancel ( - struct TALER_MERCHANT_AccountPatchHandle *tph) -{ - if (NULL != tph->job) - { - GNUNET_CURL_job_cancel (tph->job); - tph->job = NULL; - } - TALER_curl_easy_post_finished (&tph->post_ctx); - GNUNET_free (tph->url); - GNUNET_free (tph); -} - - -/* end of merchant_api_patch_account.c */ diff --git a/src/lib/merchant_api_patch_instance.c b/src/lib/merchant_api_patch_instance.c @@ -1,271 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_patch_instance.c - * @brief Implementation of the PATCH /instances/$ID request - * of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_kyclogic_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a PATCH /instances/$ID operation. - */ -struct TALER_MERCHANT_InstancePatchHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_InstancePatchCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP PATCH /instances/$ID request. - * - * @param cls the `struct TALER_MERCHANT_InstancePatchHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_patch_instance_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_InstancePatchHandle *iph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - iph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "PATCH /instances/$ID completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - iph->cb (iph->cb_cls, - &hr); - TALER_MERCHANT_instance_patch_cancel (iph); -} - - -struct TALER_MERCHANT_InstancePatchHandle * -TALER_MERCHANT_instance_patch ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - const char *name, - const json_t *address, - const json_t *jurisdiction, - bool use_stefan, - struct GNUNET_TIME_Relative default_wire_transfer_delay, - struct GNUNET_TIME_Relative default_pay_delay, - struct GNUNET_TIME_Relative default_refund_delay, - TALER_MERCHANT_InstancePatchCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_InstancePatchHandle *iph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("name", - name), - GNUNET_JSON_pack_object_incref ("address", - (json_t *) address), - GNUNET_JSON_pack_object_incref ("jurisdiction", - (json_t *) jurisdiction), - GNUNET_JSON_pack_bool ("use_stefan", - use_stefan), - GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay", - default_wire_transfer_delay), - GNUNET_JSON_pack_time_rel ("default_pay_delay", - default_pay_delay), - GNUNET_JSON_pack_time_rel ("default_refund_delay", - default_refund_delay), - /* FIXME: add eventually to arguments when we transform the API... */ - GNUNET_JSON_pack_time_rounder_interval ( - "default_wire_transfer_rounding_interval", - GNUNET_TIME_RI_NONE) - ); - iph = GNUNET_new (struct TALER_MERCHANT_InstancePatchHandle); - iph->ctx = ctx; - iph->cb = cb; - iph->cb_cls = cb_cls; - if (NULL != instance_id) - { - char *path; - - GNUNET_asprintf (&path, - "management/instances/%s", - instance_id); - iph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - else - { - /* backend_url is already identifying the instance */ - iph->url = TALER_url_join (backend_url, - "private", - NULL); - } - if (NULL == iph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (iph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (iph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&iph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (iph->url); - GNUNET_free (iph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_PATCH)); - iph->job = GNUNET_CURL_job_add2 (ctx, - eh, - iph->post_ctx.headers, - &handle_patch_instance_finished, - iph); - } - return iph; -} - - -void -TALER_MERCHANT_instance_patch_cancel ( - struct TALER_MERCHANT_InstancePatchHandle *iph) -{ - if (NULL != iph->job) - { - GNUNET_CURL_job_cancel (iph->job); - iph->job = NULL; - } - TALER_curl_easy_post_finished (&iph->post_ctx); - GNUNET_free (iph->url); - GNUNET_free (iph); -} - - -/* end of merchant_api_patch_instance.c */ diff --git a/src/lib/merchant_api_patch_order_forget.c b/src/lib/merchant_api_patch_order_forget.c @@ -1,254 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020, 2021, 2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_patch_order_forget.c - * @brief Implementation of the PATCH /orders/$ID/forget request - * of the merchant's HTTP API - * @author Jonathan Buchanan - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a PATCH /orders/$ORDER_ID/forget operation. - */ -struct TALER_MERCHANT_OrderForgetHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ForgetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP PATCH /orders/$ORDER_ID/forget request. - * - * @param cls the `struct TALER_MERCHANT_OrderForgetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_forget_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderForgetHandle *ofh = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - ofh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "PATCH /orders/$ORDER_ID/forget completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - /* fields were NOW forgotten */ - break; - case MHD_HTTP_NO_CONTENT: - /* fields were already forgotten before */ - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - ofh->cb (ofh->cb_cls, - &hr); - TALER_MERCHANT_order_forget_cancel (ofh); -} - - -struct TALER_MERCHANT_OrderForgetHandle * -TALER_MERCHANT_order_forget ( - struct GNUNET_CURL_Context *ctx, - const char *merchant_url, - const char *order_id, - unsigned int fields_length, - const char *fields[static fields_length], - TALER_MERCHANT_ForgetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OrderForgetHandle *ofh; - json_t *req_fields; - json_t *req_obj; - - req_fields = json_array (); - if (NULL == req_fields) - { - GNUNET_break (0); - return NULL; - } - for (unsigned int i = 0; i<fields_length; i++) - { - if (0 != - json_array_append_new (req_fields, - json_string (fields[i]))) - { - GNUNET_break (0); - json_decref (req_fields); - return NULL; - } - } - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("fields", - req_fields)); - ofh = GNUNET_new (struct TALER_MERCHANT_OrderForgetHandle); - ofh->ctx = ctx; - ofh->cb = cb; - ofh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/orders/%s/forget", - order_id); - ofh->url = TALER_url_join (merchant_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == ofh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (ofh); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (ofh->url); - if (GNUNET_OK != - TALER_curl_easy_post (&ofh->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (ofh); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_PATCH)); - ofh->job = GNUNET_CURL_job_add2 (ctx, - eh, - ofh->post_ctx.headers, - &handle_forget_finished, - ofh); - } - return ofh; -} - - -void -TALER_MERCHANT_order_forget_cancel ( - struct TALER_MERCHANT_OrderForgetHandle *ofh) -{ - if (NULL != ofh->job) - { - GNUNET_CURL_job_cancel (ofh->job); - ofh->job = NULL; - } - TALER_curl_easy_post_finished (&ofh->post_ctx); - GNUNET_free (ofh->url); - GNUNET_free (ofh); -} - - -/* end of merchant_api_patch_order_forget.c */ diff --git a/src/lib/merchant_api_patch_otp_device.c b/src/lib/merchant_api_patch_otp_device.c @@ -1,252 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_patch_otp_device.c - * @brief Implementation of the PATCH /otp-devices/$ID request - * of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a PATCH /otp-devices/$ID operation. - */ -struct TALER_MERCHANT_OtpDevicePatchHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OtpDevicePatchCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP PATCH /otp-devices/$ID request. - * - * @param cls the `struct TALER_MERCHANT_OtpDevicePatchHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_patch_otp_device_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OtpDevicePatchHandle *tph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "PATCH /otp-devices/$ID completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_break_op (0); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - tph->cb (tph->cb_cls, - &hr); - TALER_MERCHANT_otp_device_patch_cancel (tph); -} - - -struct TALER_MERCHANT_OtpDevicePatchHandle * -TALER_MERCHANT_otp_device_patch ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *otp_device_id, - const char *otp_device_description, - const char *otp_key, - enum TALER_MerchantConfirmationAlgorithm mca, - uint64_t otp_ctr, - TALER_MERCHANT_OtpDevicePatchCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OtpDevicePatchHandle *tph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("otp_device_description", - otp_device_description), - GNUNET_JSON_pack_uint64 ("otp_algorithm", - (uint32_t) mca), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("otp_key", - otp_key)), - GNUNET_JSON_pack_uint64 ("otp_ctr", - otp_ctr)); - tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicePatchHandle); - tph->ctx = ctx; - tph->cb = cb; - tph->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/otp-devices/%s", - otp_device_id); - tph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (tph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&tph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (tph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_PATCH)); - tph->job = GNUNET_CURL_job_add2 (ctx, - eh, - tph->post_ctx.headers, - &handle_patch_otp_device_finished, - tph); - } - return tph; -} - - -void -TALER_MERCHANT_otp_device_patch_cancel ( - struct TALER_MERCHANT_OtpDevicePatchHandle *tph) -{ - if (NULL != tph->job) - { - GNUNET_CURL_job_cancel (tph->job); - tph->job = NULL; - } - TALER_curl_easy_post_finished (&tph->post_ctx); - GNUNET_free (tph->url); - GNUNET_free (tph); -} - - -/* end of merchant_api_patch_otp_device.c */ diff --git a/src/lib/merchant_api_patch_product.c b/src/lib/merchant_api_patch_product.c @@ -1,346 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020, 2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_patch_product.c - * @brief Implementation of the PATCH /products/$ID request - * of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a PATCH /products/$ID operation. - */ -struct TALER_MERCHANT_ProductPatchHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ProductPatchCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP PATCH /products/$ID request. - * - * @param cls the `struct TALER_MERCHANT_ProductPatchHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_patch_product_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ProductPatchHandle *pph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - pph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "PATCH /products/$ID completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_break_op (0); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - pph->cb (pph->cb_cls, - &hr); - TALER_MERCHANT_product_patch_cancel (pph); -} - - -struct TALER_MERCHANT_ProductPatchHandle * -TALER_MERCHANT_product_patch2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - const char *description, - const json_t *description_i18n, - const char *unit, - const struct TALER_Amount *unit_prices, - size_t unit_price_len, - const char *image, - const json_t *taxes, - int64_t total_stock, - uint32_t total_stock_frac, - bool unit_allow_fraction, - const uint32_t *unit_precision_level, - uint64_t total_lost, - const json_t *address, - struct GNUNET_TIME_Timestamp next_restock, - TALER_MERCHANT_ProductPatchCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ProductPatchHandle *pph; - json_t *req_obj; - char unit_total_stock_buf[64]; - - TALER_MERCHANT_format_stock_string (total_stock, - total_stock_frac, - unit_total_stock_buf, - sizeof (unit_total_stock_buf)); - - { - req_obj = GNUNET_JSON_PACK ( - /* FIXME: once we move to the new-style API, - allow applications to set the product name properly! */ - GNUNET_JSON_pack_string ("product_name", - description), - GNUNET_JSON_pack_string ("description", - description), - GNUNET_JSON_pack_object_incref ("description_i18n", - (json_t *) description_i18n), - GNUNET_JSON_pack_string ("unit", - unit), - TALER_JSON_pack_amount_array ("unit_price", - unit_price_len, - unit_prices), - GNUNET_JSON_pack_string ("image", - image), - GNUNET_JSON_pack_array_incref ("taxes", - (json_t *) taxes), - GNUNET_JSON_pack_string ("unit_total_stock", - unit_total_stock_buf), - GNUNET_JSON_pack_bool ("unit_allow_fraction", - unit_allow_fraction), - GNUNET_JSON_pack_uint64 ("total_lost", - total_lost), - GNUNET_JSON_pack_object_incref ("address", - (json_t *) address), - GNUNET_JSON_pack_timestamp ("next_restock", - next_restock)); - } - if (NULL != unit_precision_level) - { - GNUNET_assert (0 == - json_object_set_new (req_obj, - "unit_precision_level", - json_integer ( - *unit_precision_level))); - } - if (! unit_allow_fraction) - { - GNUNET_assert (0 == - json_object_del (req_obj, - "unit_allow_fraction")); - if (NULL != unit_precision_level) - GNUNET_assert (0 == - json_object_del (req_obj, - "unit_precision_level")); - } - pph = GNUNET_new (struct TALER_MERCHANT_ProductPatchHandle); - pph->ctx = ctx; - pph->cb = cb; - pph->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/products/%s", - product_id); - pph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == pph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (pph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (pph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&pph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (pph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_PATCH)); - pph->job = GNUNET_CURL_job_add2 (ctx, - eh, - pph->post_ctx.headers, - &handle_patch_product_finished, - pph); - } - return pph; -} - - -struct TALER_MERCHANT_ProductPatchHandle * -TALER_MERCHANT_product_patch ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - const char *description, - const json_t *description_i18n, - const char *unit, - const struct TALER_Amount *price, - const char *image, - const json_t *taxes, - int64_t total_stock, - uint64_t total_lost, - const json_t *address, - struct GNUNET_TIME_Timestamp next_restock, - TALER_MERCHANT_ProductPatchCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_product_patch2 (ctx, - backend_url, - product_id, - description, - description_i18n, - unit, - price, - 1, - image, - taxes, - total_stock, - 0, - false, - NULL, - total_lost, - address, - next_restock, - cb, - cb_cls); -} - - -void -TALER_MERCHANT_product_patch_cancel ( - struct TALER_MERCHANT_ProductPatchHandle *pph) -{ - if (NULL != pph->job) - { - GNUNET_CURL_job_cancel (pph->job); - pph->job = NULL; - } - TALER_curl_easy_post_finished (&pph->post_ctx); - GNUNET_free (pph->url); - GNUNET_free (pph); -} - - -/* end of merchant_api_patch_product.c */ diff --git a/src/lib/merchant_api_patch_template.c b/src/lib/merchant_api_patch_template.c @@ -1,249 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_patch_template.c - * @brief Implementation of the PATCH /templates/$ID request - * of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a PATCH /templates/$ID operation. - */ -struct TALER_MERCHANT_TemplatePatchHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TemplatePatchCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP PATCH /templates/$ID request. - * - * @param cls the `struct TALER_MERCHANT_TemplatePatchHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_patch_template_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TemplatePatchHandle *tph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "PATCH /templates/$ID completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_break_op (0); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - tph->cb (tph->cb_cls, - &hr); - TALER_MERCHANT_template_patch_cancel (tph); -} - - -struct TALER_MERCHANT_TemplatePatchHandle * -TALER_MERCHANT_template_patch ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *template_id, - const char *template_description, - const char *otp_id, - json_t *template_contract, - TALER_MERCHANT_TemplatePatchCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TemplatePatchHandle *tph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("template_description", - template_description), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("otp_id", - otp_id)), - GNUNET_JSON_pack_object_incref ("template_contract", - (json_t *) template_contract)); - tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle); - tph->ctx = ctx; - tph->cb = cb; - tph->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/templates/%s", - template_id); - tph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (tph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&tph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (tph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_PATCH)); - tph->job = GNUNET_CURL_job_add2 (ctx, - eh, - tph->post_ctx.headers, - &handle_patch_template_finished, - tph); - } - return tph; -} - - -void -TALER_MERCHANT_template_patch_cancel ( - struct TALER_MERCHANT_TemplatePatchHandle *tph) -{ - if (NULL != tph->job) - { - GNUNET_CURL_job_cancel (tph->job); - tph->job = NULL; - } - TALER_curl_easy_post_finished (&tph->post_ctx); - GNUNET_free (tph->url); - GNUNET_free (tph); -} - - -/* end of merchant_api_patch_template.c */ diff --git a/src/lib/merchant_api_patch_unit.c b/src/lib/merchant_api_patch_unit.c @@ -1,291 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free - Software Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_patch_unit.c - * @brief Implementation of PATCH /private/units/$ID - * @author Bohdan Potuzhnyi - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a PATCH /private/units/$ID operation. - */ -struct TALER_MERCHANT_UnitPatchHandle -{ - /** - * Fully qualified request URL. - */ - char *url; - - /** - * In-flight CURL job. - */ - struct GNUNET_CURL_Job *job; - - /** - * Completion callback. - */ - TALER_MERCHANT_UnitPatchCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Keeps POST body and headers alive. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * Called when the HTTP transfer finishes. - * - * @param cls operation handle - * @param response_code HTTP status (0 on failure) - * @param response parsed JSON reply (NULL if unavailable) - */ -static void -handle_patch_unit_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_UnitPatchHandle *uph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - uph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "PATCH /private/units completed with status %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - case MHD_HTTP_UNAUTHORIZED: - case MHD_HTTP_FORBIDDEN: - case MHD_HTTP_NOT_FOUND: - case MHD_HTTP_CONFLICT: - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response %u/%d for PATCH /private/units\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - uph->cb (uph->cb_cls, - &hr); - TALER_MERCHANT_unit_patch_cancel (uph); -} - - -struct TALER_MERCHANT_UnitPatchHandle * -TALER_MERCHANT_unit_patch (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *unit_id, - const char *unit_name_long, - const char *unit_name_short, - const json_t *unit_name_long_i18n, - const json_t *unit_name_short_i18n, - const bool *unit_allow_fraction, - const uint32_t *unit_precision_level, - const bool *unit_active, - TALER_MERCHANT_UnitPatchCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_UnitPatchHandle *uph; - json_t *req_obj; - char *path; - - req_obj = json_object (); - if (NULL == req_obj) - return NULL; - if (NULL != unit_name_long) - { - if (0 != json_object_set_new (req_obj, - "unit_name_long", - json_string (unit_name_long))) - { - json_decref (req_obj); - return NULL; - } - } - if (NULL != unit_name_short) - { - if (0 != json_object_set_new (req_obj, - "unit_name_short", - json_string (unit_name_short))) - { - json_decref (req_obj); - return NULL; - } - } - if (NULL != unit_name_long_i18n) - { - if (0 != json_object_set_new (req_obj, - "unit_name_long_i18n", - json_incref ((json_t *) unit_name_long_i18n))) - { - json_decref (req_obj); - return NULL; - } - } - if (NULL != unit_name_short_i18n) - { - if (0 != json_object_set_new (req_obj, - "unit_name_short_i18n", - json_incref ( - (json_t *) unit_name_short_i18n))) - { - json_decref (req_obj); - return NULL; - } - } - if (NULL != unit_allow_fraction) - { - if (0 != json_object_set_new (req_obj, - "unit_allow_fraction", - json_boolean (*unit_allow_fraction))) - { - json_decref (req_obj); - return NULL; - } - } - if (NULL != unit_precision_level) - { - if (0 != json_object_set_new (req_obj, - "unit_precision_level", - json_integer ( - (json_int_t) *unit_precision_level))) - { - json_decref (req_obj); - return NULL; - } - } - if (NULL != unit_active) - { - if (0 != json_object_set_new (req_obj, - "unit_active", - json_boolean (*unit_active))) - { - json_decref (req_obj); - return NULL; - } - } - if (0 == json_object_size (req_obj)) - { - json_decref (req_obj); - GNUNET_break (0); - return NULL; - } - - GNUNET_asprintf (&path, - "private/units/%s", - unit_id); - uph = GNUNET_new (struct TALER_MERCHANT_UnitPatchHandle); - uph->ctx = ctx; - uph->cb = cb; - uph->cb_cls = cb_cls; - uph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - if (NULL == uph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to build /private/units/%s URL\n", - unit_id); - json_decref (req_obj); - GNUNET_free (uph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (uph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&uph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (uph->url); - GNUNET_free (uph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_PATCH)); - uph->job = GNUNET_CURL_job_add2 (ctx, - eh, - uph->post_ctx.headers, - &handle_patch_unit_finished, - uph); - } - return uph; -} - - -void -TALER_MERCHANT_unit_patch_cancel (struct TALER_MERCHANT_UnitPatchHandle *uph) -{ - if (NULL != uph->job) - { - GNUNET_CURL_job_cancel (uph->job); - uph->job = NULL; - } - TALER_curl_easy_post_finished (&uph->post_ctx); - GNUNET_free (uph->url); - GNUNET_free (uph); -} - - -/* end of merchant_api_patch_unit.c */ diff --git a/src/lib/merchant_api_patch_webhook.c b/src/lib/merchant_api_patch_webhook.c @@ -1,254 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_patch_webhook.c - * @brief Implementation of the PATCH /webhooks/$ID request - * of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a PATCH /webhooks/$ID operation. - */ -struct TALER_MERCHANT_WebhookPatchHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_WebhookPatchCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP PATCH /webhooks/$ID request. - * - * @param cls the `struct TALER_MERCHANT_WebhookPatchHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_patch_webhook_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_WebhookPatchHandle *wph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - wph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "PATCH /webhooks/$ID completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_break_op (0); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - wph->cb (wph->cb_cls, - &hr); - TALER_MERCHANT_webhook_patch_cancel (wph); -} - - -struct TALER_MERCHANT_WebhookPatchHandle * -TALER_MERCHANT_webhook_patch ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *webhook_id, - const char *event_type, - const char *url, - const char *http_method, - const char *header_template, - const char *body_template, - TALER_MERCHANT_WebhookPatchCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_WebhookPatchHandle *wph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("event_type", - event_type), - GNUNET_JSON_pack_string ("url", - url), - GNUNET_JSON_pack_string ("http_method", - http_method), - GNUNET_JSON_pack_string ("header_template", - header_template), - GNUNET_JSON_pack_string ("body_template", - body_template)); - wph = GNUNET_new (struct TALER_MERCHANT_WebhookPatchHandle); - wph->ctx = ctx; - wph->cb = cb; - wph->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/webhooks/%s", - webhook_id); - wph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == wph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (wph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (wph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&wph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (wph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_PATCH)); - wph->job = GNUNET_CURL_job_add2 (ctx, - eh, - wph->post_ctx.headers, - &handle_patch_webhook_finished, - wph); - } - return wph; -} - - -void -TALER_MERCHANT_webhook_patch_cancel ( - struct TALER_MERCHANT_WebhookPatchHandle *wph) -{ - if (NULL != wph->job) - { - GNUNET_CURL_job_cancel (wph->job); - wph->job = NULL; - } - TALER_curl_easy_post_finished (&wph->post_ctx); - GNUNET_free (wph->url); - GNUNET_free (wph); -} - - -/* end of merchant_api_patch_webhook.c */ diff --git a/src/lib/merchant_api_pay_service.c b/src/lib/merchant_api_pay_service.c @@ -0,0 +1,1070 @@ +/* + This file is part of TALER + Copyright (C) 2014-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LIB. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_pay_service.c + * @brief Implementation of the the ideology + * from the pay_service as copy of + * merchant_api_post_order_pay.c + * @author Bohdan Potuzhnyi + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "taler/taler_merchant_pay_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <stdio.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_exchange_service.h> +#include <taler/taler_curl_lib.h> + +/** + * @brief A Pay Handle + */ +struct TALER_MERCHANT_OrderPayHandle +{ + /** + * Reference to the GNUNET CURL execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Callback to invoke with the payment result ("pay" mode). + */ + TALER_MERCHANT_OrderPayCallback cb; + + /** + * Closure data for @a cb. + */ + TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *cb_cls; + + /* Mandatory scalars: */ + + /** + * Base URL of the merchant service. + */ + char *merchant_url; + + /** + * Identifier of the order being paid. + */ + char *order_id; + + /** + * Session identifier for this payment attempt. + */ + char *session_id; + + /** + * Timestamp when the payment request was created. + */ + struct GNUNET_TIME_Timestamp timestamp; + + /** + * Deadline after which refunds are no longer allowed. + */ + struct GNUNET_TIME_Timestamp refund_deadline; + + /** + * Wire hash for communicating payment details. + */ + struct TALER_MerchantWireHashP h_wire; + + /** + * Indicates whether @a h_wire has been set. + */ + bool has_h_wire; + + /* Wallet mode fields: */ + + /** + * Indicates whether a contract hash was provided. + */ + bool has_h_contract; + + /** + * Hash of the private contract terms (wallet mode only). + */ + struct TALER_PrivateContractHashP h_contract_terms; + + /** + * Indicates whether the merchant public key was provided. + */ + bool has_merchant_pub; + + /** + * Merchant’s public key for verifying signatures (wallet mode). + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * Indicates whether a choice index was provided. + */ + bool has_choice_index; + + /** + * Selected index of the contract choice (for token operations). + */ + int choice_index; + + /** + * Legacy: pointer to the amount structure for strcmp checks. + */ + const struct TALER_Amount *amount; + + /** + * Legacy: pointer to the maximum fee structure for strcmp checks. + */ + const struct TALER_Amount *max_fee; + + /* Raw arrays as passed in via set_options(): */ + + /** + * Coins used for payment. + */ + struct + { + /** + * Number of coins provided. + */ + unsigned int num_coins; + /** + * Array of coins to spend. + */ + const struct TALER_MERCHANT_PayCoin *coins; + } coins; + + /** + * Input tokens to use (wallet mode). + */ + struct + { + /** + * Number of tokens provided. + */ + unsigned int num_tokens; + /** + * Array of tokens to redeem. + */ + const struct TALER_MERCHANT_UseToken *tokens; + } input_tokens; + + /** + * Output tokens expected from the merchant. + */ + struct + { + /** + * Number of output tokens expected. + */ + unsigned int num_output_tokens; + /** + * Array of expected output tokens. + */ + const struct TALER_MERCHANT_OutputToken *output_tokens; + } output_tokens; + + /** + * JSON array of token envelope events (from Donau). + */ + json_t *tokens_evs; + + /* Computed once both choice_index and tokens_evs are available: */ + + /** + * JSON object containing wallet-specific data payload. + */ + json_t *wallet_data; + + /** + * Hash code of @a wallet_data for integrity checks. + */ + struct GNUNET_HashCode wallet_data_hash; + + /** + * JSON body being constructed for the HTTP POST. + */ + json_t *body; + + /* Final URL and CURL plumbing: */ + + /** + * Fully formed URL for the POST /order/$ID/pay request. + */ + char *url; + + /** + * CURL post context managing headers and body. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the asynchronous CURL job. + */ + struct GNUNET_CURL_Job *job; + + /** + * Flags indicating which payment options have been set. + */ + bool field_seen[TALER_MERCHANT_OrderPayOptionType_LENGTH]; + + /** + * True if operating in wallet mode (using tokens/contracts). + */ + bool am_wallet; + + /** + * Raw JSON data of `donau` for `wallet_data`. + */ + json_t *donau_data; +}; + +/** + * Parse blindly signed output tokens from response. + * + * @param token_sigs the JSON array with the token signatures. Can be NULL. + * @param tokens where to store the parsed tokens. + * @param num_tokens where to store the length of the @a tokens array. + */ +static enum GNUNET_GenericReturnValue +parse_tokens (const json_t *token_sigs, + struct TALER_MERCHANT_OutputToken **tokens, + unsigned int *num_tokens) +{ + GNUNET_array_grow (*tokens, + *num_tokens, + json_array_size (token_sigs)); + + for (unsigned int i = 0; i<(*num_tokens); i++) + { + struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i]; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_blinded_token_issue_sig ("blind_sig", + &token->blinded_sig), + GNUNET_JSON_spec_end () + }; + const json_t *jtoken + = json_array_get (token_sigs, + i); + + if (NULL == jtoken) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_JSON_parse (jtoken, + spec, + NULL, NULL)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + + return GNUNET_YES; +} + + +/** + * Function called when we're done processing the + * HTTP /pay request. + * + * @param cls the `struct TALER_MERCHANT_Pay` + * @param response_code HTTP response code, 0 on error + * @param resp response body, NULL if not in JSON + */ +static void +handle_finished (void *cls, + long response_code, + const void *resp) +{ + struct TALER_MERCHANT_OrderPayHandle *oph = cls; + const json_t *json = resp; + struct TALER_MERCHANT_PayResponse pr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received /pay response with status code %u\n", + (unsigned int) response_code); + + json_dumpf (json, + stderr, + JSON_INDENT (2)); + + oph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "/pay completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (oph->am_wallet) + { + const json_t *token_sigs = NULL; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("sig", + &pr.details.ok.merchant_sig), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("pos_confirmation", + &pr.details.ok.pos_confirmation), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("token_sigs", + &token_sigs), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "sig field missing in response"; + break; + } + + if (GNUNET_OK != + parse_tokens (token_sigs, + &pr.details.ok.tokens, + &pr.details.ok.num_tokens)) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "failed to parse token_sigs field in response"; + break; + } + + if (GNUNET_OK != + TALER_merchant_pay_verify (&oph->h_contract_terms, + &oph->merchant_pub, + &pr.details.ok.merchant_sig)) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "signature invalid"; + } + } + break; + /* Tolerating Not Acceptable because sometimes + * - especially in tests - we might want to POST + * coins one at a time. */ + case MHD_HTTP_NOT_ACCEPTABLE: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_BAD_REQUEST: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_PAYMENT_REQUIRED: + /* was originally paid, but then refunded */ + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_FORBIDDEN: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_REQUEST_TIMEOUT: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + /* The merchant couldn't generate a timely response, likely because + it itself waited too long on the exchange. + Pass on to application. */ + break; + case MHD_HTTP_CONFLICT: + TALER_MERCHANT_parse_error_details_ (json, + MHD_HTTP_CONFLICT, + &pr.hr); + break; + case MHD_HTTP_GONE: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* The merchant says we are too late, the offer has expired or some + denomination key of a coin involved has expired. + Might be a disagreement in timestamps? Still, pass on to application. */ + break; + case MHD_HTTP_PRECONDITION_FAILED: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* Nothing really to verify, the merchant is blaming us for failing to + satisfy some constraint (likely it does not like our exchange because + of some disagreement on the PKI). We should pass the JSON reply to the + application */ + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + { + json_t *ebus = json_object_get (json, + "exchange_base_urls"); + if (NULL == ebus) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "failed to parse exchange_base_urls field in response"; + break; + } + { + size_t alen = json_array_size (ebus); + const char *ebua[GNUNET_NZL (alen)]; + size_t idx; + json_t *jebu; + bool ok = true; + + GNUNET_assert (alen <= UINT_MAX); + json_array_foreach (ebus, idx, jebu) + { + ebua[idx] = json_string_value (jebu); + if (NULL == ebua[idx]) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "non-string value in exchange_base_urls in response"; + ok = false; + break; + } + } + if (! ok) + break; + pr.details.unavailable_for_legal_reasons.num_exchanges + = (unsigned int) alen; + pr.details.unavailable_for_legal_reasons.exchanges + = ebua; + oph->cb (oph->cb_cls, + &pr); + TALER_MERCHANT_order_pay_cancel1 (oph); + return; + } + } + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + case MHD_HTTP_BAD_GATEWAY: + /* Nothing really to verify, the merchant is blaming the exchange. + We should pass the JSON reply to the application */ + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + break; + case MHD_HTTP_SERVICE_UNAVAILABLE: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* Exchange couldn't respond properly; the retry is + left to the application */ + break; + case MHD_HTTP_GATEWAY_TIMEOUT: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* Exchange couldn't respond in a timely fashion; + the retry is left to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) pr.hr.ec); + GNUNET_break_op (0); + break; + } + oph->cb (oph->cb_cls, + &pr); + + if (pr.details.ok.tokens) + { + GNUNET_free (pr.details.ok.tokens); + pr.details.ok.tokens = NULL; + pr.details.ok.num_tokens = 0; + } + + TALER_MERCHANT_order_pay_cancel1 (oph); +} + + +/** + * @brief Create and initialize a new payment handle + * + * Allocates a TALER_MERCHANT_OrderPayHandle, sets up its context and + * prepares an empty JSON body for the /orders/$ID/pay request. + */ +struct TALER_MERCHANT_OrderPayHandle * +TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx, + TALER_MERCHANT_OrderPayCallback cb, + TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE + *cb_cls) +{ + struct TALER_MERCHANT_OrderPayHandle *ph = + GNUNET_new (struct TALER_MERCHANT_OrderPayHandle); + ph->ctx = ctx; + ph->cb = cb; + ph->cb_cls = cb_cls; + ph->body = json_object (); + GNUNET_assert (ph->body); + return ph; +} + + +/** + * @brief Cancel and free a payment handle + * + * Aborts any in-flight CURL job, releases all JSON objects and internal + * buffers, and frees the handle structure itself. + */ +void +TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph) +{ + if (ph->job) + GNUNET_CURL_job_cancel (ph->job); + ph->job = NULL; + + TALER_curl_easy_post_finished (&ph->post_ctx); + + if (ph->body) + json_decref (ph->body); + ph->body = NULL; + + if (ph->wallet_data) + json_decref (ph->wallet_data); + ph->wallet_data = NULL; + + if (ph->tokens_evs) + json_decref (ph->tokens_evs); + ph->tokens_evs = NULL; + + if (ph->donau_data) + json_decref (ph->donau_data); + + GNUNET_free (ph->url); + GNUNET_free (ph->merchant_url); + GNUNET_free (ph->session_id); + GNUNET_free (ph->order_id); + GNUNET_free (ph); +} + + +/** + * @brief Store a JSON snippet under a payment option key + * + * Ensures that an option of type @a ot has not already been set, + * then merges @a snippet into the handle's JSON @c body. Marks the + * field_seen flag and frees @a snippet. + * + * @param ph payment handle receiving the snippet + * @param ot option type under which to store @a snippet + * @param snippet JSON object representing the option payload + * @return #TALER_MERCHANT_OPOEC_OK if stored; appropriate error code otherwise + */ +static enum TALER_MERCHANT_OrderPayErrorCode +store_json_option (struct TALER_MERCHANT_OrderPayHandle *ph, + enum TALER_MERCHANT_OrderPayOptionType ot, + json_t *snippet) +{ + if (ph->field_seen[ot]) + { + json_decref (snippet); + return TALER_MERCHANT_OPOEC_DUPLICATE_OPTION; + } + ph->field_seen[ot] = true; + GNUNET_assert (0 == json_object_update (ph->body, + snippet)); + json_decref (snippet); + return TALER_MERCHANT_OPOEC_OK; +} + + +/** + * @brief Apply user-supplied options to a payment handle + * + * Iterates through a NULL-terminated array of #TALER_MERCHANT_OrderPayOption + * entries, validates and stores each into @a ph. Handles JSON packing and + * internal state updates for coins, tokens, deadlines, Donau data, etc. + */ +enum TALER_MERCHANT_OrderPayErrorCode +TALER_MERCHANT_order_pay_set_options ( + struct TALER_MERCHANT_OrderPayHandle *ph, + const struct TALER_MERCHANT_OrderPayOption options[], + size_t max_options) +{ + for (size_t i = 0; i < max_options + && options[i].ot != TALER_MERCHANT_OrderPayOptionType_END; i++) + { + const struct TALER_MERCHANT_OrderPayOption *o = &options[i]; + + switch (o->ot) + { + case TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL: + ph->merchant_url = GNUNET_strdup (o->details.merchant_url); + break; + + case TALER_MERCHANT_OrderPayOptionType_SESSION_ID: + ph->session_id = GNUNET_strdup (o->details.session_id); + /* add straight into JSON body */ + { + json_t *js = GNUNET_JSON_PACK (GNUNET_JSON_pack_string ("session_id", + o->details. + session_id)); + enum TALER_MERCHANT_OrderPayErrorCode ec = + store_json_option (ph, + o->ot, + js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; + } + + case TALER_MERCHANT_OrderPayOptionType_ORDER_ID: + { + ph->order_id = GNUNET_strdup (o->details.order_id); + break; + } + + case TALER_MERCHANT_OrderPayOptionType_H_CONTRACT: + { + ph->h_contract_terms = *o->details.h_contract; + ph->has_h_contract = true; + + break; + } + + case TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX: + ph->choice_index = o->details.choice_index; + ph->has_choice_index = true; + break; + + case TALER_MERCHANT_OrderPayOptionType_AMOUNT: + { + ph->amount = &o->details.amount; + break; + } + + case TALER_MERCHANT_OrderPayOptionType_MAX_FEE: + { + ph->max_fee = &o->details.max_fee; + break; + } + + case TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB: + { + ph->merchant_pub = o->details.merchant_pub; + ph->has_merchant_pub = true; + + break; + } + + case TALER_MERCHANT_OrderPayOptionType_TIMESTAMP: + { + ph->timestamp = o->details.timestamp; + break; + } + + case TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE: + { + ph->refund_deadline = o->details.refund_deadline; + break; + } + + case TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE: + { + /* FIXME: This one comes from the merchant_api_post_order_pay + no idea do we still need it or not? */ + break; + } + + case TALER_MERCHANT_OrderPayOptionType_H_WIRE: + { + ph->h_wire = o->details.h_wire; + ph->has_h_wire = true; + break; + } + + case TALER_MERCHANT_OrderPayOptionType_COINS: + /* stash for later signing */ + ph->coins.num_coins = o->details.coins.num_coins; + ph->coins.coins = o->details.coins.coins; + break; + + case TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS: + /* stash for later signing */ + ph->input_tokens.num_tokens = o->details.input_tokens.num_tokens; + ph->input_tokens.tokens = o->details.input_tokens.tokens; + break; + + case TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS: + /* store JSON array directly, *and* stash for hash */ + ph->output_tokens.num_output_tokens = + o->details.output_tokens.num_output_tokens; + ph->output_tokens.output_tokens = o->details.output_tokens.output_tokens; + { + /* build and store tokens_evs */ + json_t *arr = json_array (); + + GNUNET_assert (NULL != arr); + for (unsigned j = 0; j < ph->output_tokens.num_output_tokens; j++) + { + const struct TALER_MERCHANT_OutputToken *otk = + &ph->output_tokens.output_tokens[j]; + json_t *je = GNUNET_JSON_PACK (TALER_JSON_pack_token_envelope (NULL, + &otk-> + envelope)); + GNUNET_assert (0 == + json_array_append_new (arr, + je)); + } + + ph->tokens_evs = arr; + + ph->field_seen[o->ot] = true; + } + break; + + case TALER_MERCHANT_OrderPayOptionType_DONAU_URL: + if (NULL == ph->donau_data) + ph->donau_data = json_object (); + GNUNET_assert (0 == + json_object_set_new ( + ph->donau_data, + "url", + json_string (o->details.donau_url))); + break; + + case TALER_MERCHANT_OrderPayOptionType_DONAU_YEAR: + { + if (ph->donau_data == NULL) + ph->donau_data = json_object (); + GNUNET_assert (0 == json_object_set_new ( + ph->donau_data, + "year", + json_integer ((json_int_t) o->details.donau_year))); + break; + } + + case TALER_MERCHANT_OrderPayOptionType_DONAU_BUDIS: + { + if (ph->donau_data == NULL) + ph->donau_data = json_object (); + GNUNET_assert (0 == json_object_set_new ( + ph->donau_data, + "budikeypairs", + json_incref (o->details.donau_budis_json))); + break; + } + + default: + return TALER_MERCHANT_OPOEC_UNKNOWN_OPTION; + } + } + return TALER_MERCHANT_OPOEC_OK; +} + + +/** + * @brief Dispatch the /orders/$ID/pay request + * + * Validates that all mandatory parameters (merchant_url, order_id, coins) + * have been set, builds the final JSON payload, constructs the URL, + * and issues an asynchronous HTTP POST. The payment handle's callback + * will receive completion notifications. + */ +enum TALER_MERCHANT_OrderPayErrorCode +TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) +{ + /* all the old mandatory checks */ + if ( (! ph->merchant_url) || + (! ph->order_id) ) + { + GNUNET_break (0); + return TALER_MERCHANT_OPOEC_MISSING_MANDATORY; + } + if (GNUNET_YES != + TALER_amount_cmp_currency (ph->amount, + ph->max_fee)) + { + GNUNET_break (0); + return TALER_MERCHANT_OPOEC_INVALID_VALUE; + } + + /* build wallet_data hash for signing coins & tokens */ + if (ph->has_choice_index) + { + /* base fields */ + json_t *wd = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_int64 ("choice_index", + ph->choice_index), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_incref ("tokens_evs", + ph->tokens_evs)) + ); + + /* Putting prepared donau_data into the wallet_data */ + if (ph->donau_data) + GNUNET_assert (0 == json_object_set_new ( + wd, + "donau", + json_incref (ph->donau_data))); + + ph->wallet_data = wd; + + TALER_json_hash (ph->wallet_data, + &ph->wallet_data_hash); + + store_json_option (ph, + TALER_MERCHANT_OrderPayOptionType_WALLET_DATA, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_object_incref ("wallet_data", + ph->wallet_data))); + } + + /* sign coins AND build the “coins” JSON in one pass */ + { + struct TALER_Amount total_fee; + struct TALER_Amount total_amount; + json_t *arr = json_array (); + + GNUNET_assert (NULL != arr); + for (unsigned i = 0; i < ph->coins.num_coins; i++) + { + const struct TALER_MERCHANT_PayCoin *c = &ph->coins.coins[i]; + struct TALER_MERCHANT_PaidCoin pc; + json_t *je; + + /* sign */ + struct TALER_Amount fee; + struct TALER_DenominationHashP h_denom_pub; + + TALER_denom_pub_hash (&c->denom_pub, + &h_denom_pub); + if (0 > TALER_amount_subtract (&fee, + &c->amount_with_fee, + &c->amount_without_fee)) + return TALER_MERCHANT_OPOEC_INVALID_VALUE; + + + TALER_wallet_deposit_sign (&c->amount_with_fee, + &fee, + &ph->h_wire, + &ph->h_contract_terms, + (NULL != ph->wallet_data) + ? &ph->wallet_data_hash + : NULL, + c->h_age_commitment, + NULL, + &h_denom_pub, + ph->timestamp, + &ph->merchant_pub, + ph->refund_deadline, + &c->coin_priv, + &pc.coin_sig); + + pc.denom_pub = c->denom_pub; + pc.denom_sig = c->denom_sig; + pc.denom_value = c->denom_value; + pc.amount_with_fee = c->amount_with_fee; + pc.amount_without_fee = c->amount_without_fee; + pc.exchange_url = c->exchange_url; + GNUNET_CRYPTO_eddsa_key_get_public (&c->coin_priv.eddsa_priv, + &pc.coin_pub.eddsa_pub); + + /* JSON */ + je = GNUNET_JSON_PACK (TALER_JSON_pack_amount ("contribution", + &pc.amount_with_fee), + GNUNET_JSON_pack_data_auto ("coin_pub", + &pc.coin_pub), + GNUNET_JSON_pack_string ("exchange_url", + pc.exchange_url), + GNUNET_JSON_pack_data_auto ("h_denom", + &h_denom_pub), + TALER_JSON_pack_denom_sig ("ub_sig", + &pc.denom_sig), + GNUNET_JSON_pack_data_auto ("coin_sig", + &pc.coin_sig)); + GNUNET_assert (0 == + json_array_append_new (arr, + je)); + + /* optional totals if you need them later + (kept here because they existed in the legacy code) */ + if (0 == i) + { + total_fee = fee; + total_amount = pc.amount_with_fee; + } + else + { + if ( (0 > + TALER_amount_add (&total_fee, + &total_fee, + &fee)) || + (0 > + TALER_amount_add (&total_amount, + &total_amount, + &pc.amount_with_fee)) ) + { + return TALER_MERCHANT_OPOEC_INVALID_VALUE; + } + } + } + + /* Putting coins to the body*/ + { + enum TALER_MERCHANT_OrderPayErrorCode ec = + store_json_option (ph, + TALER_MERCHANT_OrderPayOptionType_COINS, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("coins", + arr) + )); + if (TALER_MERCHANT_OPOEC_OK != ec) + { + return ec; + } + } + } + + /* sign & pack input_tokens into used_tokens array in body */ + if (ph->input_tokens.num_tokens > 0) + { + struct TALER_MERCHANT_UsedToken ut[ph->input_tokens.num_tokens]; + json_t *arr = json_array (); + + GNUNET_assert (NULL != arr); + for (unsigned i = 0; i < ph->input_tokens.num_tokens; i++) + { + json_t *je; + const struct TALER_MERCHANT_UseToken *in = &ph->input_tokens.tokens[i]; + struct TALER_MERCHANT_UsedToken *t = &ut[i]; + + TALER_wallet_token_use_sign (&ph->h_contract_terms, + &ph->wallet_data_hash, + &in->token_priv, + &t->token_sig); + + t->ub_sig = in->ub_sig; + t->issue_pub = in->issue_pub; + + GNUNET_CRYPTO_eddsa_key_get_public (&in->token_priv.private_key, + &t->token_pub.public_key); + + je = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("token_sig", + &t->token_sig), + TALER_JSON_pack_token_issue_sig ("ub_sig", + &t->ub_sig), + GNUNET_JSON_pack_data_auto ("h_issue", + &t->issue_pub.public_key->pub_key_hash), + GNUNET_JSON_pack_data_auto ("token_pub", + &t->token_pub) + ); + GNUNET_assert (0 == + json_array_append_new (arr, + je)); + } + + store_json_option (ph, + TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("tokens", + arr) + ) + ); + } + + + /* post the request */ + { + char *path; + CURL *eh; + GNUNET_asprintf (&path, + "orders/%s/pay", + ph->order_id); + ph->url = TALER_url_join (ph->merchant_url, + path, + NULL); + GNUNET_free (path); + + if (NULL == ph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (ph->body); + GNUNET_free (ph); + return TALER_MERCHANT_OPOEC_URL_FAILURE; + } + + eh = TALER_MERCHANT_curl_easy_get_ (ph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&ph->post_ctx, + eh, + ph->body)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + GNUNET_free (ph->url); + GNUNET_free (ph); + return TALER_MERCHANT_OPOEC_CURL_FAILURE; + } + + ph->job = GNUNET_CURL_job_add2 (ph->ctx, + eh, + ph->post_ctx.headers, + &handle_finished, + ph); + + ph->am_wallet = true; + return TALER_MERCHANT_OPOEC_OK; + } +} diff --git a/src/lib/merchant_api_post-management-instances-INSTANCE-auth.c b/src/lib/merchant_api_post-management-instances-INSTANCE-auth.c @@ -0,0 +1,227 @@ +/* + This file is part of TALER + Copyright (C) 2014-2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-management-instances-INSTANCE-auth.c + * @brief Implementation of the POST /instance/$ID/private/auth request + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /instances/$ID/private/auth operation. + */ +struct TALER_MERCHANT_InstanceAuthPostHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_InstanceAuthPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /instances/$ID/private/auth request. + * + * @param cls the `struct TALER_MERCHANT_InstanceAuthPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_instance_auth_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_InstanceAuthPostHandle *iaph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + iaph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /instances/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + /* happens if the auth token is malformed */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + iaph->cb (iaph->cb_cls, + &hr); + TALER_MERCHANT_instance_auth_post_cancel (iaph); +} + + +struct TALER_MERCHANT_InstanceAuthPostHandle * +TALER_MERCHANT_instance_auth_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const char *auth_password, + TALER_MERCHANT_InstanceAuthPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_InstanceAuthPostHandle *iaph; + json_t *req_obj; + + iaph = GNUNET_new (struct TALER_MERCHANT_InstanceAuthPostHandle); + iaph->ctx = ctx; + iaph->cb = cb; + iaph->cb_cls = cb_cls; + if (NULL != instance_id) + { + char *path; + + GNUNET_asprintf (&path, + "management/instances/%s/auth", + instance_id); + iaph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + else + { + /* backend_url is already identifying the instance */ + iaph->url = TALER_url_join (backend_url, + "private/auth", + NULL); + } + if (NULL == iaph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (iaph); + return NULL; + } + if (NULL == auth_password) + { + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("method", + "external")); + } + else + { + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("method", + "token"), + GNUNET_JSON_pack_string ("password", + auth_password)); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + iaph->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (iaph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&iaph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (iaph->url); + GNUNET_free (iaph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_POST)); + iaph->job = GNUNET_CURL_job_add2 (ctx, + eh, + iaph->post_ctx.headers, + &handle_post_instance_auth_finished, + iaph); + } + return iaph; +} + + +void +TALER_MERCHANT_instance_auth_post_cancel ( + struct TALER_MERCHANT_InstanceAuthPostHandle *iaph) +{ + if (NULL != iaph->job) + GNUNET_CURL_job_cancel (iaph->job); + TALER_curl_easy_post_finished (&iaph->post_ctx); + GNUNET_free (iaph->url); + GNUNET_free (iaph); +} diff --git a/src/lib/merchant_api_post-management-instances.c b/src/lib/merchant_api_post-management-instances.c @@ -0,0 +1,280 @@ +/* + This file is part of TALER + Copyright (C) 2020-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-management-instances.c + * @brief Implementation of the POST /instances request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> +#include <taler/taler_kyclogic_lib.h> + + +/** + * Handle for a POST /instances/$ID operation. + */ +struct TALER_MERCHANT_InstancesPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_InstancesPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP POST /instances request. + * + * @param cls the `struct TALER_MERCHANT_InstancesPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_instances_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_InstancesPostHandle *iph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + iph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /management/instances completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + iph->cb (iph->cb_cls, + &hr); + TALER_MERCHANT_instances_post_cancel (iph); +} + + +struct TALER_MERCHANT_InstancesPostHandle * +TALER_MERCHANT_instances_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const char *name, + const json_t *address, + const json_t *jurisdiction, + bool use_stefan, + struct GNUNET_TIME_Relative default_wire_transfer_delay, + struct GNUNET_TIME_Relative default_pay_delay, + struct GNUNET_TIME_Relative default_refund_delay, + const char *auth_password, + TALER_MERCHANT_InstancesPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_InstancesPostHandle *iph; + json_t *req_obj; + json_t *auth_obj; + + if (NULL != auth_password) + { + auth_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("method", + "token"), + GNUNET_JSON_pack_string ("password", + auth_password)); + } + else + { + auth_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("method", + "external")); + } + if (NULL == auth_obj) + { + GNUNET_break (0); + return NULL; + } + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("id", + instance_id), + GNUNET_JSON_pack_string ("name", + name), + GNUNET_JSON_pack_object_incref ("address", + (json_t *) address), + GNUNET_JSON_pack_object_incref ("jurisdiction", + (json_t *) jurisdiction), + GNUNET_JSON_pack_bool ("use_stefan", + use_stefan), + GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay", + default_wire_transfer_delay), + GNUNET_JSON_pack_time_rel ("default_pay_delay", + default_pay_delay), + GNUNET_JSON_pack_time_rel ("default_refund_delay", + default_refund_delay), + /* FIXME: add eventually to arguments when we transform the API... */ + GNUNET_JSON_pack_time_rounder_interval ( + "default_wire_transfer_rounding_interval", + GNUNET_TIME_RI_NONE), + GNUNET_JSON_pack_object_steal ("auth", + auth_obj)); + iph = GNUNET_new (struct TALER_MERCHANT_InstancesPostHandle); + iph->ctx = ctx; + iph->cb = cb; + iph->cb_cls = cb_cls; + iph->url = TALER_url_join (backend_url, + "management/instances", + NULL); + if (NULL == iph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (iph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (iph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&iph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (iph); + return NULL; + } + json_decref (req_obj); + iph->job = GNUNET_CURL_job_add2 (ctx, + eh, + iph->post_ctx.headers, + &handle_post_instances_finished, + iph); + } + return iph; +} + + +void +TALER_MERCHANT_instances_post_cancel ( + struct TALER_MERCHANT_InstancesPostHandle *iph) +{ + if (NULL != iph->job) + { + GNUNET_CURL_job_cancel (iph->job); + iph->job = NULL; + } + TALER_curl_easy_post_finished (&iph->post_ctx); + GNUNET_free (iph->url); + GNUNET_free (iph); +} + + +/* end of merchant_api_post_instances.c */ diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-abort.c b/src/lib/merchant_api_post-orders-ORDER_ID-abort.c @@ -0,0 +1,436 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-orders-ORDER_ID-abort.c + * @brief Implementation of the POST /orders/$ID/abort request + * of the merchant's HTTP API + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_exchange_service.h> +#include <taler/taler_curl_lib.h> + + +/** + * Maximum number of refunds we return. + */ +#define MAX_REFUNDS 1024 + + +/** + * @brief An abort Handle + */ +struct TALER_MERCHANT_OrderAbortHandle +{ + /** + * Hash of the contract. + */ + struct TALER_PrivateContractHashP h_contract_terms; + + /** + * Public key of the merchant. + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AbortCallback abort_cb; + + /** + * Closure for @a abort_cb. + */ + void *abort_cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * The coins we are aborting on. + */ + struct TALER_MERCHANT_AbortCoin *coins; + + /** + * Number of @e coins we are paying with. + */ + unsigned int num_coins; + +}; + + +/** + * Check that the response for an abort is well-formed, + * and call the application callback with the result if it is + * OK. Otherwise returns #GNUNET_SYSERR. + * + * @param oah handle to operation that created the reply + * @param[in] ar abort response, partially initialized + * @param json the reply to parse + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah, + struct TALER_MERCHANT_AbortResponse *ar, + const json_t *json) +{ + const json_t *refunds; + unsigned int num_refunds; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("refunds", + &refunds), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + num_refunds = (unsigned int) json_array_size (refunds); + if ( (json_array_size (refunds) != (size_t) num_refunds) || + (num_refunds > MAX_REFUNDS) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + { + struct TALER_MERCHANT_AbortedCoin res[GNUNET_NZL (num_refunds)]; + + for (unsigned int i = 0; i<num_refunds; i++) + { + json_t *refund = json_array_get (refunds, i); + uint32_t exchange_status; + struct GNUNET_JSON_Specification spec_es[] = { + GNUNET_JSON_spec_uint32 ("exchange_status", + &exchange_status), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (refund, + spec_es, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (MHD_HTTP_OK == exchange_status) + { + struct GNUNET_JSON_Specification spec_detail[] = { + GNUNET_JSON_spec_fixed_auto ("exchange_sig", + &res[i].exchange_sig), + GNUNET_JSON_spec_fixed_auto ("exchange_pub", + &res[i].exchange_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (refund, + spec_detail, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + TALER_exchange_online_refund_confirmation_verify ( + &oah->h_contract_terms, + &oah->coins[i].coin_pub, + &oah->merchant_pub, + 0, /* transaction id */ + &oah->coins[i].amount_with_fee, + &res[i].exchange_pub, + &res[i].exchange_sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + } + ar->details.ok.merchant_pub = &oah->merchant_pub; + ar->details.ok.num_aborts = num_refunds; + ar->details.ok.aborts = res; + oah->abort_cb (oah->abort_cb_cls, + ar); + oah->abort_cb = NULL; + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * abort request. + * + * @param cls the `struct TALER_MERCHANT_OrderAbortHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_abort_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderAbortHandle *oah = cls; + const json_t *json = response; + struct TALER_MERCHANT_AbortResponse ar = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + oah->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "/pay completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK == + check_abort_refund (oah, + &ar, + json)) + { + TALER_MERCHANT_order_abort_cancel (oah); + return; + } + ar.hr.http_status = 0; + ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_BAD_REQUEST: + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us or the + merchant is buggy (or API version conflict); just + pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_REQUEST_TIMEOUT: + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says one of + the signatures is invalid; as we checked them, + this should never happen, we should pass the JSON + reply to the application */ + break; + case MHD_HTTP_PRECONDITION_FAILED: + /* Our *payment* already succeeded fully. */ + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + ar.hr.ec = TALER_JSON_get_error_code (json); + ar.hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + case MHD_HTTP_BAD_GATEWAY: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &ar.hr); + /* Nothing really to verify, the merchant is blaming the exchange. + We should pass the JSON reply to the application */ + break; + default: + /* unexpected response code */ + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &ar.hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) ar.hr.ec); + GNUNET_break_op (0); + break; + } + oah->abort_cb (oah->abort_cb_cls, + &ar); + TALER_MERCHANT_order_abort_cancel (oah); +} + + +struct TALER_MERCHANT_OrderAbortHandle * +TALER_MERCHANT_order_abort ( + struct GNUNET_CURL_Context *ctx, + const char *merchant_url, + const char *order_id, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_PrivateContractHashP *h_contract, + unsigned int num_coins, + const struct TALER_MERCHANT_AbortCoin coins[static num_coins], + TALER_MERCHANT_AbortCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OrderAbortHandle *oah; + json_t *abort_obj; + json_t *j_coins; + + j_coins = json_array (); + if (NULL == j_coins) + { + GNUNET_break (0); + return NULL; + } + for (unsigned int i = 0; i<num_coins; i++) + { + const struct TALER_MERCHANT_AbortCoin *ac = &coins[i]; + json_t *j_coin; + + /* create JSON for this coin */ + j_coin = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("coin_pub", + &ac->coin_pub), + /* FIXME: no longer needed since **v18**, remove eventually! */ + TALER_JSON_pack_amount ("contribution", + &ac->amount_with_fee), + GNUNET_JSON_pack_string ("exchange_url", + ac->exchange_url)); + if (0 != + json_array_append_new (j_coins, + j_coin)) + { + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + } + abort_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("coins", + j_coins), + GNUNET_JSON_pack_data_auto ("h_contract", + h_contract)); + oah = GNUNET_new (struct TALER_MERCHANT_OrderAbortHandle); + oah->h_contract_terms = *h_contract; + oah->merchant_pub = *merchant_pub; + oah->ctx = ctx; + oah->abort_cb = cb; + oah->abort_cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "orders/%s/abort", + order_id); + oah->url = TALER_url_join (merchant_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == oah->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (abort_obj); + GNUNET_free (oah); + return NULL; + } + oah->num_coins = num_coins; + oah->coins = GNUNET_new_array (num_coins, + struct TALER_MERCHANT_AbortCoin); + GNUNET_memcpy (oah->coins, + coins, + num_coins * sizeof (struct TALER_MERCHANT_AbortCoin)); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (oah->url); + if (GNUNET_OK != + TALER_curl_easy_post (&oah->post_ctx, + eh, + abort_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (abort_obj); + GNUNET_free (oah); + return NULL; + } + json_decref (abort_obj); + oah->job = GNUNET_CURL_job_add2 (ctx, + eh, + oah->post_ctx.headers, + &handle_abort_finished, + oah); + } + return oah; +} + + +void +TALER_MERCHANT_order_abort_cancel ( + struct TALER_MERCHANT_OrderAbortHandle *oah) +{ + if (NULL != oah->job) + { + GNUNET_CURL_job_cancel (oah->job); + oah->job = NULL; + } + TALER_curl_easy_post_finished (&oah->post_ctx); + GNUNET_free (oah->coins); + GNUNET_free (oah->url); + GNUNET_free (oah); +} + + +/* end of merchant_api_post_order_abort.c */ diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-claim.c b/src/lib/merchant_api_post-orders-ORDER_ID-claim.c @@ -0,0 +1,242 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with TALER; see the file COPYING.LGPL. If not, + see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-orders-ORDER_ID-claim.c + * @brief Implementation of POST /orders/$ID/claim + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_curl_lib.h> + + +/** + * Structure representing a POST /orders/$ID/claim operation. + */ +struct TALER_MERCHANT_OrderClaimHandle +{ + /** + * Full URL, includes "/orders/$ID/claim". + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OrderClaimCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Function called when we're done processing the + * POST /orders/$ID/claim request. + * + * @param cls the `struct TALER_MERCHANT_OrderClaimHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, should be NULL + */ +static void +handle_post_order_claim_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderClaimHandle *och = cls; + const json_t *json = response; + struct TALER_MERCHANT_OrderClaimResponse ocr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_object_const ( + "contract_terms", + &ocr.details.ok.contract_terms), + GNUNET_JSON_spec_fixed_auto ( + "sig", + &ocr.details.ok.sig), + GNUNET_JSON_spec_end () + }; + + och->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Order claimed with status %u\n", + (unsigned int) response_code); + + if (MHD_HTTP_OK != response_code) + { + ocr.hr.ec = TALER_JSON_get_error_code (json); + ocr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Proposal lookup failed with HTTP status code %u/%d\n", + (unsigned int) response_code, + (int) ocr.hr.ec); + och->cb (och->cb_cls, + &ocr); + TALER_MERCHANT_order_claim_cancel (och); + return; + } + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Claiming order failed: could not parse JSON response\n"); + GNUNET_break_op (0); + ocr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + ocr.hr.http_status = 0; + och->cb (och->cb_cls, + &ocr); + TALER_MERCHANT_order_claim_cancel (och); + return; + } + + if (GNUNET_OK != + TALER_JSON_contract_hash (ocr.details.ok.contract_terms, + &ocr.details.ok.h_contract_terms)) + { + GNUNET_break (0); + ocr.hr.ec = TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE; + ocr.hr.http_status = 0; + GNUNET_JSON_parse_free (spec); + och->cb (och->cb_cls, + &ocr); + TALER_MERCHANT_order_claim_cancel (och); + return; + } + och->cb (och->cb_cls, + &ocr); + GNUNET_JSON_parse_free (spec); + TALER_MERCHANT_order_claim_cancel (och); +} + + +struct TALER_MERCHANT_OrderClaimHandle * +TALER_MERCHANT_order_claim (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *order_id, + const struct GNUNET_CRYPTO_EddsaPublicKey *nonce, + const struct TALER_ClaimTokenP *claim_token, + TALER_MERCHANT_OrderClaimCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OrderClaimHandle *och; + json_t *req_obj; + + if (NULL == order_id) + { + GNUNET_break (0); + return NULL; + } + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("nonce", + nonce), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_data_auto ("token", + claim_token))); + och = GNUNET_new (struct TALER_MERCHANT_OrderClaimHandle); + och->ctx = ctx; + och->cb = cb; + och->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "orders/%s/claim", + order_id); + och->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == och->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (och); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Claiming order at %s\n", + och->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (och->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&och->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + och->job = GNUNET_CURL_job_add2 (ctx, + eh, + och->post_ctx.headers, + &handle_post_order_claim_finished, + och); + GNUNET_assert (NULL != och->job); + } + return och; +} + + +void +TALER_MERCHANT_order_claim_cancel (struct TALER_MERCHANT_OrderClaimHandle *och) +{ + if (NULL != och->job) + { + GNUNET_CURL_job_cancel (och->job); + och->job = NULL; + } + TALER_curl_easy_post_finished (&och->post_ctx); + GNUNET_free (och->url); + GNUNET_free (och); +} + + +/* end of merchant_api_post_order_claim.c */ diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-paid.c b/src/lib/merchant_api_post-orders-ORDER_ID-paid.c @@ -0,0 +1,271 @@ +/* + This file is part of TALER + Copyright (C) 2020-2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-orders-ORDER_ID-paid.c + * @brief Implementation of the POST /order/$ID/paid request + * of the merchant's HTTP API + * @author Jonathan Buchanan + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_exchange_service.h> +#include <taler/taler_curl_lib.h> + + +/** + * @brief Handle to a POST /orders/$ID/paid operation at a merchant. + */ +struct TALER_MERCHANT_OrderPaidHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OrderPaidCallback paid_cb; + + /** + * Closure for @a paid_cb. + */ + void *paid_cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /paid request. + * + * @param cls the `struct TALER_MERCHANT_OrderPaidHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_paid_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderPaidHandle *oph = cls; + const json_t *json = response; + struct TALER_MERCHANT_OrderPaidResponse opr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + oph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "/paid completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + bool refunded; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_bool ("refunded", + &refunded), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (opr.hr.reply, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + break; + } + break; + case MHD_HTTP_BAD_REQUEST: + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); + /* The signature provided was invalid */ + break; + case MHD_HTTP_NOT_FOUND: + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); + /* The hashed contract terms don't match with the order_id. */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + opr.hr.ec = TALER_JSON_get_error_code (json); + opr.hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + case MHD_HTTP_SERVICE_UNAVAILABLE: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &opr.hr); + /* Exchange couldn't respond properly; the retry is + left to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &opr.hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) opr.hr.ec); + GNUNET_break_op (0); + break; + } + oph->paid_cb (oph->paid_cb_cls, + &opr); + TALER_MERCHANT_order_paid_cancel (oph); +} + + +struct TALER_MERCHANT_OrderPaidHandle * +TALER_MERCHANT_order_paid ( + struct GNUNET_CURL_Context *ctx, + const char *merchant_url, + const char *order_id, + const char *session_id, + const struct TALER_PrivateContractHashP *h_contract_terms, + const struct GNUNET_HashCode *wallet_data_hash, + const struct TALER_MerchantSignatureP *merchant_sig, + TALER_MERCHANT_OrderPaidCallback paid_cb, + void *paid_cb_cls) +{ + struct TALER_MERCHANT_OrderPaidHandle *oph; + json_t *req_obj; + + (void) wallet_data_hash; + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("sig", + merchant_sig), + GNUNET_JSON_pack_data_auto ("h_contract", + h_contract_terms), + GNUNET_JSON_pack_string ("session_id", + session_id)); + oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle); + oph->ctx = ctx; + oph->paid_cb = paid_cb; + oph->paid_cb_cls = paid_cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "orders/%s/paid", + order_id); + oph->url = TALER_url_join (merchant_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == oph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (oph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (oph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&oph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (oph); + return NULL; + } + json_decref (req_obj); + oph->job = GNUNET_CURL_job_add2 (ctx, + eh, + oph->post_ctx.headers, + &handle_paid_finished, + oph); + } + return oph; +} + + +void +TALER_MERCHANT_order_paid_cancel ( + struct TALER_MERCHANT_OrderPaidHandle *oph) +{ + if (NULL != oph->job) + { + GNUNET_CURL_job_cancel (oph->job); + oph->job = NULL; + } + TALER_curl_easy_post_finished (&oph->post_ctx); + GNUNET_free (oph->url); + GNUNET_free (oph); +} + + +/* end of merchant_api_post_order_paid.c */ diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-pay.c b/src/lib/merchant_api_post-orders-ORDER_ID-pay.c @@ -0,0 +1,790 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-orders-ORDER_ID-pay.c + * @brief Implementation of the POST /order/$ID/pay request + * of the merchant's HTTP API + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <stdio.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_exchange_service.h> +#include <taler/taler_curl_lib.h> + + +/** + * @brief A Pay Handle + */ +struct TALER_MERCHANT_OrderPayHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result in "pay" @e mode. + */ + TALER_MERCHANT_OrderPayCallback pay_cb; + + /** + * Closure for @a pay_cb. + */ + void *pay_cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * The coins we are paying with. + */ + struct TALER_MERCHANT_PaidCoin *coins; + + /** + * Hash of the contract we are paying, set + * if @e am_wallet is true. + */ + struct TALER_PrivateContractHashP h_contract_terms; + + /** + * Public key of the merchant (instance) being paid, set + * if @e am_wallet is true. + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * JSON with the full reply, used during async + * processing. + */ + json_t *full_reply; + + /** + * Pointer into @e coins array for the coin that + * created a conflict (that we are checking). + */ + const struct TALER_MERCHANT_PaidCoin *error_pc; + + /** + * Coin history that proves a conflict. + */ + json_t *error_history; + + /** + * Number of @e coins we are paying with. + */ + unsigned int num_coins; + + /** + * Set to true if this is the wallet API and we have + * initialized @e h_contract_terms and @e merchant_pub. + */ + bool am_wallet; + +}; + + +/** + * Parse blindly signed output tokens from response. + * + * @param token_sigs the JSON array with the token signatures. Can be NULL. + * @param tokens where to store the parsed tokens. + * @param num_tokens where to store the length of the @a tokens array. + */ +static enum GNUNET_GenericReturnValue +parse_tokens (const json_t *token_sigs, + struct TALER_MERCHANT_OutputToken **tokens, + unsigned int *num_tokens) +{ + GNUNET_array_grow (*tokens, + *num_tokens, + json_array_size (token_sigs)); + + for (unsigned int i = 0; i<(*num_tokens); i++) + { + struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i]; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_blinded_token_issue_sig ("blind_sig", + &token->blinded_sig), + GNUNET_JSON_spec_end () + }; + const json_t *jtoken + = json_array_get (token_sigs, + i); + + if (NULL == jtoken) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_JSON_parse (jtoken, + spec, + NULL, NULL)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + + return GNUNET_YES; +} + + +/** + * Function called when we're done processing the + * HTTP /pay request. + * + * @param cls the `struct TALER_MERCHANT_Pay` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_pay_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderPayHandle *oph = cls; + const json_t *json = response; + struct TALER_MERCHANT_PayResponse pr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received /pay response with status code %u\n", + (unsigned int) response_code); + + json_dumpf (json, + stderr, + JSON_INDENT (2)); + + oph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "/pay completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (oph->am_wallet) + { + const json_t *token_sigs = NULL; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("sig", + &pr.details.ok.merchant_sig), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("pos_confirmation", + &pr.details.ok.pos_confirmation), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("token_sigs", + &token_sigs), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "sig field missing in response"; + break; + } + + if (GNUNET_OK != + parse_tokens (token_sigs, + &pr.details.ok.tokens, + &pr.details.ok.num_tokens)) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "failed to parse token_sigs field in response"; + break; + } + + if (GNUNET_OK != + TALER_merchant_pay_verify (&oph->h_contract_terms, + &oph->merchant_pub, + &pr.details.ok.merchant_sig)) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "signature invalid"; + } + } + break; + /* Tolerating Not Acceptable because sometimes + * - especially in tests - we might want to POST + * coins one at a time. */ + case MHD_HTTP_NOT_ACCEPTABLE: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_BAD_REQUEST: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_PAYMENT_REQUIRED: + /* was originally paid, but then refunded */ + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_FORBIDDEN: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_REQUEST_TIMEOUT: + pr.hr.ec = TALER_JSON_get_error_code (json); + pr.hr.hint = TALER_JSON_get_error_hint (json); + /* The merchant couldn't generate a timely response, likely because + it itself waited too long on the exchange. + Pass on to application. */ + break; + case MHD_HTTP_CONFLICT: + TALER_MERCHANT_parse_error_details_ (json, + MHD_HTTP_CONFLICT, + &pr.hr); + break; + case MHD_HTTP_GONE: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* The merchant says we are too late, the offer has expired or some + denomination key of a coin involved has expired. + Might be a disagreement in timestamps? Still, pass on to application. */ + break; + case MHD_HTTP_PRECONDITION_FAILED: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* Nothing really to verify, the merchant is blaming us for failing to + satisfy some constraint (likely it does not like our exchange because + of some disagreement on the PKI). We should pass the JSON reply to the + application */ + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + { + json_t *ebus = json_object_get (json, + "exchange_base_urls"); + if (NULL == ebus) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "failed to parse exchange_base_urls field in response"; + break; + } + { + size_t alen = json_array_size (ebus); + const char *ebua[GNUNET_NZL (alen)]; + size_t idx; + json_t *jebu; + bool ok = true; + + GNUNET_assert (alen <= UINT_MAX); + json_array_foreach (ebus, idx, jebu) + { + ebua[idx] = json_string_value (jebu); + if (NULL == ebua[idx]) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "non-string value in exchange_base_urls in response"; + ok = false; + break; + } + } + if (! ok) + break; + pr.details.unavailable_for_legal_reasons.num_exchanges + = (unsigned int) alen; + pr.details.unavailable_for_legal_reasons.exchanges + = ebua; + oph->pay_cb (oph->pay_cb_cls, + &pr); + TALER_MERCHANT_order_pay_cancel (oph); + return; + } + } + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + case MHD_HTTP_BAD_GATEWAY: + /* Nothing really to verify, the merchant is blaming the exchange. + We should pass the JSON reply to the application */ + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + break; + case MHD_HTTP_SERVICE_UNAVAILABLE: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* Exchange couldn't respond properly; the retry is + left to the application */ + break; + case MHD_HTTP_GATEWAY_TIMEOUT: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* Exchange couldn't respond in a timely fashion; + the retry is left to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &pr.hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) pr.hr.ec); + GNUNET_break_op (0); + break; + } + oph->pay_cb (oph->pay_cb_cls, + &pr); + TALER_MERCHANT_order_pay_cancel (oph); +} + + +struct TALER_MERCHANT_OrderPayHandle * +TALER_MERCHANT_order_pay_frontend ( + struct GNUNET_CURL_Context *ctx, + const char *merchant_url, + const char *order_id, + const char *session_id, + const json_t *wallet_data, + unsigned int num_coins, + const struct TALER_MERCHANT_PaidCoin coins[static num_coins], + unsigned int num_tokens, + const struct TALER_MERCHANT_UsedToken *tokens, + json_t *j_output_tokens, // FIXME: not used, remove? + TALER_MERCHANT_OrderPayCallback pay_cb, + void *pay_cb_cls) +{ + struct TALER_MERCHANT_OrderPayHandle *oph; + json_t *pay_obj; + json_t *j_coins; + json_t *j_tokens = NULL; + CURL *eh; + struct TALER_Amount total_fee; + struct TALER_Amount total_amount; + + j_coins = json_array (); + GNUNET_assert (NULL != j_coins); + for (unsigned int i = 0; i<num_coins; i++) + { + json_t *j_coin; + const struct TALER_MERCHANT_PaidCoin *pc = &coins[i]; + struct TALER_Amount fee; + struct TALER_DenominationHashP denom_hash; + + if (0 > + TALER_amount_subtract (&fee, + &pc->amount_with_fee, + &pc->amount_without_fee)) + { + /* Integer underflow, fee larger than total amount? + This should not happen (client violated API!) */ + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + if (0 == i) + { + total_fee = fee; + total_amount = pc->amount_with_fee; + } + else + { + if ( (0 > + TALER_amount_add (&total_fee, + &total_fee, + &fee)) || + (0 > + TALER_amount_add (&total_amount, + &total_amount, + &pc->amount_with_fee)) ) + { + /* integer overflow */ + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + } + + TALER_denom_pub_hash (&pc->denom_pub, + &denom_hash); + /* create JSON for this coin */ + j_coin = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("contribution", + &pc->amount_with_fee), + GNUNET_JSON_pack_data_auto ("coin_pub", + &pc->coin_pub), + GNUNET_JSON_pack_string ("exchange_url", + pc->exchange_url), + GNUNET_JSON_pack_data_auto ("h_denom", + &denom_hash), + TALER_JSON_pack_denom_sig ("ub_sig", + &pc->denom_sig), + GNUNET_JSON_pack_data_auto ("coin_sig", + &pc->coin_sig)); + if (0 != + json_array_append_new (j_coins, + j_coin)) + { + GNUNET_break (0); + json_decref (j_coins); + return NULL; + } + } + + if (0 < num_tokens) + { + j_tokens = json_array (); + GNUNET_assert (NULL != j_tokens); + for (unsigned int i = 0; i<num_tokens; i++) + { + json_t *j_token; + const struct TALER_MERCHANT_UsedToken *ut = &tokens[i]; + + j_token = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("token_sig", + &ut->token_sig), + GNUNET_JSON_pack_data_auto ("token_pub", + &ut->token_pub), + GNUNET_JSON_pack_data_auto ("h_issue", + &ut->issue_pub.public_key->pub_key_hash), + TALER_JSON_pack_token_issue_sig ("ub_sig", + &ut->ub_sig)); + if (0 != + json_array_append_new (j_tokens, + j_token)) + { + GNUNET_break (0); + json_decref (j_tokens); + return NULL; + } + } + } + + pay_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("coins", + j_coins), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("tokens", + j_tokens)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("wallet_data", + (json_t *) wallet_data)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("session_id", + session_id))); + + json_dumpf (pay_obj, + stderr, + JSON_INDENT (2)); + + oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle); + oph->ctx = ctx; + oph->pay_cb = pay_cb; + oph->pay_cb_cls = pay_cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "orders/%s/pay", + order_id); + oph->url = TALER_url_join (merchant_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == oph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (pay_obj); + GNUNET_free (oph); + return NULL; + } + oph->num_coins = num_coins; + oph->coins = GNUNET_new_array (num_coins, + struct TALER_MERCHANT_PaidCoin); + GNUNET_memcpy (oph->coins, + coins, + num_coins * sizeof (struct TALER_MERCHANT_PaidCoin)); + + eh = TALER_MERCHANT_curl_easy_get_ (oph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&oph->post_ctx, + eh, + pay_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (pay_obj); + GNUNET_free (oph->url); + GNUNET_free (oph); + return NULL; + } + json_decref (pay_obj); + oph->job = GNUNET_CURL_job_add2 (ctx, + eh, + oph->post_ctx.headers, + &handle_pay_finished, + oph); + return oph; +} + + +struct TALER_MERCHANT_OrderPayHandle * +TALER_MERCHANT_order_pay ( + struct GNUNET_CURL_Context *ctx, + const char *merchant_url, + const char *session_id, + const struct TALER_PrivateContractHashP *h_contract_terms, + int choice_index, + const struct TALER_Amount *amount, + const struct TALER_Amount *max_fee, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_MerchantSignatureP *merchant_sig, + struct GNUNET_TIME_Timestamp timestamp, + struct GNUNET_TIME_Timestamp refund_deadline, + struct GNUNET_TIME_Timestamp pay_deadline, + const struct TALER_MerchantWireHashP *h_wire, + const char *order_id, + unsigned int num_coins, + const struct TALER_MERCHANT_PayCoin coins[static num_coins], + unsigned int num_tokens, + const struct TALER_MERCHANT_UseToken *tokens, + unsigned int num_output_tokens, + const struct TALER_MERCHANT_OutputToken *output_tokens, + TALER_MERCHANT_OrderPayCallback pay_cb, + void *pay_cb_cls) +{ + json_t *j_output_tokens = NULL; + const json_t *wallet_data = NULL; + struct GNUNET_HashCode wallet_data_hash; + + if (GNUNET_YES != + TALER_amount_cmp_currency (amount, + max_fee)) + { + GNUNET_break (0); + return NULL; + } + if ((0 < num_tokens || 0 < num_output_tokens) && 0 > choice_index) + { + /* Tokens (input or output) require a valid choice_index to be set. + Only contracts with coices can use or issue tokens. */ + GNUNET_break (0); + return NULL; + } + if (0 < num_output_tokens) + { + /* Build token envelopes json array. */ + j_output_tokens = json_array (); + GNUNET_assert (NULL != j_output_tokens); + for (unsigned int i = 0; i<num_output_tokens; i++) + { + json_t *j_token_ev; + const struct TALER_MERCHANT_OutputToken *ev = &output_tokens[i]; + + j_token_ev = GNUNET_JSON_PACK ( + TALER_JSON_pack_token_envelope (NULL, + &ev->envelope)); + + if (0 != + json_array_append_new (j_output_tokens, + j_token_ev)) + { + GNUNET_break (0); + json_decref (j_output_tokens); + return NULL; + } + } + } + if (0 <= choice_index) + { + wallet_data = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_int64 ("choice_index", + choice_index), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_incref ("tokens_evs", + j_output_tokens))); + TALER_json_hash (wallet_data, + &wallet_data_hash); + } + { + struct TALER_MERCHANT_PaidCoin pc[num_coins]; + struct TALER_MERCHANT_UsedToken ut[num_tokens]; + + for (unsigned int i = 0; i<num_coins; i++) + { + const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv. + struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub. + struct TALER_Amount fee; + struct TALER_DenominationHashP h_denom_pub; + + if (0 > + TALER_amount_subtract (&fee, + &coin->amount_with_fee, + &coin->amount_without_fee)) + { + /* Integer underflow, fee larger than total amount? + This should not happen (client violated API!) */ + GNUNET_break (0); + return NULL; + } + TALER_denom_pub_hash (&coin->denom_pub, + &h_denom_pub); + TALER_wallet_deposit_sign (&coin->amount_with_fee, + &fee, + h_wire, + h_contract_terms, + (NULL != wallet_data) + ? &wallet_data_hash + : NULL, + coin->h_age_commitment, + NULL /* h_extensions! */, + &h_denom_pub, + timestamp, + merchant_pub, + refund_deadline, + &coin->coin_priv, + &p->coin_sig); + p->denom_pub = coin->denom_pub; + p->denom_sig = coin->denom_sig; + p->denom_value = coin->denom_value; + GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv, + &p->coin_pub.eddsa_pub); + p->amount_with_fee = coin->amount_with_fee; + p->amount_without_fee = coin->amount_without_fee; + p->exchange_url = coin->exchange_url; + } + for (unsigned int i = 0; i<num_tokens; i++) + { + const struct TALER_MERCHANT_UseToken *token = &tokens[i]; + struct TALER_MERCHANT_UsedToken *t = &ut[i]; + + TALER_wallet_token_use_sign (h_contract_terms, + &wallet_data_hash, // checked for != NULL above + &token->token_priv, + &t->token_sig); + t->ub_sig = token->ub_sig; + t->issue_pub = token->issue_pub; + GNUNET_CRYPTO_eddsa_key_get_public (&token->token_priv.private_key, + &t->token_pub.public_key); + } + { + struct TALER_MERCHANT_OrderPayHandle *oph; + + oph = TALER_MERCHANT_order_pay_frontend (ctx, + merchant_url, + order_id, + session_id, + wallet_data, + num_coins, + pc, + num_tokens, + ut, + j_output_tokens, + pay_cb, + pay_cb_cls); + if (NULL == oph) + return NULL; + oph->h_contract_terms = *h_contract_terms; + oph->merchant_pub = *merchant_pub; + oph->am_wallet = true; + return oph; + } + } +} + + +void +TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph) +{ + if (NULL != oph->job) + { + GNUNET_CURL_job_cancel (oph->job); + oph->job = NULL; + } + TALER_curl_easy_post_finished (&oph->post_ctx); + json_decref (oph->error_history); + json_decref (oph->full_reply); + GNUNET_free (oph->coins); + GNUNET_free (oph->url); + GNUNET_free (oph); +} + + +/* end of merchant_api_post_order_pay.c */ diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-refund.c b/src/lib/merchant_api_post-orders-ORDER_ID-refund.c @@ -0,0 +1,345 @@ +/* + This file is part of TALER + Copyright (C) 2020-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-orders-ORDER_ID-refund.c + * @brief Implementation of the (public) POST /orders/ID/refund request + * @author Jonathan Buchanan + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_curl_lib.h> + +/** + * Maximum number of refunds we return. + */ +#define MAX_REFUNDS 1024 + +/** + * Handle for a (public) POST /orders/ID/refund operation. + */ +struct TALER_MERCHANT_WalletOrderRefundHandle +{ + /** + * Complete URL where the backend offers /refund + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * The CURL context to connect to the backend + */ + struct GNUNET_CURL_Context *ctx; + + /** + * The callback to pass the backend response to + */ + TALER_MERCHANT_WalletRefundCallback cb; + + /** + * Clasure to pass to the callback + */ + void *cb_cls; + + /** + * Handle for the request + */ + struct GNUNET_CURL_Job *job; +}; + + +/** + * Callback to process (public) POST /orders/ID/refund response + * + * @param cls the `struct TALER_MERCHANT_OrderRefundHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not JSON + */ +static void +handle_refund_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WalletOrderRefundHandle *orh = cls; + const json_t *json = response; + struct TALER_MERCHANT_WalletRefundResponse wrr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + orh->job = NULL; + switch (response_code) + { + case 0: + wrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + const json_t *refunds; + unsigned int refund_len; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount_any ( + "refund_amount", + &wrr.details.ok.refund_amount), + GNUNET_JSON_spec_array_const ( + "refunds", + &refunds), + GNUNET_JSON_spec_fixed_auto ( + "merchant_pub", + &wrr.details.ok.merchant_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + break; + } + refund_len = json_array_size (refunds); + if ( (json_array_size (refunds) != (size_t) refund_len) || + (refund_len > MAX_REFUNDS) ) + { + GNUNET_break (0); + wrr.hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; + wrr.hr.http_status = 0; + break; + } + { + struct TALER_MERCHANT_RefundDetail rds[GNUNET_NZL (refund_len)]; + + memset (rds, + 0, + sizeof (rds)); + for (unsigned int i = 0; i<refund_len; i++) + { + struct TALER_MERCHANT_RefundDetail *rd = &rds[i]; + const json_t *jrefund = json_array_get (refunds, + i); + const char *refund_status_type; + uint32_t exchange_status; + uint32_t eec = 0; + struct GNUNET_JSON_Specification espec[] = { + GNUNET_JSON_spec_string ("type", + &refund_status_type), + GNUNET_JSON_spec_uint32 ("exchange_status", + &exchange_status), + GNUNET_JSON_spec_uint64 ("rtransaction_id", + &rd->rtransaction_id), + GNUNET_JSON_spec_fixed_auto ("coin_pub", + &rd->coin_pub), + TALER_JSON_spec_amount_any ("refund_amount", + &rd->refund_amount), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("exchange_reply", + &rd->hr.reply), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("exchange_code", + &eec), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (jrefund, + espec, + NULL, NULL)) + { + GNUNET_break_op (0); + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + goto finish; + } + + rd->hr.http_status = exchange_status; + rd->hr.ec = (enum TALER_ErrorCode) eec; + switch (exchange_status) + { + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification rspec[] = { + GNUNET_JSON_spec_fixed_auto ("exchange_sig", + &rd->details.ok.exchange_sig), + GNUNET_JSON_spec_fixed_auto ("exchange_pub", + &rd->details.ok.exchange_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (jrefund, + rspec, + NULL, + NULL)) + { + GNUNET_break_op (0); + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + goto finish; + } + /* check that type field is correct */ + if (0 != strcmp ("success", + refund_status_type)) + { + GNUNET_break_op (0); + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + goto finish; + } + } + break; /* end MHD_HTTP_OK */ + default: + if (0 != strcmp ("failure", + refund_status_type)) + { + GNUNET_break_op (0); + wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + wrr.hr.http_status = 0; + goto finish; + } + } /* switch on exchange status code */ + } /* for all refunds */ + + wrr.details.ok.refunds = rds; + wrr.details.ok.refunds_length = refund_len; + orh->cb (orh->cb_cls, + &wrr); + TALER_MERCHANT_wallet_post_order_refund_cancel (orh); + return; + } /* end 'rds' scope */ + } /* case MHD_HTTP_OK */ + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_CONFLICT: + case MHD_HTTP_NOT_FOUND: + wrr.hr.ec = TALER_JSON_get_error_code (json); + wrr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + GNUNET_break_op (0); /* unexpected status code */ + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &wrr.hr); + break; + } +finish: + orh->cb (orh->cb_cls, + &wrr); + TALER_MERCHANT_wallet_post_order_refund_cancel (orh); +} + + +struct TALER_MERCHANT_WalletOrderRefundHandle * +TALER_MERCHANT_wallet_post_order_refund ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *order_id, + const struct TALER_PrivateContractHashP *h_contract_terms, + TALER_MERCHANT_WalletRefundCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WalletOrderRefundHandle *orh; + json_t *req; + CURL *eh; + + orh = GNUNET_new (struct TALER_MERCHANT_WalletOrderRefundHandle); + orh->ctx = ctx; + orh->cb = cb; + orh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "orders/%s/refund", + order_id); + orh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == orh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (orh); + return NULL; + } + req = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("h_contract", + h_contract_terms)); + eh = TALER_MERCHANT_curl_easy_get_ (orh->url); + if (GNUNET_OK != + TALER_curl_easy_post (&orh->post_ctx, + eh, + req)) + { + GNUNET_break (0); + json_decref (req); + curl_easy_cleanup (eh); + GNUNET_free (orh->url); + GNUNET_free (orh); + return NULL; + } + json_decref (req); + orh->job = GNUNET_CURL_job_add2 (ctx, + eh, + orh->post_ctx.headers, + &handle_refund_finished, + orh); + if (NULL == orh->job) + { + GNUNET_free (orh->url); + GNUNET_free (orh); + return NULL; + } + return orh; +} + + +void +TALER_MERCHANT_wallet_post_order_refund_cancel ( + struct TALER_MERCHANT_WalletOrderRefundHandle *orh) +{ + if (NULL != orh->job) + { + GNUNET_CURL_job_cancel (orh->job); + orh->job = NULL; + } + TALER_curl_easy_post_finished (&orh->post_ctx); + GNUNET_free (orh->url); + GNUNET_free (orh); +} + + +/* end of merchant_api_wallet_post_order_refund.c */ diff --git a/src/lib/merchant_api_post-private-accounts.c b/src/lib/merchant_api_post-private-accounts.c @@ -0,0 +1,250 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-accounts.c + * @brief Implementation of the POST /account request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /private/accounts operation. + */ +struct TALER_MERCHANT_AccountsPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountsPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP POST /account request. + * + * @param cls the `struct TALER_MERCHANT_AccountPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountsPostHandle *aph = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountsPostResponse apr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + aph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /accounts completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_wire", + &apr.details.ok.h_wire), + GNUNET_JSON_spec_fixed_auto ("salt", + &apr.details.ok.salt), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + apr.hr.http_status = 0; + apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + } + break; + case MHD_HTTP_BAD_REQUEST: + GNUNET_break_op (0); + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &apr.hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) apr.hr.ec); + GNUNET_break_op (0); + break; + } + aph->cb (aph->cb_cls, + &apr); + TALER_MERCHANT_accounts_post_cancel (aph); +} + + +struct TALER_MERCHANT_AccountsPostHandle * +TALER_MERCHANT_accounts_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + struct TALER_FullPayto payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + TALER_MERCHANT_AccountsPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountsPostHandle *aph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + TALER_JSON_pack_full_payto ( + "payto_uri", + payto_uri), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ( + "credit_facade_url", + credit_facade_url)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ( + "credit_facade_credentials", + (json_t *) credit_facade_credentials)) + ); + aph = GNUNET_new (struct TALER_MERCHANT_AccountsPostHandle); + aph->ctx = ctx; + aph->cb = cb; + aph->cb_cls = cb_cls; + aph->url = TALER_url_join (backend_url, + "private/accounts", + NULL); + if (NULL == aph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (aph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (aph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&aph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + aph->job = GNUNET_CURL_job_add2 (ctx, + eh, + aph->post_ctx.headers, + &handle_post_account_finished, + aph); + GNUNET_assert (NULL != aph->job); + } + return aph; +} + + +void +TALER_MERCHANT_accounts_post_cancel ( + struct TALER_MERCHANT_AccountsPostHandle *aph) +{ + if (NULL != aph->job) + { + GNUNET_CURL_job_cancel (aph->job); + aph->job = NULL; + } + TALER_curl_easy_post_finished (&aph->post_ctx); + GNUNET_free (aph->url); + GNUNET_free (aph); +} + + +/* end of merchant_api_post_account.c */ diff --git a/src/lib/merchant_api_post-private-categories.c b/src/lib/merchant_api_post-private-categories.c @@ -0,0 +1,223 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-categories.c + * @brief Implementation of POST /private/categories + * @author Bohdan Potuzhnyi + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + +/** + * Handle for a POST /private/categories operation. + */ +struct TALER_MERCHANT_CategoriesPostHandle +{ + /** + * Fully qualified request URL. + */ + char *url; + + /** + * CURL job handle. + */ + struct GNUNET_CURL_Job *job; + + /** + * Callback to invoke with the result. + */ + TALER_MERCHANT_CategoriesPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Helper keeping POST body and headers alive. + */ + struct TALER_CURL_PostContext post_ctx; +}; + +/** + * Called when the HTTP transfer finishes. + * + * @param cls operation handle + * @param response_code HTTP status (0 on network / parsing failures) + * @param response parsed JSON reply (NULL if unavailable) + */ +static void +handle_post_categories_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_CategoriesPostHandle *cph = cls; + const json_t *json = response; + struct TALER_MERCHANT_CategoriesPostResponse cpr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + cph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /private/categories completed with status %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const char *err_name; + unsigned int err_line; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint64 ("category_id", + &cpr.category_id), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + &err_name, + &err_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid response for field %s\n", + err_name); + cpr.hr.http_status = 0; + cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + } + break; + } + case MHD_HTTP_BAD_REQUEST: + case MHD_HTTP_UNAUTHORIZED: + case MHD_HTTP_FORBIDDEN: + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_CONFLICT: + case MHD_HTTP_INTERNAL_SERVER_ERROR: + cpr.hr.ec = TALER_JSON_get_error_code (json); + cpr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case 0: + cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &cpr.hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response %u/%d for POST /private/categories\n", + (unsigned int) response_code, + (int) cpr.hr.ec); + GNUNET_break_op (0); + break; + } + cph->cb (cph->cb_cls, + &cpr); + TALER_MERCHANT_categories_post_cancel (cph); +} + + +struct TALER_MERCHANT_CategoriesPostHandle * +TALER_MERCHANT_categories_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *name, + const json_t *name_i18n, + TALER_MERCHANT_CategoriesPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_CategoriesPostHandle *cph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("name", + name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("name_i18n", + (json_t *) name_i18n))); + cph = GNUNET_new (struct TALER_MERCHANT_CategoriesPostHandle); + cph->ctx = ctx; + cph->cb = cb; + cph->cb_cls = cb_cls; + cph->url = TALER_url_join (backend_url, + "private/categories", + NULL); + if (NULL == cph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to build /private/categories URL\n"); + json_decref (req_obj); + GNUNET_free (cph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (cph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&cph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (cph->url); + GNUNET_free (cph); + return NULL; + } + json_decref (req_obj); + cph->job = GNUNET_CURL_job_add2 (ctx, + eh, + cph->post_ctx.headers, + &handle_post_categories_finished, + cph); + } + return cph; +} + + +void +TALER_MERCHANT_categories_post_cancel ( + struct TALER_MERCHANT_CategoriesPostHandle *cph) +{ + if (NULL != cph->job) + { + GNUNET_CURL_job_cancel (cph->job); + cph->job = NULL; + } + TALER_curl_easy_post_finished (&cph->post_ctx); + GNUNET_free (cph->url); + GNUNET_free (cph); +} + + +/* end of merchant_api_post_categories.c */ diff --git a/src/lib/merchant_api_post-private-donau.c b/src/lib/merchant_api_post-private-donau.c @@ -0,0 +1,237 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-donau.c + * @brief Implementation of the POST /donau request + * of the merchant's HTTP API + * @author Bohdan Potuzhnyi + * @author Vlada Svirsh + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_curl_lib.h> +#include <taler/taler_kyclogic_lib.h> +/* DONAU RELATED IMPORTS */ +#include "taler/taler_merchant_donau.h" +#include <donau/donau_service.h> + + +/** + * Handle for a POST /donau operation. + */ +struct TALER_MERCHANT_DonauInstancePostHandle +{ + /** + * URL for this request. + */ + char *url; + + /** + * Handle for the CURL job. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_DonauInstancePostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Function called when the POST /donau operation is finished. + * + * @param cls the `struct TALER_MERCHANT_DonauInstancePostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_donau_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_DonauInstancePostHandle *dph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + dph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /donau completed with response code %u\n", + (unsigned int) response_code); + + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + case MHD_HTTP_INTERNAL_SERVER_ERROR: + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + + dph->cb (dph->cb_cls, + &hr); + TALER_MERCHANT_donau_instances_post_cancel (dph); +} + + +struct TALER_MERCHANT_DonauInstancePostHandle * +TALER_MERCHANT_donau_instances_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MERCHANT_Charity *charity, + const char *auth_token, + TALER_MERCHANT_DonauInstancePostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_DonauInstancePostHandle *dph; + json_t *req_obj; + json_t *auth_obj; + + if (NULL != auth_token) + { + if (0 != strncasecmp (RFC_8959_PREFIX, + auth_token, + strlen (RFC_8959_PREFIX))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Authentication token must start with `%s'\n", + RFC_8959_PREFIX); + return NULL; + } + auth_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("method", + "token"), + GNUNET_JSON_pack_string ("token", + auth_token)); + } + else + { + auth_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("method", + "external")); + } + if (NULL == auth_obj) + { + GNUNET_break (0); + return NULL; + } + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("donau_url", + charity->charity_url), + GNUNET_JSON_pack_uint64 ("charity_id", + charity->charity_id) + ); + + dph = GNUNET_new (struct TALER_MERCHANT_DonauInstancePostHandle); + dph->ctx = ctx; + dph->cb = cb; + dph->cb_cls = cb_cls; + dph->url = TALER_url_join (backend_url, + "private/donau", + NULL); + + if (NULL == dph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to construct request URL\n"); + json_decref (req_obj); + GNUNET_free (dph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (dph->url); + if (GNUNET_OK != TALER_curl_easy_post (&dph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (dph); + return NULL; + } + + json_decref (req_obj); + + dph->job = GNUNET_CURL_job_add2 (ctx, + eh, + dph->post_ctx.headers, + &handle_post_donau_finished, + dph); + } + return dph; +} + + +void +TALER_MERCHANT_donau_instances_post_cancel ( + struct TALER_MERCHANT_DonauInstancePostHandle *dph) +{ + if (NULL != dph->job) + { + GNUNET_CURL_job_cancel (dph->job); + dph->job = NULL; + } + TALER_curl_easy_post_finished (&dph->post_ctx); + GNUNET_free (dph->url); + GNUNET_free (dph); +} + + +/* end of merchant_api_post_donau_instance.c */ diff --git a/src/lib/merchant_api_post-private-orders-ORDER_ID-refund.c b/src/lib/merchant_api_post-private-orders-ORDER_ID-refund.c @@ -0,0 +1,246 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-orders-ORDER_ID-refund.c + * @brief Implementation of the POST /orders/ID/refund request + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /orders/ID/refund operation. + */ +struct TALER_MERCHANT_OrderRefundHandle +{ + /** + * Complete URL where the backend offers /refund + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * The CURL context to connect to the backend + */ + struct GNUNET_CURL_Context *ctx; + + /** + * The callback to pass the backend response to + */ + TALER_MERCHANT_RefundCallback cb; + + /** + * Clasure to pass to the callback + */ + void *cb_cls; + + /** + * Handle for the request + */ + struct GNUNET_CURL_Job *job; +}; + + +/** + * Callback to process POST /orders/ID/refund response + * + * @param cls the `struct TALER_MERCHANT_OrderRefundHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not JSON + */ +static void +handle_refund_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OrderRefundHandle *orh = cls; + const json_t *json = response; + struct TALER_MERCHANT_RefundResponse rr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + orh->job = NULL; + switch (response_code) + { + case 0: + rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ( + "taler_refund_uri", + &rr.details.ok.taler_refund_uri), + GNUNET_JSON_spec_fixed_auto ( + "h_contract", + &rr.details.ok.h_contract), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + rr.hr.http_status = 0; + rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + break; + } + case MHD_HTTP_UNAUTHORIZED: + rr.hr.ec = TALER_JSON_get_error_code (json); + rr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + rr.hr.ec = TALER_JSON_get_error_code (json); + rr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + /* order unknown (or not paid) */ + rr.hr.ec = TALER_JSON_get_error_code (json); + rr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + /* amount not acceptable */ + rr.hr.ec = TALER_JSON_get_error_code (json); + rr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_GONE: + /* too late, wire deadline is past */ + rr.hr.ec = TALER_JSON_get_error_code (json); + rr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + GNUNET_break_op (0); /* unexpected status code */ + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &rr.hr); + break; + } + orh->cb (orh->cb_cls, + &rr); + TALER_MERCHANT_post_order_refund_cancel (orh); +} + + +void +TALER_MERCHANT_post_order_refund_cancel ( + struct TALER_MERCHANT_OrderRefundHandle *orh) +{ + if (NULL != orh->job) + { + GNUNET_CURL_job_cancel (orh->job); + orh->job = NULL; + } + TALER_curl_easy_post_finished (&orh->post_ctx); + GNUNET_free (orh->url); + GNUNET_free (orh); +} + + +struct TALER_MERCHANT_OrderRefundHandle * +TALER_MERCHANT_post_order_refund (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *order_id, + const struct TALER_Amount *refund, + const char *reason, + TALER_MERCHANT_RefundCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OrderRefundHandle *orh; + json_t *req; + CURL *eh; + + orh = GNUNET_new (struct TALER_MERCHANT_OrderRefundHandle); + orh->ctx = ctx; + orh->cb = cb; + orh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/orders/%s/refund", + order_id); + orh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == orh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (orh); + return NULL; + } + req = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("refund", + refund), + GNUNET_JSON_pack_string ("reason", + reason)); + GNUNET_assert (NULL != req); + eh = TALER_MERCHANT_curl_easy_get_ (orh->url); + if (GNUNET_OK != + TALER_curl_easy_post (&orh->post_ctx, + eh, + req)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req); + GNUNET_free (orh->url); + GNUNET_free (orh); + return NULL; + } + json_decref (req); + orh->job = GNUNET_CURL_job_add2 (ctx, + eh, + orh->post_ctx.headers, + &handle_refund_finished, + orh); + if (NULL == orh->job) + { + GNUNET_free (orh->url); + GNUNET_free (orh); + return NULL; + } + return orh; +} + + +/* end of merchant_api_post_order_refund.c */ diff --git a/src/lib/merchant_api_post-private-orders.c b/src/lib/merchant_api_post-private-orders.c @@ -0,0 +1,297 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with TALER; see the file COPYING.LGPL. If not, + see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-orders.c + * @brief Implementation of the POST /orders + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_curl_lib.h> + + +/** + * @brief A POST /orders Handle + */ +struct TALER_MERCHANT_PostOrdersHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_PostOrdersCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /orders request. + * + * @param cls the `struct TALER_MERCHANT_PostOrdersHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not JSON + */ +static void +handle_post_order_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_PostOrdersHandle *po = cls; + const json_t *json = response; + + po->job = NULL; + TALER_MERCHANT_handle_order_creation_response_ (po->cb, + po->cb_cls, + response_code, + json); + TALER_MERCHANT_orders_post_cancel (po); +} + + +struct TALER_MERCHANT_PostOrdersHandle * +TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const json_t *order, + struct GNUNET_TIME_Relative refund_delay, + TALER_MERCHANT_PostOrdersCallback cb, + void *cb_cls) +{ + static const char *no_uuids[GNUNET_NZL (0)]; + + return TALER_MERCHANT_orders_post2 (ctx, + backend_url, + order, + refund_delay, + NULL, + 0, + NULL, + 0, + no_uuids, + true, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_PostOrdersHandle * +TALER_MERCHANT_orders_post2 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const json_t *order, + struct GNUNET_TIME_Relative refund_delay, + const char *payment_target, + unsigned int inventory_products_length, + const struct TALER_MERCHANT_InventoryProduct inventory_products[], + unsigned int uuids_length, + const char *uuids[], + bool create_token, + TALER_MERCHANT_PostOrdersCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_orders_post3 ( + ctx, + backend_url, + order, + NULL, /* session ID */ + refund_delay, + payment_target, + inventory_products_length, + inventory_products, + uuids_length, + uuids, + create_token, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_PostOrdersHandle * +TALER_MERCHANT_orders_post3 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const json_t *order, + const char *session_id, + struct GNUNET_TIME_Relative refund_delay, + const char *payment_target, + unsigned int inventory_products_length, + const struct TALER_MERCHANT_InventoryProduct inventory_products[], + unsigned int uuids_length, + const char *uuids[], + bool create_token, + TALER_MERCHANT_PostOrdersCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_PostOrdersHandle *po; + json_t *req; + CURL *eh; + + po = GNUNET_new (struct TALER_MERCHANT_PostOrdersHandle); + po->ctx = ctx; + po->cb = cb; + po->cb_cls = cb_cls; + po->url = TALER_url_join (backend_url, + "private/orders", + NULL); + req = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_object_incref ("order", + (json_t *) order), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("session_id", + session_id)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("payment_target", + payment_target))); + if (0 != refund_delay.rel_value_us) + { + GNUNET_assert (0 == + json_object_set_new (req, + "refund_delay", + GNUNET_JSON_from_time_rel ( + refund_delay))); + } + if (0 != inventory_products_length) + { + json_t *ipa = json_array (); + + GNUNET_assert (NULL != ipa); + for (unsigned int i = 0; i<inventory_products_length; i++) + { + json_t *ip; + char unit_quantity_buf[64]; + + TALER_MERCHANT_format_quantity_string (inventory_products[i].quantity, + inventory_products[i].quantity_frac + , + unit_quantity_buf, + sizeof (unit_quantity_buf)); + + ip = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("product_id", + inventory_products[i].product_id), + GNUNET_JSON_pack_string ("unit_quantity", + unit_quantity_buf)); + GNUNET_assert (NULL != ip); + GNUNET_assert (0 == + json_array_append_new (ipa, + ip)); + } + GNUNET_assert (0 == + json_object_set_new (req, + "inventory_products", + ipa)); + } + if (0 != uuids_length) + { + json_t *ua = json_array (); + + GNUNET_assert (NULL != ua); + for (unsigned int i = 0; i<uuids_length; i++) + { + json_t *u; + + u = json_string (uuids[i]); + GNUNET_assert (0 == + json_array_append_new (ua, + u)); + } + GNUNET_assert (0 == + json_object_set_new (req, + "lock_uuids", + ua)); + } + if (! create_token) + { + GNUNET_assert (0 == + json_object_set_new (req, + "create_token", + json_boolean (create_token))); + } + eh = TALER_MERCHANT_curl_easy_get_ (po->url); + if (GNUNET_OK != + TALER_curl_easy_post (&po->post_ctx, + eh, + req)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req); + GNUNET_free (po); + return NULL; + } + json_decref (req); + po->job = GNUNET_CURL_job_add2 (ctx, + eh, + po->post_ctx.headers, + &handle_post_order_finished, + po); + return po; +} + + +void +TALER_MERCHANT_orders_post_cancel ( + struct TALER_MERCHANT_PostOrdersHandle *po) +{ + if (NULL != po->job) + { + GNUNET_CURL_job_cancel (po->job); + po->job = NULL; + } + GNUNET_free (po->url); + TALER_curl_easy_post_finished (&po->post_ctx); + GNUNET_free (po); +} + + +/* end of merchant_api_post_orders.c */ diff --git a/src/lib/merchant_api_post-private-otp-devices.c b/src/lib/merchant_api_post-private-otp-devices.c @@ -0,0 +1,237 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-otp-devices.c + * @brief Implementation of the POST /otp-devices request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDevicesPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicesPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /otp-devices request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicesPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_otp_devices_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicesPostHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /otp-devices completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_otp_devices_post_cancel (tph); +} + + +struct TALER_MERCHANT_OtpDevicesPostHandle * +TALER_MERCHANT_otp_devices_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + const char *otp_device_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm otp_algorithm, + uint64_t otp_ctr, + TALER_MERCHANT_OtpDevicesPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicesPostHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("otp_device_id", + otp_device_id), + GNUNET_JSON_pack_string ("otp_device_description", + otp_device_description), + GNUNET_JSON_pack_uint64 ("otp_algorithm", + (uint32_t) otp_algorithm), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_key", + otp_key)), + GNUNET_JSON_pack_uint64 ("otp_ctr", + otp_ctr)); + tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicesPostHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + tph->url = TALER_url_join (backend_url, + "private/otp-devices", + NULL); + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_post_otp_devices_finished, + tph); + GNUNET_assert (NULL != tph->job); + } + return tph; +} + + +void +TALER_MERCHANT_otp_devices_post_cancel ( + struct TALER_MERCHANT_OtpDevicesPostHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_post_otp_devices.c */ diff --git a/src/lib/merchant_api_post-private-products-PRODUCT_ID-lock.c b/src/lib/merchant_api_post-private-products-PRODUCT_ID-lock.c @@ -0,0 +1,279 @@ +/* + This file is part of TALER + Copyright (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-products-PRODUCT_ID-lock.c + * @brief Implementation of the POST /products/$ID/lock request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /products/$ID/lock operation. + */ +struct TALER_MERCHANT_ProductLockHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ProductLockCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP POST /products/$ID/lock request. + * + * @param cls the `struct TALER_MERCHANT_ProductLockHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_lock_product_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_ProductLockHandle *plh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + plh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "LOCK /products/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_GONE: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + plh->cb (plh->cb_cls, + &hr); + TALER_MERCHANT_product_lock_cancel (plh); +} + + +struct TALER_MERCHANT_ProductLockHandle * +TALER_MERCHANT_product_lock2 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *uuid, + struct GNUNET_TIME_Relative duration, + uint64_t quantity, + uint32_t quantity_frac, + bool use_fractional_quantity, + TALER_MERCHANT_ProductLockCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_ProductLockHandle *plh; + json_t *req_obj; + char unit_quantity_buf[64]; + + TALER_MERCHANT_format_quantity_string (quantity, + quantity_frac, + unit_quantity_buf, + sizeof (unit_quantity_buf)); + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("lock_uuid", + uuid), + GNUNET_JSON_pack_time_rel ("duration", + duration), + GNUNET_JSON_pack_string ("unit_quantity", + unit_quantity_buf)); + (void) use_fractional_quantity; + GNUNET_assert ( (0 == quantity_frac) || use_fractional_quantity); + plh = GNUNET_new (struct TALER_MERCHANT_ProductLockHandle); + plh->ctx = ctx; + plh->cb = cb; + plh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/products/%s/lock", + product_id); + plh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == plh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (plh); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (plh->url); + if (GNUNET_OK != + TALER_curl_easy_post (&plh->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (plh->url); + GNUNET_free (plh); + return NULL; + } + json_decref (req_obj); + plh->job = GNUNET_CURL_job_add2 (ctx, + eh, + plh->post_ctx.headers, + &handle_lock_product_finished, + plh); + } + return plh; +} + + +void +TALER_MERCHANT_product_lock_cancel ( + struct TALER_MERCHANT_ProductLockHandle *plh) +{ + if (NULL != plh->job) + { + GNUNET_CURL_job_cancel (plh->job); + plh->job = NULL; + } + TALER_curl_easy_post_finished (&plh->post_ctx); + GNUNET_free (plh->url); + GNUNET_free (plh); +} + + +struct TALER_MERCHANT_ProductLockHandle * +TALER_MERCHANT_product_lock ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *uuid, + struct GNUNET_TIME_Relative duration, + uint32_t quantity, + TALER_MERCHANT_ProductLockCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_product_lock2 (ctx, + backend_url, + product_id, + uuid, + duration, + quantity, + 0, + false, + cb, + cb_cls); +} + + +/* end of merchant_api_lock_product.c */ diff --git a/src/lib/merchant_api_post-private-products.c b/src/lib/merchant_api_post-private-products.c @@ -0,0 +1,433 @@ +/* + This file is part of TALER + Copyright (C) 2020-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-products.c + * @brief Implementation of the POST /products request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /products/$ID operation. + */ +struct TALER_MERCHANT_ProductsPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_ProductsPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP POST /products request. + * + * @param cls the `struct TALER_MERCHANT_ProductsPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_products_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_ProductsPostHandle *pph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + pph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /products completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + pph->cb (pph->cb_cls, + &hr); + TALER_MERCHANT_products_post_cancel (pph); +} + + +struct TALER_MERCHANT_ProductsPostHandle * +TALER_MERCHANT_products_post4 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *description, + const json_t *description_i18n, + const char *unit, + const struct TALER_Amount *unit_prices, + size_t unit_price_len, + const char *image, + const json_t *taxes, + int64_t total_stock, + uint32_t total_stock_frac, + bool unit_allow_fraction, + const uint32_t *unit_precision_level, + const json_t *address, + struct GNUNET_TIME_Timestamp next_restock, + uint32_t minimum_age, + unsigned int num_cats, + const uint64_t *cats, + TALER_MERCHANT_ProductsPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_ProductsPostHandle *pph; + json_t *req_obj; + json_t *categories; + char unit_total_stock_buf[64]; + + TALER_MERCHANT_format_stock_string (total_stock, + total_stock_frac, + unit_total_stock_buf, + sizeof (unit_total_stock_buf)); + + if (0 == num_cats) + { + categories = NULL; + } + else + { + categories = json_array (); + GNUNET_assert (NULL != categories); + for (unsigned int i = 0; i<num_cats; i++) + GNUNET_assert (0 == + json_array_append_new (categories, + json_integer (cats[i]))); + } + { + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("product_id", + product_id), + /* FIXME: once we move to the new-style API, + allow applications to set the product name properly! */ + GNUNET_JSON_pack_string ("product_name", + description), + GNUNET_JSON_pack_string ("description", + description), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("description_i18n", + (json_t *) description_i18n)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("categories", + categories)), + GNUNET_JSON_pack_string ("unit", + unit), + TALER_JSON_pack_amount_array ("unit_price", + unit_price_len, + unit_prices), + GNUNET_JSON_pack_string ("image", + image), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_incref ("taxes", + (json_t *) taxes)), + GNUNET_JSON_pack_string ("unit_total_stock", + unit_total_stock_buf), + GNUNET_JSON_pack_bool ("unit_allow_fraction", + unit_allow_fraction), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_uint64 ("minimum_age", + minimum_age)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("address", + (json_t *) address)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_timestamp ("next_restock", + next_restock))); + } + if (NULL != unit_precision_level) + { + GNUNET_assert (0 == + json_object_set_new (req_obj, + "unit_precision_level", + json_integer ( + *unit_precision_level))); + } + if (! unit_allow_fraction) + { + GNUNET_assert (0 == + json_object_del (req_obj, + "unit_allow_fraction")); + if (NULL != unit_precision_level) + GNUNET_assert (0 == + json_object_del (req_obj, + "unit_precision_level")); + } + pph = GNUNET_new (struct TALER_MERCHANT_ProductsPostHandle); + pph->ctx = ctx; + pph->cb = cb; + pph->cb_cls = cb_cls; + pph->url = TALER_url_join (backend_url, + "private/products", + NULL); + if (NULL == pph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (pph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (pph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&pph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + pph->job = GNUNET_CURL_job_add2 (ctx, + eh, + pph->post_ctx.headers, + &handle_post_products_finished, + pph); + GNUNET_assert (NULL != pph->job); + } + return pph; +} + + +struct TALER_MERCHANT_ProductsPostHandle * +TALER_MERCHANT_products_post3 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *description, + const json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + const char *image, + const json_t *taxes, + int64_t total_stock, + const json_t *address, + struct GNUNET_TIME_Timestamp next_restock, + uint32_t minimum_age, + unsigned int num_cats, + const uint64_t *cats, + TALER_MERCHANT_ProductsPostCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_products_post4 (ctx, + backend_url, + product_id, + description, + description_i18n, + unit, + price, + 1, + image, + taxes, + total_stock, + 0, + false, + NULL, + address, + next_restock, + minimum_age, + num_cats, + cats, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_ProductsPostHandle * +TALER_MERCHANT_products_post2 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *description, + const json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + const char *image, + const json_t *taxes, + int64_t total_stock, + const json_t *address, + struct GNUNET_TIME_Timestamp next_restock, + uint32_t minimum_age, + TALER_MERCHANT_ProductsPostCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_products_post3 (ctx, + backend_url, + product_id, + description, + description_i18n, + unit, + price, + image, + taxes, + total_stock, + address, + next_restock, + minimum_age, + 0, + NULL, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_ProductsPostHandle * +TALER_MERCHANT_products_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *product_id, + const char *description, + const json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + const char *image, + const json_t *taxes, + int64_t total_stock, + const json_t *address, + struct GNUNET_TIME_Timestamp next_restock, + TALER_MERCHANT_ProductsPostCallback cb, + void *cb_cls) +{ + return TALER_MERCHANT_products_post2 (ctx, + backend_url, + product_id, + description, + description_i18n, + unit, + price, + image, + taxes, + total_stock, + address, + next_restock, + 0, + cb, + cb_cls); +} + + +void +TALER_MERCHANT_products_post_cancel ( + struct TALER_MERCHANT_ProductsPostHandle *pph) +{ + if (NULL != pph->job) + { + GNUNET_CURL_job_cancel (pph->job); + pph->job = NULL; + } + TALER_curl_easy_post_finished (&pph->post_ctx); + GNUNET_free (pph->url); + GNUNET_free (pph); +} + + +/* end of merchant_api_post_products.c */ diff --git a/src/lib/merchant_api_post-private-templates.c b/src/lib/merchant_api_post-private-templates.c @@ -0,0 +1,495 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-templates.c + * @brief Implementation of the POST /templates request + * of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> +#include "taler/taler_merchant_util.h" + +/* FIXME: Bohdan is to stupid to figure out how util can be used here */ +static enum TALER_MERCHANT_TemplateType +template_type_from_string (const char *template_type) +{ + if (NULL == template_type) + return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER; + if (0 == strcmp (template_type, + "fixed-order")) + return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER; + if (0 == strcmp (template_type, + "inventory-cart")) + return TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART; + if (0 == strcmp (template_type, + "paivana")) + return TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA; + return TALER_MERCHANT_TEMPLATE_TYPE_INVALID; +} + + +/** + * Validate a fixed-order template contract. + * + * @param template_contract JSON template contract + * @param summary summary pointer + * @param amount amount storage + * @param minimum_age minimum age storage + * @param pay_duration pay duration storage + * @return true if valid + */ +static bool +validate_template_contract_fixed (const json_t *template_contract, + const char **summary, + struct TALER_Amount *amount, + uint32_t *minimum_age, + struct GNUNET_TIME_Relative *pay_duration) +{ + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("summary", + summary), + NULL), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_amount_any ("amount", + amount), + NULL), + GNUNET_JSON_spec_uint32 ("minimum_age", + minimum_age), + GNUNET_JSON_spec_relative_time ("pay_duration", + pay_duration), + GNUNET_JSON_spec_end () + }; + const char *ename; + unsigned int eline; + + if (GNUNET_OK != + GNUNET_JSON_parse (template_contract, + spec, + &ename, + &eline)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid template_contract for field %s\n", + ename); + return false; + } + return true; +} + + +/** + * Validate an inventory-cart template contract. + * + * @param template_contract JSON template contract + * @param summary summary pointer + * @param pay_duration pay duration storage + * @return true if valid + */ +static bool +validate_template_contract_inventory (const json_t *template_contract, + const char **summary, + struct GNUNET_TIME_Relative *pay_duration) +{ + bool selected_all = false; + bool choose_one = false; + bool request_tip = false; + const json_t *selected_categories = NULL; + const json_t *selected_products = NULL; + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("summary", + summary), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_bool ("request_tip", + &request_tip), + NULL), + GNUNET_JSON_spec_relative_time ("pay_duration", + pay_duration), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_bool ("selected_all", + &selected_all), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("selected_categories", + &selected_categories), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("selected_products", + &selected_products), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_bool ("choose_one", + &choose_one), + NULL), + GNUNET_JSON_spec_end () + }; + const char *ename; + unsigned int eline; + + (void) request_tip; + (void) selected_all; + (void) choose_one; + (void) selected_categories; + (void) selected_products; + if (GNUNET_OK != + GNUNET_JSON_parse (template_contract, + ispec, + &ename, + &eline)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid template_contract for field %s\n", + ename); + return false; + } + return true; +} + + +/** + * Validate a paivana template contract. + * + * @param template_contract JSON template contract + * @param summary summary pointer + * @param amount amount storage + * @param minimum_age minimum age storage + * @param pay_duration pay duration storage + * @return true if valid + */ +static bool +validate_template_contract_paivana (const json_t *template_contract, + const char **summary, + struct TALER_Amount *amount, + uint32_t *minimum_age, + struct GNUNET_TIME_Relative *pay_duration) +{ + /* TODO: PAIVANA validate paivana-specific fields beyond presence. */ + const char *paivana_id; + struct GNUNET_JSON_Specification pspec[] = { + GNUNET_JSON_spec_string ("paivana_id", + &paivana_id), + GNUNET_JSON_spec_end () + }; + const char *ename; + unsigned int eline; + + if (GNUNET_OK != + GNUNET_JSON_parse (template_contract, + pspec, + &ename, + &eline)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid paivana template_contract for field %s\n", + ename); + return false; + } + + if (! validate_template_contract_fixed (template_contract, + summary, + amount, + minimum_age, + pay_duration)) + return false; + + (void) paivana_id; + return true; +} + + +/** + * Handle for a POST /templates/$ID operation. + */ +struct TALER_MERCHANT_TemplatesPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TemplatesPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /templates request. + * + * @param cls the `struct TALER_MERCHANT_TemplatesPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_templates_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TemplatesPostHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /templates completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_templates_post_cancel (tph); +} + + +static bool +test_template_contract_valid (const json_t *template_contract) +{ + const char *template_type = NULL; + enum TALER_MERCHANT_TemplateType template_type_enum; + struct GNUNET_JSON_Specification type_spec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("template_type", + &template_type), + NULL), + GNUNET_JSON_spec_end () + }; + const char *summary; + struct TALER_Amount amount = { .value = 0}; + uint32_t minimum_age = 0; + struct GNUNET_TIME_Relative pay_duration = { 0 }; + const char *ename; + unsigned int eline; + + if (GNUNET_OK != + GNUNET_JSON_parse (template_contract, + type_spec, + &ename, + &eline)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid template_contract for field %s\n", + ename); + return false; + } + + template_type_enum = template_type_from_string (template_type); + if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == template_type_enum) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid template_type '%s'\n", + template_type); + return false; + } + + /* FIXME: Bohdan understands that links have to be changed, but worried, that can crash something */ + switch (template_type_enum) + { + case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART: + return validate_template_contract_inventory (template_contract, + &summary, + &pay_duration); + case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER: + return validate_template_contract_fixed (template_contract, + &summary, + &amount, + &minimum_age, + &pay_duration); + case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA: + return validate_template_contract_paivana (template_contract, + &summary, + &amount, + &minimum_age, + &pay_duration); + case TALER_MERCHANT_TEMPLATE_TYPE_INVALID: + break; + } + return false; +} + + +struct TALER_MERCHANT_TemplatesPostHandle * +TALER_MERCHANT_templates_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + const char *template_description, + const char *otp_id, + const json_t *template_contract, + TALER_MERCHANT_TemplatesPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TemplatesPostHandle *tph; + json_t *req_obj; + + if (! test_template_contract_valid (template_contract)) + { + GNUNET_break (0); + return NULL; + } + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("template_id", + template_id), + GNUNET_JSON_pack_string ("template_description", + template_description), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_id", + otp_id)), + GNUNET_JSON_pack_object_incref ("template_contract", + (json_t *) template_contract)); + tph = GNUNET_new (struct TALER_MERCHANT_TemplatesPostHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + tph->url = TALER_url_join (backend_url, + "private/templates", + NULL); + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_post_templates_finished, + tph); + GNUNET_assert (NULL != tph->job); + } + return tph; +} + + +void +TALER_MERCHANT_templates_post_cancel ( + struct TALER_MERCHANT_TemplatesPostHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_post_templates.c */ diff --git a/src/lib/merchant_api_post-private-token.c b/src/lib/merchant_api_post-private-token.c @@ -0,0 +1,235 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-token.c + * @brief Implementation of the POST /instance/$ID/private/token request + * @author Martin Schanzenbach + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /instances/$ID/private/token operation. + */ +struct TALER_MERCHANT_InstanceTokenPostHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_InstanceTokenPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /instances/$ID/private/token request. + * + * @param cls the `struct TALER_MERCHANT_InstanceTokenPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_instance_token_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_InstanceTokenPostHandle *itph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + itph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /instances/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + break; + case MHD_HTTP_BAD_REQUEST: + /* happens if the auth token is malformed */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + itph->cb (itph->cb_cls, + &hr); + TALER_MERCHANT_instance_token_post_cancel (itph); +} + + +struct TALER_MERCHANT_InstanceTokenPostHandle * +TALER_MERCHANT_instance_token_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const char *scope, + struct GNUNET_TIME_Relative duration, + bool refreshable, + TALER_MERCHANT_InstanceTokenPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_InstanceTokenPostHandle *itph; + json_t *req_obj; + + itph = GNUNET_new (struct TALER_MERCHANT_InstanceTokenPostHandle); + itph->ctx = ctx; + itph->cb = cb; + itph->cb_cls = cb_cls; + if (NULL != instance_id) + { + char *path; + + GNUNET_asprintf (&path, + "instances/%s/private/token", + instance_id); + itph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + else + { + /* backend_url is already identifying the instance */ + itph->url = TALER_url_join (backend_url, + "private/token", + NULL); + } + if (NULL == itph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (itph); + return NULL; + } + if (NULL == scope) + { + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_time_rel ("duration", + duration), + GNUNET_JSON_pack_bool ("refreshable", + refreshable), + GNUNET_JSON_pack_string ("scope", + "readonly")); + } + else + { + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_time_rel ("duration", + duration), + GNUNET_JSON_pack_bool ("refreshable", + refreshable), + GNUNET_JSON_pack_string ("scope", + scope)); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + itph->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (itph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&itph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (itph->url); + GNUNET_free (itph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_POST)); + itph->job = GNUNET_CURL_job_add2 (ctx, + eh, + itph->post_ctx.headers, + &handle_post_instance_token_finished, + itph); + } + return itph; +} + + +void +TALER_MERCHANT_instance_token_post_cancel ( + struct TALER_MERCHANT_InstanceTokenPostHandle *itph) +{ + if (NULL != itph->job) + GNUNET_CURL_job_cancel (itph->job); + TALER_curl_easy_post_finished (&itph->post_ctx); + GNUNET_free (itph->url); + GNUNET_free (itph); +} diff --git a/src/lib/merchant_api_post-private-tokenfamilies.c b/src/lib/merchant_api_post-private-tokenfamilies.c @@ -0,0 +1,257 @@ +/* + This file is part of TALER + Copyright (C) 2020-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-tokenfamilies.c + * @brief Implementation of the POST /tokenfamilies request + * of the merchant's HTTP API + * @author Christian Blättler + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <gnunet/gnunet_json_lib.h> +#include <gnunet/gnunet_time_lib.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /tokenfamilies operation. + */ +struct TALER_MERCHANT_TokenFamiliesPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_TokenFamiliesPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + +/** + * Function called when we're done processing the + * HTTP POST /tokenfamilies request. + * + * @param cls the `struct TALER_MERCHANT_TokenFamiliesPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_token_families_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TokenFamiliesPostHandle *handle = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + handle->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /tokenfamilies completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + handle->cb (handle->cb_cls, + &hr); + TALER_MERCHANT_token_families_post_cancel (handle); +} + + +struct TALER_MERCHANT_TokenFamiliesPostHandle * +TALER_MERCHANT_token_families_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *slug, + const char *name, + const char *description, + const json_t *description_i18n, + const json_t *extra_data, + struct GNUNET_TIME_Timestamp valid_after, + struct GNUNET_TIME_Timestamp valid_before, + struct GNUNET_TIME_Relative duration, + struct GNUNET_TIME_Relative validity_granularity, + struct GNUNET_TIME_Relative start_offset, + const char *kind, + TALER_MERCHANT_TokenFamiliesPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_TokenFamiliesPostHandle *handle; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("slug", + slug), + GNUNET_JSON_pack_string ("name", + name), + GNUNET_JSON_pack_string ("description", + description), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("description_i18n", + (json_t *) description_i18n)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("extra_data", + (json_t *) extra_data)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_timestamp ("valid_after", + valid_after)), + GNUNET_JSON_pack_timestamp ("valid_before", + valid_before), + GNUNET_JSON_pack_time_rel ("duration", + duration), + GNUNET_JSON_pack_time_rel ("validity_granularity", + validity_granularity), + GNUNET_JSON_pack_time_rel ("start_offset", + start_offset), + GNUNET_JSON_pack_string ("kind", + kind)); + handle = GNUNET_new (struct TALER_MERCHANT_TokenFamiliesPostHandle); + handle->ctx = ctx; + handle->cb = cb; + handle->cb_cls = cb_cls; + handle->url = TALER_url_join (backend_url, + "private/tokenfamilies", + NULL); + if (NULL == handle->url) + { + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (handle); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (handle->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&handle->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + handle->job = GNUNET_CURL_job_add2 (ctx, + eh, + handle->post_ctx.headers, + &handle_post_token_families_finished, + handle); + GNUNET_assert (NULL != handle->job); + } + return handle; +} + + +void +TALER_MERCHANT_token_families_post_cancel ( + struct TALER_MERCHANT_TokenFamiliesPostHandle *pph) +{ + if (NULL != pph->job) + { + GNUNET_CURL_job_cancel (pph->job); + pph->job = NULL; + } + TALER_curl_easy_post_finished (&pph->post_ctx); + GNUNET_free (pph->url); + GNUNET_free (pph); +} diff --git a/src/lib/merchant_api_post-private-transfers.c b/src/lib/merchant_api_post-private-transfers.c @@ -0,0 +1,256 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-transfers.c + * @brief Implementation of the POST /transfers request of the merchant's HTTP API + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_curl_lib.h> +#include <taler/taler_json_lib.h> + + +/** + * @brief A handle for POSTing transfer data. + */ +struct TALER_MERCHANT_PostTransfersHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_PostTransfersCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP POST /transfers request. + * + * @param cls the `struct TALER_MERCHANT_PostTransfersHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_transfers_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_PostTransfersHandle *pth = cls; + struct TALER_MERCHANT_PostTransfersResponse ptr = { + .hr.reply = response, + .hr.http_status = (unsigned int) response_code + }; + + pth->job = NULL; + switch (response_code) + { + case 0: + ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Did not find any data\n"); + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); + break; + case MHD_HTTP_BAD_GATEWAY: + /* Exchange had an issue; we should retry, but this API + leaves this to the application */ + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); + { + uint32_t ehc; + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_ec ("exchange_code", + &ptr.details.bad_gateway.exchange_ec), + GNUNET_JSON_spec_uint32 ("exchange_http_status", + &ehc), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (ptr.hr.reply, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + ptr.details.bad_gateway.exchange_http_status = 0; + ptr.details.bad_gateway.exchange_ec = TALER_EC_NONE; + break; + } + else + { + ptr.details.bad_gateway.exchange_http_status + = (unsigned int) ehc; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Exchange returned %u/%u\n", + (unsigned int) ptr.details.bad_gateway.exchange_ec, + (unsigned int) ehc); + } + } + break; + case MHD_HTTP_GATEWAY_TIMEOUT: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); + ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + TALER_MERCHANT_parse_error_details_ (ptr.hr.reply, + response_code, + &ptr.hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) ptr.hr.http_status, + (int) ptr.hr.ec); + break; + } + pth->cb (pth->cb_cls, + &ptr); + TALER_MERCHANT_transfers_post_cancel (pth); +} + + +struct TALER_MERCHANT_PostTransfersHandle * +TALER_MERCHANT_transfers_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_Amount *credit_amount, + const struct TALER_WireTransferIdentifierRawP *wtid, + struct TALER_FullPayto payto_uri, + const char *exchange_url, + TALER_MERCHANT_PostTransfersCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_PostTransfersHandle *pth; + CURL *eh; + json_t *req; + + pth = GNUNET_new (struct TALER_MERCHANT_PostTransfersHandle); + pth->ctx = ctx; + pth->cb = cb; + pth->cb_cls = cb_cls; + pth->url = TALER_url_join (backend_url, + "private/transfers", + NULL); + if (NULL == pth->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (pth); + return NULL; + } + req = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("credit_amount", + credit_amount), + GNUNET_JSON_pack_data_auto ("wtid", + wtid), + TALER_JSON_pack_full_payto ("payto_uri", + payto_uri), + GNUNET_JSON_pack_string ("exchange_url", + exchange_url)); + eh = TALER_MERCHANT_curl_easy_get_ (pth->url); + if (GNUNET_OK != + TALER_curl_easy_post (&pth->post_ctx, + eh, + req)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req); + GNUNET_free (pth->url); + GNUNET_free (pth); + return NULL; + } + json_decref (req); + pth->job = GNUNET_CURL_job_add2 (ctx, + eh, + pth->post_ctx.headers, + &handle_post_transfers_finished, + pth); + return pth; +} + + +void +TALER_MERCHANT_transfers_post_cancel ( + struct TALER_MERCHANT_PostTransfersHandle *pth) +{ + if (NULL != pth->job) + { + GNUNET_CURL_job_cancel (pth->job); + pth->job = NULL; + } + GNUNET_free (pth->url); + TALER_curl_easy_post_finished (&pth->post_ctx); + GNUNET_free (pth); +} + + +/* end of merchant_api_post_transfers.c */ diff --git a/src/lib/merchant_api_post-private-units.c b/src/lib/merchant_api_post-private-units.c @@ -0,0 +1,218 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-units.c + * @brief Implementation of POST /private/units + * @author Bohdan Potuzhnyi + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /private/units operation. + */ +struct TALER_MERCHANT_UnitsPostHandle +{ + /** + * Fully qualified request URL. + */ + char *url; + + /** + * CURL job handle. + */ + struct GNUNET_CURL_Job *job; + + /** + * Callback to invoke with the result. + */ + TALER_MERCHANT_UnitsPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Helper keeping POST body and headers alive. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Called when the HTTP transfer finishes. + * + * @param cls operation handle + * @param response_code HTTP status (0 on network / parsing failures) + * @param response parsed JSON reply (NULL if unavailable) + */ +static void +handle_post_units_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_UnitsPostHandle *uph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + uph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /private/units completed with status %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + case MHD_HTTP_UNAUTHORIZED: + case MHD_HTTP_FORBIDDEN: + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_CONFLICT: + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response %u/%d for POST /private/units\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + uph->cb (uph->cb_cls, + &hr); + TALER_MERCHANT_units_post_cancel (uph); +} + + +struct TALER_MERCHANT_UnitsPostHandle * +TALER_MERCHANT_units_post (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *unit_id, + const char *unit_name_long, + const char *unit_name_short, + bool unit_allow_fraction, + uint32_t unit_precision_level, + bool unit_active, + const json_t *unit_name_long_i18n, + const json_t *unit_name_short_i18n, + TALER_MERCHANT_UnitsPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_UnitsPostHandle *uph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("unit", + unit_id), + GNUNET_JSON_pack_string ("unit_name_long", + unit_name_long), + GNUNET_JSON_pack_string ("unit_name_short", + unit_name_short), + GNUNET_JSON_pack_bool ("unit_allow_fraction", + unit_allow_fraction), + GNUNET_JSON_pack_uint64 ("unit_precision_level", + (uint64_t) unit_precision_level), + GNUNET_JSON_pack_bool ("unit_active", + unit_active), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("unit_name_long_i18n", + (json_t *) unit_name_long_i18n)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("unit_name_short_i18n", + (json_t *) unit_name_short_i18n))); + uph = GNUNET_new (struct TALER_MERCHANT_UnitsPostHandle); + uph->ctx = ctx; + uph->cb = cb; + uph->cb_cls = cb_cls; + uph->url = TALER_url_join (backend_url, + "private/units", + NULL); + if (NULL == uph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to build /private/units URL\n"); + json_decref (req_obj); + GNUNET_free (uph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (uph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&uph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (uph->url); + GNUNET_free (uph); + return NULL; + } + json_decref (req_obj); + uph->job = GNUNET_CURL_job_add2 (ctx, + eh, + uph->post_ctx.headers, + &handle_post_units_finished, + uph); + } + return uph; +} + + +void +TALER_MERCHANT_units_post_cancel (struct TALER_MERCHANT_UnitsPostHandle *uph) +{ + if (NULL != uph->job) + { + GNUNET_CURL_job_cancel (uph->job); + uph->job = NULL; + } + TALER_curl_easy_post_finished (&uph->post_ctx); + GNUNET_free (uph->url); + GNUNET_free (uph); +} + + +/* end of merchant_api_post_units.c */ diff --git a/src/lib/merchant_api_post-private-webhooks.c b/src/lib/merchant_api_post-private-webhooks.c @@ -0,0 +1,240 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-private-webhooks.c + * @brief Implementation of the POST /webhooks request + * of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /webhooks/$ID operation. + */ +struct TALER_MERCHANT_WebhooksPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_WebhooksPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP POST /webhooks request. + * + * @param cls the `struct TALER_MERCHANT_WebhooksPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_webhooks_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_WebhooksPostHandle *wph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + wph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /webhooks completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + wph->cb (wph->cb_cls, + &hr); + TALER_MERCHANT_webhooks_post_cancel (wph); +} + + +struct TALER_MERCHANT_WebhooksPostHandle * +TALER_MERCHANT_webhooks_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *webhook_id, + const char *event_type, + const char *url, + const char *http_method, + const char *header_template, + const char *body_template, + TALER_MERCHANT_WebhooksPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_WebhooksPostHandle *wph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("webhook_id", + webhook_id), + GNUNET_JSON_pack_string ("event_type", + event_type), + GNUNET_JSON_pack_string ("url", + url), + GNUNET_JSON_pack_string ("http_method", + http_method), + GNUNET_JSON_pack_string ("header_template", + header_template), + GNUNET_JSON_pack_string ("body_template", + body_template)); + wph = GNUNET_new (struct TALER_MERCHANT_WebhooksPostHandle); + wph->ctx = ctx; + wph->cb = cb; + wph->cb_cls = cb_cls; + wph->url = TALER_url_join (backend_url, + "private/webhooks", + NULL); + if (NULL == wph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (wph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (wph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&wph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + wph->job = GNUNET_CURL_job_add2 (ctx, + eh, + wph->post_ctx.headers, + &handle_post_webhooks_finished, + wph); + GNUNET_assert (NULL != wph->job); + } + return wph; +} + + +void +TALER_MERCHANT_webhooks_post_cancel ( + struct TALER_MERCHANT_WebhooksPostHandle *wph) +{ + if (NULL != wph->job) + { + GNUNET_CURL_job_cancel (wph->job); + wph->job = NULL; + } + TALER_curl_easy_post_finished (&wph->post_ctx); + GNUNET_free (wph->url); + GNUNET_free (wph); +} + + +/* end of merchant_api_post_webhooks.c */ diff --git a/src/lib/merchant_api_post-templates-TEMPLATE_ID.c b/src/lib/merchant_api_post-templates-TEMPLATE_ID.c @@ -0,0 +1,233 @@ +/* + This file is part of TALER + Copyright (C) 2022-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post-templates-TEMPLATE_ID.c + * @brief Implementation of the POST /using_templates request + * of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "taler/platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /templates/$ID operation. + */ +struct TALER_MERCHANT_UsingTemplatesPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_PostOrdersCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + +/** + * Function called when we're done processing the + * HTTP POST /using-templates request. + * + * @param cls the `struct TALER_MERCHANT_UsingTemplatesPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_using_templates_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_UsingTemplatesPostHandle *utph = cls; + const json_t *json = response; + + utph->job = NULL; + TALER_MERCHANT_handle_order_creation_response_ (utph->cb, + utph->cb_cls, + response_code, + json); + TALER_MERCHANT_using_templates_post_cancel (utph); +} + + +/** + * Helper to submit a POST /templates/$ID request. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param template_id identifier of the template to use + * @param req_obj JSON request body (consumed) + * @param cb function to call with the backend's result + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +static struct TALER_MERCHANT_UsingTemplatesPostHandle * +using_templates_post_internal ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + json_t *req_obj, + TALER_MERCHANT_PostOrdersCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_UsingTemplatesPostHandle *utph; + + utph = GNUNET_new (struct TALER_MERCHANT_UsingTemplatesPostHandle); + utph->ctx = ctx; + utph->cb = cb; + utph->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "templates/%s", + template_id); + utph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == utph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (utph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (utph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&utph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + utph->job = GNUNET_CURL_job_add2 (ctx, + eh, + utph->post_ctx.headers, + &handle_post_using_templates_finished, + utph); + GNUNET_assert (NULL != utph->job); + } + return utph; +} + + +struct TALER_MERCHANT_UsingTemplatesPostHandle * +TALER_MERCHANT_using_templates_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + const char *summary, + const struct TALER_Amount *amount, + TALER_MERCHANT_PostOrdersCallback cb, + void *cb_cls) +{ + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("template_type", + "fixed-order"), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("summary", + summary)), + GNUNET_JSON_pack_allow_null ( + TALER_JSON_pack_amount ("amount", + amount))); + return using_templates_post_internal (ctx, + backend_url, + template_id, + req_obj, + cb, + cb_cls); +} + + +struct TALER_MERCHANT_UsingTemplatesPostHandle * +TALER_MERCHANT_using_templates_post2 ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *template_id, + const json_t *details, + TALER_MERCHANT_PostOrdersCallback cb, + void *cb_cls) +{ + if (NULL == details) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No request body provided for using templates.\n"); + return NULL; + } + return using_templates_post_internal (ctx, + backend_url, + template_id, + (json_t *) details, + cb, + cb_cls); +} + + +void +TALER_MERCHANT_using_templates_post_cancel ( + struct TALER_MERCHANT_UsingTemplatesPostHandle *utph) +{ + if (NULL != utph->job) + { + GNUNET_CURL_job_cancel (utph->job); + utph->job = NULL; + } + TALER_curl_easy_post_finished (&utph->post_ctx); + GNUNET_free (utph->url); + GNUNET_free (utph); +} + + +/* end of merchant_api_post_using_templates.c */ diff --git a/src/lib/merchant_api_post_account.c b/src/lib/merchant_api_post_account.c @@ -1,250 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_account.c - * @brief Implementation of the POST /account request - * of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /private/accounts operation. - */ -struct TALER_MERCHANT_AccountsPostHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_AccountsPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP POST /account request. - * - * @param cls the `struct TALER_MERCHANT_AccountPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_account_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_AccountsPostHandle *aph = cls; - const json_t *json = response; - struct TALER_MERCHANT_AccountsPostResponse apr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - aph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /accounts completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("h_wire", - &apr.details.ok.h_wire), - GNUNET_JSON_spec_fixed_auto ("salt", - &apr.details.ok.salt), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - apr.hr.http_status = 0; - apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - } - break; - case MHD_HTTP_BAD_REQUEST: - GNUNET_break_op (0); - apr.hr.ec = TALER_JSON_get_error_code (json); - apr.hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - apr.hr.ec = TALER_JSON_get_error_code (json); - apr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - apr.hr.ec = TALER_JSON_get_error_code (json); - apr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_CONFLICT: - apr.hr.ec = TALER_JSON_get_error_code (json); - apr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - apr.hr.ec = TALER_JSON_get_error_code (json); - apr.hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &apr.hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) apr.hr.ec); - GNUNET_break_op (0); - break; - } - aph->cb (aph->cb_cls, - &apr); - TALER_MERCHANT_accounts_post_cancel (aph); -} - - -struct TALER_MERCHANT_AccountsPostHandle * -TALER_MERCHANT_accounts_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - struct TALER_FullPayto payto_uri, - const char *credit_facade_url, - const json_t *credit_facade_credentials, - TALER_MERCHANT_AccountsPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_AccountsPostHandle *aph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - TALER_JSON_pack_full_payto ( - "payto_uri", - payto_uri), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - "credit_facade_url", - credit_facade_url)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ( - "credit_facade_credentials", - (json_t *) credit_facade_credentials)) - ); - aph = GNUNET_new (struct TALER_MERCHANT_AccountsPostHandle); - aph->ctx = ctx; - aph->cb = cb; - aph->cb_cls = cb_cls; - aph->url = TALER_url_join (backend_url, - "private/accounts", - NULL); - if (NULL == aph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (aph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (aph->url); - GNUNET_assert (GNUNET_OK == - TALER_curl_easy_post (&aph->post_ctx, - eh, - req_obj)); - json_decref (req_obj); - aph->job = GNUNET_CURL_job_add2 (ctx, - eh, - aph->post_ctx.headers, - &handle_post_account_finished, - aph); - GNUNET_assert (NULL != aph->job); - } - return aph; -} - - -void -TALER_MERCHANT_accounts_post_cancel ( - struct TALER_MERCHANT_AccountsPostHandle *aph) -{ - if (NULL != aph->job) - { - GNUNET_CURL_job_cancel (aph->job); - aph->job = NULL; - } - TALER_curl_easy_post_finished (&aph->post_ctx); - GNUNET_free (aph->url); - GNUNET_free (aph); -} - - -/* end of merchant_api_post_account.c */ diff --git a/src/lib/merchant_api_post_categories.c b/src/lib/merchant_api_post_categories.c @@ -1,223 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free - Software Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_categories.c - * @brief Implementation of POST /private/categories - * @author Bohdan Potuzhnyi - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - -/** - * Handle for a POST /private/categories operation. - */ -struct TALER_MERCHANT_CategoriesPostHandle -{ - /** - * Fully qualified request URL. - */ - char *url; - - /** - * CURL job handle. - */ - struct GNUNET_CURL_Job *job; - - /** - * Callback to invoke with the result. - */ - TALER_MERCHANT_CategoriesPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Helper keeping POST body and headers alive. - */ - struct TALER_CURL_PostContext post_ctx; -}; - -/** - * Called when the HTTP transfer finishes. - * - * @param cls operation handle - * @param response_code HTTP status (0 on network / parsing failures) - * @param response parsed JSON reply (NULL if unavailable) - */ -static void -handle_post_categories_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_CategoriesPostHandle *cph = cls; - const json_t *json = response; - struct TALER_MERCHANT_CategoriesPostResponse cpr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - cph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /private/categories completed with status %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const char *err_name; - unsigned int err_line; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint64 ("category_id", - &cpr.category_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - &err_name, - &err_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid response for field %s\n", - err_name); - cpr.hr.http_status = 0; - cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - } - break; - } - case MHD_HTTP_BAD_REQUEST: - case MHD_HTTP_UNAUTHORIZED: - case MHD_HTTP_FORBIDDEN: - case MHD_HTTP_NOT_FOUND: - case MHD_HTTP_CONFLICT: - case MHD_HTTP_INTERNAL_SERVER_ERROR: - cpr.hr.ec = TALER_JSON_get_error_code (json); - cpr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case 0: - cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &cpr.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response %u/%d for POST /private/categories\n", - (unsigned int) response_code, - (int) cpr.hr.ec); - GNUNET_break_op (0); - break; - } - cph->cb (cph->cb_cls, - &cpr); - TALER_MERCHANT_categories_post_cancel (cph); -} - - -struct TALER_MERCHANT_CategoriesPostHandle * -TALER_MERCHANT_categories_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *name, - const json_t *name_i18n, - TALER_MERCHANT_CategoriesPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_CategoriesPostHandle *cph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("name", - name), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("name_i18n", - (json_t *) name_i18n))); - cph = GNUNET_new (struct TALER_MERCHANT_CategoriesPostHandle); - cph->ctx = ctx; - cph->cb = cb; - cph->cb_cls = cb_cls; - cph->url = TALER_url_join (backend_url, - "private/categories", - NULL); - if (NULL == cph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to build /private/categories URL\n"); - json_decref (req_obj); - GNUNET_free (cph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (cph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&cph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (cph->url); - GNUNET_free (cph); - return NULL; - } - json_decref (req_obj); - cph->job = GNUNET_CURL_job_add2 (ctx, - eh, - cph->post_ctx.headers, - &handle_post_categories_finished, - cph); - } - return cph; -} - - -void -TALER_MERCHANT_categories_post_cancel ( - struct TALER_MERCHANT_CategoriesPostHandle *cph) -{ - if (NULL != cph->job) - { - GNUNET_CURL_job_cancel (cph->job); - cph->job = NULL; - } - TALER_curl_easy_post_finished (&cph->post_ctx); - GNUNET_free (cph->url); - GNUNET_free (cph); -} - - -/* end of merchant_api_post_categories.c */ diff --git a/src/lib/merchant_api_post_donau_instance.c b/src/lib/merchant_api_post_donau_instance.c @@ -1,237 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2024 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_donau_instance.c - * @brief Implementation of the POST /donau request - * of the merchant's HTTP API - * @author Bohdan Potuzhnyi - * @author Vlada Svirsh - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> -#include <taler/taler_kyclogic_lib.h> -/* DONAU RELATED IMPORTS */ -#include "taler/taler_merchant_donau.h" -#include <donau/donau_service.h> - - -/** - * Handle for a POST /donau operation. - */ -struct TALER_MERCHANT_DonauInstancePostHandle -{ - /** - * URL for this request. - */ - char *url; - - /** - * Handle for the CURL job. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_DonauInstancePostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * Function called when the POST /donau operation is finished. - * - * @param cls the `struct TALER_MERCHANT_DonauInstancePostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_donau_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_DonauInstancePostHandle *dph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - dph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /donau completed with response code %u\n", - (unsigned int) response_code); - - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - case MHD_HTTP_INTERNAL_SERVER_ERROR: - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - - dph->cb (dph->cb_cls, - &hr); - TALER_MERCHANT_donau_instances_post_cancel (dph); -} - - -struct TALER_MERCHANT_DonauInstancePostHandle * -TALER_MERCHANT_donau_instances_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_MERCHANT_Charity *charity, - const char *auth_token, - TALER_MERCHANT_DonauInstancePostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_DonauInstancePostHandle *dph; - json_t *req_obj; - json_t *auth_obj; - - if (NULL != auth_token) - { - if (0 != strncasecmp (RFC_8959_PREFIX, - auth_token, - strlen (RFC_8959_PREFIX))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Authentication token must start with `%s'\n", - RFC_8959_PREFIX); - return NULL; - } - auth_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("method", - "token"), - GNUNET_JSON_pack_string ("token", - auth_token)); - } - else - { - auth_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("method", - "external")); - } - if (NULL == auth_obj) - { - GNUNET_break (0); - return NULL; - } - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("donau_url", - charity->charity_url), - GNUNET_JSON_pack_uint64 ("charity_id", - charity->charity_id) - ); - - dph = GNUNET_new (struct TALER_MERCHANT_DonauInstancePostHandle); - dph->ctx = ctx; - dph->cb = cb; - dph->cb_cls = cb_cls; - dph->url = TALER_url_join (backend_url, - "private/donau", - NULL); - - if (NULL == dph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to construct request URL\n"); - json_decref (req_obj); - GNUNET_free (dph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (dph->url); - if (GNUNET_OK != TALER_curl_easy_post (&dph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (dph); - return NULL; - } - - json_decref (req_obj); - - dph->job = GNUNET_CURL_job_add2 (ctx, - eh, - dph->post_ctx.headers, - &handle_post_donau_finished, - dph); - } - return dph; -} - - -void -TALER_MERCHANT_donau_instances_post_cancel ( - struct TALER_MERCHANT_DonauInstancePostHandle *dph) -{ - if (NULL != dph->job) - { - GNUNET_CURL_job_cancel (dph->job); - dph->job = NULL; - } - TALER_curl_easy_post_finished (&dph->post_ctx); - GNUNET_free (dph->url); - GNUNET_free (dph); -} - - -/* end of merchant_api_post_donau_instance.c */ diff --git a/src/lib/merchant_api_post_instance_auth.c b/src/lib/merchant_api_post_instance_auth.c @@ -1,227 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_instance_auth.c - * @brief Implementation of the POST /instance/$ID/private/auth request - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /instances/$ID/private/auth operation. - */ -struct TALER_MERCHANT_InstanceAuthPostHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_InstanceAuthPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /instances/$ID/private/auth request. - * - * @param cls the `struct TALER_MERCHANT_InstanceAuthPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_instance_auth_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_InstanceAuthPostHandle *iaph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - iaph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /instances/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - /* happens if the auth token is malformed */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - iaph->cb (iaph->cb_cls, - &hr); - TALER_MERCHANT_instance_auth_post_cancel (iaph); -} - - -struct TALER_MERCHANT_InstanceAuthPostHandle * -TALER_MERCHANT_instance_auth_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - const char *auth_password, - TALER_MERCHANT_InstanceAuthPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_InstanceAuthPostHandle *iaph; - json_t *req_obj; - - iaph = GNUNET_new (struct TALER_MERCHANT_InstanceAuthPostHandle); - iaph->ctx = ctx; - iaph->cb = cb; - iaph->cb_cls = cb_cls; - if (NULL != instance_id) - { - char *path; - - GNUNET_asprintf (&path, - "management/instances/%s/auth", - instance_id); - iaph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - else - { - /* backend_url is already identifying the instance */ - iaph->url = TALER_url_join (backend_url, - "private/auth", - NULL); - } - if (NULL == iaph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (iaph); - return NULL; - } - if (NULL == auth_password) - { - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("method", - "external")); - } - else - { - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("method", - "token"), - GNUNET_JSON_pack_string ("password", - auth_password)); - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - iaph->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (iaph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&iaph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (iaph->url); - GNUNET_free (iaph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_POST)); - iaph->job = GNUNET_CURL_job_add2 (ctx, - eh, - iaph->post_ctx.headers, - &handle_post_instance_auth_finished, - iaph); - } - return iaph; -} - - -void -TALER_MERCHANT_instance_auth_post_cancel ( - struct TALER_MERCHANT_InstanceAuthPostHandle *iaph) -{ - if (NULL != iaph->job) - GNUNET_CURL_job_cancel (iaph->job); - TALER_curl_easy_post_finished (&iaph->post_ctx); - GNUNET_free (iaph->url); - GNUNET_free (iaph); -} diff --git a/src/lib/merchant_api_post_instance_token.c b/src/lib/merchant_api_post_instance_token.c @@ -1,235 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_instance_token.c - * @brief Implementation of the POST /instance/$ID/private/token request - * @author Martin Schanzenbach - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /instances/$ID/private/token operation. - */ -struct TALER_MERCHANT_InstanceTokenPostHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_InstanceTokenPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /instances/$ID/private/token request. - * - * @param cls the `struct TALER_MERCHANT_InstanceTokenPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_instance_token_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_InstanceTokenPostHandle *itph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - itph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /instances/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - break; - case MHD_HTTP_BAD_REQUEST: - /* happens if the auth token is malformed */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - default: - /* unexpected response code */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - itph->cb (itph->cb_cls, - &hr); - TALER_MERCHANT_instance_token_post_cancel (itph); -} - - -struct TALER_MERCHANT_InstanceTokenPostHandle * -TALER_MERCHANT_instance_token_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - const char *scope, - struct GNUNET_TIME_Relative duration, - bool refreshable, - TALER_MERCHANT_InstanceTokenPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_InstanceTokenPostHandle *itph; - json_t *req_obj; - - itph = GNUNET_new (struct TALER_MERCHANT_InstanceTokenPostHandle); - itph->ctx = ctx; - itph->cb = cb; - itph->cb_cls = cb_cls; - if (NULL != instance_id) - { - char *path; - - GNUNET_asprintf (&path, - "instances/%s/private/token", - instance_id); - itph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - else - { - /* backend_url is already identifying the instance */ - itph->url = TALER_url_join (backend_url, - "private/token", - NULL); - } - if (NULL == itph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (itph); - return NULL; - } - if (NULL == scope) - { - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_time_rel ("duration", - duration), - GNUNET_JSON_pack_bool ("refreshable", - refreshable), - GNUNET_JSON_pack_string ("scope", - "readonly")); - } - else - { - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_time_rel ("duration", - duration), - GNUNET_JSON_pack_bool ("refreshable", - refreshable), - GNUNET_JSON_pack_string ("scope", - scope)); - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - itph->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (itph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&itph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (itph->url); - GNUNET_free (itph); - return NULL; - } - json_decref (req_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - MHD_HTTP_METHOD_POST)); - itph->job = GNUNET_CURL_job_add2 (ctx, - eh, - itph->post_ctx.headers, - &handle_post_instance_token_finished, - itph); - } - return itph; -} - - -void -TALER_MERCHANT_instance_token_post_cancel ( - struct TALER_MERCHANT_InstanceTokenPostHandle *itph) -{ - if (NULL != itph->job) - GNUNET_CURL_job_cancel (itph->job); - TALER_curl_easy_post_finished (&itph->post_ctx); - GNUNET_free (itph->url); - GNUNET_free (itph); -} diff --git a/src/lib/merchant_api_post_instances.c b/src/lib/merchant_api_post_instances.c @@ -1,280 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_instances.c - * @brief Implementation of the POST /instances request - * of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> -#include <taler/taler_kyclogic_lib.h> - - -/** - * Handle for a POST /instances/$ID operation. - */ -struct TALER_MERCHANT_InstancesPostHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_InstancesPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP POST /instances request. - * - * @param cls the `struct TALER_MERCHANT_InstancesPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_instances_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_InstancesPostHandle *iph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - iph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /management/instances completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - iph->cb (iph->cb_cls, - &hr); - TALER_MERCHANT_instances_post_cancel (iph); -} - - -struct TALER_MERCHANT_InstancesPostHandle * -TALER_MERCHANT_instances_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *instance_id, - const char *name, - const json_t *address, - const json_t *jurisdiction, - bool use_stefan, - struct GNUNET_TIME_Relative default_wire_transfer_delay, - struct GNUNET_TIME_Relative default_pay_delay, - struct GNUNET_TIME_Relative default_refund_delay, - const char *auth_password, - TALER_MERCHANT_InstancesPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_InstancesPostHandle *iph; - json_t *req_obj; - json_t *auth_obj; - - if (NULL != auth_password) - { - auth_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("method", - "token"), - GNUNET_JSON_pack_string ("password", - auth_password)); - } - else - { - auth_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("method", - "external")); - } - if (NULL == auth_obj) - { - GNUNET_break (0); - return NULL; - } - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("id", - instance_id), - GNUNET_JSON_pack_string ("name", - name), - GNUNET_JSON_pack_object_incref ("address", - (json_t *) address), - GNUNET_JSON_pack_object_incref ("jurisdiction", - (json_t *) jurisdiction), - GNUNET_JSON_pack_bool ("use_stefan", - use_stefan), - GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay", - default_wire_transfer_delay), - GNUNET_JSON_pack_time_rel ("default_pay_delay", - default_pay_delay), - GNUNET_JSON_pack_time_rel ("default_refund_delay", - default_refund_delay), - /* FIXME: add eventually to arguments when we transform the API... */ - GNUNET_JSON_pack_time_rounder_interval ( - "default_wire_transfer_rounding_interval", - GNUNET_TIME_RI_NONE), - GNUNET_JSON_pack_object_steal ("auth", - auth_obj)); - iph = GNUNET_new (struct TALER_MERCHANT_InstancesPostHandle); - iph->ctx = ctx; - iph->cb = cb; - iph->cb_cls = cb_cls; - iph->url = TALER_url_join (backend_url, - "management/instances", - NULL); - if (NULL == iph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (iph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (iph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&iph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (iph); - return NULL; - } - json_decref (req_obj); - iph->job = GNUNET_CURL_job_add2 (ctx, - eh, - iph->post_ctx.headers, - &handle_post_instances_finished, - iph); - } - return iph; -} - - -void -TALER_MERCHANT_instances_post_cancel ( - struct TALER_MERCHANT_InstancesPostHandle *iph) -{ - if (NULL != iph->job) - { - GNUNET_CURL_job_cancel (iph->job); - iph->job = NULL; - } - TALER_curl_easy_post_finished (&iph->post_ctx); - GNUNET_free (iph->url); - GNUNET_free (iph); -} - - -/* end of merchant_api_post_instances.c */ diff --git a/src/lib/merchant_api_post_order_abort.c b/src/lib/merchant_api_post_order_abort.c @@ -1,436 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_order_abort.c - * @brief Implementation of the POST /orders/$ID/abort request - * of the merchant's HTTP API - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_exchange_service.h> -#include <taler/taler_curl_lib.h> - - -/** - * Maximum number of refunds we return. - */ -#define MAX_REFUNDS 1024 - - -/** - * @brief An abort Handle - */ -struct TALER_MERCHANT_OrderAbortHandle -{ - /** - * Hash of the contract. - */ - struct TALER_PrivateContractHashP h_contract_terms; - - /** - * Public key of the merchant. - */ - struct TALER_MerchantPublicKeyP merchant_pub; - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_AbortCallback abort_cb; - - /** - * Closure for @a abort_cb. - */ - void *abort_cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * The coins we are aborting on. - */ - struct TALER_MERCHANT_AbortCoin *coins; - - /** - * Number of @e coins we are paying with. - */ - unsigned int num_coins; - -}; - - -/** - * Check that the response for an abort is well-formed, - * and call the application callback with the result if it is - * OK. Otherwise returns #GNUNET_SYSERR. - * - * @param oah handle to operation that created the reply - * @param[in] ar abort response, partially initialized - * @param json the reply to parse - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah, - struct TALER_MERCHANT_AbortResponse *ar, - const json_t *json) -{ - const json_t *refunds; - unsigned int num_refunds; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("refunds", - &refunds), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - num_refunds = (unsigned int) json_array_size (refunds); - if ( (json_array_size (refunds) != (size_t) num_refunds) || - (num_refunds > MAX_REFUNDS) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - { - struct TALER_MERCHANT_AbortedCoin res[GNUNET_NZL (num_refunds)]; - - for (unsigned int i = 0; i<num_refunds; i++) - { - json_t *refund = json_array_get (refunds, i); - uint32_t exchange_status; - struct GNUNET_JSON_Specification spec_es[] = { - GNUNET_JSON_spec_uint32 ("exchange_status", - &exchange_status), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (refund, - spec_es, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (MHD_HTTP_OK == exchange_status) - { - struct GNUNET_JSON_Specification spec_detail[] = { - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &res[i].exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &res[i].exchange_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (refund, - spec_detail, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - TALER_exchange_online_refund_confirmation_verify ( - &oah->h_contract_terms, - &oah->coins[i].coin_pub, - &oah->merchant_pub, - 0, /* transaction id */ - &oah->coins[i].amount_with_fee, - &res[i].exchange_pub, - &res[i].exchange_sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - } - ar->details.ok.merchant_pub = &oah->merchant_pub; - ar->details.ok.num_aborts = num_refunds; - ar->details.ok.aborts = res; - oah->abort_cb (oah->abort_cb_cls, - ar); - oah->abort_cb = NULL; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * abort request. - * - * @param cls the `struct TALER_MERCHANT_OrderAbortHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_abort_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderAbortHandle *oah = cls; - const json_t *json = response; - struct TALER_MERCHANT_AbortResponse ar = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - oah->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "/pay completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK == - check_abort_refund (oah, - &ar, - json)) - { - TALER_MERCHANT_order_abort_cancel (oah); - return; - } - ar.hr.http_status = 0; - ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_BAD_REQUEST: - ar.hr.ec = TALER_JSON_get_error_code (json); - ar.hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us or the - merchant is buggy (or API version conflict); just - pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - ar.hr.ec = TALER_JSON_get_error_code (json); - ar.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - ar.hr.ec = TALER_JSON_get_error_code (json); - ar.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_REQUEST_TIMEOUT: - ar.hr.ec = TALER_JSON_get_error_code (json); - ar.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says one of - the signatures is invalid; as we checked them, - this should never happen, we should pass the JSON - reply to the application */ - break; - case MHD_HTTP_PRECONDITION_FAILED: - /* Our *payment* already succeeded fully. */ - ar.hr.ec = TALER_JSON_get_error_code (json); - ar.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - ar.hr.ec = TALER_JSON_get_error_code (json); - ar.hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - case MHD_HTTP_BAD_GATEWAY: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &ar.hr); - /* Nothing really to verify, the merchant is blaming the exchange. - We should pass the JSON reply to the application */ - break; - default: - /* unexpected response code */ - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &ar.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) ar.hr.ec); - GNUNET_break_op (0); - break; - } - oah->abort_cb (oah->abort_cb_cls, - &ar); - TALER_MERCHANT_order_abort_cancel (oah); -} - - -struct TALER_MERCHANT_OrderAbortHandle * -TALER_MERCHANT_order_abort ( - struct GNUNET_CURL_Context *ctx, - const char *merchant_url, - const char *order_id, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_PrivateContractHashP *h_contract, - unsigned int num_coins, - const struct TALER_MERCHANT_AbortCoin coins[static num_coins], - TALER_MERCHANT_AbortCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OrderAbortHandle *oah; - json_t *abort_obj; - json_t *j_coins; - - j_coins = json_array (); - if (NULL == j_coins) - { - GNUNET_break (0); - return NULL; - } - for (unsigned int i = 0; i<num_coins; i++) - { - const struct TALER_MERCHANT_AbortCoin *ac = &coins[i]; - json_t *j_coin; - - /* create JSON for this coin */ - j_coin = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("coin_pub", - &ac->coin_pub), - /* FIXME: no longer needed since **v18**, remove eventually! */ - TALER_JSON_pack_amount ("contribution", - &ac->amount_with_fee), - GNUNET_JSON_pack_string ("exchange_url", - ac->exchange_url)); - if (0 != - json_array_append_new (j_coins, - j_coin)) - { - GNUNET_break (0); - json_decref (j_coins); - return NULL; - } - } - abort_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("coins", - j_coins), - GNUNET_JSON_pack_data_auto ("h_contract", - h_contract)); - oah = GNUNET_new (struct TALER_MERCHANT_OrderAbortHandle); - oah->h_contract_terms = *h_contract; - oah->merchant_pub = *merchant_pub; - oah->ctx = ctx; - oah->abort_cb = cb; - oah->abort_cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "orders/%s/abort", - order_id); - oah->url = TALER_url_join (merchant_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == oah->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (abort_obj); - GNUNET_free (oah); - return NULL; - } - oah->num_coins = num_coins; - oah->coins = GNUNET_new_array (num_coins, - struct TALER_MERCHANT_AbortCoin); - GNUNET_memcpy (oah->coins, - coins, - num_coins * sizeof (struct TALER_MERCHANT_AbortCoin)); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (oah->url); - if (GNUNET_OK != - TALER_curl_easy_post (&oah->post_ctx, - eh, - abort_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (abort_obj); - GNUNET_free (oah); - return NULL; - } - json_decref (abort_obj); - oah->job = GNUNET_CURL_job_add2 (ctx, - eh, - oah->post_ctx.headers, - &handle_abort_finished, - oah); - } - return oah; -} - - -void -TALER_MERCHANT_order_abort_cancel ( - struct TALER_MERCHANT_OrderAbortHandle *oah) -{ - if (NULL != oah->job) - { - GNUNET_CURL_job_cancel (oah->job); - oah->job = NULL; - } - TALER_curl_easy_post_finished (&oah->post_ctx); - GNUNET_free (oah->coins); - GNUNET_free (oah->url); - GNUNET_free (oah); -} - - -/* end of merchant_api_post_order_abort.c */ diff --git a/src/lib/merchant_api_post_order_claim.c b/src/lib/merchant_api_post_order_claim.c @@ -1,242 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with TALER; see the file COPYING.LGPL. If not, - see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_order_claim.c - * @brief Implementation of POST /orders/$ID/claim - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - - -/** - * Structure representing a POST /orders/$ID/claim operation. - */ -struct TALER_MERCHANT_OrderClaimHandle -{ - /** - * Full URL, includes "/orders/$ID/claim". - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OrderClaimCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * Function called when we're done processing the - * POST /orders/$ID/claim request. - * - * @param cls the `struct TALER_MERCHANT_OrderClaimHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, should be NULL - */ -static void -handle_post_order_claim_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderClaimHandle *och = cls; - const json_t *json = response; - struct TALER_MERCHANT_OrderClaimResponse ocr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_object_const ( - "contract_terms", - &ocr.details.ok.contract_terms), - GNUNET_JSON_spec_fixed_auto ( - "sig", - &ocr.details.ok.sig), - GNUNET_JSON_spec_end () - }; - - och->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Order claimed with status %u\n", - (unsigned int) response_code); - - if (MHD_HTTP_OK != response_code) - { - ocr.hr.ec = TALER_JSON_get_error_code (json); - ocr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Proposal lookup failed with HTTP status code %u/%d\n", - (unsigned int) response_code, - (int) ocr.hr.ec); - och->cb (och->cb_cls, - &ocr); - TALER_MERCHANT_order_claim_cancel (och); - return; - } - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Claiming order failed: could not parse JSON response\n"); - GNUNET_break_op (0); - ocr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - ocr.hr.http_status = 0; - och->cb (och->cb_cls, - &ocr); - TALER_MERCHANT_order_claim_cancel (och); - return; - } - - if (GNUNET_OK != - TALER_JSON_contract_hash (ocr.details.ok.contract_terms, - &ocr.details.ok.h_contract_terms)) - { - GNUNET_break (0); - ocr.hr.ec = TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE; - ocr.hr.http_status = 0; - GNUNET_JSON_parse_free (spec); - och->cb (och->cb_cls, - &ocr); - TALER_MERCHANT_order_claim_cancel (och); - return; - } - och->cb (och->cb_cls, - &ocr); - GNUNET_JSON_parse_free (spec); - TALER_MERCHANT_order_claim_cancel (och); -} - - -struct TALER_MERCHANT_OrderClaimHandle * -TALER_MERCHANT_order_claim (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *order_id, - const struct GNUNET_CRYPTO_EddsaPublicKey *nonce, - const struct TALER_ClaimTokenP *claim_token, - TALER_MERCHANT_OrderClaimCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OrderClaimHandle *och; - json_t *req_obj; - - if (NULL == order_id) - { - GNUNET_break (0); - return NULL; - } - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("nonce", - nonce), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_data_auto ("token", - claim_token))); - och = GNUNET_new (struct TALER_MERCHANT_OrderClaimHandle); - och->ctx = ctx; - och->cb = cb; - och->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "orders/%s/claim", - order_id); - och->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == och->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (och); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Claiming order at %s\n", - och->url); - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (och->url); - GNUNET_assert (GNUNET_OK == - TALER_curl_easy_post (&och->post_ctx, - eh, - req_obj)); - json_decref (req_obj); - och->job = GNUNET_CURL_job_add2 (ctx, - eh, - och->post_ctx.headers, - &handle_post_order_claim_finished, - och); - GNUNET_assert (NULL != och->job); - } - return och; -} - - -void -TALER_MERCHANT_order_claim_cancel (struct TALER_MERCHANT_OrderClaimHandle *och) -{ - if (NULL != och->job) - { - GNUNET_CURL_job_cancel (och->job); - och->job = NULL; - } - TALER_curl_easy_post_finished (&och->post_ctx); - GNUNET_free (och->url); - GNUNET_free (och); -} - - -/* end of merchant_api_post_order_claim.c */ diff --git a/src/lib/merchant_api_post_order_paid.c b/src/lib/merchant_api_post_order_paid.c @@ -1,271 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_order_paid.c - * @brief Implementation of the POST /order/$ID/paid request - * of the merchant's HTTP API - * @author Jonathan Buchanan - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_exchange_service.h> -#include <taler/taler_curl_lib.h> - - -/** - * @brief Handle to a POST /orders/$ID/paid operation at a merchant. - */ -struct TALER_MERCHANT_OrderPaidHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OrderPaidCallback paid_cb; - - /** - * Closure for @a paid_cb. - */ - void *paid_cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /paid request. - * - * @param cls the `struct TALER_MERCHANT_OrderPaidHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_paid_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderPaidHandle *oph = cls; - const json_t *json = response; - struct TALER_MERCHANT_OrderPaidResponse opr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - oph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "/paid completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - bool refunded; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_bool ("refunded", - &refunded), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (opr.hr.reply, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - break; - } - break; - case MHD_HTTP_BAD_REQUEST: - opr.hr.ec = TALER_JSON_get_error_code (json); - opr.hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - opr.hr.ec = TALER_JSON_get_error_code (json); - opr.hr.hint = TALER_JSON_get_error_hint (json); - /* The signature provided was invalid */ - break; - case MHD_HTTP_NOT_FOUND: - opr.hr.ec = TALER_JSON_get_error_code (json); - opr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_CONFLICT: - opr.hr.ec = TALER_JSON_get_error_code (json); - opr.hr.hint = TALER_JSON_get_error_hint (json); - /* The hashed contract terms don't match with the order_id. */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - opr.hr.ec = TALER_JSON_get_error_code (json); - opr.hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - case MHD_HTTP_SERVICE_UNAVAILABLE: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &opr.hr); - /* Exchange couldn't respond properly; the retry is - left to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &opr.hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) opr.hr.ec); - GNUNET_break_op (0); - break; - } - oph->paid_cb (oph->paid_cb_cls, - &opr); - TALER_MERCHANT_order_paid_cancel (oph); -} - - -struct TALER_MERCHANT_OrderPaidHandle * -TALER_MERCHANT_order_paid ( - struct GNUNET_CURL_Context *ctx, - const char *merchant_url, - const char *order_id, - const char *session_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - const struct GNUNET_HashCode *wallet_data_hash, - const struct TALER_MerchantSignatureP *merchant_sig, - TALER_MERCHANT_OrderPaidCallback paid_cb, - void *paid_cb_cls) -{ - struct TALER_MERCHANT_OrderPaidHandle *oph; - json_t *req_obj; - - (void) wallet_data_hash; - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("sig", - merchant_sig), - GNUNET_JSON_pack_data_auto ("h_contract", - h_contract_terms), - GNUNET_JSON_pack_string ("session_id", - session_id)); - oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle); - oph->ctx = ctx; - oph->paid_cb = paid_cb; - oph->paid_cb_cls = paid_cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "orders/%s/paid", - order_id); - oph->url = TALER_url_join (merchant_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == oph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (oph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (oph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&oph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (oph); - return NULL; - } - json_decref (req_obj); - oph->job = GNUNET_CURL_job_add2 (ctx, - eh, - oph->post_ctx.headers, - &handle_paid_finished, - oph); - } - return oph; -} - - -void -TALER_MERCHANT_order_paid_cancel ( - struct TALER_MERCHANT_OrderPaidHandle *oph) -{ - if (NULL != oph->job) - { - GNUNET_CURL_job_cancel (oph->job); - oph->job = NULL; - } - TALER_curl_easy_post_finished (&oph->post_ctx); - GNUNET_free (oph->url); - GNUNET_free (oph); -} - - -/* end of merchant_api_post_order_paid.c */ diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c @@ -1,790 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_order_pay.c - * @brief Implementation of the POST /order/$ID/pay request - * of the merchant's HTTP API - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <gnunet/gnunet_common.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <stdio.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_exchange_service.h> -#include <taler/taler_curl_lib.h> - - -/** - * @brief A Pay Handle - */ -struct TALER_MERCHANT_OrderPayHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result in "pay" @e mode. - */ - TALER_MERCHANT_OrderPayCallback pay_cb; - - /** - * Closure for @a pay_cb. - */ - void *pay_cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * The coins we are paying with. - */ - struct TALER_MERCHANT_PaidCoin *coins; - - /** - * Hash of the contract we are paying, set - * if @e am_wallet is true. - */ - struct TALER_PrivateContractHashP h_contract_terms; - - /** - * Public key of the merchant (instance) being paid, set - * if @e am_wallet is true. - */ - struct TALER_MerchantPublicKeyP merchant_pub; - - /** - * JSON with the full reply, used during async - * processing. - */ - json_t *full_reply; - - /** - * Pointer into @e coins array for the coin that - * created a conflict (that we are checking). - */ - const struct TALER_MERCHANT_PaidCoin *error_pc; - - /** - * Coin history that proves a conflict. - */ - json_t *error_history; - - /** - * Number of @e coins we are paying with. - */ - unsigned int num_coins; - - /** - * Set to true if this is the wallet API and we have - * initialized @e h_contract_terms and @e merchant_pub. - */ - bool am_wallet; - -}; - - -/** - * Parse blindly signed output tokens from response. - * - * @param token_sigs the JSON array with the token signatures. Can be NULL. - * @param tokens where to store the parsed tokens. - * @param num_tokens where to store the length of the @a tokens array. - */ -static enum GNUNET_GenericReturnValue -parse_tokens (const json_t *token_sigs, - struct TALER_MERCHANT_OutputToken **tokens, - unsigned int *num_tokens) -{ - GNUNET_array_grow (*tokens, - *num_tokens, - json_array_size (token_sigs)); - - for (unsigned int i = 0; i<(*num_tokens); i++) - { - struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i]; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_blinded_token_issue_sig ("blind_sig", - &token->blinded_sig), - GNUNET_JSON_spec_end () - }; - const json_t *jtoken - = json_array_get (token_sigs, - i); - - if (NULL == jtoken) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_JSON_parse (jtoken, - spec, - NULL, NULL)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - } - - return GNUNET_YES; -} - - -/** - * Function called when we're done processing the - * HTTP /pay request. - * - * @param cls the `struct TALER_MERCHANT_Pay` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_pay_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderPayHandle *oph = cls; - const json_t *json = response; - struct TALER_MERCHANT_PayResponse pr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received /pay response with status code %u\n", - (unsigned int) response_code); - - json_dumpf (json, - stderr, - JSON_INDENT (2)); - - oph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "/pay completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (oph->am_wallet) - { - const json_t *token_sigs = NULL; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("sig", - &pr.details.ok.merchant_sig), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("pos_confirmation", - &pr.details.ok.pos_confirmation), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("token_sigs", - &token_sigs), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - pr.hr.http_status = 0; - pr.hr.hint = "sig field missing in response"; - break; - } - - if (GNUNET_OK != - parse_tokens (token_sigs, - &pr.details.ok.tokens, - &pr.details.ok.num_tokens)) - { - GNUNET_break_op (0); - pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - pr.hr.http_status = 0; - pr.hr.hint = "failed to parse token_sigs field in response"; - break; - } - - if (GNUNET_OK != - TALER_merchant_pay_verify (&oph->h_contract_terms, - &oph->merchant_pub, - &pr.details.ok.merchant_sig)) - { - GNUNET_break_op (0); - pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - pr.hr.http_status = 0; - pr.hr.hint = "signature invalid"; - } - } - break; - /* Tolerating Not Acceptable because sometimes - * - especially in tests - we might want to POST - * coins one at a time. */ - case MHD_HTTP_NOT_ACCEPTABLE: - pr.hr.ec = TALER_JSON_get_error_code (json); - pr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_BAD_REQUEST: - pr.hr.ec = TALER_JSON_get_error_code (json); - pr.hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_PAYMENT_REQUIRED: - /* was originally paid, but then refunded */ - pr.hr.ec = TALER_JSON_get_error_code (json); - pr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_FORBIDDEN: - pr.hr.ec = TALER_JSON_get_error_code (json); - pr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - pr.hr.ec = TALER_JSON_get_error_code (json); - pr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_REQUEST_TIMEOUT: - pr.hr.ec = TALER_JSON_get_error_code (json); - pr.hr.hint = TALER_JSON_get_error_hint (json); - /* The merchant couldn't generate a timely response, likely because - it itself waited too long on the exchange. - Pass on to application. */ - break; - case MHD_HTTP_CONFLICT: - TALER_MERCHANT_parse_error_details_ (json, - MHD_HTTP_CONFLICT, - &pr.hr); - break; - case MHD_HTTP_GONE: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &pr.hr); - /* The merchant says we are too late, the offer has expired or some - denomination key of a coin involved has expired. - Might be a disagreement in timestamps? Still, pass on to application. */ - break; - case MHD_HTTP_PRECONDITION_FAILED: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &pr.hr); - /* Nothing really to verify, the merchant is blaming us for failing to - satisfy some constraint (likely it does not like our exchange because - of some disagreement on the PKI). We should pass the JSON reply to the - application */ - break; - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - { - json_t *ebus = json_object_get (json, - "exchange_base_urls"); - if (NULL == ebus) - { - GNUNET_break_op (0); - pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - pr.hr.http_status = 0; - pr.hr.hint = "failed to parse exchange_base_urls field in response"; - break; - } - { - size_t alen = json_array_size (ebus); - const char *ebua[GNUNET_NZL (alen)]; - size_t idx; - json_t *jebu; - bool ok = true; - - GNUNET_assert (alen <= UINT_MAX); - json_array_foreach (ebus, idx, jebu) - { - ebua[idx] = json_string_value (jebu); - if (NULL == ebua[idx]) - { - GNUNET_break_op (0); - pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - pr.hr.http_status = 0; - pr.hr.hint = "non-string value in exchange_base_urls in response"; - ok = false; - break; - } - } - if (! ok) - break; - pr.details.unavailable_for_legal_reasons.num_exchanges - = (unsigned int) alen; - pr.details.unavailable_for_legal_reasons.exchanges - = ebua; - oph->pay_cb (oph->pay_cb_cls, - &pr); - TALER_MERCHANT_order_pay_cancel (oph); - return; - } - } - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &pr.hr); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - case MHD_HTTP_BAD_GATEWAY: - /* Nothing really to verify, the merchant is blaming the exchange. - We should pass the JSON reply to the application */ - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &pr.hr); - break; - case MHD_HTTP_SERVICE_UNAVAILABLE: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &pr.hr); - /* Exchange couldn't respond properly; the retry is - left to the application */ - break; - case MHD_HTTP_GATEWAY_TIMEOUT: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &pr.hr); - /* Exchange couldn't respond in a timely fashion; - the retry is left to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &pr.hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) pr.hr.ec); - GNUNET_break_op (0); - break; - } - oph->pay_cb (oph->pay_cb_cls, - &pr); - TALER_MERCHANT_order_pay_cancel (oph); -} - - -struct TALER_MERCHANT_OrderPayHandle * -TALER_MERCHANT_order_pay_frontend ( - struct GNUNET_CURL_Context *ctx, - const char *merchant_url, - const char *order_id, - const char *session_id, - const json_t *wallet_data, - unsigned int num_coins, - const struct TALER_MERCHANT_PaidCoin coins[static num_coins], - unsigned int num_tokens, - const struct TALER_MERCHANT_UsedToken *tokens, - json_t *j_output_tokens, // FIXME: not used, remove? - TALER_MERCHANT_OrderPayCallback pay_cb, - void *pay_cb_cls) -{ - struct TALER_MERCHANT_OrderPayHandle *oph; - json_t *pay_obj; - json_t *j_coins; - json_t *j_tokens = NULL; - CURL *eh; - struct TALER_Amount total_fee; - struct TALER_Amount total_amount; - - j_coins = json_array (); - GNUNET_assert (NULL != j_coins); - for (unsigned int i = 0; i<num_coins; i++) - { - json_t *j_coin; - const struct TALER_MERCHANT_PaidCoin *pc = &coins[i]; - struct TALER_Amount fee; - struct TALER_DenominationHashP denom_hash; - - if (0 > - TALER_amount_subtract (&fee, - &pc->amount_with_fee, - &pc->amount_without_fee)) - { - /* Integer underflow, fee larger than total amount? - This should not happen (client violated API!) */ - GNUNET_break (0); - json_decref (j_coins); - return NULL; - } - if (0 == i) - { - total_fee = fee; - total_amount = pc->amount_with_fee; - } - else - { - if ( (0 > - TALER_amount_add (&total_fee, - &total_fee, - &fee)) || - (0 > - TALER_amount_add (&total_amount, - &total_amount, - &pc->amount_with_fee)) ) - { - /* integer overflow */ - GNUNET_break (0); - json_decref (j_coins); - return NULL; - } - } - - TALER_denom_pub_hash (&pc->denom_pub, - &denom_hash); - /* create JSON for this coin */ - j_coin = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("contribution", - &pc->amount_with_fee), - GNUNET_JSON_pack_data_auto ("coin_pub", - &pc->coin_pub), - GNUNET_JSON_pack_string ("exchange_url", - pc->exchange_url), - GNUNET_JSON_pack_data_auto ("h_denom", - &denom_hash), - TALER_JSON_pack_denom_sig ("ub_sig", - &pc->denom_sig), - GNUNET_JSON_pack_data_auto ("coin_sig", - &pc->coin_sig)); - if (0 != - json_array_append_new (j_coins, - j_coin)) - { - GNUNET_break (0); - json_decref (j_coins); - return NULL; - } - } - - if (0 < num_tokens) - { - j_tokens = json_array (); - GNUNET_assert (NULL != j_tokens); - for (unsigned int i = 0; i<num_tokens; i++) - { - json_t *j_token; - const struct TALER_MERCHANT_UsedToken *ut = &tokens[i]; - - j_token = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("token_sig", - &ut->token_sig), - GNUNET_JSON_pack_data_auto ("token_pub", - &ut->token_pub), - GNUNET_JSON_pack_data_auto ("h_issue", - &ut->issue_pub.public_key->pub_key_hash), - TALER_JSON_pack_token_issue_sig ("ub_sig", - &ut->ub_sig)); - if (0 != - json_array_append_new (j_tokens, - j_token)) - { - GNUNET_break (0); - json_decref (j_tokens); - return NULL; - } - } - } - - pay_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("coins", - j_coins), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_steal ("tokens", - j_tokens)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("wallet_data", - (json_t *) wallet_data)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("session_id", - session_id))); - - json_dumpf (pay_obj, - stderr, - JSON_INDENT (2)); - - oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle); - oph->ctx = ctx; - oph->pay_cb = pay_cb; - oph->pay_cb_cls = pay_cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "orders/%s/pay", - order_id); - oph->url = TALER_url_join (merchant_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == oph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (pay_obj); - GNUNET_free (oph); - return NULL; - } - oph->num_coins = num_coins; - oph->coins = GNUNET_new_array (num_coins, - struct TALER_MERCHANT_PaidCoin); - GNUNET_memcpy (oph->coins, - coins, - num_coins * sizeof (struct TALER_MERCHANT_PaidCoin)); - - eh = TALER_MERCHANT_curl_easy_get_ (oph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&oph->post_ctx, - eh, - pay_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (pay_obj); - GNUNET_free (oph->url); - GNUNET_free (oph); - return NULL; - } - json_decref (pay_obj); - oph->job = GNUNET_CURL_job_add2 (ctx, - eh, - oph->post_ctx.headers, - &handle_pay_finished, - oph); - return oph; -} - - -struct TALER_MERCHANT_OrderPayHandle * -TALER_MERCHANT_order_pay ( - struct GNUNET_CURL_Context *ctx, - const char *merchant_url, - const char *session_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - int choice_index, - const struct TALER_Amount *amount, - const struct TALER_Amount *max_fee, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_MerchantSignatureP *merchant_sig, - struct GNUNET_TIME_Timestamp timestamp, - struct GNUNET_TIME_Timestamp refund_deadline, - struct GNUNET_TIME_Timestamp pay_deadline, - const struct TALER_MerchantWireHashP *h_wire, - const char *order_id, - unsigned int num_coins, - const struct TALER_MERCHANT_PayCoin coins[static num_coins], - unsigned int num_tokens, - const struct TALER_MERCHANT_UseToken *tokens, - unsigned int num_output_tokens, - const struct TALER_MERCHANT_OutputToken *output_tokens, - TALER_MERCHANT_OrderPayCallback pay_cb, - void *pay_cb_cls) -{ - json_t *j_output_tokens = NULL; - const json_t *wallet_data = NULL; - struct GNUNET_HashCode wallet_data_hash; - - if (GNUNET_YES != - TALER_amount_cmp_currency (amount, - max_fee)) - { - GNUNET_break (0); - return NULL; - } - if ((0 < num_tokens || 0 < num_output_tokens) && 0 > choice_index) - { - /* Tokens (input or output) require a valid choice_index to be set. - Only contracts with coices can use or issue tokens. */ - GNUNET_break (0); - return NULL; - } - if (0 < num_output_tokens) - { - /* Build token envelopes json array. */ - j_output_tokens = json_array (); - GNUNET_assert (NULL != j_output_tokens); - for (unsigned int i = 0; i<num_output_tokens; i++) - { - json_t *j_token_ev; - const struct TALER_MERCHANT_OutputToken *ev = &output_tokens[i]; - - j_token_ev = GNUNET_JSON_PACK ( - TALER_JSON_pack_token_envelope (NULL, - &ev->envelope)); - - if (0 != - json_array_append_new (j_output_tokens, - j_token_ev)) - { - GNUNET_break (0); - json_decref (j_output_tokens); - return NULL; - } - } - } - if (0 <= choice_index) - { - wallet_data = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_int64 ("choice_index", - choice_index), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_incref ("tokens_evs", - j_output_tokens))); - TALER_json_hash (wallet_data, - &wallet_data_hash); - } - { - struct TALER_MERCHANT_PaidCoin pc[num_coins]; - struct TALER_MERCHANT_UsedToken ut[num_tokens]; - - for (unsigned int i = 0; i<num_coins; i++) - { - const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv. - struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub. - struct TALER_Amount fee; - struct TALER_DenominationHashP h_denom_pub; - - if (0 > - TALER_amount_subtract (&fee, - &coin->amount_with_fee, - &coin->amount_without_fee)) - { - /* Integer underflow, fee larger than total amount? - This should not happen (client violated API!) */ - GNUNET_break (0); - return NULL; - } - TALER_denom_pub_hash (&coin->denom_pub, - &h_denom_pub); - TALER_wallet_deposit_sign (&coin->amount_with_fee, - &fee, - h_wire, - h_contract_terms, - (NULL != wallet_data) - ? &wallet_data_hash - : NULL, - coin->h_age_commitment, - NULL /* h_extensions! */, - &h_denom_pub, - timestamp, - merchant_pub, - refund_deadline, - &coin->coin_priv, - &p->coin_sig); - p->denom_pub = coin->denom_pub; - p->denom_sig = coin->denom_sig; - p->denom_value = coin->denom_value; - GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv, - &p->coin_pub.eddsa_pub); - p->amount_with_fee = coin->amount_with_fee; - p->amount_without_fee = coin->amount_without_fee; - p->exchange_url = coin->exchange_url; - } - for (unsigned int i = 0; i<num_tokens; i++) - { - const struct TALER_MERCHANT_UseToken *token = &tokens[i]; - struct TALER_MERCHANT_UsedToken *t = &ut[i]; - - TALER_wallet_token_use_sign (h_contract_terms, - &wallet_data_hash, // checked for != NULL above - &token->token_priv, - &t->token_sig); - t->ub_sig = token->ub_sig; - t->issue_pub = token->issue_pub; - GNUNET_CRYPTO_eddsa_key_get_public (&token->token_priv.private_key, - &t->token_pub.public_key); - } - { - struct TALER_MERCHANT_OrderPayHandle *oph; - - oph = TALER_MERCHANT_order_pay_frontend (ctx, - merchant_url, - order_id, - session_id, - wallet_data, - num_coins, - pc, - num_tokens, - ut, - j_output_tokens, - pay_cb, - pay_cb_cls); - if (NULL == oph) - return NULL; - oph->h_contract_terms = *h_contract_terms; - oph->merchant_pub = *merchant_pub; - oph->am_wallet = true; - return oph; - } - } -} - - -void -TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph) -{ - if (NULL != oph->job) - { - GNUNET_CURL_job_cancel (oph->job); - oph->job = NULL; - } - TALER_curl_easy_post_finished (&oph->post_ctx); - json_decref (oph->error_history); - json_decref (oph->full_reply); - GNUNET_free (oph->coins); - GNUNET_free (oph->url); - GNUNET_free (oph); -} - - -/* end of merchant_api_post_order_pay.c */ diff --git a/src/lib/merchant_api_post_order_refund.c b/src/lib/merchant_api_post_order_refund.c @@ -1,246 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_order_refund.c - * @brief Implementation of the POST /orders/ID/refund request - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /orders/ID/refund operation. - */ -struct TALER_MERCHANT_OrderRefundHandle -{ - /** - * Complete URL where the backend offers /refund - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * The CURL context to connect to the backend - */ - struct GNUNET_CURL_Context *ctx; - - /** - * The callback to pass the backend response to - */ - TALER_MERCHANT_RefundCallback cb; - - /** - * Clasure to pass to the callback - */ - void *cb_cls; - - /** - * Handle for the request - */ - struct GNUNET_CURL_Job *job; -}; - - -/** - * Callback to process POST /orders/ID/refund response - * - * @param cls the `struct TALER_MERCHANT_OrderRefundHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not JSON - */ -static void -handle_refund_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderRefundHandle *orh = cls; - const json_t *json = response; - struct TALER_MERCHANT_RefundResponse rr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - orh->job = NULL; - switch (response_code) - { - case 0: - rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ( - "taler_refund_uri", - &rr.details.ok.taler_refund_uri), - GNUNET_JSON_spec_fixed_auto ( - "h_contract", - &rr.details.ok.h_contract), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - rr.hr.http_status = 0; - rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - break; - } - case MHD_HTTP_UNAUTHORIZED: - rr.hr.ec = TALER_JSON_get_error_code (json); - rr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - rr.hr.ec = TALER_JSON_get_error_code (json); - rr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* order unknown (or not paid) */ - rr.hr.ec = TALER_JSON_get_error_code (json); - rr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - /* amount not acceptable */ - rr.hr.ec = TALER_JSON_get_error_code (json); - rr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_GONE: - /* too late, wire deadline is past */ - rr.hr.ec = TALER_JSON_get_error_code (json); - rr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - GNUNET_break_op (0); /* unexpected status code */ - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &rr.hr); - break; - } - orh->cb (orh->cb_cls, - &rr); - TALER_MERCHANT_post_order_refund_cancel (orh); -} - - -void -TALER_MERCHANT_post_order_refund_cancel ( - struct TALER_MERCHANT_OrderRefundHandle *orh) -{ - if (NULL != orh->job) - { - GNUNET_CURL_job_cancel (orh->job); - orh->job = NULL; - } - TALER_curl_easy_post_finished (&orh->post_ctx); - GNUNET_free (orh->url); - GNUNET_free (orh); -} - - -struct TALER_MERCHANT_OrderRefundHandle * -TALER_MERCHANT_post_order_refund (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *order_id, - const struct TALER_Amount *refund, - const char *reason, - TALER_MERCHANT_RefundCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OrderRefundHandle *orh; - json_t *req; - CURL *eh; - - orh = GNUNET_new (struct TALER_MERCHANT_OrderRefundHandle); - orh->ctx = ctx; - orh->cb = cb; - orh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "private/orders/%s/refund", - order_id); - orh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == orh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (orh); - return NULL; - } - req = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("refund", - refund), - GNUNET_JSON_pack_string ("reason", - reason)); - GNUNET_assert (NULL != req); - eh = TALER_MERCHANT_curl_easy_get_ (orh->url); - if (GNUNET_OK != - TALER_curl_easy_post (&orh->post_ctx, - eh, - req)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req); - GNUNET_free (orh->url); - GNUNET_free (orh); - return NULL; - } - json_decref (req); - orh->job = GNUNET_CURL_job_add2 (ctx, - eh, - orh->post_ctx.headers, - &handle_refund_finished, - orh); - if (NULL == orh->job) - { - GNUNET_free (orh->url); - GNUNET_free (orh); - return NULL; - } - return orh; -} - - -/* end of merchant_api_post_order_refund.c */ diff --git a/src/lib/merchant_api_post_orders.c b/src/lib/merchant_api_post_orders.c @@ -1,297 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with TALER; see the file COPYING.LGPL. If not, - see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_orders.c - * @brief Implementation of the POST /orders - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - - -/** - * @brief A POST /orders Handle - */ -struct TALER_MERCHANT_PostOrdersHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_PostOrdersCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP POST /orders request. - * - * @param cls the `struct TALER_MERCHANT_PostOrdersHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not JSON - */ -static void -handle_post_order_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_PostOrdersHandle *po = cls; - const json_t *json = response; - - po->job = NULL; - TALER_MERCHANT_handle_order_creation_response_ (po->cb, - po->cb_cls, - response_code, - json); - TALER_MERCHANT_orders_post_cancel (po); -} - - -struct TALER_MERCHANT_PostOrdersHandle * -TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const json_t *order, - struct GNUNET_TIME_Relative refund_delay, - TALER_MERCHANT_PostOrdersCallback cb, - void *cb_cls) -{ - static const char *no_uuids[GNUNET_NZL (0)]; - - return TALER_MERCHANT_orders_post2 (ctx, - backend_url, - order, - refund_delay, - NULL, - 0, - NULL, - 0, - no_uuids, - true, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_PostOrdersHandle * -TALER_MERCHANT_orders_post2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const json_t *order, - struct GNUNET_TIME_Relative refund_delay, - const char *payment_target, - unsigned int inventory_products_length, - const struct TALER_MERCHANT_InventoryProduct inventory_products[], - unsigned int uuids_length, - const char *uuids[], - bool create_token, - TALER_MERCHANT_PostOrdersCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_orders_post3 ( - ctx, - backend_url, - order, - NULL, /* session ID */ - refund_delay, - payment_target, - inventory_products_length, - inventory_products, - uuids_length, - uuids, - create_token, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_PostOrdersHandle * -TALER_MERCHANT_orders_post3 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const json_t *order, - const char *session_id, - struct GNUNET_TIME_Relative refund_delay, - const char *payment_target, - unsigned int inventory_products_length, - const struct TALER_MERCHANT_InventoryProduct inventory_products[], - unsigned int uuids_length, - const char *uuids[], - bool create_token, - TALER_MERCHANT_PostOrdersCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_PostOrdersHandle *po; - json_t *req; - CURL *eh; - - po = GNUNET_new (struct TALER_MERCHANT_PostOrdersHandle); - po->ctx = ctx; - po->cb = cb; - po->cb_cls = cb_cls; - po->url = TALER_url_join (backend_url, - "private/orders", - NULL); - req = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_object_incref ("order", - (json_t *) order), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("session_id", - session_id)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("payment_target", - payment_target))); - if (0 != refund_delay.rel_value_us) - { - GNUNET_assert (0 == - json_object_set_new (req, - "refund_delay", - GNUNET_JSON_from_time_rel ( - refund_delay))); - } - if (0 != inventory_products_length) - { - json_t *ipa = json_array (); - - GNUNET_assert (NULL != ipa); - for (unsigned int i = 0; i<inventory_products_length; i++) - { - json_t *ip; - char unit_quantity_buf[64]; - - TALER_MERCHANT_format_quantity_string (inventory_products[i].quantity, - inventory_products[i].quantity_frac - , - unit_quantity_buf, - sizeof (unit_quantity_buf)); - - ip = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("product_id", - inventory_products[i].product_id), - GNUNET_JSON_pack_string ("unit_quantity", - unit_quantity_buf)); - GNUNET_assert (NULL != ip); - GNUNET_assert (0 == - json_array_append_new (ipa, - ip)); - } - GNUNET_assert (0 == - json_object_set_new (req, - "inventory_products", - ipa)); - } - if (0 != uuids_length) - { - json_t *ua = json_array (); - - GNUNET_assert (NULL != ua); - for (unsigned int i = 0; i<uuids_length; i++) - { - json_t *u; - - u = json_string (uuids[i]); - GNUNET_assert (0 == - json_array_append_new (ua, - u)); - } - GNUNET_assert (0 == - json_object_set_new (req, - "lock_uuids", - ua)); - } - if (! create_token) - { - GNUNET_assert (0 == - json_object_set_new (req, - "create_token", - json_boolean (create_token))); - } - eh = TALER_MERCHANT_curl_easy_get_ (po->url); - if (GNUNET_OK != - TALER_curl_easy_post (&po->post_ctx, - eh, - req)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req); - GNUNET_free (po); - return NULL; - } - json_decref (req); - po->job = GNUNET_CURL_job_add2 (ctx, - eh, - po->post_ctx.headers, - &handle_post_order_finished, - po); - return po; -} - - -void -TALER_MERCHANT_orders_post_cancel ( - struct TALER_MERCHANT_PostOrdersHandle *po) -{ - if (NULL != po->job) - { - GNUNET_CURL_job_cancel (po->job); - po->job = NULL; - } - GNUNET_free (po->url); - TALER_curl_easy_post_finished (&po->post_ctx); - GNUNET_free (po); -} - - -/* end of merchant_api_post_orders.c */ diff --git a/src/lib/merchant_api_post_otp_devices.c b/src/lib/merchant_api_post_otp_devices.c @@ -1,237 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_otp_devices.c - * @brief Implementation of the POST /otp-devices request - * of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /otp-devices/$ID operation. - */ -struct TALER_MERCHANT_OtpDevicesPostHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OtpDevicesPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP POST /otp-devices request. - * - * @param cls the `struct TALER_MERCHANT_OtpDevicesPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_otp_devices_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OtpDevicesPostHandle *tph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /otp-devices completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - tph->cb (tph->cb_cls, - &hr); - TALER_MERCHANT_otp_devices_post_cancel (tph); -} - - -struct TALER_MERCHANT_OtpDevicesPostHandle * -TALER_MERCHANT_otp_devices_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *otp_device_id, - const char *otp_device_description, - const char *otp_key, - enum TALER_MerchantConfirmationAlgorithm otp_algorithm, - uint64_t otp_ctr, - TALER_MERCHANT_OtpDevicesPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OtpDevicesPostHandle *tph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("otp_device_id", - otp_device_id), - GNUNET_JSON_pack_string ("otp_device_description", - otp_device_description), - GNUNET_JSON_pack_uint64 ("otp_algorithm", - (uint32_t) otp_algorithm), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("otp_key", - otp_key)), - GNUNET_JSON_pack_uint64 ("otp_ctr", - otp_ctr)); - tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicesPostHandle); - tph->ctx = ctx; - tph->cb = cb; - tph->cb_cls = cb_cls; - tph->url = TALER_url_join (backend_url, - "private/otp-devices", - NULL); - if (NULL == tph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (tph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tph->url); - GNUNET_assert (GNUNET_OK == - TALER_curl_easy_post (&tph->post_ctx, - eh, - req_obj)); - json_decref (req_obj); - tph->job = GNUNET_CURL_job_add2 (ctx, - eh, - tph->post_ctx.headers, - &handle_post_otp_devices_finished, - tph); - GNUNET_assert (NULL != tph->job); - } - return tph; -} - - -void -TALER_MERCHANT_otp_devices_post_cancel ( - struct TALER_MERCHANT_OtpDevicesPostHandle *tph) -{ - if (NULL != tph->job) - { - GNUNET_CURL_job_cancel (tph->job); - tph->job = NULL; - } - TALER_curl_easy_post_finished (&tph->post_ctx); - GNUNET_free (tph->url); - GNUNET_free (tph); -} - - -/* end of merchant_api_post_otp_devices.c */ diff --git a/src/lib/merchant_api_post_products.c b/src/lib/merchant_api_post_products.c @@ -1,433 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2024 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_products.c - * @brief Implementation of the POST /products request - * of the merchant's HTTP API - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /products/$ID operation. - */ -struct TALER_MERCHANT_ProductsPostHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_ProductsPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP POST /products request. - * - * @param cls the `struct TALER_MERCHANT_ProductsPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_products_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_ProductsPostHandle *pph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - pph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /products completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - pph->cb (pph->cb_cls, - &hr); - TALER_MERCHANT_products_post_cancel (pph); -} - - -struct TALER_MERCHANT_ProductsPostHandle * -TALER_MERCHANT_products_post4 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - const char *description, - const json_t *description_i18n, - const char *unit, - const struct TALER_Amount *unit_prices, - size_t unit_price_len, - const char *image, - const json_t *taxes, - int64_t total_stock, - uint32_t total_stock_frac, - bool unit_allow_fraction, - const uint32_t *unit_precision_level, - const json_t *address, - struct GNUNET_TIME_Timestamp next_restock, - uint32_t minimum_age, - unsigned int num_cats, - const uint64_t *cats, - TALER_MERCHANT_ProductsPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_ProductsPostHandle *pph; - json_t *req_obj; - json_t *categories; - char unit_total_stock_buf[64]; - - TALER_MERCHANT_format_stock_string (total_stock, - total_stock_frac, - unit_total_stock_buf, - sizeof (unit_total_stock_buf)); - - if (0 == num_cats) - { - categories = NULL; - } - else - { - categories = json_array (); - GNUNET_assert (NULL != categories); - for (unsigned int i = 0; i<num_cats; i++) - GNUNET_assert (0 == - json_array_append_new (categories, - json_integer (cats[i]))); - } - { - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("product_id", - product_id), - /* FIXME: once we move to the new-style API, - allow applications to set the product name properly! */ - GNUNET_JSON_pack_string ("product_name", - description), - GNUNET_JSON_pack_string ("description", - description), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("description_i18n", - (json_t *) description_i18n)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_steal ("categories", - categories)), - GNUNET_JSON_pack_string ("unit", - unit), - TALER_JSON_pack_amount_array ("unit_price", - unit_price_len, - unit_prices), - GNUNET_JSON_pack_string ("image", - image), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_incref ("taxes", - (json_t *) taxes)), - GNUNET_JSON_pack_string ("unit_total_stock", - unit_total_stock_buf), - GNUNET_JSON_pack_bool ("unit_allow_fraction", - unit_allow_fraction), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_uint64 ("minimum_age", - minimum_age)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("address", - (json_t *) address)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_timestamp ("next_restock", - next_restock))); - } - if (NULL != unit_precision_level) - { - GNUNET_assert (0 == - json_object_set_new (req_obj, - "unit_precision_level", - json_integer ( - *unit_precision_level))); - } - if (! unit_allow_fraction) - { - GNUNET_assert (0 == - json_object_del (req_obj, - "unit_allow_fraction")); - if (NULL != unit_precision_level) - GNUNET_assert (0 == - json_object_del (req_obj, - "unit_precision_level")); - } - pph = GNUNET_new (struct TALER_MERCHANT_ProductsPostHandle); - pph->ctx = ctx; - pph->cb = cb; - pph->cb_cls = cb_cls; - pph->url = TALER_url_join (backend_url, - "private/products", - NULL); - if (NULL == pph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (pph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (pph->url); - GNUNET_assert (GNUNET_OK == - TALER_curl_easy_post (&pph->post_ctx, - eh, - req_obj)); - json_decref (req_obj); - pph->job = GNUNET_CURL_job_add2 (ctx, - eh, - pph->post_ctx.headers, - &handle_post_products_finished, - pph); - GNUNET_assert (NULL != pph->job); - } - return pph; -} - - -struct TALER_MERCHANT_ProductsPostHandle * -TALER_MERCHANT_products_post3 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - const char *description, - const json_t *description_i18n, - const char *unit, - const struct TALER_Amount *price, - const char *image, - const json_t *taxes, - int64_t total_stock, - const json_t *address, - struct GNUNET_TIME_Timestamp next_restock, - uint32_t minimum_age, - unsigned int num_cats, - const uint64_t *cats, - TALER_MERCHANT_ProductsPostCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_products_post4 (ctx, - backend_url, - product_id, - description, - description_i18n, - unit, - price, - 1, - image, - taxes, - total_stock, - 0, - false, - NULL, - address, - next_restock, - minimum_age, - num_cats, - cats, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_ProductsPostHandle * -TALER_MERCHANT_products_post2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - const char *description, - const json_t *description_i18n, - const char *unit, - const struct TALER_Amount *price, - const char *image, - const json_t *taxes, - int64_t total_stock, - const json_t *address, - struct GNUNET_TIME_Timestamp next_restock, - uint32_t minimum_age, - TALER_MERCHANT_ProductsPostCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_products_post3 (ctx, - backend_url, - product_id, - description, - description_i18n, - unit, - price, - image, - taxes, - total_stock, - address, - next_restock, - minimum_age, - 0, - NULL, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_ProductsPostHandle * -TALER_MERCHANT_products_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *product_id, - const char *description, - const json_t *description_i18n, - const char *unit, - const struct TALER_Amount *price, - const char *image, - const json_t *taxes, - int64_t total_stock, - const json_t *address, - struct GNUNET_TIME_Timestamp next_restock, - TALER_MERCHANT_ProductsPostCallback cb, - void *cb_cls) -{ - return TALER_MERCHANT_products_post2 (ctx, - backend_url, - product_id, - description, - description_i18n, - unit, - price, - image, - taxes, - total_stock, - address, - next_restock, - 0, - cb, - cb_cls); -} - - -void -TALER_MERCHANT_products_post_cancel ( - struct TALER_MERCHANT_ProductsPostHandle *pph) -{ - if (NULL != pph->job) - { - GNUNET_CURL_job_cancel (pph->job); - pph->job = NULL; - } - TALER_curl_easy_post_finished (&pph->post_ctx); - GNUNET_free (pph->url); - GNUNET_free (pph); -} - - -/* end of merchant_api_post_products.c */ diff --git a/src/lib/merchant_api_post_templates.c b/src/lib/merchant_api_post_templates.c @@ -1,495 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_templates.c - * @brief Implementation of the POST /templates request - * of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> -#include "taler/taler_merchant_util.h" - -/* FIXME: Bohdan is to stupid to figure out how util can be used here */ -static enum TALER_MERCHANT_TemplateType -template_type_from_string (const char *template_type) -{ - if (NULL == template_type) - return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER; - if (0 == strcmp (template_type, - "fixed-order")) - return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER; - if (0 == strcmp (template_type, - "inventory-cart")) - return TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART; - if (0 == strcmp (template_type, - "paivana")) - return TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA; - return TALER_MERCHANT_TEMPLATE_TYPE_INVALID; -} - - -/** - * Validate a fixed-order template contract. - * - * @param template_contract JSON template contract - * @param summary summary pointer - * @param amount amount storage - * @param minimum_age minimum age storage - * @param pay_duration pay duration storage - * @return true if valid - */ -static bool -validate_template_contract_fixed (const json_t *template_contract, - const char **summary, - struct TALER_Amount *amount, - uint32_t *minimum_age, - struct GNUNET_TIME_Relative *pay_duration) -{ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("summary", - summary), - NULL), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_amount_any ("amount", - amount), - NULL), - GNUNET_JSON_spec_uint32 ("minimum_age", - minimum_age), - GNUNET_JSON_spec_relative_time ("pay_duration", - pay_duration), - GNUNET_JSON_spec_end () - }; - const char *ename; - unsigned int eline; - - if (GNUNET_OK != - GNUNET_JSON_parse (template_contract, - spec, - &ename, - &eline)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid template_contract for field %s\n", - ename); - return false; - } - return true; -} - - -/** - * Validate an inventory-cart template contract. - * - * @param template_contract JSON template contract - * @param summary summary pointer - * @param pay_duration pay duration storage - * @return true if valid - */ -static bool -validate_template_contract_inventory (const json_t *template_contract, - const char **summary, - struct GNUNET_TIME_Relative *pay_duration) -{ - bool selected_all = false; - bool choose_one = false; - bool request_tip = false; - const json_t *selected_categories = NULL; - const json_t *selected_products = NULL; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("summary", - summary), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_bool ("request_tip", - &request_tip), - NULL), - GNUNET_JSON_spec_relative_time ("pay_duration", - pay_duration), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_bool ("selected_all", - &selected_all), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("selected_categories", - &selected_categories), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("selected_products", - &selected_products), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_bool ("choose_one", - &choose_one), - NULL), - GNUNET_JSON_spec_end () - }; - const char *ename; - unsigned int eline; - - (void) request_tip; - (void) selected_all; - (void) choose_one; - (void) selected_categories; - (void) selected_products; - if (GNUNET_OK != - GNUNET_JSON_parse (template_contract, - ispec, - &ename, - &eline)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid template_contract for field %s\n", - ename); - return false; - } - return true; -} - - -/** - * Validate a paivana template contract. - * - * @param template_contract JSON template contract - * @param summary summary pointer - * @param amount amount storage - * @param minimum_age minimum age storage - * @param pay_duration pay duration storage - * @return true if valid - */ -static bool -validate_template_contract_paivana (const json_t *template_contract, - const char **summary, - struct TALER_Amount *amount, - uint32_t *minimum_age, - struct GNUNET_TIME_Relative *pay_duration) -{ - /* TODO: PAIVANA validate paivana-specific fields beyond presence. */ - const char *paivana_id; - struct GNUNET_JSON_Specification pspec[] = { - GNUNET_JSON_spec_string ("paivana_id", - &paivana_id), - GNUNET_JSON_spec_end () - }; - const char *ename; - unsigned int eline; - - if (GNUNET_OK != - GNUNET_JSON_parse (template_contract, - pspec, - &ename, - &eline)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid paivana template_contract for field %s\n", - ename); - return false; - } - - if (! validate_template_contract_fixed (template_contract, - summary, - amount, - minimum_age, - pay_duration)) - return false; - - (void) paivana_id; - return true; -} - - -/** - * Handle for a POST /templates/$ID operation. - */ -struct TALER_MERCHANT_TemplatesPostHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TemplatesPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP POST /templates request. - * - * @param cls the `struct TALER_MERCHANT_TemplatesPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_templates_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TemplatesPostHandle *tph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /templates completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - tph->cb (tph->cb_cls, - &hr); - TALER_MERCHANT_templates_post_cancel (tph); -} - - -static bool -test_template_contract_valid (const json_t *template_contract) -{ - const char *template_type = NULL; - enum TALER_MERCHANT_TemplateType template_type_enum; - struct GNUNET_JSON_Specification type_spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("template_type", - &template_type), - NULL), - GNUNET_JSON_spec_end () - }; - const char *summary; - struct TALER_Amount amount = { .value = 0}; - uint32_t minimum_age = 0; - struct GNUNET_TIME_Relative pay_duration = { 0 }; - const char *ename; - unsigned int eline; - - if (GNUNET_OK != - GNUNET_JSON_parse (template_contract, - type_spec, - &ename, - &eline)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid template_contract for field %s\n", - ename); - return false; - } - - template_type_enum = template_type_from_string (template_type); - if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == template_type_enum) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid template_type '%s'\n", - template_type); - return false; - } - - /* FIXME: Bohdan understands that links have to be changed, but worried, that can crash something */ - switch (template_type_enum) - { - case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART: - return validate_template_contract_inventory (template_contract, - &summary, - &pay_duration); - case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER: - return validate_template_contract_fixed (template_contract, - &summary, - &amount, - &minimum_age, - &pay_duration); - case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA: - return validate_template_contract_paivana (template_contract, - &summary, - &amount, - &minimum_age, - &pay_duration); - case TALER_MERCHANT_TEMPLATE_TYPE_INVALID: - break; - } - return false; -} - - -struct TALER_MERCHANT_TemplatesPostHandle * -TALER_MERCHANT_templates_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *template_id, - const char *template_description, - const char *otp_id, - const json_t *template_contract, - TALER_MERCHANT_TemplatesPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TemplatesPostHandle *tph; - json_t *req_obj; - - if (! test_template_contract_valid (template_contract)) - { - GNUNET_break (0); - return NULL; - } - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("template_id", - template_id), - GNUNET_JSON_pack_string ("template_description", - template_description), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("otp_id", - otp_id)), - GNUNET_JSON_pack_object_incref ("template_contract", - (json_t *) template_contract)); - tph = GNUNET_new (struct TALER_MERCHANT_TemplatesPostHandle); - tph->ctx = ctx; - tph->cb = cb; - tph->cb_cls = cb_cls; - tph->url = TALER_url_join (backend_url, - "private/templates", - NULL); - if (NULL == tph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (tph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (tph->url); - GNUNET_assert (GNUNET_OK == - TALER_curl_easy_post (&tph->post_ctx, - eh, - req_obj)); - json_decref (req_obj); - tph->job = GNUNET_CURL_job_add2 (ctx, - eh, - tph->post_ctx.headers, - &handle_post_templates_finished, - tph); - GNUNET_assert (NULL != tph->job); - } - return tph; -} - - -void -TALER_MERCHANT_templates_post_cancel ( - struct TALER_MERCHANT_TemplatesPostHandle *tph) -{ - if (NULL != tph->job) - { - GNUNET_CURL_job_cancel (tph->job); - tph->job = NULL; - } - TALER_curl_easy_post_finished (&tph->post_ctx); - GNUNET_free (tph->url); - GNUNET_free (tph); -} - - -/* end of merchant_api_post_templates.c */ diff --git a/src/lib/merchant_api_post_tokenfamilies.c b/src/lib/merchant_api_post_tokenfamilies.c @@ -1,257 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2024 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_tokenfamilies.c - * @brief Implementation of the POST /tokenfamilies request - * of the merchant's HTTP API - * @author Christian Blättler - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <gnunet/gnunet_json_lib.h> -#include <gnunet/gnunet_time_lib.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /tokenfamilies operation. - */ -struct TALER_MERCHANT_TokenFamiliesPostHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_TokenFamiliesPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - -/** - * Function called when we're done processing the - * HTTP POST /tokenfamilies request. - * - * @param cls the `struct TALER_MERCHANT_TokenFamiliesPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_token_families_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TokenFamiliesPostHandle *handle = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - handle->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /tokenfamilies completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - handle->cb (handle->cb_cls, - &hr); - TALER_MERCHANT_token_families_post_cancel (handle); -} - - -struct TALER_MERCHANT_TokenFamiliesPostHandle * -TALER_MERCHANT_token_families_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *slug, - const char *name, - const char *description, - const json_t *description_i18n, - const json_t *extra_data, - struct GNUNET_TIME_Timestamp valid_after, - struct GNUNET_TIME_Timestamp valid_before, - struct GNUNET_TIME_Relative duration, - struct GNUNET_TIME_Relative validity_granularity, - struct GNUNET_TIME_Relative start_offset, - const char *kind, - TALER_MERCHANT_TokenFamiliesPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_TokenFamiliesPostHandle *handle; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("slug", - slug), - GNUNET_JSON_pack_string ("name", - name), - GNUNET_JSON_pack_string ("description", - description), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("description_i18n", - (json_t *) description_i18n)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("extra_data", - (json_t *) extra_data)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_timestamp ("valid_after", - valid_after)), - GNUNET_JSON_pack_timestamp ("valid_before", - valid_before), - GNUNET_JSON_pack_time_rel ("duration", - duration), - GNUNET_JSON_pack_time_rel ("validity_granularity", - validity_granularity), - GNUNET_JSON_pack_time_rel ("start_offset", - start_offset), - GNUNET_JSON_pack_string ("kind", - kind)); - handle = GNUNET_new (struct TALER_MERCHANT_TokenFamiliesPostHandle); - handle->ctx = ctx; - handle->cb = cb; - handle->cb_cls = cb_cls; - handle->url = TALER_url_join (backend_url, - "private/tokenfamilies", - NULL); - if (NULL == handle->url) - { - - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (handle); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (handle->url); - GNUNET_assert (GNUNET_OK == - TALER_curl_easy_post (&handle->post_ctx, - eh, - req_obj)); - json_decref (req_obj); - handle->job = GNUNET_CURL_job_add2 (ctx, - eh, - handle->post_ctx.headers, - &handle_post_token_families_finished, - handle); - GNUNET_assert (NULL != handle->job); - } - return handle; -} - - -void -TALER_MERCHANT_token_families_post_cancel ( - struct TALER_MERCHANT_TokenFamiliesPostHandle *pph) -{ - if (NULL != pph->job) - { - GNUNET_CURL_job_cancel (pph->job); - pph->job = NULL; - } - TALER_curl_easy_post_finished (&pph->post_ctx); - GNUNET_free (pph->url); - GNUNET_free (pph); -} diff --git a/src/lib/merchant_api_post_transfers.c b/src/lib/merchant_api_post_transfers.c @@ -1,256 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_transfers.c - * @brief Implementation of the POST /transfers request of the merchant's HTTP API - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_curl_lib.h> -#include <taler/taler_json_lib.h> - - -/** - * @brief A handle for POSTing transfer data. - */ -struct TALER_MERCHANT_PostTransfersHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_PostTransfersCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP POST /transfers request. - * - * @param cls the `struct TALER_MERCHANT_PostTransfersHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_transfers_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_PostTransfersHandle *pth = cls; - struct TALER_MERCHANT_PostTransfersResponse ptr = { - .hr.reply = response, - .hr.http_status = (unsigned int) response_code - }; - - pth->job = NULL; - switch (response_code) - { - case 0: - ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_UNAUTHORIZED: - ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); - ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Did not find any data\n"); - ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); - ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); - ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); - break; - case MHD_HTTP_BAD_GATEWAY: - /* Exchange had an issue; we should retry, but this API - leaves this to the application */ - ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); - ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); - { - uint32_t ehc; - struct GNUNET_JSON_Specification ispec[] = { - TALER_JSON_spec_ec ("exchange_code", - &ptr.details.bad_gateway.exchange_ec), - GNUNET_JSON_spec_uint32 ("exchange_http_status", - &ehc), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (ptr.hr.reply, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - ptr.details.bad_gateway.exchange_http_status = 0; - ptr.details.bad_gateway.exchange_ec = TALER_EC_NONE; - break; - } - else - { - ptr.details.bad_gateway.exchange_http_status - = (unsigned int) ehc; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Exchange returned %u/%u\n", - (unsigned int) ptr.details.bad_gateway.exchange_ec, - (unsigned int) ehc); - } - } - break; - case MHD_HTTP_GATEWAY_TIMEOUT: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); - ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (ptr.hr.reply, - response_code, - &ptr.hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) ptr.hr.http_status, - (int) ptr.hr.ec); - break; - } - pth->cb (pth->cb_cls, - &ptr); - TALER_MERCHANT_transfers_post_cancel (pth); -} - - -struct TALER_MERCHANT_PostTransfersHandle * -TALER_MERCHANT_transfers_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_Amount *credit_amount, - const struct TALER_WireTransferIdentifierRawP *wtid, - struct TALER_FullPayto payto_uri, - const char *exchange_url, - TALER_MERCHANT_PostTransfersCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_PostTransfersHandle *pth; - CURL *eh; - json_t *req; - - pth = GNUNET_new (struct TALER_MERCHANT_PostTransfersHandle); - pth->ctx = ctx; - pth->cb = cb; - pth->cb_cls = cb_cls; - pth->url = TALER_url_join (backend_url, - "private/transfers", - NULL); - if (NULL == pth->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (pth); - return NULL; - } - req = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("credit_amount", - credit_amount), - GNUNET_JSON_pack_data_auto ("wtid", - wtid), - TALER_JSON_pack_full_payto ("payto_uri", - payto_uri), - GNUNET_JSON_pack_string ("exchange_url", - exchange_url)); - eh = TALER_MERCHANT_curl_easy_get_ (pth->url); - if (GNUNET_OK != - TALER_curl_easy_post (&pth->post_ctx, - eh, - req)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req); - GNUNET_free (pth->url); - GNUNET_free (pth); - return NULL; - } - json_decref (req); - pth->job = GNUNET_CURL_job_add2 (ctx, - eh, - pth->post_ctx.headers, - &handle_post_transfers_finished, - pth); - return pth; -} - - -void -TALER_MERCHANT_transfers_post_cancel ( - struct TALER_MERCHANT_PostTransfersHandle *pth) -{ - if (NULL != pth->job) - { - GNUNET_CURL_job_cancel (pth->job); - pth->job = NULL; - } - GNUNET_free (pth->url); - TALER_curl_easy_post_finished (&pth->post_ctx); - GNUNET_free (pth); -} - - -/* end of merchant_api_post_transfers.c */ diff --git a/src/lib/merchant_api_post_units.c b/src/lib/merchant_api_post_units.c @@ -1,218 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free - Software Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_units.c - * @brief Implementation of POST /private/units - * @author Bohdan Potuzhnyi - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /private/units operation. - */ -struct TALER_MERCHANT_UnitsPostHandle -{ - /** - * Fully qualified request URL. - */ - char *url; - - /** - * CURL job handle. - */ - struct GNUNET_CURL_Job *job; - - /** - * Callback to invoke with the result. - */ - TALER_MERCHANT_UnitsPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Helper keeping POST body and headers alive. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * Called when the HTTP transfer finishes. - * - * @param cls operation handle - * @param response_code HTTP status (0 on network / parsing failures) - * @param response parsed JSON reply (NULL if unavailable) - */ -static void -handle_post_units_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_UnitsPostHandle *uph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - uph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /private/units completed with status %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - case MHD_HTTP_UNAUTHORIZED: - case MHD_HTTP_FORBIDDEN: - case MHD_HTTP_NOT_FOUND: - case MHD_HTTP_CONFLICT: - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response %u/%d for POST /private/units\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - uph->cb (uph->cb_cls, - &hr); - TALER_MERCHANT_units_post_cancel (uph); -} - - -struct TALER_MERCHANT_UnitsPostHandle * -TALER_MERCHANT_units_post (struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *unit_id, - const char *unit_name_long, - const char *unit_name_short, - bool unit_allow_fraction, - uint32_t unit_precision_level, - bool unit_active, - const json_t *unit_name_long_i18n, - const json_t *unit_name_short_i18n, - TALER_MERCHANT_UnitsPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_UnitsPostHandle *uph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("unit", - unit_id), - GNUNET_JSON_pack_string ("unit_name_long", - unit_name_long), - GNUNET_JSON_pack_string ("unit_name_short", - unit_name_short), - GNUNET_JSON_pack_bool ("unit_allow_fraction", - unit_allow_fraction), - GNUNET_JSON_pack_uint64 ("unit_precision_level", - (uint64_t) unit_precision_level), - GNUNET_JSON_pack_bool ("unit_active", - unit_active), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("unit_name_long_i18n", - (json_t *) unit_name_long_i18n)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("unit_name_short_i18n", - (json_t *) unit_name_short_i18n))); - uph = GNUNET_new (struct TALER_MERCHANT_UnitsPostHandle); - uph->ctx = ctx; - uph->cb = cb; - uph->cb_cls = cb_cls; - uph->url = TALER_url_join (backend_url, - "private/units", - NULL); - if (NULL == uph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to build /private/units URL\n"); - json_decref (req_obj); - GNUNET_free (uph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (uph->url); - if (GNUNET_OK != - TALER_curl_easy_post (&uph->post_ctx, - eh, - req_obj)) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (req_obj); - GNUNET_free (uph->url); - GNUNET_free (uph); - return NULL; - } - json_decref (req_obj); - uph->job = GNUNET_CURL_job_add2 (ctx, - eh, - uph->post_ctx.headers, - &handle_post_units_finished, - uph); - } - return uph; -} - - -void -TALER_MERCHANT_units_post_cancel (struct TALER_MERCHANT_UnitsPostHandle *uph) -{ - if (NULL != uph->job) - { - GNUNET_CURL_job_cancel (uph->job); - uph->job = NULL; - } - TALER_curl_easy_post_finished (&uph->post_ctx); - GNUNET_free (uph->url); - GNUNET_free (uph); -} - - -/* end of merchant_api_post_units.c */ diff --git a/src/lib/merchant_api_post_using_templates.c b/src/lib/merchant_api_post_using_templates.c @@ -1,233 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_using_templates.c - * @brief Implementation of the POST /using_templates request - * of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /templates/$ID operation. - */ -struct TALER_MERCHANT_UsingTemplatesPostHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_PostOrdersCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; -}; - -/** - * Function called when we're done processing the - * HTTP POST /using-templates request. - * - * @param cls the `struct TALER_MERCHANT_UsingTemplatesPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_using_templates_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_UsingTemplatesPostHandle *utph = cls; - const json_t *json = response; - - utph->job = NULL; - TALER_MERCHANT_handle_order_creation_response_ (utph->cb, - utph->cb_cls, - response_code, - json); - TALER_MERCHANT_using_templates_post_cancel (utph); -} - - -/** - * Helper to submit a POST /templates/$ID request. - * - * @param ctx the context - * @param backend_url HTTP base URL for the backend - * @param template_id identifier of the template to use - * @param req_obj JSON request body (consumed) - * @param cb function to call with the backend's result - * @param cb_cls closure for @a cb - * @return the request handle; NULL upon error - */ -static struct TALER_MERCHANT_UsingTemplatesPostHandle * -using_templates_post_internal ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *template_id, - json_t *req_obj, - TALER_MERCHANT_PostOrdersCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_UsingTemplatesPostHandle *utph; - - utph = GNUNET_new (struct TALER_MERCHANT_UsingTemplatesPostHandle); - utph->ctx = ctx; - utph->cb = cb; - utph->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "templates/%s", - template_id); - utph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == utph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (utph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (utph->url); - GNUNET_assert (GNUNET_OK == - TALER_curl_easy_post (&utph->post_ctx, - eh, - req_obj)); - json_decref (req_obj); - utph->job = GNUNET_CURL_job_add2 (ctx, - eh, - utph->post_ctx.headers, - &handle_post_using_templates_finished, - utph); - GNUNET_assert (NULL != utph->job); - } - return utph; -} - - -struct TALER_MERCHANT_UsingTemplatesPostHandle * -TALER_MERCHANT_using_templates_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *template_id, - const char *summary, - const struct TALER_Amount *amount, - TALER_MERCHANT_PostOrdersCallback cb, - void *cb_cls) -{ - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("template_type", - "fixed-order"), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("summary", - summary)), - GNUNET_JSON_pack_allow_null ( - TALER_JSON_pack_amount ("amount", - amount))); - return using_templates_post_internal (ctx, - backend_url, - template_id, - req_obj, - cb, - cb_cls); -} - - -struct TALER_MERCHANT_UsingTemplatesPostHandle * -TALER_MERCHANT_using_templates_post2 ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *template_id, - const json_t *details, - TALER_MERCHANT_PostOrdersCallback cb, - void *cb_cls) -{ - if (NULL == details) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No request body provided for using templates.\n"); - return NULL; - } - return using_templates_post_internal (ctx, - backend_url, - template_id, - (json_t *) details, - cb, - cb_cls); -} - - -void -TALER_MERCHANT_using_templates_post_cancel ( - struct TALER_MERCHANT_UsingTemplatesPostHandle *utph) -{ - if (NULL != utph->job) - { - GNUNET_CURL_job_cancel (utph->job); - utph->job = NULL; - } - TALER_curl_easy_post_finished (&utph->post_ctx); - GNUNET_free (utph->url); - GNUNET_free (utph); -} - - -/* end of merchant_api_post_using_templates.c */ diff --git a/src/lib/merchant_api_post_webhooks.c b/src/lib/merchant_api_post_webhooks.c @@ -1,240 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with TALER; see the file COPYING.LGPL. - If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_post_webhooks.c - * @brief Implementation of the POST /webhooks request - * of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_curl_lib.h> - - -/** - * Handle for a POST /webhooks/$ID operation. - */ -struct TALER_MERCHANT_WebhooksPostHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_WebhooksPostCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP POST /webhooks request. - * - * @param cls the `struct TALER_MERCHANT_WebhooksPostHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_webhooks_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_WebhooksPostHandle *wph = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - wph->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /webhooks completed with response code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us - * or the merchant is buggy (or API version conflict); - * just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_FORBIDDEN: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we tried to abort the payment - * after it was successful. We should pass the JSON reply to the - * application */ - break; - case MHD_HTTP_NOT_FOUND: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the - application */ - break; - case MHD_HTTP_CONFLICT: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, - but this API leaves this to the application */ - break; - default: - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - GNUNET_break_op (0); - break; - } - wph->cb (wph->cb_cls, - &hr); - TALER_MERCHANT_webhooks_post_cancel (wph); -} - - -struct TALER_MERCHANT_WebhooksPostHandle * -TALER_MERCHANT_webhooks_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *webhook_id, - const char *event_type, - const char *url, - const char *http_method, - const char *header_template, - const char *body_template, - TALER_MERCHANT_WebhooksPostCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_WebhooksPostHandle *wph; - json_t *req_obj; - - req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("webhook_id", - webhook_id), - GNUNET_JSON_pack_string ("event_type", - event_type), - GNUNET_JSON_pack_string ("url", - url), - GNUNET_JSON_pack_string ("http_method", - http_method), - GNUNET_JSON_pack_string ("header_template", - header_template), - GNUNET_JSON_pack_string ("body_template", - body_template)); - wph = GNUNET_new (struct TALER_MERCHANT_WebhooksPostHandle); - wph->ctx = ctx; - wph->cb = cb; - wph->cb_cls = cb_cls; - wph->url = TALER_url_join (backend_url, - "private/webhooks", - NULL); - if (NULL == wph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (req_obj); - GNUNET_free (wph); - return NULL; - } - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (wph->url); - GNUNET_assert (GNUNET_OK == - TALER_curl_easy_post (&wph->post_ctx, - eh, - req_obj)); - json_decref (req_obj); - wph->job = GNUNET_CURL_job_add2 (ctx, - eh, - wph->post_ctx.headers, - &handle_post_webhooks_finished, - wph); - GNUNET_assert (NULL != wph->job); - } - return wph; -} - - -void -TALER_MERCHANT_webhooks_post_cancel ( - struct TALER_MERCHANT_WebhooksPostHandle *wph) -{ - if (NULL != wph->job) - { - GNUNET_CURL_job_cancel (wph->job); - wph->job = NULL; - } - TALER_curl_easy_post_finished (&wph->post_ctx); - GNUNET_free (wph->url); - GNUNET_free (wph); -} - - -/* end of merchant_api_post_webhooks.c */ diff --git a/src/lib/merchant_api_wallet_get_order.c b/src/lib/merchant_api_wallet_get_order.c @@ -1,306 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018, 2020, 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_wallet_get_order.c - * @brief Implementation of the GET /orders/$ID request - * @author Christian Grothoff - * @author Marcello Stanisci - * @author Florian Dold - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include "merchant_api_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * @brief A GET /orders/$ID handle - */ -struct TALER_MERCHANT_OrderWalletGetHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_OrderWalletGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Convenience function to call the callback in @a owgh with an error code of - * @a ec and the exchange body being set to @a reply. - * - * @param owgh handle providing callback - * @param ec error code to return to application - * @param reply JSON reply we got from the exchange, can be NULL - */ -static void -cb_failure (struct TALER_MERCHANT_OrderWalletGetHandle *owgh, - enum TALER_ErrorCode ec, - const json_t *reply) -{ - struct TALER_MERCHANT_OrderWalletGetResponse owgr = { - .hr.ec = ec, - .hr.reply = reply - }; - - owgh->cb (owgh->cb_cls, - &owgr); -} - - -/** - * Function called when we're done processing the GET /check-payment request. - * - * @param cls the `struct TALER_MERCHANT_OrderWalletGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, should be NULL - */ -static void -handle_wallet_get_order_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_OrderWalletGetHandle *owgh = cls; - const json_t *json = response; - - owgh->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - { - struct TALER_MERCHANT_OrderWalletGetResponse owgr = { - .hr.reply = json, - .hr.http_status = MHD_HTTP_OK - }; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_bool ("refunded", - &owgr.details.ok.refunded), - GNUNET_JSON_spec_bool ("refund_pending", - &owgr.details.ok.refund_pending), - TALER_JSON_spec_amount_any ("refund_amount", - &owgr.details.ok.refund_amount), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - cb_failure (owgh, - TALER_EC_GENERIC_REPLY_MALFORMED, - json); - TALER_MERCHANT_wallet_order_get_cancel (owgh); - return; - } - owgh->cb (owgh->cb_cls, - &owgr); - GNUNET_JSON_parse_free (spec); - break; - } - case MHD_HTTP_PAYMENT_REQUIRED: - { - struct TALER_MERCHANT_OrderWalletGetResponse owgr = { - .hr.reply = json, - .hr.http_status = MHD_HTTP_PAYMENT_REQUIRED - }; - - /* Status is: unpaid */ - owgr.details.payment_required.taler_pay_uri - = json_string_value (json_object_get (json, - "taler_pay_uri")); - owgr.details.payment_required.already_paid_order_id - = json_string_value (json_object_get (json, - "already_paid_order_id")); - if (NULL == owgr.details.payment_required.taler_pay_uri) - { - GNUNET_break_op (0); - cb_failure (owgh, - TALER_EC_GENERIC_REPLY_MALFORMED, - json); - break; - } - owgh->cb (owgh->cb_cls, - &owgr); - break; - } - default: - { - struct TALER_MERCHANT_OrderWalletGetResponse owgr = { - .hr.reply = json, - .hr.http_status = response_code - }; - - TALER_MERCHANT_parse_error_details_ (response, - response_code, - &owgr.hr); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Checking order status failed with HTTP status code %u/%d\n", - (unsigned int) response_code, - (int) owgr.hr.ec); - GNUNET_break_op (0); - owgh->cb (owgh->cb_cls, - &owgr); - break; - } - } - TALER_MERCHANT_wallet_order_get_cancel (owgh); -} - - -struct TALER_MERCHANT_OrderWalletGetHandle * -TALER_MERCHANT_wallet_order_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *order_id, - const struct TALER_PrivateContractHashP *h_contract, - struct GNUNET_TIME_Relative timeout, - const char *session_id, - const struct TALER_Amount *min_refund, - bool await_refund_obtained, - TALER_MERCHANT_OrderWalletGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_OrderWalletGetHandle *owgh; - unsigned int tms; - - GNUNET_assert (NULL != backend_url); - GNUNET_assert (NULL != order_id); - owgh = GNUNET_new (struct TALER_MERCHANT_OrderWalletGetHandle); - owgh->ctx = ctx; - owgh->cb = cb; - owgh->cb_cls = cb_cls; - tms = (unsigned int) (timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); - { - char timeout_ms[32]; - struct GNUNET_CRYPTO_HashAsciiEncoded h_contract_s; - char *path; - - GNUNET_CRYPTO_hash_to_enc (&h_contract->hash, - &h_contract_s); - GNUNET_snprintf (timeout_ms, - sizeof (timeout_ms), - "%u", - tms); - GNUNET_asprintf (&path, - "orders/%s", - order_id); - owgh->url = TALER_url_join (backend_url, - path, - "h_contract", - h_contract_s.encoding, - "session_id", - session_id, - "timeout_ms", - (0 != tms) - ? timeout_ms - : NULL, - "refund", - (NULL != min_refund) - ? TALER_amount2s (min_refund) - : NULL, - "await_refund_obtained", - await_refund_obtained - ? "yes" - : NULL, - NULL); - GNUNET_free (path); - } - if (NULL == owgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (owgh); - return NULL; - } - - { - CURL *eh; - - eh = TALER_MERCHANT_curl_easy_get_ (owgh->url); - if (0 != tms) - { - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TIMEOUT_MS, - (long) (tms + 100L))); - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Checking order status at %s\n", - owgh->url); - if (NULL == (owgh->job = - GNUNET_CURL_job_add (ctx, - eh, - &handle_wallet_get_order_finished, - owgh))) - { - GNUNET_break (0); - GNUNET_free (owgh->url); - GNUNET_free (owgh); - return NULL; - } - } - return owgh; -} - - -void -TALER_MERCHANT_wallet_order_get_cancel ( - struct TALER_MERCHANT_OrderWalletGetHandle *owgh) -{ - if (NULL != owgh->job) - { - GNUNET_CURL_job_cancel (owgh->job); - owgh->job = NULL; - } - GNUNET_free (owgh->url); - GNUNET_free (owgh); -} - - -/* end of merchant_api_wallet_get_order.c */ diff --git a/src/lib/merchant_api_wallet_get_template.c b/src/lib/merchant_api_wallet_get_template.c @@ -1,195 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_wallet_get_template.c - * @brief Implementation of the GET /templates/$ID request of the merchant's HTTP API - * @author Priscilla HUANG - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> - - -/** - * Handle for a GET /templates/$ID operation. - */ -struct TALER_MERCHANT_WalletTemplateGetHandle -{ - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_MERCHANT_WalletTemplateGetCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; - -}; - - -/** - * Function called when we're done processing the - * HTTP GET /templates/$ID request. - * - * @param cls the `struct TALER_MERCHANT_TemplateGetHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_get_template_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_WalletTemplateGetHandle *tgh = cls; - const json_t *json = response; - struct TALER_MERCHANT_WalletTemplateGetResponse tgr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - tgh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got /templates/$ID response with status code %u\n", - (unsigned int) response_code); - switch (response_code) - { - case MHD_HTTP_OK: - { - const json_t *contract; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_object_const ("template_contract", - &contract), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK == - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - tgr.details.ok.template_contract = contract; - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_wallet_template_get_cancel (tgh); - return; - } - tgr.hr.http_status = 0; - tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - case MHD_HTTP_UNAUTHORIZED: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, merchant says we need to authenticate. */ - break; - case MHD_HTTP_NOT_FOUND: - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - tgr.hr.ec = TALER_JSON_get_error_code (json); - tgr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) tgr.hr.ec); - break; - } - tgh->cb (tgh->cb_cls, - &tgr); - TALER_MERCHANT_wallet_template_get_cancel (tgh); -} - - -struct TALER_MERCHANT_WalletTemplateGetHandle * -TALER_MERCHANT_wallet_template_get ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *template_id, - TALER_MERCHANT_WalletTemplateGetCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_WalletTemplateGetHandle *tgh; - CURL *eh; - - tgh = GNUNET_new (struct TALER_MERCHANT_WalletTemplateGetHandle); - tgh->ctx = ctx; - tgh->cb = cb; - tgh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "templates/%s", - template_id); - tgh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == tgh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (tgh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tgh->url); - eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); - tgh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_get_template_finished, - tgh); - return tgh; -} - - -void -TALER_MERCHANT_wallet_template_get_cancel ( - struct TALER_MERCHANT_WalletTemplateGetHandle *tgh) -{ - if (NULL != tgh->job) - GNUNET_CURL_job_cancel (tgh->job); - GNUNET_free (tgh->url); - GNUNET_free (tgh); -} diff --git a/src/lib/merchant_api_wallet_post_order_refund.c b/src/lib/merchant_api_wallet_post_order_refund.c @@ -1,345 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file merchant_api_wallet_post_order_refund.c - * @brief Implementation of the (public) POST /orders/ID/refund request - * @author Jonathan Buchanan - */ -#include "taler/platform.h" -#include <curl/curl.h> -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_merchant_service.h" -#include "merchant_api_common.h" -#include "merchant_api_curl_defaults.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_signatures.h> -#include <taler/taler_curl_lib.h> - -/** - * Maximum number of refunds we return. - */ -#define MAX_REFUNDS 1024 - -/** - * Handle for a (public) POST /orders/ID/refund operation. - */ -struct TALER_MERCHANT_WalletOrderRefundHandle -{ - /** - * Complete URL where the backend offers /refund - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * The CURL context to connect to the backend - */ - struct GNUNET_CURL_Context *ctx; - - /** - * The callback to pass the backend response to - */ - TALER_MERCHANT_WalletRefundCallback cb; - - /** - * Clasure to pass to the callback - */ - void *cb_cls; - - /** - * Handle for the request - */ - struct GNUNET_CURL_Job *job; -}; - - -/** - * Callback to process (public) POST /orders/ID/refund response - * - * @param cls the `struct TALER_MERCHANT_OrderRefundHandle` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not JSON - */ -static void -handle_refund_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_WalletOrderRefundHandle *orh = cls; - const json_t *json = response; - struct TALER_MERCHANT_WalletRefundResponse wrr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - orh->job = NULL; - switch (response_code) - { - case 0: - wrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - const json_t *refunds; - unsigned int refund_len; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ( - "refund_amount", - &wrr.details.ok.refund_amount), - GNUNET_JSON_spec_array_const ( - "refunds", - &refunds), - GNUNET_JSON_spec_fixed_auto ( - "merchant_pub", - &wrr.details.ok.merchant_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - wrr.hr.http_status = 0; - break; - } - refund_len = json_array_size (refunds); - if ( (json_array_size (refunds) != (size_t) refund_len) || - (refund_len > MAX_REFUNDS) ) - { - GNUNET_break (0); - wrr.hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; - wrr.hr.http_status = 0; - break; - } - { - struct TALER_MERCHANT_RefundDetail rds[GNUNET_NZL (refund_len)]; - - memset (rds, - 0, - sizeof (rds)); - for (unsigned int i = 0; i<refund_len; i++) - { - struct TALER_MERCHANT_RefundDetail *rd = &rds[i]; - const json_t *jrefund = json_array_get (refunds, - i); - const char *refund_status_type; - uint32_t exchange_status; - uint32_t eec = 0; - struct GNUNET_JSON_Specification espec[] = { - GNUNET_JSON_spec_string ("type", - &refund_status_type), - GNUNET_JSON_spec_uint32 ("exchange_status", - &exchange_status), - GNUNET_JSON_spec_uint64 ("rtransaction_id", - &rd->rtransaction_id), - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &rd->coin_pub), - TALER_JSON_spec_amount_any ("refund_amount", - &rd->refund_amount), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("exchange_reply", - &rd->hr.reply), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("exchange_code", - &eec), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (jrefund, - espec, - NULL, NULL)) - { - GNUNET_break_op (0); - wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - wrr.hr.http_status = 0; - goto finish; - } - - rd->hr.http_status = exchange_status; - rd->hr.ec = (enum TALER_ErrorCode) eec; - switch (exchange_status) - { - case MHD_HTTP_OK: - { - struct GNUNET_JSON_Specification rspec[] = { - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &rd->details.ok.exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &rd->details.ok.exchange_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (jrefund, - rspec, - NULL, - NULL)) - { - GNUNET_break_op (0); - wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - wrr.hr.http_status = 0; - goto finish; - } - /* check that type field is correct */ - if (0 != strcmp ("success", - refund_status_type)) - { - GNUNET_break_op (0); - wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - wrr.hr.http_status = 0; - goto finish; - } - } - break; /* end MHD_HTTP_OK */ - default: - if (0 != strcmp ("failure", - refund_status_type)) - { - GNUNET_break_op (0); - wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - wrr.hr.http_status = 0; - goto finish; - } - } /* switch on exchange status code */ - } /* for all refunds */ - - wrr.details.ok.refunds = rds; - wrr.details.ok.refunds_length = refund_len; - orh->cb (orh->cb_cls, - &wrr); - TALER_MERCHANT_wallet_post_order_refund_cancel (orh); - return; - } /* end 'rds' scope */ - } /* case MHD_HTTP_OK */ - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_CONFLICT: - case MHD_HTTP_NOT_FOUND: - wrr.hr.ec = TALER_JSON_get_error_code (json); - wrr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - GNUNET_break_op (0); /* unexpected status code */ - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &wrr.hr); - break; - } -finish: - orh->cb (orh->cb_cls, - &wrr); - TALER_MERCHANT_wallet_post_order_refund_cancel (orh); -} - - -struct TALER_MERCHANT_WalletOrderRefundHandle * -TALER_MERCHANT_wallet_post_order_refund ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const char *order_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - TALER_MERCHANT_WalletRefundCallback cb, - void *cb_cls) -{ - struct TALER_MERCHANT_WalletOrderRefundHandle *orh; - json_t *req; - CURL *eh; - - orh = GNUNET_new (struct TALER_MERCHANT_WalletOrderRefundHandle); - orh->ctx = ctx; - orh->cb = cb; - orh->cb_cls = cb_cls; - { - char *path; - - GNUNET_asprintf (&path, - "orders/%s/refund", - order_id); - orh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } - if (NULL == orh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (orh); - return NULL; - } - req = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("h_contract", - h_contract_terms)); - eh = TALER_MERCHANT_curl_easy_get_ (orh->url); - if (GNUNET_OK != - TALER_curl_easy_post (&orh->post_ctx, - eh, - req)) - { - GNUNET_break (0); - json_decref (req); - curl_easy_cleanup (eh); - GNUNET_free (orh->url); - GNUNET_free (orh); - return NULL; - } - json_decref (req); - orh->job = GNUNET_CURL_job_add2 (ctx, - eh, - orh->post_ctx.headers, - &handle_refund_finished, - orh); - if (NULL == orh->job) - { - GNUNET_free (orh->url); - GNUNET_free (orh); - return NULL; - } - return orh; -} - - -void -TALER_MERCHANT_wallet_post_order_refund_cancel ( - struct TALER_MERCHANT_WalletOrderRefundHandle *orh) -{ - if (NULL != orh->job) - { - GNUNET_CURL_job_cancel (orh->job); - orh->job = NULL; - } - TALER_curl_easy_post_finished (&orh->post_ctx); - GNUNET_free (orh->url); - GNUNET_free (orh); -} - - -/* end of merchant_api_wallet_post_order_refund.c */