-
Notifications
You must be signed in to change notification settings - Fork 122
v1.0.41 #826
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
v1.0.41 #826
Conversation
…e structure, update service method to use new request type
…icesToBePaidResponse
feat(invoice): account for unpaid usage charges calculating total pending charges
…ect usage charges only
refactor(wallet): update wallet balance calculations in tests to reflect usage charges only
…ent status update
…top-ups via tenant-level settings
|
Warning Rate limit exceeded@hiteshshimpi-55 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 16 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
WalkthroughThis PR refactors the unpaid invoice retrieval API from a slice/decimal return pattern to a structured request/response DTO pattern, introduces auto-complete support for purchased-credit wallet transactions with invoice metadata flags, and updates wallet balance calculations to use usage charges exclusively. Changes
Sequence DiagramsequenceDiagram
actor Client
participant WalletSvc as Wallet Service
participant SettingsSvc as Settings Service
participant InvoiceSvc as Invoice Service
participant DB as Database
participant Webhook as Webhook Publisher
Client->>WalletSvc: TopUpWallet(amount, currency)
WalletSvc->>SettingsSvc: GetSettingWithDefaults(invoice_config)
SettingsSvc->>DB: Fetch invoice config
DB-->>SettingsSvc: Config with auto_complete flag
SettingsSvc-->>WalletSvc: Config response
alt auto_complete_purchased_credit_transaction enabled
WalletSvc->>InvoiceSvc: CreateInvoice(auto_completed=true, PaymentStatus=Succeeded)
InvoiceSvc->>DB: Create invoice
DB-->>InvoiceSvc: Invoice created
InvoiceSvc-->>WalletSvc: Invoice response
WalletSvc->>DB: Create wallet transaction as Completed
WalletSvc->>DB: Update wallet balance immediately
DB-->>WalletSvc: Balance updated
WalletSvc->>Webhook: Publish wallet.transaction.created
Webhook-->>WalletSvc: Published
WalletSvc-->>Client: TopUpWallet succeeds (credits available)
else auto_complete disabled
WalletSvc->>InvoiceSvc: CreateInvoice(auto_completed=false, PaymentStatus=Pending)
InvoiceSvc->>DB: Create invoice
DB-->>InvoiceSvc: Invoice created
InvoiceSvc-->>WalletSvc: Invoice response
WalletSvc->>DB: Create wallet transaction as Pending
DB-->>WalletSvc: Transaction created
WalletSvc-->>Client: TopUpWallet succeeds (credits pending)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–75 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
feat(wallet): add support for tenant-level auto-complete settings and invoice-status–triggered top-ups for purchased credit invoices
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.
Important
Looks good to me! 👍
Reviewed everything up to 77b7b1c in 1 minute and 1 seconds. Click for details.
- Reviewed
219lines of code in4files - Skipped
0files when reviewing. - Skipped posting
7draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. internal/service/wallet_test.go:1213
- Draft comment:
Repeated use of the same idempotency key ("test_topup_1") in TestDebitWithMultipleCredits may suppress duplicate operations unintentionally. Consider generating unique idempotency keys for each top‐up request. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
2. internal/service/wallet_test.go:1633
- Draft comment:
TestDebitIdempotency is currently skipped with a note on non‑idempotency. Consider adding a reference or issue ID for the known bug so tracking is easier. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
3. internal/service/wallet_test.go:1450
- Draft comment:
Validation tests in GetCustomerWallets rely on string matching for error messages. Consider checking error codes/constants to make tests more robust against message changes. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
4. internal/service/wallet_test.go:1190
- Draft comment:
In TestDebitWithExpiredCredits, the expected error is checked via string content. It’s preferable to verify using an error code so that future changes in wording do not break tests. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
5. internal/service/wallet_test.go:1631
- Draft comment:
In TestDebitWithPrioritizedCredits, adding inline comments that explain the expected credit consumption and sorting order (by priority, then nil) would improve clarity and maintainability. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
6. internal/service/wallet_test.go:851
- Draft comment:
Consider splitting lengthy test functions into smaller helper functions to improve readability and maintenance. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
7. internal/service/invoice.go:47
- Draft comment:
Typographical note: Consider renamingGetInvoicePDFUrltoGetInvoicePDFURLto maintain consistent acronyms (e.g., using 'URL' instead of 'Url'). - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
Workflow ID: wflow_nd23I7GvFT9LXfpM
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
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.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
internal/service/wallet.go (1)
1852-1959: V2 balance calculation omits unpaid invoice charges, creating an inconsistency with V1.
GetWalletBalanceV2calculatestotalPendingChargesfrom subscriptions only (line 1881–1918), whereasGetWalletBalance(V1, line 829–952) also includes unpaid invoice usage charges viaGetUnpaidInvoicesToBePaidand adds them to the total (line 921, 102). This means V2 underreports pending charges and does not setUnpaidInvoicesAmountin the response. If V2 is intended to provide accurate real-time balance calculations like V1, it should include unpaid invoice charges.internal/service/invoice.go (1)
1490-1543: Fix the semantic mismatch inGetUnpaidInvoicesToBePaid: line-item charge splits must reflect actual unpaid portionsThe function sums original
LineItem.Amountvalues for invoices withAmountRemaining > 0, but for partially paid invoices this over-counts usage and fixed charges. The response field names ("TotalUnpaidUsageCharges", "TotalUnpaidFixedCharges") imply unpaid amounts, but the implementation sums full original line items. This causes wallet balance calculations to include charges that have already been partially paid.For correct semantics, pro-rate line-item charges by the ratio
AmountRemaining / AmountDuewhen summing usage and fixed splits. Alternatively, clarify field names to "TotalUsageCharges" and "TotalFixedCharges" if full original amounts are intended, and adjust wallet balance logic accordingly.
🧹 Nitpick comments (1)
internal/api/dto/invoice.go (1)
1218-1231: Consider adding currency validation for consistency.The
Currencyfield is marked as required but theValidate()method only delegates tovalidator.ValidateRequest. Other DTOs in this file (e.g.,CreateInvoiceRequest.ToInvoice) validate currency usingtypes.ValidateCurrencyCode. Consider adding similar validation to ensure the currency is a valid ISO code.func (r *GetUnpaidInvoicesToBePaidRequest) Validate() error { if err := validator.ValidateRequest(r); err != nil { return err } + if err := types.ValidateCurrencyCode(r.Currency); err != nil { + return err + } return nil }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
internal/api/dto/invoice.go(1 hunks)internal/service/invoice.go(5 hunks)internal/service/wallet.go(7 hunks)internal/service/wallet_test.go(5 hunks)internal/types/settings.go(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
internal/api/dto/invoice.go (1)
internal/validator/validator.go (1)
ValidateRequest(35-52)
internal/types/settings.go (2)
internal/errors/builder.go (1)
NewErrorf(21-23)internal/errors/errors.go (1)
ErrValidation(16-16)
internal/service/wallet.go (8)
internal/service/settings.go (1)
NewSettingsService(29-33)internal/types/settings.go (1)
SettingKeyInvoiceConfig(14-14)ent/wallet/where.go (7)
CreditBalance(129-131)Description(119-121)ID(14-16)EnvironmentID(99-101)Balance(124-126)CustomerID(109-111)Currency(114-116)internal/domain/wallet/transaction.go (1)
Transaction(12-32)internal/types/wallet.go (1)
WalletTxReferenceTypeExternal(99-99)internal/types/payment.go (2)
PaymentStatusPending(13-13)PaymentStatusSucceeded(15-15)internal/types/webhook.go (1)
WebhookEventWalletTransactionCreated(62-62)internal/api/dto/invoice.go (1)
GetUnpaidInvoicesToBePaidRequest(1218-1224)
internal/service/invoice.go (6)
internal/api/dto/invoice.go (3)
GetUnpaidInvoicesToBePaidRequest(1218-1224)GetUnpaidInvoicesToBePaidResponse(1233-1245)InvoiceResponse(755-877)internal/types/settings.go (1)
SettingKeyInvoiceConfig(14-14)internal/types/payment.go (1)
PaymentStatusSucceeded(15-15)internal/service/wallet.go (1)
NewWalletService(97-102)internal/types/currency.go (1)
IsMatchingCurrency(82-84)internal/types/price.go (1)
PRICE_TYPE_USAGE(96-96)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: deploy
🔇 Additional comments (14)
internal/api/dto/invoice.go (1)
1233-1245: LGTM!The response DTO is well-structured with clear field documentation. The separation of
TotalUnpaidUsageChargesandTotalUnpaidFixedChargesenables the wallet service to use only usage charges for balance calculations as intended by this PR.internal/service/wallet_test.go (2)
795-798: LGTM - Test expectations correctly reflect the new usage-only balance calculation.The updated comments and expected values align with the PR's behavioral change where wallet balance now excludes unpaid invoices and only considers usage charges. The calculation
1000 - 123 = 877is correct.
1812-1816: LGTM - Entitlement test expectations are consistent with the new balance logic.All four test scenarios correctly reflect that wallet balance now only includes usage charges (not unpaid invoices). The calculations are consistent:
- With entitlements affecting usage:
1000 - 78 = 922- With disabled entitlements (baseline):
1000 - 123 = 877Also applies to: 1837-1840, 1861-1864, 1894-1897
internal/types/settings.go (3)
117-117: LGTM - Safe default for new setting.The default value of
falseforauto_complete_purchased_credit_transactionis a safe choice. This ensures existing behavior is preserved and requires explicit opt-in for the auto-complete flow.
186-233: Partial update logic is well-implemented.The approach of checking for the presence of required fields to determine whether full validation should proceed is sensible. This allows updating just
due_date_daysorauto_complete_purchased_credit_transactionwithout requiring all other fields.
212-219: LGTM - Boolean validation is consistent with existing patterns.The validation correctly checks the type and provides a clear error message if the value is not a boolean. This is consistent with the validation pattern used for
auto_cancellation_enabledelsewhere in the file.internal/service/wallet.go (6)
428-441: LGTM - Safe extraction of auto_complete setting with sensible default.The type assertion with ignored
okvalue safely defaults tofalseif the setting is missing or has an unexpected type. This preserves existing behavior and requires explicit configuration to enable auto-complete. Good debug logging is included.
446-506: LGTM - Clean conditional logic for auto-complete vs pending transactions.The implementation correctly differentiates between auto-complete and pending flows:
- Auto-complete: Transaction marked COMPLETED, balance updated immediately, credits available
- Non-auto-complete: Transaction marked PENDING, balance unchanged, zero credits available
This ensures wallet balance only reflects completed transactions.
522-557: LGTM - Invoice creation correctly reflects auto-complete state.The invoice metadata includes the
auto_completedflag for traceability, and thePaymentStatusis correctly set toSUCCEEDEDfor auto-complete orPENDINGotherwise. This maintains consistency between the wallet transaction and invoice states.
658-669: LGTM - Improved idempotency handling for auto-completed transactions.The method now correctly handles the case where a transaction was auto-completed by returning success (nil) instead of an error. Downgrading to
Debuglog level is appropriate since this is expected behavior, not an anomaly. This prevents issues if payment webhooks fire for already-completed transactions.
920-951: Verify: Balance calculation now uses usage charges only from unpaid invoices.The balance calculation now uses
resp.TotalUnpaidUsageChargesinstead of the total unpaid amount. This means fixed charges from unpaid invoices are excluded from the wallet's pending charges calculation.Please confirm this is the intended behavior - that the wallet balance should only reflect usage-based charges, not fixed subscription fees from unpaid invoices.
588-596: LGTM - Webhook publication timing is correct.The webhook is published immediately only for auto-completed transactions. For pending transactions, the webhook will be published when
completePurchasedCreditTransactionis called after payment succeeds (line 739). This prevents duplicate events and ensures webhooks fire at the appropriate time.internal/service/invoice.go (2)
174-197: UsingGetSettingWithDefaultsfor invoice_config is appropriateReplacing direct config fetch with
settingsService.GetSettingWithDefaults(ctx, types.SettingKeyInvoiceConfig)beforedto.ConvertToInvoiceConfigis a good move: it centralizes defaults and keeps the conversion logic in one place. Behavior looks correct as long as the defaultinvoice_configshape matches whatConvertToInvoiceConfigexpects, which seems to be the intent of the settings refactor.
34-55: InvoiceService interface:GetUnpaidInvoicesToBePaidsignature change is properly propagatedThe signature change to
GetUnpaidInvoicesToBePaid(ctx context.Context, req dto.GetUnpaidInvoicesToBePaidRequest) (*dto.GetUnpaidInvoicesToBePaidResponse, error)is correctly implemented across all locations. The call site inwallet.go(line 921) and all implementations ininvoice.goare consistent with the new signature, and the corresponding DTOs are properly defined with validation methods. No orphaned references to an old signature were found.
| // If invoice is for a purchased credit (has wallet_transaction_id in metadata) and the payment status transitioned to succeeded, | ||
| // complete the wallet transaction to credit the wallet | ||
| if status == types.PaymentStatusSucceeded { | ||
| // Check if this invoice is for a purchased credit (has wallet_transaction_id in metadata) | ||
| if inv.Metadata != nil { | ||
| if walletTransactionID, ok := inv.Metadata["wallet_transaction_id"]; ok && walletTransactionID != "" { | ||
| walletService := NewWalletService(s.ServiceParams) | ||
| if err := walletService.CompletePurchasedCreditTransactionWithRetry(ctx, walletTransactionID); err != nil { | ||
| s.Logger.Errorw("failed to complete purchased credit transaction", | ||
| "error", err, | ||
| "invoice_id", inv.ID, | ||
| "wallet_transaction_id", walletTransactionID, | ||
| ) | ||
| } else { | ||
| s.Logger.Debugw("successfully completed purchased credit transaction", | ||
| "invoice_id", inv.ID, | ||
| "wallet_transaction_id", walletTransactionID, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } |
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.
Wallet completion in UpdatePaymentStatus: consider Overpaid + deduplication
Adding wallet completion for purchased-credit invoices to UpdatePaymentStatus makes manual status transitions consistent with reconciliation and avoids stranded wallet transactions. A couple of details to consider:
- You only complete the purchased-credit wallet transaction when
status == types.PaymentStatusSucceeded. InReconcilePaymentStatusyou treat bothSUCCEEDEDandOVERPAIDas “fully paid” for wallet completion. If someone manually moves a purchased-credit invoice toOVERPAID, the wallet would not be credited here. If you want parity with reconciliation, consider:
- if status == types.PaymentStatusSucceeded {
+ if status == types.PaymentStatusSucceeded || status == types.PaymentStatusOverpaid {- This block duplicates the logic already present in
ReconcilePaymentStatus(down to logging keys). Extracting a small helper, e.g.completePurchasedCreditIfApplicable(ctx, inv, status), would reduce drift risk between the two paths.
Functionally this is otherwise sound: the guard against existing payment records above prevents this manual path from overlapping with the reconciliation path.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // If invoice is for a purchased credit (has wallet_transaction_id in metadata) and the payment status transitioned to succeeded, | |
| // complete the wallet transaction to credit the wallet | |
| if status == types.PaymentStatusSucceeded { | |
| // Check if this invoice is for a purchased credit (has wallet_transaction_id in metadata) | |
| if inv.Metadata != nil { | |
| if walletTransactionID, ok := inv.Metadata["wallet_transaction_id"]; ok && walletTransactionID != "" { | |
| walletService := NewWalletService(s.ServiceParams) | |
| if err := walletService.CompletePurchasedCreditTransactionWithRetry(ctx, walletTransactionID); err != nil { | |
| s.Logger.Errorw("failed to complete purchased credit transaction", | |
| "error", err, | |
| "invoice_id", inv.ID, | |
| "wallet_transaction_id", walletTransactionID, | |
| ) | |
| } else { | |
| s.Logger.Debugw("successfully completed purchased credit transaction", | |
| "invoice_id", inv.ID, | |
| "wallet_transaction_id", walletTransactionID, | |
| ) | |
| } | |
| } | |
| } | |
| } | |
| // If invoice is for a purchased credit (has wallet_transaction_id in metadata) and the payment status transitioned to succeeded, | |
| // complete the wallet transaction to credit the wallet | |
| if status == types.PaymentStatusSucceeded || status == types.PaymentStatusOverpaid { | |
| // Check if this invoice is for a purchased credit (has wallet_transaction_id in metadata) | |
| if inv.Metadata != nil { | |
| if walletTransactionID, ok := inv.Metadata["wallet_transaction_id"]; ok && walletTransactionID != "" { | |
| walletService := NewWalletService(s.ServiceParams) | |
| if err := walletService.CompletePurchasedCreditTransactionWithRetry(ctx, walletTransactionID); err != nil { | |
| s.Logger.Errorw("failed to complete purchased credit transaction", | |
| "error", err, | |
| "invoice_id", inv.ID, | |
| "wallet_transaction_id", walletTransactionID, | |
| ) | |
| } else { | |
| s.Logger.Debugw("successfully completed purchased credit transaction", | |
| "invoice_id", inv.ID, | |
| "wallet_transaction_id", walletTransactionID, | |
| ) | |
| } | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In internal/service/invoice.go around lines 1142 to 1163, the wallet completion
code in UpdatePaymentStatus only runs for PaymentStatusSucceeded and duplicates
logic from ReconcilePaymentStatus; change it to treat OVERPAID the same as
SUCCEEDED (i.e. run when status == types.PaymentStatusSucceeded || status ==
types.PaymentStatusOverpaid) and refactor the duplicated logic into a small
helper function (e.g., completePurchasedCreditIfApplicable(ctx, inv, status) or
completePurchasedCreditIfApplicable(ctx, inv)) that checks inv.Metadata for
"wallet_transaction_id", calls the wallet service
CompletePurchasedCreditTransactionWithRetry, and performs the same logging so
both UpdatePaymentStatus and ReconcilePaymentStatus call that helper to avoid
drift.
feat(wallet): resolve auto-completed purchased credit invoice line items' value
Important
Enhance invoice and wallet services by updating unpaid invoice handling and wallet balance calculations.
GetUnpaidInvoicesToBePaidininvoice.gonow returnsGetUnpaidInvoicesToBePaidResponse, including unpaid usage and fixed charges.GetWalletBalanceinwallet.goupdated to useTotalUnpaidUsageChargesfromGetUnpaidInvoicesToBePaidResponse.GetUnpaidInvoicesToBePaidRequestandGetUnpaidInvoicesToBePaidResponseindto/invoice.go.TestGetWalletBalanceandTestGetWalletBalanceWithEntitlementsinwallet_test.goto reflect changes in balance calculation logic.This description was created by
for 77b7b1c. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit
Release Notes
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.