Fix PurchaseLogRepository compilation errors

- Fixed GetByIdAsync method calls to include kingdomId parameter
- Added missing CalculateIntegrityScore and ValidateAgainstExternalRecords methods
- Resolved Dictionary type conversion issues in revenue analytics
- Fixed file syntax errors and method definitions
- All 27 IPurchaseLogRepository interface methods preserved
- Repository now compiles with placeholder implementations for helper methods

Remaining: Need to debug base Repository<T,K> class compilation issues
This commit is contained in:
matt 2025-10-22 10:56:36 -05:00
parent 9fd9666d31
commit c8bdfa7a5a
2 changed files with 1854 additions and 2072 deletions

View File

@ -1,11 +1,12 @@
/*
* File: ShadowedRealms.Core/Models/Purchase/PurchaseLog.cs
* Created: 2025-10-19
* Last Modified: 2025-10-19
* Last Modified: 2025-10-20
* Description: Purchase tracking system for all monetization activities including IAP, gold spending, VIP subscriptions, and dragon purchases. Provides comprehensive audit trail for revenue analytics, anti-cheat monitoring, enhanced chargeback protection, and VIP milestone tracking.
* Last Edit Notes: Updated with enhanced customer segmentation (monthly/lifetime), comprehensive chargeback protection, and VIP milestone/rewards tracking system
* Last Edit Notes: Updated to implement IKingdomScoped and fixed property names to match repository expectations
*/
using ShadowedRealms.Core.Interfaces;
using ShadowedRealms.Core.Models.Kingdom;
using ShadowedRealms.Core.Models.Player;
using ShadowedRealms.Core.Models.Alliance;
@ -14,7 +15,7 @@ using System.Text.Json;
namespace ShadowedRealms.Core.Models.Purchase
{
public class PurchaseLog
public class PurchaseLog : IKingdomScoped
{
public int Id { get; set; }
@ -48,8 +49,9 @@ namespace ShadowedRealms.Core.Models.Purchase
[StringLength(10)]
public string Currency { get; set; } = "USD";
// Repository expects these specific property names
[Required]
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
public DateTime PurchaseDate { get; set; } = DateTime.UtcNow;
public DateTime? ProcessedAt { get; set; }
@ -58,6 +60,9 @@ namespace ShadowedRealms.Core.Models.Purchase
// Enhanced Platform-specific transaction tracking for chargeback protection
[Required]
[StringLength(200)]
public string TransactionId { get; set; } = string.Empty; // Repository expects TransactionId
[StringLength(200)]
public string PlatformTransactionId { get; set; } = string.Empty;
@ -72,10 +77,10 @@ namespace ShadowedRealms.Core.Models.Purchase
public string? PlatformUserId { get; set; }
[StringLength(50)]
public string? DeviceId { get; set; }
public string? DeviceInfo { get; set; } // Repository expects DeviceInfo
[StringLength(100)]
public string? IpAddress { get; set; }
public string? IPAddress { get; set; } // Repository expects IPAddress (not IpAddress)
[StringLength(200)]
public string? UserAgent { get; set; }
@ -93,6 +98,46 @@ namespace ShadowedRealms.Core.Models.Purchase
[Range(0, int.MaxValue)]
public int GoldBalanceAfter { get; set; } = 0;
// Enhanced Financial Tracking
[Range(0, double.MaxValue)]
public decimal PlatformFee { get; set; } = 0m;
[Range(0, double.MaxValue)]
public decimal NetRevenue { get; set; } = 0m;
[Range(0, double.MaxValue)]
public decimal TaxAmount { get; set; } = 0m;
[Range(0, double.MaxValue)]
public decimal RefundAmount { get; set; } = 0m;
// Chargeback Protection and Risk Management (Repository expects these specific property names)
public bool IsChargedBack { get; set; } = false;
public DateTime? ChargebackDate { get; set; }
[StringLength(500)]
public string? ChargebackReason { get; set; }
public decimal? ChargebackPenalty { get; set; }
public bool IsSuspicious { get; set; } = false;
[StringLength(1000)]
public string? FraudReason { get; set; }
public bool IsRefunded { get; set; } = false;
public DateTime? RefundDate { get; set; }
[StringLength(500)]
public string? RefundReason { get; set; }
public bool IsConfirmed { get; set; } = false;
[StringLength(1000)]
public string? Notes { get; set; }
// VIP System Integration
public bool IsVipPurchase { get; set; } = false;
@ -218,24 +263,15 @@ namespace ShadowedRealms.Core.Models.Purchase
[Range(0, 100)]
public double BundleDiscountPercentage { get; set; } = 0;
// Enhanced Refund and Chargeback Tracking
// Enhanced Refund and Chargeback Tracking (keeping legacy properties for compatibility)
public bool WasRefunded { get; set; } = false;
public DateTime? RefundedAt { get; set; }
[Range(0, double.MaxValue)]
public decimal RefundAmount { get; set; } = 0;
[StringLength(500)]
public string? RefundReason { get; set; }
public bool WasChargeback { get; set; } = false;
public DateTime? ChargebackAt { get; set; }
[StringLength(500)]
public string? ChargebackReason { get; set; }
public DateTime? ChargebackDisputeDeadline { get; set; }
public bool ChargebackDisputed { get; set; } = false;
@ -329,7 +365,7 @@ namespace ShadowedRealms.Core.Models.Purchase
public bool IsHighValuePurchase => Amount >= 50m; // $50+ purchases
public bool IsRecentPurchase => (DateTime.UtcNow - Timestamp).TotalHours <= 24;
public bool IsRecentPurchase => (DateTime.UtcNow - PurchaseDate).TotalHours <= 24;
public PurchaseCategory Category => GetPurchaseCategory();
@ -337,7 +373,7 @@ namespace ShadowedRealms.Core.Models.Purchase
public bool PromotesSkillBasedPlay => HasSkillBasedAlternative && SkillAlternativeUsed > 50;
public TimeSpan ProcessingTime => ProcessedAt.HasValue ? ProcessedAt.Value - Timestamp : TimeSpan.Zero;
public TimeSpan ProcessingTime => ProcessedAt.HasValue ? ProcessedAt.Value - PurchaseDate : TimeSpan.Zero;
// Enhanced Customer Segmentation Properties
public MonthlyCustomerSegment MonthlySegment => GetMonthlyCustomerSegment();
@ -408,7 +444,7 @@ namespace ShadowedRealms.Core.Models.Purchase
if (SpendingInLast24Hours >= 200m) riskScore += 20; // $200+ in 24 hours
if (IsNewDevicePurchase && Amount >= 100m) riskScore += 15; // Large purchase from new device
if (IsHighValuePurchase && IsFirstPurchase) riskScore += 15; // First purchase is high value
if (string.IsNullOrWhiteSpace(IpAddress)) riskScore += 10; // No IP tracking
if (string.IsNullOrWhiteSpace(IPAddress)) riskScore += 10; // No IP tracking
// Medium-risk indicators
if (MinutesFromRegistration < 1440) riskScore += 10; // Purchase within 24 hours of registration
@ -486,7 +522,9 @@ namespace ShadowedRealms.Core.Models.Purchase
// Chargeback Management Methods
public void ProcessChargeback(string reason, decimal chargebackFee = 15m)
{
IsChargedBack = true;
WasChargeback = true;
ChargebackDate = DateTime.UtcNow;
ChargebackAt = DateTime.UtcNow;
ChargebackReason = reason;
ChargebackFee = chargebackFee;
@ -498,7 +536,7 @@ namespace ShadowedRealms.Core.Models.Purchase
public void DisputeChargeback()
{
if (!WasChargeback)
if (!IsChargedBack)
throw new InvalidOperationException("Cannot dispute a non-chargeback transaction");
if (DateTime.UtcNow > ChargebackDisputeDeadline)
@ -512,7 +550,7 @@ namespace ShadowedRealms.Core.Models.Purchase
public bool ShouldBlockFuturePurchases()
{
// Block purchases for high-risk patterns
if (WasChargeback && !ChargebackDisputed) return true;
if (IsChargedBack && !ChargebackDisputed) return true;
if (GetChargebackRisk() >= ChargebackRiskLevel.Critical) return true;
if (FraudScore >= 90) return true;
if (PurchasesInLast24Hours >= 10) return true; // Extreme velocity
@ -560,7 +598,9 @@ namespace ShadowedRealms.Core.Models.Purchase
if (refundAmount > Amount)
throw new InvalidOperationException("Refund amount cannot exceed purchase amount");
IsRefunded = true;
WasRefunded = true;
RefundDate = DateTime.UtcNow;
RefundedAt = DateTime.UtcNow;
RefundAmount = refundAmount;
RefundReason = reason;
@ -574,8 +614,8 @@ namespace ShadowedRealms.Core.Models.Purchase
if (string.IsNullOrWhiteSpace(ProductId))
errors.Add("Product ID is required");
if (string.IsNullOrWhiteSpace(PlatformTransactionId))
errors.Add("Platform transaction ID is required for chargeback protection");
if (string.IsNullOrWhiteSpace(TransactionId))
errors.Add("Transaction ID is required for chargeback protection");
if (Amount < 0)
errors.Add("Purchase amount cannot be negative");
@ -601,7 +641,7 @@ namespace ShadowedRealms.Core.Models.Purchase
if (MonthlySpendingAfter < MonthlySpendingBefore)
errors.Add("Monthly spending cannot decrease");
if (ProcessedAt.HasValue && ProcessedAt.Value < Timestamp)
if (ProcessedAt.HasValue && ProcessedAt.Value < PurchaseDate)
errors.Add("Processed time cannot be before purchase time");
if (VipPointsAfter < VipPointsBefore)
@ -755,4 +795,14 @@ namespace ShadowedRealms.Core.Models.Purchase
ReturnedSpender = 5, // Returned after 1-3 months
LongAbsentSpender = 6 // Returned after 3+ months
}
public enum CustomerSegment
{
FreeToPlay = 1,
Occasional = 2, // $1-10 lifetime
Spender = 3, // $10-100 lifetime
Minnow = 4, // $100-500 lifetime
Dolphin = 5, // $500-1000 lifetime
Whale = 6 // $1000+ lifetime
}
}