diff options
author | Christian Grothoff <christian@grothoff.org> | 2024-09-08 23:13:54 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2024-09-08 23:13:54 +0200 |
commit | 735574522eddea9f5c94f4c4922674d8fa9ba88d (patch) | |
tree | 895489af289f806c3c8347ceb661c5e42b31d4a4 | |
parent | 4aa9f87fe66d02c65c652e1de4c25d93e5368d56 (diff) |
fix /kyc-check long-polling logic
-rw-r--r-- | src/exchange/taler-exchange-httpd_kyc-check.c | 138 | ||||
-rw-r--r-- | src/include/taler_exchange_service.h | 6 | ||||
-rw-r--r-- | src/include/taler_testing_lib.h | 29 | ||||
-rw-r--r-- | src/include/taler_util.h | 32 | ||||
-rw-r--r-- | src/lib/Makefile.am | 2 | ||||
-rw-r--r-- | src/lib/exchange_api_kyc_check.c | 67 | ||||
-rw-r--r-- | src/lib/exchange_api_kyc_info.c | 4 | ||||
-rw-r--r-- | src/testing/test_exchange_api_age_restriction.c | 1 | ||||
-rw-r--r-- | src/testing/test_exchange_p2p.c | 2 | ||||
-rw-r--r-- | src/testing/test_kyc_api.c | 8 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_kyc_check_get.c | 37 |
11 files changed, 222 insertions, 104 deletions
diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c index 1b41c10f2..52e32a336 100644 --- a/src/exchange/taler-exchange-httpd_kyc-check.c +++ b/src/exchange/taler-exchange-httpd_kyc-check.c @@ -34,7 +34,6 @@ #include "taler-exchange-httpd_kyc-wallet.h" #include "taler-exchange-httpd_responses.h" - /** * Reserve GET request that is long-polling. */ @@ -78,15 +77,14 @@ struct KycPoller union TALER_AccountSignatureP account_sig; /** - * True if we are still suspended. + * What are we long-polling for (if anything)? */ - bool suspended; + enum TALER_EXCHANGE_KycLongPollTarget lpt; /** - * True if we are long polling for a KYC authorization - * wire transfer. + * True if we are still suspended. */ - bool await_auth; + bool suspended; }; @@ -198,6 +196,7 @@ TEH_handler_kyc_check ( struct TALER_AccountAccessTokenP access_token; bool aml_review; bool kyc_required; + bool access_ok = false; if (NULL == kyp) { @@ -230,13 +229,22 @@ TEH_handler_kyc_check ( TALER_MHD_parse_request_timeout (rc->connection, &kyp->timeout); { - enum TALER_EXCHANGE_YesNoAll yna; - - TALER_MHD_parse_request_yna (rc->connection, - "await_auth", - TALER_EXCHANGE_YNA_NO, - &yna); - kyp->await_auth = (TALER_EXCHANGE_YNA_YES == yna); + uint64_t num = 0; + int val; + + TALER_MHD_parse_request_number (rc->connection, + "lpt", + &num); + val = (int) num; + if ( (val < 0) || + (val > TALER_EXCHANGE_KLPT_MAX) ) + { + /* Protocol violation, but we can be graceful and + just ignore the long polling! */ + GNUNET_break_op (0); + val = TALER_EXCHANGE_KLPT_NONE; + } + kyp->lpt = (enum TALER_EXCHANGE_KycLongPollTarget) val; } /* long polling needed? */ if (GNUNET_TIME_absolute_is_future (kyp->timeout)) @@ -272,6 +280,7 @@ TEH_handler_kyc_check ( { enum GNUNET_DB_QueryStatus qs; + bool do_suspend; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Looking up KYC requirements for account %s\n", @@ -293,25 +302,72 @@ TEH_handler_kyc_check ( TALER_EC_GENERIC_DB_STORE_FAILED, "lookup_kyc_requirement_by_row"); } + do_suspend = false; if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { /* account unknown */ - if ( (kyp->await_auth) && + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Account unknown!\n"); + if ( (TALER_EXCHANGE_KLPT_NONE != kyp->lpt) && + (TALER_EXCHANGE_KLPT_KYC_OK != kyp->lpt) && (GNUNET_TIME_absolute_is_future (kyp->timeout)) ) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Suspending HTTP request on timeout (%s) now...\n", - GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_remaining ( - kyp->timeout), - true)); - GNUNET_assert (NULL != kyp->eh); - kyp->suspended = true; - GNUNET_CONTAINER_DLL_insert (kyp_head, - kyp_tail, - kyp); - MHD_suspend_connection (kyp->connection); - return MHD_YES; + do_suspend = true; + access_ok = true; /* for now */ + } + } + else + { + access_ok = + ( (! GNUNET_is_zero (&account_pub) && + (GNUNET_OK == + TALER_account_kyc_auth_verify (&account_pub, + &kyp->account_sig)) ) || + (! GNUNET_is_zero (&reserve_pub) && + (GNUNET_OK == + TALER_account_kyc_auth_verify (&reserve_pub, + &kyp->account_sig)) ) ); + + if (GNUNET_TIME_absolute_is_future (kyp->timeout)) + { + switch (kyp->lpt) + { + case TALER_EXCHANGE_KLPT_NONE: + break; + case TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER: + if (! access_ok) + do_suspend = true; + break; + case TALER_EXCHANGE_KLPT_INVESTIGATION_DONE: + if (! aml_review) + do_suspend = true; + break; + case TALER_EXCHANGE_KLPT_KYC_OK: + if (kyc_required) + do_suspend = true; + break; + } } + } + + if (do_suspend && access_ok) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Suspending HTTP request on timeout (%s) for %d\n", + GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_remaining ( + kyp->timeout), + true), + (int) kyp->lpt); + GNUNET_assert (NULL != kyp->eh); + kyp->suspended = true; + GNUNET_CONTAINER_DLL_insert (kyp_head, + kyp_tail, + kyp); + MHD_suspend_connection (kyp->connection); + return MHD_YES; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { return TALER_MHD_reply_with_error ( rc->connection, MHD_HTTP_NOT_FOUND, @@ -320,14 +376,7 @@ TEH_handler_kyc_check ( } } - if ( (GNUNET_is_zero (&account_pub) || - (GNUNET_OK != - TALER_account_kyc_auth_verify (&account_pub, - &kyp->account_sig)) ) && - (GNUNET_is_zero (&reserve_pub) || - (GNUNET_OK != - TALER_account_kyc_auth_verify (&reserve_pub, - &kyp->account_sig)) ) ) + if (! access_ok) { json_decref (jrules); jrules = NULL; @@ -364,31 +413,14 @@ TEH_handler_kyc_check ( json_decref (jrules); jrules = NULL; - if ( (kyc_required) && - (! kyp->await_auth) && - GNUNET_TIME_absolute_is_future (kyp->timeout)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Suspending HTTP request on timeout (%s) now...\n", - GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_remaining ( - kyp->timeout), - true)); - GNUNET_assert (NULL != kyp->eh); - kyp->suspended = true; - GNUNET_CONTAINER_DLL_insert (kyp_head, - kyp_tail, - kyp); - MHD_suspend_connection (kyp->connection); - return MHD_YES; - } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returning KYC %s\n", kyc_required ? "required" : "optional"); return TALER_MHD_REPLY_JSON_PACK ( rc->connection, kyc_required - ? MHD_HTTP_ACCEPTED - : MHD_HTTP_OK, + ? MHD_HTTP_ACCEPTED + : MHD_HTTP_OK, GNUNET_JSON_pack_bool ("aml_review", aml_review), GNUNET_JSON_pack_data_auto ("access_token", diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index c7d8c5c06..74bff838d 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -36,7 +36,7 @@ * Version of the Taler Exchange API, in hex. * Thus 0.8.4-1 = 0x00080401. */ -#define TALER_EXCHANGE_API_VERSION 0x00100003 +#define TALER_EXCHANGE_API_VERSION 0x00100004 /** * Information returned when a client needs to pass @@ -4430,7 +4430,8 @@ typedef void * @param url exchange base URL * @param h_payto hash of the account the KYC check is about * @param pk private key to authorize the request with - * @param timeout how long to wait for a positive KYC status + * @param lpt target for long polling + * @param timeout how long to wait for an answer, including possibly long polling for the desired @a lpt status * @param cb function to call with the result * @param cb_cls closure for @a cb * @return NULL on error @@ -4441,6 +4442,7 @@ TALER_EXCHANGE_kyc_check ( const char *url, const struct TALER_PaytoHashP *h_payto, const union TALER_AccountPrivateKeyP *pk, + enum TALER_EXCHANGE_KycLongPollTarget lpt, struct GNUNET_TIME_Relative timeout, TALER_EXCHANGE_KycStatusCallback cb, void *cb_cls); diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index b87b9d95b..98a74f39f 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -1658,11 +1658,12 @@ TALER_TESTING_cmd_track_transfer (const char *label, * @return the command */ struct TALER_TESTING_Command -TALER_TESTING_cmd_check_bank_transfer (const char *label, - const char *exchange_base_url, - const char *amount, - const char *debit_payto, - const char *credit_payto); +TALER_TESTING_cmd_check_bank_transfer ( + const char *label, + const char *exchange_base_url, + const char *amount, + const char *debit_payto, + const char *credit_payto); /** @@ -1677,11 +1678,12 @@ TALER_TESTING_cmd_check_bank_transfer (const char *label, * @return the command */ struct TALER_TESTING_Command -TALER_TESTING_cmd_check_bank_admin_transfer (const char *label, - const char *amount, - const char *debit_payto, - const char *credit_payto, - const char *reserve_pub_ref); +TALER_TESTING_cmd_check_bank_admin_transfer ( + const char *label, + const char *amount, + const char *debit_payto, + const char *credit_payto, + const char *reserve_pub_ref); /** @@ -1696,8 +1698,9 @@ TALER_TESTING_cmd_check_bank_admin_transfer (const char *label, * @return the command. */ struct TALER_TESTING_Command -TALER_TESTING_cmd_check_bank_transfer_with_ref (const char *label, - const char *deposit_reference); +TALER_TESTING_cmd_check_bank_transfer_with_ref ( + const char *label, + const char *deposit_reference); /** @@ -2209,6 +2212,7 @@ TALER_TESTING_cmd_wallet_kyc_get ( * @param label command label. * @param payment_target_reference command with a payment target to query * @param account_reference command with account private key to query + * @param lpt target for long polling * @param expected_response_code expected HTTP status * @return the command */ @@ -2217,6 +2221,7 @@ TALER_TESTING_cmd_check_kyc_get ( const char *label, const char *payment_target_reference, const char *account_reference, + enum TALER_EXCHANGE_KycLongPollTarget lpt, unsigned int expected_response_code); diff --git a/src/include/taler_util.h b/src/include/taler_util.h index 5dab47b0b..b599ade76 100644 --- a/src/include/taler_util.h +++ b/src/include/taler_util.h @@ -597,6 +597,38 @@ TALER_iban_validate (const char *iban); /** + * Possible choices for long-polling for the KYC status. + */ +enum TALER_EXCHANGE_KycLongPollTarget +{ + /** + * No long polling. + */ + TALER_EXCHANGE_KLPT_NONE = 0, + + /** + * Wait for KYC auth transfer to be complete. + */ + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER = 1, + + /** + * Wait for AML investigation to be complete. + */ + TALER_EXCHANGE_KLPT_INVESTIGATION_DONE = 2, + + /** + * Wait for KYC status to be OK. + */ + TALER_EXCHANGE_KLPT_KYC_OK = 3, + + /** + * Maximum legal value in this enumeration. + */ + TALER_EXCHANGE_KLPT_MAX = 3 +}; + + +/** * Possible values for a binary filter. */ enum TALER_EXCHANGE_YesNoAll diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index d1fc1c9d1..a3a1a5fc5 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -18,7 +18,7 @@ lib_LTLIBRARIES = \ libtalerexchange.la libtalerexchange_la_LDFLAGS = \ - -version-info 11:0:1 \ + -version-info 12:0:0 \ -no-undefined libtalerexchange_la_SOURCES = \ exchange_api_add_aml_decision.c \ diff --git a/src/lib/exchange_api_kyc_check.c b/src/lib/exchange_api_kyc_check.c index 006419332..1231c7cc1 100644 --- a/src/lib/exchange_api_kyc_check.c +++ b/src/lib/exchange_api_kyc_check.c @@ -258,6 +258,7 @@ TALER_EXCHANGE_kyc_check ( const char *url, const struct TALER_PaytoHashP *h_payto, const union TALER_AccountPrivateKeyP *account_priv, + enum TALER_EXCHANGE_KycLongPollTarget lpt, struct GNUNET_TIME_Relative timeout, TALER_EXCHANGE_KycStatusCallback cb, void *cb_cls) @@ -265,32 +266,49 @@ TALER_EXCHANGE_kyc_check ( struct TALER_EXCHANGE_KycCheckHandle *kch; CURL *eh; char arg_str[128]; + char timeout_ms[32]; + char lpt_str[32]; struct curl_slist *job_headers = NULL; unsigned long long tms; - char *hps; - hps = GNUNET_STRINGS_data_to_string_alloc (h_payto, - sizeof (*h_payto)); - tms = timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; - if (0 != tms) - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "kyc-check/%s?timeout_ms=%llu", - hps, - tms); - else + { + char *hps; + + hps = GNUNET_STRINGS_data_to_string_alloc ( + h_payto, + sizeof (*h_payto)); GNUNET_snprintf (arg_str, sizeof (arg_str), "kyc-check/%s", hps); - GNUNET_free (hps); + GNUNET_free (hps); + } + tms = timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; + GNUNET_snprintf (timeout_ms, + sizeof (timeout_ms), + "%llu", + tms); + GNUNET_snprintf (lpt_str, + sizeof (lpt_str), + "%d", + (int) lpt); kch = GNUNET_new (struct TALER_EXCHANGE_KycCheckHandle); kch->cb = cb; kch->cb_cls = cb_cls; - kch->url = TALER_url_join (url, - arg_str, - NULL); + kch->url + = TALER_url_join ( + url, + arg_str, + "timeout_ms", + GNUNET_TIME_relative_is_zero (timeout) + ? NULL + : timeout_ms, + "lpt", + TALER_EXCHANGE_KLPT_NONE == lpt + ? NULL + : lpt_str, + NULL); if (NULL == kch->url) { GNUNET_free (kch); @@ -311,8 +329,10 @@ TALER_EXCHANGE_kyc_check ( CURLOPT_TIMEOUT_MS, (long) (tms + 500L))); } - job_headers = curl_slist_append (job_headers, - "Content-Type: application/json"); + job_headers + = curl_slist_append ( + job_headers, + "Content-Type: application/json"); { union TALER_AccountSignatureP account_sig; char *sig_hdr; @@ -339,11 +359,12 @@ TALER_EXCHANGE_kyc_check ( return NULL; } } - kch->job = GNUNET_CURL_job_add2 (ctx, - eh, - job_headers, - &handle_kyc_check_finished, - kch); + kch->job + = GNUNET_CURL_job_add2 (ctx, + eh, + job_headers, + &handle_kyc_check_finished, + kch); curl_slist_free_all (job_headers); return kch; } diff --git a/src/lib/exchange_api_kyc_info.c b/src/lib/exchange_api_kyc_info.c index dc5823a33..073b5f4ba 100644 --- a/src/lib/exchange_api_kyc_info.c +++ b/src/lib/exchange_api_kyc_info.c @@ -245,6 +245,10 @@ handle_kyc_info_finished (void *cls, /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ break; + case MHD_HTTP_FORBIDDEN: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + break; case MHD_HTTP_NOT_FOUND: lr.hr.ec = TALER_JSON_get_error_code (j); lr.hr.hint = TALER_JSON_get_error_hint (j); diff --git a/src/testing/test_exchange_api_age_restriction.c b/src/testing/test_exchange_api_age_restriction.c index 4d42ac6a2..38e536d52 100644 --- a/src/testing/test_exchange_api_age_restriction.c +++ b/src/testing/test_exchange_api_age_restriction.c @@ -301,6 +301,7 @@ run (void *cls, "check-kyc-withdraw", "withdraw-coin-1-lacking-kyc", "setup-account-key", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info", diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c index 8fae0864e..2a21bb723 100644 --- a/src/testing/test_exchange_p2p.c +++ b/src/testing/test_exchange_p2p.c @@ -499,6 +499,7 @@ run (void *cls, "check-kyc-close-pending", "reserve-101-close-kyc", "setup-account-key", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info", @@ -519,6 +520,7 @@ run (void *cls, "check-kyc-close-ok", "reserve-101-close-kyc", "setup-account-key", + TALER_EXCHANGE_KLPT_KYC_OK, MHD_HTTP_OK), /* Now it should pass */ TALER_TESTING_cmd_reserve_close ( diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c index 3af972569..617f2ed2d 100644 --- a/src/testing/test_kyc_api.c +++ b/src/testing/test_kyc_api.c @@ -144,6 +144,7 @@ run (void *cls, "check-kyc-withdraw", "withdraw-coin-1-lacking-kyc", "setup-account-key-withdraw", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info-withdraw", @@ -226,6 +227,7 @@ run (void *cls, "check-kyc-deposit", "track-deposit-kyc-ready", "setup-account-key-deposit", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info-deposit", @@ -256,6 +258,7 @@ run (void *cls, "check-kyc-deposit-again", "track-deposit-kyc-ready", "setup-account-key-deposit", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info-deposit-again", @@ -295,6 +298,7 @@ run (void *cls, "check-kyc-wallet", "wallet-kyc-fail", "wallet-kyc-fail", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info-kyc-wallet", @@ -315,6 +319,7 @@ run (void *cls, "wallet-kyc-check", "wallet-kyc-fail", "wallet-kyc-fail", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_OK), TALER_TESTING_cmd_reserve_get_attestable ( "wallet-get-attestable", @@ -429,6 +434,7 @@ run (void *cls, "check-kyc-purse-merge", "purse-merge-into-reserve", "p2p_create-reserve-1", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info-purse-merge-into-reserve", @@ -482,6 +488,7 @@ run (void *cls, "check-kyc-purse-create", "purse-create-with-reserve", "purse-create-with-reserve", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info-purse-create", @@ -722,6 +729,7 @@ run (void *cls, "check-kyc-form", "wallet-trigger-kyc-for-form-aml", "wallet-trigger-kyc-for-form-aml", + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, MHD_HTTP_ACCEPTED), TALER_TESTING_cmd_get_kyc_info ( "get-kyc-info-form", diff --git a/src/testing/testing_api_cmd_kyc_check_get.c b/src/testing/testing_api_cmd_kyc_check_get.c index 7d0ce06b1..fa0556d47 100644 --- a/src/testing/testing_api_cmd_kyc_check_get.c +++ b/src/testing/testing_api_cmd_kyc_check_get.c @@ -34,6 +34,22 @@ struct KycCheckGetState { /** + * Set to the KYC URL *if* the exchange replied with + * a request for KYC (#MHD_HTTP_ACCEPTED or #MHD_HTTP_OK). + */ + struct TALER_AccountAccessTokenP access_token; + + /** + * Handle to the "track transaction" pending operation. + */ + struct TALER_EXCHANGE_KycCheckHandle *kwh; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** * Command to get a reserve private key from. */ const char *payment_target_reference; @@ -49,20 +65,10 @@ struct KycCheckGetState unsigned int expected_response_code; /** - * Set to the KYC URL *if* the exchange replied with - * a request for KYC (#MHD_HTTP_ACCEPTED or #MHD_HTTP_OK). - */ - struct TALER_AccountAccessTokenP access_token; - - /** - * Handle to the "track transaction" pending operation. + * What are we waiting for when long-polling? */ - struct TALER_EXCHANGE_KycCheckHandle *kwh; + enum TALER_EXCHANGE_KycLongPollTarget lpt; - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; }; @@ -171,7 +177,10 @@ check_kyc_run (void *cls, TALER_TESTING_get_exchange_url (is), h_payto, account_priv, - GNUNET_TIME_UNIT_ZERO, + kcg->lpt, + TALER_EXCHANGE_KLPT_NONE == kcg->lpt + ? GNUNET_TIME_UNIT_ZERO + : GNUNET_TIME_UNIT_MINUTES, &check_kyc_cb, kcg); GNUNET_assert (NULL != kcg->kwh); @@ -235,6 +244,7 @@ TALER_TESTING_cmd_check_kyc_get ( const char *label, const char *payment_target_reference, const char *account_reference, + enum TALER_EXCHANGE_KycLongPollTarget lpt, unsigned int expected_response_code) { struct KycCheckGetState *kcg; @@ -243,6 +253,7 @@ TALER_TESTING_cmd_check_kyc_get ( kcg->payment_target_reference = payment_target_reference; kcg->account_reference = account_reference; kcg->expected_response_code = expected_response_code; + kcg->lpt = lpt; { struct TALER_TESTING_Command cmd = { .cls = kcg, |