merchant

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

commit a0ce2fd3c295559f11e27c0f375d11d2e6a96908
parent e03008ad962fae68116da0fd4556ed93e4ffd852
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 30 Mar 2026 01:55:25 +0200

add options for GET /private/products API in libtalermerchant

Diffstat:
Msrc/include/taler/taler-merchant/get-private-products.h | 245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/merchant_api_get-private-products.c | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 424 insertions(+), 3 deletions(-)

diff --git a/src/include/taler/taler-merchant/get-private-products.h b/src/include/taler/taler-merchant/get-private-products.h @@ -26,6 +26,114 @@ /** + * Possible options we can set for the GET /private/products request. + */ +enum TALER_MERCHANT_GetPrivateProductsOption +{ + /** + * End of list of options. + */ + TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_END = 0, + + /** + * Return at most N values. Negative for descending by row ID, + * positive for ascending by row ID. Default is 20. + * @since protocol v12. + */ + TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT, + + /** + * Starting product_serial_id for pagination. + * @since protocol v12. + */ + TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET, + + /** + * Filter by category name (case-insensitive substring match). + * @since protocol v23. + */ + TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER, + + /** + * Filter by product name (case-insensitive substring match). + * @since protocol v23. + */ + TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER, + + /** + * Filter by product description (case-insensitive substring match). + * @since protocol v23. + */ + TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER, + + /** + * Filter by product group serial. + * @since protocol v25. + */ + TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL + +}; + + +/** + * Value for an option for the GET /private/products request. + */ +struct TALER_MERCHANT_GetPrivateProductsOptionValue +{ + + /** + * Type of the option being set. + */ + enum TALER_MERCHANT_GetPrivateProductsOption option; + + /** + * Specific option value. + */ + union + { + + /** + * Value if @e option is + * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT. + */ + int64_t limit; + + /** + * Value if @e option is + * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET. + */ + uint64_t offset; + + /** + * Value if @e option is + * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER. + */ + const char *category_filter; + + /** + * Value if @e option is + * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER. + */ + const char *name_filter; + + /** + * Value if @e option is + * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER. + */ + const char *description_filter; + + /** + * Value if @e option is + * #TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL. + */ + uint64_t product_group_serial; + + } details; + +}; + + +/** * Handle for a GET /private/products request. */ struct TALER_MERCHANT_GetPrivateProductsHandle; @@ -105,6 +213,143 @@ TALER_MERCHANT_get_private_products_create ( const char *url); +/** + * Terminate the list of the options. + * + * @return the terminating object of struct TALER_MERCHANT_GetPrivateProductsOptionValue + */ +#define TALER_MERCHANT_get_private_products_option_end_() \ + (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \ + { \ + .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_END \ + } + +/** + * Set limit on the number of results to return. + * + * @param l limit on the number of results to return + * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue + */ +#define TALER_MERCHANT_get_private_products_option_limit(l) \ + (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \ + { \ + .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT, \ + .details.limit = (l) \ + } + +/** + * Set row offset from which to return results. + * + * @param o offset to use + * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue + */ +#define TALER_MERCHANT_get_private_products_option_offset(o) \ + (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \ + { \ + .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET, \ + .details.offset = (o) \ + } + +/** + * Set category name filter (case-insensitive substring match). + * + * @param f category filter string + * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue + */ +#define TALER_MERCHANT_get_private_products_option_category_filter(f) \ + (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \ + { \ + .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER, \ + .details.category_filter = (f) \ + } + +/** + * Set product name filter (case-insensitive substring match). + * + * @param f name filter string + * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue + */ +#define TALER_MERCHANT_get_private_products_option_name_filter(f) \ + (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \ + { \ + .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER, \ + .details.name_filter = (f) \ + } + +/** + * Set product description filter (case-insensitive substring match). + * + * @param f description filter string + * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue + */ +#define TALER_MERCHANT_get_private_products_option_description_filter(f) \ + (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \ + { \ + .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER, \ + .details.description_filter = (f) \ + } + +/** + * Set product group serial filter. + * + * @param s product group serial to filter by + * @return representation of the option as a struct TALER_MERCHANT_GetPrivateProductsOptionValue + */ +#define TALER_MERCHANT_get_private_products_option_product_group_serial(s) \ + (const struct TALER_MERCHANT_GetPrivateProductsOptionValue) \ + { \ + .option = TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL, \ + .details.product_group_serial = (s) \ + } + + +/** + * Set the requested options for the operation. + * + * If any option fail other options may be or may be not applied. + * + * @param gpph the request to set the options for + * @param num_options length of the @a options array + * @param options an array of options + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure, + * #GNUNET_SYSERR on internal error + */ +enum GNUNET_GenericReturnValue +TALER_MERCHANT_get_private_products_set_options_ ( + struct TALER_MERCHANT_GetPrivateProductsHandle *gpph, + unsigned int num_options, + const struct TALER_MERCHANT_GetPrivateProductsOptionValue *options); + + +/** + * Set the requested options for the operation. + * + * If any option fail other options may be or may be not applied. + * + * It should be used with helpers that create required options, for example: + * + * TALER_MERCHANT_get_private_products_set_options ( + * gpph, + * TALER_MERCHANT_get_private_products_option_limit (20), + * TALER_MERCHANT_get_private_products_option_name_filter ("widget")); + * + * @param gpph the request to set the options for + * @param ... the list of the options, each option must be created + * by helpers TALER_MERCHANT_get_private_products_option_NAME(VALUE) + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure, + * #GNUNET_SYSERR on internal error + */ +#define TALER_MERCHANT_get_private_products_set_options(gpph,...) \ + TALER_MERCHANT_get_private_products_set_options_ ( \ + gpph, \ + TALER_MERCHANT_COMMON_OPTIONS_ARRAY_MAX_SIZE, \ + ((const struct TALER_MERCHANT_GetPrivateProductsOptionValue[]) \ + {__VA_ARGS__, TALER_MERCHANT_get_private_products_option_end_ () } \ + )) + + #ifndef TALER_MERCHANT_GET_PRIVATE_PRODUCTS_RESULT_CLOSURE /** * Type of the closure used by diff --git a/src/lib/merchant_api_get-private-products.c b/src/lib/merchant_api_get-private-products.c @@ -70,6 +70,46 @@ struct TALER_MERCHANT_GetPrivateProductsHandle * Reference to the execution context. */ struct GNUNET_CURL_Context *ctx; + + /** + * Starting row for pagination. + */ + uint64_t offset; + + /** + * Limit on number of results. + */ + int64_t limit; + + /** + * Category name filter, or NULL. + */ + char *category_filter; + + /** + * Product name filter, or NULL. + */ + char *name_filter; + + /** + * Product description filter, or NULL. + */ + char *description_filter; + + /** + * Product group serial filter. + */ + uint64_t product_group_serial; + + /** + * True if offset was explicitly set. + */ + bool have_offset; + + /** + * True if product_group_serial was explicitly set. + */ + bool have_product_group_serial; }; @@ -215,10 +255,68 @@ TALER_MERCHANT_get_private_products_create ( gpph = GNUNET_new (struct TALER_MERCHANT_GetPrivateProductsHandle); gpph->ctx = ctx; gpph->base_url = GNUNET_strdup (url); + gpph->limit = 20; + gpph->offset = INT64_MAX; return gpph; } +enum GNUNET_GenericReturnValue +TALER_MERCHANT_get_private_products_set_options_ ( + struct TALER_MERCHANT_GetPrivateProductsHandle *gpph, + unsigned int num_options, + const struct TALER_MERCHANT_GetPrivateProductsOptionValue *options) +{ + for (unsigned int i = 0; i < num_options; i++) + { + const struct TALER_MERCHANT_GetPrivateProductsOptionValue *opt = + &options[i]; + + switch (opt->option) + { + case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_END: + return GNUNET_OK; + case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_LIMIT: + gpph->limit = opt->details.limit; + break; + case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_OFFSET: + if (opt->details.offset > INT64_MAX) + { + GNUNET_break (0); + return GNUNET_NO; + } + gpph->offset = opt->details.offset; + gpph->have_offset = true; + break; + case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_CATEGORY_FILTER: + GNUNET_free (gpph->category_filter); + if (NULL != opt->details.category_filter) + gpph->category_filter = GNUNET_strdup (opt->details.category_filter); + break; + case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_NAME_FILTER: + GNUNET_free (gpph->name_filter); + if (NULL != opt->details.name_filter) + gpph->name_filter = GNUNET_strdup (opt->details.name_filter); + break; + case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_DESCRIPTION_FILTER: + GNUNET_free (gpph->description_filter); + if (NULL != opt->details.description_filter) + gpph->description_filter = + GNUNET_strdup (opt->details.description_filter); + break; + case TALER_MERCHANT_GET_PRIVATE_PRODUCTS_OPTION_PRODUCT_GROUP_SERIAL: + gpph->product_group_serial = opt->details.product_group_serial; + gpph->have_product_group_serial = true; + break; + default: + GNUNET_break (0); + return GNUNET_NO; + } + } + return GNUNET_OK; +} + + enum TALER_ErrorCode TALER_MERCHANT_get_private_products_start ( struct TALER_MERCHANT_GetPrivateProductsHandle *gpph, @@ -229,20 +327,95 @@ TALER_MERCHANT_get_private_products_start ( gpph->cb = cb; gpph->cb_cls = cb_cls; - gpph->url = TALER_url_join (gpph->base_url, - "private/products", - NULL); + { + char *cfilt = NULL; + char *nfilt = NULL; + char *dfilt = NULL; + char lbuf[30]; + char obuf[30]; + char pgsbuf[30]; + bool have_offset; + + GNUNET_snprintf (lbuf, + sizeof (lbuf), + "%lld", + (long long) gpph->limit); + GNUNET_snprintf (obuf, + sizeof (obuf), + "%llu", + (unsigned long long) gpph->offset); + if (gpph->have_product_group_serial) + GNUNET_snprintf (pgsbuf, + sizeof (pgsbuf), + "%llu", + (unsigned long long) gpph->product_group_serial); + if (gpph->limit > 0) + { + have_offset = gpph->have_offset + && (0 != gpph->offset); + } + else + { + have_offset = gpph->have_offset + && (INT64_MAX != gpph->offset); + } + if (NULL != gpph->category_filter) + (void) GNUNET_STRINGS_urlencode (strlen (gpph->category_filter), + gpph->category_filter, + &cfilt); + if (NULL != gpph->name_filter) + (void) GNUNET_STRINGS_urlencode (strlen (gpph->name_filter), + gpph->name_filter, + &nfilt); + if (NULL != gpph->description_filter) + (void) GNUNET_STRINGS_urlencode (strlen (gpph->description_filter), + gpph->description_filter, + &dfilt); + gpph->url = TALER_url_join (gpph->base_url, + "private/products", + "limit", + (20 != gpph->limit) + ? lbuf + : NULL, + "offset", + have_offset + ? obuf + : NULL, + "category_filter", + cfilt, + "name_filter", + nfilt, + "description_filter", + dfilt, + "product_group_serial", + gpph->have_product_group_serial + ? pgsbuf + : NULL, + NULL); + GNUNET_free (cfilt); + GNUNET_free (nfilt); + GNUNET_free (dfilt); + } if (NULL == gpph->url) + { + GNUNET_break (0); return TALER_EC_GENERIC_CONFIGURATION_INVALID; + } eh = TALER_MERCHANT_curl_easy_get_ (gpph->url); if (NULL == eh) + { + GNUNET_break (0); return TALER_EC_GENERIC_CONFIGURATION_INVALID; + } gpph->job = GNUNET_CURL_job_add (gpph->ctx, eh, &handle_get_products_finished, gpph); if (NULL == gpph->job) + { + GNUNET_break (0); return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } return TALER_EC_NONE; } @@ -257,6 +430,9 @@ TALER_MERCHANT_get_private_products_cancel ( gpph->job = NULL; } GNUNET_free (gpph->url); + GNUNET_free (gpph->category_filter); + GNUNET_free (gpph->name_filter); + GNUNET_free (gpph->description_filter); GNUNET_free (gpph->base_url); GNUNET_free (gpph); }