commit 0301428cc8d1cc3bc0006003a9abbdf9bcdfdb0b
parent bb2d5e75a9e44689c2020a12a524835d4e586bb9
Author: Iván Ávalos <avalos@disroot.org>
Date: Sun, 9 Aug 2026 16:48:18 +0200
serve the account pay URI on object upload 402s
Diffstat:
1 file changed, 72 insertions(+), 5 deletions(-)
diff --git a/src/sync/sync-httpd2_post-backups-KEY-objects-UID.c b/src/sync/sync-httpd2_post-backups-KEY-objects-UID.c
@@ -26,6 +26,7 @@
#include <taler/taler_mhd2_lib.h>
#include <taler/taler_signatures.h>
#include <sync/sync_signatures.h>
+#include "sync-httpd2_payments.h"
#include "sync-httpd2_post-backups-KEY-objects-UID.h"
@@ -80,6 +81,17 @@ struct ObjectUploadContext
* Signature from the request.
*/
struct SYNC_AccountSignatureP object_sig;
+
+ /**
+ * Context for a merchant payment.
+ *
+ * The object store answers `402 Payment required' on a not-yet-paid
+ * account. Like block uploads, the response must carry the `Taler:'
+ * pay URI so the wallet can prepare the account payment -- the request
+ * is suspended while the payment order is created and resumed with the
+ * response (or, once the payment went through, to retry the store).
+ */
+ struct BackupPaymentContext bpc;
};
@@ -94,6 +106,7 @@ cleanup_object_upload_free (struct TM_HandlerContext *hc)
struct ObjectUploadContext *ouc;
ouc = (struct ObjectUploadContext *) hc;
+ SH_payment_cleanup (&ouc->bpc);
GNUNET_free (ouc->data);
GNUNET_free (ouc);
}
@@ -117,6 +130,19 @@ handle_object_upload (void *upload_cls,
{
struct ObjectUploadContext *ouc = upload_cls;
+ if (NULL != ouc->bpc.resp)
+ {
+ /* Resume after the payment order was created; answer with it. */
+ return MHD_upload_action_from_response (request,
+ ouc->bpc.resp);
+ }
+ if (NULL != ouc->data)
+ {
+ /* Resume after the account was paid; body was already parsed and
+ verified, retry the store. */
+ goto store_object;
+ }
+
if ( (0 == content_data_size) &&
(NULL == content_data) )
{
@@ -191,6 +217,7 @@ handle_object_upload (void *upload_cls,
NULL));
}
+store_object:
/* Store object */
{
enum SYNC_DB_QueryStatus qs;
@@ -218,11 +245,37 @@ handle_object_upload (void *upload_cls,
case SYNC_DB_PAYMENT_REQUIRED:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Object upload failed: payment required\n");
- return MHD_upload_action_from_response (
- request,
- SYNC_MHD_MAKE_ERROR (MHD_HTTP_STATUS_PAYMENT_REQUIRED,
- TALER_EC_SYNC_ACCOUNT_UNKNOWN,
- NULL));
+ {
+ struct MHD_StringNullable order_id;
+ bool suspend;
+
+ if ( (MHD_YES !=
+ MHD_request_get_value (ouc->bpc.request,
+ MHD_VK_URI_QUERY_PARAM,
+ "paying",
+ &order_id)) ||
+ (NULL == order_id.cstr) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Payment required, starting payment process\n");
+ SH_begin_payment (&ouc->bpc,
+ GNUNET_NO,
+ &suspend);
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Payment required, awaiting completion of `%s'\n",
+ order_id.cstr);
+ SH_await_payment (&ouc->bpc,
+ order_id.cstr,
+ &suspend);
+ }
+ if (suspend)
+ return MHD_upload_action_suspend (request);
+ return MHD_upload_action_from_response (request,
+ ouc->bpc.resp);
+ }
case SYNC_DB_HARD_ERROR:
GNUNET_break (0);
return MHD_upload_action_from_response (
@@ -270,6 +323,8 @@ SH_backup_object_post (struct MHD_Request *request,
ouc = GNUNET_new (struct ObjectUploadContext);
ouc->hc.cc = &cleanup_object_upload_free;
ouc->account_pub = *account_pub;
+ ouc->bpc.request = request;
+ ouc->bpc.account_pub = *account_pub;
*fi.v_app_context_ppvoid = ouc;
/* Parse UID from URL */
@@ -289,6 +344,18 @@ SH_backup_object_post (struct MHD_Request *request,
}
}
+ if (NULL != ouc->bpc.resp)
+ {
+ /* Resume after the payment order was created; answer with it and
+ release the upload context. */
+ struct MHD_Response *resp = ouc->bpc.resp;
+
+ cleanup_object_upload_free (&ouc->hc);
+ *fi.v_app_context_ppvoid = NULL;
+ return MHD_action_from_response (request,
+ resp);
+ }
+
/* Same upload limit as for blocks */
if (SH_upload_too_large (upload_size,
&ouc->buff_size))