Skip to content

Commit bf58825

Browse files
authored
docs: update mppx hooks documentation (#649)
1 parent adfb72f commit bf58825

17 files changed

Lines changed: 498 additions & 48 deletions

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
"@stripe/stripe-js": "^9.3.1",
2626
"@vercel/blob": "^2.3.3",
2727
"mermaid": "^11.14.0",
28-
"mppx": "0.6.20",
28+
"mppx": "0.6.27",
2929
"react": "^19",
3030
"react-dom": "^19",
3131
"stripe": "^22.1.0",
3232
"tailwindcss": "^4.2.4",
33-
"viem": "^2.48.4",
33+
"viem": "^2.50.4",
3434
"vocs": "https://pkg.pr.new/vocs@425",
3535
"wagmi": "^3.6.5",
3636
"waku": "^1.0.0-alpha.8"

pnpm-lock.yaml

Lines changed: 38 additions & 38 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/pages/blog/index.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ imageDescription: "Updates on the Machine Payments Protocol"
1515
import { BlogPostList } from "../../components/BlogPostList";
1616

1717
<BlogPostList posts={[
18+
{
19+
date: "Thursday, May 21, 2026",
20+
title: "Payment hooks",
21+
description: <>Core SDKs now expose typed payment hooks for logging, monitoring, and request context.</>,
22+
to: "/blog/payment-hooks",
23+
},
1824
{
1925
date: "May 12, 2026",
2026
title: "Subscriptions",

src/pages/blog/payment-hooks.mdx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
layout: minimal
3+
outline: false
4+
showAskAi: false
5+
showFeedback: false
6+
showSearch: false
7+
description: "Core SDKs now expose typed payment hooks for logging, monitoring, and request context."
8+
imageDescription: "Monitor request status with SDK hooks"
9+
---
10+
11+
<div className="blog-narrow">
12+
13+
<a href="/blog" className="blog-back">Blog</a>
14+
15+
<p className="blog-date" style={{ color: 'var(--vocs-color_text3)', fontSize: '14px' }}>Thursday, May 21, 2026</p>
16+
17+
# Payment hooks [Observe MPP requests with typed lifecycle events]
18+
19+
MPP's core 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.
20+
21+
## Why hooks matter
22+
23+
MPP puts the payment flow close to the request flow in REST APIs and MCP servers. That makes basic integration simple, but becomes fragile when used in production applications and existing tech stacks.
24+
25+
Hooks give you a typed place to attach that visibility:
26+
27+
- **Monitoring and observability**: Count `Challenge`s, successful payments, failed `Credential`s, and paid retry responses, then attach `Challenge` IDs, method names, intents, amounts, currencies, and `Receipt` references to traces.
28+
- **Logging**: Record enough context to debug a failed payment without logging secrets.
29+
- **Support**: Connect a user-facing request to the payment attempt that authorized it.
30+
31+
## What changed
32+
33+
Server hooks observe issued Challenges, successful payments, and rejected Credentials. Use typed helpers for common events, canonical strings for direct event names, or `*` for a single catch-all handler:
34+
35+
```ts twoslash [server.ts]
36+
import { Mppx, tempo } from 'mppx/server'
37+
38+
const payment = Mppx.create({
39+
methods: [tempo.charge()],
40+
})
41+
42+
const log = (event: string, data: Record<string, unknown>) => {
43+
console.log(event, data)
44+
}
45+
46+
payment.onChallengeCreated(({ challenge, request }) => { // [!code hl]
47+
log('payment.challenge.created', {
48+
amount: request.amount,
49+
challengeId: challenge.id,
50+
})
51+
})
52+
53+
payment.on('payment.success', ({ receipt, request }) => { // [!code hl]
54+
log('payment.success', {
55+
amount: request.amount,
56+
reference: receipt.reference,
57+
})
58+
})
59+
60+
payment.onPaymentFailed(({ challenge, error, request }) => { // [!code hl]
61+
log('payment.failed', {
62+
amount: request.amount,
63+
challengeId: challenge.id,
64+
error: error.name,
65+
})
66+
})
67+
```
68+
69+
## What's next
70+
71+
This release starts with core lifecycle events of the MPP request flow. If there is an event or payload field which you would like to monitor, please leave feedback on [GitHub](https://github.com/wevm/mppx/issues).
72+
73+
</div>

src/pages/sdk/features.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ This page tracks which features are implemented in each official SDK.
1313
| Component | [TypeScript](https://github.com/wevm/mppx) | [Rust](https://github.com/tempoxyz/mpp-rs) | [Python](https://github.com/tempoxyz/pympp) | [Ruby](https://github.com/stripe/mpp-rb) |
1414
|---|---|---|---|---|
1515
| Client |||||
16-
| Server |||||
16+
| Event handling |||||
1717
| Proxy |||||
18+
| Server |||||
1819

1920
## Payment methods
2021

2122
| Method | TypeScript | Rust | Python | Ruby |
2223
|---|---|---|---|---|
2324
| [Tempo](/payment-methods/tempo) |||||
24-
| [Stripe](/payment-methods/stripe) || | ||
25+
| [Stripe](/payment-methods/stripe) || | ||
2526

2627
Additional payment methods implement their own SDKs. Refer to the maintaining organizations for support and availability.
2728

src/pages/sdk/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { TypeScriptSdkCard, PythonSdkCard, RustSdkCard, GoSdkCard, RubySdkCard,
2525
| **Server** | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> |
2626
| **Core types** | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> |
2727
| **Charge intent** | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> |
28+
| **Event handling** | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="warning">—</Badge> | <Badge variant="success">✓</Badge> |
2829
| **Session intent** | <Badge variant="success">✓</Badge> | <Badge variant="warning">—</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="warning">—</Badge> | <Badge variant="warning">—</Badge> |
2930
| **Stripe method** | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="warning">—</Badge> | <Badge variant="success">✓</Badge> |
3031
| **Fee sponsorship** | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> | <Badge variant="success">✓</Badge> |

src/pages/sdk/python/client.mdx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,50 @@ async with Client(methods=[tempo(account=account, intents={"charge": ChargeInten
2121
print(response.json())
2222
```
2323

24+
## Event handling
25+
26+
Register event handlers to record the automatic `402` flow. Use helper methods for named events, event constants for shared wiring, and `*` for every client event.
27+
28+
```python [client.py]
29+
from mpp.client import Client
30+
from mpp.events import PAYMENT_RESPONSE
31+
from mpp.methods.tempo import tempo, TempoAccount, ChargeIntent
32+
33+
account = TempoAccount.from_key("0x...")
34+
35+
async with Client(methods=[tempo(account=account, intents={"charge": ChargeIntent()})]) as client:
36+
def log(event: str, data: dict[str, object]) -> None:
37+
print(event, data)
38+
39+
# Record the selected Challenge before the client creates a Credential.
40+
client.on_challenge_received(
41+
lambda payload: log(
42+
"payment.challenge.received",
43+
{
44+
"challenge_id": payload["challenge"].id,
45+
"intent": payload["challenge"].intent,
46+
"method": payload["challenge"].method,
47+
},
48+
)
49+
)
50+
51+
# Record the retried response after payment.
52+
client.on(
53+
PAYMENT_RESPONSE,
54+
lambda payload: log(
55+
"payment.response",
56+
{"status": payload["response"].status_code},
57+
),
58+
)
59+
60+
# Catch every client payment event in one handler.
61+
client.on("*", lambda event: log("payment.event", {"name": event.name}))
62+
63+
response = await client.get("https://api.example.com/resource")
64+
```
65+
66+
`on_challenge_received` can return a `Credential` to override the default Credential creation path. Other handlers only record payment handling. Each registration returns an unsubscribe function.
67+
2468
## Common methods
2569

2670
| Method | Description |

0 commit comments

Comments
 (0)