Getting Started with Tax Configuration
This guide walks you through configuring tax on the Hii Retail platform. By the end of this guide, you will understand how to set tax regions for Business Units and apply the correct taxable groups to Price Specifications.
Overview
How Tax Works in Hii Retail
Tax configuration in Hii Retail involves two key components:
- Tax Region - Set on the Business Unit to specify which country/region's tax rules apply
- Taxable Group - Set on each Price Specification to specify the tax category (Standard, Reduced, etc.)
The combination of Tax Region + Taxable Group determines the actual tax rate applied.
┌─────────────────────────────────────────────────────────────────────┐
│ Tax Calculation │
├────────────────────────────── ───────────────────────────────────────┤
│ │
│ Business Unit Price Specification Tax Rate │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ taxRegionId: │ + │taxableGroupId│ = │ 25% │ │
│ │ "SE" │ │ "Standard" │ │ (VAT) │ │
│ └──────────────┘ └──────────────┘ └──────────┘ │
│ │
│ Example: Sweden (SE) + Standard rate = 25% VAT │
│ Sweden (SE) + Reduced rate = 12% VAT (food) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Tax Regions
Tax regions are geographic areas with specific tax rules. Examples:
| Tax Region | Description |
|---|---|
SE | Sweden (mainland) |
NO | Norway |
PT | Portugal (mainland) |
PT-MA | Portugal - Madeira (autonomous region with different rates) |
ES | Spain (mainland) |
ES-CN | Spain - Canary Islands |
Taxable Groups
Taxable groups represent tax categories that can have different rates depending on the region:
| Taxable Group | Description | Example Rates |
|---|---|---|
Standard | Standard VAT rate | 25% (SE), 23% (PT), 21% (ES) |
Reduced | Reduced rate for certain goods | 12% (SE), 13% (PT), 10% (ES) |
ReducedLow | Lower reduced rate | 6% (SE), 6% (PT), 4% (ES) |
SuperReduced | Super reduced rate | N/A (SE), N/A (PT), 4% (ES) |
Zero | Zero-rated goods | 0% |
The actual tax rate is determined by the Tax Rules service based on the combination of tax region and taxable group. You don't need to specify the percentage—just the taxable group.
Prerequisites
Before you begin, ensure you have:
- Business Units created: At least one Business Unit (see Business Units guide)
- Authentication Token: A valid JWT bearer token with appropriate permissions
For details on obtaining an access token, see the OAuth2 Authentication documentation.
API Base URLs
| API | Base URL |
|---|---|
| Tax Rules API | https://tax-rules.retailsvc.com/api/v1 |
| Business Unit API | https://business-unit.retailsvc.com/api/v1 |
| Price Specification API | https://price-specification-input.retailsvc.com/api/v2 |
Step 1: List Available Tax Regions
First, retrieve the list of available tax regions to find the correct taxRegionId for your Business Units.
- cURL
- Python
- Node.js
- Java
- .NET
curl -X GET 'https://tax-rules.retailsvc.com/api/v1/tax-regions' \
-H 'Authorization: Bearer <your-access-token>'
response = requests.get(
"https://tax-rules.retailsvc.com/api/v1/tax-regions",
headers={"Authorization": f"Bearer {access_token}"}
)
tax_regions = response.json()["taxRegions"]
for region in tax_regions:
print(f"{region['taxRegionId']} - {region['countryCode']}")
const response = await fetch('https://tax-rules.retailsvc.com/api/v1/tax-regions', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
const { taxRegions } = await response.json();
taxRegions.forEach(region => {
console.log(`${region.taxRegionId} - ${region.countryCode}`);
});
var request = HttpRequest.newBuilder()
.uri(URI.create("https://tax-rules.retailsvc.com/api/v1/tax-regions"))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
var response = await httpClient.GetAsync("https://tax-rules.retailsvc.com/api/v1/tax-regions");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
Example Response
{
"taxRegions": [
{ "countryCode": "SE", "taxRegionId": "SE" },
{ "countryCode": "NO", "taxRegionId": "NO" },
{ "countryCode": "PT", "taxRegionId": "PT" },
{ "countryCode": "PT", "taxRegionId": "PT-MA" },
{ "countryCode": "ES", "taxRegionId": "ES" },
{ "countryCode": "ES", "taxRegionId": "ES-CN" }
]
}
Step 2: View Tax Rules for a Region
To see the specific tax rates for a region, query the tax rules endpoint.
- cURL
- Python
- Node.js
- Java
- .NET
curl -X GET 'https://tax-rules.retailsvc.com/api/v1/tax-rules?taxRegionId=SE' \
-H 'Authorization: Bearer <your-access-token>'
response = requests.get(
"https://tax-rules.retailsvc.com/api/v1/tax-rules",
params={"taxRegionId": "SE"},
headers={"Authorization": f"Bearer {access_token}"}
)
tax_rules = response.json()["taxRules"]
for rule in tax_rules:
print(f"{rule['taxableGroupId']}: {rule['rate']}%")
const response = await fetch('https://tax-rules.retailsvc.com/api/v1/tax-rules?taxRegionId=SE', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
const { taxRules } = await response.json();
taxRules.forEach(rule => {
console.log(`${rule.taxableGroupId}: ${rule.rate}%`);
});
var request = HttpRequest.newBuilder()
.uri(URI.create("https://tax-rules.retailsvc.com/api/v1/tax-rules?taxRegionId=SE"))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
var response = await httpClient.GetAsync("https://tax-rules.retailsvc.com/api/v1/tax-rules?taxRegionId=SE");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
Example Response (Sweden)
{
"taxRules": [
{
"id": "SE_Standard",
"countryCode": "SE",
"taxRegionId": "SE",
"taxableGroupId": "Standard",
"rate": 25,
"validFrom": "2024-01-01T00:00:00Z"
},
{
"id": "SE_Reduced",
"countryCode": "SE",
"taxRegionId": "SE",
"taxableGroupId": "Reduced",
"rate": 12,
"validFrom": "2024-01-01T00:00:00Z"
},
{
"id": "SE_ReducedLow",
"countryCode": "SE",
"taxRegionId": "SE",
"taxableGroupId": "ReducedLow",
"rate": 6,
"validFrom": "2024-01-01T00:00:00Z"
},
{
"id": "SE_Zero",
"countryCode": "SE",
"taxRegionId": "SE",
"taxableGroupId": "Zero",
"rate": 0,
"validFrom": "2024-01-01T00:00:00Z"
}
],
"exemptions": []
}
Step 3: Set Tax Region on a Business Unit
When creating or updating a Business Unit, set the taxRegionId to specify which tax rules apply.
When Creating a Business Unit
- cURL
- Python
- Node.js
- Java
- .NET
curl -X PUT 'https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001' \
-H 'Authorization: Bearer <your-access-token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Stockholm City Store",
"status": "Active",
"type": "PHYSICAL_STORE",
"countryCode": "SE",
"taxRegionId": "SE",
"addresses": [
{
"addressType": "Visit",
"street": "Kungsgatan 1",
"city": "Stockholm",
"zipCode": "111 43",
"country": "Sweden"
}
]
}'
response = requests.put(
"https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001",
headers={"Authorization": f"Bearer {access_token}"},
json={
"name": "Stockholm City Store",
"status": "Active",
"type": "PHYSICAL_STORE",
"countryCode": "SE",
"taxRegionId": "SE",
"addresses": [
{
"addressType": "Visit",
"street": "Kungsgatan 1",
"city": "Stockholm",
"zipCode": "111 43",
"country": "Sweden"
}
]
}
)
const response = await fetch('https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Stockholm City Store',
status: 'Active',
type: 'PHYSICAL_STORE',
countryCode: 'SE',
taxRegionId: 'SE',
addresses: [
{
addressType: 'Visit',
street: 'Kungsgatan 1',
city: 'Stockholm',
zipCode: '111 43',
country: 'Sweden'
}
]
})
});
var payload = """
{
"name": "Stockholm City Store",
"status": "Active",
"type": "PHYSICAL_STORE",
"countryCode": "SE",
"taxRegionId": "SE",
"addresses": [
{
"addressType": "Visit",
"street": "Kungsgatan 1",
"city": "Stockholm",
"zipCode": "111 43",
"country": "Sweden"
}
]
}
""";
var request = HttpRequest.newBuilder()
.uri(URI.create("https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(payload))
.build();
var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
var payload = new {
name = "Stockholm City Store",
status = "Active",
type = "PHYSICAL_STORE",
countryCode = "SE",
taxRegionId = "SE",
addresses = new[] {
new {
addressType = "Visit",
street = "Kungsgatan 1",
city = "Stockholm",
zipCode = "111 43",
country = "Sweden"
}
}
};
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync("https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001", content);
Updating Tax Region on an Existing Business Unit
Use PATCH to update only the tax region without affecting other fields.
- cURL
- Python
- Node.js
- Java
- .NET
curl -X PATCH 'https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001' \
-H 'Authorization: Bearer <your-access-token>' \
-H 'Content-Type: application/json-patch+json' \
-d '[
{"op": "replace", "path": "/taxRegionId", "value": "SE"}
]'
response = requests.patch(
"https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json-patch+json"
},
json=[
{"op": "replace", "path": "/taxRegionId", "value": "SE"}
]
)
const response = await fetch('https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json-patch+json'
},
body: JSON.stringify([
{ op: 'replace', path: '/taxRegionId', value: 'SE' }
])
});
var operations = """
[{"op": "replace", "path": "/taxRegionId", "value": "SE"}]
""";
var request = HttpRequest.newBuilder()
.uri(URI.create("https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json-patch+json")
.method("PATCH", HttpRequest.BodyPublishers.ofString(operations))
.build();
var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
var operations = new[] {
new { op = "replace", path = "/taxRegionId", value = "SE" }
};
var content = new StringContent(JsonSerializer.Serialize(operations), Encoding.UTF8, "application/json-patch+json");
var request = new HttpRequestMessage(HttpMethod.Patch,
"https://business-unit.retailsvc.com/api/v1/business-units/store-stockholm-001") { Content = content };
var response = await httpClient.SendAsync(request);
Step 4: Apply Taxable Group to Prices
When creating Price Specifications, set the taxableGroupId to specify which tax category applies to the item.
Sales Price with Standard Tax
- cURL
- Python
- Node.js
- Java
- .NET
curl -X POST 'https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications' \
-H 'Authorization: Bearer <your-access-token>' \
-H 'Content-Type: application/json' \
-d '{
"id": "s-5449000051578",
"itemId": "5449000051578",
"type": "SALES",
"status": "ACTIVE",
"value": 14.99,
"taxableGroupId": "Standard",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "SEK",
"businessUnitGroupId": "acme-retail-group"
}'
response = requests.post(
"https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications",
headers={"Authorization": f"Bearer {access_token}"},
json={
"id": "s-5449000051578",
"itemId": "5449000051578",
"type": "SALES",
"status": "ACTIVE",
"value": 14.99,
"taxableGroupId": "Standard",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "SEK",
"businessUnitGroupId": "acme-retail-group"
}
)
const response = await fetch('https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: 's-5449000051578',
itemId: '5449000051578',
type: 'SALES',
status: 'ACTIVE',
value: 14.99,
taxableGroupId: 'Standard',
validFrom: '2024-01-01T00:00:00Z',
currencyId: 'SEK',
businessUnitGroupId: 'acme-retail-group'
})
});
var payload = """
{
"id": "s-5449000051578",
"itemId": "5449000051578",
"type": "SALES",
"status": "ACTIVE",
"value": 14.99,
"taxableGroupId": "Standard",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "SEK",
"businessUnitGroupId": "acme-retail-group"
}
""";
var request = HttpRequest.newBuilder()
.uri(URI.create("https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
var payload = new {
id = "s-5449000051578",
itemId = "5449000051578",
type = "SALES",
status = "ACTIVE",
value = 14.99,
taxableGroupId = "Standard",
validFrom = "2024-01-01T00:00:00Z",
currencyId = "SEK",
businessUnitGroupId = "acme-retail-group"
};
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync("https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications", content);
Food Item with Reduced Tax
For food items, use the Reduced taxable group to apply the lower VAT rate.
- cURL
- Python
- Node.js
- Java
- .NET
curl -X POST 'https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications' \
-H 'Authorization: Bearer <your-access-token>' \
-H 'Content-Type: application/json' \
-d '{
"id": "s-bread-001",
"itemId": "bread-001",
"type": "SALES",
"status": "ACTIVE",
"value": 29.90,
"taxableGroupId": "Reduced",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "SEK",
"businessUnitGroupId": "acme-retail-group"
}'
response = requests.post(
"https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications",
headers={"Authorization": f"Bearer {access_token}"},
json={
"id": "s-bread-001",
"itemId": "bread-001",
"type": "SALES",
"status": "ACTIVE",
"value": 29.90,
"taxableGroupId": "Reduced",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "SEK",
"businessUnitGroupId": "acme-retail-group"
}
)
const response = await fetch('https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: 's-bread-001',
itemId: 'bread-001',
type: 'SALES',
status: 'ACTIVE',
value: 29.90,
taxableGroupId: 'Reduced',
validFrom: '2024-01-01T00:00:00Z',
currencyId: 'SEK',
businessUnitGroupId: 'acme-retail-group'
})
});
var payload = """
{
"id": "s-bread-001",
"itemId": "bread-001",
"type": "SALES",
"status": "ACTIVE",
"value": 29.90,
"taxableGroupId": "Reduced",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "SEK",
"businessUnitGroupId": "acme-retail-group"
}
""";
var request = HttpRequest.newBuilder()
.uri(URI.create("https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
var payload = new {
id = "s-bread-001",
itemId = "bread-001",
type = "SALES",
status = "ACTIVE",
value = 29.90,
taxableGroupId = "Reduced",
validFrom = "2024-01-01T00:00:00Z",
currencyId = "SEK",
businessUnitGroupId = "acme-retail-group"
};
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync("https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications", content);
| Product Type | Taxable Group |
|---|---|
| Electronics, clothing, general merchandise | Standard |
| Food, beverages | Reduced |
| Books, newspapers, public transport | ReducedLow |
| Medical supplies, exports | Zero |
Complete Example: Setting Up Tax for a Store
Here's a complete workflow for configuring tax for a new store in Portugal - Madeira:
- cURL
- Python
- Node.js
- Java
- .NET
# 1. Check available tax regions
curl -X GET 'https://tax-rules.retailsvc.com/api/v1/tax-regions' \
-H 'Authorization: Bearer <your-access-token>'
# 2. View tax rates for Madeira
curl -X GET 'https://tax-rules.retailsvc.com/api/v1/tax-rules?taxRegionId=PT-MA' \
-H 'Authorization: Bearer <your-access-token>'
# 3. Create Business Unit with Madeira tax region
curl -X PUT 'https://business-unit.retailsvc.com/api/v1/business-units/store-funchal-001' \
-H 'Authorization: Bearer <your-access-token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Funchal Store",
"status": "Active",
"type": "PHYSICAL_STORE",
"countryCode": "PT",
"taxRegionId": "PT-MA",
"groups": ["acme-retail-group"],
"addresses": [
{
"addressType": "Visit",
"street": "Avenida do Mar 100",
"city": "Funchal",
"zipCode": "9000-000",
"country": "Portugal"
}
]
}'
# 4. Create price with Standard tax (22% in Madeira)
curl -X POST 'https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications' \
-H 'Authorization: Bearer <your-access-token>' \
-H 'Content-Type: application/json' \
-d '{
"id": "s-electronics-001",
"itemId": "electronics-001",
"type": "SALES",
"status": "ACTIVE",
"value": 99.99,
"taxableGroupId": "Standard",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "EUR",
"businessUnitGroupId": "acme-retail-group"
}'
# 5. Create price with Reduced tax (12% in Madeira for food)
curl -X POST 'https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications' \
-H 'Authorization: Bearer <your-access-token>' \
-H 'Content-Type: application/json' \
-d '{
"id": "s-food-001",
"itemId": "food-001",
"type": "SALES",
"status": "ACTIVE",
"value": 4.99,
"taxableGroupId": "Reduced",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "EUR",
"businessUnitGroupId": "acme-retail-group"
}'
import requests
# 1. Check available tax regions
regions_response = requests.get(
"https://tax-rules.retailsvc.com/api/v1/tax-regions",
headers={"Authorization": f"Bearer {access_token}"}
)
print("Available regions:", regions_response.json())
# 2. View tax rates for Madeira
rules_response = requests.get(
"https://tax-rules.retailsvc.com/api/v1/tax-rules",
params={"taxRegionId": "PT-MA"},
headers={"Authorization": f"Bearer {access_token}"}
)
print("Madeira tax rates:", rules_response.json())
# 3. Create Business Unit with Madeira tax region
bu_response = requests.put(
"https://business-unit.retailsvc.com/api/v1/business-units/store-funchal-001",
headers={"Authorization": f"Bearer {access_token}"},
json={
"name": "Funchal Store",
"status": "Active",
"type": "PHYSICAL_STORE",
"countryCode": "PT",
"taxRegionId": "PT-MA",
"groups": ["acme-retail-group"],
"addresses": [{
"addressType": "Visit",
"street": "Avenida do Mar 100",
"city": "Funchal",
"zipCode": "9000-000",
"country": "Portugal"
}]
}
)
# 4. Create prices with appropriate tax groups
def create_price(item_id, price_id, value, taxable_group):
return requests.post(
"https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications",
headers={"Authorization": f"Bearer {access_token}"},
json={
"id": price_id,
"itemId": item_id,
"type": "SALES",
"status": "ACTIVE",
"value": value,
"taxableGroupId": taxable_group,
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "EUR",
"businessUnitGroupId": "acme-retail-group"
}
)
# Electronics with Standard tax (22% in Madeira)
create_price("electronics-001", "s-electronics-001", 99.99, "Standard")
# Food with Reduced tax (12% in Madeira)
create_price("food-001", "s-food-001", 4.99, "Reduced")
// 1. Check available tax regions
const regionsResponse = await fetch('https://tax-rules.retailsvc.com/api/v1/tax-regions', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
console.log('Available regions:', await regionsResponse.json());
// 2. View tax rates for Madeira
const rulesResponse = await fetch('https://tax-rules.retailsvc.com/api/v1/tax-rules?taxRegionId=PT-MA', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
console.log('Madeira tax rates:', await rulesResponse.json());
// 3. Create Business Unit with Madeira tax region
await fetch('https://business-unit.retailsvc.com/api/v1/business-units/store-funchal-001', {
method: 'PUT',
headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Funchal Store',
status: 'Active',
type: 'PHYSICAL_STORE',
countryCode: 'PT',
taxRegionId: 'PT-MA',
groups: ['acme-retail-group'],
addresses: [{
addressType: 'Visit',
street: 'Avenida do Mar 100',
city: 'Funchal',
zipCode: '9000-000',
country: 'Portugal'
}]
})
});
// 4. Helper to create prices
async function createPrice(itemId, priceId, value, taxableGroup) {
return fetch('https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications', {
method: 'POST',
headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
id: priceId,
itemId: itemId,
type: 'SALES',
status: 'ACTIVE',
value: value,
taxableGroupId: taxableGroup,
validFrom: '2024-01-01T00:00:00Z',
currencyId: 'EUR',
businessUnitGroupId: 'acme-retail-group'
})
});
}
// Electronics with Standard tax, Food with Reduced tax
await createPrice('electronics-001', 's-electronics-001', 99.99, 'Standard');
await createPrice('food-001', 's-food-001', 4.99, 'Reduced');
// Helper method to make GET requests
String get(String url) throws Exception {
var request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
return httpClient.send(request, HttpResponse.BodyHandlers.ofString()).body();
}
// 1. Check available tax regions
System.out.println(get("https://tax-rules.retailsvc.com/api/v1/tax-regions"));
// 2. View tax rates for Madeira
System.out.println(get("https://tax-rules.retailsvc.com/api/v1/tax-rules?taxRegionId=PT-MA"));
// 3. Create Business Unit with Madeira tax region
var buPayload = """
{
"name": "Funchal Store",
"status": "Active",
"type": "PHYSICAL_STORE",
"countryCode": "PT",
"taxRegionId": "PT-MA",
"groups": ["acme-retail-group"],
"addresses": [{
"addressType": "Visit",
"street": "Avenida do Mar 100",
"city": "Funchal",
"zipCode": "9000-000",
"country": "Portugal"
}]
}
""";
var buRequest = HttpRequest.newBuilder()
.uri(URI.create("https://business-unit.retailsvc.com/api/v1/business-units/store-funchal-001"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(buPayload))
.build();
httpClient.send(buRequest, HttpResponse.BodyHandlers.ofString());
// 4. Create prices with tax groups
void createPrice(String itemId, String priceId, double value, String taxableGroup) throws Exception {
var payload = String.format("""
{
"id": "%s",
"itemId": "%s",
"type": "SALES",
"status": "ACTIVE",
"value": %.2f,
"taxableGroupId": "%s",
"validFrom": "2024-01-01T00:00:00Z",
"currencyId": "EUR",
"businessUnitGroupId": "acme-retail-group"
}
""", priceId, itemId, value, taxableGroup);
var request = HttpRequest.newBuilder()
.uri(URI.create("https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}
createPrice("electronics-001", "s-electronics-001", 99.99, "Standard");
createPrice("food-001", "s-food-001", 4.99, "Reduced");
// 1. Check available tax regions
var regionsResponse = await httpClient.GetStringAsync("https://tax-rules.retailsvc.com/api/v1/tax-regions");
Console.WriteLine($"Available regions: {regionsResponse}");
// 2. View tax rates for Madeira
var rulesResponse = await httpClient.GetStringAsync("https://tax-rules.retailsvc.com/api/v1/tax-rules?taxRegionId=PT-MA");
Console.WriteLine($"Madeira tax rates: {rulesResponse}");
// 3. Create Business Unit with Madeira tax region
var buPayload = new {
name = "Funchal Store",
status = "Active",
type = "PHYSICAL_STORE",
countryCode = "PT",
taxRegionId = "PT-MA",
groups = new[] { "acme-retail-group" },
addresses = new[] {
new {
addressType = "Visit",
street = "Avenida do Mar 100",
city = "Funchal",
zipCode = "9000-000",
country = "Portugal"
}
}
};
var buContent = new StringContent(JsonSerializer.Serialize(buPayload), Encoding.UTF8, "application/json");
await httpClient.PutAsync("https://business-unit.retailsvc.com/api/v1/business-units/store-funchal-001", buContent);
// 4. Helper to create prices
async Task CreatePriceAsync(string itemId, string priceId, decimal value, string taxableGroup)
{
var payload = new {
id = priceId,
itemId = itemId,
type = "SALES",
status = "ACTIVE",
value = value,
taxableGroupId = taxableGroup,
validFrom = "2024-01-01T00:00:00Z",
currencyId = "EUR",
businessUnitGroupId = "acme-retail-group"
};
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
await httpClient.PostAsync("https://price-specification-input.retailsvc.com/api/v2/bu-g-price-specifications", content);
}
// Electronics with Standard tax, Food with Reduced tax
await CreatePriceAsync("electronics-001", "s-electronics-001", 99.99m, "Standard");
await CreatePriceAsync("food-001", "s-food-001", 4.99m, "Reduced");
Next Steps
Now that you understand tax configuration, you can:
- Set up stores in different regions: Configure Business Units with the appropriate tax regions
- Apply correct tax categories: Use the right taxable group for each product type
- Handle special tax zones: Configure autonomous regions like Madeira, Canary Islands, etc.
Related Resources
Next: Deposits - Learn how to configure deposit and fee rules for returnable containers.