404 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			404 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| :bug# Lottery System API Documentation
 | ||
| 
 | ||
| ## Overview
 | ||
| 
 | ||
| The DysonNetwork Lottery System provides a daily lottery where users can purchase tickets with custom number selections. Each day features a new draw with random winning numbers. Users purchase tickets using ISP (Dyson Network Points), with results announced each morning.
 | ||
| 
 | ||
| The API is handled by the DysonNetwork.Pass service. Which means if you use it with the Gateway the `/api` should be replaced with `/pass`
 | ||
| 
 | ||
| ### Key Features
 | ||
| 
 | ||
| - **Daily Draws**: Automated draws at midnight UTC
 | ||
| - **Custom Number Selection**: Users choose 5 unique numbers (0-99) + 1 special number (0-99)
 | ||
| - **Flexible Pricing**: Base cost 10 ISP + extra ISP per multiplier (e.g., multiplier=2 costs 20 ISP)
 | ||
| - **Daily Limits**: One ticket purchase per user per day
 | ||
| - **Prize System**: Multiple prize tiers based on matches
 | ||
| - **Instant Payment**: Tickets purchased using in-app points
 | ||
| - **Historical Records**: Complete draw history and statistics
 | ||
| 
 | ||
| ## Data Models
 | ||
| 
 | ||
| ### LotteryDrawStatus Enum
 | ||
| ```csharp
 | ||
| public enum LotteryDrawStatus
 | ||
| {
 | ||
|     Pending = 0,    // Ticket awaiting draw
 | ||
|     Drawn = 1       // Ticket has been processed in draw
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### SnLottery Model
 | ||
| ```csharp
 | ||
| public class SnLottery : ModelBase
 | ||
| {
 | ||
|     public Guid Id { get; set; }
 | ||
|     public SnAccount Account { get; set; } = null!;
 | ||
|     public Guid AccountId { get; set; }
 | ||
|     public List<int> RegionOneNumbers { get; set; } = new(); // 5 numbers (0-99)
 | ||
|     public int RegionTwoNumber { get; set; }                    // Special number (0-99)
 | ||
|     public int Multiplier { get; set; } = 1;                     // Prize multiplier (≥1)
 | ||
|     public LotteryDrawStatus DrawStatus { get; set; }
 | ||
|     public DateTime? DrawDate { get; set; }                      // Date when drawn
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### SnLotteryRecord Model
 | ||
| ```csharp
 | ||
| public class SnLotteryRecord : ModelBase
 | ||
| {
 | ||
|     public Guid Id { get; set; }
 | ||
|     public DateTime DrawDate { get; set; }
 | ||
|     public List<int> WinningRegionOneNumbers { get; set; } = new(); // 5 winning numbers
 | ||
|     public int WinningRegionTwoNumber { get; set; }                   // Winning special number
 | ||
|     public int TotalTickets { get; set; }                             // Total tickets processed
 | ||
|     public int TotalPrizesAwarded { get; set; }                       // Number of winning tickets
 | ||
|     public long TotalPrizeAmount { get; set; }                        // Total ISP prize amount
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ## Prize Structure
 | ||
| 
 | ||
| | Region 1 Matches | Base Prize (ISP) | Notes |
 | ||
| |-----------------|------------------|-------|
 | ||
| | 0 | 0 | No prize |
 | ||
| | 1 | 10 | Minimum win |
 | ||
| | 2 | 20 | Double minimum |
 | ||
| | 3 | 50 | Five times minimum |
 | ||
| | 4 | 100 | Ten times minimum |
 | ||
| | 5 | 1000 | Maximum prize |
 | ||
| 
 | ||
| **Special Number Bonus**: If Region 2 number matches, multiply any prize by 10x.
 | ||
| 
 | ||
| ## API Endpoints
 | ||
| 
 | ||
| All endpoints require authentication via Bearer token.
 | ||
| 
 | ||
| ### Purchase Ticket
 | ||
| **POST** `/api/lotteries`
 | ||
| 
 | ||
| Creates a lottery order and deducts ISP from user's wallet.
 | ||
| 
 | ||
| **Request Body:**
 | ||
| ```json
 | ||
| {
 | ||
|   "RegionOneNumbers": [5, 23, 47, 68, 89],
 | ||
|   "RegionTwoNumber": 42,
 | ||
|   "Multiplier": 1
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| **Response:**
 | ||
| ```json
 | ||
| {
 | ||
|   "id": "guid",
 | ||
|   "accountId": "guid",
 | ||
|   "createdAt": "2025-10-24T00:00:00Z",
 | ||
|   "status": "Paid",
 | ||
|   "currency": "isp",
 | ||
|   "amount": 10,
 | ||
|   "productIdentifier": "lottery"
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| **Validation Rules:**
 | ||
| - `RegionOneNumbers`: Exactly 5 unique integers between 0-99
 | ||
| - `RegionTwoNumber`: Single integer between 0-99
 | ||
| - `Multiplier`: Integer ≥ 1
 | ||
| - User can only purchase 1 ticket per day
 | ||
| 
 | ||
| **Pricing:**
 | ||
| - Base cost: 10 ISP
 | ||
| - Additional cost: (Multiplier - 1) × 10 ISP
 | ||
| - Total cost = (Multiplier × 10) ISP
 | ||
| 
 | ||
| ### Get User Tickets
 | ||
| **GET** `/api/lotteries`
 | ||
| 
 | ||
| Retrieves user's lottery tickets with pagination.
 | ||
| 
 | ||
| **Query Parameters:**
 | ||
| - `offset` (optional, default 0): Page offset
 | ||
| - `limit` (optional, default 20, max 100): Items per page
 | ||
| 
 | ||
| **Response:**
 | ||
| ```json
 | ||
| [
 | ||
|   {
 | ||
|     "id": "guid",
 | ||
|     "regionOneNumbers": [5, 23, 47, 68, 89],
 | ||
|     "regionTwoNumber": 42,
 | ||
|     "multiplier": 1,
 | ||
|     "drawStatus": "Pending",
 | ||
|     "drawDate": null,
 | ||
|     "createdAt": "2025-10-24T10:30:00Z"
 | ||
|   }
 | ||
| ]
 | ||
| ```
 | ||
| 
 | ||
| **Response Headers:**
 | ||
| ```
 | ||
| X-Total: 42  // Total number of user's tickets
 | ||
| ```
 | ||
| 
 | ||
| ### Get Specific Ticket
 | ||
| **GET** `/api/lotteries/{id}`
 | ||
| 
 | ||
| Retrieves a specific lottery ticket by ID.
 | ||
| 
 | ||
| **Response:**
 | ||
| Same structure as individual items from Get User Tickets.
 | ||
| 
 | ||
| **Error Responses:**
 | ||
| - `404 Not Found`: Ticket doesn't exist or user doesn't own it
 | ||
| 
 | ||
| ### Get Lottery Records
 | ||
| **GET** `/api/lotteries/records`
 | ||
| 
 | ||
| Retrieves historical lottery draw results.
 | ||
| 
 | ||
| **Query Parameters:**
 | ||
| - `startDate` (optional): Filter by draw date (YYYY-MM-DD)
 | ||
| - `endDate` (optional): Filter by draw date (YYYY-MM-DD)
 | ||
| - `offset` (optional, default 0): Page offset
 | ||
| - `limit` (optional, default 20): Items per page
 | ||
| 
 | ||
| **Response:**
 | ||
| ```json
 | ||
| [
 | ||
|   {
 | ||
|     "id": "guid",
 | ||
|     "drawDate": "2025-10-24T00:00:00Z",
 | ||
|     "winningRegionOneNumbers": [7, 15, 23, 46, 82],
 | ||
|     "winningRegionTwoNumber": 19,
 | ||
|     "totalTickets": 245,
 | ||
|     "totalPrizesAwarded": 23,
 | ||
|     "totalPrizeAmount": 4820
 | ||
|   }
 | ||
| ]
 | ||
| ```
 | ||
| 
 | ||
| ## Integration Examples
 | ||
| 
 | ||
| ### Frontend Integration (JavaScript/React)
 | ||
| 
 | ||
| ```javascript
 | ||
| // Purchase a lottery ticket
 | ||
| async function purchaseLottery(numbers, specialNumber, multiplier = 1) {
 | ||
|   try {
 | ||
|     const response = await fetch('/api/lotteries', {
 | ||
|       method: 'POST',
 | ||
|       headers: {
 | ||
|         'Content-Type': 'application/json',
 | ||
|         'Authorization': `Bearer ${userToken}`
 | ||
|       },
 | ||
|       body: JSON.stringify({
 | ||
|         RegionOneNumbers: numbers,      // Array of 5 unique numbers 0-99
 | ||
|         RegionTwoNumber: specialNumber, // Number 0-99
 | ||
|         Multiplier: multiplier          // Optional, defaults to 1
 | ||
|       })
 | ||
|     });
 | ||
| 
 | ||
|     const order = await response.json();
 | ||
| 
 | ||
|     if (response.ok) {
 | ||
|       console.log('Ticket purchased successfully!', order);
 | ||
|       // Refresh user ISP balance
 | ||
|       updateWalletBalance();
 | ||
|     } else {
 | ||
|       console.error('Purchase failed:', order);
 | ||
|     }
 | ||
|   } catch (error) {
 | ||
|     console.error('Network error:', error);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // Get user's tickets
 | ||
| async function getUserTickets() {
 | ||
|   try {
 | ||
|     const response = await fetch('/api/lotteries?limit=20', {
 | ||
|       headers: {
 | ||
|         'Authorization': `Bearer ${userToken}`
 | ||
|       }
 | ||
|     });
 | ||
| 
 | ||
|     const tickets = await response.json();
 | ||
|     const totalTickets = response.headers.get('X-Total');
 | ||
| 
 | ||
|     return { tickets, total: parseInt(totalTickets) };
 | ||
|   } catch (error) {
 | ||
|     console.error('Error fetching tickets:', error);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // Get draw history
 | ||
| async function getDrawHistory() {
 | ||
|   try {
 | ||
|     const response = await fetch('/api/lotteries/records', {
 | ||
|       headers: {
 | ||
|         'Authorization': `Bearer ${userToken}`
 | ||
|       }
 | ||
|     });
 | ||
| 
 | ||
|     return await response.json();
 | ||
|   } catch (error) {
 | ||
|     console.error('Error fetching history:', error);
 | ||
|   }
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### Mobile Integration (React Native/TypeScript)
 | ||
| 
 | ||
| ```typescript
 | ||
| interface LotteryTicket {
 | ||
|   id: string;
 | ||
|   regionOneNumbers: number[];
 | ||
|   regionTwoNumber: number;
 | ||
|   multiplier: number;
 | ||
|   drawStatus: 'Pending' | 'Drawn';
 | ||
|   drawDate?: string;
 | ||
|   createdAt: string;
 | ||
| }
 | ||
| 
 | ||
| interface PurchaseRequest {
 | ||
|   RegionOneNumbers: number[];
 | ||
|   RegionTwoNumber: number;
 | ||
|   Multiplier: number;
 | ||
| }
 | ||
| 
 | ||
| class LotteryService {
 | ||
|   private apiUrl = 'https://your-api-domain.com/api/lotteries';
 | ||
| 
 | ||
|   async purchaseTicket(
 | ||
|     ticket: Omit<PurchaseRequest, 'RegionOneNumbers'> & { numbers: number[] },
 | ||
|     token: string
 | ||
|   ): Promise<any> {
 | ||
|     const request: PurchaseRequest = {
 | ||
|       RegionOneNumbers: ticket.numbers,
 | ||
|       RegionTwoNumber: ticket.RegionTwoNumber,
 | ||
|       Multiplier: ticket.Multiplier
 | ||
|     };
 | ||
| 
 | ||
|     const response = await fetch(this.apiUrl, {
 | ||
|       method: 'POST',
 | ||
|       headers: {
 | ||
|         'Content-Type': 'application/json',
 | ||
|         'Authorization': `Bearer ${token}`
 | ||
|       },
 | ||
|       body: JSON.stringify(request)
 | ||
|     });
 | ||
| 
 | ||
|     return response.json();
 | ||
|   }
 | ||
| 
 | ||
|   async getTickets(token: string, offset = 0, limit = 20): Promise<LotteryTicket[]> {
 | ||
|     const response = await fetch(`${this.apiUrl}?offset=${offset}&limit=${limit}`, {
 | ||
|       headers: { 'Authorization': `Bearer ${token}` }
 | ||
|     });
 | ||
| 
 | ||
|     return response.json();
 | ||
|   }
 | ||
| 
 | ||
|   async getDrawRecords(token: string): Promise<any[]> {
 | ||
|     const response = await fetch(`${this.apiUrl}/records`, {
 | ||
|       headers: { 'Authorization': `Bearer ${token}` }
 | ||
|     });
 | ||
| 
 | ||
|     return response.json();
 | ||
|   }
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### Number Validation
 | ||
| 
 | ||
| ```javascript
 | ||
| function validateLotteryNumbers(numbers, specialNumber, multiplier = 1) {
 | ||
|   // Validate region one numbers
 | ||
|   if (!Array.isArray(numbers) || numbers.length !== 5) {
 | ||
|     return { valid: false, error: 'Must select exactly 5 numbers' };
 | ||
|   }
 | ||
| 
 | ||
|   const uniqueNumbers = new Set(numbers);
 | ||
|   if (uniqueNumbers.size !== 5) {
 | ||
|     return { valid: false, error: 'Numbers must be unique' };
 | ||
|   }
 | ||
| 
 | ||
|   // Check range 0-99
 | ||
|   for (const num of numbers) {
 | ||
|     if (!Number.isInteger(num) || num < 0 || num > 99) {
 | ||
|       return { valid: false, error: 'Numbers must be integers between 0-99' };
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Validate special number
 | ||
|   if (!Number.isInteger(specialNumber) || specialNumber < 0 || specialNumber > 99) {
 | ||
|     return { valid: false, error: 'Special number must be between 0-99' };
 | ||
|   }
 | ||
| 
 | ||
|   // Validate multiplier
 | ||
|   if (!Number.isInteger(multiplier) || multiplier < 1) {
 | ||
|     return { valid: false, error: 'Multiplier must be 1 or greater' };
 | ||
|   }
 | ||
| 
 | ||
|   return { valid: true };
 | ||
| }
 | ||
| 
 | ||
| // Example usage
 | ||
| const validation = validateLotteryNumbers([5, 12, 23, 47, 89], 42, 2);
 | ||
| if (!validation.valid) {
 | ||
|   console.error(validation.error);
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ## Daily Draw Schedule
 | ||
| 
 | ||
| - **Draw Time**: Every midnight UTC (00:00 UTC)
 | ||
| - **Processing**: Only tickets from the previous day are included
 | ||
| - **Prize Distribution**: Winners automatically receive ISP credits
 | ||
| - **History**: Draws are preserved indefinitely
 | ||
| 
 | ||
| ## Error Handling
 | ||
| 
 | ||
| ### Common Error Codes
 | ||
| - `400 Bad Request`: Invalid request data (bad numbers, duplicate purchase, etc.)
 | ||
| - `401 Unauthorized`: Missing or invalid authentication token
 | ||
| - `404 Not Found`: Ticket doesn't exist or access denied
 | ||
| - `403 Forbidden`: Insufficient permissions (admin endpoints)
 | ||
| 
 | ||
| ### Error Response Format
 | ||
| ```json
 | ||
| {
 | ||
|   "message": "You can only purchase one lottery per day.",
 | ||
|   "type": "ArgumentException",
 | ||
|   "statusCode": 400
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ## Testing Guidelines
 | ||
| 
 | ||
| ### Test Cases
 | ||
| 1. **Valid Purchase**: Select valid numbers, verify wallet deduction
 | ||
| 2. **Invalid Numbers**: Try duplicate region one numbers, out-of-range values
 | ||
| 3. **Daily Limit**: Attempt second purchase in same day
 | ||
| 4. **Insufficient Funds**: Try purchase without enough ISP
 | ||
| 5. **Draw Processing**: Verify winning tickets receive correct prizes
 | ||
| 6. **Historical Data**: Check draw records match processed tickets
 | ||
| 
 | ||
| ### Test Data Examples
 | ||
| ```javascript
 | ||
| // Valid ticket
 | ||
| { numbers: [1, 15, 23, 67, 89], special: 42, multiplier: 1 }
 | ||
| 
 | ||
| // Invalid - duplicate numbers
 | ||
| { numbers: [1, 15, 23, 15, 89], special: 42, multiplier: 1 }
 | ||
| 
 | ||
| // Invalid - out of range
 | ||
| { numbers: [1, 15, 23, 67, 150], special: 42, multiplier: 1 }
 | ||
| ```
 | ||
| 
 | ||
| ## Support
 | ||
| 
 | ||
| For API integration questions or support:
 | ||
| - Check network documentation for authentication details
 | ||
| - Contact Dyson Network development team for assistance
 | ||
| - Monitor API response headers for pagination metadata
 |