Skip to content

[codex] Implement native payments runtime#92

Merged
Telli merged 6 commits intomainfrom
codex/native-payments-runtime
May 5, 2026
Merged

[codex] Implement native payments runtime#92
Telli merged 6 commits intomainfrom
codex/native-payments-runtime

Conversation

@Telli
Copy link
Copy Markdown
Contributor

@Telli Telli commented May 5, 2026

Summary

Implements payments as a native OpenClaw.NET runtime/security capability with a thin first-party payment tool and gateway-backed CLI surface.

  • Adds OpenClaw.Payments.Abstractions, OpenClaw.Payments.Core, OpenClaw.Payments.StripeLink, and OpenClaw.Plugins.Payment projects.
  • Adds safe payment DTOs, provider interfaces, deterministic mock provider, in-memory payment vault, policy/approval gates, audit sink, payment redaction, sentinel substitution, and machine-payment HTTP 402 support.
  • Wires the runtime into gateway DI, agent tool execution, persistence redaction, integration endpoints, client SDK, and openclaw payment ... CLI commands.
  • Adds Stripe Link link-cli setup/status adapter with safe ProcessStartInfo.ArgumentList execution and redacted process output.
  • Adds payment docs and compatibility/security notes restricting TypeScript payment plugin live execution.

Security Invariants

  • Payments are disabled by default.
  • Live money-moving actions require policy and critical approval.
  • Tool/CLI/API responses expose safe metadata only and never return PAN, CVV, provider secrets, shared payment tokens, or payment authorization headers.
  • Browser payment sentinels resolve only inside the approved browser fill execution boundary; persisted arguments keep the sentinel text.
  • PaymentSecret cannot be JSON-serialized.

Validation

  • dotnet build OpenClaw.Net.slnx -v:minimal
  • dotnet test OpenClaw.Net.slnx -v:minimal (1078 passed)

@Telli Telli marked this pull request as ready for review May 5, 2026 08:35
Copilot AI review requested due to automatic review settings May 5, 2026 08:35
Comment thread src/OpenClaw.Agent/OpenClawToolExecutor.cs Fixed
Comment thread src/OpenClaw.Payments.Core/PaymentAwareHttpHandler.cs Fixed
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment thread src/OpenClaw.Gateway/Endpoints/IntegrationEndpoints.cs Fixed
Comment thread src/OpenClaw.Gateway/Endpoints/IntegrationEndpoints.cs Fixed
Comment thread src/OpenClaw.Payments.Core/PaymentAwareHttpHandler.cs Fixed
Comment thread src/OpenClaw.Payments.StripeLink/LinkCliCommandRunner.cs Fixed
Comment thread src/OpenClaw.Payments.StripeLink/LinkCliCommandRunner.cs Fixed
Comment thread src/OpenClaw.Plugins.Payment/PaymentTool.cs Fixed
@augmentcode
Copy link
Copy Markdown

augmentcode Bot commented May 5, 2026

🤖 Augment PR Summary

Summary: This PR implements native payments as a first-party OpenClaw.NET runtime capability (disabled by default), with a thin built-in payment tool plus gateway-backed API/CLI surfaces.

Changes:

  • Added payment projects: OpenClaw.Payments.Abstractions, OpenClaw.Payments.Core, OpenClaw.Payments.StripeLink, and OpenClaw.Plugins.Payment.
  • Introduced safe payment DTOs/interfaces, a deterministic mock provider, in-memory secret vault, approval/policy gates, and an audit sink.
  • Integrated payment endpoints under /api/integration/payment/* and added client SDK methods + openclaw payment ... CLI commands.
  • Added payment redaction + baseline secret redaction, wired through agent runtime responses, tool execution persistence, session/note storage, trajectory export, and prompt-cache tracing.
  • Implemented browser “payment sentinel” substitution for approved browser fill execution boundaries while keeping persisted arguments sentinel-only.
  • Added Stripe Link provider adapter around link-cli using ProcessStartInfo.ArgumentList, cancellation/timeout handling, and redacted process output.
  • Registered the native payment tool only when payments + tool surface are enabled via config.
  • Added documentation covering security posture, compatibility caveats, CLI usage, and plugin/tool contract.
  • Added tests for vault semantics, redaction, sentinel substitution, Stripe Link setup, 402 handler behavior, and safe tool outputs.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 5 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

{
ct.ThrowIfCancellationRequested();

var live = string.Equals(request.Environment, PaymentEnvironments.Live, StringComparison.OrdinalIgnoreCase);
Copy link
Copy Markdown

@augmentcode augmentcode Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultPaymentPolicy.EvaluateAsync (line 25) treats anything other than the literal "live" as non-live, so an unexpected value (e.g. "prod") can get auto-allowed when AllowTestModeWithoutApproval=true, potentially bypassing approval gating for intended-live flows. Other locations where this applies: src/OpenClaw.Cli/PaymentCommands.cs:28, src/OpenClaw.Gateway/Endpoints/IntegrationEndpoints.cs:709, src/OpenClaw.Plugins.Payment/PaymentTool.cs:73.

Severity: high

Other Locations
  • src/OpenClaw.Cli/PaymentCommands.cs:28
  • src/OpenClaw.Gateway/Endpoints/IntegrationEndpoints.cs:709
  • src/OpenClaw.Plugins.Payment/PaymentTool.cs:73

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.


private static HttpRequestMessage CloneRequest(HttpRequestMessage request)
{
var clone = new HttpRequestMessage(request.Method, request.RequestUri)
Copy link
Copy Markdown

@augmentcode augmentcode Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PaymentAwareHttpHandler.CloneRequest (line 59) copies method/URI/headers but drops request.Content (and content headers), so a 402 retry for POST/PUT may send an empty/altered request body.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Action = PaymentActions.BrowserSentinelFill,
Summary = $"Fill browser checkout field using payment handle {handleId}.",
ProviderId = "payment",
Environment = PaymentEnvironments.Live,
Copy link
Copy Markdown

@augmentcode augmentcode Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PaymentSentinelSubstitutionService builds the approval request with Environment = live (line 69) and ProviderId = "payment", which ignores the current payment/tool context and can force live-style approval/denial even for test-mode sentinels.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Comment thread src/OpenClaw.Agent/AgentRuntime.cs Outdated
// Yield buffered text deltas
foreach (var delta in streamResult.TextDeltas)
yield return AgentStreamEvent.TextDelta(delta);
yield return AgentStreamEvent.TextDelta(_redaction.Redact(delta));
Copy link
Copy Markdown

@augmentcode augmentcode Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In RunStreamingAsync (line 560), redacting each streamed delta independently can miss secrets that span chunks, so sensitive strings may leak to streaming consumers even if FullText is redacted later.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.


if (onDelta is not null && tool is IStreamingTool streamingTool)
result = await ExecuteStreamingToolCollectAsync(streamingTool, argsJson, onDelta, ct);
result = await ExecuteStreamingToolCollectAsync(streamingTool, executionArgsJson, onDelta, ct);
Copy link
Copy Markdown

@augmentcode augmentcode Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenClawToolExecutor.ExecuteAsync now passes executionArgsJson into streaming tools (line 285); if sentinel substitution injected raw secrets, ExecuteStreamingToolCollectAsync forwards chunk to onDelta unredacted, which can leak secrets during streaming before the final result redaction runs.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a native payments capability to OpenClaw.NET (runtime + security controls), exposing it through a first-party payment tool, gateway integration endpoints, and a gateway-backed CLI surface. It also adds a redaction pipeline and payment-sentinel substitution so sensitive payment material can be resolved only inside an approved execution boundary while persisted artifacts remain safe.

Changes:

  • Added payment abstractions, core runtime (policy/vault/audit/redaction), mock provider, and a Stripe Link CLI adapter.
  • Wired payments into gateway DI/tool registration/integration endpoints/client SDK/CLI, plus added payment docs and security notes.
  • Added a generalized redaction pipeline and sentinel substitution plumbing across tool execution and persistence/export paths.

Reviewed changes

Copilot reviewed 56 out of 56 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/OpenClaw.Tests/PaymentRuntimeTests.cs Adds tests for payment redaction, vault semantics, approvals, sentinels, Stripe Link runner, and HTTP 402 flow.
src/OpenClaw.Tests/OpenClaw.Tests.csproj References new payments projects for test coverage.
src/OpenClaw.Plugins.Payment/PaymentTool.cs Implements the first-party payment tool with action routing + JSON schema.
src/OpenClaw.Plugins.Payment/PaymentPluginRegistration.cs Simple factory for registering the payment tool.
src/OpenClaw.Plugins.Payment/OpenClaw.Plugins.Payment.csproj New packable payment plugin/tool project.
src/OpenClaw.Payments.StripeLink/StripeLinkPaymentProvider.cs Stripe Link link-cli provider adapter (setup/list/issue/execute/status).
src/OpenClaw.Payments.StripeLink/StripeLinkOptions.cs Options model for Stripe Link adapter.
src/OpenClaw.Payments.StripeLink/OpenClaw.Payments.StripeLink.csproj New Stripe Link provider project + logging abstractions.
src/OpenClaw.Payments.StripeLink/LinkCliCommandRunner.cs Process runner using ArgumentList, redacted output, and timeout handling.
src/OpenClaw.Payments.Core/PaymentServiceCollectionExtensions.cs DI registration for payment core runtime, providers, redactors, and sentinel service.
src/OpenClaw.Payments.Core/PaymentSentinelSubstitutionService.cs Resolves {{payment.vcard:...}} only for browser fill boundary.
src/OpenClaw.Payments.Core/PaymentSensitiveDataRedactor.cs Adds PAN/CVV/payment-token/auth header redaction implementation.
src/OpenClaw.Payments.Core/PaymentRuntimeService.cs Core runtime orchestration: provider routing, approval/policy enforcement, vaulting, audit.
src/OpenClaw.Payments.Core/PaymentAwareHttpHandler.cs Delegating handler to satisfy machine-payment HTTP 402 challenges.
src/OpenClaw.Payments.Core/PaymentAuditSinks.cs In-memory audit sink implementation.
src/OpenClaw.Payments.Core/OpenClaw.Payments.Core.csproj New packable core runtime project.
src/OpenClaw.Payments.Core/MockPaymentProvider.cs Deterministic mock provider for tests/local development.
src/OpenClaw.Payments.Core/InMemoryPaymentSecretVault.cs In-memory secret vault with TTL/retrieve-once/revoke semantics.
src/OpenClaw.Payments.Core/DefaultPaymentPolicy.cs Default policy: allow test mode (optional), require approval for money-moving, optional live limits.
src/OpenClaw.Payments.Abstractions/PaymentModels.cs Payment DTOs + JSON source-gen context + PaymentSecret non-serializable converter.
src/OpenClaw.Payments.Abstractions/PaymentInterfaces.cs Provider/policy/vault/audit/approval interfaces.
src/OpenClaw.Payments.Abstractions/OpenClaw.Payments.Abstractions.csproj New packable abstractions project.
src/OpenClaw.Gateway/PromptCaching/PromptCacheTraceWriter.cs Adds redaction support when emitting cache trace entries.
src/OpenClaw.Gateway/OpenClaw.Gateway.csproj Adds gateway references to payments projects.
src/OpenClaw.Gateway/GatewayPaymentApprovalService.cs Implements IPaymentApprovalService via existing gateway tool-approval system.
src/OpenClaw.Gateway/Endpoints/IntegrationEndpoints.cs Adds integration payment endpoints (setup/funding/virtual-card/execute/status).
src/OpenClaw.Gateway/Endpoints/AdminEndpoints.cs Passes redaction pipeline into admin observability service construction.
src/OpenClaw.Gateway/Composition/RuntimeInitializationExtensions.RuntimeFactories.cs Registers the payment tool when enabled by config.
src/OpenClaw.Gateway/Composition/RuntimeInitializationExtensions.CompositionStages.cs Adds PaymentRuntimeService to runtime services resolution.
src/OpenClaw.Gateway/Composition/GatewayAppRuntime.cs Adds PaymentRuntime field to gateway runtime container.
src/OpenClaw.Gateway/Composition/CoreServicesExtensions.cs Wires payments + redaction pipeline + sentinel substitution into DI; injects redaction into memory stores.
src/OpenClaw.Gateway/AdminObservabilityService.cs Adds redaction to trajectory export surfaces (and integrates with anonymization).
src/OpenClaw.Core/Security/SentinelSubstitution.cs Adds core sentinel substitution service contracts + noop implementation.
src/OpenClaw.Core/Security/RedactionPipeline.cs Adds redaction pipeline + baseline secret redactor + noop pipeline.
src/OpenClaw.Core/Models/Session.cs Adds JSON source-gen entries for new payment config types.
src/OpenClaw.Core/Models/GatewayConfig.cs Adds Payments configuration section and related config models.
src/OpenClaw.Core/Memory/SqliteMemoryStore.cs Adds optional redaction pipeline use for sessions/branches/notes.
src/OpenClaw.Core/Memory/FileMemoryStore.cs Adds optional redaction pipeline use for sessions/branches/notes.
src/OpenClaw.Client/OpenClawHttpClient.cs Adds payment integration client methods and URIs.
src/OpenClaw.Client/OpenClaw.Client.csproj Adds payments abstractions reference for SDK types/JSON context.
src/OpenClaw.Cli/Program.cs Adds openclaw payment ... command entry.
src/OpenClaw.Cli/PaymentCommands.cs Implements gateway-backed payment CLI commands + formatting.
src/OpenClaw.Cli/OpenClawHttpClient.cs Exposes payment methods via CLI wrapper client.
src/OpenClaw.Cli/OpenClaw.Cli.csproj Adds payments abstractions reference for CLI.
src/OpenClaw.Cli/CliArgs.cs Adds new CLI flags (--test, --yes).
src/OpenClaw.Agent/OpenClawToolExecutor.cs Adds redaction + sentinel substitution to tool execution and persistence of tool call artifacts.
src/OpenClaw.Agent/NativeAgentRuntimeFactory.cs Plumbs redaction + sentinel substitution from DI into agent runtime.
src/OpenClaw.Agent/AgentRuntime.cs Applies redaction to user input, assistant output, and streaming deltas/history capture.
SECURITY.md Documents payment security invariants and links to payment docs.
OpenClaw.Net.slnx Adds new payments projects to solution.
docs/TOOLS_GUIDE.md Documents payment tool and browser payment-sentinel behavior.
docs/security/payments.md Adds detailed payment security model documentation.
docs/README.md Adds docs index entries for payment plugin/CLI/security docs.
docs/plugins/payment.md Documents payment tool behavior, config, and sentinels.
docs/COMPATIBILITY.md Notes TypeScript payment plugin live execution not supported.
docs/cli/payment.md Documents payment CLI usage and safety contract.

Comment on lines +37 to 41
StableSystemPrompt = ShouldIncludeSystem() ? _redaction.Redact(descriptor.StableSystemPrompt) : null,
PromptText = ShouldIncludePrompt() ? _redaction.Redact(descriptor.VolatileSuffix) : null,
MessageCount = messages.Count,
AdditionalProperties = options.AdditionalProperties?.ToDictionary(static kvp => kvp.Key, static kvp => RenderPropertyValue(kvp.Value))
AdditionalProperties = options.AdditionalProperties?.ToDictionary(kvp => kvp.Key, kvp => (string?)_redaction.Redact(RenderPropertyValue(kvp.Value)))
});
Comment on lines +57 to +66
private static HttpRequestMessage CloneRequest(HttpRequestMessage request)
{
var clone = new HttpRequestMessage(request.Method, request.RequestUri)
{
Version = request.Version,
VersionPolicy = request.VersionPolicy
};
foreach (var header in request.Headers)
clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
return clone;
Comment on lines +64 to +74
var approval = new ApprovalRequest
{
Action = PaymentActions.BrowserSentinelFill,
Summary = $"Fill browser checkout field using payment handle {handleId}.",
ProviderId = "payment",
Environment = PaymentEnvironments.Live,
SessionId = context.SessionId,
ChannelId = context.ChannelId,
SenderId = context.SenderId,
Severity = PaymentApprovalSeverities.Critical
};
Comment on lines +93 to +106
catch
{
if (!string.IsNullOrWhiteSpace(request.ProviderId))
await _audit.RecordAsync(new PaymentAuditEvent
{
EventType = "virtual_card_failed",
ProviderId = provider.ProviderId,
MerchantName = request.MerchantName,
AmountMinor = request.AmountMinor,
Currency = request.Currency,
Status = "failed",
Environment = effectiveContext.Environment
}, CancellationToken.None);
throw;
Comment on lines +158 to +171
var results = new List<FundingSource>();
foreach (var item in items)
{
results.Add(new FundingSource
{
FundingSourceId = ReadString(item, "id") ?? ReadString(item, "fundingSourceId") ?? "",
ProviderId = ProviderId,
DisplayName = ReadString(item, "display_name") ?? ReadString(item, "displayName") ?? "Stripe Link funding source",
Type = ReadString(item, "type") ?? "link",
Last4 = ReadString(item, "last4"),
Currency = ReadString(item, "currency"),
TestMode = string.Equals(_options.Mode, PaymentEnvironments.Test, StringComparison.OrdinalIgnoreCase),
Available = ReadBool(item, "available") ?? true
});
Comment thread src/OpenClaw.Cli/PaymentCommands.cs Outdated

case "execute":
{
var env = environment ?? PaymentEnvironments.Live;
Comment on lines +98 to +110
private static VirtualCardRequest BuildVirtualCardRequest(JsonElement root, string provider, string environment)
=> new()
{
ProviderId = provider,
FundingSourceId = ReadString(root, "funding_source_id"),
MerchantName = ReadRequiredString(root, "merchant"),
MerchantUrl = ReadString(root, "merchant_url"),
AmountMinor = ReadLong(root, "amount_minor") ?? 0,
Currency = ReadString(root, "currency") ?? "USD",
Purpose = ReadString(root, "purpose"),
ValidUntilUtc = DateTimeOffset.UtcNow.AddMinutes(Math.Clamp((int)(ReadLong(root, "valid_minutes") ?? 30), 1, 1440)),
Environment = environment
};
Comment on lines +120 to +129
Challenge = new MachinePaymentChallenge
{
ChallengeId = ReadString(root, "challenge_id"),
Protocol = ReadString(root, "protocol") ?? "http-402",
ResourceUrl = ReadString(root, "resource_url"),
MerchantName = merchant,
AmountMinor = ReadLong(root, "amount_minor") ?? 0,
Currency = ReadString(root, "currency") ?? "USD",
ProviderId = provider
}
Comment on lines 181 to 188
public async ValueTask SaveSessionAsync(Session session, CancellationToken ct)
{
if (session is null)
throw new ArgumentNullException(nameof(session));

_redaction?.RedactSessionInPlace(session);
var json = JsonSerializer.Serialize(session, CoreJsonContext.Default.Session);
var updatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
Comment on lines 193 to 200
public async ValueTask SaveSessionAsync(Session session, CancellationToken ct)
{
if (session is null)
throw new ArgumentNullException(nameof(session));

_redaction?.RedactSessionInPlace(session);
var encodedId = EncodeKey(session.Id);
var filePath = Path.Combine(_sessionsPath, $"{encodedId}.json");
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment thread src/OpenClaw.Payments.StripeLink/LinkCliCommandRunner.cs Fixed
Comment thread src/OpenClaw.Payments.StripeLink/LinkCliCommandRunner.cs Fixed
Comment thread src/OpenClaw.Payments.StripeLink/LinkCliCommandRunner.cs Fixed
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment thread src/OpenClaw.Payments.Core/InMemoryPaymentSecretVault.cs Fixed
Comment thread src/OpenClaw.Payments.Core/PaymentSensitiveDataRedactor.cs Fixed
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment on lines +66 to +70
foreach (var item in _entries.Where(item => item.Value.ExpiresAtUtc <= nowUtc))
{
if (_entries.TryRemove(item.Key, out var removed))
removed.Secret.Clear();
}
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
Comment thread src/OpenClaw.Tests/PaymentRuntimeTests.cs Fixed
@Telli Telli merged commit 03f1381 into main May 5, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants