"""Explicit purchase only; durable proof prevents an automatic second payment.""" import argparse,asyncio,base64,hashlib,inspect,json,os from pathlib import Path import httpx URL='https://api.neurodynamic.tech/v1/audio/speech' async def main(): p=argparse.ArgumentParser(description=__doc__);p.add_argument('--text-file',required=True);p.add_argument('--voice',default='af_heart');p.add_argument('--output',required=True);p.add_argument('--pay',action='store_true');a=p.parse_args() body={'input':Path(a.text_file).read_text(),'voice':a.voice} if not 1<=len(body['input'])<=1000:raise SystemExit('Text must contain 1 to 1000 characters') output=Path(a.output);proofpath=Path(str(output)+'.payment.json') async with httpx.AsyncClient(timeout=100,follow_redirects=False) as c: r=await c.post(URL);assert r.status_code==402 q=r.json();req=q['accepts'][0] assert len(q['accepts'])==1 and q['resource']['url']==URL and req['scheme']=='exact' and req['network']=='eip155:8453' and req['amount']=='5000' assert req['asset'].lower()=='0x833589fcd6edb6e08f4c7c32d4f71b54bda02913' and req['payTo'].lower()=='0xa369ac3412360206db63507b190e691c0c5b9f3e' print('Quote verified: 0.005 USDC; no account or reference approval.') if not a.pay:return if proofpath.exists(): saved=json.loads(proofpath.read_text());assert saved['body']==body and saved['url']==URL,'Saved payment belongs to a different request' else: from eth_account import Account from x402.client import x402Client from x402.mechanisms.evm.exact.client import ExactEvmScheme from x402.mechanisms.evm.signers import EthAccountSigner from x402.schemas import PaymentRequired keyfile=Path(os.environ['NARRATION_BUYER_KEY_FILE']) if keyfile.stat().st_mode&0o077:raise SystemExit('Restrict key file permissions to its owner') buyer=x402Client().register(req['network'],ExactEvmScheme(EthAccountSigner(Account.from_key(keyfile.read_text().strip())))) payload=buyer.create_payment_payload(PaymentRequired.model_validate(q)) if inspect.isawaitable(payload):payload=await payload saved={'url':URL,'body':body,'header':base64.b64encode(json.dumps(payload.model_dump(mode='json',by_alias=True,exclude_none=True)).encode()).decode()} fd=os.open(proofpath,os.O_CREAT|os.O_EXCL|os.O_WRONLY,0o600) with os.fdopen(fd,'w') as f:json.dump(saved,f);f.flush();os.fsync(f.fileno()) r=await c.post(URL,json=body,headers={'PAYMENT-SIGNATURE':saved['header']}) if r.status_code!=200:raise SystemExit('HTTP '+str(r.status_code)+'. Preserve saved proof; do not create a replacement payment. See /narration for recovery.') assert r.headers.get('content-type','').startswith('audio/mpeg') receipt=json.loads(base64.b64decode(r.headers['payment-response']));assert receipt['success'] and receipt['network']=='eip155:8453' and receipt['transaction'] output.write_bytes(r.content);Path(str(output)+'.receipt.json').write_text(json.dumps(receipt,indent=2)) print(json.dumps({'saved':str(output),'replayed':r.headers.get('x-payment-replayed')=='true','audio_seconds':r.headers.get('x-audio-duration-seconds'),'sha256':hashlib.sha256(r.content).hexdigest()})) if __name__=='__main__':asyncio.run(main())