Conversation
🤖 Augment PR SummarySummary: This PR implements native payments as a first-party OpenClaw.NET runtime capability (disabled by default), with a thin built-in Changes:
🤖 Was this summary useful? React with 👍 or 👎 |
| { | ||
| ct.ThrowIfCancellationRequested(); | ||
|
|
||
| var live = string.Equals(request.Environment, PaymentEnvironments.Live, StringComparison.OrdinalIgnoreCase); |
There was a problem hiding this comment.
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:28src/OpenClaw.Gateway/Endpoints/IntegrationEndpoints.cs:709src/OpenClaw.Plugins.Payment/PaymentTool.cs:73
🤖 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) |
There was a problem hiding this comment.
| Action = PaymentActions.BrowserSentinelFill, | ||
| Summary = $"Fill browser checkout field using payment handle {handleId}.", | ||
| ProviderId = "payment", | ||
| Environment = PaymentEnvironments.Live, |
There was a problem hiding this comment.
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
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| // Yield buffered text deltas | ||
| foreach (var delta in streamResult.TextDeltas) | ||
| yield return AgentStreamEvent.TextDelta(delta); | ||
| yield return AgentStreamEvent.TextDelta(_redaction.Redact(delta)); |
There was a problem hiding this comment.
|
|
||
| if (onDelta is not null && tool is IStreamingTool streamingTool) | ||
| result = await ExecuteStreamingToolCollectAsync(streamingTool, argsJson, onDelta, ct); | ||
| result = await ExecuteStreamingToolCollectAsync(streamingTool, executionArgsJson, onDelta, ct); |
There was a problem hiding this comment.
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
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
There was a problem hiding this comment.
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. |
| 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))) | ||
| }); |
| 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; |
| 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 | ||
| }; |
| 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; |
| 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 | ||
| }); |
|
|
||
| case "execute": | ||
| { | ||
| var env = environment ?? PaymentEnvironments.Live; |
| 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 | ||
| }; |
| 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 | ||
| } |
| 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(); |
| 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"); |
| foreach (var item in _entries.Where(item => item.Value.ExpiresAtUtc <= nowUtc)) | ||
| { | ||
| if (_entries.TryRemove(item.Key, out var removed)) | ||
| removed.Secret.Clear(); | ||
| } |
Summary
Implements payments as a native OpenClaw.NET runtime/security capability with a thin first-party
paymenttool and gateway-backed CLI surface.OpenClaw.Payments.Abstractions,OpenClaw.Payments.Core,OpenClaw.Payments.StripeLink, andOpenClaw.Plugins.Paymentprojects.openclaw payment ...CLI commands.link-clisetup/status adapter with safeProcessStartInfo.ArgumentListexecution and redacted process output.Security Invariants
PaymentSecretcannot be JSON-serialized.Validation
dotnet build OpenClaw.Net.slnx -v:minimaldotnet test OpenClaw.Net.slnx -v:minimal(1078 passed)