with inline SVG instead of here! */
GNUNET_buffer_write_str (&buf,
"\n\n\n\n ");
for (unsigned int y = 0; ywidth; y++)
{
for (unsigned int x = 0; xwidth; x++)
{
unsigned int off = x + y * qrc->width;
GNUNET_buffer_write_fstr (&buf,
"%s",
(0 != (qrc->data[off] & 1)) ? "██" : " ");
}
GNUNET_buffer_write_str (&buf,
"\n ");
}
GNUNET_buffer_write_str (&buf,
"\n\n\n\n
");
QRcode_free (qrc);
return GNUNET_buffer_reap_str (&buf);
}
/**
* Force resuming all suspended order lookups, needed during shutdown.
*/
void
TMH_force_wallet_get_order_resume (void)
{
struct GetOrderData *god;
while (NULL != (god = god_head))
{
GNUNET_CONTAINER_DLL_remove (god_head,
god_tail,
god);
GNUNET_assert (god->suspended);
god->suspended = false;
MHD_resume_connection (god->sc.con);
}
}
/**
* Suspend this @a god until the trigger is satisfied.
*
* @param god request to suspend
*/
static void
suspend_god (struct GetOrderData *god)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Suspending GET /orders/%s\n",
god->order_id);
TMH_long_poll_suspend (god->order_id,
god->hc->instance,
&god->sc,
(god->sc.awaiting_refund)
? &god->sc.refund_expected
: NULL);
}
/**
* Create a taler://pay/ URI for the given @a con and @a order_id
* and @a session_id and @a instance_id.
*
* @param con HTTP connection
* @param order_id the order id
* @param session_id session, may be NULL
* @param instance_id instance, may be "default"
* @return corresponding taler://pay/ URI, or NULL on missing "host"
*/
char *
TMH_make_taler_pay_uri (struct MHD_Connection *con,
const char *order_id,
const char *session_id,
const char *instance_id)
{
const char *host;
const char *forwarded_host;
const char *uri_path;
const char *uri_instance_id;
const char *query;
char *result;
host = MHD_lookup_connection_value (con,
MHD_HEADER_KIND,
"Host");
forwarded_host = MHD_lookup_connection_value (con,
MHD_HEADER_KIND,
"X-Forwarded-Host");
uri_path = MHD_lookup_connection_value (con,
MHD_HEADER_KIND,
"X-Forwarded-Prefix");
if (NULL == uri_path)
uri_path = "-";
if (NULL != forwarded_host)
host = forwarded_host;
if (0 == strcmp (instance_id,
"default"))
uri_instance_id = "-";
else
uri_instance_id = instance_id;
if (NULL == host)
{
/* Should never happen, at least the host header should be defined */
GNUNET_break (0);
return NULL;
}
if (GNUNET_YES == TALER_mhd_is_https (con))
query = "";
else
query = "?insecure=1";
GNUNET_assert (NULL != order_id);
GNUNET_assert (0 < GNUNET_asprintf (&result,
"taler://pay/%s/%s/%s/%s%s%s%s",
host,
uri_path,
uri_instance_id,
order_id,
(NULL == session_id) ? "" : "/",
(NULL == session_id) ? "" : session_id,
query));
return result;
}
/**
* The client did not yet pay, send it the payment request.
*
* @param god check pay request context
* @param already_paid_order_id if for the fulfillment URI there is
* already a paid order, this is the order ID to redirect
* the wallet to; NULL if not applicable
* @return #MHD_YES on success
*/
static MHD_RESULT
send_pay_request (struct GetOrderData *god,
const char *already_paid_order_id)
{
MHD_RESULT ret;
char *taler_pay_uri;
struct GNUNET_TIME_Relative remaining;
remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
if (0 != remaining.rel_value_us)
{
/* long polling: do not queue a response, suspend connection instead */
suspend_god (god);
return MHD_YES;
}
/* Check if resource_id has been paid for in the same session
* with another order_id.
*/
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Sending payment request in /poll-payment\n");
taler_pay_uri = TMH_make_taler_pay_uri (god->sc.con,
god->order_id,
god->session_id,
god->hc->instance->settings.id);
if (god->generate_html)
{
struct MHD_Response *reply;
char *qr;
char *body;
size_t body_size;
qr = create_qrcode (taler_pay_uri);
if (NULL == qr)
{
GNUNET_break (0);
return MHD_NO;
}
{
struct KVC kvc[] = {
{ "pay_uri",
GNUNET_strdup (taler_pay_uri) },
{ "pay_qr",
qr },
{ NULL, NULL }
};
const char *tmpl;
tmpl = load_template ("request_payment");
if (NULL == tmpl)
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"mustach");
return MHD_NO; // FIXME: add nicer error reply...
}
if (0 !=
mustach (tmpl,
&itf,
&kvc,
&body,
&body_size))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"mustach");
return MHD_NO; // FIXME: add nicer error reply...
}
}
reply = MHD_create_response_from_buffer (body_size,
body,
MHD_RESPMEM_MUST_FREE);
if (NULL == reply)
{
GNUNET_break (0);
return MHD_NO;
}
GNUNET_break (MHD_NO !=
MHD_add_response_header (reply,
"Taler",
taler_pay_uri));
GNUNET_break (MHD_NO !=
MHD_add_response_header (reply,
MHD_HTTP_HEADER_CONTENT_TYPE,
"text/html"));
ret = MHD_queue_response (god->sc.con,
MHD_HTTP_PAYMENT_REQUIRED,
reply);
MHD_destroy_response (reply);
}
else
{
ret = TALER_MHD_reply_json_pack (god->sc.con,
MHD_HTTP_PAYMENT_REQUIRED,
"{s:s, s:s?}",
"taler_pay_uri", taler_pay_uri,
"already_paid_order_id",
already_paid_order_id);
}
GNUNET_free (taler_pay_uri);
return ret;
}
/**
* Check if @a god has sub-activities still pending.
*
* @param god request to check
* @return true if activities are still pending
*/
static bool
god_pending (struct GetOrderData *god)
{
for (struct CoinRefund *cr = god->cr_head;
NULL != cr;
cr = cr->next)
{
if ( (NULL != cr->fo) ||
(NULL != cr->rh) )
return true;
}
return false;
}
/**
* Check if @a god is ready to be resumed, and if so, do it.
*
* @param god refund request to be possibly ready
*/
static void
check_resume_god (struct GetOrderData *god)
{
if (god_pending (god))
return;
GNUNET_CONTAINER_DLL_remove (god_head,
god_tail,
god);
GNUNET_assert (god->suspended);
god->suspended = false;
MHD_resume_connection (god->sc.con);
TMH_trigger_daemon ();
}
/**
* Callbacks of this type are used to serve the result of submitting a
* refund request to an exchange.
*
* @param cls a `struct CoinRefund`
* @param hr HTTP response data
* @param exchange_pub exchange key used to sign refund confirmation
* @param exchange_sig exchange's signature over refund
*/
static void
refund_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const struct TALER_ExchangeSignatureP *exchange_sig)
{
struct CoinRefund *cr = cls;
cr->rh = NULL;
cr->exchange_status = hr->http_status;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Exchange refund status for coin %s is %u\n",
TALER_B2S (&cr->coin_pub),
hr->http_status);
if (MHD_HTTP_OK != hr->http_status)
{
cr->exchange_code = hr->ec;
cr->exchange_reply = json_incref ((json_t*) hr->reply);
}
else
{
enum GNUNET_DB_QueryStatus qs;
cr->exchange_pub = *exchange_pub;
cr->exchange_sig = *exchange_sig;
qs = TMH_db->insert_refund_proof (TMH_db->cls,
cr->refund_serial,
exchange_sig,
exchange_pub);
if (0 >= qs)
{
/* generally, this is relatively harmless for the merchant, but let's at
least log this. */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to persist exchange response to /refund in database: %d\n",
qs);
}
}
check_resume_god (cr->god);
}
/**
* Function called with the result of a #TMH_EXCHANGES_find_exchange()
* operation.
*
* @param cls a `struct CoinRefund *`
* @param hr HTTP response details
* @param eh handle to the exchange context
* @param payto_uri payto://-URI of the exchange
* @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
static void
exchange_found_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
const char *payto_uri,
const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct CoinRefund *cr = cls;
(void) payto_uri;
cr->fo = NULL;
if (TALER_EC_NONE == hr->ec)
{
cr->rh = TALER_EXCHANGE_refund (eh,
&cr->refund_amount,
&cr->god->h_contract_terms,
&cr->coin_pub,
cr->rtransaction_id,
&cr->god->hc->instance->merchant_priv,
&refund_cb,
cr);
return;
}
cr->exchange_status = hr->http_status;
cr->exchange_code = hr->ec;
cr->exchange_reply = json_incref ((json_t*) hr->reply);
check_resume_god (cr->god);
}
/**
* Function called with detailed information about a refund.
* It is responsible for packing up the data to return.
*
* @param cls closure
* @param refund_serial unique serial number of the refund
* @param timestamp time of the refund (for grouping of refunds in the wallet UI)
* @param coin_pub public coin from which the refund comes from
* @param exchange_url URL of the exchange that issued @a coin_pub
* @param rtransaction_id identificator of the refund
* @param reason human-readable explanation of the refund
* @param refund_amount refund amount which is being taken from @a coin_pub
*/
static void
process_refunds_cb (void *cls,
uint64_t refund_serial,
struct GNUNET_TIME_Absolute timestamp,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const char *exchange_url,
uint64_t rtransaction_id,
const char *reason,
const struct TALER_Amount *refund_amount)
{
struct GetOrderData *god = cls;
struct CoinRefund *cr;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Found refund of %s for coin %s with reason `%s' in database\n",
TALER_B2S (coin_pub),
TALER_amount2s (refund_amount),
reason);
cr = GNUNET_new (struct CoinRefund);
cr->refund_serial = refund_serial;
cr->exchange_url = GNUNET_strdup (exchange_url);
cr->god = god;
cr->coin_pub = *coin_pub;
cr->rtransaction_id = rtransaction_id;
cr->refund_amount = *refund_amount;
cr->execution_time = timestamp;
GNUNET_CONTAINER_DLL_insert (god->cr_head,
god->cr_tail,
cr);
if (god->refunded)
{
GNUNET_assert (0 <=
TALER_amount_add (&god->refund_amount,
&god->refund_amount,
refund_amount));
return;
}
god->refund_amount = *refund_amount;
god->refunded = true;
}
/**
* Clean up refund processing.
*
* @param god handle to clean up refund processing for
*/
static void
rf_cleanup (struct GetOrderData *god)
{
struct CoinRefund *cr;
while (NULL != (cr = god->cr_head))
{
GNUNET_CONTAINER_DLL_remove (god->cr_head,
god->cr_tail,
cr);
if (NULL != cr->fo)
{
TMH_EXCHANGES_find_exchange_cancel (cr->fo);
cr->fo = NULL;
}
if (NULL != cr->rh)
{
TALER_EXCHANGE_refund_cancel (cr->rh);
cr->rh = NULL;
}
if (NULL != cr->exchange_reply)
{
json_decref (cr->exchange_reply);
cr->exchange_reply = NULL;
}
GNUNET_free (cr->exchange_url);
GNUNET_free (cr);
}
}
/**
* Clean up the session state for a GET /orders/$ID request.
*
* @param cls must be a `struct GetOrderData *`
*/
static void
god_cleanup (void *cls)
{
struct GetOrderData *god = cls;
rf_cleanup (god);
if (NULL != god->contract_terms)
json_decref (god->contract_terms);
GNUNET_free (god);
}
/**
* Handle a GET "/orders/$ID" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] hc context with further information about the request
* @return MHD result code
*/
MHD_RESULT
TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
struct GetOrderData *god = hc->ctx;
const char *order_id = hc->infix;
enum GNUNET_DB_QueryStatus qs;
if (NULL == god)
{
god = GNUNET_new (struct GetOrderData);
hc->ctx = god;
hc->cc = &god_cleanup;
god->sc.con = connection;
god->hc = hc;
god->order_id = order_id;
{
const char *cts;
cts = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"h_contract");
if (NULL == cts)
{
/* h_contract required but missing */
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_PARAMETER_MISSING,
"h_contract required");
}
if (GNUNET_OK !=
GNUNET_CRYPTO_hash_from_string (cts,
&god->h_contract_terms))
{
/* cts has wrong encoding */
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_PARAMETER_MALFORMED,
"h_contract malformed");
}
}
{ /* check for 'Accept' header */
const char *accept;
accept = MHD_lookup_connection_value (connection,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_ACCEPT);
if (NULL != accept)
{
char *a = GNUNET_strdup (accept);
char *saveptr;
for (char *t = strtok_r (a, ",", &saveptr);
NULL != t;
t = strtok_r (NULL, ",", &saveptr))
{
char *end;
/* skip leading whitespace */
while (isspace ((unsigned char) t[0]))
t++;
/* trim of ';q=' parameter and everything after space */
end = strchr (t, ';');
if (NULL != end)
*end = '\0';
end = strchr (t, ' ');
if (NULL != end)
*end = '\0';
if (0 == strcasecmp ("text/html",
t))
{
god->generate_html = true;
break;
}
}
GNUNET_free (a);
}
} /* end check for 'Accept' header */
{
const char *long_poll_timeout_s;
long_poll_timeout_s = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"timeout");
if (NULL != long_poll_timeout_s)
{
unsigned int timeout;
if (1 != sscanf (long_poll_timeout_s,
"%u",
&timeout))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_PARAMETER_MALFORMED,
"timeout must be non-negative number");
}
god->sc.long_poll_timeout
= GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (
GNUNET_TIME_UNIT_SECONDS,
timeout));
}
else
{
god->sc.long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS;
}
}
{
const char *min_refund;
min_refund = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"refund");
if (NULL != min_refund)
{
if ( (GNUNET_OK !=
TALER_string_to_amount (min_refund,
&god->sc.refund_expected)) ||
(0 != strcasecmp (god->sc.refund_expected.currency,
TMH_currency) ) )
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_PARAMETER_MALFORMED,
"invalid amount given for refund argument");
}
god->sc.awaiting_refund = true;
}
}
god->session_id = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"session_id");
/* Convert order_id to h_contract_terms */
TMH_db->preflight (TMH_db->cls);
{
uint64_t order_serial;
qs = TMH_db->lookup_contract_terms (TMH_db->cls,
hc->instance->settings.id,
order_id,
&god->contract_terms,
&order_serial);
}
if (0 > qs)
{
/* single, read-only SQL statements should never cause
serialization problems */
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
/* Always report on hard error as well to enable diagnostics */
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
"database error looking up contract");
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Unknown order id given: `%s'\n",
order_id);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_GET_ORDERS_ID_UNKNOWN,
"order_id not found in database");
}
/* Check client provided the right hash code of the contract terms */
{
struct GNUNET_HashCode h;
if (GNUNET_OK !=
TALER_JSON_contract_hash (god->contract_terms,
&h))
{
GNUNET_break (0);
GNUNET_free (god);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_INTERNAL_LOGIC_ERROR,
"Could not hash contract terms");
}
if (0 !=
GNUNET_memcmp (&h,
&god->h_contract_terms))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_GET_ORDER_WRONG_CONTRACT,
"Contract hash does not match order");
}
}
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("fulfillment_url",
&god->fulfillment_url),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (god->contract_terms,
spec,
NULL, NULL))
{
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
"Merchant database error (contract terms corrupted)");
}
}
} /* end of first-time initialization / sanity checks */
if ( (NULL != god->session_id) &&
(NULL != god->fulfillment_url) )
{
/* Check if paid within a session. */
char *already_paid_order_id = NULL;
qs = TMH_db->lookup_order_by_fulfillment (TMH_db->cls,
hc->instance->settings.id,
god->fulfillment_url,
god->session_id,
&already_paid_order_id);
if (qs < 0)
{
/* single, read-only SQL statements should never cause
serialization problems */
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
/* Always report on hard error as well to enable diagnostics */
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
"db error fetching pay session info");
}
if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
(0 != strcmp (order_id,
already_paid_order_id)) )
{
MHD_RESULT ret;
ret = send_pay_request (god,
already_paid_order_id);
GNUNET_free (already_paid_order_id);
return ret;
}
GNUNET_break (1 == qs);
GNUNET_free (already_paid_order_id);
}
else
{
/* Check if paid regardless of session. */
struct GNUNET_HashCode h_contract;
bool paid;
qs = TMH_db->lookup_order_status (TMH_db->cls,
hc->instance->settings.id,
order_id,
&h_contract,
&paid);
if (0 >= qs)
{
/* Always report on hard error as well to enable diagnostics */
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
"Merchant database error");
}
GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
GNUNET_break (0 ==
GNUNET_memcmp (&h_contract,
&god->h_contract_terms));
if (! paid)
{
return send_pay_request (god,
NULL);
}
}
/* At this point, we know the contract was paid. Let's check for
refunds. First, clear away refunds found from previous invocations. */
rf_cleanup (god);
GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (TMH_currency,
&god->refund_amount));
qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
hc->instance->settings.id,
&god->h_contract_terms,
&process_refunds_cb,
god);
if (0 > qs)
{
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
"Failed to lookup refunds for contract");
}
/* Now launch exchange interactions, unless we already have the
response in the database! */
for (struct CoinRefund *cr = god->cr_head;
NULL != cr;
cr = cr->next)
{
enum GNUNET_DB_QueryStatus qs;
qs = TMH_db->lookup_refund_proof (TMH_db->cls,
cr->refund_serial,
&cr->exchange_sig,
&cr->exchange_pub);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
case GNUNET_DB_STATUS_SOFT_ERROR:
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
"Merchant database error");
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
/* We need to talk to the exchange */
cr->fo = TMH_EXCHANGES_find_exchange (cr->exchange_url,
NULL,
GNUNET_NO,
&exchange_found_cb,
cr);
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
/* We got a reply earlier, set status accordingly */
cr->exchange_status = MHD_HTTP_OK;
break;
}
}
if ( (god->sc.awaiting_refund) &&
( (! god->refunded) ||
(1 != TALER_amount_cmp (&god->refund_amount,
&god->sc.refund_expected)) ) )
{
/* Client is waiting for a refund larger than what we have, suspend
until timeout */
struct GNUNET_TIME_Relative remaining;
remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
if (0 != remaining.rel_value_us)
{
/* yes, indeed suspend */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Awaiting refund exceeding %s\n",
TALER_amount2s (&god->sc.refund_expected));
suspend_god (god);
return MHD_YES;
}
}
/* Check if there are still exchange operations pending */
if (god_pending (god))
{
if (! god->suspended)
{
god->suspended = true;
MHD_suspend_connection (connection);
GNUNET_CONTAINER_DLL_insert (god_head,
god_tail,
god);
}
return MHD_YES; /* we're still talking to the exchange */
}
/* All operations done, build final response */
{
json_t *ra;
ra = json_array ();
GNUNET_assert (NULL != ra);
for (struct CoinRefund *cr = god->cr_head;
NULL != cr;
cr = cr->next)
{
json_t *refund;
if (MHD_HTTP_OK != cr->exchange_status)
{
if (NULL == cr->exchange_reply)
{
refund = json_pack ("{s:s, s:I,s:I,s:o,s:o,s:o}"
"type",
"failure",
"exchange_status",
(json_int_t) cr->exchange_status,
"rtransaction_id",
(json_int_t) cr->rtransaction_id,
"coin_pub",
GNUNET_JSON_from_data_auto (&cr->coin_pub),
"refund_amount",
TALER_JSON_from_amount (&cr->refund_amount),
"execution_time",
GNUNET_JSON_from_time_abs (cr->execution_time));
}
else
{
refund = json_pack ("{s:s,s:I,s:I,s:o,s:I,s:o,s:o,s:o}"
"type",
"failure",
"exchange_status",
(json_int_t) cr->exchange_status,
"exchange_code",
(json_int_t) cr->exchange_code,
"exchange_reply",
cr->exchange_reply,
"rtransaction_id",
(json_int_t) cr->rtransaction_id,
"coin_pub",
GNUNET_JSON_from_data_auto (&cr->coin_pub),
"refund_amount",
TALER_JSON_from_amount (&cr->refund_amount),
"execution_time",
GNUNET_JSON_from_time_abs (cr->execution_time));
}
}
else
{
refund = json_pack ("{s:s,s:I,s:o,s:o,s:I,s:o,s:o,s:o}",
"type",
"success",
"exchange_status",
(json_int_t) cr->exchange_status,
"exchange_sig",
GNUNET_JSON_from_data_auto (&cr->exchange_sig),
"exchange_pub",
GNUNET_JSON_from_data_auto (&cr->exchange_pub),
"rtransaction_id",
(json_int_t) cr->rtransaction_id,
"coin_pub",
GNUNET_JSON_from_data_auto (&cr->coin_pub),
"refund_amount",
TALER_JSON_from_amount (&cr->refund_amount),
"execution_time",
GNUNET_JSON_from_time_abs (cr->execution_time));
}
GNUNET_assert (
0 ==
json_array_append_new (ra,
refund));
}
if (god->generate_html)
{
int ret;
struct MHD_Response *reply;
char *body;
size_t body_size;
char *qr;
qr = create_qrcode ("taler://refund/FIXME");
if (NULL == qr)
{
GNUNET_break (0);
return MHD_NO; // FIXME: add nicer error reply...
}
if (god->refunded)
{
struct KVC kvc[] = {
{ "refund_amount",
GNUNET_strdup (TALER_amount2s (&god->refund_amount)) },
{ "refund_uri",
qr },
{ NULL, NULL }
};
const char *tmpl;
tmpl = load_template ("offer_refund");
if (NULL == tmpl)
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"mustach");
return MHD_NO; // FIXME: add nicer error reply...
}
if (0 !=
mustach (tmpl,
&itf,
&kvc,
&body,
&body_size))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"mustach");
return MHD_NO; // FIXME: add nicer error reply...
}
}
else
{
struct KVC kvc[] = {
{ NULL, NULL }
};
const char *tmpl;
tmpl = load_template ("show_order_details");
if (NULL == tmpl)
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"mustach");
return MHD_NO; // FIXME: add nicer error reply...
}
if (0 !=
mustach (tmpl,
&itf,
&kvc,
&body,
&body_size))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"mustach");
return MHD_NO; // FIXME: add nicer error reply...
}
}
reply = MHD_create_response_from_buffer (body_size,
body,
MHD_RESPMEM_MUST_FREE);
if (NULL == reply)
{
GNUNET_break (0);
return MHD_NO;
}
GNUNET_break (MHD_NO !=
MHD_add_response_header (reply,
MHD_HTTP_HEADER_CONTENT_TYPE,
"text/html"));
ret = MHD_queue_response (god->sc.con,
MHD_HTTP_OK,
reply);
MHD_destroy_response (reply);
return ret;
}
else
{
return TALER_MHD_reply_json_pack (
connection,
MHD_HTTP_OK,
"{s:b, s:o, s:o, s:o}",
"refunded", god->refunded,
"refund_amount",
TALER_JSON_from_amount (&god->refund_amount),
"refunds",
ra,
"merchant_pub",
GNUNET_JSON_from_data_auto (&hc->instance->merchant_pub));
}
}
}
/**
* Nicely shut down.
*/
void __attribute__ ((destructor))
get_orders_fini ()
{
for (unsigned int i = 0; i