From a48af85c36a3340ee9303b57428f2929b08995e4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 13 May 2020 15:43:07 +0200 Subject: implement GET /transfers --- src/lib/merchant_api_get_transfers.c | 352 +++++++++++++++++++---------------- 1 file changed, 194 insertions(+), 158 deletions(-) (limited to 'src/lib/merchant_api_get_transfers.c') diff --git a/src/lib/merchant_api_get_transfers.c b/src/lib/merchant_api_get_transfers.c index 5743f2fe..ac8382ca 100644 --- a/src/lib/merchant_api_get_transfers.c +++ b/src/lib/merchant_api_get_transfers.c @@ -15,9 +15,8 @@ */ /** - * @file lib/merchant_api_track_transfer.c - * @brief Implementation of the /track/transfer request of the - * merchant's HTTP API + * @file lib/merchant_api_get_transfers.c + * @brief Implementation of the GET /transfers request of the merchant's HTTP API * @author Marcello Stanisci * @author Christian Grothoff */ @@ -35,7 +34,7 @@ /** * @brief A Handle for tracking wire transfers. */ -struct TALER_MERCHANT_TrackTransferHandle +struct TALER_MERCHANT_GetTransfersHandle { /** @@ -51,7 +50,7 @@ struct TALER_MERCHANT_TrackTransferHandle /** * Function to call with the result. */ - TALER_MERCHANT_TrackTransferCallback cb; + TALER_MERCHANT_GetTransfersCallback cb; /** * Closure for @a cb. @@ -65,97 +64,11 @@ struct TALER_MERCHANT_TrackTransferHandle }; -/** - * We got a #MHD_HTTP_OK response for the /track/transfer request. - * Check that the response is well-formed and if it is, call the - * callback. If not, return an error code. - * - * This code is very similar to - * exchange_api_transfers_get.c::check_transfers_get_response_ok. - * (Except we do not check the signature, as that was done by the - * backend which we trust already.) - * Any changes should likely be reflected there as well. - * - * @param wdh handle to the operation - * @param json response we got - * @return #GNUNET_OK if we are done and all is well, - * #GNUNET_SYSERR if the response was bogus - */ -static int -check_transfers_get_response_ok ( - struct TALER_MERCHANT_TrackTransferHandle *wdh, - const json_t *json) -{ - json_t *deposits; - struct GNUNET_HashCode h_wire; - struct TALER_Amount total_amount; - struct TALER_MerchantPublicKeyP merchant_pub; - unsigned int num_details; - struct TALER_ExchangePublicKeyP exchange_pub; - struct GNUNET_JSON_Specification inner_spec[] = { - TALER_JSON_spec_amount ("total", &total_amount), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub), - GNUNET_JSON_spec_fixed_auto ("h_wire", &h_wire), - GNUNET_JSON_spec_json ("deposits_sums", &deposits), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", &exchange_pub), - GNUNET_JSON_spec_end () - }; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK, - .reply = json - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - inner_spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - num_details = json_array_size (deposits); - { - struct TALER_MERCHANT_TrackTransferDetails details[num_details]; - - for (unsigned int i = 0; iorder_id), - TALER_JSON_spec_amount ("deposit_value", &detail->deposit_value), - TALER_JSON_spec_amount ("deposit_fee", &detail->deposit_fee), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (deposit, - spec_detail, - NULL, NULL)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (inner_spec); - return GNUNET_SYSERR; - } - } - wdh->cb (wdh->cb_cls, - &hr, - &exchange_pub, - &h_wire, - &total_amount, - num_details, - details); - } - GNUNET_JSON_parse_free (inner_spec); - return GNUNET_OK; -} - - /** * Function called when we're done processing the - * HTTP /track/transfer request. + * HTTP GET /transfers request. * - * @param cls the `struct TALER_MERCHANT_TrackTransferHandle` + * @param cls the `struct TALER_MERCHANT_GetTransfersHandle` * @param response_code HTTP response code, 0 on error * @param json response body, NULL if not in JSON */ @@ -164,37 +77,110 @@ handle_transfers_get_finished (void *cls, long response_code, const void *response) { - struct TALER_MERCHANT_TrackTransferHandle *tdo = cls; + struct TALER_MERCHANT_GetTransfersHandle *gth = cls; const json_t *json = response; struct TALER_MERCHANT_HttpResponse hr = { .http_status = (unsigned int) response_code, .reply = json }; - tdo->job = NULL; + gth->job = NULL; switch (response_code) { case 0: hr.ec = TALER_EC_INVALID_RESPONSE; break; case MHD_HTTP_OK: - if (GNUNET_OK == - check_transfers_get_response_ok (tdo, - json)) { - TALER_MERCHANT_track_transfer_cancel (tdo); - return; + json_t *transfers; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("transfers", + &transfers), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + hr.http_status = 0; + hr.ec = TALER_EC_INVALID_RESPONSE; + break; + } + else + { + size_t tds_length; + struct TALER_MERCHANT_TransferData *tds; + json_t *transfer; + unsigned int i; + bool ok; + + if (! json_is_array (transfers)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + hr.http_status = 0; + hr.ec = TALER_EC_INVALID_RESPONSE; + break; + } + tds_length = json_array_size (transfers); + tds = GNUNET_new_array (tds_length, + struct TALER_MERCHANT_TransferData); + ok = true; + json_array_foreach (transfers, i, transfer) { + struct TALER_MERCHANT_TransferData *td = &tds[i]; + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_amount ("credit_amount", + &td->credit_amount), + GNUNET_JSON_spec_fixed_auto ("wtid", + &td->wtid), + GNUNET_JSON_spec_string ("payto_uri", + &td->payto_uri), + GNUNET_JSON_spec_string ("exchange_url", + &td->exchange_url), + GNUNET_JSON_spec_uint64 ("credit_serial", + &td->credit_serial), + GNUNET_JSON_spec_absolute_time ("execution_time", + &td->execution_time), + GNUNET_JSON_spec_bool ("verified", + &td->verified), + GNUNET_JSON_spec_bool ("confirmed", + &td->confirmed), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (transfer, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + ok = false; + break; + } + } + + if (! ok) + { + GNUNET_break_op (0); + GNUNET_free (tds); + GNUNET_JSON_parse_free (spec); + hr.http_status = 0; + hr.ec = TALER_EC_INVALID_RESPONSE; + break; + } + gth->cb (gth->cb_cls, + &hr, + tds_length, + tds); + GNUNET_free (tds); + GNUNET_JSON_parse_free (spec); + TALER_MERCHANT_transfers_get_cancel (gth); + return; + } } - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_INVALID_RESPONSE; // TODO: use more specific code! - break; - case MHD_HTTP_FAILED_DEPENDENCY: - /* Not a reason to break execution. */ - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ @@ -220,95 +206,145 @@ handle_transfers_get_finished (void *cls, response_code = 0; break; } - tdo->cb (tdo->cb_cls, + gth->cb (gth->cb_cls, &hr, - NULL, - NULL, - NULL, 0, NULL); - TALER_MERCHANT_track_transfer_cancel (tdo); + TALER_MERCHANT_transfers_get_cancel (gth); } /** - * Request backend to return transfers associated with a given wtid. + * Request backend to return list of all wire transfers that + * we received (or that the exchange claims we should have received). + * + * Note that when filtering by timestamp (using “before” and/or “after”), we + * use the time reported by the exchange and thus will ONLY return results for + * which we already have a response from the exchange. This should be + * virtually all transfers, however it is conceivable that for some transfer + * the exchange responded with a temporary error (i.e. HTTP status 500+) and + * then we do not yet have an execution time to filter by. Thus, IF timestamp + * filters are given, transfers for which we have no response from the + * exchange yet are automatically excluded. * * @param ctx execution context * @param backend_url base URL of the backend - * @param wire_method wire method used for the wire transfer - * @param wtid base32 string indicating a wtid - * @param exchange_url base URL of the exchange in charge of returning the wanted information - * @param track_transfer_cb the callback to call when a reply for this request is available - * @param track_transfer_cb_cls closure for @a contract_cb + * @param payto_uri filter by this credit account of the merchant + * @param before limit to transactions before this timestamp, use + * #GNUNET_TIME_UNIT_FOREVER_ABS to not filter by @a before + * @param after limit to transactions after this timestamp, use + * #GNUNET_TIME_UNIT_ZERO_ABS to not filter by @a after + * @param limit return at most ths number of results; negative to descend in execution time + * @param offset start at this "credit serial" number (exclusive) + * @param verified filter results by verification status + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for @a cb * @return a handle for this request */ -struct TALER_MERCHANT_TrackTransferHandle * -TALER_MERCHANT_track_transfer ( +struct TALER_MERCHANT_GetTransfersHandle * +TALER_MERCHANT_transfers_get ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *wire_method, - const struct TALER_WireTransferIdentifierRawP *wtid, - const char *exchange_url, - TALER_MERCHANT_TrackTransferCallback track_transfer_cb, - void *track_transfer_cb_cls) + const char *payto_uri, + const struct GNUNET_TIME_Absolute before, + const struct GNUNET_TIME_Absolute after, + int64_t limit, + uint64_t offset, + enum TALER_MERCHANT_YesNoAll verified, + TALER_MERCHANT_GetTransfersCallback cb, + void *cb_cls) { - struct TALER_MERCHANT_TrackTransferHandle *tdo; + struct TALER_MERCHANT_GetTransfersHandle *gth; CURL *eh; - char *wtid_str; + const char *verified_s = NULL; + char limit_s[30]; + char offset_s[30]; + char *before_s; + char *after_s; - wtid_str = GNUNET_STRINGS_data_to_string_alloc ( - wtid, - sizeof (struct TALER_WireTransferIdentifierRawP)); - tdo = GNUNET_new (struct TALER_MERCHANT_TrackTransferHandle); - tdo->ctx = ctx; - tdo->cb = track_transfer_cb; // very last to be called - tdo->cb_cls = track_transfer_cb_cls; - tdo->url = TALER_url_join (backend_url, "track/transfer", - "wtid", wtid_str, - "exchange", exchange_url, - "wire_method", wire_method, + gth = GNUNET_new (struct TALER_MERCHANT_GetTransfersHandle); + gth->ctx = ctx; + gth->cb = cb; + gth->cb_cls = cb_cls; + if (TALER_MERCHANT_YNA_YES == verified) + verified_s = "yes"; + if (TALER_MERCHANT_YNA_NO == verified) + verified_s = "no"; + GNUNET_snprintf (limit_s, + sizeof (limit_s), + "%lld", + (long long) limit); + GNUNET_snprintf (offset_s, + sizeof (offset_s), + "%lld", + (unsigned long long) offset); + before_s = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string (before)); + after_s = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string (after)); + gth->url = TALER_url_join (backend_url, + "transfers", + "payto_uri", + payto_uri, + "verified", + verified_s, + "limit", + 0 != limit + ? limit_s + : NULL, + "offset", + ((0 != offset) && (UINT64_MAX != offset)) + ? offset_s + : NULL, + "before", + before.abs_value_us != + GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us + ? before_s + : NULL, + "after", + after.abs_value_us != 0 + ? after_s + : NULL, NULL); - GNUNET_free (wtid_str); - if (NULL == tdo->url) + GNUNET_free (before_s); + GNUNET_free (after_s); + if (NULL == gth->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not construct request URL.\n"); - GNUNET_free (tdo); + GNUNET_free (gth); return NULL; } eh = curl_easy_init (); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_URL, - tdo->url)); - tdo->job = GNUNET_CURL_job_add (ctx, + gth->url)); + gth->job = GNUNET_CURL_job_add (ctx, eh, GNUNET_YES, &handle_transfers_get_finished, - tdo); - return tdo; + gth); + return gth; } /** - * Cancel a /track/transfer request. This function cannot be used + * Cancel a GET /transfers request. This function cannot be used * on a request handle if a response is already served for it. * - * @param tdo handle to the tracking operation being cancelled + * @param gth handle to the tracking operation being cancelled */ void -TALER_MERCHANT_track_transfer_cancel ( - struct TALER_MERCHANT_TrackTransferHandle *tdo) +TALER_MERCHANT_transfers_get_cancel ( + struct TALER_MERCHANT_GetTransfersHandle *gth) { - if (NULL != tdo->job) + if (NULL != gth->job) { - GNUNET_CURL_job_cancel (tdo->job); - tdo->job = NULL; + GNUNET_CURL_job_cancel (gth->job); + gth->job = NULL; } - GNUNET_free (tdo->url); - GNUNET_free (tdo); + GNUNET_free (gth->url); + GNUNET_free (gth); } -/* end of merchant_api_track_transfer.c */ +/* end of merchant_api_get_transfers.c */ -- cgit v1.2.3