Point Service API Documentation
Overview
The Point Service provides a comprehensive points management system with support for:
- Point Transactions: Earn, spend, and track point transactions with detailed history
- Point Balance: Query current and historical point balances
- Point Rules: Shop-based and event-based point earning rules with configurable conversion rates
- Receipt Verification: Upload and verify receipts to earn points with manual and automatic verification
- Point Request Tickets: Request-based workflow for point adjustments requiring approval
Base URL
baseUrl/point/api
Authentication
Most endpoints require authentication via JWT token in the Authorization header:
Authorization: Bearer <token>
The token should contain a sub field with the member/user ID.
Point Balance API
GET baseUrl/point/api/balance/me
Get the current authenticated user's point balance.
Request
Endpoint: GET baseUrl/point/api/balance/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Example Request
GET baseUrl/point/api/balance/me
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"pointAmount": 1250,
"purchaseAmount": 15250.75
}
}
GET baseUrl/point/api/balance/:memberId
Get point balance for a specific member with optional date range filtering.
Request
Endpoint: GET baseUrl/point/api/balance/:memberId
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
memberId: string; // UUID of the member
}
Query Parameters:
{
start?: string; // Start date (ISO 8601 format or date string)
end?: string; // End date (ISO 8601 format or date string)
format?: "earn" | "burn"; // Format: "earn" for earned points, "burn" for spent points
}
Example Request
GET baseUrl/point/api/balance/550e8400-e29b-41d4-a716-446655440000?start=2024-01-01&end=2024-12-31&format=earn
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"pointAmount": 1250,
"purchaseAmount": 15250.75
}
}
Point Transaction API
POST baseUrl/point/api/earn
Earn points for a member.
Request
Endpoint: POST baseUrl/point/api/earn
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
memberId: string; // Required, UUID
amount: number; // Required, must be >= 0
purchaseAmount?: number; // Optional, must be >= 0
shopId?: string; // Optional shop ID
categoryId?: string; // Optional category ID
note?: string; // Optional note
sourceBy?: "Manual" | "Reward" | "Campaign" | "Purchase"; // Optional source
}
Example Request
{
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100,
"purchaseAmount": 5000,
"shopId": "shop-001",
"categoryId": "cat-001",
"note": "Points earned from purchase",
"sourceBy": "Purchase"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point earned successfully"
}
}
POST baseUrl/point/api/spend
Spend points for a member.
Request
Endpoint: POST baseUrl/point/api/spend
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
memberId: string; // Required, UUID
amount: number; // Required, must be >= 0
purchaseAmount?: number; // Optional, must be >= 0
shopId?: string; // Optional shop ID
categoryId?: string; // Optional category ID
note?: string; // Optional note
sourceBy?: "Manual" | "Reward" | "Campaign" | "Purchase"; // Optional source
}
Example Request
{
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"amount": 50,
"note": "Redeemed for voucher",
"sourceBy": "Reward"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point spent successfully"
}
}
GET baseUrl/point/api/history
Get point transaction history with filtering and pagination.
Request
Endpoint: GET baseUrl/point/api/history
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
memberIds?: string; // Comma-separated UUIDs (e.g., "id1,id2,id3")
startDate?: string; // Start date (ISO 8601 or date string)
endDate?: string; // End date (ISO 8601 or date string)
actionType?: "EARN" | "SPEND" | "EXPIRE"; // Filter by action type
sourceBy?: "Manual" | "Reward" | "Campaign" | "Purchase"; // Filter by source
includeMember?: boolean; // Include member information (default: false)
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
}
Example Request
GET baseUrl/point/api/history?memberIds=550e8400-e29b-41d4-a716-446655440000&actionType=EARN&startDate=2024-01-01&endDate=2024-12-31&includeMember=true&page=1&limit=20
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 45,
"totalPages": 3
},
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"actionType": "EARN",
"amount": 100,
"purchaseAmount": 5000,
"shopId": "shop-001",
"categoryId": "cat-001",
"note": "Points earned from purchase",
"sourceBy": "Purchase",
"balanceAfter": 1250,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"member": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"firstname": "John",
"lastname": "Doe"
}
}
]
}
}
GET baseUrl/point/api/history/me
Get the current authenticated user's point transaction history.
Request
Endpoint: GET baseUrl/point/api/history/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
Same as GET baseUrl/point/api/history (excluding memberIds as it's automatically set from the token)
Example Request
GET baseUrl/point/api/history/me?actionType=EARN&startDate=2024-01-01&endDate=2024-12-31&page=1&limit=20
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point transaction history fetched successfully",
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 12,
"totalPages": 1
},
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"actionType": "EARN",
"amount": 100,
"purchaseAmount": 5000,
"shopId": "shop-001",
"note": "Points earned from purchase",
"sourceBy": "Purchase",
"balanceAfter": 1250,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
}
GET baseUrl/point/api/summary/daily
Get daily point summary aggregated by date.
Request
Endpoint: GET baseUrl/point/api/summary/daily
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
startDate?: string; // Start date (ISO 8601 format)
endDate?: string; // End date (ISO 8601 format)
}
Example Request
GET baseUrl/point/api/summary/daily?startDate=2024-01-01&endDate=2024-01-31
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": [
{
"date": "2024-01-15",
"earned": 500,
"spent": 200,
"expired": 50,
"netChange": 250
},
{
"date": "2024-01-16",
"earned": 300,
"spent": 100,
"expired": 0,
"netChange": 200
}
]
}
Point Request Tickets API
POST baseUrl/point/api/request/earn
Create a request to earn points (requires approval).
Request
Endpoint: POST baseUrl/point/api/request/earn
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
amount: number; // Required, must be positive
memberId: string; // Required, UUID
purchaseAmount?: number; // Optional, must be >= 0
shopId?: string; // Optional shop ID
categoryId?: string; // Optional category ID
requestReason: string; // Required, minimum 1 character
}
Example Request
{
"amount": 150,
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"purchaseAmount": 7500,
"shopId": "shop-001",
"categoryId": "cat-001",
"requestReason": "Customer service adjustment for missing points"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Request earned successfully"
}
}
POST baseUrl/point/api/request/spend
Create a request to spend points (requires approval).
Request
Endpoint: POST baseUrl/point/api/request/spend
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
amount: number; // Required, must be positive
memberId: string; // Required, UUID
purchaseAmount?: number; // Optional, must be >= 0
shopId?: string; // Optional shop ID
categoryId?: string; // Optional category ID
requestReason: string; // Required, minimum 1 character
}
Example Request
{
"amount": 75,
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"requestReason": "Refund for cancelled order"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Request spent successfully"
}
}
PATCH baseUrl/point/api/request/:id/review
Review (approve/reject) a point request ticket.
Request
Endpoint: PATCH baseUrl/point/api/request/:id/review
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the request ticket
}
Request Body:
{
status: "pending" | "approved" | "rejected"; // Required
reviewReason: string; // Required, minimum 1 character
}
Example Request
{
"status": "approved",
"reviewReason": "Verified purchase receipt, points granted"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Request reviewed successfully"
}
}
GET baseUrl/point/api/request
Get all point request tickets with filtering and pagination.
Request
Endpoint: GET baseUrl/point/api/request
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
status?: "pending" | "approved" | "rejected"; // Filter by status
actionType?: "EARN" | "SPEND"; // Filter by action type
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
}
Example Request
GET baseUrl/point/api/request?status=pending&actionType=EARN&page=1&limit=20
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1
},
"data": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"actionType": "EARN",
"amount": 150,
"purchaseAmount": 7500,
"shopId": "shop-001",
"status": "pending",
"requestReason": "Customer service adjustment",
"reviewReason": null,
"requestedById": "staff-123",
"reviewedById": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"reviewedAt": null
}
]
}
}
GET baseUrl/point/api/request/:id
Get a specific point request ticket by ID.
Request
Endpoint: GET baseUrl/point/api/request/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the request ticket
}
Example Request
GET baseUrl/point/api/request/b2c3d4e5-f6a7-8901-bcde-f12345678901
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"actionType": "EARN",
"amount": 150,
"purchaseAmount": 7500,
"shopId": "shop-001",
"categoryId": "cat-001",
"status": "approved",
"requestReason": "Customer service adjustment for missing points",
"reviewReason": "Verified purchase receipt, points granted",
"requestedById": "staff-123",
"reviewedById": "staff-456",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T11:00:00.000Z",
"reviewedAt": "2024-01-15T11:00:00.000Z"
}
}
Error Response (404 Not Found):
{
"statusCode": 404,
"data": {
"message": "Request not found",
"error": "Request not found"
}
}
Receipt Verification API
POST baseUrl/point/api/receipt/verify
Open a new receipt verification ticket (upload receipt image).
Request
Endpoint: POST baseUrl/point/api/receipt/verify
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Request Body (multipart/form-data):
{
memberId: string; // Required, UUID
image: File; // Required, receipt image file
}
Example Request
curl -X POST \
-H "Authorization: Bearer <token>" \
-F "memberId=550e8400-e29b-41d4-a716-446655440000" \
-F "image=@/path/to/receipt.jpg" \
/receipt/verify
Response
Success Response (201 Created):
{
"statusCode": 201,
"data": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PENDING",
"receiptImageUrl": "https://storage.example.com/receipts/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
},
"message": "Receipt verification ticket opened successfully"
}
POST baseUrl/point/api/receipt/verify/me
Open a receipt verification ticket for the authenticated user.
Request
Endpoint: POST baseUrl/point/api/receipt/verify/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Request Body (multipart/form-data):
{
image: File; // Required, receipt image file
}
Example Request
curl -X POST \
-H "Authorization: Bearer <token>" \
-F "image=@/path/to/receipt.jpg" \
/receipt/verify/me
Response
Success Response (201 Created):
{
"statusCode": 201,
"data": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PENDING",
"receiptImageUrl": "https://storage.example.com/receipts/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
},
"message": "Receipt verification ticket opened by me successfully"
}
GET baseUrl/point/api/receipt/verify
Get all receipt verification transactions with filtering and pagination.
Request
Endpoint: GET baseUrl/point/api/receipt/verify
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
memberId?: string; // Filter by member ID (UUID)
status?: "PENDING" | "PROCESSING" | "APPROVED" | "REJECTED"; // Filter by status
includeMember?: boolean; // Include member information (default: false)
includeShopRule?: boolean; // Include shop rule information (default: false)
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/point/api/receipt/verify?status=PENDING&includeMember=true&page=1&limit=20
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Receipt verify transactions retrieved successfully",
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 10,
"totalPages": 1
},
"data": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PENDING",
"receiptImageUrl": "https://storage.example.com/receipts/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg",
"money": null,
"pointRuleId": null,
"reviewReason": null,
"reviewById": null,
"shopId": null,
"categoryId": null,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"member": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"firstname": "John",
"lastname": "Doe"
}
}
]
}
}
}
GET baseUrl/point/api/receipt/verify/me
Get receipt verification transactions for the authenticated user.
Request
Endpoint: GET baseUrl/point/api/receipt/verify/me
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
Same as GET baseUrl/point/api/receipt/verify (excluding memberId as it's automatically set from the token)
Example Request
GET baseUrl/point/api/receipt/verify/me?status=PENDING&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Receipt verify transactions retrieved successfully",
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 3,
"totalPages": 1
},
"data": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PENDING",
"receiptImageUrl": "https://storage.example.com/receipts/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
}
GET baseUrl/point/api/receipt/verify/stats
Get receipt verification statistics.
Request
Endpoint: GET baseUrl/point/api/receipt/verify/stats
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Example Request
GET baseUrl/point/api/receipt/verify/stats
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"total": 100,
"pending": 15,
"processing": 5,
"approved": 70,
"rejected": 10
},
"message": "Receipt verify transactions stats retrieved successfully"
}
GET baseUrl/point/api/receipt/verify/:id
Get a specific receipt verification transaction by ID.
Request
Endpoint: GET baseUrl/point/api/receipt/verify/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the receipt verification transaction
}
Example Request
GET baseUrl/point/api/receipt/verify/c3d4e5f6-a7b8-9012-cdef-123456789012
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"status": "APPROVED",
"receiptImageUrl": "https://storage.example.com/receipts/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg",
"money": 5000,
"pointRuleId": "rule-123",
"reviewReason": "Receipt verified and points granted",
"reviewById": "staff-456",
"shopId": "shop-001",
"categoryId": "cat-001",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T11:00:00.000Z"
},
"message": "Receipt verify transaction retrieved successfully"
}
Error Response (404 Not Found):
{
"statusCode": 404,
"data": {
"message": "Receipt verify transaction not found",
"data": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
}
}
POST baseUrl/point/api/receipt/verify/:id
Verify a receipt automatically (auto verification).
Request
Endpoint: POST baseUrl/point/api/receipt/verify/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the receipt verification transaction
}
Request Body:
{
approved: boolean; // Required
receiptId: string; // Required, UUID of the extracted receipt
}
Example Request
{
"approved": true,
"receiptId": "receipt-abc123"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"status": "APPROVED",
"receiptImageUrl": "https://storage.example.com/receipts/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg",
"money": 5000,
"pointRuleId": "rule-123",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T11:00:00.000Z"
},
"message": "Receipt verified successfully"
}
POST baseUrl/point/api/receipt/verify/:id/manual
Verify a receipt manually (manual verification with reviewer input).
Request
Endpoint: POST baseUrl/point/api/receipt/verify/:id/manual
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the receipt verification transaction
}
Request Body:
{
approved: boolean; // Required
pointRuleId?: string; // Optional, UUID of point rule to apply
money?: number; // Optional, must be positive (> 0)
reviewReason: string; // Required, minimum 1 character
reviewById: string; // Required, UUID of reviewer
shopId?: string; // Optional shop ID
categoryId?: string; // Optional category ID
}
Example Request
{
"approved": true,
"pointRuleId": "rule-123",
"money": 5000,
"reviewReason": "Manually verified receipt, amount confirmed",
"reviewById": "staff-456",
"shopId": "shop-001",
"categoryId": "cat-001"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"status": "APPROVED",
"receiptImageUrl": "https://storage.example.com/receipts/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg",
"money": 5000,
"pointRuleId": "rule-123",
"reviewReason": "Manually verified receipt, amount confirmed",
"reviewById": "staff-456",
"shopId": "shop-001",
"categoryId": "cat-001",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T11:00:00.000Z"
},
"message": "Receipt verified manually successfully"
}
PUT baseUrl/point/api/receipt/verify/:id/manual
Change the status of a receipt verification manually (update existing verification).
Request
Endpoint: PUT baseUrl/point/api/receipt/verify/:id/manual
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the receipt verification transaction
}
Request Body: Same as POST baseUrl/point/api/receipt/verify/:id/manual
Example Request
{
"approved": false,
"reviewReason": "Receipt quality too low, cannot verify amount",
"reviewById": "staff-456"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"status": "REJECTED",
"receiptImageUrl": "https://storage.example.com/receipts/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg",
"reviewReason": "Receipt quality too low, cannot verify amount",
"reviewById": "staff-456",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T11:30:00.000Z"
},
"message": "Receipt status changed successfully"
}
Point Shop Rules API
GET baseUrl/point/api/point-rules/shop
Get all point shop rules with pagination.
Request
Endpoint: GET baseUrl/point/api/point-rules/shop
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
search?: string; // Search in rule name/description
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/point/api/point-rules/shop?page=1&limit=20&search=special&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 8,
"totalPages": 1
},
"data": [
{
"id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
"shopId": "shop-001",
"name_en": "Standard Rate",
"name_th": "อัตรามาตรฐาน",
"name_cn": "标准费率",
"description_en": "Standard point conversion rate",
"description_th": "อัตราการแปลงคะแนนมาตรฐาน",
"description_cn": "标准积分转换率",
"conversionRate": 0.02,
"isFixed": false,
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
GET baseUrl/point/api/point-rules/shop/default
Get the default conversion rate (where isFixed = true).
Request
Endpoint: GET baseUrl/point/api/point-rules/shop/default
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/point/api/point-rules/shop/default?locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "default-rule-id",
"shopId": null,
"name_en": "Default Rate",
"name_th": "อัตราเริ่มต้น",
"name_cn": "默认费率",
"description_en": "Default point conversion rate",
"description_th": "อัตราการแปลงคะแนนเริ่มต้น",
"description_cn": "默认积分转换率",
"conversionRate": 0.01,
"isFixed": true,
"isActive": true,
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
}
Error Response (404 Not Found):
{
"statusCode": 404,
"data": {
"error": "Default conversion rate not found",
"message": "Default conversion rate not found"
}
}
GET baseUrl/point/api/point-rules/shop/:id
Get a specific point shop rule by ID.
Request
Endpoint: GET baseUrl/point/api/point-rules/shop/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the point shop rule
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/point/api/point-rules/shop/d4e5f6a7-b8c9-0123-def4-567890abcdef?locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
"shopId": "shop-001",
"name_en": "Standard Rate",
"name_th": "อัตรามาตรฐาน",
"name_cn": "标准费率",
"description_en": "Standard point conversion rate",
"description_th": "อัตราการแปลงคะแนนมาตรฐาน",
"description_cn": "标准积分转换率",
"conversionRate": 0.02,
"isFixed": false,
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}
POST baseUrl/point/api/point-rules/shop
Create a new point shop rule.
Request
Endpoint: POST baseUrl/point/api/point-rules/shop
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
shopId?: string; // Optional shop ID (null for default rule)
name_en: string; // Required, English name
name_th?: string; // Optional, Thai name
name_cn?: string; // Optional, Chinese name
description_en: string; // Required, English description
description_th?: string; // Optional, Thai description
description_cn?: string; // Optional, Chinese description
conversionRate: number; // Required, conversion rate (e.g., 0.02 = 2%)
isFixed?: boolean; // Optional, default: false (true for default rule)
isActive?: boolean; // Optional, default: true
}
Example Request
{
"shopId": "shop-001",
"name_en": "Special Rate",
"name_th": "อัตราพิเศษ",
"name_cn": "特殊费率",
"description_en": "Special point conversion rate for this shop",
"description_th": "อัตราการแปลงคะแนนพิเศษสำหรับร้านนี้",
"description_cn": "此商店的特殊积分转换率",
"conversionRate": 0.03,
"isFixed": false,
"isActive": true
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point shop rule created successfully"
}
}
PUT baseUrl/point/api/point-rules/shop/default
Update the default conversion rate.
Request
Endpoint: PUT baseUrl/point/api/point-rules/shop/default
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Request Body:
{
conversionRate: number; // Required, new conversion rate
}
Example Request
{
"conversionRate": 0.015
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Default conversion rate updated successfully"
}
}
PUT baseUrl/point/api/point-rules/shop/:id
Update an existing point shop rule.
Request
Endpoint: PUT baseUrl/point/api/point-rules/shop/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the point shop rule
}
Request Body: All fields optional (same structure as POST)
Example Request
{
"conversionRate": 0.025,
"isActive": false
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point shop rule updated successfully"
}
}
PATCH baseUrl/point/api/point-rules/shop/:id/set-active
Set the active status of a point shop rule.
Request
Endpoint: PATCH baseUrl/point/api/point-rules/shop/:id/set-active
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the point shop rule
}
Request Body:
{
isActive: boolean; // Required
}
Example Request
{
"isActive": false
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point shop rule status updated successfully"
}
}
DELETE baseUrl/point/api/point-rules/shop/:id
Delete a point shop rule.
Request
Endpoint: DELETE baseUrl/point/api/point-rules/shop/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the point shop rule
}
Example Request
DELETE baseUrl/point/api/point-rules/shop/d4e5f6a7-b8c9-0123-def4-567890abcdef
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point shop rule deleted successfully"
}
}
Point Event Rules API
GET baseUrl/point/api/point-rules/event
Get all point event rules with pagination.
Request
Endpoint: GET baseUrl/point/api/point-rules/event
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Query Parameters:
{
page?: number; // Page number (default: 1, min: 1)
limit?: number; // Items per page (default: 10, min: 1, max: 100)
search?: string; // Search in rule name/description
tierIds?: string; // Comma-separated tier IDs
status?: string; // Filter by status
locale?: "en" | "th" | "cn"; // Localize response content
}
Example Request
GET baseUrl/point/api/point-rules/event?page=1&limit=20&tierIds=tier-1,tier-2&locale=en
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"meta": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1
},
"data": [
{
"id": "e5f6a7b8-c9d0-1234-efab-567890abcdef",
"name_en": "Birthday Bonus",
"name_th": "โบนัสวันเกิด",
"name_cn": "生日奖励",
"description_en": "Extra points on member birthday",
"description_th": "คะแนนพิเศษในวันเกิดสมาชิก",
"description_cn": "会员生日额外积分",
"pointAmount": 100,
"tierIds": ["tier-1", "tier-2"],
"imageUrl": "https://storage.example.com/events/birthday.jpg",
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
]
}
}
GET baseUrl/point/api/point-rules/event/:id
Get a specific point event rule by ID.
Request
Endpoint: GET baseUrl/point/api/point-rules/event/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the point event rule
}
Query Parameters:
{
locale?: "en" | "th" | "cn"; // Localize response content
includeTierInfo?: boolean; // Include tier information (default: false)
}
Example Request
GET baseUrl/point/api/point-rules/event/e5f6a7b8-c9d0-1234-efab-567890abcdef?locale=en&includeTierInfo=true
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"id": "e5f6a7b8-c9d0-1234-efab-567890abcdef",
"name_en": "Birthday Bonus",
"name_th": "โบนัสวันเกิด",
"name_cn": "生日奖励",
"description_en": "Extra points on member birthday",
"description_th": "คะแนนพิเศษในวันเกิดสมาชิก",
"description_cn": "会员生日额外积分",
"pointAmount": 100,
"tierIds": ["tier-1", "tier-2"],
"imageUrl": "https://storage.example.com/events/birthday.jpg",
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z",
"tiers": [
{
"id": "tier-1",
"name_en": "Gold",
"level": 3
}
]
}
}
POST baseUrl/point/api/point-rules/event
Create a new point event rule.
Request
Endpoint: POST baseUrl/point/api/point-rules/event
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Request Body (multipart/form-data):
{
name_en: string; // Required, English name
name_th?: string; // Optional, Thai name
name_cn?: string; // Optional, Chinese name
description_en: string; // Required, English description
description_th?: string; // Optional, Thai description
description_cn?: string; // Optional, Chinese description
pointAmount: number; // Required, point amount to award
tierIds?: string[]; // Optional array of tier IDs
image?: File; // Optional image file
isActive?: boolean; // Optional, default: true
}
Example Request
{
"name_en": "First Purchase Bonus",
"name_th": "โบนัสซื้อครั้งแรก",
"name_cn": "首次购买奖励",
"description_en": "Bonus points for first purchase",
"description_th": "คะแนนโบนัสสำหรับการซื้อครั้งแรก",
"description_cn": "首次购买奖励积分",
"pointAmount": "200",
"tierIds": "[\"tier-1\"]",
"isActive": "true"
}
Note: When using multipart/form-data, arrays in tierIds should be stringified JSON strings.
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point event rule created successfully"
}
}
PUT baseUrl/point/api/point-rules/event/:id
Update an existing point event rule.
Request
Endpoint: PUT baseUrl/point/api/point-rules/event/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
Path Parameters:
{
id: string; // UUID of the point event rule
}
Request Body (multipart/form-data): All fields optional (same structure as POST)
Example Request
{
"pointAmount": "250",
"isActive": "false"
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point event rule updated successfully"
}
}
PATCH baseUrl/point/api/point-rules/event/:id/set-active
Set the active status of a point event rule.
Request
Endpoint: PATCH baseUrl/point/api/point-rules/event/:id/set-active
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Path Parameters:
{
id: string; // UUID of the point event rule
}
Request Body:
{
isActive: boolean; // Required
}
Example Request
{
"isActive": false
}
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point event rule status updated successfully"
}
}
DELETE baseUrl/point/api/point-rules/event/:id
Delete a point event rule.
Request
Endpoint: DELETE baseUrl/point/api/point-rules/event/:id
Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Path Parameters:
{
id: string; // UUID of the point event rule
}
Example Request
DELETE baseUrl/point/api/point-rules/event/e5f6a7b8-c9d0-1234-efab-567890abcdef
Response
Success Response (200 OK):
{
"statusCode": 200,
"data": {
"message": "Point event rule deleted successfully"
}
}
Data Types & Enums
Action Types
EARN- Points earnedSPEND- Points spentEXPIRE- Points expired
Source Types
Manual- Manually added/spent by adminReward- From reward redemptionCampaign- From campaign/promotionPurchase- From purchase transaction
Receipt Verification Status
PENDING- Pending verificationPROCESSING- Being processedAPPROVED- Approved and points grantedREJECTED- Rejected
Point Request Ticket Status
pending- Awaiting reviewapproved- Approved and processedrejected- Rejected
Locales
en- Englishth- Thaicn- Chinese (Simplified)
Error Responses
All error responses follow this structure:
{
"statusCode": <HTTP_STATUS_CODE>,
"data": {
"message": "<Error message>",
"errors": [ // Optional, for validation errors
{
"field": "<field_name>",
"message": "<validation message>"
}
]
}
}
Common HTTP Status Codes
200 OK- Request succeeded201 Created- Resource created successfully400 Bad Request- Invalid request data or business rule violation401 Unauthorized- Missing or invalid authentication token403 Forbidden- Insufficient permissions404 Not Found- Resource not found500 Internal Server Error- Server error
Notes
-
Authentication: Most endpoints require a valid JWT token in the
Authorizationheader. The token'ssubfield should contain the member/user ID. -
Multipart Form Data: When uploading receipt images or event rule images, use
multipart/form-datacontent type. JSON objects/arrays in form fields should be stringified JSON strings. -
Localization: Use the
localequery parameter to get localized content. If not specified, all language fields (name_en,name_th,name_cn, etc.) will be returned. -
Image URLs: Image URLs are automatically generated and mapped from the uploaded files. They are returned as full URLs in the response.
-
Point Balance:
- Balance is calculated in real-time from all transactions
- Purchase amount balance tracks total purchase amount for a member
- Date range filters allow querying balance at specific points in time
-
Receipt Verification:
- Receipts can be verified automatically (with receipt extraction) or manually (by staff)
- Approved receipts automatically grant points based on configured shop rules
- Rejected receipts do not grant points
-
Point Rules:
- Shop rules define conversion rates per shop (e.g., 2% of purchase amount)
- Event rules define fixed point amounts for specific events/tiers
- Default shop rule (
isFixed: true) applies when no shop-specific rule exists
-
Point Request Tickets:
- Used for manual point adjustments requiring approval workflow
- Staff members create requests, other staff members review and approve/reject
- Approved requests automatically create point transactions
-
Date Formats: All date fields use ISO 8601 format or standard date strings (e.g.,
2024-12-31T23:59:59.000Zor2024-12-31). -
Pagination: Pagination defaults to page 1 with 10 items per page. Maximum limit is 100 items per page.
Support
For issues or questions, please contact the development team or refer to the main project documentation.