Skip to main content

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 earned
  • SPEND - Points spent
  • EXPIRE - Points expired

Source Types

  • Manual - Manually added/spent by admin
  • Reward - From reward redemption
  • Campaign - From campaign/promotion
  • Purchase - From purchase transaction

Receipt Verification Status

  • PENDING - Pending verification
  • PROCESSING - Being processed
  • APPROVED - Approved and points granted
  • REJECTED - Rejected

Point Request Ticket Status

  • pending - Awaiting review
  • approved - Approved and processed
  • rejected - Rejected

Locales

  • en - English
  • th - Thai
  • cn - 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 succeeded
  • 201 Created - Resource created successfully
  • 400 Bad Request - Invalid request data or business rule violation
  • 401 Unauthorized - Missing or invalid authentication token
  • 403 Forbidden - Insufficient permissions
  • 404 Not Found - Resource not found
  • 500 Internal Server Error - Server error

Notes

  1. Authentication: Most endpoints require a valid JWT token in the Authorization header. The token's sub field should contain the member/user ID.

  2. Multipart Form Data: When uploading receipt images or event rule images, use multipart/form-data content type. JSON objects/arrays in form fields should be stringified JSON strings.

  3. Localization: Use the locale query parameter to get localized content. If not specified, all language fields (name_en, name_th, name_cn, etc.) will be returned.

  4. Image URLs: Image URLs are automatically generated and mapped from the uploaded files. They are returned as full URLs in the response.

  5. 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
  6. 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
  7. 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
  8. 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
  9. Date Formats: All date fields use ISO 8601 format or standard date strings (e.g., 2024-12-31T23:59:59.000Z or 2024-12-31).

  10. 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.