merchant

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

commit 432177857128fd3b541d861eff160a817cb4c1bc
parent 374146b643d5238ef1fdad0b5b1ceffc27d6906a
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 22 Mar 2026 13:58:13 +0100

fix API and tests to work also with donation tokens

Diffstat:
Msrc/include/taler/taler-merchant/post-orders-ORDER_ID-pay-new.h | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/lib/Makefile.am | 1+
Msrc/lib/merchant_api_post-orders-ORDER_ID-pay-new.c | 105++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/testing/test_merchant_api.c | 25+++++++++++++++++++------
Msrc/testing/testing_api_cmd_pay_order.c | 43+++++++++----------------------------------
5 files changed, 200 insertions(+), 42 deletions(-)

diff --git a/src/include/taler/taler-merchant/post-orders-ORDER_ID-pay-new.h b/src/include/taler/taler-merchant/post-orders-ORDER_ID-pay-new.h @@ -25,6 +25,11 @@ #include <taler/taler-merchant/common.h> +/** + * Forward-declaration here to keep the API stable even if + * the Donau header is not available. + */ +struct DONAU_BlindedUniqueDonorIdentifierKeyPair; /** * One coin used to pay (frontend / external wallet mode). @@ -256,7 +261,12 @@ enum TALER_MERCHANT_PostOrdersPayOption /** * Output tokens as JSON array (for frontend mode). */ - TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_TOKENS_JSON + TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_TOKENS_JSON, + + /** + * Output donation receipts. + */ + TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_DONAU }; @@ -296,6 +306,9 @@ struct TALER_MERCHANT_PostOrdersPayOptionValue */ struct { + /** + * Length of @e tokens array + */ unsigned int num; const struct TALER_MERCHANT_PostOrdersPayUsedToken *tokens; } used_tokens; @@ -306,6 +319,9 @@ struct TALER_MERCHANT_PostOrdersPayOptionValue */ struct { + /** + * Length of @e tokens array + */ unsigned int num; const struct TALER_MERCHANT_PostOrdersPayUseToken *tokens; } use_tokens; @@ -316,6 +332,9 @@ struct TALER_MERCHANT_PostOrdersPayOptionValue */ struct { + /** + * Length of @e tokens array + */ unsigned int num; const struct TALER_MERCHANT_PostOrdersPayOutputToken *tokens; } output_tokens; @@ -326,6 +345,33 @@ struct TALER_MERCHANT_PostOrdersPayOptionValue */ json_t *output_tokens_json; + /** + * Value if @e option is + * #TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_DONAU. + */ + struct + { + /** + * Base URL of the donau. + */ + const char *donau_base_url; + + /** + * Array of blinded dontation receipts. + */ + const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps; + + /** + * Length of @e bkps array + */ + size_t num_bkps; + + /** + * Year for which the donation receipts are issued. + */ + uint64_t year; + } output_donau; + } details; }; @@ -499,6 +545,26 @@ struct TALER_MERCHANT_PostOrdersPayResponse /** + * Set blinded donation receipts as output. + * + * @param u base URL of the selected Donau + * @param y year for which receipts are requested + * @param l length of the @a bkps array + * @param a array of blinded donation receipts + * @return representation of the option + */ +#define TALER_MERCHANT_post_orders_pay_option_output_donau(u,y,l,a) \ + (const struct TALER_MERCHANT_PostOrdersPayOptionValue) \ + { \ + .option = TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_DONAU, \ + .details.output_donau.donau_base_url = (u), \ + .details.output_donau.bkps = (a), \ + .details.output_donau.num_bkps = (l), \ + .details.output_donau.year = (y) \ + } + + +/** * Set the requested options for the operation. * * @param poph the request to set the options for diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -101,6 +101,7 @@ if HAVE_DONAU merchant_api_delete-private-donau-DONAU_SERIAL-new.c libtalermerchant_la_LIBADD += \ + -ldonaujson \ -ldonau endif diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-pay-new.c b/src/lib/merchant_api_post-orders-ORDER_ID-pay-new.c @@ -34,7 +34,10 @@ #include <taler/taler_json_lib.h> #include <taler/taler_curl_lib.h> #include <taler/taler_signatures.h> - +#if HAVE_DONAU_DONAU_SERVICE_H +#include <donau/donau_service.h> +#include <donau/donau_json_lib.h> +#endif /** * Handle for a POST /orders/$ORDER_ID/pay operation. @@ -197,6 +200,26 @@ struct TALER_MERCHANT_PostOrdersPayHandle json_t *output_tokens_json; /** + * Base URL of the selected donau for donation receipts. + */ + char *donau_url; + + /** + * Tax year used for the donau. + */ + unsigned int donau_year; + + /** + * Array of blinded donation receipts. + */ + struct DONAU_BlindedUniqueDonorIdentifierKeyPair *donau_bkps; + + /** + * Length of the @e donau_bkps array. + */ + size_t num_donau_bkps; + + /** * Set to true if this is the wallet mode (private keys available). */ bool am_wallet; @@ -630,6 +653,40 @@ TALER_MERCHANT_post_orders_pay_set_options_ ( poph->output_tokens_json = json_incref ( options[i].details.output_tokens_json); break; + case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_DONAU: +#if HAVE_DONAU_DONAU_SERVICE_H + if (NULL != poph->donau_url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Only one set of donation receipts is allowed to be specified\n"); + return GNUNET_NO; + } + poph->donau_url + = GNUNET_strdup (options[i].details.output_donau.donau_base_url); + poph->donau_year + = options[i].details.output_donau.year; + poph->num_donau_bkps = options[i].details.output_donau.num_bkps; + poph->donau_bkps = GNUNET_new_array ( + poph->num_donau_bkps, + struct DONAU_BlindedUniqueDonorIdentifierKeyPair); + for (size_t j=0; j<poph->num_donau_bkps; j++) + { + const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *src + = &options[i].details.output_donau.bkps[j]; + struct DONAU_BlindedUniqueDonorIdentifierKeyPair *dst + = &poph->donau_bkps[j]; + + dst->h_donation_unit_pub = src->h_donation_unit_pub; + dst->blinded_udi.blinded_message + = GNUNET_CRYPTO_blinded_message_incref ( + src->blinded_udi.blinded_message); + } + break; +#else + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Donation receipts are not supported by this build!\n"); + return GNUNET_NO; +#endif default: GNUNET_break (0); return GNUNET_SYSERR; @@ -671,8 +728,39 @@ TALER_MERCHANT_post_orders_pay_start ( { /* Wallet mode: sign coins and tokens, build wallet_data */ json_t *wallet_data = poph->wallet_data; + json_t *j_donau_data = NULL; struct GNUNET_HashCode wallet_data_hash; + if (NULL != poph->donau_url) + { + json_t *budis; + + budis = json_array (); + GNUNET_assert (NULL != budis); + for (size_t i=0; i<poph->num_donau_bkps; i++) + { + const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp + = &poph->donau_bkps[i]; + json_t *budikeypair = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("h_donation_unit_pub", + &bkp->h_donation_unit_pub), + DONAU_JSON_pack_blinded_donation_identifier ("blinded_udi", + &bkp->blinded_udi)); + + GNUNET_assert (0 == + json_array_append_new (budis, + budikeypair)); + } + + j_donau_data = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("url", + poph->donau_url), + GNUNET_JSON_pack_int64 ("year", + poph->donau_year), + GNUNET_JSON_pack_array_steal ("budikeypairs", + budis)); + } + /* Build output token envelopes JSON if we have output tokens */ if (0 < poph->num_output_tokens) { @@ -693,6 +781,7 @@ TALER_MERCHANT_post_orders_pay_start ( { GNUNET_break (0); json_decref (j_output_tokens); + json_decref (j_donau_data); return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; } } @@ -711,12 +800,17 @@ TALER_MERCHANT_post_orders_pay_start ( GNUNET_JSON_pack_int64 ("choice_index", poph->choice_index), GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("donau", + j_donau_data)), + GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_array_incref ("tokens_evs", j_output_tokens))); } TALER_json_hash (wallet_data, &wallet_data_hash); } + json_decref (j_donau_data); + j_donau_data = NULL; if ( (0 < poph->num_use_tokens || 0 < poph->num_output_tokens || NULL != poph->output_tokens_json) @@ -1004,6 +1098,15 @@ TALER_MERCHANT_post_orders_pay_cancel ( } GNUNET_free (poph->coins); } + for (size_t j = 0; j<poph->num_donau_bkps; j++) + { + struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bpk + = &poph->donau_bkps[j]; + + GNUNET_CRYPTO_blinded_message_decref (bpk->blinded_udi.blinded_message); + } + GNUNET_free (poph->donau_bkps); + GNUNET_free (poph->donau_url); GNUNET_free (poph->used_tokens); GNUNET_free (poph->use_tokens); GNUNET_free (poph->output_tokens); diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c @@ -3301,23 +3301,36 @@ run (void *cls, repurchase), TALER_TESTING_cmd_batch ("tokens", tokens), - - #ifdef HAVE_DONAU_DONAU_SERVICE_H +#ifdef HAVE_DONAU_DONAU_SERVICE_H TALER_TESTING_cmd_batch ("donau", donau), - #endif +#endif TALER_TESTING_cmd_merchant_get_statisticsamount ("stats-refund", merchant_url, - "refunds-granted", 6, 0, + "refunds-granted", + 6, + 0, MHD_HTTP_OK), TALER_TESTING_cmd_merchant_get_statisticscounter ("stats-tokens-issued", merchant_url, - "tokens-issued", 6, 0, + "tokens-issued", + 6, +#ifdef HAVE_DONAU_DONAU_SERVICE_H + 2, +#else + 0, +#endif MHD_HTTP_OK), TALER_TESTING_cmd_merchant_get_statisticscounter ("stats-tokens-used", merchant_url, - "tokens-used", 6, 0, + "tokens-used", + 6, +#ifdef HAVE_DONAU_DONAU_SERVICE_H + 2, +#else + 0, +#endif MHD_HTTP_OK), /** diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c @@ -149,11 +149,6 @@ struct MerchantDonauPayData * asynchronous calls are still pending, etc. */ unsigned int cs_pending; - - /** - * Budis Key Pairs json - */ - json_t *budis_json; }; @@ -321,26 +316,6 @@ prepare_donau_data (struct TALER_TESTING_Interpreter *is, GNUNET_break (0); } } - - { - json_t *budikeypairs = json_array (); - - GNUNET_assert (NULL != budikeypairs); - for (size_t i = 0; i < ss->num_bkps; i++) - { - json_t *budikeypair = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("h_donation_unit_pub", - &ss->bkps[i].h_donation_unit_pub), - DONAU_JSON_pack_blinded_donation_identifier ("blinded_udi", - &ss->bkps[i].blinded_udi) - ); - - /* steal the reference into the array */ - GNUNET_assert (0 == json_array_append_new (budikeypairs, - budikeypair)); - } - ss->budis_json = budikeypairs; - } } return GNUNET_OK; }; @@ -1446,20 +1421,20 @@ pay_run (void *cls, len_output_tokens, output_tokens))); -#if 1 #ifdef HAVE_DONAU_DONAU_SERVICE_H if (ps->donau_data.charity_reference) { - ADD ( - TALER_MERCHANT_POST_ORDERS_PAY_OPTION_DONAU_URL (ps->donau_data. - donau_url)); - ADD (TALER_MERCHANT_POST_ORDERS_PAY_OPTION_DONAU_YEAR (ps->donau_data.year - )); - ADD (TALER_MERCHANT_POST_ORDERS_PAY_OPTION_DONAU_BUDIS ( - ps->donau_data.budis_json)); + GNUNET_assert ( + GNUNET_OK == + TALER_MERCHANT_post_orders_pay_set_options ( + ps->oph, + TALER_MERCHANT_post_orders_pay_option_output_donau ( + ps->donau_data.donau_url, + ps->donau_data.year, + ps->donau_data.num_bkps, + ps->donau_data.bkps))); } #endif -#endif if (TALER_EC_NONE != TALER_MERCHANT_post_orders_pay_start (ps->oph, &pay_cb,