-
-
Notifications
You must be signed in to change notification settings - Fork 88
RG-T51 Business Ops and Invoicing #512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| namespace Resgrid.Config | ||
| { | ||
| /// <summary> | ||
| /// Department-level monthly Business Operations add-on prices and provider product ids (Workforce & Business | ||
| /// Operations plan, decision 42; PlanAddonTypes.BusinessOperations = 4). Provider checkout lives in the Billing API | ||
| /// (int-CommonApis), cloned from Readiness Pro; Core only proxies it. Environment keys: | ||
| /// RESGRID:BusinessOperationsAddonConfig:StripeProductId, :StripeTestProductId, :StripePriceId, :StripeTestPriceId, | ||
| /// :PaddleProductId, :PaddleTestProductId, :StripeMonthlyAmount, :PaddleMonthlyAmount. The Paddle price id lives in | ||
| /// PaymentProviderConfig.PaddleBusinessOperationsAddon (the Readiness Pro convention). Live ids set 2026-09-18: | ||
| /// Stripe product prod_VHnlBsvKsSpqeP / price price_0UHEA6qJFDZJcnkVnj0ZaAFw (USD 250/month), Paddle product | ||
| /// pro_01m2vrcv37k2pnqseb22d8r244 / price pri_01m2vrdycmx8kfhys5xxcjgqnx (EUR 295/month); test-mode ids are still empty. | ||
| /// </summary> | ||
| public static class BusinessOperationsAddonConfig | ||
| { | ||
| /// <summary>Fixed PlanAddons row id so every data center's catalog row matches (M0211; the PTT/ADP/Readiness Pro convention).</summary> | ||
| public const string PlanAddonId = "8c2f0d6e-5b1a-4f2e-9d3c-7a6b5e4d3c2b"; | ||
|
|
||
| public static string StripeProductId = "prod_VHnlBsvKsSpqeP"; | ||
| public static string StripeTestProductId = ""; | ||
| public static string StripePriceId = "price_0UHEA6qJFDZJcnkVnj0ZaAFw"; | ||
| public static string StripeTestPriceId = ""; | ||
| public static string PaddleProductId = "pro_01m2vrcv37k2pnqseb22d8r244"; | ||
| public static string PaddleTestProductId = ""; | ||
|
|
||
| /// <summary>USD per month through Stripe (US cluster). Set 2026-09-18.</summary> | ||
| public static decimal StripeMonthlyAmount = 250m; | ||
|
|
||
| /// <summary>EUR per month through Paddle (EU cluster). Set 2026-09-18.</summary> | ||
| public static decimal PaddleMonthlyAmount = 295m; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| namespace Resgrid.Config | ||
| { | ||
| /// <summary> | ||
| /// Department-connected Stripe accounts for collecting payments on Resgrid invoices (Workforce & Business | ||
| /// Operations plan, Phase B2). Off unless this switch is on; the operator feature flag Payments.StripeConnect is | ||
| /// the second, per-cluster lock, so a cluster that does not offer payment collection leaves both off. These are | ||
| /// the platform's own Connect credentials and are deliberately separate from PaymentProviderConfig, which serves | ||
| /// Resgrid's SaaS subscription billing even when both point at the same Stripe account. Environment keys: | ||
| /// RESGRID:PaymentConnectConfig:Enabled, :CredentialPassphrase, :PublicBaseUrl, :StripeClientId, :StripeSecretKey, | ||
| /// :StripeConnectWebhookSecret, :StripeLiveMode, :PayLinkTokenTtlDays, :RequestReconcileAfterMinutes, | ||
| /// :EventRetentionDays, :PayPageRateLimitPerMinute, :WebhookStaleAfterHours, :WebhookEndpointProbeEnabled. | ||
| /// </summary> | ||
| public static class PaymentConnectConfig | ||
| { | ||
| /// <summary>Process-level master switch. Off means no provider is registered, no connection can be made and the webhook endpoint answers 503.</summary> | ||
| public static bool Enabled = false; | ||
|
|
||
| /// <summary> | ||
| /// Passphrase for the symmetric encryption of stored provider tokens. Stripe Connect stores no per-account token, | ||
| /// so it is unused in v1, but a later token-holding provider (Square, PayPal, Authorize.net) cannot be enabled without it. | ||
| /// </summary> | ||
| public static string CredentialPassphrase = ""; | ||
|
|
||
| /// <summary>Public origin of this cluster (for example https://api.resgrid.com). The pay page, the OAuth redirect and the webhook URL are built from this and nothing else.</summary> | ||
| public static string PublicBaseUrl = ""; | ||
|
|
||
| /// <summary>Connect OAuth client id of the platform (ca_...). Live and sandbox ids differ.</summary> | ||
| public static string StripeClientId = ""; | ||
|
|
||
| /// <summary>The platform's secret key used for the OAuth token exchange and for every call made as a connected account.</summary> | ||
| public static string StripeSecretKey = ""; | ||
|
|
||
| /// <summary>Signing secret of the Connect-scoped webhook endpoint (events from connected accounts). Never the SaaS endpoint's secret.</summary> | ||
| public static string StripeConnectWebhookSecret = ""; | ||
|
|
||
| /// <summary>Whether the keys above are live-mode keys. A webhook whose livemode does not match is rejected.</summary> | ||
| public static bool StripeLiveMode = false; | ||
|
|
||
| /// <summary>How long an e-mailed or printed pay-page link stays valid.</summary> | ||
| public static int PayLinkTokenTtlDays = 30; | ||
|
|
||
| /// <summary>Age after which an open payment request is polled at Stripe by the invoice maintenance worker instead of waiting for a webhook.</summary> | ||
| public static int RequestReconcileAfterMinutes = 10; | ||
|
|
||
| /// <summary>Days raw webhook bodies are kept for forensics before the worker purges them.</summary> | ||
| public static int EventRetentionDays = 90; | ||
|
|
||
| /// <summary>Per-IP request limit on the anonymous pay page.</summary> | ||
| public static int PayPageRateLimitPerMinute = 20; | ||
|
|
||
| /// <summary>Hours without any webhook event, while there was payment activity in the last seven days, before the health check reports the webhook as stale.</summary> | ||
| public static int WebhookStaleAfterHours = 24; | ||
|
|
||
| /// <summary>Whether the health check may ask Stripe whether an enabled webhook endpoint exists at this cluster's webhook URL (one call per process per fifteen minutes).</summary> | ||
| public static bool WebhookEndpointProbeEnabled = true; | ||
|
|
||
| /// <summary>Route of the Connect webhook receiver on the Web.Services host. A constant, so the config processor never overwrites it.</summary> | ||
| public const string WebhookPath = "/api/PaymentWebhooks/stripe"; | ||
|
|
||
| /// <summary>The webhook URL Stripe must be configured with for this cluster, or an empty string when PublicBaseUrl is not set.</summary> | ||
| public static string GetWebhookUrl() | ||
| { | ||
| if (string.IsNullOrWhiteSpace(PublicBaseUrl)) | ||
| return string.Empty; | ||
|
|
||
| return PublicBaseUrl.TrimEnd('/') + WebhookPath; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sensitive identifier exposure in
Core/Resgrid.Config/BusinessOperationsAddonConfig.csandCore/Resgrid.Services/Invoicing/InvoicingService.cs:875-884: source comments embed live provider identifiers such asprod_VHnlBsvKsSpqePandprice_0UHEA6qJFDZJcnkVnj0ZaAFw. Remove concrete IDs from comments and reference secured configuration or audit records instead.Kody rule violation: Emit tamper-evident audit logs with required fields
Prompt for LLM
Talk to Kody by mentioning @kody
Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.