ci: add CI; fix the Docker build, the live annual price, and Paystack initialize - #33
Merged
Conversation
Nothing ran the Go tests. The only check on a PR was Cloudflare building the UI, so a change could break billing outright and still merge green — every test written for the billing work gated nothing. Three jobs: the main module (build, vet, gofmt, tests, and tests under the race detector), the CLI module, and the UI. Two details that matter: The UI job runs `pnpm run build`, which is `tsc -b && vite build`, not `tsc --noEmit`. ui/tsconfig.json is solution-style — "files": [] with only references — so --noEmit type-checks nothing and exits 0. That is exactly how a type error reached the Cloudflare build and failed there instead of in review. Go tests run with -count=1. A cached pass says nothing about the commit under test. Every step verified locally against this tree before committing.
The Dockerfile ran `go build -o hookdrop ./main.go`, which compiles that single file rather than package main. Adding repair.go therefore broke the production image while `go build ./...` still passed locally — main was not deployable and nothing said so until the deploy itself failed. Builds `.` instead, and adds a CI job that builds the production image and runs the binary. The Go job alone cannot catch this: it compiles the package correctly, so only the image build exposes the difference.
I got this wrong in #32. The annual price was changed to ₦33,500 on the strength of the test-mode plan; the LIVE plan PLN_pqaarazyxu6np2o charges 3360000, so production has been quoting ₦100 less than customers are actually charged — worse than the discrepancy the change meant to fix. ₦33,600 ÷ 12 is exactly ₦2,800, which is why the original figures were what they were. Records both live plan codes and their amounts next to the constants, and notes that the test-mode plans are priced differently, so these must be checked against live rather than whatever the test keys report.
Two things, both found against the live API. transaction/initialize rejects a request with no amount — "Invalid Amount Sent" — even though passing a plan supersedes it. CreateCheckout never sent one, so the Paystack redirect path failed every time. It went unnoticed because the NGN UI uses the inline popup, which supplies its own amount; reaching /billing/checkout with currency=ngn exposes it. The amount now comes from the plan itself, which is the source of truth for what the customer is charged. The failure also reported only "paystack initialization failed", discarding the message that says why. It now includes it. Separately, the verified transaction's authorization.country_code and payer IP are recorded, and a NGN payment made with a card issued outside Paystack's markets is logged for review. NGN pricing is much cheaper than USD and the currency is picked client-side — localStorage is enough — so the card's country is the only server-side evidence of where the payer is. Flagged rather than blocked, deliberately: a Nigerian customer paying with a foreign-issued card is a real case, and refusing them after they have been charged is a worse failure than the mispricing. This makes the exposure measurable before any policy is written against real customers.
Contributor
Deploying hookdrop-frontend with
|
| Latest commit: |
a7937d1
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8175968e.hookdrop-frontend.pages.dev |
| Branch Preview URL: | https://chore-ci-and-currency-guard.hookdrop-frontend.pages.dev |
Contributor
Deploying hookdrop with
|
| Latest commit: |
a7937d1
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://9cbf60b4.hookdrop.pages.dev |
| Branch Preview URL: | https://chore-ci-and-currency-guard.hookdrop.pages.dev |
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.
Opened after investigating the live 500 on USD checkout. The 500 itself was a deployment gap — production had 11.9 days uptime and predated all five merged PRs, so
hookdrop.appwas serving the new frontend against the original untested backend. Production is now deployed frommainplus these commits, and all four checkout paths return 200.Four changes, two of which are mistakes of mine.
The Docker build excluded half of package main
RUN CGO_ENABLED=1 GOOS=linux go build -o hookdrop ./main.goThat compiles one file, not the package. Adding
repair.goin #32 therefore broke the production image whilego build ./...still passed —mainwas not deployable and nothing reported it until the deploy failed. Builds.now.The annual price fix in #32 was wrong
I changed the annual price to ₦33,500 based on the test-mode plan. The live plan
PLN_pqaarazyxu6np2ocharges 3360000, so production was quoting ₦100 less than customers are charged — the opposite of the problem it meant to fix, and worse. Restored to ₦33,600 (₦2,800/mo, which divides exactly, as the original figures did). The live plan codes and amounts are now recorded beside the constants with a note that test-mode prices differ.Paystack initialize never worked
transaction/initializerejects a request with noamount— "Invalid Amount Sent" — even though passing a plan supersedes it. The amount was never sent, so this path failed every time it was reached. Unnoticed because the NGN UI uses the inline popup, which supplies its own amount;/billing/checkout?currency=ngnreaches it. The amount now comes from the plan, which is what the customer is actually charged. The error also reported only"paystack initialization failed", discarding the message explaining why.CI
Nothing ran the Go tests — the only PR check was Cloudflare building the UI, so every test written for the billing work gated nothing. Four jobs: main module (build, vet, gofmt, tests, race), CLI module, UI, and the production Docker image. That last one exists because the Go job cannot catch the Dockerfile bug above: it compiles the package correctly, so only the image build exposes it.
The UI job runs
pnpm run build, nottsc --noEmit—ui/tsconfig.jsonis solution-style, so--noEmitchecks nothing and exits 0.Currency guard — recorded, not blocked
NGN pricing is much cheaper than USD and the currency is chosen client-side (
localStorage.hookdrop_currencyis enough). The verified transaction'sauthorization.country_codeand payer IP are now recorded, and a NGN payment on a card issued outside Paystack's markets is logged for review.Deliberately not blocked: a Nigerian customer paying with a foreign-issued card is a real case, and refusing them after they have been charged is a worse failure than the mispricing. This makes the exposure measurable before any policy is written against real customers.
Verified in production
DB backed up before and after shutdown;
-repair-paystackrun and reports nothing to do — production has 0 subscriptions.