feat(mpp): Makes mpp pay handle bot-blocked merchants transparently#110
feat(mpp): Makes mpp pay handle bot-blocked merchants transparently#110nvp-stripe wants to merge 3 commits into
mpp pay handle bot-blocked merchants transparently#110Conversation
… bypass) When `mpp pay` receives a 403, it now calls `webBotAuth.getHeaders()`, attaches `Signature` + `Signature-Input` headers, and retries the probe. If the retry returns 402, the SPT flow continues with both header sets carried through to the final payment request. - `runMppPay` and `MppPay` component both gain the `webBotAuth` param - New `bypassing` step label for interactive progress display - 7-test unit suite for `runMppPay` including the full 403→402→SPT flow - CLAUDE.md updated with the 2-step auto-retry behavior Committed-By-Agent: claude
raubrey-stripe
left a comment
There was a problem hiding this comment.
A request could in theory return a 403 for a few reasons not just due to lack of web bot auth. How do we determine it's because of lack of web bot auth? If we can't, what do you think about trying to include the signature/signature-input headers of each MPP request? Seems like little downside to just automatically including them?
Ah yes - good question on the 403 ambiguity. That said, I'd push back a bit on proactively including the headers on every request. Getting WBA headers requires a round-trip to the link api; so if I'm understanding your point, we'd be adding that latency to every mpp pay call even for merchants with no bot protection. The cache helps on repeat calls to the same domain but the first call still pays the cost. If that endpoint is slow or has an issue it would break mpp pay for everyone, not just for bot-blocked flows. The underlying gap I think you're pointing at is what happens when the WBA retry also gets a 403. Right now that probably surfaces a confusing error. I think the better fix is to explicitly distinguish that case: if we retry with WBA headers and I think the better fix is to explicitly distinguish that case: if we retry with WBA headers and still get a 403, fail with a clear message with something like the bot bypass didn't work and this is likely a different kind of 403. That way, agents get the right signal without the happy path paying a tax for merchants that don't need WBA at all. lmk what you think? Happy to make the retry failure path more explicit if that addresses the concern. |
chatting offline - my take is that latency here isn't a concern and we should keep this surface area simple. Automatic is better and no one interacting with mpp pay will notice/care about the milliseconds, they'll appreciate link doing smart thigns on their behalf to ensure the traffic is prepped in the smartest way possible! |
Summary
Makes
mpp payhandle bot-blocked merchants transparently. When the initial probe to a merchant URL returns 403, the CLI now fetches Web Bot Auth headers automatically, retries the probe withSignature+Signature-Inputheaders attached, and continues the SPT payment flow if the retry returns 402.Before this PR: agents hitting a Cloudflare-protected merchant had to manually call
web-bot-auth sign <url>and inject the headers themselves.mpp paywould return a raw 403 and the payment would fail.After this PR:
mpp payhandles the full 403 => bot-bypass => 402 => SPT chain in one command. No manualweb-bot-auth signcall needed.New retry logic (both
runMppPayandMppPaycomponent)Implementation notes
webBotAuth: IWebBotAuthResourcethreaded through 7 callers:createMppCli,MppPay,runMppPay,SptFlow(demo),DemoRunner,createDemoCli,OnboardRunner,createOnboardCli. All wired fromfactory.createWebBotAuthResource()incli.tsx.botAuthHeadersaccumulator: aRecord<string, string>initialized to{}before the 403 branch. If a 403 is encountered, it is populated withSignatureandSignature-Input. It is spread into every subsequent fetch (the bot-bypass retry and the final SPT retry), so the merchant always sees both headers on any credentialed request.New
bypassingstep added to theSteptype for interactive progress display:'retrieving' | 'probing' | 'bypassing' | 'signing' | 'submitting' | 'done'.Both
runMppPayandMppPayupdated in parallel:runMppPayis the function-level API used by tests and JSON mode;MppPayis the Ink component used in interactive mode. The logic is intentionally duplicated (pre-existingdesign, not introduced here) - refactoring into a shared hook is a separate task.
Depends on WebBotAuthResource (
WebBotAuthResourcein SDK, merged as feat(sdk): add WebBotAuthResource for Web Bot Auth header minting #105) andweb-bot-auth signcommand, merged as Addweb-bot-auth sign <url>command #106. This branch is based onmainafter both merged.Motivation
mpp payis the one-command path for agent-initiated payments. Requiring agents to separately callweb-bot-auth signbeforempp paydefeats the purpose of a single-command payment flow, especially in MCP/tool-use contexts where orchestration overhead matters.Making bot bypass automatic in
mpp paymeans:mpp payget Cloudflare bypass for free, no prompt engineering needed.web-bot-auth signremains available as a standalone tool for agents thatneed the headers for non-
mpp payflows (e.g., streaming checkouts, customHTTP clients).
What's coming next
SKILL.mdwith browsing instructions: documentsweb-bot-auth signandmpp pay's bot-bypass behavior so LLMs discover and use them correctly.Testing
Unit tests
packages/cli/src/commands/mpp/__tests__/pay.test.tsThe full-chain test (
403 => 402 => SPT) uses a realmppxMppx.create({...}).createCredential(response)call against a properly formattedWWW-Authenticatefixture: no mocking of the credential library.