From 3e5d171f5d63abf07fd2e282a316180f9dba176f Mon Sep 17 00:00:00 2001 From: Mshehu5 Date: Thu, 5 Mar 2026 22:44:05 +0100 Subject: [PATCH] Add async OHTTP key fetch helper for Python Introduce `fetch_ohttp_keys_async` in the Python bindings and update integration tests to use it instead of `TestServices.fetch_ohttp_keys()`. This keeps key retrieval inside Python HTTP code and aligns test flow with the async sender/receiver path. The helper fetches `/.well-known/ohttp-gateway` through the configured relay proxy, validates TLS with the provided DER certificate and decodes the response into `OhttpKeys`. --- payjoin-ffi/python/src/payjoin/http.py | 28 +++++++++++++++++++ .../test/test_payjoin_integration_test.py | 6 ++-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 payjoin-ffi/python/src/payjoin/http.py diff --git a/payjoin-ffi/python/src/payjoin/http.py b/payjoin-ffi/python/src/payjoin/http.py new file mode 100644 index 000000000..70c391700 --- /dev/null +++ b/payjoin-ffi/python/src/payjoin/http.py @@ -0,0 +1,28 @@ +import ssl +from urllib.parse import urljoin + +import httpx + +from .payjoin import OhttpKeys + + +async def fetch_ohttp_keys( + ohttp_relay_url: str, + directory_url: str, + certificate: bytes, +) -> OhttpKeys: + keys_url = urljoin(directory_url, "/.well-known/ohttp-gateway") + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ssl_context.check_hostname = True + ssl_context.verify_mode = ssl.CERT_REQUIRED + pem_certificate = ssl.DER_cert_to_PEM_cert(certificate) + ssl_context.load_verify_locations(cadata=pem_certificate) + async with httpx.AsyncClient( + proxy=ohttp_relay_url, verify=ssl_context, timeout=30.0 + ) as client: + response = await client.get( + keys_url, + headers={"Accept": "application/ohttp-keys"}, + ) + response.raise_for_status() + return OhttpKeys.decode(response.content) diff --git a/payjoin-ffi/python/test/test_payjoin_integration_test.py b/payjoin-ffi/python/test/test_payjoin_integration_test.py index 6ef45de28..4763a3050 100644 --- a/payjoin-ffi/python/test/test_payjoin_integration_test.py +++ b/payjoin-ffi/python/test/test_payjoin_integration_test.py @@ -4,6 +4,7 @@ import json from payjoin import * +from payjoin.http import fetch_ohttp_keys from typing import Optional import unittest @@ -98,7 +99,8 @@ async def test_ffi_validation(self): services = TestServices.initialize() services.wait_for_services_ready() directory = services.directory_url() - ohttp_keys = services.fetch_ohttp_keys() + ohttp_relay = services.ohttp_relay_url() + ohttp_keys = await fetch_ohttp_keys(ohttp_relay, directory, services.cert()) recv_persister = InMemoryReceiverSessionEventLog(999) pj_uri = self.create_receiver_context( receiver_address, directory, ohttp_keys, recv_persister @@ -261,8 +263,8 @@ async def test_integration_v2_to_v2(self): services.wait_for_services_ready() directory = services.directory_url() - ohttp_keys = services.fetch_ohttp_keys() ohttp_relay = services.ohttp_relay_url() + ohttp_keys = await fetch_ohttp_keys(ohttp_relay, directory, services.cert()) agent = httpx.AsyncClient() # **********************