summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-11-20 21:22:01 +0100
committerChristian Grothoff <christian@grothoff.org>2021-11-20 21:22:01 +0100
commit8edd5641430d7e352cf9d14edd3e57b6f75642a3 (patch)
tree770278308c1952fe22ca0a179659aca7491f6e77
parent5483c2b5c798fea1fb101604c6766aa071bafce2 (diff)
downloadmerchant-8edd5641430d7e352cf9d14edd3e57b6f75642a3.tar.gz
merchant-8edd5641430d7e352cf9d14edd3e57b6f75642a3.tar.bz2
merchant-8edd5641430d7e352cf9d14edd3e57b6f75642a3.zip
implement #7052
-rw-r--r--src/backend/taler-merchant-httpd.c13
-rw-r--r--src/backend/taler-merchant-httpd.h9
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-pay.c316
3 files changed, 328 insertions, 10 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 9f460df1..73d3327f 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -199,13 +199,8 @@ TMH_compute_auth (const char *token,
}
-/**
- * Decrement reference counter of @a mi, and free if it hits zero.
- *
- * @param[in,out] mi merchant instance to update and possibly free
- */
-static void
-instance_decref (struct TMH_MerchantInstance *mi)
+void
+TMH_instance_decref (struct TMH_MerchantInstance *mi)
{
struct TMH_WireMethod *wm;
@@ -252,7 +247,7 @@ TMH_instance_free_cb (void *cls,
GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map,
&mi->h_instance,
mi));
- instance_decref (mi);
+ TMH_instance_decref (mi);
return GNUNET_YES;
}
@@ -360,7 +355,7 @@ handle_mhd_completion_callback (void *cls,
if (NULL != hc->request_body)
json_decref (hc->request_body);
if (NULL != hc->instance)
- instance_decref (hc->instance);
+ TMH_instance_decref (hc->instance);
GNUNET_free (hc);
*con_cls = NULL;
}
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index 7fbc8bb5..f81b15aa 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -640,6 +640,15 @@ TMH_add_instance (struct TMH_MerchantInstance *mi);
/**
+ * Decrement reference counter of @a mi, and free if it hits zero.
+ *
+ * @param[in,out] mi merchant instance to update and possibly free
+ */
+void
+TMH_instance_decref (struct TMH_MerchantInstance *mi);
+
+
+/**
* Lookup a merchant instance by its instance ID.
*
* @param instance_id identifier of the instance to resolve
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
index fcdb9821..3baf7293 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -47,6 +47,13 @@
#define MAX_COIN_ALLOWED_COINS 1024
/**
+ * How often do we ask the exchange again about our
+ * KYC status? Very rarely, as if the user actively
+ * changes it, we should usually notice anyway.
+ */
+#define KYC_RETRY_FREQUENCY GNUNET_TIME_UNIT_WEEKS
+
+/**
* Information we keep for an individual call to the pay handler.
*/
struct PayContext;
@@ -360,6 +367,69 @@ struct PayContext
/**
+ * Active KYC operation with an exchange.
+ */
+struct KycContext
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct KycContext *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct KycContext *prev;
+
+ /**
+ * Looking for the exchange.
+ */
+ struct TMH_EXCHANGES_FindOperation *fo;
+
+ /**
+ * Exchange this is about.
+ */
+ char *exchange_url;
+
+ /**
+ * Merchant instance this is for.
+ */
+ struct TMH_MerchantInstance *mi;
+
+ /**
+ * Wire method we are checking the status of.
+ */
+ struct TMH_WireMethod *wm;
+
+ /**
+ * Handle for the GET /deposits operation.
+ */
+ struct TALER_EXCHANGE_DepositGetHandle *dg;
+
+ /**
+ * Contract we are looking up.
+ */
+ struct TALER_PrivateContractHash h_contract_terms;
+
+ /**
+ * Coin we are looking up.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Initial DB timestamp.
+ */
+ struct GNUNET_TIME_Absolute kyc_timestamp;
+
+ /**
+ * Initial KYC status.
+ */
+ bool kyc_ok;
+
+};
+
+
+/**
* Head of active pay context DLL.
*/
static struct PayContext *pc_head;
@@ -369,6 +439,44 @@ static struct PayContext *pc_head;
*/
static struct PayContext *pc_tail;
+/**
+ * Head of active KYC context DLL.
+ */
+static struct KycContext *kc_head;
+
+/**
+ * Tail of active KYC context DLL.
+ */
+static struct KycContext *kc_tail;
+
+
+/**
+ * Free resources used by @a kc.
+ *
+ * @param[in] kc object to free
+ */
+static void
+destroy_kc (struct KycContext *kc)
+{
+ if (NULL != kc->fo)
+ {
+ TMH_EXCHANGES_find_exchange_cancel (kc->fo);
+ kc->fo = NULL;
+ }
+ if (NULL != kc->dg)
+ {
+ TALER_EXCHANGE_deposits_get_cancel (kc->dg);
+ kc->dg = NULL;
+ }
+ TMH_instance_decref (kc->mi);
+ kc->mi = NULL;
+ GNUNET_free (kc->exchange_url);
+ GNUNET_CONTAINER_DLL_remove (kc_head,
+ kc_tail,
+ kc);
+ GNUNET_free (kc);
+}
+
/**
* Compute the timeout for a /pay request based on the number of coins
@@ -419,6 +527,15 @@ abort_active_deposits (struct PayContext *pc)
void
TMH_force_pc_resume ()
{
+ struct KycContext *kc;
+
+ while (NULL != (kc = kc_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Aborting KYC check at %s\n",
+ kc->exchange_url);
+ destroy_kc (kc);
+ }
for (struct PayContext *pc = pc_head;
NULL != pc;
pc = pc->next)
@@ -556,6 +673,200 @@ execute_pay_transaction (struct PayContext *pc);
/**
+ * Function called with detailed wire transfer data.
+ *
+ * @param cls a `struct KycContext *`
+ * @param hr HTTP response data
+ * @param dd details about the deposit (NULL on errors)
+ */
+static void
+deposit_get_callback (
+ void *cls,
+ const struct TALER_EXCHANGE_GetDepositResponse *dr)
+{
+ struct KycContext *kc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ kc->dg = NULL;
+ switch (dr->hr.http_status)
+ {
+ case MHD_HTTP_OK:
+ qs = TMH_db->account_kyc_set_status (
+ TMH_db->cls,
+ kc->mi->settings.id,
+ &kc->wm->h_wire,
+ kc->exchange_url,
+ dr->details.success.payment_target_uuid,
+ NULL, /* no signature */
+ NULL, /* no signature */
+ GNUNET_TIME_absolute_get (),
+ true);
+ GNUNET_break (qs > 0);
+ break;
+ case MHD_HTTP_ACCEPTED:
+ qs = TMH_db->account_kyc_set_status (
+ TMH_db->cls,
+ kc->mi->settings.id,
+ &kc->wm->h_wire,
+ kc->exchange_url,
+ dr->details.accepted.payment_target_uuid,
+ NULL, /* no signature */
+ NULL, /* no signature */
+ GNUNET_TIME_absolute_get (),
+ dr->details.accepted.kyc_ok);
+ GNUNET_break (qs > 0);
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "KYC check failed at %s with unexpected status %u\n",
+ kc->exchange_url,
+ dr->hr.http_status);
+ }
+ destroy_kc (kc);
+}
+
+
+/**
+ * Function called with the result of our exchange lookup.
+ *
+ * @param cls the `struct KycContext`
+ * @param hr HTTP response details
+ * @param exchange_handle NULL if exchange was not found to be acceptable
+ * @param payto_uri payto://-URI of the exchange
+ * @param wire_fee current applicable fee for dealing with @a exchange_handle,
+ * NULL if not available
+ * @param exchange_trusted true if this exchange is
+ * trusted by config
+ */
+static void
+process_kyc_with_exchange (void *cls,
+ const struct TALER_EXCHANGE_HttpResponse *hr,
+ struct TALER_EXCHANGE_Handle *exchange_handle,
+ const char *payto_uri,
+ const struct TALER_Amount *wire_fee,
+ bool exchange_trusted)
+{
+ struct KycContext *kc = cls;
+
+ kc->fo = NULL;
+ if (NULL == exchange_handle)
+ {
+ destroy_kc (kc);
+ return;
+ }
+ kc->dg = TALER_EXCHANGE_deposits_get (exchange_handle,
+ &kc->mi->merchant_priv,
+ &kc->wm->h_wire,
+ &kc->h_contract_terms,
+ &kc->coin_pub,
+ &deposit_get_callback,
+ kc);
+ if (NULL == kc->dg)
+ {
+ GNUNET_break (0);
+ destroy_kc (kc);
+ }
+}
+
+
+/**
+ * Function called from ``account_kyc_get_status``
+ * with KYC status information for this merchant.
+ *
+ * @param cls a `struct KycContext *`
+ * @param h_wire hash of the wire account
+ * @param exchange_kyc_serial serial number for the KYC process at the exchange, 0 if unknown
+ * @param payto_uri payto:// URI of the merchant's bank account
+ * @param exchange_url base URL of the exchange for which this is a status
+ * @param last_check when did we last get an update on our KYC status from the exchange
+ * @param kyc_ok true if we satisfied the KYC requirements
+ */
+static void
+kyc_cb (
+ void *cls,
+ const struct TALER_MerchantWireHash *h_wire,
+ uint64_t exchange_kyc_serial,
+ const char *payto_uri,
+ const char *exchange_url,
+ struct GNUNET_TIME_Absolute last_check,
+ bool kyc_ok)
+{
+ struct KycContext *kc = cls;
+
+ kc->kyc_timestamp = last_check;
+ kc->kyc_ok = kyc_ok;
+}
+
+
+/**
+ * Check for our KYC status at @a exchange_url for the
+ * payment of @a pc. First checks if we already have a
+ * positive result from the exchange, and if not checks
+ * with the exchange.
+ *
+ * @param pc payment context to use as starting point
+ * @param dc deposit confirmation we are triggering on
+ */
+static void
+check_kyc (struct PayContext *pc,
+ const struct DepositConfirmation *dc)
+{
+ enum GNUNET_DB_QueryStatus qs;
+ struct KycContext *kc;
+
+ kc = GNUNET_new (struct KycContext);
+ qs = TMH_db->account_kyc_get_status (TMH_db->cls,
+ pc->hc->instance->settings.id,
+ &pc->wm->h_wire,
+ dc->exchange_url,
+ &kyc_cb,
+ kc);
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ GNUNET_free (kc);
+ return;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ {
+ if (kc->kyc_ok)
+ {
+ GNUNET_free (kc);
+ return; /* we are done */
+ }
+ if (GNUNET_TIME_absolute_get_duration (kc->kyc_timestamp).rel_value_us <
+ KYC_RETRY_FREQUENCY.rel_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Not re-checking KYC status at `%s', as we already recently asked\n",
+ dc->exchange_url);
+ GNUNET_free (kc);
+ return;
+ }
+ }
+ kc->mi = pc->hc->instance;
+ kc->mi->rc++;
+ kc->wm = pc->wm;
+ kc->exchange_url = GNUNET_strdup (dc->exchange_url);
+ kc->h_contract_terms = pc->h_contract_terms;
+ kc->coin_pub = dc->coin_pub;
+ GNUNET_CONTAINER_DLL_insert (kc_head,
+ kc_tail,
+ kc);
+ kc->fo = TMH_EXCHANGES_find_exchange (dc->exchange_url,
+ NULL,
+ GNUNET_NO,
+ &process_kyc_with_exchange,
+ kc);
+ if (NULL == kc->fo)
+ {
+ GNUNET_break (0);
+ destroy_kc (kc);
+ }
+}
+
+
+/**
* Callback to handle a deposit permission's response.
*
* @param cls a `struct DepositConfirmation` (i.e. a pointer
@@ -629,6 +940,8 @@ deposit_cb (void *cls,
if (0 != pc->pending_at_ce)
return; /* still more to do with current exchange */
+ check_kyc (pc,
+ dc);
find_next_exchange (pc);
return;
}
@@ -1337,8 +1650,9 @@ trigger_payment_notification (struct PayContext *pc)
/**
+ * Actually perform the payment transaction.
*
- *
+ * @param pc payment transaction to run
*/
static void
execute_pay_transaction (struct PayContext *pc)