commit a075795020d665fd12cf99e96f08969e6df9406a
parent 534edf2d0fc0e2a03736b9251703bad2ffaf14f7
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 18 May 2022 18:34:20 +0200
Merge branch 'master' of git+ssh://git.taler.net/merchant
Diffstat:
11 files changed, 162 insertions(+), 111 deletions(-)
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
@@ -1186,6 +1186,7 @@ url_handler (void *cls,
TMH_compute_auth (TMH_default_auth,
&hc->instance->auth.auth_salt,
&hc->instance->auth.auth_hash);
+ hc->instance->auth_override = true;
GNUNET_free (TMH_default_auth);
}
}
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
@@ -163,6 +163,13 @@ struct TMH_MerchantInstance
* True if this instance was deleted (but not yet purged).
*/
bool deleted;
+
+ /**
+ * The authentication settings for this instance
+ * were changed via the command-line. Do not check
+ * against the DB value when updating the auth token.
+ */
+ bool auth_override;
};
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c
@@ -125,6 +125,12 @@ struct GetOrderData
struct TALER_Amount refund_amount;
/**
+ * Total refunds already collected.
+ * if @e refunded is set to true.
+ */
+ struct TALER_Amount refund_taken;
+
+ /**
* Return code: #TALER_EC_NONE if successful.
*/
enum TALER_ErrorCode ec;
@@ -154,8 +160,9 @@ struct GetOrderData
/**
* Set to true if a refund is still available for the
* wallet for this payment.
+ * @deprecated: true if refund_taken < refund_amount
*/
- bool refund_available;
+ bool refund_pending;
/**
* Set to true if the client requested HTML, otherwise we generate JSON.
@@ -756,16 +763,18 @@ process_refunds_cb (void *cls,
TALER_amount2s (refund_amount),
TALER_B2S (coin_pub),
reason);
- god->refund_available |= pending;
- if (god->refunded)
- {
+ god->refund_pending |= pending;
+ if (!pending)
+ {
GNUNET_assert (0 <=
- TALER_amount_add (&god->refund_amount,
- &god->refund_amount,
- refund_amount));
- return;
+ TALER_amount_add (&god->refund_taken,
+ &god->refund_taken,
+ refund_amount));
}
- god->refund_amount = *refund_amount;
+ GNUNET_assert (0 <=
+ TALER_amount_add (&god->refund_amount,
+ &god->refund_amount,
+ refund_amount));
god->refunded = true;
}
@@ -1052,33 +1061,53 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
/* Check if client provided the right hash code of the contract terms */
if (NULL != god->contract_terms)
{
- struct TALER_PrivateContractHashP h;
-
contract_available = true;
- if (GNUNET_OK !=
- TALER_JSON_contract_hash (god->contract_terms,
- &h))
+
+ if (GNUNET_YES == GNUNET_is_zero (&god->h_contract_terms))
{
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
- "contract terms");
+
+ if (GNUNET_OK !=
+ TALER_JSON_contract_hash (god->contract_terms,
+ &god->h_contract_terms))
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
+ "contract terms");
+ }
+
}
- contract_match = (0 ==
- GNUNET_memcmp (&h,
- &god->h_contract_terms));
- if ( (GNUNET_NO ==
- GNUNET_is_zero (&god->h_contract_terms)) &&
- (! contract_match) )
+ else
{
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER,
- NULL);
+
+ struct TALER_PrivateContractHashP h;
+
+ if (GNUNET_OK !=
+ TALER_JSON_contract_hash (god->contract_terms,
+ &h))
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
+ "contract terms");
+ }
+ contract_match = (0 ==
+ GNUNET_memcmp (&h,
+ &god->h_contract_terms));
+ if ( !contract_match )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER,
+ NULL);
+ }
+
}
+
}
if (contract_available)
@@ -1341,6 +1370,9 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (TMH_currency,
&god->refund_amount));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (TMH_currency,
+ &god->refund_taken));
qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
hc->instance->settings.id,
&god->h_contract_terms,
@@ -1360,7 +1392,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
(1 != TALER_amount_cmp (&god->refund_amount,
&god->sc.refund_expected)) )) ||
( (god->sc.awaiting_refund_obtained) &&
- (god->refund_available) ) )
+ (god->refund_pending) ) )
{
/* Client is waiting for a refund larger than what we have, suspend
until timeout */
@@ -1387,7 +1419,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
{
enum GNUNET_GenericReturnValue res;
- if (god->refund_available)
+ if (god->refund_pending)
{
char *qr;
char *uri;
@@ -1421,6 +1453,8 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
get_order_summary (god)),
TALER_JSON_pack_amount ("refund_amount",
&god->refund_amount),
+ TALER_JSON_pack_amount ("refund_taken",
+ &god->refund_taken),
GNUNET_JSON_pack_string ("taler_refund_uri",
uri),
GNUNET_JSON_pack_string ("taler_refund_qrcode_svg",
@@ -1446,7 +1480,9 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
GNUNET_JSON_pack_string ("order_summary",
get_order_summary (god)),
TALER_JSON_pack_amount ("refund_amount",
- &god->refund_amount));
+ &god->refund_amount),
+ TALER_JSON_pack_amount ("refund_taken",
+ &god->refund_taken));
res = TMH_return_from_template (god->sc.con,
MHD_HTTP_OK,
"show_order_details",
@@ -1471,7 +1507,9 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
GNUNET_JSON_pack_bool ("refunded",
god->refunded),
GNUNET_JSON_pack_bool ("refund_pending",
- god->refund_available),
+ god->refund_pending),
+ TALER_JSON_pack_amount ("refund_taken",
+ &god->refund_taken),
TALER_JSON_pack_amount ("refund_amount",
&god->refund_amount));
}
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-claim.c b/src/backend/taler-merchant-httpd_post-orders-ID-claim.c
@@ -265,11 +265,9 @@ TMH_post_orders_ID_claim (const struct TMH_RequestHandler *rh,
/* create contract signature */
{
+ struct TALER_PrivateContractHashP hash;
struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
- struct TALER_ProposalDataPS pdps = {
- .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT),
- .purpose.size = htonl (sizeof (pdps))
- };
+
/**
* Hash of the JSON contract in UTF-8 including 0-termination,
* using JSON_COMPACT | JSON_SORT_KEYS
@@ -277,7 +275,7 @@ TMH_post_orders_ID_claim (const struct TMH_RequestHandler *rh,
if (GNUNET_OK !=
TALER_JSON_contract_hash (contract_terms,
- &pdps.hash))
+ &hash))
{
GNUNET_break (0);
json_decref (contract_terms);
@@ -287,9 +285,9 @@ TMH_post_orders_ID_claim (const struct TMH_RequestHandler *rh,
NULL);
}
- GNUNET_CRYPTO_eddsa_sign (&hc->instance->merchant_priv.eddsa_priv,
- &pdps,
- &merchant_sig);
+ TALER_merchant_contract_sign (&hash,
+ &hc->instance->merchant_priv,
+ &merchant_sig);
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_OK,
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-paid.c b/src/backend/taler-merchant-httpd_post-orders-ID-paid.c
@@ -67,13 +67,10 @@ TMH_post_orders_ID_paid (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
- struct TALER_PaymentResponsePS pr = {
- .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK),
- .purpose.size = htonl (sizeof (pr))
- };
const char *order_id = hc->infix;
struct TALER_MerchantSignatureP merchant_sig;
const char *session_id;
+ struct TALER_PrivateContractHashP hct;
json_t *contract_terms;
const char *fulfillment_url;
enum GNUNET_DB_QueryStatus qs;
@@ -83,7 +80,7 @@ TMH_post_orders_ID_paid (const struct TMH_RequestHandler *rh,
GNUNET_JSON_spec_fixed_auto ("sig",
&merchant_sig),
GNUNET_JSON_spec_fixed_auto ("h_contract",
- &pr.h_contract_terms),
+ &hct),
GNUNET_JSON_spec_string ("session_id",
&session_id),
GNUNET_JSON_spec_end ()
@@ -104,10 +101,9 @@ TMH_post_orders_ID_paid (const struct TMH_RequestHandler *rh,
if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK,
- &pr,
- &merchant_sig.eddsa_sig,
- &hc->instance->merchant_pub.eddsa_pub))
+ TALER_merchant_pay_verify (&hct,
+ &hc->instance->merchant_pub,
+ &merchant_sig))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
@@ -164,7 +160,7 @@ TMH_post_orders_ID_paid (const struct TMH_RequestHandler *rh,
TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
NULL);
}
- if (0 != GNUNET_memcmp (&pr.h_contract_terms,
+ if (0 != GNUNET_memcmp (&hct,
&h_contract_terms))
{
json_decref (contract_terms);
@@ -186,7 +182,7 @@ TMH_post_orders_ID_paid (const struct TMH_RequestHandler *rh,
fulfillment_url);
qs = TMH_db->mark_contract_paid (TMH_db->cls,
hc->instance->settings.id,
- &pr.h_contract_terms,
+ &hct,
session_id);
/* If the order was paid already, we get qs == 0. */
if (0 > qs)
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -124,9 +124,10 @@ struct DepositConfirmation
/**
* If a minimum age was required (i. e. pc->minimum_age is large enough),
* this is the signature of the minimum age (as a single uint8_t), using the
- * private key to the corresponding age group. Might be NULL.
+ * private key to the corresponding age group. Might be all zeroes for no
+ * age attestation.
*/
- struct TALER_AgeAttestation *minimum_age_sig;
+ struct TALER_AgeAttestation minimum_age_sig;
/* If a minimum age was required (i. e. pc->minimum_age is large enought),
* this is the age commitment (i. e. age mask and vector of EdDSA public
@@ -1193,7 +1194,7 @@ process_pay_with_exchange (void *cls,
}
dc->age_commitment->mask = denom_details->key.age_mask;
- if (dc->age_commitment->num !=
+ if ((dc->age_commitment->num + 1) !=
__builtin_popcount (dc->age_commitment->mask.bits))
{
code =
@@ -1205,7 +1206,7 @@ process_pay_with_exchange (void *cls,
TALER_age_commitment_verify (
dc->age_commitment,
pc->minimum_age,
- dc->minimum_age_sig))
+ &dc->minimum_age_sig))
code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_VERIFICATION_FAILED;
AGE_FAIL:
@@ -1927,17 +1928,9 @@ execute_pay_transaction (struct PayContext *pc)
/* Sign on our end (as the payment did go through, even if it may
have been refunded already) */
- {
- struct TALER_PaymentResponsePS mr = {
- .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK),
- .purpose.size = htonl (sizeof (mr)),
- .h_contract_terms = pc->h_contract_terms
- };
-
- GNUNET_CRYPTO_eddsa_sign (&pc->hc->instance->merchant_priv.eddsa_priv,
- &mr,
- &sig);
- }
+ TALER_merchant_pay_sign (&pc->h_contract_terms,
+ &pc->hc->instance->merchant_priv,
+ &sig);
/* Build the response */
resume_pay_with_response (
@@ -2042,7 +2035,6 @@ parse_pay (struct MHD_Connection *connection,
{
struct DepositConfirmation *dc = &pc->dc[coins_index];
const char *exchange_url;
- struct TALER_AgeAttestation minimum_age_sig = {0};
json_t *age_commitment = NULL;
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_sig",
@@ -2060,7 +2052,7 @@ parse_pay (struct MHD_Connection *connection,
&exchange_url),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("minimum_age_sig",
- &minimum_age_sig),
+ &dc->minimum_age_sig),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("age_commitment",
@@ -2092,8 +2084,8 @@ parse_pay (struct MHD_Connection *connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"duplicate coin in list"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
}
@@ -2107,26 +2099,32 @@ parse_pay (struct MHD_Connection *connection,
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
+ return (MHD_YES == TALER_MHD_reply_with_error (connection,
MHD_HTTP_CONFLICT,
TALER_EC_GENERIC_CURRENCY_MISMATCH,
- TMH_currency);
+ TMH_currency))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
{
bool has_commitment = (NULL != age_commitment) &&
json_is_array (age_commitment);
- bool has_sig = ! GNUNET_is_zero_ (&minimum_age_sig,
- sizeof(minimum_age_sig));
+ bool has_sig = ! GNUNET_is_zero_ (&dc->minimum_age_sig,
+ sizeof(dc->minimum_age_sig));
if (has_sig != has_commitment)
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "inconsistency: 'mininum_age_sig' vs. 'age_commitment'");
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "inconsistency: 'mininum_age_sig' vs. 'age_commitment'")
+ )
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
/* Parse the AgeCommitment, i. e. the public keys */
@@ -2138,14 +2136,18 @@ parse_pay (struct MHD_Connection *connection,
/* Sanity check the amount of AgeCommitment's public keys. The
* actual check will be performed once we now the denominations. */
- if (32 >= num)
+ if (32 <= num)
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "'age_commitment' too large");
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "'age_commitment' too large"
+ ))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
dc->age_commitment = GNUNET_new (struct TALER_AgeCommitment);
@@ -2172,10 +2174,14 @@ parse_pay (struct MHD_Connection *connection,
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "age_commitment");
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "age_commitment"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
}
}
@@ -2288,8 +2294,8 @@ parse_pay (struct MHD_Connection *connection,
GNUNET_JSON_spec_fixed_auto ("h_wire",
&pc->h_wire),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_fixed_auto ("minimum_age",
- &pc->minimum_age),
+ GNUNET_JSON_spec_uint32 ("minimum_age",
+ &pc->minimum_age),
NULL),
GNUNET_JSON_spec_end ()
};
@@ -2451,6 +2457,7 @@ TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
= GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->coins_cnt),
&handle_pay_timeout,
pc);
+ GNUNET_assert (NULL != pc->wm);
execute_pay_transaction (pc);
return MHD_YES;
}
diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
@@ -776,6 +776,8 @@ process_refunds_cb (void *cls,
GNUNET_JSON_PACK (
TALER_JSON_pack_amount ("amount",
refund_amount),
+ GNUNET_JSON_pack_bool ("pending",
+ pending),
GNUNET_JSON_pack_timestamp ("timestamp",
timestamp),
GNUNET_JSON_pack_string ("reason",
@@ -801,7 +803,7 @@ process_refunds_cb (void *cls,
&gorc->refund_amount,
refund_amount));
gorc->refunded = true;
- gorc->refund_pending = pending;
+ gorc->refund_pending |= pending;
}
diff --git a/src/backend/taler-merchant-httpd_private-get-orders.c b/src/backend/taler-merchant-httpd_private-get-orders.c
@@ -323,7 +323,8 @@ add_order (void *cls,
&contract_terms,
&os,
NULL);
- GNUNET_break (os == order_serial);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ GNUNET_break (os == order_serial);
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
diff --git a/src/backend/taler-merchant-httpd_private-post-instances-ID-auth.c b/src/backend/taler-merchant-httpd_private-post-instances-ID-auth.c
@@ -102,6 +102,7 @@ post_instances_ID_auth (struct TMH_MerchantInstance *mi,
to the authentication. */
{
struct TALER_MERCHANTDB_InstanceAuthSettings db_ias;
+
qs = TMH_db->lookup_instance_auth (TMH_db->cls,
mi->settings.id,
&db_ias);
@@ -112,8 +113,8 @@ post_instances_ID_auth (struct TMH_MerchantInstance *mi,
/* Instance got purged. */
TMH_db->rollback (TMH_db->cls);
return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_UNAUTHORIZED,
- TALER_EC_GENERIC_DB_COMMIT_FAILED,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
NULL);
case GNUNET_DB_STATUS_SOFT_ERROR:
TMH_db->rollback (TMH_db->cls);
@@ -129,12 +130,16 @@ post_instances_ID_auth (struct TMH_MerchantInstance *mi,
break;
}
- if (GNUNET_OK !=
- TMH_check_auth (hc->auth_token,
- &db_ias.auth_salt,
- &db_ias.auth_hash))
+ if ( (NULL == TMH_default_auth) &&
+ (! mi->auth_override) &&
+ (GNUNET_OK !=
+ TMH_check_auth (hc->auth_token,
+ &db_ias.auth_salt,
+ &db_ias.auth_hash)) )
{
TMH_db->rollback (TMH_db->cls);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Refusing auth change: old token does not match\n");
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_UNAUTHORIZED,
TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
@@ -175,6 +180,7 @@ retry:
/* Finally, also update our running process */
mi->auth = ias;
}
+ mi->auth_override = false;
if (0 == strcmp (mi->settings.id,
"default"))
{
diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c
@@ -265,11 +265,6 @@ handle_pay_finished (void *cls,
if (oph->am_wallet)
{
/* Here we can (and should) verify the merchant's signature */
- struct TALER_PaymentResponsePS pr = {
- .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK),
- .purpose.size = htonl (sizeof (pr)),
- .h_contract_terms = oph->h_contract_terms
- };
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("sig",
&merchant_sig),
@@ -289,10 +284,9 @@ handle_pay_finished (void *cls,
}
if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK,
- &pr,
- &merchant_sig.eddsa_sig,
- &oph->merchant_pub.eddsa_pub))
+ TALER_merchant_pay_verify (&oph->h_contract_terms,
+ &oph->merchant_pub,
+ &merchant_sig))
{
GNUNET_break_op (0);
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
diff --git a/src/testing/test_kyc_api.conf b/src/testing/test_kyc_api.conf
@@ -113,7 +113,8 @@ KYC_WITHDRAW_LIMIT = EUR:20
[exchange-kyc-oauth2]
-KYC_OAUTH2_URL = http://localhost:6666/oauth/v2/login
+KYC_OAUTH2_AUTH_URL = http://localhost:6666/oauth/v2/token
+KYC_OAUTH2_LOGIN_URL = http://localhost:6666/oauth/v2/login
KYC_INFO_URL = http://localhost:6666/api/user/me
KYC_OAUTH2_CLIENT_ID = taler-exchange
KYC_OAUTH2_CLIENT_SECRET = exchange-secret