work on kycaid attribute extraction

This commit is contained in:
Christian Grothoff 2023-01-27 17:42:56 +01:00
parent 59716ffdc4
commit 85e44ceea6
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
4 changed files with 237 additions and 32 deletions

View File

@ -291,7 +291,9 @@ libtalerexchangedb_la_LDFLAGS = \
check_PROGRAMS = \ check_PROGRAMS = \
test-exchangedb-postgres \ test-exchangedb-postgres
noinst_PROGRAMS = \
bench-db-postgres\ bench-db-postgres\
perf-exchangedb-reserves-in-insert-postgres\ perf-exchangedb-reserves-in-insert-postgres\
test-exchangedb-by-j-postgres\ test-exchangedb-by-j-postgres\

View File

@ -34,6 +34,17 @@ extern "C" {
#endif #endif
#endif #endif
/**
* Legal name of the business/company.
*/
#define TALER_ATTRIBUTE_COMPANY_NAME "company_name"
/**
* Legal country of registration of the business/company,
* 2-letter country code using ISO 3166-2.
*/
#define TALER_ATTRIBUTE_REGISTRATION_COUNTRY "registration_country"
/** /**
* Full name, when known/possible using "Lastname, Firstname(s)" format, * Full name, when known/possible using "Lastname, Firstname(s)" format,
* but "Firstname(s) Lastname" or "Firstname M. Lastname" should also be * but "Firstname(s) Lastname" or "Firstname M. Lastname" should also be
@ -43,6 +54,22 @@ extern "C" {
*/ */
#define TALER_ATTRIBUTE_FULL_NAME "full_name" #define TALER_ATTRIBUTE_FULL_NAME "full_name"
/**
* True/false indicator if the individual is a politically
* exposed person.
*/
#define TALER_ATTRIBUTE_PEP "pep"
/**
* Phone number (of business or individual).
*/
#define TALER_ATTRIBUTE_PHONE "phone"
/**
* Email address (of business or individual).
*/
#define TALER_ATTRIBUTE_EMAIL "email"
/** /**
* Birthdate of the person, as far as known. YYYY-MM-DD, a value * Birthdate of the person, as far as known. YYYY-MM-DD, a value
* of 0 (for DD, MM or even YYYY) is to be used for 'unknown' * of 0 (for DD, MM or even YYYY) is to be used for 'unknown'
@ -63,6 +90,15 @@ extern "C" {
*/ */
#define TALER_ATTRIBUTE_NATIONALITIES "nationalities" #define TALER_ATTRIBUTE_NATIONALITIES "nationalities"
/**
* Residence countries(s) of the person using 2-letter country codes ("US",
* "DE", "FR", "IT", etc.) separated by commas if multiple residences are
* confirmed ("EN,US,DE"). Note that in the latter case it is not guaranteed
* that all residences were necessarily recorded. Empty string for
* international nomads. NULL for not collected.
*/
#define TALER_ATTRIBUTE_RESIDENCES "residences"
#if 0 /* keep Emacsens' auto-indent happy */ #if 0 /* keep Emacsens' auto-indent happy */
{ {

View File

@ -77,8 +77,6 @@ enum TALER_KYCLOGIC_KycStatus
* The provider is still checking. * The provider is still checking.
*/ */
TALER_KYCLOGIC_STATUS_PROVIDER_PENDING TALER_KYCLOGIC_STATUS_PROVIDER_PENDING
= TALER_KYCLOGIC_STATUS_PROVIDER = TALER_KYCLOGIC_STATUS_PROVIDER
| TALER_KYCLOGIC_STATUS_PENDING, | TALER_KYCLOGIC_STATUS_PENDING,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of GNU Taler This file is part of GNU Taler
Copyright (C) 2022 Taler Systems SA Copyright (C) 2022, 2023 Taler Systems SA
Taler is free software; you can redistribute it and/or modify it under the Taler 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 terms of the GNU Affero General Public License as published by the Free Software
@ -19,6 +19,8 @@
* @author Christian Grothoff * @author Christian Grothoff
*/ */
#include "platform.h" #include "platform.h"
#include "taler_attributes.h"
#include "taler_kyclogic_lib.h"
#include "taler_kyclogic_plugin.h" #include "taler_kyclogic_plugin.h"
#include "taler_mhd_lib.h" #include "taler_mhd_lib.h"
#include "taler_curl_lib.h" #include "taler_curl_lib.h"
@ -749,7 +751,7 @@ log_failure (json_t *verifications)
/** /**
* Function called when we're done processing the * Function called when we're done processing the
* HTTP "/verifications/{verification_id}" request. * HTTP "/applicants/{verification_id}" request.
* *
* @param cls the `struct TALER_KYCLOGIC_WebhookHandle` * @param cls the `struct TALER_KYCLOGIC_WebhookHandle`
* @param response_code HTTP response code, 0 on error * @param response_code HTTP response code, 0 on error
@ -769,30 +771,116 @@ handle_webhook_finished (void *cls,
{ {
case MHD_HTTP_OK: case MHD_HTTP_OK:
{ {
const char *applicant_id; const char *type;
const char *verification_id; const char *profile_status;
const char *status; const char *first_name = NULL;
bool verified; const char *last_name = NULL;
json_t *verifications; const char *middle_name = NULL;
const char *dob = NULL;
const char *residence_country = NULL;
const char *gender = NULL;
bool pep = false;
bool no_pep = false;
const char *company_name = NULL;
const char *business_activity_id = NULL;
const char *registration_country = NULL;
const char *email = NULL;
const char *phone = NULL;
json_t *addresses = NULL;
json_t *documents = NULL;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("applicant_id", GNUNET_JSON_spec_string ("type",
&applicant_id), &type),
GNUNET_JSON_spec_string ("verification_id", GNUNET_JSON_spec_string ("profile_status",
&verification_id), &profile_status), /* valid, invalid, pending */
GNUNET_JSON_spec_string ("status", GNUNET_JSON_spec_mark_optional (
&status), /* completed, pending, ... */ GNUNET_JSON_spec_string ("email",
GNUNET_JSON_spec_bool ("verified", &email),
&verified), NULL),
GNUNET_JSON_spec_json ("verifications", GNUNET_JSON_spec_mark_optional (
&verifications), GNUNET_JSON_spec_string ("phone",
&phone),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("addresses",
&addresses),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("documents",
&documents),
NULL),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
struct GNUNET_JSON_Specification bspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("company_name",
&company_name),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("business_activity_id",
&business_activity_id),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("registration_country",
&registration_country),
NULL),
GNUNET_JSON_spec_end ()
};
struct GNUNET_JSON_Specification pspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("first_name",
&first_name),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("middle_name",
&middle_name),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("last_name",
&last_name),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("dob",
&dob),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("residence_country",
&residence_country),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("gender",
&gender),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_bool ("pep",
&pep),
&no_pep),
GNUNET_JSON_spec_end ()
};
struct GNUNET_JSON_Specification *ispec = NULL;
struct GNUNET_TIME_Absolute expiration; struct GNUNET_TIME_Absolute expiration;
bool no_parse;
enum TALER_KYCLOGIC_KycUserType ut;
if (GNUNET_OK != no_parse = (GNUNET_OK !=
GNUNET_JSON_parse (j, GNUNET_JSON_parse (j,
spec, spec,
NULL, NULL)) NULL, NULL));
if (! no_parse)
{
ut = (0 == strcasecmp ("person",
type))
? TALER_KYCLOGIC_KYC_UT_INDIVIDUAL
: TALER_KYCLOGIC_KYC_UT_BUSINESS;
ispec = (ut == TALER_KYCLOGIC_KYC_UT_INDIVIDUAL)
? pspec
: bspec;
no_parse = (GNUNET_OK !=
GNUNET_JSON_parse (j,
ispec,
NULL, NULL));
}
if (no_parse)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
json_dumpf (j, json_dumpf (j,
@ -816,20 +904,94 @@ handle_webhook_finished (void *cls,
resp); resp);
break; break;
} }
if (! verified) if (0 == strcasecmp ("valid",
profile_status))
{ {
log_failure (verifications); log_failure (json_object_get (j,
"decline_reasons"));
} }
resp = MHD_create_response_from_buffer (0, resp = MHD_create_response_from_buffer (0,
"", "",
MHD_RESPMEM_PERSISTENT); MHD_RESPMEM_PERSISTENT);
if (verified) if (0 == strcasecmp ("valid",
profile_status))
{ {
json_t *attr; json_t *attr;
attr = json_object (); if (ut == TALER_KYCLOGIC_KYC_UT_INDIVIDUAL)
// FIXME: initialize attributes! {
GNUNET_assert (NULL != attr); char *name = NULL;
if ( (NULL != last_name) ||
(NULL != first_name) ||
(NULL != middle_name) )
{
GNUNET_asprintf (&name,
"%s, %s %s",
(NULL != last_name)
? last_name
: "",
(NULL != first_name)
? first_name
: "",
(NULL != middle_name)
? middle_name
: "");
}
attr = GNUNET_JSON_PACK (
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_BIRTHDATE,
dob)),
GNUNET_JSON_pack_allow_null (
no_pep
? GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_PEP,
NULL)
: GNUNET_JSON_pack_bool (
TALER_ATTRIBUTE_PEP,
pep)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_FULL_NAME,
name)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_PHONE,
phone)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_EMAIL,
email)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_RESIDENCES,
residence_country))
);
GNUNET_free (name);
}
else
{
attr = GNUNET_JSON_PACK (
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_COMPANY_NAME,
company_name)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_PHONE,
phone)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_EMAIL,
email)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string (
TALER_ATTRIBUTE_REGISTRATION_COUNTRY,
residence_country))
);
}
// FIXME: do something about addresses & documents!
expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity); expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
wh->cb (wh->cb_cls, wh->cb (wh->cb_cls,
wh->process_row, wh->process_row,
@ -846,18 +1008,25 @@ handle_webhook_finished (void *cls,
} }
else else
{ {
enum TALER_KYCLOGIC_KycStatus ks;
ks = (0 == strcasecmp ("pending",
profile_status))
? TALER_KYCLOGIC_STATUS_PENDING
: TALER_KYCLOGIC_STATUS_USER_ABORTED;
wh->cb (wh->cb_cls, wh->cb (wh->cb_cls,
wh->process_row, wh->process_row,
&wh->h_payto, &wh->h_payto,
wh->pd->section, wh->pd->section,
wh->applicant_id, wh->applicant_id,
wh->verification_id, wh->verification_id,
TALER_KYCLOGIC_STATUS_USER_ABORTED, ks,
GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
NULL, NULL,
MHD_HTTP_NO_CONTENT, MHD_HTTP_NO_CONTENT,
resp); resp);
} }
GNUNET_JSON_parse_free (ispec);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
} }
break; break;
@ -1201,7 +1370,7 @@ kycaid_webhook (void *cls,
} }
GNUNET_asprintf (&wh->url, GNUNET_asprintf (&wh->url,
"https://api.kycaid.com/verifications/%s", "https://api.kycaid.com/applicants/%s",
verification_id); verification_id);
GNUNET_break (CURLE_OK == GNUNET_break (CURLE_OK ==
curl_easy_setopt (eh, curl_easy_setopt (eh,