Add a Go + React example of the Hosted Fields payment - #15
Merged
Merged
Conversation
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.
An eleventh example: the same Hosted Fields payment with a React + TypeScript single-page
application in front of a Go server.
nextjs/already answers "what does this look like in React". It does not answer "which half ofthis runs where": there the server components, the client components and the edge middleware
share a language, a
src/tree and a framework, and nothing in the file listing says which iswhich. This one is the same page written as a plain SPA in
web/, in front of a server that isgo-js's near enough to diff — so the boundary is a directory line and an HTTP request, and theonly things that cross it are one generated
<script>and twofetchcalls.Port
3010, prefix/hosted-fields-examples-go-react, e2e port4022.What it demonstrates that
nextjs/does notconfig.jsincluded.nextjs/isexempt from two of them, because a React page gets its config as props.
BASE_PATHstays a runtime setting.output.assetPrefixis'./'and the pages reach theserver only through
window.CONFIG.basePath, so oneweb/dist— and one released binary —serves under any prefix.
next buildbakes itsbasePathin, which is why that example'sDockerfile takes a build argument, its compose service repeats the value and its e2e entry
rebuilds. None of that is here.
PUBLIC_*inlining and no settingsmodule imported at build time.
processis not even nameable underweb/src:tsconfig.jsonsets
"types": [], and onlytsconfig.node.jsonaddsnode, forrsbuild.config.ts— theone file in the project that runs in Node.
<script>survives the build, so the Content-Security-Policy needs no nonce andthe page needs no middleware to mint one. The visible cost is that the stored theme is applied
by
applyStoredTheme()at the top of each entry module rather than before first paint.web/distis compiled in with//go:embed, so the artefact is still one static binary — aReact application included. Only
web/dist/.gitkeepis committed, andcleanDistPath.keepstops a build from deleting it, so a fresh clone compiles.
The four React↔SDK rules are the same as
nextjs/'s, because the hazard is React's rather thanthe framework's: constant
className, no children in the containers, oneinit()behind a refguard,
setStyle()on every theme change.One thing worth a second look
Both pages are build output and live in
web/distbeside the bundle, so that directory is servedthrough an allowlist —
styles.cssandstatic/— rather than by a file server: otherwise{prefix}/result.htmlwould serve the 3DS return page without the checksum check thatGET {prefix}/resultperforms.The first version of that check read the path as it arrived, and
{prefix}/static/%2e%2e/result.htmlanswered
200. The mux redirects a literal..and leaves the escaped one alone, and the fileserver resolves it anyway. The name is now
path.Cleaned before it is checked, andasset_test.gocovers that case and three more like it.The React code is a port, not a synced copy
scripts/sync-shared.shgives this example onlystyles.css, as it doesnextjs/. Neither appimports or is synced from the other;
shared/public/checkout.jsis the original both were portedfrom. So wording the payer reads —
ERROR_COPY,STATUS_COPY— now exists three times, and bothCLAUDE.mdfiles say to change all three together.Checks run locally
./scripts/sync-shared.sh && git diff --exit-code— cleanyarn lint(Biome +tscover both projects + Steiger) andyarn buildingo-react/webgofmt -l . && go vet ./... && go test ./... && go build ./...— 4 tests, on the samecross-language OAuth and callback vectors as the other examples, plus the new allowlist one
npm run test:docker:go-react— 10 e2e specs, including the full 3DS round trip, which is alsowhat exercises the shipped
deploy/nginx.conf301, a CSP with nounsafe-inline,403on a forged result query,
404onresult.htmland on every traversal spelling of it, and thesame
web/distserved under a differentBASE_PATHwithout a rebuildweb/distholding just.gitkeep) compiles