Skip to content

Fix: Spell receiptUrl the way the 2.1 schemas do - #780

Merged
OSkrk merged 1 commit into
mobilityhouse:masterfrom
OSkrk:fix/receipt-url-camel-case
Sep 30, 2026
Merged

OSkrk merged 1 commit into
mobilityhouse:masterfrom
OSkrk:fix/receipt-url-camel-case

Conversation

@OSkrk

@OSkrk OSkrk commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

Current behavior

snake_to_camel_case rewrites any key ending in _url to ...URL. That is correct for
responderURL, and ocpp_csms_url is already special-cased just above it, but OCPP 2.1
spells the NotifySettlement field receiptUrl:

>>> from ocpp.charge_point import snake_to_camel_case
>>> snake_to_camel_case({"receipt_url": "https://example.com/receipt/1"})
{'receiptURL': 'https://example.com/receipt/1'}

The library therefore produces a payload its own validation rejects, in both directions:

jsonschema.exceptions.ValidationError:
Additional properties are not allowed ('receiptURL' was unexpected)

Failed validating 'additionalProperties' in schema:
    {'$id': 'urn:OCPP:Cp:2:2025:1:NotifySettlementResponse',
     'properties': {'receiptId': {...}, 'receiptUrl': {...}}, ...}

In practice a charging station reporting a settled payment can neither send the receipt
URL it generated nor relay the one the CSMS returned — NotifySettlementRequest and
NotifySettlementResponse both carry receiptUrl, so both fail with FormatViolation
and the receipt is lost. Found on a live 2.1 link between a charging station and a CSMS,
where the CSMS answered with a correct receiptUrl and the station's own schema check
refused it on the way back.

The comment above these lines already notes that the spec is inconsistent about URL
casing; receipt_url is simply a case that was never added.

New behavior

One replacement alongside the existing ocpp_csms_url case:

key = key.replace("ocpp_csms_url", "ocppCsmsUrl")
key = key.replace("receipt_url", "receiptUrl")
key = key.replace("csms", "CSMS")
key = key.replace("_url", "URL")

camel_to_snake_case already reverses receiptUrl correctly, so nothing was needed
there; a test covers both directions.

Impact

No breaking change, and nothing to do for users.

  • receiptUrl, responderURL and ocppCsmsUrl are the complete set of URL-shaped keys
    across the 1.6, 2.0.1 and 2.1 schemas. responderURL and ocppCsmsUrl are untouched
    and serialise exactly as before; this completes the set.
  • 1.6 and 2.0.1 have no receipt field at all, so no message in any earlier version
    goes near this rule. receipt_url appears in exactly two places in the library,
    v21/call.py and v21/call_result.py, both NotifySettlement.
  • No schema in any version expects receiptURL, so the old spelling was never valid
    and nothing can have depended on it — anything that produced it was already being
    rejected by payload validation.

Checklist

  1. Does your submission pass the existing tests?
    pytest tests passes (tests/test_messages.py needs hypothesis, which my
    environment lacks; it is untouched by this change).
  2. Are there new tests that cover these additions/changes?
    One row added to each of the existing test_snake_to_camel_case and
    test_camel_to_snake_case tables, so both directions are covered.
  3. Have you linted your code locally before submission?
    black --check, isort --check-only and flake8 --config .flake8 are clean on the
    changed files.

snake_to_camel_case turns any key ending in _url into ...URL. That is right for
responderURL, and ocpp_csms_url is already special-cased, but the OCPP 2.1
NotifySettlement schemas spell the field receiptUrl - so a NotifySettlement
carrying a receipt is serialised as receiptURL and rejected by the library's own
payload validation, in both directions:

    >>> snake_to_camel_case({"receipt_url": "https://example.com/r/1"})
    {'receiptURL': 'https://example.com/r/1'}

    jsonschema.exceptions.ValidationError:
    Additional properties are not allowed ('receiptURL' was unexpected)

A charging station reporting a settled payment therefore cannot send the receipt
URL it generated, and cannot receive the one the CSMS generated.

receiptUrl, responderURL and ocppCsmsUrl are the only URL-shaped keys in the 2.1
schemas, so this completes the set. camel_to_snake_case already reverses
receiptUrl correctly; a test is added for both directions.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The focused change matches the NotifySettlement schemas and has tests for both conversions.

Review effort: Balanced
Findings: None

What changed in this PR

This PR makes URL field conversion match the OCPP 2.1 NotifySettlement schemas, so receipt URLs use receiptUrl.

Changes:

  • Special-cases receipt_url during serialization.
  • Adds tests for conversion in both directions.
File Description
tests/​test_charge_point.py Tests both receiptUrl conversions.
ocpp/​charge_point.py Serializes receipt_url as receiptUrl.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@OSkrk
OSkrk merged commit f92059f into mobilityhouse:master Sep 30, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants