Supplier Payment Assistant
A client-only buyer workflow for the Supplier Payment Decision Agent. It registers an ephemeral RSA public key, signs a caller-supplied invoice/PO/GRN request and obtains an advisory supplier-payment decision.
It never purchases goods or pays supplier invoices. Optional x402 fees buy verification only. There is no backend, database, customer invoice or credential in this repository.
Completely offline, clearly labelled synthetic demonstration.
Inspection first: quotes without a wallet or uploading invoice data.
Explicit payment authorization with a combined 0.055 USDC maximum.
Installable CLI, JSON reports, public request models and Python interface.
Real optional LangChain and OpenAI Agents tools with operator-owned approval.
No LLM key or subscription required.
Install and try offline
Requires Python 3.12 or later:
python -m pip install supplier-payment-assistant
supplier-payment-assistant --demo
python -m supplier_payment_assistant accepts the same arguments.
The offline mode is SYNTHETIC_OFFLINE_ILLUSTRATION. It generates an ephemeral RSA signature over fictional input and shows an explicitly unverified FLAG_FOR_HUMAN_REVIEW example. It performs no live checks, key registration, wallet access or network requests. No synthetic supplier identity should be treated as real.
Inspect the real workflow without paying
Prepare your own unsigned JSON request in a local invoice.json file. Required top-level fields:
transaction_id: stable for the same business request.
supplier: CRO number, VAT number and IBAN.
invoice: invoice/PO/GRN references, amount, currency, payee name and lines.
purchase_order: PO reference, supplier, amount, currency and lines.
goods_receipt: GRN/PO references, supplier and received lines.
Do not include signature: the client creates it. The PaymentRequest model in supplier_payment_assistant.contracts defines exact fields, constraints and canonical signing bytes. The live service also publishes /schemas/decision.schema.json. The model checks structure, not registry status or account ownership.
supplier-payment-assistant https://supplier-payment-verifier-production.up.railway.app --request .\invoice.json
The default is inspection only. It sends empty quote probes for both operations, not your invoice, public key or signature. No wallet is needed and no on-chain payment is signed.
Use from another agent
import json
from pathlib import Path
from supplier_payment_assistant import run_live
unsigned = json.loads(Path("invoice.json").read_text(encoding="utf-8"))
report = run_live(
"https://supplier-payment-verifier-production.up.railway.app",
unsigned,
) # Inspection only: no payment or invoice upload.
Payment additionally requires the boolean pay=True and an exact cap such as max_usdc="0.055" (or a Decimal). Truthy strings such as "false" are rejected. BuyerError.report preserves partial workflow status; the CLI writes errors to stderr and exits nonzero.
The reusable sign_request function normalizes decimal fields with PaymentRequest before signing compact, sorted-key JSON without the signature field. Signatures use RSA-PSS, SHA-256, MGF1(SHA-256) and digest-length salt. Hand-signing the original JSON without normalization can produce different bytes.
Requests identify as supplier-payment-assistant. The label is not a verified customer identity; the payer wallet establishes the service tenant.
Agent-framework integrations (source version 0.2.0)
PyPI currently serves 0.1.0; the integrations below are in the 0.2.0 source tree until that version is released. Install from a reviewed checkout:
python -m pip install -e ".[langchain,openai]"
python -m examples.framework_demo --framework langchain
python -m examples.framework_demo --framework openai
These examples construct real framework objects and invoke only the synthetic demo locally: no Agent runner, model/provider/API key, wallet or network. Both extras are optional; the base package imports neither framework.
from supplier_payment_assistant import SupplierPaymentTools
tools = SupplierPaymentTools() # Keep ONE instance across calls.
langchain_tools = tools.langchain_tools() # Real StructuredTool objects.
openai_tools = tools.openai_tools() # Real Agents FunctionTool objects.
The default tools are offline_demo and inspect_supplier only. inspect_supplier(unsigned_request_json: str) accepts an unsigned JSON string conforming to PaymentRequest and delegates to run_live(pay=False): only empty quote probes are sent, never invoice data. It reads no wallet key. Each tool returns a JSON-compatible dictionary. Async framework invocations offload synchronous work using asyncio.to_thread.
For example, an application can construct an OpenAI agent as follows (this snippet does not run it or call a model):
from agents import Agent
agent = Agent(
name="Advisory supplier verification",
instructions="Inspect quotes or show the synthetic demo; never pay invoices.",
tools=tools.openai_tools(),
)
For LangChain, an application can construct a tool-bound chat model without invoking it (langchain-openai is a separate application dependency):
from langchain_openai import ChatOpenAI
advisory_model = ChatOpenAI(model="gpt-4.1-mini").bind_tools(tools.langchain_tools())
# No invoke/stream/agent loop here.
Privacy: giving invoice JSON to an LLM may send private invoice/supplier data to your chosen model/provider. Framework tracing or debug logging may record tool inputs and results. This package enables no tracing, adds no capturing, and makes no automatic model calls; operators must approve data use, configure their own framework/provider privacy controls, and avoid logging raw invoices or keys. Returning a paid verification report can also disclose data.
Paid verification is opt-in operator code, never a model argument:
from examples.trusted_approval import tools_for_trusted_digests
# Supply exact SHA-256 digests from authenticated ERP/operator records,
# never from an LLM, prompt, or untrusted incoming invoice.
approved_digests = frozenset() # Empty is deny-all until trusted records exist.
tools = tools_for_trusted_digests(approved_digests)
paid_tools = tools.openai_tools()
Only supplying an approval_policy callable exposes verify_supplier_paid. The callback receives an immutable ApprovalRequest containing a recursively frozen, normalized unsigned_request, canonical_body bytes, their canonical_sha256 digest and workflow_cap_usdc. Canonical bytes are exactly PaymentRequest.signed_body() with a validation-only signature placeholder; validation happens before approval without creating a key or sending data. Generate trusted digests independently from the same contract/canonical bytes. The policy must return literal True; truthy strings are denied. Callback errors deny approval. Its immutable copy cannot mutate the actual normalized request passed to the existing signer/workflow. No supplied signature or unexpected request/tool fields are accepted. The model cannot supply approval, payment flags, budget, URL, or wallet parameters. Base URL is trusted constructor configuration, defaulting to the production origin.
workflow_cap_usdc defaults to Decimal("0.055") and max_paid_calls to one. Before any paid workflow, a lock atomically reserves the full workflow cap, with a cumulative ceiling of 0.055 USDC per tools object. Approval is called outside that lock. Denial reserves nothing and sends no requests. tools.reserved_usdc and tools.paid_calls count reserved amounts/attempts, not confirmed settlement. Failures, rejected payments, partial completion and unconfirmed settlement never refund the reservation: authorizations might have escaped. There are no automatic paid retries. BuyerError.report retains partial workflow status; errors are propagated rather than converted to success. The existing client performs all signing and fee settlement.
These limits are in-memory, per object, not persistent or wallet-wide daily limits. Recreating objects or restarting processes resets them. Do not create a new tools object per call. Production requires durable external approval and budget enforcement across processes and wallets. This is advisory verification only, not a complete autonomous invoice-payment runner.
Assurance boundaries
EXECUTE_PAYMENT, REJECT_PAYMENT and FLAG_FOR_HUMAN_REVIEW are advisory result codes, not bank instructions this client executes.
IBAN checksum/name checks are not bank Confirmation of Payee or ownership.
The three-way match compares supplied documents, not authenticated ERP data.
RSA signatures establish who signed supplied data, not whether it is true.
Confidence 1.0 is a rule result, not a guarantee of payment safety.
Keep customer-controlled approval, account-ownership and bank-payment checks.
Development
git clone https://github.com/LKennedyShare/supplier-payment-assistant.git
cd supplier-payment-assistant
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e ".[test,langchain,openai]"
python -m pytest -q
Tests invoke real optional framework objects locally, with no model calls. Tests use mocked HTTP/facilitator responses and predictable unfunded test wallet material only. The CI workflow runs tests and the offline demonstration; it never authorizes a real payment.
Release and unpaid discovery automation
Version 0.2.0 adds optional framework adapters. Trusted publishing is configured for this repository without stored API tokens. The publisher identity is:
PyPI project: supplier-payment-assistant
GitHub owner: LKennedyShare
Repository: supplier-payment-assistant
Workflow filename: publish.yml
Environment: pypi (exact, fixed GitHub environment name)
The GitHub environment is configured. No API token or password is used. No manual approval per release is required by default; environment protection rules can impose approval if the owner chooses. Publishing a GitHub release with a matching version tag builds that version’s wheel and sdist, runs mocked tests, the installed offline CLI and strict Twine checks, then publishes via OIDC. Only release.published triggers publication, not a PR or manual workflow run. Tags must exactly match v<pyproject.version> and package identity is checked before build. PyPI versions are immutable: future releases need a new version and matching tag. Duplicate uploads fail rather than silently skipping existing files. The initial published version is 0.1.0; do not reuse that release tag.
discovery.yml runs nightly at 03:41 UTC and supports manual dispatch. It uses Python’s standard library only, no SDK, wallet, secrets or funds. An independent job installs the published PyPI package and runs only its offline synthetic demo, outside a repository checkout; it never invokes a paid command. It reads public health/OpenAPI/MCP/agent cards/homepage/buyers metadata and sends one empty-body IBAN REST quote probe, one empty-arguments MCP IBAN quote probe and one A2A message/send with operation=check_iban and request={}. It never registers keys, signs, pays, executes verification or uploads invoices or supplier identities. No output URLs are followed. All requests identify as scout-distribution-monitor: diagnostics, not customers, purchases, demand or independent adoption.
Run locally with python scripts/check_discovery.py --service supplier --output discovery.json. The JSON, Actions artifact and step summary contain only fixed check names and statuses, never response bodies, headers or private data. Requests refuse redirects, cap bodies at 2 MB and use 15-second socket timeouts; failures exit nonzero. A missing PyPI project is explicitly pending, without masking service failures. The scheduled workflow uses --require-pypi to enforce PyPI availability and repository/MIT/name metadata. The homepage and /buyers.txt must link this public client repository.
GitHub schedules can be delayed or dropped and public-repository schedules may be disabled after 60 days without repository activity. This is not guaranteed continuous uptime monitoring; review Actions and re-enable schedules when needed. No artificial activity, stars or promotional posting is generated.
License
MIT licensed; see LICENSE. This covers this buyer client’s source code, not the separate verification service, registry data or API usage terms.
Metadata
Release files for supplier-payment-assistant 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| supplier_payment_assistant-0.2.0.tar.gz | 36.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| supplier_payment_assistant-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.7 kB
Release files / supplier_payment_assistant-0.2.0.tar.gz
| Download URL | supplier_payment_assistant-0.2.0.tar.gz |
|---|---|
| Size | 36.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
90b73238f751e83174ba836ca983043466768c6e6b9ace9539d09b8064714692
|
|
BLAKE2b-256 checksum How to use checksums |
e07d6f8d9442da093b29bcf3946834109ebf32693f45b22d00f64bea075771c6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / supplier_payment_assistant-0.2.0-py3-none-any.whl
| Download URL | supplier_payment_assistant-0.2.0-py3-none-any.whl |
|---|---|
| Size | 19.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2aec7843ac8f6922133c5b532bdf71305a0a3126157d93d3d29958bd5e52d832
|
|
BLAKE2b-256 checksum How to use checksums |
3b7ecffd13048ae40e1896bfaf9bb539c897c57e1f1220ae8bcfedc4cc7ad1e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency log