Chuyển tới nội dung chính

Voucher Shop Service API

Overview

API dành cho V-App (Customer-facing) để hiển thị và mua bán voucher trên Voucher Shop. Service cung cấp các chức năng:

  • Duyệt & Tìm kiếm voucher — hiển thị danh sách voucher, lọc theo category/tag, tìm kiếm theo từ khóa
  • Campaign management — hiển thị campaign carousel, danh sách campaign, chi tiết campaign
  • Purchase flow — reserve → commit → release voucher qua checkout flow

Service Name: promotion-shop-service

Environments

Môi trườngBase URL
DEVhttps://promotion-shop-service.dev.vsf.services
SIThttps://promotion-shop-service.sit.vsf.services
UAThttps://promotion-shop-service.uat.vsf.services
Productionhttps://promotion-shop-service.prd.vsf.services

Endpoints Summary

#EndpointMethodPurpose
1/api/v1/vouchersGETDanh sách voucher (browse, filter theo tag/category/campaign)
2/api/v1/vouchers/\{id\}GETChi tiết voucher
3/api/v1/vouchers/searchGETTìm kiếm voucher theo từ khóa
4/api/v1/campaigns/carouselGETCarousel campaign cho trang chủ
5/api/v1/campaignsGETDanh sách campaign
6/api/v1/campaigns/\{id\}GETChi tiết campaign
7/api/v1/vouchers/reservePOSTReserve voucher cho checkout
8/api/v1/vouchers/commitPOSTCommit purchase sau payment thành công
9/api/v1/vouchers/releasePOSTRelease/cancel reservation
10/api/v1/vouchers/reservation/\{key\}GETCheck trạng thái reservation

Sequence Diagrams

Browse & Search Flow

V-App Client              Shop Service              Database
| | |
| GET /vouchers | |
| (tag, category, page) | |
|------------------------->| Query vouchers |
| |------------------------>|
| | vouchers + inventory |
| |<------------------------|
| Page<ShopVoucherResponse>| |
|<-------------------------| |
| | |
| GET /vouchers/search | |
| (q=keyword) | |
|------------------------->| Search PM + Campaign |
| |------------------------>|
| Page<ShopVoucherResponse>| |
|<-------------------------| |

Purchase Flow

V-App Client              Shop Service              Inventory Service
| | |
| POST /vouchers/reserve | |
| (orderId, items) | |
|------------------------->| Reserve inventory |
| |------------------------>|
| | Issue voucher codes |
| |<------------------------|
| reservationKey, items | |
|<-------------------------| |
| | |
| [Payment success] | |
| POST /vouchers/commit | |
| (reservationKey) | |
|------------------------->| Confirm purchase |
| |------------------------>|
| committedAt | |
|<-------------------------| |
| | |
| [Payment fail/cancel] | |
| POST /vouchers/release | |
| (reservationKey) | |
|------------------------->| Release inventory |
| |------------------------>|
| releasedAt | |
|<-------------------------| |

Purchase State Transition

StateÝ nghĩa
RESERVEDĐã reserve cho order; chờ commit hoặc release
COMMITTEDĐã commit sau payment thành công, voucher cấp vào wallet
RELEASEDReservation bị huỷ, inventory trả về
[Reserve]          [Commit]
| |
v v
RESERVED --------> COMMITTED
|
| [Release]
v
RELEASED

Authentication

Shop Service sử dụng API Key để xác thực và phân biệt merchant. Mỗi merchant được cấp một X-Promotion-Api-Key riêng khi onboard.

FieldMô tảQuản lý bởi
X-Promotion-Api-KeyAPI Key xác định merchant. Có expiry và statusPromotion Service

Common Headers

Header Definitions:

HeaderRequiredDescription
client_idYesClient ID để authenticate
client_secretYesClient secret để authenticate
X-Promotion-Api-KeyYesAPI Key xác định merchant
X-Merchant-IdYesMerchant identifier (ví dụ: vinfast, vgreen)
X-User-IdYesCustomer user ID. Xác định theo merchant hoặc org tương ứng
X-Request-IdYesUnique request ID cho tracing (UUID)
X-Idempotency-KeyYes (/reserve)Ngăn duplicate operations. Format: UUID. Gửi lại cùng key sẽ trả response giống lần đầu
Content-TypeYes (POST)application/json
AcceptNoapplication/json

Common Response Format

Tất cả API response đều tuân theo cấu trúc:

{
"code": "SUCCESS",
"message": "Success",
"data": { ... }
}

Paginated Response:

{
"code": "SUCCESS",
"message": "Success",
"data": {
"content": [ ... ],
"page": {
"size": 20,
"number": 0,
"totalElements": 100,
"totalPages": 5
}
}
}

I. Voucher APIs

1. GET /api/v1/vouchers

Lấy danh sách voucher đang bán trên Voucher Shop. Hỗ trợ filter theo tag, category, campaign.

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/vouchers?tag=HOT_DEAL&page=0&size=10' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}'

Query Parameters

ParameterTypeRequiredDefaultDescription
tagstringNoFilter theo single campaign tag (HOT_DEAL, FLASH_SALE, ...)
tagsstringNoFilter theo multiple tags, phân cách bởi dấu phẩy (HOT_DEAL,FLASH_SALE)
categoryIdsarray[long]NoFilter theo danh mục voucher
campaignIdsarray[long]NoChỉ lấy voucher từ campaign IDs cụ thể
excludeCampaignIdsarray[long]NoLoại trừ voucher từ campaign IDs
channelstringNoV-APPKênh hiển thị (V-APP, WEB)
pageintNo0Trang (0-indexed)
sizeintNo20Số lượng mỗi trang (max 50)

Lưu ý: Nếu cả tagtags đều có, tags được ưu tiên. Chỉ hiển thị voucher từ campaign APPROVED + ACTIVE + type SELL.

Response

{
"code": "SUCCESS",
"data": {
"content": [
{
"id": 402,
"categoryId": 14,
"miniAppIds": ["vinmec.v-app.vn"],
"campaign": {
"id": 1171,
"name": "Voucher giảm giá giảm giá của Vgreen khi sạc",
"tag": "HOT_DEAL",
"displayPriority": 1,
"validity": {
"startAt": "2026-03-15T08:25:00Z",
"endAt": "2028-05-31T16:59:00Z"
},
"countdown": null
},
"display": {
"title": "Voucher giảm giá 30K cho đơn hàng 100k",
"shortTitle": "Voucher giảm giá 30K cho đơn hàng 100k",
"brandName": "VGREEN",
"logoUrl": "https://core-services-uploader-public.sit.vsf.services/promotion/promotion_master_logo/.../ffffff.png",
"bannerUrl": "https://core-services-uploader-public.sit.vsf.services/promotion/promotion_master_banner/.../ffffff1.png",
"thumbnailUrl": "https://core-services-uploader-public.sit.vsf.services/promotion/promotion_master_logo/.../ffffff.png",
"description": "Voucher giảm giá 30K cho đơn hàng 100k"
},
"pricing": {
"faceValue": 30000.00,
"sellingPrice": 27000.00,
"discountedPrice": null,
"currency": "VND",
"discountPercentage": 10
},
"inventory": {
"totalQuantity": 40,
"remainingQuantity": 3,
"isOutOfStock": false
},
"userPurchaseUsage": {
"dailyPurchasedQty": 0,
"totalPurchasedQty": 2,
"dailyRemaining": 3,
"totalRemaining": null
},
"validity": null,
"terms": {
"usageType": "ONLINE",
"termsAndConditions": "Voucher giảm giá 30K cho đơn hàng 100k",
"validityDays": 1,
"usageLimit": null,
"maxPerDay": 3,
"maxPerOrder": null
}
}
],
"page": {
"size": 10,
"number": 0,
"totalElements": 15,
"totalPages": 2
}
},
"message": "Success"
}

Response Fields

FieldTypeDescription
idnumberVoucher pool ID
categoryIdnumberMã danh mục voucher
miniAppIdsarray[string]Danh sách MiniApp IDs được phép sử dụng voucher
campaignobjectThông tin campaign chứa voucher
campaign.idnumberCampaign ID
campaign.namestringTên campaign
campaign.tagstringTag phân loại (HOT_DEAL, FLASH_SALE)
campaign.displayPrioritynumberThứ tự ưu tiên hiển thị (1 = cao nhất)
campaign.validityobjectThời gian hiệu lực campaign
campaign.validity.startAtstring (ISO 8601)Ngày bắt đầu
campaign.validity.endAtstring (ISO 8601)Ngày kết thúc
campaign.countdownobject/nullThông tin đếm ngược (nếu Flash Sale)
campaign.countdown.endAtstring (ISO 8601)Thời điểm kết thúc countdown
campaign.countdown.remainingSecondsnumberSố giây còn lại
displayobjectThông tin hiển thị (lấy từ PromotionMaster)
display.titlestringTiêu đề đầy đủ
display.shortTitlestringTiêu đề ngắn gọn (hiển thị trên card)
display.brandNamestringTên thương hiệu
display.logoUrlstringURL logo
display.bannerUrlstringURL banner
display.thumbnailUrlstringURL thumbnail
display.descriptionstringMô tả chi tiết
pricingobjectThông tin giá
pricing.faceValuenumberMệnh giá voucher (VND/VPOINT)
pricing.sellingPricenumberGiá bán
pricing.discountedPricenumber/nullGiá sau giảm (nếu có)
pricing.currencystringLoại tiền (VND, VPOINT)
pricing.discountPercentagenumberPhần trăm giảm so với mệnh giá
inventoryobjectThông tin tồn kho
inventory.totalQuantitynumberTổng số lượng
inventory.remainingQuantitynumberSố lượng còn lại
inventory.isOutOfStockbooleantrue nếu hết hàng
userPurchaseUsageobject/nullThông tin mua hàng của user (khi có X-User-Id)
userPurchaseUsage.dailyPurchasedQtynumberSố lượng đã mua trong ngày
userPurchaseUsage.totalPurchasedQtynumberTổng số lượng đã mua
userPurchaseUsage.dailyRemainingnumber/nullSố lượng còn được mua trong ngày
userPurchaseUsage.totalRemainingnumber/nullTổng số lượng còn được mua
termsobjectĐiều kiện sử dụng
terms.usageTypestringLoại sử dụng (ONLINE, OFFLINE)
terms.termsAndConditionsstringĐiều khoản & điều kiện
terms.validityDaysnumberSố ngày hiệu lực sau khi mua
terms.usageLimitnumber/nullGiới hạn sử dụng tổng
terms.maxPerDaynumber/nullGiới hạn mua/ngày
terms.maxPerOrdernumber/nullGiới hạn mua/đơn

2. GET /api/v1/vouchers/{id}

Chi tiết một voucher cụ thể, bao gồm thông tin mua hàng của user (nếu có X-User-Id).

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/vouchers/402' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}'

Path Parameters

ParameterTypeRequiredDescription
idlongYesVoucher pool ID

Response

Trả về cùng cấu trúc ShopVoucherResponse như endpoint list (xem mục 1), nhưng không wrap trong page.

{
"code": "SUCCESS",
"data": {
"id": 402,
"categoryId": 14,
"campaign": { ... },
"display": { ... },
"pricing": { ... },
"inventory": { ... },
"userPurchaseUsage": { ... },
"terms": { ... }
},
"message": "Success"
}

Error Responses

HTTPCodeMô tả
404SHOP-VOU-001Voucher không tồn tại hoặc đã bị xóa

3. GET /api/v1/vouchers/search

Tìm kiếm voucher theo từ khóa. Hỗ trợ tìm kiếm tiếng Việt có dấu. Tìm trong tên campaign, tiêu đề voucher, mô tả, và tên thương hiệu.

cURL Example

curl -G 'https://promotion-shop-service.sit.vsf.services/api/v1/vouchers/search' \
--data-urlencode 'q=giảm giá' \
-d 'page=0&size=20' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}'

Query Parameters

ParameterTypeRequiredDefaultDescription
qstringYesTừ khóa tìm kiếm (tối thiểu 2 ký tự, hỗ trợ Unicode/tiếng Việt)
tagstringNoFilter thêm theo campaign tag
channelstringNoV-APPKênh hiển thị (V-APP, WEB)
pageintNo0Trang (0-indexed)
sizeintNo20Số lượng mỗi trang (max 100)

Quan trọng: Từ khóa q phải từ 2 ký tự trở lên. Nếu < 2 ký tự sẽ trả lỗi SHOP-VOU-002.

Lưu ý encoding: Client phải URL-encode đúng UTF-8 cho từ khóa tiếng Việt có dấu. Ví dụ: giảm giági%E1%BA%A3m+gi%C3%A1.

Response

Cùng cấu trúc phân trang như endpoint list (xem mục 1).

{
"code": "SUCCESS",
"data": {
"content": [ ... ],
"page": {
"size": 20,
"number": 0,
"totalElements": 5,
"totalPages": 1
}
},
"message": "Success"
}

Error Responses

HTTPCodeMô tả
400SHOP-VOU-002Từ khóa tìm kiếm quá ngắn (< 2 ký tự)

II. Campaign APIs

4. GET /api/v1/campaigns/carousel

Lấy danh sách campaign hiển thị dạng carousel trên trang chủ Voucher Shop.

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/campaigns/carousel?page=0&size=5' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}'

Query Parameters

ParameterTypeRequiredDefaultDescription
channelstringNoKênh hiển thị (filter banner theo channel)
pageintNo0Trang (0-indexed)
sizeintNo10Số lượng mỗi trang

Response

{
"code": "SUCCESS",
"data": {
"content": [
{
"id": 1171,
"code": "VGREEN-SALE-2026",
"name": "Voucher giảm giá của Vgreen khi sạc",
"description": "Mô tả campaign",
"campaignTag": "HOT_DEAL",
"displayPriority": 1,
"themeUrl": "https://example.com/theme.png",
"startAt": "2026-03-15T08:25:00Z",
"endAt": "2028-05-31T16:59:00Z",
"presale": null,
"banners": [
{
"id": 101,
"channelCode": "APP",
"positionCode": "CAROUSEL",
"assetUrl": "https://core-services-uploader-public.sit.vsf.services/promotion/.../banner.png",
"assetType": "IMAGE",
"altText": "Banner mô tả",
"clickUrl": null,
"displayOrder": 1
}
],
"voucherSummary": {
"totalCount": 3,
"availableCount": 2,
"soldCount": 15,
"displayedCount": 3
},
"vouchers": null
}
],
"page": {
"size": 5,
"number": 0,
"totalElements": 3,
"totalPages": 1
}
},
"message": "Success"
}

Response Fields — ShopCampaignResponse

FieldTypeDescription
idnumberCampaign ID
codestringMã campaign
namestringTên campaign
descriptionstringMô tả campaign
campaignTagstringTag phân loại (HOT_DEAL, FLASH_SALE)
displayPrioritynumberThứ tự ưu tiên (1 = cao nhất)
themeUrlstringURL theme/background campaign
startAtstring (ISO 8601)Ngày bắt đầu
endAtstring (ISO 8601)Ngày kết thúc
presaleobject/nullThông tin presale (nếu có)
presale.enabledbooleanPresale đang bật
presale.startAtstring (ISO 8601)Thời gian bắt đầu presale
presale.countdownEnabledbooleanHiển thị countdown
presale.teaserTextstringText teaser presale
presale.teaserImageUrlstringURL ảnh teaser
bannersarrayDanh sách banner
banners[].idnumberBanner ID
banners[].channelCodestringKênh (APP, WEB)
banners[].positionCodestringVị trí (CAROUSEL, DETAIL_TOP)
banners[].assetUrlstringURL asset (ảnh/video)
banners[].assetTypestringLoại asset (IMAGE, VIDEO)
banners[].altTextstringAlt text
banners[].clickUrlstringURL khi click vào banner
banners[].displayOrdernumberThứ tự hiển thị
voucherSummaryobjectTổng hợp voucher trong campaign
voucherSummary.totalCountnumberTổng số voucher pool
voucherSummary.availableCountnumberSố voucher pool còn hàng
voucherSummary.soldCountnumberTổng số voucher đã bán
voucherSummary.displayedCountnumberSố voucher đang hiển thị
vouchersarray/nullDanh sách voucher (khi includeVouchers=true)

5. GET /api/v1/campaigns

Lấy danh sách campaign theo tag. Hỗ trợ kèm danh sách voucher trong mỗi campaign khi bật includeVouchers=true.

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/campaigns?tags=HOT_DEAL,FLASH_SALE&includeVouchers=true&vouchersPerCampaign=5&page=0&size=10' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}'

Query Parameters

ParameterTypeRequiredDefaultDescription
tagstringNoFilter single tag
tagsstringNoFilter multiple tags (phân cách bởi ,). Ưu tiên hơn tag
includeVouchersbooleanNofalseKèm danh sách voucher trong mỗi campaign
vouchersPerCampaignintNo10Số voucher tối đa mỗi campaign (max 50)
channelstringNoFilter banner theo channel
pageintNo0Trang (0-indexed)
sizeintNo20Số lượng mỗi trang

Response

Cùng cấu trúc ShopCampaignResponse như endpoint carousel (xem mục 4). Khi includeVouchers=true, field vouchers sẽ chứa mảng ShopVoucherResponse.


6. GET /api/v1/campaigns/{id}

Lấy thông tin chi tiết một campaign, bao gồm banner, presale info, và tổng hợp voucher.

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/campaigns/1171' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}'

Path Parameters

ParameterTypeRequiredDescription
idlongYesCampaign ID

Query Parameters

ParameterTypeRequiredDefaultDescription
channelstringNoFilter banner theo channel

Response

{
"code": "SUCCESS",
"data": {
"id": 1171,
"code": "VGREEN-SALE-2026",
"name": "Voucher giảm giá của Vgreen khi sạc",
"description": "Mô tả chi tiết campaign...",
"campaignTag": "HOT_DEAL",
"displayPriority": 1,
"themeUrl": null,
"startAt": "2026-03-15T08:25:00Z",
"endAt": "2028-05-31T16:59:00Z",
"presale": null,
"banners": [ ... ],
"voucherSummary": {
"totalCount": 3,
"availableCount": 2,
"soldCount": 15,
"displayedCount": 3
},
"vouchers": null
},
"message": "Success"
}

Error Responses

HTTPCodeMô tả
404SHOP-CAM-001Campaign không tồn tại hoặc không APPROVED + ACTIVE

III. Purchase APIs

Purchase Flow Overview

Quy trình mua voucher trên V-App tuân theo flow Reserve → Commit/Release:

  1. Reserve — User chọn voucher + số lượng → app gọi /reserve để hold voucher
  2. Payment — User thanh toán qua payment gateway
  3. Commit — Payment thành công → app gọi /commit để xác nhận mua
  4. Release — Payment thất bại/cancel → app gọi /release để huỷ reservation

Ghi chú: Một số tính năng mở rộng (batch purchase, partial commit, auto-expire reservation, v.v.) hiện chưa được hỗ trợ. Nếu client có yêu cầu riêng, vui lòng liên hệ team backend để thảo luận.


7. POST /api/v1/vouchers/reserve

Reserve voucher cho checkout. Tạo reservation key, hold inventory, issue voucher codes.

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/vouchers/reserve' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}' \
--header 'X-Idempotency-Key: {your-idempotency-key}' \
--header 'Content-Type: application/json' \
--data '{
"orderId": "ORDER-20260319-001",
"amount": 54000,
"currency": "VND",
"items": [
{
"voucherPoolId": 402,
"unitPrice": 27000,
"quantity": 2
}
]
}'

Request

{
"orderId": "ORDER-20260319-001",
"amount": 54000,
"currency": "VND",
"items": [
{
"voucherPoolId": 402,
"unitPrice": 27000.00,
"quantity": 2
}
]
}

Request Fields:

FieldTypeRequiredDescription
orderIdstringYesOrder ID từ hệ thống đặt hàng (unique)
amountnumberYesTổng tiền thanh toán (≥ 0)
currencystringNoLoại tiền (VND, VPOINT). Default: VND
itemsarrayYesDanh sách voucher cần reserve
items[].voucherPoolIdnumberYesVoucher pool ID (từ API list vouchers)
items[].unitPricenumberYesGiá đơn vị (phải khớp với sellingPrice của pool)
items[].quantitynumberYesSố lượng (≥ 1)

Response

{
"code": "SUCCESS",
"data": {
"reservationKey": "{your-reservation-key}",
"orderId": "ORDER-20260319-001",
"reservedAt": "2026-03-19T01:30:00Z",
"items": [
{
"voucherPoolId": 402,
"quantity": 2,
"vouchers": [
{
"issuedPromotionId": 1500,
"voucherCode": "VCH-ABC123",
"faceValue": 30000.00,
"sellingPrice": 27000.00,
"currency": "VND",
"purchasedAt": "2026-03-19T01:30:00Z",
"expiresAt": "2026-03-20T01:30:00Z"
},
{
"issuedPromotionId": 1501,
"voucherCode": "VCH-DEF456",
"faceValue": 30000.00,
"sellingPrice": 27000.00,
"currency": "VND",
"purchasedAt": "2026-03-19T01:30:00Z",
"expiresAt": "2026-03-20T01:30:00Z"
}
]
}
]
},
"message": "Success"
}

Response Fields:

FieldTypeDescription
reservationKeystringUUID unique cho reservation. Dùng cho /commit/release
orderIdstringOrder ID (trùng request)
reservedAtstring (ISO 8601)Thời điểm reserve
itemsarrayDanh sách voucher đã reserve
items[].voucherPoolIdnumberVoucher pool ID
items[].quantitynumberSố lượng đã reserve
items[].vouchersarrayChi tiết từng voucher code đã issue
items[].vouchers[].issuedPromotionIdnumberID voucher đã cấp
items[].vouchers[].voucherCodestringMã voucher (đã decrypt)
items[].vouchers[].faceValuenumberMệnh giá
items[].vouchers[].sellingPricenumberGiá bán
items[].vouchers[].currencystringLoại tiền
items[].vouchers[].purchasedAtstring (ISO 8601)Thời điểm mua
items[].vouchers[].expiresAtstring (ISO 8601)Thời điểm hết hạn voucher

Error Responses

HTTPCodeMô tả
400SHOP-PUR-001Tổng tiền amount không khớp với giá × số lượng
400SHOP-PUR-012Voucher pool hết hàng hoặc không đủ số lượng
400SHOP-PUR-014Giá unitPrice không khớp với sellingPrice hiện tại
400SHOP-PUR-015Loại tiền currency không khớp với voucher pool
400SHOP-PUR-016Vượt giới hạn mua/ngày hoặc tổng giới hạn mua
404SHOP-VOU-001Voucher pool không tồn tại hoặc đã bị xóa
409SHOP-PUR-011Order ID đã được sử dụng cho reservation khác

8. POST /api/v1/vouchers/commit

Xác nhận mua voucher sau khi payment thành công. Chuyển reservation sang trạng thái COMMITTED, voucher được cấp vào wallet user.

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/vouchers/commit' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}' \
--header 'Content-Type: application/json' \
--data '{
"reservationKey": "{your-reservation-key}"
}'

Request

{
"reservationKey": "{your-reservation-key}"
}

Request Fields:

FieldTypeRequiredDescription
reservationKeystringYesReservation key từ /reserve

Response

{
"code": "SUCCESS",
"data": {
"reservationKey": "{your-reservation-key}",
"orderId": "ORDER-20260319-001",
"committedAt": "2026-03-19T01:35:00Z",
"items": [
{
"voucherPoolId": 402,
"quantity": 2,
"vouchers": [
{
"issuedPromotionId": 1500,
"voucherCode": "VCH-ABC123",
"faceValue": 30000.00,
"sellingPrice": 27000.00,
"currency": "VND",
"purchasedAt": "2026-03-19T01:30:00Z",
"expiresAt": "2026-03-20T01:30:00Z"
}
]
}
]
},
"message": "Success"
}

Response Fields:

FieldTypeDescription
reservationKeystringReservation key (trùng request)
orderIdstringOrder ID gắn với reservation
committedAtstring (ISO 8601)Thời điểm commit
itemsarrayDanh sách voucher đã commit (cùng cấu trúc như /reserve)

Error Responses

HTTPCodeMô tả
400SHOP-PUR-006Reservation đã commit hoặc đã release (không thể commit lại)
404SHOP-PUR-003Reservation không tồn tại hoặc không thuộc user

9. POST /api/v1/vouchers/release

Huỷ reservation, trả voucher về inventory. Gọi khi payment thất bại hoặc user cancel đơn hàng.

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/vouchers/release' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}' \
--header 'Content-Type: application/json' \
--data '{
"reservationKey": "{your-reservation-key}",
"reason": "PAYMENT_FAILED"
}'

Request

{
"reservationKey": "{your-reservation-key}",
"reason": "PAYMENT_FAILED"
}

Request Fields:

FieldTypeRequiredDescription
reservationKeystringYesReservation key từ /reserve
reasonstringNoLý do release (PAYMENT_FAILED, USER_CANCELLED, TIMEOUT, ...). Dùng cho logging

Response

{
"code": "SUCCESS",
"data": {
"reservationKey": "{your-reservation-key}",
"orderId": "ORDER-20260319-001",
"releasedAt": "2026-03-19T01:40:00Z"
},
"message": "Success"
}

Response Fields:

FieldTypeDescription
reservationKeystringReservation key (trùng request)
orderIdstringOrder ID gắn với reservation
releasedAtstring (ISO 8601)Thời điểm release

Error Responses

HTTPCodeMô tả
400SHOP-PUR-007Reservation đã release hoặc đã commit (không thể release)
400SHOP-PUR-006Reservation đã bị hết hạn
404SHOP-PUR-003Reservation không tồn tại hoặc không thuộc user

10. GET /api/v1/vouchers/reservation/{key}

Check trạng thái reservation hiện tại. Dùng để polling hoặc verify trạng thái reservation trước khi thực hiện hành động.

cURL Example

curl --location 'https://promotion-shop-service.sit.vsf.services/api/v1/vouchers/reservation/{your-reservation-key}' \
--header 'client_id: {your-client-id}' \
--header 'client_secret: {your-client-secret}' \
--header 'X-Promotion-Api-Key: {your-api-key}' \
--header 'X-Merchant-Id: {your-merchant-id}' \
--header 'X-User-Id: 349874708' \
--header 'X-Request-Id: {your-request-id}'

Path Parameters

ParameterTypeRequiredDescription
keystringYesReservation key

Response

{
"code": "SUCCESS",
"data": {
"reservationKey": "{your-reservation-key}",
"status": "RESERVED",
"reservedAt": "2026-03-19T01:30:00Z",
"expiresAt": "2026-03-19T01:45:00Z",
"items": [
{
"voucherPoolId": 402,
"quantity": 2,
"vouchers": [ ... ]
}
]
},
"message": "Success"
}

Response Fields:

FieldTypeDescription
reservationKeystringReservation key
statusstringTrạng thái: RESERVED, COMMITTED, RELEASED
reservedAtstring (ISO 8601)Thời điểm tạo reservation
expiresAtstring (ISO 8601)Thời điểm reservation hết hạn
itemsarrayDanh sách voucher trong reservation

Error Responses

HTTPCodeMô tả
404SHOP-PUR-003Reservation không tồn tại hoặc không thuộc user

IV. Integration Notes

Authentication

Xem mục Authentication ở đầu tài liệu để biết cách lấy X-Promotion-Api-Key.

Encoding

  • Tất cả request/response đều sử dụng UTF-8
  • Query parameters tiếng Việt phải được URL-encoded đúng (RFC 3986)
  • Ví dụ: giảm giági%E1%BA%A3m+gi%C3%A1

Pagination

  • Tất cả list APIs đều hỗ trợ pagination
  • Page bắt đầu từ 0 (zero-indexed)
  • Default size: 20, max size: 50 (search: max 100)

Idempotency

  • /reserve — Hỗ trợ idempotency qua header X-Idempotency-Key. Gửi lại cùng key sẽ trả lỗi SHOP-PUR-011 (409 Conflict) thay vì tạo reservation mới
  • /commit — Idempotent. Gọi lại với cùng reservationKey không tạo side effect mới
  • /release — Idempotent. Gọi lại với cùng reservationKey không tạo side effect mới

Reservation Lifecycle

  • Reservation hiện không có auto-expire (TTL chưa được hỗ trợ)
  • Client phải gọi /release explicitly khi cancel hoặc payment thất bại
  • Client phải gọi /commit khi payment thành công

Currency Support

CurrencyMô tảPaymentTrạng thái
VNDThanh toán bằng VNĐQua payment gatewayMặc định, luôn hỗ trợ
VPOINTThanh toán bằng VPointTrừ trực tiếp từ walletHỗ trợ

V. Phụ lục

A. Bảng mã lỗi

HTTPCodeMô tả
200SUCCESSThành công
400SHOP-001Lỗi validation chung
400SHOP-PUR-001Tổng tiền amount không khớp
400SHOP-PUR-002Giá voucher đã thay đổi (stale price)
400SHOP-PUR-006Reservation đã commit/release/hết hạn
400SHOP-PUR-007Reservation đã release/commit (không thể hủy)
400SHOP-PUR-012Voucher hết hàng hoặc không đủ số lượng
400SHOP-PUR-013Lỗi khi tạo issued promotion
400SHOP-PUR-014Giá unitPrice không khớp sellingPrice
400SHOP-PUR-015Loại tiền currency không khớp
400SHOP-PUR-016Vượt giới hạn mua (daily limit / total limit)
400SHOP-VOU-002Từ khóa tìm kiếm quá ngắn (< 2 ký tự)
404SHOP-VOU-001Voucher không tồn tại hoặc đã xóa
404SHOP-CAM-001Campaign không tồn tại hoặc không active
404SHOP-PUR-003Reservation không tồn tại hoặc không thuộc user
409SHOP-PUR-011Order ID đã được sử dụng (duplicate reservation)

B. Campaign Tags

TagMô tảHiển thị
HOT_DEALƯu đãi hotTrang chủ + Tab Hot Deal
FLASH_SALEFlash sale (có countdown)Trang chủ + Tab Flash Sale

C. Voucher Usage Type

TypeMô tả
ONLINESử dụng online trên V-App
OFFLINESử dụng tại cửa hàng

D. Reservation Status

StatusMô tảTransition
RESERVEDĐang giữ chỗ, chờ thanh toánCOMMITTED / RELEASED
COMMITTEDĐã thanh toán thành côngTerminal state
RELEASEDĐã huỷ bởi user/systemTerminal state