Skip to main content
Fees Engine controls how you calculate, apply, and track fees. Production use demands careful configuration and operational discipline, because accuracy and auditability are non-negotiable. These recommendations complement the Fees Engine overview and the calculation mechanics guide.

1. Design fee packages with clear naming and segmentation


Fee packages are the foundation of your fee logic. A well-organized package structure makes it easier to maintain, debug, and audit your fee configuration over time.
  • Use descriptive names that reflect the business context (e.g., “pix-transfer-standard”, “wire-premium-segment”).
  • Segment by product and customer group using segmentId. This allows you to apply different fee rules to different customer tiers without creating conflicting packages.
  • Keep packages focused. A package that tries to cover too many scenarios becomes hard to test and maintain. Prefer multiple focused packages over one that does everything.
  • Document your package structure internally. As your package count grows, a clear reference of which package applies where prevents misconfiguration.

2. Set fee priorities carefully


When a package contains multiple fees, the priority field determines the order of execution. Getting this wrong can produce incorrect calculations.
  • Priority 1 must always use referenceAmount: originalAmount. The engine enforces this.
  • Fees with isDeductibleFrom: true must also use referenceAmount: originalAmount. Deductible fees then always apply to the full transaction value.
  • Priority must be unique within a package. The engine rejects duplicate priorities.
  • Think about fee dependencies. If one fee adjusts the transaction value and another fee should be calculated on the adjusted value, use referenceAmount: afterFeesAmount with a higher priority number. If the second fee should reference the original value, use originalAmount.
When in doubt, start with a simple configuration (one or two fees per package) and validate the results using the estimate endpoint before adding complexity.

3. Always estimate before applying fees in production


Fees Engine provides an estimate endpoint that lets you preview fee calculations without writing anything to the ledger. Use estimation to:
  • Validate new packages before activating them. Confirm that the calculated values match your expected results across different transaction amounts.
  • Test edge cases: zero-amount transactions, boundary values at minimumAmount and maximumAmount thresholds, and exempted accounts.
  • Preview fees for users. If your product shows fees before confirmation, use the estimate endpoint to provide accurate previews.
  • Debug unexpected results. If a calculated fee doesn’t match expectations, estimate the same transaction with a specific packageId to isolate the issue.
The calculate endpoint automatically selects the best matching package. The estimate endpoint requires a specific packageId, giving you full control over which package to test.

4. Manage exemptions explicitly


Fees Engine supports two types of exemptions: by transaction amount range and by account.
  • Amount ranges (minimumAmount, maximumAmount): Define the transaction value window in which fees apply. Transactions outside this range are exempt. Use this for promotional thresholds or tiered pricing.
  • Waived accounts (waivedAccounts): Specific accounts exempt from fees within a package. Use this for internal accounts, employee accounts, or partnership arrangements.
Best practices for exemptions:
  • Keep waived account lists short and reviewed. Large lists become hard to audit. Periodically review which accounts are exempted and why.
  • Document the business reason for each exemption in your internal records.
  • Test exemption boundaries. If your range is R0300,makesuretransactionsatexactlyR 0–300, make sure transactions at exactly R 300 and R$ 301 behave as expected.

5. Enable and tune caching for performance


Fees Engine caches fee packages in memory to reduce database queries during high traffic. Configure caching through environment variables:
  • PACKAGE_CACHE_ENABLED (default: true): Enables or disables the package cache.
  • PACKAGE_CACHE_TTL_SECONDS (default: 180): Time-to-live in seconds before the engine refreshes cached packages from the database.
Recommendations:
  • Keep caching enabled in production. It significantly reduces latency for high-volume transaction processing.
  • Adjust TTL based on your change frequency. If you update packages frequently, use a shorter TTL (e.g., 60–120 seconds). If packages are stable, the default 180 seconds is appropriate.
  • Be aware of cache delay. After you update a package, the change may take up to the configured TTL to propagate. If you need immediate effect, restart the service or temporarily reduce the TTL.

6. Use correct numeric values


Express all financial values in Fees Engine as strings using the numeric type. This prevents floating-point precision errors that are common with decimal arithmetic.
  • Always send values as strings, even whole numbers (e.g., "100" not 100).
  • Never use floating-point types for monetary calculations in your integration layer.
  • Note that the engine automatically adjusts fee splits with repeating decimals (e.g., R$ 10 divided by 3 accounts) to keep ledger totals exact.
Fees Engine requires Midaz v3.x.x or later. The v2.x.x amount + scale format is not compatible. Upgrade Midaz before deploying Fees Engine.

7. Use soft delete for auditability


When you delete a fee package, Fees Engine marks it with a deletedAt timestamp rather than removing it from the database. This preserves the audit trail for historical transactions that referenced that package.
  • Don’t rely on hard deletion for fee packages in production. Historical transactions may reference deleted packages for reconciliation.
  • Periodically review deleted packages if your database grows significantly. Archiving strategies can help manage storage without losing audit capability.

8. Monitor the Fees Engine in production


Fees Engine supports OpenTelemetry for traces and metrics. Enable it to gain visibility into fee calculation performance and behavior.
In production:
  • Monitor health endpoints. Fees Engine exposes /health for readiness and liveness checks (default port: 4002).
  • Set up alerts for sustained high latency on fee calculations, which may indicate database contention or cache misconfiguration.
  • Monitor MongoDB connection pool usage, disk space, and replication health. The default MONGO_MAX_POOL_SIZE is 100.
  • Review pod resource usage against your traffic patterns and autoscaling behavior.

9. Keep versions compatible


Before upgrading Fees Engine:
  • Check the version compatibility table to confirm compatibility with your Midaz Core version.
  • Always upgrade Midaz Core first, then Fees Engine.
  • Back up your MongoDB data and Helm values before any major upgrade.
  • Test the upgrade in a staging environment before applying it to production.
For upgrade procedures, see the Helm upgrade guide.

10. Review the security recommendations


Fees Engine processes financial data and integrates with Midaz ledger operations. Make sure your deployment follows the platform-wide Security recommendations, which cover:
  • Network segmentation and Zero Trust Architecture
  • Secret management and rotation (including LICENSE_KEY and database credentials)
  • TLS 1.2+ enforcement for all communications
  • RBAC configuration via Access Manager
  • Patch management and vulnerability scanning

11. Design billing packages with clear scope


Each billing package should represent a single, well-defined charge. Avoid packing unrelated pricing into one package.
  • Separate packages per transaction route. A package for Pix billing and a package for boleto billing are clearer than one package that tries to handle both.
  • Use descriptive labels that include the billing type and target: “Pix Send Monthly Billing — Standard Tier” is better than “Billing Package 1”.
  • One accountTarget type per maintenance package. You cannot combine segmentId, portfolioId, and aliases in the same package. If you need different targets, create separate packages — a single /billing/calculate call evaluates all active packages.

12. Write commercial terms against route volume


Volume calculation counts transactions per transaction route across the whole ledger. The free quota, the tiers, and the discount tiers all apply to that route-level total.
  • State thresholds as route volume. “The first 100 transactions on the pix-send route are free” maps directly onto a package. Write the contract in the same terms the engine bills in.
  • Split routes to split counts. One package per transaction route keeps each flow on its own quota, tiers, and discounts.

13. Plan free quotas and discount tiers carefully


Fees Engine evaluates free quotas and discounts in a specific order:
  1. The engine subtracts the free quota from the total count to get the billable count.
  2. The engine prices the billable count — for tiered packages it charges every billable unit at the single matching tier’s rate (volume pricing, not graduated).
  3. The engine applies one discount tier to the gross amount, chosen against the total count (before the free quota was subtracted).
Design considerations:
  • Free quotas reset each billing period. Set the value based on your commercial agreement per period (monthly, weekly, or daily), not lifetime.
  • Tiers are volume brackets, not slices. A billable count of 1,750 against a 501–2,000 tier prices all 1,750 units at that tier’s rate. Crossing a bracket boundary changes the rate for the whole volume, so tier boundaries can move the bill sharply.
  • Cover every positive billable count. If the billable count is positive and no tier’s range contains it, the calculation fails for that package. Leave the top tier’s upper bound open. Start the first tier at 1 — a billable count of zero produces a zero amount and an empty payload without a tier lookup.
  • Discount tiers are cumulative thresholds, not ranges, and only one applies: the highest minQuantity the total count reaches. If you define discounts at 200 and 400 transactions, a client with 500 transactions gets the 400+ discount — not both.
  • Watch the two different counts. Pricing uses the billable count (after free quota); the discount uses the total count (before it).
Use test calculations with known transaction counts to validate your tier and discount configuration before enabling the package in production.

14. Size maintenance account targets appropriately


Maintenance packages support three target types with different scale profiles: Choose the target type that matches your operational scale. If you find yourself listing hundreds of aliases, migrate to a segment or portfolio in Midaz instead.

15. Handle billing failures with re-execution


Billing calculation follows an all-or-nothing policy. If any package fails, the engine returns no results.
  • Build retry logic into your orchestrator. The calculation is stateless — re-executing for the same period produces the same results.
  • Check error responses for the specific package and resource that failed. Common causes: invalid ledgerId, unreachable Midaz API, or disabled billing packages.
  • Separate volume and maintenance calculations if one type consistently succeeds while the other fails. Call /billing/calculate with "type": "volume" and "type": "maintenance" independently to isolate failures.