Skip to content
LogoLogo
Blog

May 18, 2026

Payment hooks

Observe MPP requests with typed lifecycle events

SDKs now expose typed payment hooks for client and server payment flows. Use them to record what happened around an MPP request without rewriting your payment handler.

Hooks are useful when payment telemetry belongs next to the rest of your application telemetry: logs, metrics, traces, audit records, support dashboards, or local debugging context.

What changed

Client hooks observe Challenge selection, Credential creation, retry responses, and failures. Use typed helpers for common events, canonical strings for direct event names, or * for a single catch-all handler:

client.ts
import { ,  } from 'mppx/client'
import {  } from 'viem/accounts'
 
const  = (.. as `0x${string}`)
 
const  = .({
  : [({  })],
  : false,
})
 
const  = (: string, : <string, unknown>) => {
  .(, )
}
 
.(({  }) => {
  // Observe the selected Challenge before the SDK creates a Credential.
  ('payment.challenge.received', {
    : .,
    : .,
    : .,
  })
  return 
})
 
.('payment.response', ({ ,  }) => {
  // Use canonical event names when you want to share event wiring.
  ('payment.response', {
    : .,
    : .,
  })
})
 
.('*', ({ ,  }) => {
  // Use `*` to send all payment events through one telemetry path.
  ('payment.event', {
    : 'challenge' in ,
    ,
  })
})
 
.(({ ,  }) => {
  // Capture failures from Challenge parsing, Credential creation, or retry handling.
  ('payment.failed', {
    :  instanceof  ? . : (),
    : (),
  })
})
 
const  = await .('https://api.example.com/report')

Server hooks observe issued Challenges, successful payments, and rejected Credentials:

server.ts
import { ,  } from 'mppx/server'
 
const  = .({
  : [.()],
})
 
const  = (: string, : <string, unknown>) => {
  .(, )
}
 
.(({ ,  }) => {
  // Observe each `402` Challenge before it is returned to the client.
  ('payment.challenge.created', {
    : .,
    : .,
    : .,
  })
})
 
.('payment.success', ({ ,  }) => {
  // Use canonical event names when you want to share event wiring.
  ('payment.success', {
    : .,
    : .,
    : .,
    : .,
  })
})
 
.('*', ({ ,  }) => {
  // Use `*` to send all payment events through one telemetry path.
  ('payment.event', {
    : ..,
    : ..,
    ,
  })
})
 
.(({ , ,  }) => {
  // Record failed Credential verification with request and Challenge context.
  ('payment.failed', {
    : .,
    : .,
    : .,
  })
})

Why hooks matter

MPP keeps the payment flow close to the request flow. That makes integration simple, but production systems still need visibility.

Hooks give you a typed place to attach that visibility:

  • Monitoring and observability: Count Challenges, successful payments, failed Credentials, and paid retry responses, then attach Challenge IDs, method names, intents, amounts, currencies, and Receipt references to traces.
  • Logging: Record enough context to debug a failed payment without logging secrets.
  • Support: Connect a user-facing request to the payment attempt that authorized it.

Use hooks carefully

Client observation hooks don't change payment handling. If a client observation hook throws, mppx ignores the error and continues the payment flow.

onChallengeReceived is the one client hook that can affect handling: return a non-empty Credential string to use it for the retry. This lets advanced clients create Credentials with custom context before falling back to onChallenge or the default flow.

Server hooks run inline on the payment request path. Keep them short, or hand work to your logger, metrics client, or queue. mppx ignores thrown server hook errors so observability code doesn't block payment verification, but slow hooks still delay the response.

What's next

This release starts with core lifecycle events. If there is an event or payload field you need, leave feedback on GitHub.