Tariff and Cost — CSMS Developer Guide
Based on OCPP 2.1 Edition 2 Specification (Part 2), Section I (pages 268-301). This guide covers all tariff and cost flows (I01–I12, NotifySettlement), including central cost calculation, local cost calculation, tariff management, cost details, and payment settlement from the CSMS perspective using OCPP-J (JSON over WebSocket).
1. Overview & Architecture
IntroductionThe Tariff and Cost functional block provides tariff and cost information to EV Drivers via the Charging Station display. OCPP 2.1 supports two cost calculation models that the CSMS must understand and implement.
Cost Calculation Models
Central Cost Calculation
CSMS calculates all costs and sends running/final totals to the Charging Station.
CSMS sends CostUpdatedRequest or returns totalCost in TransactionEventResponse
Local Cost Calculation
Charging Station calculates costs locally using a tariff structure provided by CSMS.
CSMS sends tariff structures via SetDefaultTariffRequest, AuthorizeResponse.tariff, or ChangeTransactionTariffRequest
Message Direction Summary
| Message | Direction | Initiator |
|---|---|---|
AuthorizeRequest | CS → CSMS | Charging Station |
AuthorizeResponse | CSMS → CS | CSMS |
TransactionEventRequest | CS → CSMS | Charging Station |
TransactionEventResponse | CSMS → CS | CSMS |
CostUpdatedRequest | CSMS → CS | CSMS |
CostUpdatedResponse | CS → CSMS | Charging Station |
SetDefaultTariffRequest | CSMS → CS | CSMS |
SetDefaultTariffResponse | CS → CSMS | Charging Station |
GetTariffsRequest | CSMS → CS | CSMS |
GetTariffsResponse | CS → CSMS | Charging Station |
ClearTariffsRequest | CSMS → CS | CSMS |
ClearTariffsResponse | CS → CSMS | Charging Station |
ChangeTransactionTariffRequest | CSMS → CS | CSMS |
ChangeTransactionTariffResponse | CS → CSMS | Charging Station |
NotifySettlementRequest | CS → CSMS | Charging Station |
NotifySettlementResponse | CSMS → CS | CSMS |
2. Tariff Data Structures Reference
Reference2.1 TariffType (Core Tariff Object)
The TariffType is the central
structure used across all tariff-related messages.
{
"tariffId": "string", // REQUIRED. Unique id, max 60 chars
"currency": "string", // REQUIRED. ISO 4217 currency code, max 3 chars (e.g. "EUR", "USD")
"validFrom": "date-time", // Optional. When absent, tariff is immediately active. UTC recommended.
"description": [ // Optional. Human-readable text, array of MessageContentType, max 10 items
{
"format": "ASCII|HTML|URI|UTF8|QRCODE", // REQUIRED
"language": "string", // Optional. RFC 5646 language code, max 8 chars
"content": "string" // REQUIRED. Max 1024 chars
}
],
"energy": { /* TariffEnergyType */ }, // Optional. Energy pricing (per kWh)
"chargingTime": { /* TariffTimeType */ }, // Optional. Charging time pricing (per minute)
"idleTime": { /* TariffTimeType */ }, // Optional. Idle time pricing (per minute)
"fixedFee": { /* TariffFixedType */ }, // Optional. Fixed fee (e.g. start fee)
"reservationTime": { /* TariffTimeType */ }, // Optional. Reservation time pricing
"reservationFixed": { /* TariffFixedType */ }, // Optional. Reservation fixed fee
"minCost": { /* PriceType */ }, // Optional. Minimum cost for the transaction
"maxCost": { /* PriceType */ } // Optional. Maximum cost cap for the transaction
}2.2 TariffEnergyType
{
"prices": [ // REQUIRED. Array of TariffEnergyPriceType, min 1 item
{
"priceKwh": 0.25, // REQUIRED. Price per kWh (excl. tax)
"conditions": { /* TariffConditionsType, optional */ }
}
],
"taxRates": [ // Optional. Array of TaxRateType, max 5 items
{ "type": "vat", "tax": 10.0, "stack": 0 }
]
}2.3 TariffTimeType (chargingTime, idleTime, reservationTime)
{
"prices": [ // REQUIRED. Array of TariffTimePriceType, min 1 item
{
"priceMinute": 1.00, // REQUIRED. Price per minute (excl. tax)
"conditions": { /* TariffConditionsType, optional */ }
}
],
"taxRates": [ // Optional. Array of TaxRateType, max 5 items
{ "type": "vat", "tax": 15.0 }
]
}2.4 TariffFixedType (fixedFee, reservationFixed)
{
"prices": [ // REQUIRED. Array of TariffFixedPriceType, min 1 item
{
"priceFixed": 2.50, // REQUIRED. Fixed price (excl. tax)
"conditions": { /* TariffConditionsFixedType, optional */ }
}
],
"taxRates": [ // Optional. Array of TaxRateType, max 5 items
{ "type": "vat", "tax": 10.0 }
]
}2.5 TariffConditionsType (for energy and time prices)
All fields are optional. When multiple conditions are set, they are treated as logical AND.
{
"startTimeOfDay": "08:00", // Local time, format "HH:MM"
"endTimeOfDay": "18:00", // Local time. If end < start, wraps to next day. Use "00:00" for end of day.
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
// Enum: Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday
"validFromDate": "2024-01-01", // Local date, inclusive
"validToDate": "2024-12-31", // Local date, exclusive
"evseKind": "AC", // Enum: AC|DC
"minEnergy": 0, // Wh, inclusive
"maxEnergy": 50000, // Wh, exclusive
"minCurrent": 5, // Amperes (sum over all phases), inclusive
"maxCurrent": 32, // Amperes, exclusive
"minPower": 5000, // Watts, inclusive
"maxPower": 22000, // Watts, exclusive
"minTime": 0, // Seconds (total transaction duration), inclusive
"maxTime": 3600, // Seconds, exclusive
"minChargingTime": 0, // Seconds (charging duration only), inclusive
"maxChargingTime": 3600, // Seconds, exclusive
"minIdleTime": 300, // Seconds (idle duration only), inclusive
"maxIdleTime": 1800 // Seconds, exclusive
}Important notes on conditions:
startTimeOfDayandendTimeOfDayare in local time (not UTC)- For reverse energy flow (V2G/discharging), use negative values for energy, power, and current
minXXXmeans "closest to zero",maxXXXmeans "furthest from zero"- A price element without conditions is always applicable (use as fallback at end of list)
- When multiple prices are applicable, only the first applicable element in the list is used
2.6 TariffConditionsFixedType (for fixed fees only)
Same as TariffConditionsType but without energy/power/current/time conditions. Additional fields:
{
"startTimeOfDay": "08:00",
"endTimeOfDay": "18:00",
"dayOfWeek": ["Monday"],
"validFromDate": "2024-01-01",
"validToDate": "2024-12-31",
"evseKind": "AC",
"paymentBrand": "string", // Max 20 chars. From idToken.additionalInfo.type = "PaymentBrand"
"paymentRecognition": "CC" // Max 20 chars. From idToken.additionalInfo.type = "PaymentRecognition"
}2.7 TaxRateType
{
"type": "vat", // REQUIRED. Type label (e.g. "Federal", "State", "vat"), max 20 chars
"tax": 10.0, // REQUIRED. Tax percentage
"stack": 0 // Optional. Default 0. stack=0: tax on net price; stack=1: tax on top of stack 0; etc.
}Tax calculation order:
- Apply all
stack=0taxes to the net price - Apply all
stack=1taxes to the result of step 1 - Apply all
stack=2taxes to the result of step 2, etc.
2.8 PriceType
{
"exclTax": 2.50, // Optional (at least one of exclTax/inclTax required)
"inclTax": 2.75, // Optional
"taxRates": [ // Optional. Array of TaxRateType, max 5
{ "type": "vat", "tax": 10.0 }
]
}2.9 Tariff Examples
{
"tariffId": "10",
"currency": "USD",
"energy": {
"taxRates": [
{ "type": "federal", "tax": 6.0 },
{ "type": "state", "tax": 4.0 }
],
"prices": [{ "priceKwh": 0.25 }]
}
}{
"tariffId": "11",
"currency": "EUR",
"energy": {
"taxRates": [{ "type": "vat", "tax": 4 }],
"prices": [
{
"priceKwh": 0.4,
"conditions": { "startTimeOfDay": "08:00", "endTimeOfDay": "18:00" }
},
{ "priceKwh": 0.25 }
]
},
"idleTime": {
"taxRates": [{ "type": "vat", "tax": 4 }],
"prices": [
{
"priceMinute": 1,
"conditions": { "startTimeOfDay": "08:00", "endTimeOfDay": "18:00" }
}
]
}
}{
"tariffId": "12",
"currency": "EUR",
"fixedFee": {
"taxRates": [{ "type": "vat", "tax": 10.0 }],
"prices": [
{ "priceFixed": 3.00, "conditions": { "paymentRecognition": "CC" } },
{ "priceFixed": 2.50 }
]
},
"chargingTime": {
"taxRates": [{ "type": "vat", "tax": 15.0 }],
"prices": [
{ "priceMinute": 1.00, "conditions": { "maxPower": 11000 } },
{ "priceMinute": 2.00, "conditions": { "minPower": 11000 } }
]
},
"idleTime": {
"taxRates": [{ "type": "vat", "tax": 15.0 }],
"prices": [
{
"priceMinute": 1.00,
"conditions": {
"startTimeOfDay": "09:00", "endTimeOfDay": "18:00",
"minIdleTime": 300,
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
}
},
{
"priceMinute": 0.60,
"conditions": {
"startTimeOfDay": "10:00", "endTimeOfDay": "17:00",
"dayOfWeek": ["Saturday"]
}
}
]
}
}3. Configuration Variables
ReferenceThese configuration variables control tariff and cost behavior. CSMS can set them via SetVariablesRequest.
| Variable | Description |
|---|---|
TariffCostCtrlr.Enabled[Tariff] | Enable/disable local cost calculation on CS |
TariffCostCtrlr.Interval[Tariff] | Frequency (seconds) for monitoring current/power/energy variations for tariff calculation |
TariffCostCtrlr.MaxElements[Tariff] | Maximum supported complexity (number of elements) of a tariff |
TariffCostCtrlr.Enabled[Cost] | Enable/disable sending intermediate running cost updates |
TariffCostCtrlr.Interval[Cost] | Frequency (seconds) for sending running cost updates |
TariffCostCtrlr.ConditionsSupported | Whether CS supports TariffConditionsType (true/false) |
TariffFallbackMessage | Fallback tariff text shown when specific tariff is unavailable |
TotalCostFallbackMessage | Fallback cost text shown when CS is offline at end of transaction |
HandleFailedTariff | What to do when tariff processing fails: "Deauthorize", "UseDefault", or "CentralCost" |
Currency | ISO 4217 currency code for central cost calculation |
4. I01 — Show EV Driver-Specific Tariff Information
CS-Initiated| Use Case ID | I01 |
| Direction | CS → CSMS (CS initiates) |
| Objective | Show an EV Driver-specific tariff before the start of a transaction |
| CSMS Role | Respond to AuthorizeRequest with tariff information in AuthorizeResponse |
Flow Diagram
EV Driver Charging Station CSMS
| | |
|-- present IdToken ---->| |
| |-- AuthorizeRequest ----->|
| | |-- Look up driver-specific tariff
| |<-- AuthorizeResponse ----| (include personalMessage with tariff text)
|<-- display tariff -----| |CSMS Handler: AuthorizeRequest
When received, the CSMS MAY include tariff information in the idTokenInfo.personalMessage field.
{
"idToken": { // REQUIRED
"idToken": "string", // REQUIRED. Max 255 chars, case insensitive
"type": "string" // REQUIRED. Token type (e.g. "ISO14443", "eMAID"), max 20 chars
}
}{
"idTokenInfo": { // REQUIRED
"status": "Accepted", // REQUIRED. Enum: Accepted|Blocked|ConcurrentTx|Expired|Invalid|
// NoCredit|NotAllowedTypeEVSE|NotAtThisLocation|NotAtThisTime|Unknown
"personalMessage": { // Optional. Tariff info as text for display
"format": "ASCII", // REQUIRED
"language": "en", // Optional
"content": "0.25/kWh" // REQUIRED. Max 1024 chars
}
}
}Requirements for CSMS
| Req ID | Requirement |
|---|---|
I01.FR.01 | CSMS MAY send EV Driver-specific tariff information in idTokenInfo.personalMessage of AuthorizeResponse |
I01.FR.02 | CSMS SHALL only send tariff information if the CS supports tariff or DisplayMessage functionality |
Implementation Notes
- This flow uses text-based tariff display only (the
personalMessagefield) - For structured tariff delivery (local cost calculation), see Flow I08
- The tariff information may be the same for all drivers or driver-specific
- Show tariff in a language understood by the EV Driver when possible (use
language1/language2fromIdTokenInfoType)
5. I02 — Show EV Driver Running Total Cost During Charging
CSMS-Initiated| Use Case ID | I02 |
| Direction | CSMS → CS (CSMS initiates) or reactive via TransactionEventResponse |
| Objective | Send running cost updates to the Charging Station during an active transaction |
| CSMS Role | Periodically send CostUpdatedRequest OR return totalCost in TransactionEventResponse |
Option A: CostUpdatedRequest (CSMS-initiated)
CSMS Charging Station EV Driver
| | |
| [loop: every Y seconds while transaction ongoing] |
|-- CostUpdatedRequest --->| |
|<-- CostUpdatedResponse --| |
| |-- show cost: X.XX ------>|{
"totalCost": 12.50, // REQUIRED. number. Running total cost including taxes.
// Currency as configured in Configuration Variable: Currency
"transactionId": "string" // REQUIRED. string, max 36 chars. Transaction ID
}// Response is empty (only optional customData)
{}The response contains no status. It is a simple acknowledgment.
Option B: totalCost in TransactionEventResponse (CSMS-reactive)
When the CSMS receives a TransactionEventRequest with eventType = Updated,
it can return the running cost in the response:
{
"totalCost": 12.50 // Optional. Running cost including taxes
}Requirements for CSMS
| Req ID | Requirement |
|---|---|
I02.FR.01 | CSMS SHALL send either a CostUpdatedRequest at a relevant interval/moment OR return running cost in TransactionEventResponse. This might depend on charging speed, running cost, etc. |
Implementation Notes
- Do not update too frequently — frequent updates generate high mobile data costs
- The
totalCostvalue includes taxes and is in the currency configured via theCurrencyconfiguration variable - Both Option A and Option B can be used together or independently
- For local cost calculation (where CS calculates costs), this flow is not needed
6. I03 — Show EV Driver Final Total Cost After Charging
CS-Initiated| Use Case ID | I03 |
| Direction | CS → CSMS (CS sends TransactionEventRequest with eventType = Ended) |
| Objective | Provide the final total cost when a transaction ends |
| CSMS Role | Return totalCost in TransactionEventResponse when eventType = Ended |
Flow Diagram
EV Driver Charging Station CSMS
| | |
|-- present IdToken ---->| |
| | |
| |-- TransactionEventReq -->| (eventType = Ended)
| |<-- TransactionEventRes --| (totalCost = X.XX)
|<-- show cost: X.XX ----| |CSMS Handler: TransactionEventRequest (eventType = Ended)
Key fields to inspect in the incoming request:
{
"eventType": "Ended", // REQUIRED
"timestamp": "2024-01-15T14:30:00Z", // REQUIRED
"triggerReason": "StopAuthorized", // REQUIRED
"seqNo": 5, // REQUIRED
"transactionInfo": { // REQUIRED
"transactionId": "tx-123", // REQUIRED
"stoppedReason": "Local", // Optional. Why transaction stopped
"tariffId": "tariff-1" // Optional. Tariff used for transaction
},
"idToken": { /* IdTokenType */ }, // Optional
"meterValue": [ /* ... */ ], // Optional. Final meter values
"costDetails": { /* CostDetailsType */ } // Optional. Cost breakdown from CS (local calc)
}{
"totalCost": 15.75 // Optional. Final total cost including taxes.
// Set to 0.00 for free transactions.
// Absence does NOT mean free - it means cost is unknown.
}Requirements for CSMS
| Req ID | Requirement |
|---|---|
I03.FR.02 | When total cost is known, CSMS SHALL send it in the totalCost field of TransactionEventResponse |
I03.FR.04 | To indicate a free transaction, CSMS SHALL set totalCost to 0.00. Omitting totalCost does NOT imply free. |
I03.FR.05 | If TxStopPoint is ParkingBayOccupancy, CSMS SHOULD NOT display total cost (driver has left) |
Implementation Notes
- The CSMS should calculate or retrieve the total cost based on the transaction's meter values, tariff, and duration
- If the CS was offline when the transaction ended, the
TransactionEventResponsewithtotalCostmay arrive late; the driver may have already left - When
costDetailsis present in the incoming request (local cost calculation), the CSMS should store this for invoicing/CDR generation
7. I04 — Show Fallback Tariff Information
CSMS-Initiated| Use Case ID | I04 |
| Direction | CSMS → CS (via SetVariablesRequest) |
| Objective | Configure a fallback tariff message displayed when specific tariff info is unavailable |
| CSMS Role | Configure the TariffFallbackMessage configuration variable |
CSMS Action
The CSMS configures this proactively via SetVariablesRequest:
{
"setVariableData": [
{
"component": { "name": "TariffCostCtrlr" },
"variable": { "name": "TariffFallbackMessage" },
"attributeValue": "Charging costs 0.30 EUR/kWh. See example.com/tariffs for details."
}
]
}Requirements for CSMS
| Req ID | Requirement |
|---|---|
I04.FR.02 | CSMS MAY configure the TariffFallbackMessage via SetVariablesRequest |
When This Is Shown
The Charging Station shows this message when:
- The CS is offline and cannot reach CSMS for authorization
- No EV Driver-specific tariff is available in the
AuthorizeResponse - The driver authorizes from the Authorization Cache while offline
8. I05 — Show Fallback Total Cost Message
CSMS-Initiated| Use Case ID | I05 |
| Direction | CSMS → CS (via SetVariablesRequest) |
| Objective | Configure a fallback message shown instead of the total cost when the CS is offline at transaction end |
| CSMS Role | Configure the TotalCostFallbackMessage configuration variable |
CSMS Action
{
"setVariableData": [
{
"component": { "name": "TariffCostCtrlr" },
"variable": { "name": "TotalCostFallbackMessage" },
"attributeValue": "Total cost will be available in your app shortly."
}
]
}| Req ID | Requirement |
|---|---|
I05.FR.01 | CSMS MAY configure the fallback total cost information message via TotalCostFallbackMessage |
9. I06 — Update Tariff Information During Transaction
CS-Initiated| Use Case ID | I06 |
| Direction | CS → CSMS (CS sends TransactionEventRequest), CSMS responds with updated tariff text |
| Objective | Provide updated tariff information text to the EV driver during a transaction |
| CSMS Role | Check for updated tariff text upon receiving TransactionEventRequest with eventType = Updated |
Flow Diagram
Charging Station CSMS
| |
|-- TransactionEventReq -->| (eventType = Updated)
| |-- Check for updated tariff info
|<-- TransactionEventRes --| (updatedPersonalMessage = "New price: 0.32 EUR/kWh")
| |CSMS Action: Build TransactionEventResponse
When the CSMS receives a TransactionEventRequest with eventType = Updated,
check if there is updated tariff information to communicate.
{
"updatedPersonalMessage": { // Optional. Updated tariff info in default language
"format": "ASCII",
"language": "en",
"content": "Price updated: 0.32 EUR/kWh (was 0.28 EUR/kWh)"
},
"updatedPersonalMessageExtra": [ // Optional. Same message in other languages, max 4 items
{
"format": "ASCII",
"language": "de",
"content": "Preis aktualisiert: 0,32 EUR/kWh (vorher 0,28 EUR/kWh)"
}
]
}Requirements for CSMS
| Req ID | Requirement |
|---|---|
I06.FR.01 | When receiving TransactionEventRequest with eventType = Updated, CSMS SHALL check if there is updated tariff information |
I06.FR.02 | When updated info is available, CSMS SHALL respond with the updated tariff in updatedPersonalMessage (default language) and optionally other languages in updatedPersonalMessageExtra |
Implementation Notes
- Use this for tariffs with variable pricing (e.g., bandwidth pricing like "0.25-0.40 EUR/kWh depending on current energy price")
- Consider legal requirements: some jurisdictions require the tariff communicated at the start to remain for the entire transaction
- This is text-based information only, not a tariff structure change (for that, see Flow I11)
10. I07 — Local Cost Calculation: Set Default Tariff
CSMS-Initiated New in 2.1| Use Case ID | I07 |
| Direction | CSMS → CS (CSMS initiates) |
| Objective | Install a default tariff on the Charging Station for local cost calculation |
| CSMS Role | Send SetDefaultTariffRequest to install/update default tariffs |
Flow Diagram
CSMS Charging Station
| |
|-- SetDefaultTariffReq -->| (evseId=0, tariff={...})
|<-- SetDefaultTariffRes --| (status=Accepted)CSMS Action: Send SetDefaultTariffRequest
{
"evseId": 0, // REQUIRED. integer, min 0.
// 0 = apply to ALL EVSEs
// >0 = apply only to specific EVSE
"tariff": { // REQUIRED. TariffType object
"tariffId": "Tariff1", // REQUIRED. Unique identifier, max 60 chars
"currency": "EUR", // REQUIRED. ISO 4217
"validFrom": "2024-06-01T00:00:00Z", // Optional. When absent, immediately active
"description": [
{ "format": "ASCII", "language": "en", "content": "0.50 ct/kWh" }
],
"energy": {
"prices": [{ "priceKwh": 0.50 }],
"taxRates": [{ "type": "vat", "tax": 19.0 }]
}
}
}CSMS Handler: SetDefaultTariffResponse
{
"status": "Accepted", // REQUIRED. Enum values:
// "Accepted" - Tariff installed successfully
// "Rejected" - Invalid tariff structure (check statusInfo.reasonCode)
// "TooManyElements" - Tariff exceeds TariffMaxElements
// "ConditionNotSupported" - CS doesn't support conditions
// "DuplicateTariffId" - tariffId already exists on CS
"statusInfo": { // Optional
"reasonCode": "string", // REQUIRED if statusInfo present. Max 20 chars.
// e.g. "InvalidValue", "UnknownEVSE"
"additionalInfo": "string" // Optional. Max 1024 chars
}
}Response Handling Matrix
| Response Status | CSMS Action |
|---|---|
Accepted | Tariff installed. No further action needed. |
Rejected (InvalidValue) | Fix the tariff structure and retry |
Rejected (UnknownEVSE) | The evseId doesn't exist on this CS. Verify EVSE config. |
TooManyElements | Simplify the tariff. Check TariffCostCtrlr.MaxElements[Tariff] |
ConditionNotSupported | Remove conditions from tariff. CS has ConditionsSupported = false |
DuplicateTariffId | A tariff with this tariffId already exists. Use a new tariffId for updates. |
Scenarios
{ "evseId": 0, "tariff": { "tariffId": "T1", "currency": "EUR", /* ... */ } }// First: set default for all
{ "evseId": 0, "tariff": { "tariffId": "T1", "currency": "EUR", /* ... */ } }
// Then: override for EVSE #4
{ "evseId": 4, "tariff": { "tariffId": "T2", "currency": "EUR", /* ... */ } }{
"evseId": 0,
"tariff": {
"tariffId": "T3",
"currency": "EUR",
"validFrom": "2024-07-01T00:00:00Z", // Becomes active on July 1st
/* ... pricing ... */
}
}Requirements for CSMS
| Req ID | Key Requirement |
|---|---|
I07.FR.10 | On valid tariff and no errors → CS responds Accepted |
I07.FR.13 | CS stores default tariff in persistent memory |
I07.FR.17 | startTimeOfDay, endTimeOfDay, validFromDate, validToDate must be in local time ("hh:mm" format) |
I07.FR.18 | validFrom in TariffType SHALL be in system time (usually UTC) |
I07.FR.24 | An active tariff in use for a transaction continues until the transaction ends (cannot be replaced mid-transaction unless via ChangeTransactionTariffRequest) |
Important Notes
- A
tariffIduniquely identifies a tariff. Updated tariffs must have a newtariffId. evseId = 0copies the tariff to every EVSE- Only one tariff structure per message (tariffs can be large)
- The CS will NOT respond with
NotSupportedCALLERROR ifTariffCostCtrlr.Enabled[Tariff] = false
11. I08 — Local Cost Calculation: Receive Driver Tariff
CS-Initiated New in 2.1| Use Case ID | I08 |
| Direction | CS → CSMS (AuthorizeRequest), CSMS responds with tariff |
| Objective | Provide a driver-specific tariff via AuthorizeResponse for local cost calculation |
| CSMS Role | Include a tariff field in the AuthorizeResponse when a driver-specific tariff exists |
Flow Diagram
TxStartPoint = PowerPathClosed, authorize first:
EV Driver Charging Station CSMS
| | |
|-- present card ------->| |
| |-- AuthorizeRequest ----->|
| | |-- Lookup driver tariff
| |<-- AuthorizeResponse ----| (status=Accepted, tariff={...})
|<-- show tariff --------| |
|-- plug in cable ------>| |
| |-- TransactionEventReq -->| (Started, tariffId="100")
| |<-- TransactionEventRes --| (status=Accepted)CSMS Action: Build AuthorizeResponse with Tariff
When receiving AuthorizeRequest,
check if a driver-specific tariff exists for the idToken.
{
"idTokenInfo": {
"status": "Accepted"
},
"tariff": { // Optional. Include when driver-specific tariff exists
"tariffId": "100", // REQUIRED. Use a unique ID per distinct tariff content
"currency": "EUR",
"description": [
{ "format": "ASCII", "language": "en", "content": "0.55 ct/kWh" }
],
"energy": {
"prices": [{ "priceKwh": 0.55 }],
"taxRates": [{ "type": "vat", "tax": 19.0 }]
}
}
}Tariff Acceptance by Driver
The Charging Station shows the tariff to the driver. The driver can accept or reject it:
| Driver Action | CS Behavior | What CSMS Sees |
|---|---|---|
| Accepts (explicit or implicit by plugging in) | Associates tariff with transaction, includes transactionInfo.tariffId | TransactionEventRequest with tariffId set |
| Rejects (already has a transaction) | CS deauthorizes and sends TransactionEventRequest with triggerReason = TariffNotAccepted | No tariffId in transaction, no cost, no energy delivered |
| Rejects (no transaction yet) | CS deauthorizes the idToken | No transaction starts (or starts without authorization/energy) |
Error Handling: CS Cannot Process Tariff
The behavior depends on the HandleFailedTariff configuration variable:
| HandleFailedTariff | CS Behavior | CSMS Implication |
|---|---|---|
Deauthorize | CS does NOT authorize the idToken | No transaction, no charging |
UseDefault | CS authorizes and uses the default tariff | Costs based on default tariff, not driver-specific |
CentralCost | CS authorizes but does NOT do local cost calculation | CSMS must calculate costs centrally (use I02/I03 flows) |
Requirements for CSMS
| Req ID | Requirement |
|---|---|
I08.FR.01 | When CSMS receives AuthorizeRequest for an accepted idToken with a driver-specific tariff, CSMS SHALL respond with a tariff field and relevant idTokenInfo |
I08.FR.08 | CSMS SHALL ensure that TariffTypes with different content have a different tariffId (so CostDetails records remain valid for historical transactions) |
I08.FR.09 | CSMS SHALL NOT provide validFrom in a driver-specific tariff in AuthorizeResponse |
Important Notes
- Authorization Cache or Local Authorization List cannot be used with
driver-specific tariffs (because
AuthorizeRequestis needed to get the tariff) - The
tariffIdchoice is up to the CSMS — it can be a sequential number, UUID, or hash of tariff contents - If the CS has
AuthCacheEnabled = true, the driver-specific tariff is stored in the Authorization Cache with the idToken
12. I09 — Local Cost Calculation: Get Tariffs
CSMS-Initiated New in 2.1| Use Case ID | I09 |
| Direction | CSMS → CS (CSMS initiates) |
| Objective | Query the Charging Station for all currently installed tariffs and their assignments |
| CSMS Role | Send GetTariffsRequest and process the response |
Flow Diagram
CSMS Charging Station
| |
|-- GetTariffsRequest ---->| (evseId=0)
|<-- GetTariffsResponse ---| (status=Accepted, tariffAssignments=[...])CSMS Action: Send GetTariffsRequest
{
"evseId": 0 // REQUIRED. integer, min 0.
// 0 = get tariffs from ALL EVSEs
// >0 = get tariffs only from specific EVSE
}CSMS Handler: GetTariffsResponse
{
"status": "Accepted", // REQUIRED. Enum: Accepted | Rejected | NoTariff
"tariffAssignments": [ // Optional. Present when status=Accepted
{
"tariffId": "Default01", // REQUIRED. Max 60 chars
"tariffKind": "DefaultTariff", // REQUIRED. Enum: DefaultTariff | DriverTariff
"validFrom": "2024-01-01T00:00:00Z", // Optional. Present if tariff has a validFrom
"evseIds": [1, 2], // Optional. Which EVSEs this tariff is installed on
"idTokens": ["ABCD1234"] // Optional. Which idTokens this tariff is associated with
},
{
"tariffId": "MSP01",
"tariffKind": "DriverTariff",
"evseIds": [1],
"idTokens": ["ABCD1234"]
}
]
}Response Handling
| Status | Meaning | CSMS Action |
|---|---|---|
Accepted | Tariffs found, assignments listed | Process the tariffAssignments array |
Rejected | Request rejected | Check statusInfo for reason |
NoTariff | No tariffs installed on the CS/EVSE | tariffAssignments is absent |
Understanding TariffAssignment Fields
tariffKind = DefaultTariff: Installed viaSetDefaultTariffRequest.evseIdslists where it's installed.validFrompresent if the tariff has a future activation date.tariffKind = DriverTariff: Installed viaAuthorizeResponseorChangeTransactionTariffRequest.idTokenslists which drivers it's for.evseIdslists where there's an active transaction with this tariff.
13. I10 — Local Cost Calculation: Clear Tariffs
CSMS-Initiated New in 2.1| Use Case ID | I10 |
| Direction | CSMS → CS (CSMS initiates) |
| Objective | Remove one or more default tariffs from a Charging Station |
| CSMS Role | Send ClearTariffsRequest and handle the response |
Flow Diagram
CSMS Charging Station
| |
|-- ClearTariffsRequest -->| (tariffIds=["Default01"])
|<-- ClearTariffsResponse -| ([{tariffId:"Default01", status:Accepted}])CSMS Action: Send ClearTariffsRequest
{
// All fields are optional
"tariffIds": ["Default01", "Default02"], // Optional. Specific tariff IDs to clear, max 60 chars each
// When absent: clears ALL tariffs matching evseId
"evseId": 1 // Optional. Only clear tariffs on this EVSE
// When absent: clear from all EVSEs
}Parameter Combinations
| tariffIds | evseId | Effect |
|---|---|---|
| absent | absent | Clear all tariffs from CS |
| present | absent | Clear specified tariffs from all EVSEs |
| absent | present | Clear all tariffs from specified EVSE |
| present | present | Clear only matching tariffs on specified EVSE |
CSMS Handler: ClearTariffsResponse
{
"clearTariffsResult": [ // REQUIRED. Array of ClearTariffsResultType, min 1 item
{
"tariffId": "Default01", // Optional. Absent when no tariffs found
"status": "Accepted", // REQUIRED. Enum: Accepted | Rejected | NoTariff
"statusInfo": { /* optional StatusInfoType */ }
}
]
}Response Handling
| Status | Meaning |
|---|---|
Accepted | Tariff was cleared (or will be cleared after current transaction ends) |
Rejected | Tariff could not be cleared |
NoTariff | No matching tariff was found (no tariffId in this result) |
Important Notes
ClearTariffsRequestonly applies to default tariffs. Driver-specific tariffs are automatically removed when no longer in use.- A tariff currently in use for an active transaction will be cleared (marked for removal) but the CS continues to use it until that transaction ends.
- To replace a tariff, send a new
SetDefaultTariffRequestwith a newtariffIdrather than clearing and re-adding.
14. I11 — Local Cost Calculation: Change Transaction Tariff
CSMS-Initiated New in 2.1| Use Case ID | I11 |
| Direction | CSMS → CS (CSMS initiates) |
| Objective | Change the tariff associated with an ongoing transaction |
| CSMS Role | Send ChangeTransactionTariffRequest when a mid-transaction tariff change is needed |
Flow Diagram
CSMS Charging Station
| |
|-- ChangeTransactionTariffReq -->| (transactionId="1234", tariff={...})
|<-- ChangeTransactionTariffRes --| (status=Accepted)
| |
| [CS starts using new tariff]
| |
|<-- TransactionEventReq --| (triggerReason=TariffChanged, tariffId=new)
|-- TransactionEventRes -->|CSMS Action: Send ChangeTransactionTariffRequest
{
"transactionId": "1234", // REQUIRED. string, max 36 chars. The active transaction ID
"tariff": { // REQUIRED. TariffType - the complete new tariff
"tariffId": "NewTariff1",
"currency": "EUR", // MUST be same currency as the current tariff
"energy": {
"prices": [{ "priceKwh": 0.35 }],
"taxRates": [{ "type": "vat", "tax": 19.0 }]
}
}
}CSMS Handler: ChangeTransactionTariffResponse
{
"status": "Accepted", // REQUIRED. Enum values:
// "Accepted" - Tariff change accepted
// "Rejected" - Generic rejection
// "TooManyElements" - Tariff too complex
// "ConditionNotSupported" - CS doesn't support conditions
// "TxNotFound" - Transaction ID not found or inactive
// "NoCurrencyChange" - Cannot change currency mid-transaction
"statusInfo": { // Optional
"reasonCode": "string",
"additionalInfo": "string"
}
}Response Handling Matrix
| Response Status | CSMS Action |
|---|---|
Accepted | Tariff change applied. Expect a TransactionEventRequest with triggerReason = TariffChanged |
TxNotFound | Transaction doesn't exist or is no longer active. No action needed. |
NoCurrencyChange | Currency mismatch. Resend with the correct currency. |
TooManyElements | Simplify tariff structure. |
ConditionNotSupported | Remove conditions from tariff. |
Rejected | Check statusInfo for details. Existing tariff remains active. |
Requirements for CSMS
| Req ID | Requirement |
|---|---|
I11.FR.05 | The new tariff's currency MUST match the currently active tariff's currency |
I11.FR.06 | Accepted when no error conditions (I11.FR.01-05) apply |
I11.FR.07 | After acceptance, CS sends TransactionEventRequest with triggerReason = TariffChanged and new tariffId |
I11.FR.08 | New tariff is applied from the moment of acceptance forward (no retroactive recalculation) |
Important Notes
- Changing a tariff during a transaction may not be legally allowed in certain jurisdictions
- Dynamic prices based on time-of-day can be handled within a single tariff using conditions, without needing mid-transaction changes
- The tariff change applies to a transaction (not a driver/idToken), because the same driver could have multiple transactions on different EVSEs
15. I12 — Local Cost Calculation: Cost Details of Transaction
CS-Initiated New in 2.1| Use Case ID | I12 |
| Direction | CS → CSMS (CS sends costDetails in TransactionEventRequest) |
| Objective | Receive detailed cost breakdown from the Charging Station |
| CSMS Role | Process the costDetails field in TransactionEventRequest messages |
Flow Diagram
Charging Station CSMS
| |
| [Transaction starts] |
|-- TransactionEventReq -->| (Started, tariffId="1")
|<-- TransactionEventRes --|
| |
| [Running cost updates, every CostInterval seconds]
|-- TransactionEventReq -->| (Updated, triggerReason=RunningCost, costDetails={totalCost only})
|<-- TransactionEventRes --|
| |
| [Transaction ends] |
|-- TransactionEventReq -->| (Ended, costDetails={full breakdown with chargingPeriods})
|<-- TransactionEventRes --|During Transaction (Running Cost)
eventType = Updated, triggerReason = RunningCost.
Contains totalCost and totalUsage but no chargingPeriods (to limit data usage).
At End of Transaction (Final Cost)
eventType = Ended.
Contains full breakdown including chargingPeriods.
CostDetailsType Structure
{
"totalCost": { // REQUIRED. TotalCostType
"currency": "EUR", // REQUIRED. ISO 4217
"typeOfCost": "NormalCost", // REQUIRED. Enum: NormalCost | MinCost | MaxCost
"fixed": { // Optional. PriceType - fixed fee costs
"exclTax": 2.50,
"inclTax": 2.875,
"taxRates": [{ "type": "vat", "tax": 15.0 }]
},
"energy": { // Optional. PriceType - energy costs
"exclTax": 5.00,
"inclTax": 5.50,
"taxRates": [{ "type": "vat", "tax": 10.0 }]
},
"chargingTime": { // Optional. PriceType
"exclTax": 2.00,
"inclTax": 2.40,
"taxRates": [{ "type": "vat", "tax": 20.0 }]
},
"idleTime": { // Optional. PriceType
"exclTax": 0,
"inclTax": 0
},
"reservationTime": { // Optional. PriceType
"exclTax": 1.00,
"inclTax": 1.15,
"taxRates": [{ "type": "vat", "tax": 15.0 }]
},
"reservationFixed": { // Optional. PriceType
"exclTax": 0.50,
"inclTax": 0.575,
"taxRates": [{ "type": "vat", "tax": 15.0 }]
},
"total": { // REQUIRED. TotalPriceType - sum of all above
"exclTax": 11.00,
"inclTax": 12.50
}
},
"totalUsage": { // REQUIRED. TotalUsageType
"energy": 15000, // REQUIRED. Wh consumed
"chargingTime": 3600, // REQUIRED. Seconds of charging
"idleTime": 300, // REQUIRED. Seconds of idle time
"reservationTime": 600 // Optional. Seconds of reservation
},
"chargingPeriods": [ // Optional. Present in final Ended message; absent in running cost
{
"startPeriod": "2024-01-15T14:00:00Z", // REQUIRED. Start of this period
"tariffId": "10", // Optional. Which tariff applied
"dimensions": [ // Optional. Cost dimensions for this period
{ "type": "Energy", "volume": 10000 },
{ "type": "ChargingTime", "volume": 3600 }
]
}
],
"failureToCalculate": false, // Optional. true = CS failed to calculate cost
"failureReason": "string" // Optional. Human-readable reason for failure. Max 500 chars.
}CostDimensionEnumType Values
| Value | Description |
|---|---|
Energy | Energy consumed in Wh |
ChargingTime | Time spent charging in seconds |
IdleTIme | Time spent idle in seconds (note: typo in spec) |
MaxCurrent | Maximum current during this period |
MinCurrent | Minimum current during this period |
MaxPower | Maximum power during this period |
MinPower | Minimum power during this period |
TariffCostEnumType for typeOfCost
| Value | Meaning |
|---|---|
NormalCost | Regular calculated cost |
MinCost | The tariff's minCost was applied (actual cost was lower) |
MaxCost | The tariff's maxCost was applied (actual cost was higher, capped) |
CostDetails Examples
{
"chargingPeriods": [{
"tariffId": "10",
"startPeriod": "2023-04-05T14:01:02Z",
"dimensions": [
{ "type": "Energy", "volume": 10000 }
]
}],
"totalCost": {
"currency": "EUR",
"typeOfCost": "NormalCost",
"energy": {
"exclTax": 2.50,
"inclTax": 2.75,
"taxRates": [
{ "type": "federal", "tax": 6.0 },
{ "type": "state", "tax": 4.0 }
]
},
"total": { "exclTax": 2.50, "inclTax": 2.75 }
},
"totalUsage": { "energy": 10000, "chargingTime": 3600, "idleTime": 0 }
}{
"chargingPeriods": [
{
"tariffId": "11",
"startPeriod": "2023-04-05T17:30:00Z",
"dimensions": [{ "type": "Energy", "volume": 6000 }]
},
{
"tariffId": "11",
"startPeriod": "2023-04-05T18:00:00Z",
"dimensions": [{ "type": "Energy", "volume": 4000 }]
}
],
"totalCost": {
"currency": "EUR",
"typeOfCost": "NormalCost",
"energy": { "exclTax": 3.10, "inclTax": 3.41, "taxRates": [{ "type": "vat", "tax": 10.0 }] },
"total": { "exclTax": 3.10, "inclTax": 3.41 }
},
"totalUsage": { "energy": 10000, "chargingTime": 1800, "idleTime": 0 }
}CSMS Processing Requirements
| Req ID | Requirement |
|---|---|
I12.FR.01 | When transactionInfo.tariffId is set AND TariffCostCtrlr.Enabled[Cost] = true AND TariffCostCtrlr.Enabled[Tariff] = true, the CS provides costDetails in the final TransactionEventRequest with eventType = Ended |
I12.FR.02 | Running cost updates (when RunningCostEnabled = true) include costDetails without chargingPeriods |
I12.FR.04 | Each chargingPeriods entry contains a tariffId reference |
I12.FR.17 | total in TotalCostType is the sum of all exclTax and inclTax from fixed, energy, chargingTime, idleTime, reservationTime, and reservationFixed |
Important Notes for CSMS
- Use
costDetailsfor invoice generation and CDR (Charge Detail Record) creation - The
chargingPeriodscombined with the referencedtariffIdprovides full auditability - If
failureToCalculate = true, the CSMS should fall back to its own calculation or flag the transaction for manual review startPerioduses timestamps (not seconds since start) for easier CSO/EMSP tariff validation
16. NotifySettlement — Payment Settlement
CS-Initiated| Direction | CS → CSMS (CS initiates) |
| Objective | Receive payment settlement information from the Charging Station |
| CSMS Role | Process incoming NotifySettlementRequest and optionally return receipt information |
Flow Diagram
Charging Station CSMS
| |
|-- NotifySettlementReq -->| (pspRef, status, amount, time)
|<-- NotifySettlementRes --| (optional: receiptUrl, receiptId)CSMS Handler: NotifySettlementRequest
{
"pspRef": "PSP-123456", // REQUIRED. Payment reference from terminal, max 255 chars
// Used as idToken value
"status": "Settled", // REQUIRED. Enum: Settled | Canceled | Rejected | Failed
"settlementAmount": 15.75, // REQUIRED. number. Amount that was settled (or attempted)
"settlementTime": "2024-01-15T15:00:00Z", // REQUIRED. date-time. When settlement occurred
"transactionId": "tx-123", // Optional. OCPP transaction ID, max 36 chars
// Can be empty if payment cancelled before OCPP transaction started
"statusInfo": "Payment approved", // Optional. Additional info from payment terminal, max 500 chars
"receiptId": "REC-001", // Optional. Receipt ID, max 50 chars
"receiptUrl": "https://...", // Optional. Receipt URL from CS/terminal, max 2000 chars
"vatCompany": { // Optional. AddressType for company receipt
"name": "Company Name", // REQUIRED
"address1": "Street 1", // REQUIRED
"city": "City", // REQUIRED
"country": "NL", // REQUIRED
"address2": "string", // Optional
"postalCode": "1234AB" // Optional
},
"vatNumber": "NL123456789B01" // Optional. VAT number, max 20 chars
}{
"receiptUrl": "https://csms.example.com/receipts/REC-001", // Optional. CSMS-generated receipt URL
// CS can QR-encode for the driver
"receiptId": "CSMS-REC-001" // Optional. CSMS-generated receipt ID
}Payment Status Handling
| Status | CSMS Action |
|---|---|
Settled | Record successful payment. Link to transaction. Generate invoice/CDR. |
Canceled | Payment was cancelled (possibly before transaction started). No charge to customer. |
Rejected | Payment was rejected by the payment processor. Flag for follow-up. |
Failed | Payment failed technically. May need retry or manual intervention. |
17. CostDetails Structure Reference
ReferenceComplete Type Hierarchy
TransactionEventRequest
└── costDetails: CostDetailsType
├── chargingPeriods: ChargingPeriodType[]
│ ├── startPeriod: date-time (REQUIRED)
│ ├── tariffId: string
│ └── dimensions: CostDimensionType[]
│ ├── type: CostDimensionEnumType (REQUIRED)
│ └── volume: number (REQUIRED)
├── totalCost: TotalCostType (REQUIRED)
│ ├── currency: string (REQUIRED)
│ ├── typeOfCost: TariffCostEnumType (REQUIRED)
│ ├── fixed: PriceType
│ ├── energy: PriceType
│ ├── chargingTime: PriceType
│ ├── idleTime: PriceType
│ ├── reservationTime: PriceType
│ ├── reservationFixed: PriceType
│ └── total: TotalPriceType (REQUIRED)
│ ├── exclTax: number
│ └── inclTax: number
├── totalUsage: TotalUsageType (REQUIRED)
│ ├── energy: number (REQUIRED, Wh)
│ ├── chargingTime: integer (REQUIRED, seconds)
│ ├── idleTime: integer (REQUIRED, seconds)
│ └── reservationTime: integer (optional, seconds)
├── failureToCalculate: boolean
└── failureReason: stringQuick Decision Matrix: Which Flow to Implement?
| Scenario | Central Cost Calc | Local Cost Calc |
|---|---|---|
| Display tariff before charging | I01 (personalMessage) | I07 + I08 (TariffType) |
| Show running cost during charging | I02 (CostUpdatedRequest or totalCost in TransactionEventResponse) | I12 (costDetails in TransactionEventRequest) |
| Show final cost after charging | I03 (totalCost in TransactionEventResponse) | I12 (costDetails in TransactionEventRequest) |
| Fallback when offline | I04, I05 (config variables) | I04, I05 (config variables) |
| Update tariff info during charging | I06 (updatedPersonalMessage) | I11 (ChangeTransactionTariffRequest) |
| Query installed tariffs | N/A | I09 (GetTariffsRequest) |
| Remove tariffs | N/A | I10 (ClearTariffsRequest) |
| Payment settlement | NotifySettlement | NotifySettlement |
TransactionEventRequest triggerReason Values (Tariff-Related)
| triggerReason | Meaning | CSMS Action |
|---|---|---|
RunningCost | CS is sending a running cost update | Process costDetails, optionally return totalCost |
TariffChanged | Tariff was changed (via ChangeTransactionTariffRequest) | Note the new tariffId in transactionInfo |
TariffNotAccepted | EV Driver rejected the tariff | Transaction has no tariff/cost. No energy will be delivered. |
CostLimitReached | Transaction cost limit was reached | Transaction may stop soon. Process accordingly. |
TransactionEventResponse Fields (Tariff-Related)
| Field | Type | Context | Description |
|---|---|---|---|
totalCost | number | Updated/Ended | Running or final total cost including taxes |
updatedPersonalMessage | MessageContentType | Updated | Updated tariff text (default language) |
updatedPersonalMessageExtra | MessageContentType[] | Updated | Updated tariff text (other languages, max 4) |
transactionLimit | TransactionLimitType | Any | Cost/energy/time/SoC limits for the transaction |
idTokenInfo | IdTokenInfoType | Any | Authorization status update |
TransactionLimitType (set by CSMS in TransactionEventResponse)
{
"maxCost": 50.00, // Optional. Max cost in tariff currency
"maxEnergy": 30000, // Optional. Max energy in Wh
"maxTime": 7200, // Optional. Max duration in seconds
"maxSoC": 80 // Optional. Max State of Charge (0-100%)
}