work on kyc-tester
This commit is contained in:
parent
ff48ada7d5
commit
f50a2e11b0
@ -14,7 +14,8 @@ pkgcfg_DATA = \
|
|||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
kyclogic.conf \
|
kyclogic.conf \
|
||||||
kyclogic-oauth2.conf
|
kyclogic-oauth2.conf \
|
||||||
|
sample.conf
|
||||||
|
|
||||||
lib_LTLIBRARIES = \
|
lib_LTLIBRARIES = \
|
||||||
libtalerkyclogic.la
|
libtalerkyclogic.la
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
# This file is in the public domain.
|
||||||
|
|
||||||
|
# Example Oauth2.0 provider configuration.
|
||||||
|
|
||||||
|
[kyc-provider-example-oauth2]
|
||||||
|
|
||||||
|
COST = 42
|
||||||
|
LOGIC = oauth2
|
||||||
|
USER_TYPE = INDIVIDUAL
|
||||||
|
PROVIDED_CHECKS = EXAMPLE_DO_NOT_USE
|
||||||
|
|
||||||
|
# How long is the KYC check valid?
|
||||||
|
KYC_OAUTH2_VALIDITY = forever
|
||||||
|
|
||||||
|
# URL where we initiate the user's login process
|
||||||
|
KYC_OAUTH2_LOGIN_URL = http://kyc.example.com/login
|
||||||
|
# URL where we send the user's authentication information
|
||||||
|
KYC_OAUTH2_AUTH_URL = http://kyc.example.com/auth
|
||||||
|
# URL of the user info access point.
|
||||||
|
KYC_OAUTH2_INFO_URL = http://kyc.example.com/info
|
||||||
|
|
||||||
|
# Where does the client get redirected upon completion?
|
||||||
|
KYC_OAUTH2_POST_URL = http://example.com/thank-you
|
||||||
|
|
||||||
|
# For authentication to the OAuth2.0 service
|
||||||
|
KYC_OAUTH2_CLIENT_ID = testcase
|
||||||
|
KYC_OAUTH2_CLIENT_SECRET = password
|
@ -0,0 +1,15 @@
|
|||||||
|
# This file is in the public domain.
|
||||||
|
#
|
||||||
|
# Sample legitimization rule set.
|
||||||
|
|
||||||
|
#[kyc-legitimization-withdraw-high]
|
||||||
|
# KYC hook is this rule is about.
|
||||||
|
#OPERATION_TYPE = WITHDRAW
|
||||||
|
# Which checks must be done. Give names used by providers.
|
||||||
|
#REQUIRED_CHECKS = PHONE GOVID SSN
|
||||||
|
# Treshold amount above which the checks are required.
|
||||||
|
#THRESHOLD = KUDOS:100
|
||||||
|
# Timeframe over which amounts involved in the
|
||||||
|
# operation type are accumulated to test against
|
||||||
|
# the threshold.
|
||||||
|
#TIMEFRAME = 1a
|
@ -101,9 +101,9 @@ struct TALER_KYCLOGIC_ProviderDetails
|
|||||||
char *post_kyc_redirect_url;
|
char *post_kyc_redirect_url;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expiration time for a successful KYC process.
|
* Validity time for a successful KYC process.
|
||||||
*/
|
*/
|
||||||
struct GNUNET_TIME_Relative expiration;
|
struct GNUNET_TIME_Relative validity;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -295,12 +295,12 @@ oauth2_load_configuration (void *cls,
|
|||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_CONFIGURATION_get_value_time (ps->cfg,
|
GNUNET_CONFIGURATION_get_value_time (ps->cfg,
|
||||||
provider_section_name,
|
provider_section_name,
|
||||||
"KYC_OAUTH2_EXPIRATION",
|
"KYC_OAUTH2_VALIDITY",
|
||||||
&pd->expiration))
|
&pd->validity))
|
||||||
{
|
{
|
||||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||||
provider_section_name,
|
provider_section_name,
|
||||||
"KYC_OAUTH2_EXPIARTION");
|
"KYC_OAUTH2_VALIDITY");
|
||||||
oauth2_unload_configuration (pd);
|
oauth2_unload_configuration (pd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -367,12 +367,12 @@ oauth2_load_configuration (void *cls,
|
|||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
||||||
provider_section_name,
|
provider_section_name,
|
||||||
"KYC_INFO_URL",
|
"KYC_OAUTH2_INFO_URL",
|
||||||
&s))
|
&s))
|
||||||
{
|
{
|
||||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||||
provider_section_name,
|
provider_section_name,
|
||||||
"KYC_INFO_URL");
|
"KYC_OAUTH2_INFO_URL");
|
||||||
oauth2_unload_configuration (pd);
|
oauth2_unload_configuration (pd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -553,7 +553,7 @@ return_proof_response (void *cls)
|
|||||||
ph->status,
|
ph->status,
|
||||||
ph->provider_user_id,
|
ph->provider_user_id,
|
||||||
ph->provider_legitimization_id,
|
ph->provider_legitimization_id,
|
||||||
GNUNET_TIME_relative_to_absolute (ph->pd->expiration),
|
GNUNET_TIME_relative_to_absolute (ph->pd->validity),
|
||||||
ph->http_status,
|
ph->http_status,
|
||||||
ph->response);
|
ph->response);
|
||||||
GNUNET_free (ph->provider_user_id);
|
GNUNET_free (ph->provider_user_id);
|
||||||
|
33
src/kyclogic/sample.conf
Normal file
33
src/kyclogic/sample.conf
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# This file is in the public domain.
|
||||||
|
#
|
||||||
|
|
||||||
|
[exchange]
|
||||||
|
|
||||||
|
# HTTP port the exchange listens to
|
||||||
|
PORT = 8081
|
||||||
|
|
||||||
|
# Base URL of the exchange. Must be set to a URL where the
|
||||||
|
# exchange (or the twister) is actually listening.
|
||||||
|
BASE_URL = "http://localhost:8081/"
|
||||||
|
|
||||||
|
[kyc-provider-test-oauth2]
|
||||||
|
|
||||||
|
COST = 0
|
||||||
|
LOGIC = oauth2
|
||||||
|
USER_TYPE = INDIVIDUAL
|
||||||
|
PROVIDED_CHECKS = DUMMY
|
||||||
|
|
||||||
|
KYC_OAUTH2_VALIDITY = forever
|
||||||
|
KYC_OAUTH2_AUTH_URL = http://kyc.taler.net/auth
|
||||||
|
KYC_OAUTH2_LOGIN_URL = http://kyc.taler.net/login
|
||||||
|
KYC_OAUTH2_INFO_URL = http://kyc.taler.net/info
|
||||||
|
KYC_OAUTH2_POST_URL = http://kyc.taler.net/thank-you
|
||||||
|
KYC_OAUTH2_CLIENT_ID = testcase
|
||||||
|
KYC_OAUTH2_CLIENT_SECRET = password
|
||||||
|
|
||||||
|
[kyc-legitimization-withdraw-high]
|
||||||
|
|
||||||
|
OPERATION_TYPE = WITHDRAW
|
||||||
|
REQUIRED_CHECKS = DUMMY
|
||||||
|
THRESHOLD = KUDOS:100
|
||||||
|
TIMEFRAME = 1a
|
@ -171,21 +171,51 @@ static const struct GNUNET_CONFIGURATION_Handle *TEKT_cfg;
|
|||||||
*/
|
*/
|
||||||
static struct MHD_Daemon *mhd;
|
static struct MHD_Daemon *mhd;
|
||||||
|
|
||||||
/**
|
|
||||||
* Our currency.
|
|
||||||
*/
|
|
||||||
static char *TEKT_currency;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our base URL.
|
* Our base URL.
|
||||||
*/
|
*/
|
||||||
static char *TEKT_base_url;
|
static char *TEKT_base_url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payto set via command-line (or otherwise random).
|
||||||
|
*/
|
||||||
|
static struct TALER_PaytoHashP cmd_line_h_payto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Row ID to use, override with '-r'
|
||||||
|
*/
|
||||||
|
static unsigned int kyc_row_id = 42;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -P command-line option.
|
||||||
|
*/
|
||||||
|
static int print_h_payto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -w command-line option.
|
||||||
|
*/
|
||||||
|
static int run_webservice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value to return from main()
|
* Value to return from main()
|
||||||
*/
|
*/
|
||||||
static int global_ret;
|
static int global_ret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -i command-line flag.
|
||||||
|
*/
|
||||||
|
static char *initiate_section;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for ongoing initiation operation.
|
||||||
|
*/
|
||||||
|
static struct TALER_KYCLOGIC_InitiateHandle *ih;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KYC logic running for @e ih.
|
||||||
|
*/
|
||||||
|
static struct TALER_KYCLOGIC_Plugin *logic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Port to run the daemon on.
|
* Port to run the daemon on.
|
||||||
*/
|
*/
|
||||||
@ -203,24 +233,6 @@ static struct GNUNET_CURL_Context *TEKT_curl_ctx;
|
|||||||
static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
|
static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a 404 "not found" reply on @a connection with
|
|
||||||
* the hint @a details.
|
|
||||||
*
|
|
||||||
* @param connection where to send the reply on
|
|
||||||
* @param details details for the error message, can be NULL
|
|
||||||
*/
|
|
||||||
static MHD_RESULT
|
|
||||||
r404 (struct MHD_Connection *connection,
|
|
||||||
const char *details)
|
|
||||||
{
|
|
||||||
return TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_NOT_FOUND,
|
|
||||||
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
|
|
||||||
details);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context for the webhook.
|
* Context for the webhook.
|
||||||
*/
|
*/
|
||||||
@ -427,11 +439,12 @@ kyc_provider_account_lookup (
|
|||||||
const char *provider_legitimization_id,
|
const char *provider_legitimization_id,
|
||||||
struct TALER_PaytoHashP *h_payto)
|
struct TALER_PaytoHashP *h_payto)
|
||||||
{
|
{
|
||||||
// FIXME: pass value to use for h_payto via command-line?
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
memset (h_payto,
|
"Simulated account lookup using `%s/%s'\n",
|
||||||
42,
|
provider_section,
|
||||||
sizeof (*h_payto));
|
provider_legitimization_id);
|
||||||
return 1; // FIXME...
|
*h_payto = cmd_line_h_payto;
|
||||||
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -745,25 +758,13 @@ handle_mhd_request (void *cls,
|
|||||||
{
|
{
|
||||||
static struct TEKT_RequestHandler handlers[] = {
|
static struct TEKT_RequestHandler handlers[] = {
|
||||||
#if FIXME
|
#if FIXME
|
||||||
/* KYC endpoints */
|
/* simulated KYC endpoints */
|
||||||
{
|
|
||||||
.url = "kyc-check",
|
|
||||||
.method = MHD_HTTP_METHOD_GET,
|
|
||||||
.handler.get = &TEKT_handler_kyc_check,
|
|
||||||
.nargs = 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.url = "kyc-proof",
|
.url = "kyc-proof",
|
||||||
.method = MHD_HTTP_METHOD_GET,
|
.method = MHD_HTTP_METHOD_GET,
|
||||||
.handler.get = &TEKT_handler_kyc_proof,
|
.handler.get = &TEKT_handler_kyc_proof,
|
||||||
.nargs = 1
|
.nargs = 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.url = "kyc-wallet",
|
|
||||||
.method = MHD_HTTP_METHOD_POST,
|
|
||||||
.handler.post = &TEKT_handler_kyc_wallet,
|
|
||||||
.nargs = 0
|
|
||||||
},
|
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
.url = "kyc-webhook",
|
.url = "kyc-webhook",
|
||||||
@ -994,6 +995,11 @@ do_shutdown (void *cls)
|
|||||||
struct MHD_Daemon *mhd;
|
struct MHD_Daemon *mhd;
|
||||||
(void) cls;
|
(void) cls;
|
||||||
|
|
||||||
|
if (NULL != ih)
|
||||||
|
{
|
||||||
|
logic->initiate_cancel (ih);
|
||||||
|
ih = NULL;
|
||||||
|
}
|
||||||
kyc_webhook_cleanup ();
|
kyc_webhook_cleanup ();
|
||||||
TALER_KYCLOGIC_kyc_done ();
|
TALER_KYCLOGIC_kyc_done ();
|
||||||
mhd = TALER_MHD_daemon_stop ();
|
mhd = TALER_MHD_daemon_stop ();
|
||||||
@ -1012,6 +1018,45 @@ do_shutdown (void *cls)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called with the result of a KYC initiation
|
||||||
|
* operation.
|
||||||
|
*
|
||||||
|
* @param cls closure
|
||||||
|
* @param ec #TALER_EC_NONE on success
|
||||||
|
* @param redirect_url set to where to redirect the user on success, NULL on failure
|
||||||
|
* @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
|
||||||
|
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
|
||||||
|
* @param error_msg_hint set to additional details to return to user, NULL on success
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
initiate_cb (
|
||||||
|
void *cls,
|
||||||
|
enum TALER_ErrorCode ec,
|
||||||
|
const char *redirect_url,
|
||||||
|
const char *provider_user_id,
|
||||||
|
const char *provider_legitimization_id,
|
||||||
|
const char *error_msg_hint)
|
||||||
|
{
|
||||||
|
ih = NULL;
|
||||||
|
if (TALER_EC_NONE != ec)
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"Failed to start KYC process: %s (#%d)\n",
|
||||||
|
error_msg_hint,
|
||||||
|
ec);
|
||||||
|
global_ret = EXIT_FAILURE;
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fprintf (stdout,
|
||||||
|
"Visit `%s' to begin KYC process (%s/%s)\n",
|
||||||
|
redirect_url,
|
||||||
|
provider_user_id,
|
||||||
|
provider_legitimization_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main function that will be run by the scheduler.
|
* Main function that will be run by the scheduler.
|
||||||
*
|
*
|
||||||
@ -1032,6 +1077,17 @@ run (void *cls,
|
|||||||
(void) cls;
|
(void) cls;
|
||||||
(void) args;
|
(void) args;
|
||||||
(void ) cfgfile;
|
(void ) cfgfile;
|
||||||
|
if (print_h_payto)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
s = GNUNET_STRINGS_data_to_string_alloc (&cmd_line_h_payto,
|
||||||
|
sizeof (cmd_line_h_payto));
|
||||||
|
fprintf (stdout,
|
||||||
|
"%s\n",
|
||||||
|
s);
|
||||||
|
GNUNET_free (s);
|
||||||
|
}
|
||||||
TALER_MHD_setup (TALER_MHD_GO_NONE);
|
TALER_MHD_setup (TALER_MHD_GO_NONE);
|
||||||
TEKT_cfg = config;
|
TEKT_cfg = config;
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
@ -1041,14 +1097,42 @@ run (void *cls,
|
|||||||
GNUNET_SCHEDULER_shutdown ();
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
|
||||||
|
NULL);
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
exchange_serve_process_config ())
|
exchange_serve_process_config ())
|
||||||
{
|
{
|
||||||
global_ret = EXIT_NOTCONFIGURED;
|
global_ret = EXIT_NOTCONFIGURED;
|
||||||
TALER_KYCLOGIC_kyc_done ();
|
|
||||||
GNUNET_SCHEDULER_shutdown ();
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
global_ret = EXIT_SUCCESS;
|
||||||
|
if (NULL != initiate_section)
|
||||||
|
{
|
||||||
|
struct TALER_KYCLOGIC_ProviderDetails *pd;
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_KYCLOGIC_kyc_get_logic (initiate_section,
|
||||||
|
&logic,
|
||||||
|
&pd))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"Could not initiate KYC with provider `%s' (configuration error?)\n",
|
||||||
|
initiate_section);
|
||||||
|
global_ret = EXIT_NOTCONFIGURED;
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ih = logic->initiate (logic->cls,
|
||||||
|
pd,
|
||||||
|
&cmd_line_h_payto,
|
||||||
|
kyc_row_id,
|
||||||
|
&initiate_cb,
|
||||||
|
NULL);
|
||||||
|
GNUNET_break (NULL != ih);
|
||||||
|
}
|
||||||
|
if (run_webservice)
|
||||||
|
{
|
||||||
TEKT_curl_ctx
|
TEKT_curl_ctx
|
||||||
= GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
|
= GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
|
||||||
&exchange_curl_rc);
|
&exchange_curl_rc);
|
||||||
@ -1060,8 +1144,6 @@ run (void *cls,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEKT_curl_ctx);
|
exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEKT_curl_ctx);
|
||||||
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
|
|
||||||
NULL);
|
|
||||||
fh = TALER_MHD_bind (TEKT_cfg,
|
fh = TALER_MHD_bind (TEKT_cfg,
|
||||||
"exchange",
|
"exchange",
|
||||||
&serve_port);
|
&serve_port);
|
||||||
@ -1094,9 +1176,9 @@ run (void *cls,
|
|||||||
GNUNET_SCHEDULER_shutdown ();
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
global_ret = EXIT_SUCCESS;
|
|
||||||
TALER_MHD_daemon_start (mhd);
|
TALER_MHD_daemon_start (mhd);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1113,11 +1195,43 @@ main (int argc,
|
|||||||
const struct GNUNET_GETOPT_CommandLineOption options[] = {
|
const struct GNUNET_GETOPT_CommandLineOption options[] = {
|
||||||
GNUNET_GETOPT_option_help (
|
GNUNET_GETOPT_option_help (
|
||||||
"tool to test KYC provider integrations"),
|
"tool to test KYC provider integrations"),
|
||||||
|
GNUNET_GETOPT_option_flag (
|
||||||
|
'P',
|
||||||
|
"print-payto-hash",
|
||||||
|
"output the hash of the payto://-URI",
|
||||||
|
&print_h_payto),
|
||||||
|
GNUNET_GETOPT_option_uint (
|
||||||
|
'r',
|
||||||
|
"rowid",
|
||||||
|
"NUMBER",
|
||||||
|
"override row ID to use in simulation (default: 42)",
|
||||||
|
&kyc_row_id),
|
||||||
|
GNUNET_GETOPT_option_flag (
|
||||||
|
'w',
|
||||||
|
"run-webservice",
|
||||||
|
"run the integrated HTTP service",
|
||||||
|
&run_webservice),
|
||||||
|
GNUNET_GETOPT_option_string (
|
||||||
|
'i',
|
||||||
|
"initiate",
|
||||||
|
"SECTION_NAME",
|
||||||
|
"initiate KYC check using provider configured in SECTION_NAME of the configuration",
|
||||||
|
&initiate_section),
|
||||||
|
GNUNET_GETOPT_option_base32_fixed_size (
|
||||||
|
'p',
|
||||||
|
"payto-hash",
|
||||||
|
"URI",
|
||||||
|
"base32 encoding of the hash of a payto://-URI to use for the account (otherwise a random value will be used)",
|
||||||
|
&cmd_line_h_payto,
|
||||||
|
sizeof (cmd_line_h_payto)),
|
||||||
GNUNET_GETOPT_OPTION_END
|
GNUNET_GETOPT_OPTION_END
|
||||||
};
|
};
|
||||||
enum GNUNET_GenericReturnValue ret;
|
enum GNUNET_GenericReturnValue ret;
|
||||||
|
|
||||||
TALER_OS_init ();
|
TALER_OS_init ();
|
||||||
|
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||||
|
&cmd_line_h_payto,
|
||||||
|
sizeof (cmd_line_h_payto));
|
||||||
ret = GNUNET_PROGRAM_run (argc, argv,
|
ret = GNUNET_PROGRAM_run (argc, argv,
|
||||||
"taler-exchange-kyc-tester",
|
"taler-exchange-kyc-tester",
|
||||||
"tool to test KYC provider integrations",
|
"tool to test KYC provider integrations",
|
||||||
|
Loading…
Reference in New Issue
Block a user