commit d1f0265a59c858eb03157fb83daf0a5bfebaf004
parent b63af4a1ddb829714189b34f56d0582f4a9ee54f
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Wed, 23 Apr 2025 14:24:10 +0200
playing around with pay function
Diffstat:
7 files changed, 876 insertions(+), 142 deletions(-)
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
@@ -10,6 +10,7 @@ talerinclude_HEADERS = \
taler_merchantdb_plugin.h \
taler_merchant_util.h \
taler_merchant_service.h \
+ taler_merchant_pay_service.h \
taler_merchant_testing_lib.h
if HAVE_DONAU
diff --git a/src/include/cutted_from_service b/src/include/cutted_from_service
@@ -0,0 +1,155 @@
+///* PART FROM WHICH IDEAS FOR NEW IMPLEMENTATION HAVE BEEN DEFINED*/
+//
+///* application *before* including this header should do: */
+//#define TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE struct PayState
+//
+//
+//#ifndef TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE
+//#define TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE void
+//#endif
+//
+//struct TALER_MERCHANT_OrderPayHandle *
+//TALER_MERCHANT_order_pay_create (
+// struct GNUNET_CURL_Context *ctx,
+// TALER_MERCHANT_OrderPayCallback pay_cb,
+// TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *pay_cb_cls)
+//{
+// ph->body = json_object (); // json_t *
+//}
+//
+//
+//struct TALER_MERCHANT_OrderPayOption
+//{
+// enum TALER_MERCHANT_OrderPayOptionType
+// {
+// TALER_MERCHANT_OrderPayOptionType_END = 0,
+// TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL,
+// TALER_MERCHANT_OrderPayOptionType_SESSION_ID,
+// TALER_MERCHANT_OrderPayOptionType_H_CONTRACT,
+// TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX,
+// TALER_MERCHANT_OrderPayOptionType_AMOUNT,
+// TALER_MERCHANT_OrderPayOptionType_MAX_FEE,
+// TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB,
+// TALER_MERCHANT_OrderPayOptionType_TIMESTAMP,
+// TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE,
+// TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE,
+// TALER_MERCHANT_OrderPayOptionType_H_WIRE,
+// TALER_MERCHANT_OrderPayOptionType_ORDER_ID,
+// TALER_MERCHANT_OrderPayOptionType_COINS,
+// TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS,
+// TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS
+// } ot;
+//
+// union
+// {
+// const char *merchant_url;
+// const char *session_id;
+// const struct TALER_PrivateContractHashP *h_contract;
+// int choice_index;
+// struct TALER_Amount amount;
+// struct TALER_Amount max_fee;
+// struct TALER_MerchantPublicKeyP merchant_pub;
+// struct GNUNET_TIME_Timestamp timestamp;
+// struct GNUNET_TIME_Timestamp refund_deadline;
+// struct GNUNET_TIME_Timestamp pay_deadline;
+// struct TALER_MerchantWireHashP h_wire;
+// const char *order_id;
+// struct
+// {
+// unsigned int num_coins;
+// const struct TALER_MERCHANT_PayCoin *coins;
+// } coins;
+// struct
+// {
+// unsigned int num_tokens;
+// const struct TALER_MERCHANT_UseToken *tokens;
+// } input_tokens;
+// struct
+// {
+// unsigned int num_output_tokens,
+// const struct TALER_MERCHANT_OutputToken *output_tokens;
+// } output_tokens;
+// } details;
+//};
+//
+///*FIXME: THIS PARTS NEEDS TO BE DEFINED CLEARLY and ideally in new file with previous struct*/
+//enum TALER_MERCHANT_OrderPayOptionErrorCode
+//TALER_MERCHANT_order_pay_set_options (
+// struct TALER_MERCHANT_OrderPayHandle *ph,
+// struct TALER_MERCHANT_OrderPayOption options[],
+// size_t max_options);
+//
+//{
+// for (i-- not 0 - terminateor && i < max_options; i++)
+// switch (options[i].type)
+// case ...
+// GNUNET_assert (0 ==
+// json_object_set_new (ph->body,
+// "field",
+// json_string ("foo")));
+// return OK;
+// case ...
+// GNUNET_assert (0 ==
+// json_object_set_new (ph->body,
+// "field",
+// json_bool (true)));
+//
+// return OK;
+// case ...
+// GNUNET_assert (0 ==
+// json_object_set_new (ph->body,
+// "wire_hash",
+// GNUNET_JSON_PACK (
+// GNUNET_JSON_pack_data_auto (NULL,
+// &options[i
+// ].details.
+// h_wire))))
+// return OK;
+//}
+//
+//}
+//return ERROR_UNKNOWN_OPTION;
+//}
+//
+//#define TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() \
+// (const struct TALER_MERCHANT_OrderPayOption) { \
+// .ot = TALER_MERCHANT_OrderPayOptionType_END \
+// }
+//
+//
+//#define TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID(session_id) \
+// (const struct TALER_MERCHANT_OrderPayOption) { \
+// .ot = TALER_MERCHANT_OrderPayOptionType_SESSION_ID, \
+// .details.session_id = session_id \
+// }
+//
+//#define TALER_MERCHANT_ORDER_PAY_SET_OPTIONS(ph,...) \
+// MHD_NOWARN_COMPOUND_LITERALS_ \
+// TALER_MERCHANT_order_pay_set_options ( \
+// daemon, \
+// ((const struct TALER_MERCHANT_OrderPayHandle[]) \
+// {__VA_ARGS__, TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE ()}), \
+// MHD_OPTIONS_ARRAY_MAX_SIZE) \
+//
+///* END OF THE PART THAT NEEDS TO BE EXTENDED */
+//
+//enum TALER_MERCHANT_OrderPayOptionErrorCode
+//TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph);
+
+//#if APP /*SAMPLE OF USAGE OF PREVIOUS DEFINITIONS*/
+//{
+// struct TALER_MERCHANT_OrderPayHandle *ph;
+//
+// ph = TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx,
+// TALER_MERCHANT_OrderPayCallback pay_cb,
+// pay_cb_cls);
+//
+// GNUNET_assert (OK ==
+// TALER_MERCHANT_ORDER_PAY_SET_OPTIONS (
+// ph,
+// TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID ("session")));
+// pay_start (ph);
+//}
+//#endif
+
+/* END OF LIST OF IDEAS */
+\ No newline at end of file
diff --git a/src/include/taler_merchant_pay_service.h b/src/include/taler_merchant_pay_service.h
@@ -0,0 +1,246 @@
+/*
+ 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 taler_merchant_pay_service.h
+ * @brief Payment‑specific interface extracted from taler_merchant_service.h
+ * **Only** declarations and helper‑macros live here – the actual
+ * implementation must be provided in the accompanying *.c file.
+ * This header can be included by both merchant front‑ends and wallets
+ * that want to create or proxy /orders/$ID/pay requests.
+ */
+#ifndef TALER_MERCHANT_PAY_SERVICE_H
+#define TALER_MERCHANT_PAY_SERVICE_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_error_codes.h>
+#include <taler/taler_exchange_service.h>
+#include "taler_merchant_service.h"
+#include <gnunet/gnunet_time_lib.h>
+
+
+/**
+ * seems a bit odd
+ */
+// #define TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE struct PayState
+
+#ifndef TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE
+#define TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE void
+#endif
+
+/**
+ * Opaque handle returned by the payment helper APIs declared below.
+ */
+struct TALER_MERCHANT_OrderPayHandle;
+
+/**
+ * Which option is being supplied to #TALER_MERCHANT_order_pay_set_options().
+ */
+enum TALER_MERCHANT_OrderPayOptionType
+{
+ TALER_MERCHANT_OrderPayOptionType_END = 0,
+ TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL,
+ TALER_MERCHANT_OrderPayOptionType_SESSION_ID,
+ TALER_MERCHANT_OrderPayOptionType_H_CONTRACT,
+ TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX,
+ TALER_MERCHANT_OrderPayOptionType_AMOUNT,
+ TALER_MERCHANT_OrderPayOptionType_MAX_FEE,
+ TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB,
+ TALER_MERCHANT_OrderPayOptionType_TIMESTAMP,
+ TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE,
+ TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE,
+ TALER_MERCHANT_OrderPayOptionType_H_WIRE,
+ TALER_MERCHANT_OrderPayOptionType_ORDER_ID,
+ TALER_MERCHANT_OrderPayOptionType_COINS,
+ TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS,
+ TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS,
+ /*New objects go in here*/
+ TALER_MERCHANT_OrderPayOptionType_LENGTH
+};
+
+/**
+ * Container describing *one* option for a payment request.
+ */
+struct TALER_MERCHANT_OrderPayOption
+{
+ enum TALER_MERCHANT_OrderPayOptionType ot; /**< Which field inside the union is valid */
+ union
+ {
+ const char *merchant_url;
+ const char *session_id;
+ const struct TALER_PrivateContractHashP *h_contract;
+ int choice_index;
+ struct TALER_Amount amount;
+ struct TALER_Amount max_fee;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ struct GNUNET_TIME_Timestamp timestamp;
+ struct GNUNET_TIME_Timestamp refund_deadline;
+ struct GNUNET_TIME_Timestamp pay_deadline;
+ struct TALER_MerchantWireHashP h_wire;
+ const char *order_id;
+ struct {
+ unsigned int num_coins;
+ const struct TALER_MERCHANT_PayCoin *coins;
+ } coins;
+ struct {
+ unsigned int num_tokens;
+ const struct TALER_MERCHANT_UseToken *tokens;
+ } input_tokens;
+ struct {
+ unsigned int num_output_tokens;
+ const struct TALER_MERCHANT_OutputToken *output_tokens;
+ } output_tokens;
+ } details;
+};
+
+
+/**
+ * Status codes returned from #TALER_MERCHANT_order_pay_set_options() and
+ * #TALER_MERCHANT_order_pay_start().
+ */
+enum TALER_MERCHANT_OrderPayOptionErrorCode
+{
+ TALER_MERCHANT_OPOEC_OK = 0, /**< everything fine */
+ TALER_MERCHANT_OPOEC_UNKNOWN_OPTION, /**< unrecognised option kind */
+ TALER_MERCHANT_OPOEC_DUPLICATE_OPTION, /**< option given more than once */
+ TALER_MERCHANT_OPOEC_INVALID_VALUE, /**< semantic/format error in value */
+ TALER_MERCHANT_OPOEC_MISSING_MANDATORY /**< required field missing at start */
+};
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_END \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL(_url) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL, \
+ .details.merchant_url = (_url) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID(_sid) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_SESSION_ID,\
+ .details.session_id = (_sid) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_H_CONTRACT(_h) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_H_CONTRACT, \
+ .details.h_contract = (_h) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_CHOICE_INDEX(_idx) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX,\
+ .details.choice_index = (_idx) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT(_amt) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_AMOUNT,\
+ .details.amount = *(_amt) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE(_fee) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_MAX_FEE,\
+ .details.max_fee = *(_fee) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_PUB(_mpub) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB, \
+ .details.merchant_pub = *(_mpub) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_TIMESTAMP(_ts) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_TIMESTAMP, \
+ .details.timestamp = (_ts) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_REFUND_DEADLINE(_ts) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE, \
+ .details.refund_deadline = (_ts) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_PAY_DEADLINE(_ts) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE, \
+ .details.pay_deadline = (_ts) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_H_WIRE(_hwire) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_H_WIRE, \
+ .details.h_wire = *(_hwire) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_ORDER_ID(_oid) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_ORDER_ID,\
+ .details.order_id = (_oid) \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_COINS(_num,_coins) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_COINS, \
+ .details.coins = { .num_coins = (_num), \
+ .coins = (_coins) } \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_INPUT_TOKENS(_num,_tokens) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS, \
+ .details.input_tokens = { .num_tokens = (_num), \
+ .tokens = (_tokens) } \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_OPTION_OUTPUT_TOKENS(_num,_otokens) \
+ (struct TALER_MERCHANT_OrderPayOption){ \
+ .ot = TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS, \
+ .details.output_tokens = { .num_output_tokens = (_num), \
+ .output_tokens = (_otokens) } \
+ }
+
+#define TALER_MERCHANT_ORDER_PAY_SET_OPTIONS(ph,...) \
+ MHD_NOWARN_COMPOUND_LITERALS_ \
+ TALER_MERCHANT_order_pay_set_options ( \
+ daemon, \
+ ((const struct TALER_MERCHANT_OrderPayHandle[]) \
+ {__VA_ARGS__, TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE ()}), \
+ MHD_OPTIONS_ARRAY_MAX_SIZE) \
+
+struct TALER_MERCHANT_OrderPayHandle *
+TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx,
+ TALER_MERCHANT_OrderPayCallback pay_cb,
+ TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *pay_cb_cls);
+
+enum TALER_MERCHANT_OrderPayOptionErrorCode
+TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph,
+ const struct TALER_MERCHANT_OrderPayOption options[],
+ size_t max_options);
+
+enum TALER_MERCHANT_OrderPayOptionErrorCode
+TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph);
+
+void
+TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *ph);
+
+
+#endif /* TALER_MERCHANT_PAY_SERVICE_H */
+\ No newline at end of file
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h
@@ -3566,144 +3566,6 @@ TALER_MERCHANT_order_pay (
TALER_MERCHANT_OrderPayCallback pay_cb,
void *pay_cb_cls);
-/* application *before* including this header should do: */
-#define TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE struct PayState
-
-
-#ifndef TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE
-#define TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE void
-#endif
-
-struct TALER_MERCHANT_OrderPayHandle *
-TALER_MERCHANT_order_pay_create (
- struct GNUNET_CURL_Context *ctx,
- TALER_MERCHANT_OrderPayCallback pay_cb,
- TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *pay_cb_cls)
-{
- ph->body = json_object (); // json_t *
-}
-
-
-struct TALER_MERCHANT_OrderPayOption
-{
- enum TALER_MERCHANT_OrderPayOptionType
- {
- TALER_MERCHANT_OrderPayOptionType_END = 0,
- TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL,
- TALER_MERCHANT_OrderPayOptionType_SESSION_ID,
- TALER_MERCHANT_OrderPayOptionType_H_CONTRACT,
- TALER_MERCHANT_OrderPayOptionType_XXX,
- } ot;
-
- union
- {
- const char *merchant_url;
- const char *session_id;
- const struct TALER_PrivateContractHashP *h_contract;
- int choice_index;
- struct TALER_Amount amount;
- struct TALER_Amount max_fee;
- struct TALER_MerchantPublicKeyP merchant_pub;
- struct GNUNET_TIME_Timestamp timestamp;
- struct GNUNET_TIME_Timestamp refund_deadline;
- struct GNUNET_TIME_Timestamp pay_deadline;
- struct TALER_MerchantWireHashP h_wire;
- const char *order_id;
- struct
- {
- unsigned int num_coins;
- const struct TALER_MERCHANT_PayCoin *coins;
- } coins;
- struct
- {
- unsigned int num_tokens;
- const struct TALER_MERCHANT_UseToken *tokens;
- } input_tokens;
- struct
- {
- unsigned int num_output_tokens,
- const struct TALER_MERCHANT_OutputToken *output_tokens;
- } output_tokens;
- } details;
-};
-
-enum TALER_MERCHANT_OrderPayOptionErrorCode
-TALER_MERCHANT_order_pay_set_options (
- struct TALER_MERCHANT_OrderPayHandle *ph,
- struct TALER_MERCHANT_OrderPayOption options[],
- size_t max_options);
-
-{
- for (i-- not 0 - terminateor && i < max_options; i++)
- switch (options[i].type)
- case ...
- GNUNET_assert (0 ==
- json_object_set_new (ph->body,
- "field",
- json_string ("foo")));
- return OK;
- case ...
- GNUNET_assert (0 ==
- json_object_set_new (ph->body,
- "field",
- json_bool (true)));
-
- return OK;
- case ...
- GNUNET_assert (0 ==
- json_object_set_new (ph->body,
- "wire_hash",
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto (NULL,
- &options[i
- ].details.
- h_wire))))
- return OK;
-}
-
-}
-return ERROR_UNKNOWN_OPTION;
-}
-
-#define TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() \
- (const struct TALER_MERCHANT_OrderPayOption) { \
- .ot = TALER_MERCHANT_OrderPayOptionType_END \
- }
-
-
-#define TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID(session_id) \
- (const struct TALER_MERCHANT_OrderPayOption) { \
- .ot = TALER_MERCHANT_OrderPayOptionType_SESSION_ID, \
- .details.session_id = session_id \
- }
-
-#define TALER_MERCHANT_ORDER_PAY_SET_OPTIONS(ph,...) \
- MHD_NOWARN_COMPOUND_LITERALS_ \
- TALER_MERCHANT_order_pay_set_options ( \
- daemon, \
- ((const struct TALER_MERCHANT_OrderPayHandle[]) \
- {__VA_ARGS__, TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE ()}), \
- MHD_OPTIONS_ARRAY_MAX_SIZE) \
-
-enum TALER_MERCHANT_OrderPayOptionErrorCode
-TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph);
-
-#if APP
-{
- struct TALER_MERCHANT_OrderPayHandle *ph;
-
- ph = TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx,
- TALER_MERCHANT_OrderPayCallback pay_cb,
- pay_cb_cls);
-
- GNUNET_assert (OK ==
- TALER_MERCHANT_ORDER_PAY_SET_OPTIONS (
- ph,
- TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID ("session")));
- pay_start (ph);
-}
-#endif
-
/**
* Cancel a POST /orders/$ID/pay request. Note that if you cancel a request
* like this, you have no assurance that the request has not yet been
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
@@ -67,7 +67,8 @@ libtalermerchant_la_SOURCES = \
merchant_api_post_webhooks.c \
merchant_api_wallet_get_order.c \
merchant_api_wallet_get_template.c \
- merchant_api_wallet_post_order_refund.c
+ merchant_api_wallet_post_order_refund.c \
+ taler_merchant_pay_service.c
libtalermerchant_la_LIBADD = \
-ltalerexchange \
diff --git a/src/lib/taler_merchant_pay_service.c b/src/lib/taler_merchant_pay_service.c
@@ -0,0 +1,469 @@
+/*
+ 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 taler_merchant_pay_service.h
+ * @brief Implementation of the the ideology from the pay_service
+ */
+
+#include "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_merchant_service.h"
+#include "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>
+
+/* ------------------------------------------------------------------------- */
+/* internal helpers */
+/* ------------------------------------------------------------------------- */
+
+/** Opaque handle implementation. */
+struct TALER_MERCHANT_OrderPayHandle
+{
+ struct GNUNET_CURL_Context *ctx;
+ TALER_MERCHANT_OrderPayCallback cb;
+ TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *cb_cls;
+
+ /* mandatory scalars: */
+ char *merchant_url;
+ char *order_id;
+
+ struct GNUNET_TIME_Timestamp timestamp;
+ struct GNUNET_TIME_Timestamp refund_deadline;
+
+ /* for wallet mode: */
+ bool has_h_contract;
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ bool has_merchant_pub;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ bool has_choice_index;
+ int choice_index;
+
+ /* raw arrays as passed in via set_options(): */
+ struct {
+ unsigned int num_coins;
+ const struct TALER_MERCHANT_PayCoin *coins;
+ } coins;
+
+ struct {
+ unsigned int num_tokens;
+ const struct TALER_MERCHANT_UseToken *tokens;
+ } input_tokens;
+
+ struct {
+ unsigned int num_output_tokens;
+ const struct TALER_MERCHANT_OutputToken *output_tokens;
+ } output_tokens;
+
+ /* computed once we see both choice_index and token_evs(outputs in the env): */
+ json_t *wallet_data;
+ struct GNUNET_HashCode wallet_data_hash;
+
+ /* the JSON body we’ll keep appending into… */
+ json_t *body;
+
+ /* final URL and CURL plumbing */
+ char *url;
+ struct TALER_CURL_PostContext post_ctx;
+ struct GNUNET_CURL_Job *job;
+
+ bool field_seen[TALER_MERCHANT_OrderPayOptionType_LENGTH];
+};
+
+/* create / destroy */
+
+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;
+}
+
+void
+TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *ph)
+{
+ if (ph->job)
+ GNUNET_CURL_job_cancel (ph->job);
+ TALER_curl_easy_post_finished (&ph->post_ctx);
+ json_decref (ph->body);
+ if (ph->wallet_data) json_decref (ph->wallet_data);
+ GNUNET_free (ph->url);
+ GNUNET_free (ph->merchant_url);
+ GNUNET_free (ph->order_id);
+ GNUNET_free (ph);
+}
+
+/* option setter */
+
+static enum TALER_MERCHANT_OrderPayOptionErrorCode
+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;
+}
+
+enum TALER_MERCHANT_OrderPayOptionErrorCode
+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_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;
+ {
+ json_t *js = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_contract", o->details.h_contract)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX:
+ ph->choice_index = o->details.choice_index;
+ ph->has_choice_index= true;
+ {
+ json_t *js = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("choice_index",
+ o->details.choice_index)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_AMOUNT:
+ {
+ json_t *js = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("amount",
+ &o->details.amount)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_MAX_FEE:
+ {
+ json_t *js = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("max_fee",
+ &o->details.max_fee)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB:
+ ph->merchant_pub = o->details.merchant_pub;
+ ph->has_merchant_pub = true;
+ {
+ json_t *js = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("merchant_pub",
+ &o->details.merchant_pub)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_TIMESTAMP:
+ {
+ ph->timestamp = o->details.timestamp;
+
+ json_t *js = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("timestamp",
+ o->details.timestamp)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE:
+ {
+ ph->refund_deadline = o->details.refund_deadline;
+
+ //Do we really need to pack it?
+ json_t *js = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("refund_deadline",
+ o->details.refund_deadline)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE:
+ {
+
+
+ json_t *js = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("pay_deadline",
+ o->details.pay_deadline)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_H_WIRE:
+ {
+ json_t *js = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_wire",
+ &o->details.h_wire)
+ );
+ return store_json_option (ph, o->ot, js);
+ }
+
+ case TALER_MERCHANT_OrderPayOptionType_COINS:
+ /* stash for later signing */
+ GNUNET_assert (o->details.coins.num_coins > 0);
+ ph->coins.num_coins = o->details.coins.num_coins;
+ ph->coins.coins = o->details.coins.coins;
+ ph->field_seen[o->ot] = true;
+ 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;
+ ph->field_seen[o->ot] = true;
+ 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 token_evs */
+ json_t *arr = json_array ();
+ 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)
+ );
+ json_array_append_new (arr, je);
+ }
+ json_t *js = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("token_evs", arr)
+ );
+ store_json_option (ph, o->ot, js);
+ }
+ break;
+
+ default:
+ return TALER_MERCHANT_OPOEC_UNKNOWN_OPTION;
+ }
+ }
+ return TALER_MERCHANT_OPOEC_OK;
+}
+
+/* ============= network submission ============= */
+
+static void
+handle_finished (void *cls, long hc, const void *resp)
+{
+ struct TALER_MERCHANT_OrderPayHandle *ph = cls;
+ struct TALER_MERCHANT_PayResponse pr = {
+ .hr.http_status = (unsigned int) hc,
+ .hr.reply = resp ? (const json_t *)resp : NULL,
+ .hr.ec = (resp && json_is_object (resp))
+ ? TALER_JSON_get_error_code ((const json_t *)resp)
+ : TALER_EC_NONE,
+ .hr.hint = (resp && json_is_object (resp))
+ ? TALER_JSON_get_error_hint ((const json_t *)resp)
+ : NULL
+ };
+
+ ph->job = NULL;
+ ph->cb (ph->cb_cls, &pr);
+ TALER_MERCHANT_order_pay_cancel (ph);
+}
+
+static enum TALER_MERCHANT_OrderPayOptionErrorCode
+fire_request (struct TALER_MERCHANT_OrderPayHandle *ph)
+{
+ char *path;
+ GNUNET_asprintf (&path, "orders/%s/pay", ph->order_id);
+ ph->url = TALER_url_join (ph->merchant_url, path, NULL);
+ GNUNET_free (path);
+ if (!ph->url) return TALER_MERCHANT_OPOEC_INVALID_VALUE;
+
+ CURL *eh = TALER_MERCHANT_curl_easy_get_ (ph->url);
+ if (GNUNET_OK != TALER_curl_easy_post (&ph->post_ctx, eh, ph->body)) {
+ curl_easy_cleanup (eh);
+ return TALER_MERCHANT_OPOEC_INVALID_VALUE;
+ }
+
+ ph->job = GNUNET_CURL_job_add2 (ph->ctx, eh, ph->post_ctx.headers,
+ &handle_finished, ph);
+ return TALER_MERCHANT_OPOEC_OK;
+}
+
+enum TALER_MERCHANT_OrderPayOptionErrorCode
+TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph)
+{
+ /* all the old mandatory checks */
+ if (!ph->merchant_url || !ph->order_id)
+ return TALER_MERCHANT_OPOEC_MISSING_MANDATORY;
+ if (!ph->field_seen[TALER_MERCHANT_OrderPayOptionType_COINS])
+ return TALER_MERCHANT_OPOEC_MISSING_MANDATORY;
+ if (!ph->field_seen[TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE])
+ return TALER_MERCHANT_OPOEC_MISSING_MANDATORY;
+
+ /* --- build wallet_data hash for signing coins & tokens --- */
+ if (ph->has_choice_index) {
+ /* wallet_data = { choice_index: …, token_evs: … } */
+ ph->wallet_data = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64("choice_index", ph->choice_index),
+ GNUNET_JSON_pack_allow_null(
+ GNUNET_JSON_pack_array_incref("token_evs", ph->body))
+ );
+ TALER_json_hash (ph->wallet_data, &ph->wallet_data_hash);
+ }
+
+ /* --- sign & pack coins into paid_coins array in body --- */
+ {
+ struct TALER_MERCHANT_PaidCoin pc[ph->coins.num_coins];
+ for (unsigned i = 0; i < ph->coins.num_coins; i++) {
+
+ const struct TALER_MERCHANT_PayCoin *coin = &ph->coins.coins[i];
+ struct TALER_MERCHANT_PaidCoin *p = &pc[i];
+ struct TALER_Amount fee;
+
+ 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 TALER_MERCHANT_OPOEC_INVALID_VALUE;
+ }
+
+ TALER_denom_pub_hash ( &coin->denom_pub,
+ &p->denom_pub);
+ TALER_wallet_deposit_sign ( &coin->amount_with_fee,
+ &fee,
+ &ph->h_wire,
+ &ph->h_contract_terms, /* contract */
+ ph->has_choice_index
+ ? &ph->wallet_data_hash
+ : NULL,
+ coin->h_age_commitment,
+ NULL, /* extensions */
+ &p->denom_pub,
+ ph->timestamp,
+ ph->refund_deadline,
+ &ph->merchant_pub,
+ &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;
+ }
+
+ /* now JSON-pack paid_coins[] into request body */
+ json_t *arr = json_array ();
+ for (unsigned i = 0; i < ph->coins.num_coins; i++) {
+ struct TALER_MERCHANT_PaidCoin *p = &pc[i];
+ json_t *je = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("coin_sig", &p->coin_sig),
+ GNUNET_JSON_pack_data_auto ("coin_pub", &p->coin_pub),
+ TALER_JSON_pack_amount ("amount_with_fee", &p->amount_with_fee),
+ TALER_JSON_pack_amount ("amount_without_fee",&p->amount_without_fee),
+ GNUNET_JSON_pack_string ("exchange_url", p->exchange_url)
+ );
+ json_array_append_new (arr, je);
+ }
+ store_json_option (ph,
+ TALER_MERCHANT_OrderPayOptionType_COINS,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("paid_coins", arr)
+ )
+ );
+ }
+
+ /* --- 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];
+ for (unsigned i = 0; i < ph->input_tokens.num_tokens; i++) {
+ 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;
+ }
+
+ json_t *arr = json_array ();
+ for (unsigned i = 0; i < ph->input_tokens.num_tokens; i++) {
+ struct TALER_MERCHANT_UsedToken *t = &ut[i];
+ json_t *je = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("token_sig", &t->token_sig),
+ GNUNET_JSON_pack_data_auto ("ub_sig", &t->ub_sig),
+ GNUNET_JSON_pack_data_auto ("issue_pub", &t->issue_pub)
+ );
+ json_array_append_new (arr, je);
+ }
+ store_json_option (ph,
+ TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("used_tokens", arr)
+ )
+ );
+ }
+
+ return fire_request (ph);
+}
diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c
@@ -1447,9 +1447,7 @@ TALER_TESTING_cmd_merchant_pay_order_donau (const char *label,
{
crypto_hash_sha512_state state;
size_t tax_length = strlen (donor_tax_id);
- unsigned int salt_length;
- for (salt_length = 0; salt[salt_length]!= '\0'; ++salt_length)
- ;
+ size_t salt_length = strlen (salt);
crypto_hash_sha512_init (&state);
crypto_hash_sha512_update (&state,