# `MPP.Methods.Tempo.SessionReceipt`
[🔗](https://github.com/ZenHive/mpp/blob/v0.10.0/lib/mpp/methods/tempo/session_receipt.ex#L1)

Session-intent receipt for Tempo — returned in the `Payment-Receipt` header
for session/pay-as-you-go payments.

Extends the base [`MPP.Receipt`](`MPP.Receipt`) contract with session-specific
fields: `channel_id`, `accepted_cumulative`, `spent`, and the optional
`units` / `tx_hash`. The `reference` field mirrors `channel_id` so this
receipt satisfies the base receipt shape.

Serialized as base64url-encoded JSON (no padding) for transport. Wire keys
are camelCase (`challengeId`, `channelId`, `acceptedCumulative`, `txHash`) to
stay compatible with the `mppx` and `mpp-rs` reference SDKs. Optional fields
are omitted from the wire JSON entirely when `nil` (not serialized as
`null`).

Receipts only represent success. Session failures are communicated via
HTTP 402 responses with RFC 9457 Problem Details (see `MPP.Errors`).

## Fields

  * `method` — always `"tempo"`
  * `intent` — always `"session"`
  * `status` — always `"success"`
  * `timestamp` — RFC 3339 datetime string (set by `new/1` from
    `DateTime.utc_now/0` unless overridden)
  * `reference` — mirrors `channel_id`; satisfies the base receipt contract
  * `challenge_id` — challenge identifier
  * `channel_id` — payment channel identifier (hex)
  * `accepted_cumulative` — highest accepted cumulative voucher amount
    (decimal string; bigint wire format)
  * `spent` — amount spent in this session (decimal string)
  * `units` — optional integer, number of units consumed
  * `tx_hash` — optional settlement transaction hash (hex)
  * `external_id` — optional merchant correlation ID echoed from the charge request

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `from_header` | 1 | Decode a base64url JSON `Payment-Receipt` header value into a session receipt. | `encoded: value` |
| `to_header` | 1 | Encode a session receipt to a base64url JSON string (no padding) for the `Payment-Receipt` header. | `receipt: value` |
| `new` | 1 | Create a new Tempo session receipt with defaults for method/intent/status/timestamp. | `opts: value` |

# `t`

```elixir
@type t() :: %MPP.Methods.Tempo.SessionReceipt{
  accepted_cumulative: String.t(),
  challenge_id: String.t(),
  channel_id: String.t(),
  external_id: String.t() | nil,
  intent: String.t(),
  method: String.t(),
  reference: String.t(),
  spent: String.t(),
  status: String.t(),
  timestamp: String.t(),
  tx_hash: String.t() | nil,
  units: non_neg_integer() | nil
}
```

# `from_header`

```elixir
@spec from_header(String.t()) :: {:ok, t()} | {:error, atom()}
```

Decode a base64url JSON `Payment-Receipt` header value into a session receipt.

## Parameters

  * `encoded` - Base64url-encoded JSON session receipt string (value)

## Returns

`{:ok, receipt}` on success, `{:error, reason}` on failure (`tagged_tuple`)

## Errors

  * `:invalid_base64`
  * `:invalid_json`
  * `:missing_required_fields`
  * `:invalid_field_type`
  * `:token_too_large`

## Composes With

  * `to_header`

```elixir
# descripex:contract
%{
  params: %{
    encoded: %{
      description: "Base64url-encoded JSON session receipt string",
      kind: :value
    }
  },
  errors: [:invalid_base64, :invalid_json, :missing_required_fields,
   :invalid_field_type, :token_too_large],
  returns: %{
    type: :tagged_tuple,
    description: "`{:ok, receipt}` on success, `{:error, reason}` on failure"
  },
  composes_with: [:to_header]
}
```

# `new`

```elixir
@spec new(keyword()) :: t()
```

Create a new Tempo session receipt with defaults for method/intent/status/timestamp.

## Parameters

  * `opts` - Keyword list with `:challenge_id`, `:channel_id`, `:accepted_cumulative`, `:spent` (all required), and optional `:timestamp`, `:units`, `:tx_hash`. `:reference` defaults to `channel_id`. (value)

## Returns

SessionReceipt struct with method `"tempo"`, intent `"session"`, status `"success"`, RFC 3339 timestamp, and `reference` mirroring `channel_id` (`struct`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      description: "Keyword list with `:challenge_id`, `:channel_id`, `:accepted_cumulative`, `:spent` (all required), and optional `:timestamp`, `:units`, `:tx_hash`. `:reference` defaults to `channel_id`.",
      kind: :value
    }
  },
  returns: %{
    type: :struct,
    description: "SessionReceipt struct with method `\"tempo\"`, intent `\"session\"`, status `\"success\"`, RFC 3339 timestamp, and `reference` mirroring `channel_id`"
  }
}
```

# `to_header`

```elixir
@spec to_header(t()) :: String.t()
```

Encode a session receipt to a base64url JSON string (no padding) for the `Payment-Receipt` header.

## Parameters

  * `receipt` - SessionReceipt struct to encode (value)

## Returns

Base64url-encoded JSON string (`string`)

## Composes With

  * `from_header`

```elixir
# descripex:contract
%{
  params: %{
    receipt: %{description: "SessionReceipt struct to encode", kind: :value}
  },
  returns: %{type: :string, description: "Base64url-encoded JSON string"},
  composes_with: [:from_header]
}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
