Add idempotent verify with serializable transaction and wallet-history check
to stop concurrent callback/race from charging the wallet twice.
Co-authored-by: Cursor <cursoragent@cursor.com>
- commission.proto: add BulkCreditPayouts rpc + request/response messages; bump version to 0.0.198
- BulkCreditPayoutsCommand + Handler: load all Pending payouts for given week, credit UserWallet.Balance for each user, create UserWalletHistory + CommissionPayoutHistory records, set Status=Paid + PaidAt
- CommissionService: wire up BulkCreditPayouts gRPC method
- CommissionProfile: add Mapster mappings for request/result ↔ proto types
Previously ProcessUserPayouts created records with Status=Pending but never credited wallets, making CustomerWithdrawBalance unreachable (it requires Status==Paid).
Co-authored-by: Cursor <cursoragent@cursor.com>
- UserOrderService.SubmitShopBuyOrder: inject IInventoryService; validate
available stock per item before any financial operation; call ConfirmSaleAsync
and increment Product.SaleCount after order is persisted.
- CancelOrderByAdminCommandHandler: inject IInventoryService; include
FactorDetails in query; call ProcessReturnAsync for each item so stock
is restored when an order is cancelled by admin.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Adjust MagicWalletEntryThreshold from 1,000,000 to 100,000
- Update MagicWalletDefaultMaxDeposit from 1,000,000,000 to 100,000,000
- Change MagicWalletDefaultMaxCredit from 2,500,000,000 to 250,000,000
- Modify comments in UserOrderService to reflect updated threshold values
- Refactor ActivateClubMembershipCommandHandler to use UserPackagePurchases instead of UserOrders for package activation.
- Modify PlaceOrderCommandHandler to redirect payment callbacks to the front office.
- Update ChargeDiscountWalletCommandHandler and ChargeMagicWalletCommandHandler to direct payment callbacks to the front office.
- Remove PaymentCallbackController and integrate payment verification directly into DiscountOrderService and UserWalletService.
- Add CustomerVerifyDiscountOrderPayment RPC to DiscountOrderService for verifying discount order payments.
- Implement VerifyMagicCharge and VerifyDiscountCharge methods in UserWalletService for wallet charge verifications.
- Update appsettings.json to use local URLs for development.
- Remove appsettings.Development.json as it is no longer needed.
- Comment out history tracking methods in ClubMembershipCycle and Package classes.
- Update PackageService to automatically activate club membership after successful payment verification.
- Adjust UserService to generate JWT tokens with user details.
- PackageService: build callback URL from FrontOfficeBaseUrl config instead of request.CallbackUrl
- TransactionsService: same fix — callback URL from config
- Prevents potential open redirect vulnerability (user could send arbitrary URL)
All callback URLs now come from appsettings FrontOfficeBaseUrl or CmsBaseUrl.
- PackageService.CustomerVerifyPackagePurchase: look up PaymentTransaction.Amount and pass to 3-arg overload
- TransactionsService.CustomerPaymentVerification: same fix
- VerifyDiscountWalletChargeCommandHandler: look up amount from PaymentTransaction
- VerifyPackagePurchaseCommandHandler: fix copy-paste bug (Authority as verificationToken) + add amount
- IPaymentGatewayService: throw NotImplementedException in default 3-arg impl to prevent silent amount=0
- MockPaymentGatewayService & DayaPaymentService: add 3-arg overload for compatibility
Root cause: ZarinPal requires the exact amount in verify request.
The 2-arg overload was sending amount=0 which caused Code=-1 (تأیید تراکنش ناموفق).
F2: اضافه شدن packageName به SmsTemplates، IUserNotificationService، UserNotificationService
- DayaLoan، ClubActivated، CommissionDeposited حالا نام پکیج را نشان میدهند
F4: اضافه شدن ستون پکیج به CSV خروجیها
- commission.proto: package_name در WithdrawalRequestModel
- manualpayment.proto: package_name در ManualPaymentModel
- کوئریهندلرها UserPackagePurchases لوکاپ اضافه شد
F6: مقادیر داینامیک کیفپول جادویی از پکیج
- userwallet.proto: magic_multiplier + magic_max_credit
- UserWalletService: پاپیولیت فیلدهای جدید + فالبک به SystemConstants
F7: ولیدیتورها از SystemConstants استفاده میکنند
- WalletMaxSafeAmount (10B ریال) به عنوان حصار ایمنی
- MagicWalletMinCharge، DiscountWalletMinCharge ثابتهای مرکزی
- سقف واقعی per-package در هندلرها اعمال میشود
Proto: v0.0.189
- CustomerPurchasePackage: append orderId to callback URL before sending to gateway
- Ensures PaymentCallback page receives orderId via query string for verification
- Mark PurchaseGoldenPackage/VerifyGoldenPackagePurchase RPCs as deprecated
- Mark InitiateBasePackagePayment/VerifyBasePackagePayment RPCs as deprecated
- Remove deprecated SystemConstants from GetAllAsDict/GetAllWithDescriptions helpers
- Add MagicWallet per-package values to ConfigurationService (Multiplier, MaxDeposit, MaxCredit)
- All deprecated constants have zero active code usages — safe dead code
- ActivateClubMembership: Load Package, set First/Last fields, PackageId on Cycle+Pool
- AcceptClubMembershipContract: Load Package, set First/Last fields, PackageId on Pool
- CalculateWeeklyBalances: Load base package, use package.MaxBalancesPerLeg/MaxNetworkLevel, set PackageId on NetworkWeeklyBalance
- ProcessUserPayouts: Load base package, use package.MaxNetworkLevel, set PackageId on UserCommissionPayout
- CheckAndProcessDayaLoans: Load base package (IsBasePackage), replace DayaLoanAmount→package.Price, fix hardcoded p.Id==4
- CreateManualPayment: Load base package, replace BasePackageAmount→package.Price, use DiscountMultiplier
- InitiateBasePackagePayment: Remove hardcoded BasePackageId=4, load from IsBasePackage, use package.Price/Id
- VerifyBasePackagePayment: Load Package from order.PackageId, use package.Price+DiscountMultiplier
- ChargeMagicWallet: Load user's package from current cycle, use package.MagicWalletMaxDeposit
- VerifyMagicWalletCharge: Load user's package, use package.MagicWalletMultiplier/MagicWalletMaxDeposit
- OrmCommissionCalculationStrategy: Load base package in both methods, set PackageId on Balance+Payout
- ConfigurationService: Inject IApplicationDbContext, read package values from DB instead of SystemConstants
- UserOrderService: Load user's package for EXIT trigger MagicWalletMaxDeposit check
- UserWalletService: Load user's package for GetMagicWalletStatus display
All 9 deprecated SystemConstants now have ZERO usages outside SystemConstants.cs
Build: 0 errors, 521 warnings (all pre-existing)
B6: PackagePurchaseMethod was NOT reset when exiting Magic Mode.
This caused Guards G1-G3 to permanently block re-purchase.
Changes:
- user.PackagePurchaseMethod = None (allows re-purchase via G1-G3)
- currentCycle.IsCurrentCycle = false (ready for new cycle)
Note: membership.IsActive is intentionally preserved (Q19 — contract is one-time).
Features are preserved — DIFF will be applied on next purchase (Q20).
- userwallet.proto: added string user_name = 5 to GetAllUserWalletByFilterResponseModel
- UserWalletService: enriches response with user names from Users table
- Proto version bumped to 0.0.183
- ActivateClubMembershipCommandHandler: block activation in Magic mode
- CheckAndProcessDayaLoansCommandHandler: reject Daya after first cycle
- userwallet.proto: add purchase_cycle_count to GetMagicWalletStatusResponse
- UserWalletService: query ClubMembershipCycles count for cycle info
- EF Migration u21: ClubMembershipCycle table + UserWallet Magic fields
- Added ClubMembershipCycle entity and DbSet to IApplicationDbContext.
- Updated VAT rate from 9% to 10% in VatCalculator and related areas.
- Introduced Magic Wallet settings in SystemConstants.
- Enhanced UserWallet entity to support Magic Wallet features.
- Updated TransactionType enum to include Magic Wallet transactions.
- Configured UserWallet to handle Magic Wallet properties in ApplicationDbContext.
- Implemented OrmCommissionCalculationStrategy to exclude Magic Wallet users from commission calculations.
- Added Protobuf definitions for Magic Wallet methods and responses.
- Created PaymentCallbackController endpoint for handling Magic Wallet charge callbacks.
- Updated UserOrderService to manage Magic Wallet state transitions.
- Developed UserWalletService to support Magic Wallet operations.
- Created ChargeMagicWalletCommand and its handler for initiating Magic Wallet charges.
- Implemented VerifyMagicWalletChargeCommand and handler for payment verification.
- Added validation for ChargeMagicWalletCommand.
- Established ClubMembershipCycle configuration for EF Core.
- Introduced WalletMode enum to differentiate between Normal and Magic modes.
- Added MapDeliveryStatus() to correctly map Domain→Proto enum values
- Domain Pending(1) was incorrectly cast to Proto PROCESSING(1) instead of PROCESSING
- Set DeliveryStatus=Cancelled when payment fails (CompleteOrderPaymentCommandHandler)
- Set DeliveryStatus=Cancelled when order expires (ExpirePendingOrdersService)
- Added payment_status field to GetOrderByIdResponse and OrderSummaryDto in proto
- Proto version bumped to 0.0.179
- Added PaymentStatus mapping in DiscountOrderService gRPC responses
- Created ExpirePendingOrdersService: expires pending orders after 30 min, releases inventory
- Registered background service in ConfigureServices
- GetOrderById and GetUserOrders were using Mapster auto-mapping which
could not map 'PaymentStatus' (enum: Success=0) to 'payment_completed' (bool)
- Replaced with manual mapping: PaymentStatus == Success → true
- Also maps DeliveryStatus and all other fields correctly
- Fixed FrontOfficeBaseUrl and CmsBaseUrl to use HTTP for local dev
Payment Gateway:
- Add PYMSPaymentService: IPaymentGatewayService via gRPC to PYMS microservice
- Add ZarinPalPaymentService: direct ZarinPal integration (backup)
- Register 'pyms' payment provider in DI ConfigureServices
- Add PYMS proto files (pyms_transaction.proto, pyms_public_messages.proto)
- Fix VerifyDiscountWalletCharge: pass 'OK' as status instead of Authority
- Update appsettings: PaymentProvider=pyms, sandbox mode, merchant ID
Blog System:
- Add BlogCategory, BlogPost, BlogPostImage entities and CQRS
- Add proto files and gRPC services for blog management
- Add Mapster profiles for blog responses
Content Management:
- Add SitePage entity and CQRS for static pages
- Add proto and gRPC service for site pages
Image/File Management:
- Add LocalFileManager with disk storage + base64 serving + FMS fallback
- Add ImagePathResolverInterceptor for gRPC responses
- Add ImageResolverService for explicit image resolution
- Add UploadsController for public file serving with FMS fallback
- Add PaymentCallbackController for discount order payment callbacks
Database:
- Add blog and content entity migrations
- Remove ImagePath MaxLength constraints
- Remove old FileManagementService (replaced by LocalFileManager)
- Add RequiresPermissionAttribute for gRPC method access control.
- Create IFileManagementService interface for file upload and management.
- Implement AddProductImageCommand and handler for adding product images.
- Implement CreateNewProductsCommand and handler for creating new products with image uploads.
- Implement DeleteProductsCommand and handler for deleting products and their associations.
- Implement RemoveProductImageCommand and handler for removing product images from galleries.
- Implement UpdateProductsCommand and handler for updating product details and images.
- Create GetProductGalleryQuery and handler for retrieving product galleries.
- Implement PermissionService for role-based access control using JWT claims.
- Implement FileManagementService for handling file uploads and image optimization.
- Define gRPC service and messages for file management in fms.proto.
- Add FluentValidation for request validation in various commands.
- Create PermissionInterceptor for enforcing permissions on gRPC methods.
- Added CurrentDiscountBalance and ChangeDiscountValue properties to GetCustomerWalletChangeLogResponseDto.
- Updated userwallet.proto to include current_discount_balance and change_discount_value fields.
- Enhanced CommissionProfile mapping to support GetMyCommissionPayouts requests and responses.
- Implemented GetMyCommissionPayouts query and handler to retrieve user commission payouts.
- Added validation for GetMyCommissionPayouts query.
- Updated UserOrderService to handle user orders, including VAT calculations and wallet transactions.
- Enhanced UserWalletService to include new properties in wallet change log responses.
- Add GetCustomerProfileResponseDto for retrieving customer profile information.
- Create GetCustomerReferralsQuery and GetCustomerReferralsQueryHandler to fetch customer referrals with pagination and filtering options.
- Introduce GetCustomerReferralsResponseDto to structure the response for customer referrals.
- Implement GetCustomerSettingsQuery and GetCustomerSettingsQueryHandler to retrieve user settings.
- Add GetCustomerOrder and GetCustomerOrderQueryHandler for fetching specific customer orders.
- Create GetCustomerOrderHistoryQuery and GetCustomerOrderHistoryQueryHandler to retrieve order history with filtering options.
- Implement GetCustomerOrdersQuery and GetCustomerOrdersQueryHandler for fetching multiple customer orders with filters.
- Add GetCustomerWalletChangeLogQuery and GetCustomerWalletChangeLogQueryHandler for retrieving wallet change logs.
- Implement GetCustomerWithdrawalSettingsQuery and GetCustomerWithdrawalSettingsQueryHandler for fetching withdrawal settings.
- Create GetCustomerWithdrawalsQuery and GetCustomerWithdrawalsQueryHandler to retrieve customer withdrawal requests.
- Migrated all 9 services from FrontOffice.BFF to CMS architecture
- Enhanced user.proto with 7 additional Customer API endpoints:
* UpdateCustomerProfile, GetCustomerProfile
* ChangeCustomerPassword with validation
* GetCustomerReferrals with commission stats
* UploadCustomerAvatar with file validation
* GetCustomerSettings, UpdateCustomerSettings
- All services now support Customer endpoints with /Customer/ prefix
- Mock implementations with realistic Persian data
- Fixed namespace conflicts and compilation issues
- Comprehensive testing completed for all endpoints
- Services migrated: Categories, City, UserCarts, Products, UserWallet,
Transaction, UserOrder, Package, User (enhanced)
- Add GetLowStockItemsResponseDto and LowStockItemDto for low stock item queries.
- Create CreateStockMovementCommand and its handler for managing stock movements.
- Implement CreateStockMovementCommandValidator for validating stock movement commands.
- Add GetStockMovementsQuery and its handler to retrieve stock movement records.
- Create GetStockMovementsResponseDto and StockMovementListDto for stock movement responses.
- Implement GetStockMovementsByInventoryItemQuery and its handler for fetching movements by inventory item.
- Add CreateWarehouseCommand and its handler for creating new warehouses.
- Implement CreateWarehouseCommandValidator for warehouse creation validation.
- Add DeleteWarehouseCommand and its handler for removing warehouses.
- Implement SetDefaultWarehouseCommand and its handler for setting a default warehouse.
- Create UpdateWarehouseCommand and its handler for updating warehouse details.
- Implement GetAllWarehousesQuery and its handler to retrieve all warehouses.
- Add GetWarehouseQuery and its handler for fetching a specific warehouse by ID.
- Implement SearchWarehousesQuery and its handler for searching warehouses based on criteria.
- Created a new table `DiscountProductImages` in the CMS schema.
- Added columns for image details including `Title`, `AltText`, `ImagePath`, `ThumbnailPath`, `SortOrder`, `IsActive`, `Created`, `CreatedBy`, `LastModified`, `LastModifiedBy`, and `IsDeleted`.
- Established a foreign key relationship with the `DiscountProducts` table.
- Created indexes on `DiscountProductId` and a composite index on `DiscountProductId` and `SortOrder`.