MPP.JCS (mpp v0.6.0)

Copy Markdown View Source

RFC 8785 JSON Canonicalization Scheme (JCS) — MPP subset.

Produces deterministic JSON output by sorting object keys lexicographically and using minimal whitespace. This ensures two implementations serializing the same logical data produce byte-identical JSON — critical for HMAC reproducibility across SDKs.

Used by MPP.Plug and MPP.Mcp when encoding the request field before base64url encoding and HMAC computation. Matches the canonicalization in mppx (ox.Json.canonicalize) and mpp-rs (serde_json_canonicalizer) for all MPP protocol data.

Scope

This implements the subset of RFC 8785 needed by MPP:

  • Keys: ASCII-only (all MPP protocol fields). Sorted by Elixir binary comparison, which matches UTF-16 code-unit ordering for ASCII.
  • Values: strings, integers, booleans, nil, maps, lists.
  • Not supported: floats. MPP amounts are strings in base units (never floats). Passing a float raises FunctionClauseError.

For a general-purpose RFC 8785 implementation (non-ASCII keys, IEEE 754 float serialization), use a dedicated library.

Examples

iex> MPP.JCS.canonicalize(%{"currency" => "usd", "amount" => "1000"})
~S({"amount":"1000","currency":"usd"})

iex> MPP.JCS.canonicalize(%{"b" => %{"d" => 1, "c" => 2}, "a" => 3})
~S({"a":3,"b":{"c":2,"d":1}})

API Functions

FunctionArityDescriptionParam Kinds
canonicalize1Serialize a term to canonical JSON per RFC 8785 (MPP subset: ASCII keys, no floats).term: value

Summary

Functions

Serialize a term to canonical JSON per RFC 8785 (MPP subset: ASCII keys, no floats).

Types

canonicalizable()

@type canonicalizable() :: map() | list() | String.t() | integer() | boolean() | nil

Functions

canonicalize(term)

@spec canonicalize(canonicalizable()) :: String.t()

Serialize a term to canonical JSON per RFC 8785 (MPP subset: ASCII keys, no floats).

Parameters

  • term - Elixir term (map, list, string, integer, boolean, or nil) (value)

Returns

Canonical JSON string (string)

# descripex:contract
%{
  params: %{
    term: %{
      description: "Elixir term (map, list, string, integer, boolean, or nil)",
      kind: :value
    }
  },
  returns: %{type: :string, description: "Canonical JSON string"}
}