cash2ecash

cash2ecash: cash acceptor that issues digital cash (experimental)
Log | Files | Refs | README | LICENSE

commit c3347d0e469b617a0cac1dbb2f8d9ec23f08f1a8
parent 5f5f97e08bf3f0fa67eac5a0ad02cfd98dbc6973
Author: Tellenbach Reto <tellr1@bfh.ch>
Date:   Sun,  7 Jun 2026 23:11:25 +0200

[new] Tapler-api: GET /accounts//withdrawals//confirm api implmentation done

Diffstat:
Asrc/lib/bank_api_post_accounts_withdrawals_confirm.c | 353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/bank_api_post_accounts_withdrawals_confirm.h | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 502 insertions(+), 0 deletions(-)

diff --git a/src/lib/bank_api_post_accounts_withdrawals_confirm.c b/src/lib/bank_api_post_accounts_withdrawals_confirm.c @@ -0,0 +1,353 @@ +/* + This file is part of TALER cash2ecash + Copyright (C) 2026 GNUnet e.V. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +/** + * @file lib/bank_api_post_accounts_withdrawals_confirm.c + * @brief implements the Taler Bank API "POST /accounts/$USERNAME/withdrawals/$WITHDRAWAL_ID/confirm" handler + * @author Reto Tellenbach + */ + +#include <microhttpd.h> +#include "taler/taler_json_lib.h" +#include "bank_api_post_accounts_withdrawals_confirm.h" + +/** + * Log error related to CURL operations. + * + * @param type log level + * @param function which function failed to run + * @param code what was the curl error code + */ +#define CURL_STRERROR(type, function, code) \ + GNUNET_log (type, \ + "Curl function `%s' has failed at `%s:%d' with error: %s", \ + function, __FILE__, __LINE__, curl_easy_strerror (code)); + +/** + * Handle for the accounts create withdrawal request. + */ +struct TALER_BANK_PostWithdrawalConfirmHandle +{ + /** + * The context of this handle + */ + struct GNUNET_CURL_Context *ctx; + + /** + * curle easy handle + */ + CURL *easy_handle; + + /** + * Context for curl easy post. Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Authentification date to access bank account + */ + const struct DIGITIZER_BankAuthenticationData *authorization; + + /** + * Function to call with the , + * NULL if this has already been done. + */ + TALER_BANK_WithdrawalConfirmCallback woc_cb; + + /** + * Closure to pass to + * ater withdrawal operation creation + */ + void *woc_cb_cls; + + /** + * Data for the request to get the accounts/$USERNAME of a bank, + * NULL once we are past stage #MHS_INIT. + */ + struct GNUNET_CURL_Job *job; + + /** + * The whole request line + */ + char *job_url; + +}; + + +/** + * Decode the JSON in @a resp_obj from the accounts/$USERNAME/withdrawal/confirm response when challenged + * + * @param[in] resp_obj JSON object to parse + * @param[in,out] vi where to store the results we decoded + * @param[out] vc where to store challenge information + * @return #TALER_EC_NONE on success + */ +static enum TALER_ErrorCode +decode_challenge_json (const json_t *resp_obj, + struct TALER_BANK_ChallengeResponse *vi) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received body\n`%s'\n", + json_dumps(resp_obj, 0)); + + + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("balance", + &vi->blc), + GNUNET_JSON_spec_end () + }; + + if (JSON_OBJECT != json_typeof (resp_obj)) + { + GNUNET_break_op (0); + return TALER_EC_GENERIC_JSON_INVALID; + } + if (GNUNET_OK != + GNUNET_JSON_parse (resp_obj, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return TALER_EC_GENERIC_JSON_INVALID; + } + + return TALER_EC_NONE; +} + + +/** + * Callback used when http reply arived to a /accounts/$USERNAME/withdrawal/confirm request. + * + * @param cls the `struct TALER_BANK_PostWithdrawalConfirmHandle` + * @param response_code HTTP response code or 0 on error + * @param gresp_obj JSON result, NULL on error, must be a `const json_t *` + */ +static void +response_cb(void *cls, + long response_code, + const void *gresp_obj) +{ + struct TALER_BANK_PostWithdrawalConfirmHandle *pcwh = cls; + const json_t *resp_obj = gresp_obj; + + struct TALER_BANK_WithdrawalConfirmResponse pacwr = { + .hr.response = resp_obj, + .hr.http_status = (unsigned int)response_code + }; + + pcwh->job = NULL; //job was successfull, curl job cancel not needed anymore in cleanup + pcwh->easy_handle = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received from URL `%s' with status %ld.\n", + pcwh->job_url, + response_code); + + switch (response_code) + { + case 0: + GNUNET_break_op (0); + pacwr.hr.ec = TALER_EC_INVALID; + break; + case MHD_HTTP_ACCEPTED: + if (NULL == resp_obj) + { + GNUNET_break_op (0); + pacwr.hr.http_status = 0; + pacwr.hr.ec = decode_challenge_json (resp_obj, + &pacwr.details.ok.challenge); + break; + } + case MHD_HTTP_OK: + if (NULL == resp_obj) + { + GNUNET_break_op (0); + pacwr.hr.http_status = 0; + pacwr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (TALER_EC_NONE != pacwr.hr.ec) + { + GNUNET_break_op (0); + pacwr.hr.http_status = 0; + break; + } + break; + case MHD_HTTP_UNAUTHORIZED: + pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); + pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid or missing credentials %u/%d\n", + (unsigned int) response_code, + (int) pacwr.hr.ec); + break; + case MHD_HTTP_FORBIDDEN: + pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); + pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Missing rights %u/%d\n", + (unsigned int) response_code, + (int) pacwr.hr.ec); + break; + case MHD_HTTP_NOT_FOUND: + pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); + pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "The operation was not found. %u/%d\n", + (unsigned int) response_code, + (int) pacwr.hr.ec); + break; + case MHD_HTTP_CONFLICT: + pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); + pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Conflict %u/%d\n", + (unsigned int) response_code, + (int) pacwr.hr.ec); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); + pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); + break; + default: + pacwr.hr.ec = TALER_JSON_get_error_code (resp_obj); + pacwr.hr.hint = TALER_JSON_get_error_hint (resp_obj); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) pacwr.hr.ec); + break; + } + + pcwh->woc_cb (pcwh->woc_cb_cls, &pacwr); + TALER_BANK_post_withdrawal_create_cancel(pcwh); +} + +/** + * create handle for poost accounts/withdrawal/confirm request + */ +struct TALER_BANK_PostWithdrawalConfirmHandle * +TALER_BANK_post_withdrawal_confirm_create ( struct GNUNET_CURL_Context *ctx, + const char *base_url, + const char *username, + const char *wopid, + const struct DIGITIZER_BankAuthenticationData *authorization) +{ + struct TALER_BANK_PostWithdrawalConfirmHandle *pwc; + char *usr; + pwc = GNUNET_new(struct TALER_BANK_PostWithdrawalConfirmHandle); + + pwc->ctx = ctx; + pwc->authorization = authorization; + + GNUNET_asprintf(&usr, + "accounts/%s/withdrawals/%s/confirm", + username, + wopid); + pwc->job_url = TALER_url_join(base_url, + usr, + NULL); + pwc->easy_handle = TALER_BANK_curl_easy_get_ (pwc->job_url); + if (NULL == pwc->easy_handle) + { + GNUNET_free (usr); + GNUNET_break (0); + TALER_BANK_post_withdrawal_create_cancel (pwc); + return GNUNET_NO; + } + GNUNET_free (usr); + return pwc; +} + +/** + * Post accounts/withdrawal + */ +enum GNUNET_GenericReturnValue +TALER_BANK_post_withdrawal_confirm ( struct TALER_BANK_PostWithdrawalConfirmHandle *handle, + const struct TALER_BANK_AccountWithdrawalConfirmRequest*req_ctx, + TALER_BANK_WithdrawalConfirmCallback pwc_cb, + void *pwc_cb_cls) +{ + json_t *req; + + handle->woc_cb = pwc_cb; + handle->woc_cb_cls = pwc_cb_cls; + + req = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ((TALER_amount_is_valid(&req_ctx->amount) & + !TALER_amount_is_zero(&req_ctx->amount)) ? + TALER_JSON_pack_amount ("amount", + &req_ctx->amount): + GNUNET_JSON_pack_string ("amount", NULL))); + + + if(GNUNET_OK != DIGITIZER_setup_auth_(handle->easy_handle,handle->authorization)) + { + GNUNET_break(0); + TALER_BANK_post_withdrawal_create_cancel(handle); + return GNUNET_NO; + } + if(GNUNET_OK != + TALER_curl_easy_post(&handle->post_ctx, + handle->easy_handle, + req)) + { + GNUNET_break(0); + TALER_BANK_post_withdrawal_create_cancel(handle); + return GNUNET_NO; + } + + json_decref(req); + GNUNET_log( GNUNET_ERROR_TYPE_INFO, + "Requesting URL `%s'.\n", + handle->job_url); + handle->job = GNUNET_CURL_job_add2(handle->ctx, + handle->easy_handle, + handle->post_ctx.headers, + &response_cb, + handle); + if(NULL == handle->job) + { + GNUNET_break(0); + TALER_BANK_post_withdrawal_create_cancel(handle); + return GNUNET_NO; + } + + return GNUNET_OK; +} + + + +void +TALER_BANK_post_withdrawal_confirm_cancel ( struct TALER_BANK_PostWithdrawalConfirmHandle *pwch) +{ + if(NULL != pwch->job) + { + GNUNET_CURL_job_cancel(pwch->job); + pwch->job = NULL; + pwch->easy_handle = NULL; + } + if(NULL != pwch->easy_handle) + { + curl_easy_cleanup (pwch->easy_handle); + pwch->easy_handle = NULL; + } + TALER_curl_easy_post_finished(&pwch->post_ctx); + GNUNET_free(pwch->job_url); + GNUNET_free(pwch); +} diff --git a/src/lib/bank_api_post_accounts_withdrawals_confirm.h b/src/lib/bank_api_post_accounts_withdrawals_confirm.h @@ -0,0 +1,149 @@ +/* + This file is part of TALER cash2ecash + Copyright (C) 2026 GNUnet e.V. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ +/** + * @file lib/bank_api_post_accounts_withdrawals_confirm.h + * @brief implements the Taler Bank API "POST /accounts/$USERNAME/withdrawals/$WITHDRAWAL_ID/confirm" handler + * @author Reto Tellenbach + */ +#ifndef BANK_API_POST_ACCOUNTS_WITHDRAWALS_CONFIRM_H +#define BANK_API_POST_ACCOUNTS_WITHDRAWALS_CONFIRM_H + +#include <taler/taler_curl_lib.h> +#include "bank_api_curl_defaults.h" +#include "api_common.h" +#include "taler/taler_bank_service.h" + + +/** + * @brief withdrawal operation information + * provided for request + */ +struct TALER_BANK_AccountWithdrawalConfirmRequest +{ + /** + * Amount to withdraw. + * Has to be set if not set at withrawal creation + */ + struct TALER_Amount amount; +}; + +/** + * challenge response not jet implemented + */ +struct TALER_BANK_ChallengeResponse +{ + /** + * temporal json object for parsing + */ + json_t blc; +}; + +/** + * Response details for a accounts/$USERNAME/withdrawals request + */ +struct TALER_BANK_WithdrawalConfirmResponse +{ + + /** + * HTTP response + */ + struct TALER_BANK_HttpResponse hr; + + /** + * Details returned depending on the @e http_status. + */ + union + { + + /** + * Details if status was request was succesfull + */ + struct + { + + /** + * KYC challenge + */ + struct TALER_BANK_ChallengeResponse challenge; + + } ok; + + } details; + +}; + + +/** + * Callback after confirming the withdrawal + * + * @param cls closure + * @param vr response data + */ +typedef void +(*TALER_BANK_WithdrawalConfirmCallback) ( + void *cls, + const struct TALER_BANK_WithdrawalConfirmResponse *vr); + + +/** + * Handle for the post confirm withdrawal request. + */ +struct TALER_BANK_PostWithdrawalConfirmHandle; + + +/** + * create handle for post TALER_BANK_post_accounts_withdrawal_confirm_confirm() + * @param ctx curl context + * @param url bank url + * @param username bank account name + * @param authorization authentication for account + * @param accounts_cb callback + * @param accounts_cb_cls callback context + * @return NULL on failure + */ +struct TALER_BANK_PostWithdrawalConfirmHandle * +TALER_BANK_post_accounts_withdrawal_confirm_confirm_create ( struct GNUNET_CURL_Context *ctx, + const char *base_url, + const char *username, + const struct DIGITIZER_BankAuthenticationData *authorization); + + +/** + * Http request POST /accounts/withdrawal/confirm + * @param handle handle, is created with TALER_BANK_post_accounts_withdrawal_confirm_confirm_create() + * @param req_ctx request body data + * @param wo_confirm_cb callback + * @param wo_confirm_cb_cls callback context + * @return GNUNET_NO on failure + */ +enum GNUNET_GenericReturnValue +TALER_BANK_post_withdrawal_confirm ( struct TALER_BANK_PostWithdrawalConfirmHandle *handle, + const struct TALER_BANK_AccountWithdrawalConfirmRequest*req_ctx, + TALER_BANK_WithdrawalConfirmCallback pwc_cb, + void *pwc_cb_cls); + + +/** + * Cancel accounts confirm withdrawal request. Frees if neccessary + * @param account handle + */ +void +TALER_BANK_post_withdrawal_confirm_cancel ( struct TALER_BANK_PostWithdrawalConfirmHandle *pwch); + +#endif