From e8d2cde63cefeba9f6c6dc242a075de48875c9bd Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 2 Nov 2019 12:13:01 +0100 Subject: clean up check payment logic --- src/backend/taler-merchant-httpd_check-payment.c | 595 +++++++++++++---------- src/lib/merchant_api_check_payment.c | 27 +- 2 files changed, 345 insertions(+), 277 deletions(-) diff --git a/src/backend/taler-merchant-httpd_check-payment.c b/src/backend/taler-merchant-httpd_check-payment.c index 74b8c5c1..d9284c21 100644 --- a/src/backend/taler-merchant-httpd_check-payment.c +++ b/src/backend/taler-merchant-httpd_check-payment.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2017 Taler Systems SA + (C) 2017, 2019 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -17,6 +17,7 @@ * @file backend/taler-merchant-httpd_check-payment.c * @brief implementation of /check-payment handler * @author Florian Dold + * @author Christian Grothoff */ #include "platform.h" #include @@ -37,21 +38,114 @@ #define MAX_RETRIES 5 +/** + * Data structure we keep for a check payment request. + */ +struct CheckPaymentRequestContext +{ + /** + * Must be first for #handle_mhd_completion_callback. + */ + struct TM_HandlerContext hc; + + /** + * Connection we are processing a request for. + */ + struct MHD_Connection *connection; + + /** + * URL where the final contract can be found for this payment. + */ + char *final_contract_url; + + /** + * order ID for the payment + */ + const char *order_id; + + /** + * Where to get the contract + */ + const char *contract_url; + + /** + * session of the client + */ + const char *session_id; + + /** + * fulfillment URL of the contract (valid as long as + * @e contract_terms is valid). + */ + const char *fulfillment_url; + + /** + * At what time does this request expire? If set in the future, we + * may wait this long for a payment to arrive before responding. + */ + struct GNUNET_TIME_Absolute long_poll_timeout; + + /** + * Contract terms of the payment we are checking. NULL when they + * are not (yet) known. + */ + json_t *contract_terms; + + /** + * Hash of @e contract_terms, set only once @e contract_terms + * is available. + */ + struct GNUNET_HashCode h_contract_terms; + + /** + * Total refunds granted for this payment. Only initialized + * if @e refunded is set to #GNUNET_YES. + */ + struct TALER_Amount refund_amount; + + /** + * Set to #GNUNET_YES if this payment has been refunded and + * @e refund_amount is initialized. + */ + int refunded; + + /** + * Initially #GNUNET_SYSERR. If we queued a response, set to the + * result code (i.e. #MHD_YES or #MHD_NO). + */ + int ret; + +}; + + +/** + * Clean up the session state for a check payment request. + * + * @param hc must be a `struct CheckPaymentRequestContext *` + */ +static void +cprc_cleanup (struct TM_HandlerContext *hc) +{ + struct CheckPaymentRequestContext *cprc = (struct + CheckPaymentRequestContext *) hc; + + if (NULL != cprc->contract_terms) + json_decref (cprc->contract_terms); + GNUNET_free_non_null (cprc->final_contract_url); + GNUNET_free (cprc); +} + + /** * Make a taler://pay URI * - * @param connection MHD connection to take host and path from * @param instance_id merchant's instance ID - * @param order_id order ID to request a payment for - * @param session_id session ID for the payment or NULL - * if not a session-bound payment + * @param cprc payment request context * @returns the URI, must be freed with #GNUNET_free */ static char * -make_taler_pay_uri (struct MHD_Connection *connection, - const char *instance_id, - const char *order_id, - const char *session_id) +make_taler_pay_uri (const char *instance_id, + const struct CheckPaymentRequestContext *cprc) { const char *host; const char *forwarded_host; @@ -60,23 +154,25 @@ make_taler_pay_uri (struct MHD_Connection *connection, const char *query; char *result; - host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host"); - forwarded_host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, + host = MHD_lookup_connection_value (cprc->connection, + MHD_HEADER_KIND, + "Host"); + forwarded_host = MHD_lookup_connection_value (cprc->connection, + MHD_HEADER_KIND, "X-Forwarded-Host"); - uri_path = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, + uri_path = MHD_lookup_connection_value (cprc->connection, + 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")) + 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 */ @@ -84,21 +180,20 @@ make_taler_pay_uri (struct MHD_Connection *connection, return NULL; } - if (GNUNET_YES == TALER_mhd_is_https (connection)) + if (GNUNET_YES == TALER_mhd_is_https (cprc->connection)) query = ""; else query = "?insecure=1"; - - GNUNET_assert (NULL != order_id); - + GNUNET_assert (NULL != cprc->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, - (session_id == NULL) ? "" : "/", - (session_id == NULL) ? "" : session_id, + cprc->order_id, + (cprc->session_id == NULL) ? "" : "/", + (cprc->session_id == NULL) ? "" : + cprc->session_id, query)); return result; } @@ -123,50 +218,48 @@ process_refunds_cb (void *cls, const struct TALER_Amount *refund_amount, const struct TALER_Amount *refund_fee) { - struct TALER_Amount *acc_amount = cls; + struct CheckPaymentRequestContext *cprc = cls; - GNUNET_assert (GNUNET_SYSERR != - TALER_amount_add (acc_amount, - acc_amount, - refund_amount)); + if (cprc->refunded) + { + GNUNET_assert (GNUNET_SYSERR != + TALER_amount_add (&cprc->refund_amount, + &cprc->refund_amount, + refund_amount)); + return; + } + cprc->refund_amount = *refund_amount; + cprc->refunded = GNUNET_YES; } /** * The client did not yet pay, send it the payment request. * - * @param connection connection to send on - * @param order_id order ID for the payment - * @param final_contract_url where to get the contract - * @param session_id session of the client - * @param fulfillment_url fulfillment URL of the contract - * @param h_contract_terms_str hash of the contract terms, stringified + * @param cprc check pay request context * @param mi merchant instance * @return #MHD_YES on success */ static int -send_pay_request (struct MHD_Connection *connection, - const char *order_id, - const char *final_contract_url, - const char *session_id, - const char *fulfillment_url, - const char *h_contract_terms_str, +send_pay_request (const struct CheckPaymentRequestContext *cprc, const struct MerchantInstance *mi) { int ret; - int qs; char *already_paid_order_id = NULL; char *taler_pay_uri; /* Check if resource_id has been paid for in the same session * with another order_id. */ - if ( (NULL != session_id) && (NULL != fulfillment_url) ) + if ( (NULL != cprc->session_id) && + (NULL != cprc->fulfillment_url) ) { + enum GNUNET_DB_QueryStatus qs; + qs = db->find_session_info (db->cls, &already_paid_order_id, - session_id, - fulfillment_url, + cprc->session_id, + cprc->fulfillment_url, &mi->pubkey); if (qs < 0) { @@ -175,60 +268,98 @@ send_pay_request (struct MHD_Connection *connection, 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 TMH_RESPONSE_reply_internal_error (connection, + return TMH_RESPONSE_reply_internal_error (cprc->connection, TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR, "db error fetching pay session info"); } } - - taler_pay_uri = make_taler_pay_uri (connection, mi->id, order_id, session_id); - - ret = TMH_RESPONSE_reply_json_pack (connection, + taler_pay_uri = make_taler_pay_uri (mi->id, + cprc); + ret = TMH_RESPONSE_reply_json_pack (cprc->connection, MHD_HTTP_OK, "{s:s, s:s, s:b, s:s?}", - "taler_pay_uri", - taler_pay_uri, - "contract_url", - final_contract_url, - "paid", - 0, + "taler_pay_uri", taler_pay_uri, + "contract_url", cprc->final_contract_url, + "paid", 0, "already_paid_order_id", - already_paid_order_id - ); - GNUNET_free_non_null (already_paid_order_id); + already_paid_order_id); GNUNET_free (taler_pay_uri); + GNUNET_free_non_null (already_paid_order_id); return ret; } +/** + * Parse the "contract_terms" in @a cprc and set the + * "fulfillment_url" and the "h_contract_terms" in @a cprc + * accordingly. + * + * On errors, the response is being queued and the status + * code set in @cprc "ret". + * + * @param cprc[in,out] context to process + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +static int +parse_contract_terms (struct CheckPaymentRequestContext *cprc) +{ + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("fulfillment_url", + &cprc->fulfillment_url), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (cprc->contract_terms, + spec, + NULL, NULL)) + { + GNUNET_break (0); + cprc->ret + = TMH_RESPONSE_reply_internal_error (cprc->connection, + TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR, + "Merchant database error (contract terms corrupted)"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_JSON_hash (cprc->contract_terms, + &cprc->h_contract_terms)) + { + GNUNET_break (0); + cprc->ret + = TMH_RESPONSE_reply_internal_error (cprc->connection, + TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH, + "Failed to hash proposal"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /** * Check that we are aware of @a order_id and if so request the payment, * otherwise generate an error response. * - * @param connection where to send the response * @param mi the merchant's instance - * @param final_contract_url where to redirect for the contract - * @param session_id the session_id - * @param order_id the order to look up - * @return #MHD_YES on success + * @param cprc session state + * @return status code to return to MHD for @a connection */ static int -check_order_and_request_payment (struct MHD_Connection *connection, - struct MerchantInstance *mi, - const char *final_contract_url, - const char *session_id, - const char *order_id) +check_order_and_request_payment (struct MerchantInstance *mi, + struct CheckPaymentRequestContext *cprc) { enum GNUNET_DB_QueryStatus qs; - json_t *contract_terms; - struct GNUNET_HashCode h_contract_terms; - char *h_contract_terms_str; - int ret; - const char *fulfillment_url; + if (NULL != cprc->contract_terms) + { + /* This should never happen. */ + GNUNET_break (0); + json_decref (cprc->contract_terms); + cprc->contract_terms = NULL; + } qs = db->find_order (db->cls, - &contract_terms, - order_id, + &cprc->contract_terms, + cprc->order_id, &mi->pubkey); if (0 > qs) { @@ -237,55 +368,23 @@ check_order_and_request_payment (struct MHD_Connection *connection, 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 TMH_RESPONSE_reply_internal_error (connection, + return TMH_RESPONSE_reply_internal_error (cprc->connection, TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR, "db error fetching order"); } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { - return TMH_RESPONSE_reply_not_found (connection, + return TMH_RESPONSE_reply_not_found (cprc->connection, TALER_EC_CHECK_PAYMENT_ORDER_ID_UNKNOWN, "unknown order_id"); } - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("fulfillment_url", &fulfillment_url), - GNUNET_JSON_spec_end () - }; - if (GNUNET_OK != GNUNET_JSON_parse (contract_terms, spec, NULL, NULL)) - { - GNUNET_break (0); - json_decref (contract_terms); - return TMH_RESPONSE_reply_internal_error (connection, - TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR, - "Merchant database error (contract terms corrupted)"); - } - } if (GNUNET_OK != - TALER_JSON_hash (contract_terms, - &h_contract_terms)) - { - GNUNET_break (0); - json_decref (contract_terms); - return TMH_RESPONSE_reply_internal_error (connection, - TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH, - "Failed to hash proposal"); - } + parse_contract_terms (cprc)) + return cprc->ret; /* Offer was not picked up yet, but we ensured that it exists */ - h_contract_terms_str = GNUNET_STRINGS_data_to_string_alloc (&h_contract_terms, - sizeof (struct - GNUNET_HashCode)); - ret = send_pay_request (connection, - order_id, - final_contract_url, - session_id, - fulfillment_url, - h_contract_terms_str, - mi); - GNUNET_free_non_null (h_contract_terms_str); - json_decref (contract_terms); - return ret; + return send_pay_request (cprc, + mi); } @@ -310,131 +409,119 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh, size_t *upload_data_size, struct MerchantInstance *mi) { - const char *order_id; - const char *contract_url; - const char *session_id; - const char *fulfillment_url; - char *final_contract_url; - char *h_contract_terms_str; + struct CheckPaymentRequestContext *cprc = *connection_cls; enum GNUNET_DB_QueryStatus qs; - json_t *contract_terms; - struct GNUNET_HashCode h_contract_terms; - struct TALER_Amount refund_amount; int ret; - int refunded; - order_id = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "order_id"); - if (NULL == order_id) + if (NULL == cprc) { - /* order_id is required but missing */ - GNUNET_break_op (0); - return TMH_RESPONSE_reply_bad_request (connection, - TALER_EC_PARAMETER_MISSING, - "order_id required"); - } - contract_url = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "contract_url"); - if (NULL == contract_url) - { - final_contract_url = TALER_url_absolute_mhd (connection, - "/public/proposal", - "instance", mi->id, - "order_id", order_id, - NULL); - GNUNET_assert (NULL != final_contract_url); - } - else - { - final_contract_url = GNUNET_strdup (contract_url); - } - session_id = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "session_id"); - - db->preflight (db->cls); - qs = db->find_contract_terms (db->cls, - &contract_terms, - order_id, - &mi->pubkey); - 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); - GNUNET_free (final_contract_url); - return TMH_RESPONSE_reply_internal_error (connection, - TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR, - "db error fetching contract terms"); - } - - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - /* Check that we're at least aware of the order */ - ret = check_order_and_request_payment (connection, - mi, - final_contract_url, - session_id, - order_id); - GNUNET_free (final_contract_url); - return ret; - } - - GNUNET_assert (NULL != contract_terms); - - /* Get the amount and fulfillment_url from the contract. */ - { - struct TALER_Amount amount; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount ("amount", &amount), - GNUNET_JSON_spec_string ("fulfillment_url", &fulfillment_url), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != GNUNET_JSON_parse (contract_terms, spec, NULL, NULL)) + /* First time here, parse request and check order is known */ + const char *long_poll_timeout_s; + + cprc = GNUNET_new (struct CheckPaymentRequestContext); + cprc->hc.cc = &cprc_cleanup; + cprc->ret = GNUNET_SYSERR; + cprc->connection = connection; + *connection_cls = cprc; + + cprc->order_id = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "order_id"); + if (NULL == cprc->order_id) + { + /* order_id is required but missing */ + GNUNET_break_op (0); + return TMH_RESPONSE_reply_bad_request (connection, + TALER_EC_PARAMETER_MISSING, + "order_id required"); + } + 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 TMH_RESPONSE_reply_bad_request (connection, + TALER_EC_PARAMETER_MALFORMED, + "timeout must be non-negative number"); + } + cprc->long_poll_timeout + = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + timeout)); + } + else { - GNUNET_break (0); - GNUNET_free (final_contract_url); - json_decref (contract_terms); + cprc->long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS; + } + cprc->contract_url = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "contract_url"); + if (NULL == cprc->contract_url) + { + cprc->final_contract_url = TALER_url_absolute_mhd (connection, + "/public/proposal", + "instance", mi->id, + "order_id", + cprc->order_id, + NULL); + GNUNET_assert (NULL != cprc->final_contract_url); + } + else + { + cprc->final_contract_url = GNUNET_strdup (cprc->contract_url); + } + cprc->session_id = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "session_id"); + db->preflight (db->cls); + qs = db->find_contract_terms (db->cls, + &cprc->contract_terms, + cprc->order_id, + &mi->pubkey); + 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 TMH_RESPONSE_reply_internal_error (connection, TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR, - "Merchant database error (contract terms corrupted)"); + "db error fetching contract terms"); } - TALER_amount_get_zero (amount.currency, &refund_amount); - } - if (GNUNET_OK != - TALER_JSON_hash (contract_terms, - &h_contract_terms)) - { - GNUNET_break (0); - json_decref (contract_terms); - GNUNET_free (final_contract_url); - return TMH_RESPONSE_reply_internal_error (connection, - TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH, - "Failed to hash proposal"); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* Check that we're at least aware of the order */ + return check_order_and_request_payment (mi, + cprc); + } + + GNUNET_assert (NULL != cprc->contract_terms); } - h_contract_terms_str = GNUNET_STRINGS_data_to_string_alloc (&h_contract_terms, - sizeof (struct - GNUNET_HashCode)); + if (GNUNET_OK != + parse_contract_terms (cprc)) + return cprc->ret; /* Check if the order has been paid for. */ - if (NULL != session_id) + if (NULL != cprc->session_id) { /* Check if paid within a session. */ - char *already_paid_order_id = NULL; qs = db->find_session_info (db->cls, &already_paid_order_id, - session_id, - fulfillment_url, + cprc->session_id, + cprc->fulfillment_url, &mi->pubkey); if (qs < 0) { @@ -449,56 +536,39 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh, } else if (0 == qs) { - ret = send_pay_request (connection, - order_id, - final_contract_url, - session_id, - fulfillment_url, - h_contract_terms_str, + ret = send_pay_request (cprc, mi); GNUNET_free_non_null (already_paid_order_id); return ret; } GNUNET_break (1 == qs); - GNUNET_break (0 == strcmp (order_id, already_paid_order_id)); + GNUNET_break (0 == strcmp (cprc->order_id, + already_paid_order_id)); GNUNET_free_non_null (already_paid_order_id); } else { /* Check if paid regardless of session. */ - json_t *xcontract_terms = NULL; qs = db->find_paid_contract_terms_from_hash (db->cls, &xcontract_terms, - &h_contract_terms, + &cprc->h_contract_terms, &mi->pubkey); if (0 > qs) { /* Always report on hard error as well to enable diagnostics */ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - GNUNET_free_non_null (h_contract_terms_str); - GNUNET_free (final_contract_url); - json_decref (contract_terms); return TMH_RESPONSE_reply_internal_error (connection, TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR, "Merchant database error"); } if (0 == qs) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "not paid yet\n"); - ret = send_pay_request (connection, - order_id, - final_contract_url, - session_id, - fulfillment_url, - h_contract_terms_str, - mi); - GNUNET_free_non_null (h_contract_terms_str); - GNUNET_free (final_contract_url); - json_decref (contract_terms); - return ret; - + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "not paid yet\n"); + return send_pay_request (cprc, + mi); } GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); GNUNET_assert (NULL != xcontract_terms); @@ -510,9 +580,9 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh, { qs = db->get_refunds_from_contract_terms_hash (db->cls, &mi->pubkey, - &h_contract_terms, + &cprc->h_contract_terms, &process_refunds_cb, - &refund_amount); + cprc); if (GNUNET_DB_STATUS_SOFT_ERROR != qs) break; } @@ -520,26 +590,25 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh, { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database hard error on refunds_from_contract_terms_hash lookup: %s\n", - GNUNET_h2s (&h_contract_terms)); - GNUNET_free_non_null (h_contract_terms_str); - GNUNET_free (final_contract_url); - json_decref (contract_terms); + GNUNET_h2s (&cprc->h_contract_terms)); return TMH_RESPONSE_reply_internal_error (connection, TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR, "Merchant database error"); } - GNUNET_free_non_null (h_contract_terms_str); - - refunded = (0 != refund_amount.value) || (0 != refund_amount.fraction); - - ret = TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:o, s:b, s:b, s:o}", - "contract_terms", contract_terms, - "paid", 1, - "refunded", refunded, - "refund_amount", TALER_JSON_from_amount ( - &refund_amount)); - GNUNET_free (final_contract_url); - return ret; + if (cprc->refunded) + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o, s:b, s:b, s:o}", + "contract_terms", cprc->contract_terms, + "paid", 1, + "refunded", cprc->refunded, + "refund_amount", + TALER_JSON_from_amount ( + &cprc->refund_amount)); + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o, s:b, s:b }", + "contract_terms", cprc->contract_terms, + "paid", 1, + "refunded", 0); } diff --git a/src/lib/merchant_api_check_payment.c b/src/lib/merchant_api_check_payment.c index 8a85d26a..35cd44ce 100644 --- a/src/lib/merchant_api_check_payment.c +++ b/src/lib/merchant_api_check_payment.c @@ -79,12 +79,10 @@ handle_check_payment_finished (void *cls, { struct TALER_MERCHANT_CheckPaymentOperation *cpo = cls; struct TALER_Amount refund_amount = { 0 }; - int refunded; const json_t *json = response; + const json_t *refunded; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_boolean ("refunded", - &refunded), TALER_JSON_spec_amount ("refund_amount", &refund_amount), GNUNET_JSON_spec_end () @@ -103,7 +101,7 @@ handle_check_payment_finished (void *cls, json, GNUNET_SYSERR, GNUNET_SYSERR, - &refund_amount, + NULL, NULL); TALER_MERCHANT_check_payment_cancel (cpo); return; @@ -123,7 +121,7 @@ handle_check_payment_finished (void *cls, json, GNUNET_SYSERR, GNUNET_SYSERR, - &refund_amount, + NULL, NULL); } else @@ -133,17 +131,19 @@ handle_check_payment_finished (void *cls, json, GNUNET_NO, GNUNET_NO, - &refund_amount, + NULL, taler_pay_uri); } TALER_MERCHANT_check_payment_cancel (cpo); return; } - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) + if ( (NULL == (refunded = json_object_get (json, "refunded"))) || + ( (json_true () == refunded) && + (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) ) ) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "check payment failed to parse JSON\n"); @@ -153,7 +153,7 @@ handle_check_payment_finished (void *cls, json, GNUNET_SYSERR, GNUNET_SYSERR, - &refund_amount, + NULL, NULL); TALER_MERCHANT_check_payment_cancel (cpo); return; @@ -163,10 +163,9 @@ handle_check_payment_finished (void *cls, MHD_HTTP_OK, json, GNUNET_YES, - refunded, - &refund_amount, + (json_true () == refunded), + (json_true () == refunded) ? &refund_amount : NULL, NULL); - GNUNET_JSON_parse_free (spec); TALER_MERCHANT_check_payment_cancel (cpo); } -- cgit v1.2.3