, # RevenueCat.NET
A professional, production-ready .NET 8 client library for the RevenueCat REST API v2. Built with Refit for type-safe, reliable HTTP communication.
- Complete API Coverage: Full implementation of RevenueCat REST API v2 with 100+ endpoints
- Built with Refit: Type-safe HTTP client with automatic request/response handling
- Modern .NET 8: Built with latest C# features (primary constructors, records, file-scoped namespaces)
- SOLID Principles: Clean architecture with dependency injection support
- Type-Safe: Strong typing with nullable reference types and comprehensive models
- Async/Await: Fully asynchronous API with cancellation token support
- Resilient: Built-in retry logic, rate limiting handling, and timeout management
- Performance: Connection pooling, HTTP compression, and efficient JSON serialization
- Extensible: Interface-based design for easy testing and mocking
- Well-Documented: Comprehensive XML documentation and usage examples
dotnet add package RevenueCat.NETusing RevenueCat.NET;
var client = new RevenueCatClient("your_v2_api_key");
var projects = await client.Projects.ListAsync();
var customers = await client.Customers.ListAsync("proj_abc123");
var customer = await client.Customers.GetAsync(
"proj_abc123",
"customer_id",
expand: new[] { "attributes" }
);var client = new RevenueCatClient("your_api_key", options =>
{
options.Timeout = TimeSpan.FromSeconds(60);
options.MaxRetryAttempts = 5;
options.RetryDelay = TimeSpan.FromSeconds(1);
options.EnableRetryOnRateLimit = true;
});var projects = await client.Projects.ListAsync(limit: 20);var apps = await client.Apps.ListAsync("proj_abc123");
var app = await client.Apps.CreateAsync("proj_abc123", new CreateAppRequest(
Name: "My iOS App",
Type: AppType.AppStore,
AppStore: new AppStoreConfig(
BundleId: "com.example.app",
SharedSecret: "your_shared_secret"
)
));// Create a customer with attributes
var customer = await client.Customers.CreateAsync("proj_abc123", new CreateCustomerRequest(
Id: "user_12345",
Attributes: new[]
{
new CustomerAttributeInput("$email", "user@example.com"),
new CustomerAttributeInput("$displayName", "John Doe")
}
));
// Grant an entitlement to a customer
await client.Customers.GrantEntitlementAsync("proj_abc123", "customer_id",
new GrantEntitlementRequest(
EntitlementId: "ent_premium",
ExpiresAt: DateTimeOffset.UtcNow.AddMonths(1).ToUnixTimeMilliseconds()
));
// Revoke a granted entitlement
await client.Customers.RevokeGrantedEntitlementAsync("proj_abc123", "customer_id",
new RevokeGrantedEntitlementRequest(EntitlementId: "ent_premium"));
// Assign an offering override
await client.Customers.AssignOfferingAsync("proj_abc123", "customer_id",
new AssignOfferingRequest(OfferingId: "offering_special"));
// Transfer customer data
await client.Customers.TransferAsync("proj_abc123", "old_customer_id",
new TransferCustomerRequest("new_customer_id"));var products = await client.Products.ListAsync(
"proj_abc123",
appId: "app_xyz789",
expand: new[] { "items.app" }
);
var product = await client.Products.CreateAsync("proj_abc123", new CreateProductRequest(
StoreIdentifier: "com.example.premium.monthly",
AppId: "app_xyz789",
Type: ProductType.Subscription,
DisplayName: "Premium Monthly"
));var entitlement = await client.Entitlements.CreateAsync("proj_abc123",
new CreateEntitlementRequest(
LookupKey: "premium",
DisplayName: "Premium Access"
));
await client.Entitlements.AttachProductsAsync("proj_abc123", "ent_abc123",
new AttachProductsRequest(new[] { "prod_123", "prod_456" }));var offering = await client.Offerings.CreateAsync("proj_abc123",
new CreateOfferingRequest(
LookupKey: "default",
DisplayName: "Default Offering",
IsDefault: true
));
var package = await client.Packages.CreateAsync("proj_abc123", "offering_id",
new CreatePackageRequest(
LookupKey: "monthly",
DisplayName: "Monthly Package",
ProductId: "prod_123",
Position: 1
));var subscriptions = await client.Subscriptions.ListAsync("proj_abc123", "customer_id");
await client.Subscriptions.CancelAsync("proj_abc123", "customer_id", "sub_id");
await client.Subscriptions.RefundAsync("proj_abc123", "customer_id", "sub_id");var purchases = await client.Purchases.ListAsync("proj_abc123", "customer_id");
await client.Purchases.RefundAsync("proj_abc123", "customer_id", "purchase_id");var metrics = await client.Charts.GetMetricsAsync(
"proj_abc123",
ChartMetricType.Revenue,
startDate: 1704067200000, // Unix timestamp
endDate: 1735689600000,
appId: "app_123"
);try
{
var customer = await client.Customers.GetAsync("proj_abc123", "customer_id");
}
catch (NotFoundException ex)
{
Console.WriteLine($"Customer not found: {ex.Message}");
}
catch (RateLimitException ex)
{
Console.WriteLine($"Rate limited. Retry after: {ex.ErrorResponse?.BackoffMs}ms");
}
catch (RevenueCatException ex)
{
Console.WriteLine($"API error: {ex.Message}");
Console.WriteLine($"Error type: {ex.ErrorResponse?.Type}");
}services.AddSingleton<IRevenueCatClient>(sp =>
new RevenueCatClient("your_api_key"));- ✅ Projects - List and manage projects
- ✅ Apps - Full CRUD operations for all store types
- ✅ Customers - Complete customer lifecycle management
- ✅ Products - Product catalog management
- ✅ Entitlements - Entitlement configuration and product attachment
- ✅ Offerings - Offering management with metadata support
- ✅ Packages - Package configuration with eligibility criteria
- ✅ Paywalls - Paywall creation and management
- ✅ Subscriptions - List, search, cancel, refund operations
- ✅ Purchases - One-time purchase management and refunds
- ✅ Invoices - Invoice retrieval and file access
- ✅ Virtual Currency - Balance management and transactions
- ✅ Charts & Metrics - Revenue, MRR, ARR, churn, and more
- ✅ Search - Search subscriptions and purchases by store identifiers
- ✅ Customer Transfer - Transfer data between customers
- ✅ Customer Attributes - Manage custom attributes
- ✅ Customer Aliases - List customer aliases
- ✅ Active Entitlements - Query customer entitlements
- ✅ Grant/Revoke Entitlements - Grant and revoke promotional entitlements
- ✅ Assign Offering - Override customer offerings
- ✅ Authenticated Management URLs - Generate customer portal links
- ✅ Store Product Creation - Push products to App Store Connect
- ✅ Expandable Fields - Reduce API calls with field expansion
- ✅ Pagination - Efficient handling of large datasets
- ✅ Apple App Store
- ✅ Apple Mac App Store
- ✅ Google Play Store
- ✅ Amazon Appstore
- ✅ Stripe
- ✅ RevenueCat Billing (Web)
- ✅ Roku
- ✅ Paddle
- ✅ Promotional
Comprehensive examples are available in the examples/ directory:
- BasicUsage - Quick start guide
- CustomerManagement - Customer CRUD, attributes, transfer
- SubscriptionManagement - Subscription lifecycle, cancel, refund
- ProductCatalog - Products, entitlements, offerings, packages
- ErrorHandling - Error handling patterns and retry logic
- VirtualCurrency - Virtual currency management
- Pagination - Efficient pagination techniques
See the examples README for detailed information.
Reduce API calls by expanding related resources:
var customer = await client.Customers.GetAsync(
projectId,
customerId,
expand: new[] { "attributes", "active_entitlements" }
);
// Access expanded data directly
foreach (var attr in customer.Attributes.Items)
{
Console.WriteLine($"{attr.Key}: {attr.Value}");
}Efficiently handle large datasets:
string? startingAfter = null;
do
{
var page = await client.Customers.ListAsync(
projectId,
limit: 100,
startingAfter: startingAfter
);
// Process page.Items
// Get cursor for next page
if (page.NextPage != null)
{
var uri = new Uri(page.NextPage);
var query = HttpUtility.ParseQueryString(uri.Query);
startingAfter = query["starting_after"];
}
else
{
startingAfter = null;
}
} while (startingAfter != null);Search by store identifiers:
// Search subscriptions
var subscriptions = await client.Subscriptions.SearchSubscriptionsAsync(
projectId,
storeSubscriptionIdentifier: "GPA.1234-5678-9012-34567"
);
// Search purchases
var purchases = await client.Purchases.SearchPurchasesAsync(
projectId,
storePurchaseIdentifier: "1000000123456789"
);Manage in-app currencies:
// Add currency (multiple currencies at once)
var balances = await client.Customers.CreateVirtualCurrencyTransactionAsync(
projectId,
customerId,
new CreateVirtualCurrencyTransactionRequest(
Adjustments: new Dictionary<string, int>
{
{ "GEMS", 100 },
{ "COINS", 500 }
},
Reference: "purchase_reward"
),
idempotencyKey: "unique-key"
);
// Update balance directly (without transaction record)
await client.Customers.UpdateVirtualCurrencyBalanceAsync(
projectId,
customerId,
new UpdateVirtualCurrencyBalanceRequest(
Adjustments: new Dictionary<string, int>
{
{ "GEMS", 50 } // Set absolute balance
}
),
idempotencyKey: "another-unique-key"
);
// List all balances
var allBalances = await client.Customers.ListVirtualCurrencyBalancesAsync(
projectId,
customerId,
includeEmptyBalances: true
);Transfer data between customers:
var transfer = await client.Customers.TransferAsync(
projectId,
sourceCustomerId,
new TransferCustomerRequest(
TargetCustomerId: targetCustomerId,
AppIds: new[] { "app_123" } // Optional: filter by apps
)
);| Resource | List | Get | Create | Update | Delete | Actions |
|---|---|---|---|---|---|---|
| Projects | ✅ | - | - | - | - | - |
| Apps | ✅ | ✅ | ✅ | ✅ | ✅ | Get StoreKit Config, List API Keys |
| Customers | ✅ | ✅ | ✅ | - | ✅ | Transfer, Grant/Revoke Entitlement, Assign Offering, Manage Attributes |
| Products | ✅ | ✅ | ✅ | - | ✅ | Create in Store |
| Entitlements | ✅ | ✅ | ✅ | ✅ | ✅ | Attach/Detach Products |
| Offerings | ✅ | ✅ | ✅ | ✅ | ✅ | Set Default |
| Packages | ✅ | ✅ | ✅ | ✅ | ✅ | Attach/Detach Products |
| Paywalls | - | - | ✅ | - | - | - |
| Subscriptions | ✅ | ✅ | - | - | - | Cancel, Refund, Get Management URL, List Transactions |
| Purchases | ✅ | ✅ | - | - | - | Refund |
| Invoices | ✅ | - | - | - | - | Get File URL |
| Charts | - | ✅ | - | - | - | Get Overview Metrics |
| Virtual Currency | ✅ | - | ✅ | ✅ | - | - |
- .NET 8.0 or higher
- RevenueCat API v2 key
See MIGRATION.md for detailed migration instructions from version 1.x to 2.0.
MIT License - see LICENSE file for details
Contributions are welcome! Please feel free to submit a Pull Request.
frknlkn - GitHub
For issues and questions:
- GitHub Issues: Create an issue
- RevenueCat Documentation: https://www.revenuecat.com/docs/api-v2
- Examples: examples/