# `MPP.Credential`
[🔗](https://github.com/ZenHive/mpp/blob/v0.16.1/lib/mpp/credential.ex#L1)

Payment credential — the client's response to a 402 challenge.

A credential is sent in the `Authorization: Payment <base64url JSON>` header
after the client has fulfilled payment. It echoes the original challenge
parameters alongside method-specific payment proof, enabling the server to
verify both the challenge binding (via `MPP.Challenge.verify/2`) and the
payment itself (via the method module).

## Wire Format

The base64url-decoded JSON contains:

    %{
      "challenge" => %{...},   # Echoed challenge parameters
      "payload"   => %{...},   # Method-specific payment proof
      "source"    => "did:..."  # (optional) Payer identifier
    }

The `challenge.request` field remains as its raw base64url string — never
re-serialized — to preserve the exact bytes used in HMAC computation.

## Hash credentials

Several payment methods (Tempo, EVM, Hedera, Stellar, Near Intents) accept
`payload.type = "hash"`: the client broadcasts a transfer and presents the
confirmed transaction identifier. That type was backfilled as first-class in
the EVM spec (`draft-evm-charge-00` §1.3, mpp-specs #261) alongside
`permit2` / `authorization` / `transaction`.

`decode/1` still treats `payload` as an opaque map — the same as mppx
`Credential.deserialize` and mpp-rs `PaymentCredential.payload`. Typed hash
access is `hash_payload/1` (construct) and `parse_hash_payload/1` (extract),
matching mpp-rs `PaymentPayload::hash` / `charge_payload()`. Method-specific
hash format (0x-prefixed 32-byte hex vs chain-native) is verified by the
method, not here. `MPP.Verifier` rejects a well-formed hash payload when the
method's `credential_types/0` does not include `"hash"`.

## Fields

  * `challenge` — echoed `MPP.Challenge` struct from the 402 response
  * `payload` — method-specific payment proof (opaque map at this layer)
  * `source` — (optional) payer identifier, recommended as DID format

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `parse_hash_payload` | 1 | Extract the hash from a `type="hash"` payload (mpp-rs `PaymentPayload` / `charge_payload()`). | `payload: value` |
| `hash_payload` | 1 | Build a `type="hash"` credential payload map (mpp-rs `PaymentPayload::hash`). | `hash: value` |
| `encode` | 1 | Encode a credential to a base64url JSON string (no padding) for the Authorization header. | `credential: value` |
| `decode` | 1 | Decode a base64url JSON string into a credential with echoed challenge validation. | `encoded: value` |

# `t`

```elixir
@type t() :: %MPP.Credential{
  challenge: MPP.Challenge.t(),
  payload: map(),
  source: String.t() | nil
}
```

# `decode`

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

Decode a base64url JSON string into a credential with echoed challenge validation.

## Parameters

  * `encoded` - Base64url-encoded JSON credential string (value)

## Returns

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

## Errors

  * `:invalid_base64`
  * `:invalid_json`
  * `:missing_required_fields`
  * `:invalid_optional_field`
  * `:empty_id`
  * `:invalid_method`
  * `:invalid_request`
  * `:invalid_digest`

## Composes With

  * `encode`

```elixir
# descripex:contract
%{
  params: %{
    encoded: %{
      description: "Base64url-encoded JSON credential string",
      kind: :value
    }
  },
  errors: [:invalid_base64, :invalid_json, :missing_required_fields,
   :invalid_optional_field, :empty_id, :invalid_method, :invalid_request,
   :invalid_digest],
  returns: %{
    type: :tagged_tuple,
    description: "`{:ok, credential}` on success, `{:error, reason}` on failure"
  },
  composes_with: [:encode]
}
```

# `encode`

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

Encode a credential to a base64url JSON string (no padding) for the Authorization header.

## Parameters

  * `credential` - Credential struct to encode (value)

## Returns

Base64url-encoded JSON string (`string`)

## Composes With

  * `decode`

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

# `hash_payload`

```elixir
@spec hash_payload(String.t()) :: map()
```

Build a `type="hash"` credential payload map (mpp-rs `PaymentPayload::hash`).

## Parameters

  * `hash` - Transaction hash or chain-native identifier string (value)

## Returns

Map with type=hash and hash — no signature field (`map`)

## Composes With

  * `parse_hash_payload`
  * `encode`

```elixir
# descripex:contract
%{
  params: %{
    hash: %{
      description: "Transaction hash or chain-native identifier string",
      kind: :value
    }
  },
  returns: %{
    type: :map,
    description: "Map with type=hash and hash — no signature field"
  },
  composes_with: [:parse_hash_payload, :encode]
}
```

# `parse_hash_payload`

```elixir
@spec parse_hash_payload(t() | map()) ::
  {:ok, String.t()} | {:error, :missing_hash | :not_hash_payload}
```

Extract the hash from a `type="hash"` payload (mpp-rs `PaymentPayload` / `charge_payload()`).

## Parameters

  * `payload` - Credential struct or payload map (value)

## Returns

`{:ok, hash}` on a hash payload, `{:error, reason}` otherwise (`tagged_tuple`)

## Errors

  * `:missing_hash`
  * `:not_hash_payload`

## Composes With

  * `hash_payload`

```elixir
# descripex:contract
%{
  params: %{
    payload: %{description: "Credential struct or payload map", kind: :value}
  },
  errors: [:missing_hash, :not_hash_payload],
  returns: %{
    type: :tagged_tuple,
    description: "`{:ok, hash}` on a hash payload, `{:error, reason}` otherwise"
  },
  composes_with: [:hash_payload]
}
```

---

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