Skip to content

Commit b394ad4

Browse files
committed
Harden x402 paid-action example responses
1 parent b27061d commit b394ad4

3 files changed

Lines changed: 37 additions & 2 deletions

File tree

examples/x402-paid-action-receipt/server.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ function dedupeKey(requestId, paymentId) {
1010
}
1111

1212
function sendJson(res, statusCode, body) {
13-
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
13+
res.writeHead(statusCode, {
14+
'Content-Type': 'application/json',
15+
'Cache-Control': 'no-store'
16+
});
1417
res.end(JSON.stringify(body, null, 2));
1518
}
1619

package-lock.json

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

tests/x402-paid-action-receipt.test.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function postJson(port, path, body) {
2020
raw += chunk;
2121
});
2222
res.on('end', () => {
23-
resolve({ statusCode: res.statusCode, body: JSON.parse(raw) });
23+
resolve({ statusCode: res.statusCode, headers: res.headers, body: JSON.parse(raw) });
2424
});
2525
}
2626
);
@@ -55,6 +55,7 @@ test('paid-action emits receipt and enforces idempotency', async () => {
5555

5656
const first = await postJson(port, '/paid-action', payload);
5757
assert.equal(first.statusCode, 200);
58+
assert.equal(first.headers['cache-control'], 'no-store');
5859
assert.equal(first.body.duplicate, false);
5960
assert.equal(first.body.receipt.request_id, 'req_test_1');
6061
assert.equal(first.body.receipt.payment_id, 'pay_test_1');
@@ -63,8 +64,29 @@ test('paid-action emits receipt and enforces idempotency', async () => {
6364

6465
const second = await postJson(port, '/paid-action', payload);
6566
assert.equal(second.statusCode, 200);
67+
assert.equal(second.headers['cache-control'], 'no-store');
6668
assert.equal(second.body.duplicate, true);
6769
assert.equal(second.body.receipt.receipt_id, first.body.receipt.receipt_id);
6870

6971
await new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
7072
});
73+
74+
test('paid-action payment errors are not cacheable', async () => {
75+
const server = createServer();
76+
await new Promise((resolve) => server.listen(0, resolve));
77+
const port = server.address().port;
78+
79+
const res = await postJson(port, '/paid-action', {
80+
paid_action_request: {
81+
request_id: 'req_missing_payment',
82+
action: 'summarize.text',
83+
input: { text: 'Payment is intentionally missing.' },
84+
payment: { required: true, plan: 'pro', max_amount: '0.05', currency: 'USD' }
85+
}
86+
});
87+
88+
assert.equal(res.statusCode, 402);
89+
assert.equal(res.headers['cache-control'], 'no-store');
90+
91+
await new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
92+
});

0 commit comments

Comments
 (0)