porkbun: use Porkbun's own pre-flight, and keep structured refusals - #32
Merged
Conversation
Three things learned from trying to register a real domain with this. The refusal came back as one sentence — "Your account phone number and email address must be verified." — and that reads exactly like a transient, so it got retried six times instead of acted on. Porkbun actually sends a `code`, a `next_action.hint`, a `url` and a `retryable` verdict alongside it; `unwrap` was throwing all of that away. It now keeps them, and the same failure prints the fix, where to do it, and "retrying will not help". `/domain/create` takes `dryRun: true`, which runs every pre-flight — availability, price match, eligibility, funds, spend cap, account verification — while charging and creating nothing, and without consuming the create rate-limit budget. That is strictly better than checking locally, because funds, the monthly API spend cap and verification status are account-level gates that no read endpoint reports at all. `register` now runs it before asking, and `--dry-run` stops there. The real create carries an `Idempotency-Key`. A create that times out has very likely registered the domain anyway, and the obvious response — run it again — buys and bills a second year; keyed, a retry within 24h replays the first result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLFd9jDQ4D5UpHCWFJi8YM
ThreatCrush Security Scan16 finding(s) HIGH/CRITICAL: 5 | MEDIUM: 2 | LOW: 9
Snippets are redacted; ThreatCrush never prints matched credential material. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #29. Three things learned from actually trying to register a domain with it.
The refusal was hiding its own answer
Porkbun refused the purchase with one sentence — "Your account phone number and email address must be verified." That reads like a transient, so it got retried six times instead of acted on.
It isn't a transient. Porkbun sends a
code, anext_action.hint, aurland aretryableverdict beside the prose, andunwrapwas throwing all of it away:{ "code": "VERIFICATION_REQUIRED", "next_action": { "hint": "Verify your account email and phone number, then retry.", "retryable": false, "url": "https://porkbun.com/account" } }unwrapnow keeps them, andPorkbunErrorcarriescodeandretryable. The same failure prints:--dry-runwas mine when Porkbun has a real one/domain/createacceptsdryRun: true, which runs every pre-flight — availability, price match, eligibility, funds, spend cap, account verification — charging and creating nothing, and without consuming the create rate-limit budget.This is strictly better than checking locally, and not by a little: funds, the monthly API spend cap and verification status are account-level gates that no read endpoint reports at all. My local
--dry-runpassed happily on a purchase that could never have succeeded.registernow runs the real pre-flight before prompting, reports the balance, and--dry-runstops there.The purchase wasn't idempotent
A create that times out has very likely registered the domain anyway, and the obvious response — run it again — buys and bills a second year. The create now carries an
Idempotency-Key, so a retry within 24h replays the first result. Reads don't send one.Testing
Unrelated, but worth knowing
pnpm typecheckis already red on master — twoexactOptionalPropertyTypeserrors insrc/free-names.ts:80and:89, from #30/#31. That file is byte-identical here, so it is not from this branch and I have left it alone. Happy to fix it separately.🤖 Generated with Claude Code
https://claude.ai/code/session_01QLFd9jDQ4D5UpHCWFJi8YM