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
 |