commit e28c2fc790a07dda17de2794ce2de8a5ee422529
parent 598f13b038827f45d61c6c1f9810e14ad286568d
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Sun, 22 Jun 2025 12:29:05 +0200
update to better match the current pay logic
Diffstat:
3 files changed, 286 insertions(+), 237 deletions(-)
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
@@ -35,6 +35,7 @@ sql_DATA = \
merchant-0016.sql \
merchant-0017.sql \
merchant-0018.sql \
+ merchant-0019.sql \
drop.sql
BUILT_SOURCES = \
diff --git a/src/include/taler_merchant_pay_service.h b/src/include/taler_merchant_pay_service.h
@@ -240,7 +240,7 @@ 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);
+TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph);
#endif /* TALER_MERCHANT_PAY_SERVICE_H */
\ No newline at end of file
diff --git a/src/lib/taler_merchant_pay_service.c b/src/lib/taler_merchant_pay_service.c
@@ -14,10 +14,12 @@
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
+ * @file taler_merchant_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 "platform.h"
#include <curl/curl.h>
#include <gnunet/gnunet_common.h>
@@ -36,15 +38,21 @@
#include <taler/taler_exchange_service.h>
#include <taler/taler_curl_lib.h>
-/* ------------------------------------------------------------------------- */
-/* internal helpers */
-/* ------------------------------------------------------------------------- */
-
-/** Opaque handle implementation. */
+/**
+ * @brief A Pay Handle
+ */
struct TALER_MERCHANT_OrderPayHandle
{
struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Function to call with the result in "pay" @e mode.
+ */
TALER_MERCHANT_OrderPayCallback cb;
+
+ /**
+ * Closure for @a pay_cb.
+ */
TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *cb_cls;
/* mandatory scalars: */
@@ -97,224 +105,16 @@ struct TALER_MERCHANT_OrderPayHandle
bool field_seen[TALER_MERCHANT_OrderPayOptionType_LENGTH];
- /**
- * Function to call with the result in "pay" @e mode.
- */
- TALER_MERCHANT_OrderPayCallback pay_cb;
-
bool am_wallet;
};
-/* 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_cancel1 (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:
- {
- ph->h_wire = o->details.h_wire;
- ph->has_h_wire = true;
-
- 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 ============= */
-
+/**
+ * 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,
@@ -354,10 +154,18 @@ parse_tokens (const json_t *token_sigs,
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_finished (void *cls,
- long response_code,
- const void *resp)
+ long response_code,
+ const void *resp)
{
struct TALER_MERCHANT_OrderPayHandle *oph = cls;
const json_t *json = resp;
@@ -535,7 +343,7 @@ handle_finished (void *cls,
= (unsigned int) alen;
pr.details.unavailable_for_legal_reasons.exchanges
= ebua;
- oph->pay_cb (oph->cb_cls,
+ oph->cb (oph->cb_cls,
&pr);
TALER_MERCHANT_order_pay_cancel1 (oph);
return;
@@ -582,28 +390,266 @@ handle_finished (void *cls,
GNUNET_break_op (0);
break;
}
- oph->pay_cb (oph->cb_cls,
+ oph->cb (oph->cb_cls,
&pr);
TALER_MERCHANT_order_pay_cancel1 (oph);
}
+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_cancel1 (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);
+}
+
+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:
+ {
+ ph->h_wire = o->details.h_wire;
+ ph->has_h_wire = true;
+
+ 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;
+}
+
+
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;
+ {
+ 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 (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_INVALID_VALUE;
+ }
+
+// ph->num_coins = num_coins;
+// ph->coins = GNUNET_new_array (num_coins,
+// struct TALER_MERCHANT_PaidCoin);
+// GNUNET_memcpy (ph->coins,
+// coins,
+// num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
CURL *eh = TALER_MERCHANT_curl_easy_get_ (ph->url);
- if (GNUNET_OK != TALER_curl_easy_post (&ph->post_ctx, eh, ph->body)) {
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&ph->post_ctx,
+ eh,
+ ph->body))
+ {
+ GNUNET_break (0);
curl_easy_cleanup (eh);
+ json_decref (ph->body);
+ GNUNET_free (ph->url);
+ GNUNET_free (ph);
+ //TODO: Replace with proper error code
return TALER_MERCHANT_OPOEC_INVALID_VALUE;
}
-
- ph->job = GNUNET_CURL_job_add2 (ph->ctx, eh, ph->post_ctx.headers,
- &handle_finished, ph);
+ json_decref (ph->body);
+ ph->job = GNUNET_CURL_job_add2 (ph->ctx,
+ eh,
+ ph->post_ctx.headers,
+ &handle_finished,
+ ph);
return TALER_MERCHANT_OPOEC_OK;
}
@@ -731,5 +777,7 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph)
);
}
+
+ //TODO: Change the usage of this function to how it is in the api_post_order_pay.c in the order_pay_frontend
return fire_request (ph);
}