merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 35678332df0f2bd45491bc062f06cdc7a4d3b86a
parent 35d05353540894b603591b216d27d46dcbee1013
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 25 Feb 2025 20:17:58 +0100

try to fix unthrottled merchant-exchange interaction on missing wire transfer

Diffstat:
Msrc/backend/taler-merchant-depositcheck.c | 20+++++++++++++++++++-
Msrc/backenddb/pg_insert_deposit_to_transfer.c | 9+++++----
Msrc/backenddb/pg_insert_deposit_to_transfer.h | 4+++-
Msrc/backenddb/pg_insert_deposit_to_transfer.sql | 19+++++++++++++------
Msrc/backenddb/pg_update_deposit_confirmation_status.c | 2+-
Msrc/backenddb/test_merchantdb.c | 39++++++++++++++++++++++++++++-----------
Msrc/include/taler_merchantdb_plugin.h | 4+++-
7 files changed, 72 insertions(+), 25 deletions(-)

diff --git a/src/backend/taler-merchant-depositcheck.c b/src/backend/taler-merchant-depositcheck.c @@ -423,6 +423,7 @@ deposit_get_cb ( case MHD_HTTP_OK: { enum GNUNET_DB_QueryStatus qs; + bool cleared = false; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Exchange returned wire transfer over %s for deposited coin %s\n", @@ -431,7 +432,24 @@ deposit_get_cb ( qs = db_plugin->insert_deposit_to_transfer ( db_plugin->cls, w->deposit_serial, - &dr->details.ok); + &dr->details.ok, + &cleared); + if (qs < 0) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (! cleared) + { + qs = db_plugin->update_deposit_confirmation_status ( + db_plugin->cls, + w->deposit_serial, + true, /* this failed, wire_pending remains true */ + GNUNET_TIME_absolute_to_timestamp (future_retry), + w->retry_backoff, + "wire transfer unknown"); + } if (qs < 0) { GNUNET_break (0); diff --git a/src/backenddb/pg_insert_deposit_to_transfer.c b/src/backenddb/pg_insert_deposit_to_transfer.c @@ -30,7 +30,8 @@ enum GNUNET_DB_QueryStatus TMH_PG_insert_deposit_to_transfer ( void *cls, uint64_t deposit_serial, - const struct TALER_EXCHANGE_DepositData *dd) + const struct TALER_EXCHANGE_DepositData *dd, + bool *wpc) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -43,12 +44,11 @@ TMH_PG_insert_deposit_to_transfer ( GNUNET_PQ_query_param_auto_from_type (&dd->wtid), GNUNET_PQ_query_param_end }; - bool wpc; bool conflict; bool no_exchange_pub; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_bool ("out_wire_pending_cleared", - &wpc), + wpc), GNUNET_PQ_result_spec_bool ("out_conflict", &conflict), GNUNET_PQ_result_spec_bool ("out_no_exchange_pub", @@ -57,6 +57,7 @@ TMH_PG_insert_deposit_to_transfer ( }; enum GNUNET_DB_QueryStatus qs; + *wpc = false; PREPARE (pg, "insert_deposit_to_transfer", "SELECT" @@ -74,7 +75,7 @@ TMH_PG_insert_deposit_to_transfer ( if (no_exchange_pub) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Exchange public key unknown (bug!?)\n"); - if (wpc) + if (*wpc) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Wire pending flag cleared (good!)\n"); if (conflict) diff --git a/src/backenddb/pg_insert_deposit_to_transfer.h b/src/backenddb/pg_insert_deposit_to_transfer.h @@ -32,13 +32,15 @@ * @param cls closure * @param deposit_serial serial number of the deposit * @param dd deposit transfer data from the exchange to store + * @param[out] wpc set to true if the wire_pending flag was cleared * @return transaction status */ enum GNUNET_DB_QueryStatus TMH_PG_insert_deposit_to_transfer ( void *cls, uint64_t deposit_serial, - const struct TALER_EXCHANGE_DepositData *dd); + const struct TALER_EXCHANGE_DepositData *dd, + bool *wpc); #endif diff --git a/src/backenddb/pg_insert_deposit_to_transfer.sql b/src/backenddb/pg_insert_deposit_to_transfer.sql @@ -80,12 +80,19 @@ INSERT INTO merchant_deposit_to_transfer IF NOT FOUND THEN - -- Same or conflicting wire transfer existed in the table already - -- Note: we don't distinguish here between - -- conflict and duplicate. Do we need to? + PERFORM FROM merchant_deposit_to_transfer + WHERE deposit_serial=in_deposit_serial + AND wtid=in_wtid + AND signkey_serial=my_signkey_serial + AND exchange_sig=in_exchange_sig; +END IF; + +IF NOT FOUND +THEN + -- Conflicting (!) wire transfer existed in the table already out_conflict=TRUE; out_wire_pending_cleared=FALSE; - return; + RETURN; END IF; out_conflict=FALSE; @@ -123,9 +130,9 @@ UPDATE merchant_deposit_confirmations (SELECT 1 FROM merchant_deposits md LEFT JOIN merchant_deposit_to_transfer mdtt - USING (wtid) + USING (deposit_serial) WHERE md.deposit_confirmation_serial=my_decose - AND mdtt.credit_serial IS NULL); + AND mdtt.signkey_serial IS NULL); -- credit_serial will be NULL due to LEFT JOIN -- if we do not have an entry in mdtt for the deposit -- and thus some entry in md was not yet wired. diff --git a/src/backenddb/pg_update_deposit_confirmation_status.c b/src/backenddb/pg_update_deposit_confirmation_status.c @@ -54,7 +54,7 @@ TMH_PG_update_deposit_confirmation_status ( " wire_transfer_deadline=$2" ",exchange_failure=$3" ",retry_backoff=$4" - ",wire_pending=$5" + ",wire_pending=$5 AND wire_pending" " WHERE deposit_confirmation_serial=" " (SELECT deposit_confirmation_serial" " FROM merchant_deposits" diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c @@ -4208,6 +4208,7 @@ test_insert_transfer (const struct InstanceData *instance, * @param order the order the deposit and transfer were made for. * @param transfer the transfer. * @param expected_result the result the database should return. + * @param expected_cleared clearance status the database should return. * @return 0 on success, 1 otherwise. */ static int @@ -4216,7 +4217,8 @@ test_insert_deposit_to_transfer (const struct InstanceData *instance, const struct OrderData *order, const struct DepositData *deposit, const struct TransferData *transfer, - enum GNUNET_DB_QueryStatus expected_result) + enum GNUNET_DB_QueryStatus expected_result, + bool expect_cleared) { const struct TALER_EXCHANGE_DepositData deposit_data = { .exchange_pub = signkey->exchange_pub, @@ -4228,12 +4230,17 @@ test_insert_deposit_to_transfer (const struct InstanceData *instance, uint64_t deposit_serial = get_deposit_serial (instance, order, deposit); + bool cleared; TEST_COND_RET_ON_FAIL (expected_result == plugin->insert_deposit_to_transfer (plugin->cls, deposit_serial, - &deposit_data), + &deposit_data, + &cleared), "insert deposit to transfer failed\n"); + TEST_COND_RET_ON_FAIL (expect_cleared == + cleared, + "cleared status wrong"); return 0; } @@ -4255,13 +4262,13 @@ test_insert_transfer_details ( enum GNUNET_DB_QueryStatus expected_result) { TEST_COND_RET_ON_FAIL (expected_result == - plugin->insert_transfer_details (plugin->cls, - instance->instance.id, - transfer->exchange_url - , - account->payto_uri, - &transfer->wtid, - &transfer->data), + plugin->insert_transfer_details ( + plugin->cls, + instance->instance.id, + transfer->exchange_url, + account->payto_uri, + &transfer->wtid, + &transfer->data), "Insert transfer details failed\n"); return 0; } @@ -4452,13 +4459,15 @@ run_test_transfers (struct TestTransfers_Closure *cls) &cls->order, &cls->deposit, &cls->transfers[0], - GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); + GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, + false)); TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance, &cls->signkey, &cls->order, &cls->deposit, &cls->transfers[0], - GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); + GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, + false)); TEST_RET_ON_FAIL (test_insert_transfer_details (&cls->instance, &cls->account, &cls->transfers[0], @@ -4482,6 +4491,14 @@ run_test_transfers (struct TestTransfers_Closure *cls) cls->transfers[0].confirmed = true; TEST_RET_ON_FAIL (test_lookup_transfer (&cls->instance, &cls->transfers[0])); + TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance, + &cls->signkey, + &cls->order, + &cls->deposit, + &cls->transfers[0], + GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, + true)); + TEST_RET_ON_FAIL (test_lookup_transfer_summary (cls->deposit.exchange_url, &cls->transfers[0].wtid, cls->order.id, diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h @@ -2627,12 +2627,14 @@ struct TALER_MERCHANTDB_Plugin * @param cls closure * @param deposit_serial serial number of the deposit * @param dd deposit transfer data from the exchange to store + * @param[out] wpc set to true if the wire_pending flag was cleared * @return transaction status */ enum GNUNET_DB_QueryStatus (*insert_deposit_to_transfer)(void *cls, uint64_t deposit_serial, - const struct TALER_EXCHANGE_DepositData *dd); + const struct TALER_EXCHANGE_DepositData *dd, + bool *wpc); /**