summaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/taler-merchant-httpd_helper.c')
-rw-r--r--src/backend/taler-merchant-httpd_helper.c664
1 files changed, 567 insertions, 97 deletions
diff --git a/src/backend/taler-merchant-httpd_helper.c b/src/backend/taler-merchant-httpd_helper.c
index 2cffa20c..8fb5823e 100644
--- a/src/backend/taler-merchant-httpd_helper.c
+++ b/src/backend/taler-merchant-httpd_helper.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014--2021 Taler Systems SA
+ (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
@@ -20,74 +20,183 @@
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_db_lib.h>
#include <taler/taler_util.h>
#include <taler/taler_json_lib.h>
#include "taler-merchant-httpd_helper.h"
#include <taler/taler_templating_lib.h>
+#include <taler/taler_dbevents.h>
+
+
+enum GNUNET_GenericReturnValue
+TMH_cmp_wire_account (
+ const json_t *account,
+ const struct TMH_WireMethod *wm)
+{
+ const char *credit_facade_url = NULL;
+ const json_t *credit_facade_credentials = NULL;
+ const char *uri;
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_payto_uri ("payto_uri",
+ &uri),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_web_url ("credit_facade_url",
+ &credit_facade_url),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("credit_facade_credentials",
+ &credit_facade_credentials),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue res;
+ const char *ename;
+ unsigned int eline;
+
+ res = GNUNET_JSON_parse (account,
+ ispec,
+ &ename,
+ &eline);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to parse account spec: %s (%u)\n",
+ ename,
+ eline);
+ return GNUNET_SYSERR;
+ }
+ if (0 !=
+ strcmp (wm->payto_uri,
+ uri))
+ {
+ return GNUNET_SYSERR;
+ }
+ if ( (NULL == credit_facade_url) !=
+ (NULL == wm->credit_facade_url) ||
+ (NULL == credit_facade_credentials) !=
+ (NULL == wm->credit_facade_credentials) )
+ {
+ return GNUNET_NO;
+ }
+ if ( (NULL != credit_facade_url) &&
+ (0 != strcmp (credit_facade_url,
+ wm->credit_facade_url)) )
+ {
+ return GNUNET_NO;
+ }
+ if ( (NULL != credit_facade_credentials) &&
+ (0 != json_equal (credit_facade_credentials,
+ wm->credit_facade_credentials)) )
+ {
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
-/**
- * check @a payto_uris for well-formedness
- *
- * @param payto_uris JSON array of payto URIs (presumably)
- * @return true if they are all valid URIs (and this is an array of strings)
- */
bool
-TMH_payto_uri_array_valid (const json_t *payto_uris)
+TMH_accounts_array_valid (const json_t *accounts)
{
- bool payto_ok = true;
+ size_t len;
- if (! json_is_array (payto_uris))
+ if (! json_is_array (accounts))
{
GNUNET_break_op (0);
- payto_ok = false;
+ return false;
}
- else
+ len = json_array_size (accounts);
+ for (size_t i = 0; i<len; i++)
{
- unsigned int len = json_array_size (payto_uris);
+ json_t *payto_uri = json_array_get (accounts,
+ i);
+ const char *credit_facade_url = NULL;
+ const json_t *credit_facade_credentials = NULL;
+ const char *uri;
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_payto_uri ("payto_uri",
+ &uri),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_web_url ("credit_facade_url",
+ &credit_facade_url),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("credit_facade_credentials",
+ &credit_facade_credentials),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue res;
+ const char *ename;
+ unsigned int eline;
+
+ res = GNUNET_JSON_parse (payto_uri,
+ ispec,
+ &ename,
+ &eline);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to parse account spec: %s (%u)\n",
+ ename,
+ eline);
+ return false;
+ }
- for (unsigned int i = 0; i<len; i++)
+ /* Test for the same payto:// URI being given twice */
+ for (unsigned int j = 0; j<i; j++)
{
- json_t *payto_uri = json_array_get (payto_uris,
- i);
- const char *uri;
-
- if (! json_is_string (payto_uri))
- payto_ok = false;
- uri = json_string_value (payto_uri);
- /* Test for the same payto:// URI being given twice */
- for (unsigned int j = 0; j<i; j++)
+ json_t *old_uri = json_array_get (accounts,
+ j);
+ if (0 == strcmp (uri,
+ json_string_value (
+ json_object_get (old_uri,
+ "payto_uri"))))
{
- json_t *old_uri = json_array_get (payto_uris,
- j);
- if (json_equal (payto_uri,
- old_uri))
- {
- GNUNET_break_op (0);
- payto_ok = false;
- break;
- }
+ GNUNET_break_op (0);
+ return false;
}
- if (! payto_ok)
- break;
+ }
+ {
+ char *err;
+
+ if (NULL !=
+ (err = TALER_payto_validate (uri)))
{
- char *err;
-
- if (NULL !=
- (err = TALER_payto_validate (uri)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Encountered invalid payto://-URI `%s': %s\n",
- uri,
- err);
- GNUNET_free (err);
- payto_ok = false;
- break;
- }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Encountered invalid payto://-URI `%s': %s\n",
+ uri,
+ err);
+ GNUNET_free (err);
+ return false;
}
}
- }
- return payto_ok;
+ if ( (NULL == credit_facade_url) !=
+ (NULL == credit_facade_credentials) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "If credit_facade_url is given, credit_facade_credentials must also be specified (violated for %s)\n",
+ uri);
+ return false;
+ }
+ if ( (NULL != credit_facade_url) ||
+ (NULL != credit_facade_credentials) )
+ {
+ struct TALER_MERCHANT_BANK_AuthenticationData auth;
+
+ if (GNUNET_OK !=
+ TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
+ credit_facade_url,
+ &auth))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid credit facade URL or credentials `%s'\n",
+ credit_facade_url);
+ return false;
+ }
+ TALER_MERCHANT_BANK_auth_free (&auth);
+ }
+ } /* end for all accounts */
+ return true;
}
@@ -103,7 +212,7 @@ TMH_location_object_valid (const json_t *location)
const char *street = NULL;
const char *building = NULL;
const char *building_no = NULL;
- json_t *lines = NULL;
+ const json_t *lines = NULL;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("country",
@@ -142,8 +251,8 @@ TMH_location_object_valid (const json_t *location)
&building_no),
NULL),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("address_lines",
- &lines),
+ GNUNET_JSON_spec_array_const ("address_lines",
+ &lines),
NULL),
GNUNET_JSON_spec_end ()
};
@@ -166,13 +275,6 @@ TMH_location_object_valid (const json_t *location)
size_t idx;
json_t *line;
- if (! json_is_array (lines))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid location for field %s\n",
- "lines");
- return false;
- }
json_array_foreach (lines, idx, line)
{
if (! json_is_string (line))
@@ -204,24 +306,19 @@ TMH_products_array_valid (const json_t *products)
{
const char *product_id = NULL;
const char *description;
- json_t *description_i18n = NULL;
uint64_t quantity = 0;
const char *unit = NULL;
struct TALER_Amount price = { .value = 0 };
const char *image_data_url = NULL;
- json_t *taxes = NULL;
+ const json_t *taxes = NULL;
struct GNUNET_TIME_Timestamp delivery_date = { 0 };
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("product_id",
&product_id),
NULL),
- GNUNET_JSON_spec_string ("description",
- &description),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("description_i18n",
- &description_i18n),
- NULL),
+ TALER_JSON_spec_i18n_str ("description",
+ &description),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_uint64 ("quantity",
&quantity),
@@ -231,17 +328,16 @@ TMH_products_array_valid (const json_t *products)
&unit),
NULL),
GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount ("price",
- TMH_currency,
- &price),
+ TALER_JSON_spec_amount_any ("price",
+ &price),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("image",
&image_data_url),
NULL),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("taxes",
- &taxes),
+ GNUNET_JSON_spec_array_const ("taxes",
+ &taxes),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_timestamp ("delivery_date",
@@ -276,12 +372,6 @@ TMH_products_array_valid (const json_t *products)
GNUNET_break_op (0);
valid = false;
}
- if ( (NULL != description_i18n) &&
- (! TALER_JSON_check_i18n (description_i18n)) )
- {
- GNUNET_break_op (0);
- valid = false;
- }
GNUNET_JSON_parse_free (spec);
if (! valid)
break;
@@ -323,6 +413,7 @@ bool
TMH_template_contract_valid (const json_t *template_contract)
{
const char *summary;
+ const char *currency;
struct TALER_Amount amount = { .value = 0};
uint32_t minimum_age = 0;
struct GNUNET_TIME_Relative pay_duration = { 0 };
@@ -332,9 +423,12 @@ TMH_template_contract_valid (const json_t *template_contract)
&summary),
NULL),
GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount ("amount",
- TMH_currency,
- &amount),
+ GNUNET_JSON_spec_string ("currency",
+ &currency),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("amount",
+ &amount),
NULL),
GNUNET_JSON_spec_uint32 ("minimum_age",
&minimum_age),
@@ -395,14 +489,17 @@ TMH_taxes_array_valid (const json_t *taxes)
struct TMH_WireMethod *
-TMH_setup_wire_account (const char *payto_uri)
+TMH_setup_wire_account (
+ const char *payto_uri,
+ const char *credit_facade_url,
+ const json_t *credit_facade_credentials)
{
struct TMH_WireMethod *wm;
char *emsg;
if (NULL != (emsg = TALER_payto_validate (payto_uri)))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Invalid URI `%s': %s\n",
payto_uri,
emsg);
@@ -411,6 +508,12 @@ TMH_setup_wire_account (const char *payto_uri)
}
wm = GNUNET_new (struct TMH_WireMethod);
+ if (NULL != credit_facade_url)
+ wm->credit_facade_url
+ = GNUNET_strdup (credit_facade_url);
+ if (NULL != credit_facade_credentials)
+ wm->credit_facade_credentials
+ = json_incref ((json_t*) credit_facade_credentials);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&wm->wire_salt,
sizeof (wm->wire_salt));
@@ -471,19 +574,14 @@ TMH_check_auth_config (struct MHD_Connection *connection,
TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
- "bad authentication config")) ?
- GNUNET_NO : GNUNET_SYSERR;
+ "bad authentication config"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
return GNUNET_OK;
}
-/**
- * Generate binary UUID from client-provided UUID-string.
- *
- * @param uuids string intpu
- * @param[out] uuid set to binary UUID
- */
void
TMH_uuid_from_string (const char *uuids,
struct GNUNET_Uuid *uuid)
@@ -494,9 +592,9 @@ TMH_uuid_from_string (const char *uuids,
strlen (uuids),
&hc);
GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
- memcpy (uuid,
- &hc,
- sizeof (*uuid));
+ GNUNET_memcpy (uuid,
+ &hc,
+ sizeof (*uuid));
}
@@ -560,7 +658,8 @@ trigger_webhook_cb (void *cls,
t->rv = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
- GNUNET_assert ('\0' == ((const char *) header)[header_size-1]);
+ /* Note: header is actually header_size+1 bytes long here, see mustach.c::memfile_close() */
+ GNUNET_assert ('\0' == ((const char *) header)[header_size]);
}
if (NULL != body_template)
{
@@ -572,13 +671,14 @@ trigger_webhook_cb (void *cls,
if (0 != ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to expand webhook header template for webhook %llu (%d)\n",
+ "Failed to expand webhook body template for webhook %llu (%d)\n",
(unsigned long long) webhook_serial,
ret);
t->rv = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
- GNUNET_assert ('\0' == ((const char *) body)[body_size-1]);
+ /* Note: body is actually body_size+1 bytes long here, see mustach.c::memfile_close() */
+ GNUNET_assert ('\0' == ((const char *) body)[body_size]);
}
t->rv = TMH_db->insert_pending_webhook (TMH_db->cls,
t->instance,
@@ -587,6 +687,19 @@ trigger_webhook_cb (void *cls,
http_method,
header,
body);
+ if (t->rv > 0)
+ {
+ struct GNUNET_DB_EventHeaderP es = {
+ .size = htons (sizeof(es)),
+ .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
+ };
+ const void *extra = NULL;
+ size_t extra_size = 0;
+ TMH_db->event_notify (TMH_db->cls,
+ &es,
+ &extra,
+ extra_size);
+ }
free (header);
free (body);
}
@@ -620,3 +733,360 @@ TMH_trigger_webhook (const char *instance,
return qs;
return t.rv;
}
+
+
+enum GNUNET_GenericReturnValue
+TMH_base_url_by_connection (struct MHD_Connection *connection,
+ const char *instance,
+ struct GNUNET_Buffer *buf)
+{
+ const char *host;
+ const char *forwarded_host;
+ const char *forwarded_port;
+ const char *uri_path;
+
+ memset (buf,
+ 0,
+ sizeof (*buf));
+ if (NULL != TMH_base_url)
+ {
+ GNUNET_buffer_write_str (buf,
+ TMH_base_url);
+ }
+ else
+ {
+ if (GNUNET_YES ==
+ TALER_mhd_is_https (connection))
+ GNUNET_buffer_write_str (buf,
+ "https://");
+ else
+ GNUNET_buffer_write_str (buf,
+ "http://");
+ host = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_HOST);
+ forwarded_host = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ "X-Forwarded-Host");
+ if (NULL != forwarded_host)
+ {
+ GNUNET_buffer_write_str (buf,
+ forwarded_host);
+ }
+ else
+ {
+ if (NULL == host)
+ {
+ GNUNET_buffer_clear (buf);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_buffer_write_str (buf,
+ host);
+ }
+ forwarded_port = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ "X-Forwarded-Port");
+ if (NULL != forwarded_port)
+ {
+ GNUNET_buffer_write_str (buf,
+ ":");
+ GNUNET_buffer_write_str (buf,
+ forwarded_port);
+ }
+ uri_path = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ "X-Forwarded-Prefix");
+ if (NULL != uri_path)
+ GNUNET_buffer_write_path (buf,
+ uri_path);
+ }
+ if (0 != strcmp (instance,
+ "default"))
+ {
+ GNUNET_buffer_write_path (buf,
+ "/instances/");
+ GNUNET_buffer_write_str (buf,
+ instance);
+ }
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TMH_taler_uri_by_connection (struct MHD_Connection *connection,
+ const char *method,
+ const char *instance,
+ struct GNUNET_Buffer *buf)
+{
+ const char *host;
+ const char *forwarded_host;
+ const char *forwarded_port;
+ const char *uri_path;
+
+ memset (buf,
+ 0,
+ sizeof (*buf));
+ host = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ "Host");
+ forwarded_host = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ "X-Forwarded-Host");
+ forwarded_port = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ "X-Forwarded-Port");
+ uri_path = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ "X-Forwarded-Prefix");
+ if (NULL != forwarded_host)
+ host = forwarded_host;
+ if (NULL == host)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_buffer_write_str (buf,
+ "taler");
+ if (GNUNET_NO == TALER_mhd_is_https (connection))
+ GNUNET_buffer_write_str (buf,
+ "+http");
+ GNUNET_buffer_write_str (buf,
+ "://");
+ GNUNET_buffer_write_str (buf,
+ method);
+ GNUNET_buffer_write_str (buf,
+ "/");
+ GNUNET_buffer_write_str (buf,
+ host);
+ if (NULL != forwarded_port)
+ {
+ GNUNET_buffer_write_str (buf,
+ ":");
+ GNUNET_buffer_write_str (buf,
+ forwarded_port);
+ }
+ if (NULL != uri_path)
+ GNUNET_buffer_write_path (buf,
+ uri_path);
+ if (0 != strcmp ("default",
+ instance))
+ {
+ GNUNET_buffer_write_path (buf,
+ "instances");
+ GNUNET_buffer_write_path (buf,
+ instance);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Closure for #add_matching_account().
+ */
+struct ExchangeMatchContext
+{
+ /**
+ * Wire method to match, NULL for all.
+ */
+ const char *wire_method;
+
+ /**
+ * Array of accounts to return.
+ */
+ json_t *accounts;
+};
+
+
+/**
+ * If the given account is feasible, add it to the array
+ * of accounts we return.
+ *
+ * @param cls a `struct PostReserveContext`
+ * @param payto_uri URI of the account
+ * @param conversion_url URL of a conversion service
+ * @param debit_restrictions restrictions for debits from account
+ * @param credit_restrictions restrictions for credits to account
+ * @param master_sig signature affirming the account
+ */
+static void
+add_matching_account (
+ void *cls,
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ const struct TALER_MasterSignatureP *master_sig)
+{
+ struct ExchangeMatchContext *rc = cls;
+ char *method;
+
+ method = TALER_payto_get_method (payto_uri);
+ if ( (NULL == rc->wire_method) ||
+ (0 == strcmp (method,
+ rc->wire_method)) )
+ {
+ json_t *acc;
+
+ acc = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("payto_uri",
+ payto_uri),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ master_sig),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("conversion_url",
+ conversion_url)),
+ GNUNET_JSON_pack_array_incref ("credit_restrictions",
+ (json_t *) credit_restrictions),
+ GNUNET_JSON_pack_array_incref ("debit_restrictions",
+ (json_t *) debit_restrictions)
+ );
+ GNUNET_assert (0 ==
+ json_array_append_new (rc->accounts,
+ acc));
+ }
+ GNUNET_free (method);
+}
+
+
+/**
+ * Return JSON array with all of the exchange accounts
+ * that support the given @a wire_method.
+ *
+ * @param master_pub master public key to match exchange by
+ * @param wire_method NULL for any
+ * @return JSON array with information about all matching accounts
+ */
+json_t *
+TMH_exchange_accounts_by_method (
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *wire_method)
+{
+ struct ExchangeMatchContext emc = {
+ .wire_method = wire_method,
+ .accounts = json_array ()
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ GNUNET_assert (NULL != emc.accounts);
+ qs = TMH_db->select_accounts_by_exchange (TMH_db->cls,
+ master_pub,
+ &add_matching_account,
+ &emc);
+ if (qs < 0)
+ {
+ json_decref (emc.accounts);
+ return NULL;
+ }
+ return emc.accounts;
+}
+
+
+char *
+TMH_make_order_status_url (struct MHD_Connection *con,
+ const char *order_id,
+ const char *session_id,
+ const char *instance_id,
+ struct TALER_ClaimTokenP *claim_token,
+ struct TALER_PrivateContractHashP *h_contract)
+{
+ struct GNUNET_Buffer buf;
+ /* Number of query parameters written so far */
+ unsigned int num_qp = 0;
+
+ GNUNET_assert (NULL != instance_id);
+ GNUNET_assert (NULL != order_id);
+ if (GNUNET_OK !=
+ TMH_base_url_by_connection (con,
+ instance_id,
+ &buf))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ GNUNET_buffer_write_path (&buf,
+ "/orders");
+ GNUNET_buffer_write_path (&buf,
+ order_id);
+ if ( (NULL != claim_token) &&
+ (! GNUNET_is_zero (claim_token)) )
+ {
+ /* 'token=' for human readability */
+ GNUNET_buffer_write_str (&buf,
+ "?token=");
+ GNUNET_buffer_write_data_encoded (&buf,
+ (char *) claim_token,
+ sizeof (*claim_token));
+ num_qp++;
+ }
+
+ if (NULL != session_id)
+ {
+ if (num_qp > 0)
+ GNUNET_buffer_write_str (&buf,
+ "&session_id=");
+ else
+ GNUNET_buffer_write_str (&buf,
+ "?session_id=");
+ GNUNET_buffer_write_str (&buf,
+ session_id);
+ num_qp++;
+ }
+
+ if (NULL != h_contract)
+ {
+ if (num_qp > 0)
+ GNUNET_buffer_write_str (&buf,
+ "&h_contract=");
+ else
+ GNUNET_buffer_write_str (&buf,
+ "?h_contract=");
+ GNUNET_buffer_write_data_encoded (&buf,
+ (char *) h_contract,
+ sizeof (*h_contract));
+ }
+
+ return GNUNET_buffer_reap_str (&buf);
+}
+
+
+char *
+TMH_make_taler_pay_uri (struct MHD_Connection *con,
+ const char *order_id,
+ const char *session_id,
+ const char *instance_id,
+ struct TALER_ClaimTokenP *claim_token)
+{
+ struct GNUNET_Buffer buf;
+
+ GNUNET_assert (NULL != instance_id);
+ GNUNET_assert (NULL != order_id);
+ if (GNUNET_OK !=
+ TMH_taler_uri_by_connection (con,
+ "pay",
+ instance_id,
+ &buf))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ GNUNET_buffer_write_path (&buf,
+ order_id);
+ GNUNET_buffer_write_path (&buf,
+ (NULL == session_id)
+ ? ""
+ : session_id);
+ if ( (NULL != claim_token) &&
+ (! GNUNET_is_zero (claim_token)))
+ {
+ /* Just 'c=' because this goes into QR
+ codes, so this is more compact. */
+ GNUNET_buffer_write_str (&buf,
+ "?c=");
+ GNUNET_buffer_write_data_encoded (&buf,
+ (char *) claim_token,
+ sizeof (struct TALER_ClaimTokenP));
+ }
+
+ return GNUNET_buffer_reap_str (&buf);
+}