import { ethers } from 'ethers';
const ERC20_ABI = [
'function transfer(address to, uint256 amount) returns (bool)',
];
async function callWithMPP(
url: string,
body: unknown,
signer: ethers.Wallet,
) {
// Attempt 1: no payment
const r1 = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (r1.status !== 402) return r1;
const wwwAuth = r1.headers.get('www-authenticate')!;
// Parse challenge fields
const field = (name: string) =>
wwwAuth.match(new RegExp(`${name}="([^"]+)"`))?.[1];
const challengeId = field('id')!;
const request = Buffer.from(field('request')!, 'base64url').toString();
const requestObj = JSON.parse(request);
const expires = field('expires')!;
const realm = field('realm');
const intent = field('intent');
const method = field('method');
if (new Date() > new Date(expires)) {
throw new Error('Challenge expired');
}
// Send on-chain payment
const token = new ethers.Contract(requestObj.currency, ERC20_ABI, signer);
const tx = await token.transfer(requestObj.recipient, BigInt(requestObj.amount));
const receipt = await tx.wait(1);
// Build credential
const credential = Buffer.from(JSON.stringify({
txHash: receipt.hash,
protocol: 'mpp',
challengeId, intent, method, realm,
request: field('request'),
expires,
})).toString('base64url');
// Attempt 2: with payment proof
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Payment ${credential}`,
},
body: JSON.stringify(body),
});
}