This document is a complete guide for the app's offer tab system. In this system, you can fetch CPA (Cost Per Action) offers from multiple APIs and display them to users for completion and earning rewards.
| File | Path | Purpose |
|---|---|---|
| Main Fragment | app/src/main/java/com/lagradost/cloudstream3/ui/offers/OffersFragment.kt |
Main offers list UI and logic |
| Detail Fragment | app/src/main/java/com/lagradost/cloudstream3/ui/offers/OfferDetailFragment.kt |
Individual offer detail view |
| ViewModel | app/src/main/java/com/lagradost/cloudstream3/ui/offers/OffersViewModel.kt |
API calls and data management |
| Adapter | app/src/main/java/com/lagradost/cloudstream3/ui/offers/OffersAdapter.kt |
RecyclerView adapter for offers |
| Data Models | app/src/main/java/com/lagradost/cloudstream3/ui/offers/model/CpaOffer.kt |
Offer data classes |
| Main Layout | app/src/main/res/layout/fragment_offers.xml |
Main offers screen layout |
| Detail Layout | app/src/main/res/layout/fragment_offer_detail.xml |
Offer detail screen layout |
| Item Layout | app/src/main/res/layout/item_offer.xml |
Individual offer item layout |
| GitHub Config | https://cdn.jsdelivr.net/gh/am-abdulmueed/offers@main/offers.json |
Dynamic API configuration |
The offer tab fetches offers from two different APIs:
- CPALead API - Primary offer source
- Dynamic URL fetched from GitHub config
- Requires no authentication
- Returns ranked offers
- OGAds API - Secondary offer source
- Fixed endpoint:
https://authenticateapp.online/api/v2 - Requires API key authentication
- Uses IP-based targeting
- Fixed endpoint:
{
"offer": "https://cpalead.com/dashboard/reports/campaigns/list.json?api_key=YOUR_API_KEY"
}- Endpoint:
https://authenticateapp.online/api/v2 - Method: GET
- Headers:
Authorization: Bearer 43897|vGaDKh19mgaEz7YfSFe1nynTv5gIiez9fF6U4MA05ed58814 - Query Parameters:
ip: User's public IP addressuser_agent: Device user agentmax: Maximum offers to return (default: 10)
data class CpaOffer(
val id: Int, // Unique offer identifier
val title: String, // Offer title/name
val description: String?, // Offer description
val conversion: String?, // Conversion requirements
val device: String?, // Supported device type
val dailyCap: Int?, // Daily conversion limit
val isFastPay: Boolean?, // Fast payment eligibility
val link: String, // Offer completion link
val previewLink: String?, // Preview link
val amount: Double, // Payout amount
val payoutCurrency: String?, // Currency code (USD, EUR, etc.)
val payoutType: String?, // Payout type (CPI, CPE, CPR)
val countries: List<String>?, // Supported countries
val epc: Double?, // Earnings per click
val creatives: OfferCreatives?, // Image assets
val offerRank: Int?, // Display ranking
val payoutsPerCountry: Map<String, Double>? // Country-specific payouts
)data class OfferCreatives(
val url: String? // Image URL for offer
)Features:
- RecyclerView with offer cards
- Swipe-to-refresh functionality
- Loading, empty, and offline states
- Debug Panel: Live logs for troubleshooting.
- Visibility: The debug toggle icon is only visible in Debug builds. It is automatically hidden in Release builds for security and a cleaner UI.
- Note: Debug logs icon will only be enabled in debug mode, not in release.
- Automatic caching (30 minutes)
Layout Structure:
- Debug Panel (toggleable)
- Live API logs
- Copy to clipboard functionality
- Loading State
- Progress indicator
- Empty State
- Gift icon with "No offers available" message
- Offers List
- RecyclerView with offer cards
- SwipeRefreshLayout for refresh
- Offline Screen
- Beautiful offline UI with shimmer effect
- Retry button with loading animation
Features:
- Offer image display
- Detailed offer information
- Country and device compatibility
- Share functionality
- Install button (opens offer link)
Information Displayed:
- Offer title and image
- Payout amount and currency
- Supported countries with flags
- Device compatibility with icons
- Conversion requirements
- Description
Card Contents:
- Offer image (from creatives.url)
- Offer title
- Payout amount (formatted to 2 decimals)
- Currency symbol
- Payout type (CPI→Install, CPE→Action, CPR→Registration)
OffersFragment.onCreate() →
ViewModel.fetchOffers() →
Check cache →
If fresh: Return cached data
If expired: Fetch from APIs
fetchOffers() →
Parallel execution:
├── fetchPublicIP() → fetchOffersFromNewAPI() (OGAds)
└── fetchOffersFromExistingAPI() (CPALead)
→ Combine results → Cache → Update UI
GitHub Config → Get Dynamic URL →
API Request → Parse JSON →
Filter by offerRank → Sort by rank →
Return offers
Get Public IP → Encode parameters →
API Request with Auth → Parse JSON →
Map to CpaOffer model → Return offers
- Duration: 30 minutes
- Storage: In-memory (ViewModel)
- Cache Key: Last fetch time + offers list
if (!forceRefresh && cachedOffers != null &&
(currentTime - lastFetchTime) < CACHE_DURATION_MS) {
// Return cached offers
return cachedOffers
}- Toggle Button: Floating action button (bug icon)
- Live Logs: Real-time API call logs
- Copy Function: Copy all logs to clipboard
- Timestamp: Each log entry has timestamp
[CPALead]- CPALead API operations[OGAds]- OGAds API operations- General operations (cache, IP fetch, etc.)
[14:30:15] Starting offers fetch...
[14:30:15] User Agent: Mozilla/5.0 (Linux; Android 10; SM-G973F)
[14:30:16] Fetching public IP from https://api.ipify.org/?format=json
[14:30:16] Got public IP: 192.168.1.1
[14:30:16] [CPALead] Fetching offers...
[14:30:17] [CPALead] Using URL: https://cpalead.com/...
[14:30:18] [CPALead] Success! Got 15 offers
[14:30:18] [OGAds] Fetching offers from: https://authenticateapp.online/api/v2
[14:30:19] [OGAds] Success! Got 8 offers
[14:30:19] Total offers: CPALead(15) + OGAds(8) = 23
- Detection: Checks for "network", "timeout", "connection" in error messages
- UI Response: Shows offline screen with retry option
- User Feedback: Offline screen with shimmer effect
- CPALead: Checks response.status == "success"
- OGAds: Checks response.success == true
- Fallback: If one API fails, continues with the other
- No Offers: Shows gift icon with message
- Network Error: Shows offline screen
- Loading: Shows progress indicator
when (offer.payoutType?.uppercase()) {
"CPI" -> "Install" // Cost Per Install
"CPE" -> "Action" // Cost Per Engagement
"CPR" -> "Registration" // Cost Per Registration
else -> offer.payoutType ?: "Offer"
}- Android:
ic_android - iOS:
ic_ios - Desktop:
ic_desktop - Mobile:
ic_mobile - Default:
ic_device
- Flag Generation: Unicode flag emojis from country codes
- Country Names: Mapping for common countries
- Supported Countries: US, CA, GB, AU, NZ, DE, FR, IN, BR, MX
🎁 *Offer Title*
🤖 *Supported Device:* Android
🇺🇸 *Available in:* United States
📝 *Description:*
Complete this offer to earn reward.
🔗 *Get this offer:*
https://offer-link.com
──────────────
📲 Download from PluginStream Max
🌐 https://pluginstream.pages.dev
- Intent: ACTION_SEND with text/plain
- Subject: "Check out this offer: [Title]"
- Chooser: "Share Offer via"
- Uses
asyncfor parallel execution - Combines results from both APIs
- Reduces total fetch time
- Uses ImageLoader utility
- Placeholder image for missing creatives
- Efficient memory management
- DiffUtil for efficient updates
- ViewHolder pattern
- Proper item recycling
- OGAds API key stored in code
- Consider moving to secure storage
- CPALead uses dynamic URL from GitHub
- All offer links opened with ACTION_VIEW
- Proper URL parsing in Intent
- Exception handling for malformed URLs
- Cache Duration: Offers are cached for 30 minutes to reduce API calls
- IP Detection: Public IP is fetched for OGAds targeting
- Ranking: CPALead offers are sorted by offerRank
- Device Targeting: Some offers are device-specific
- Country Restrictions: Offers may be geo-restricted
- Debug Mode: Can be toggled via FAB button
- Offline Support: Graceful handling of network issues
{
"status": "success",
"number_offers": 15,
"country": "US",
"devices": "android",
"offers": [
{
"id": 1234,
"title": "Game App Install",
"description": "Install and play this game",
"amount": 1.50,
"payout_currency": "USD",
"payout_type": "CPI",
"offer_rank": 1,
"countries": ["US", "CA"],
"device": "android",
"creatives": {
"url": "https://example.com/image.jpg"
}
}
]
}{
"success": true,
"offers": [
{
"offerid": 5678,
"name_short": "Survey App",
"name": "Complete Survey App",
"description": "Complete surveys to earn",
"payout": "2.00",
"link": "https://offer-link.com",
"picture": "https://image-url.com",
"country": "US,CA,GB",
"device": "mobile"
}
]
}Licensed under the MIT License © 2026 Abdul Mueed