Add a PHP example - #5
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.
A fourth example: the same payment, on PHP.
php-js/implements exactly the seven routes of theserver contract, with the same status codes, the same OAuth base string and the same
sha1callback checksum as
go-js/.No Composer, no framework, no autoloader — PHP 8.4 with the bundled
curl,opensslandjson.Six files, four of them the integration:
Port 3003, prefix
/hosted-fields-examples-php.php -S 127.0.0.1:3003 router.phpfor a look,PHP-FPM behind nginx in
deploy/for anything else.What adding a language actually cost
scripts/sync-shared.sh: one line, and the six shared frontend files are copied andcommitted like everyone else's;
e2e-tests/src/apps.ts: one registry entry. No spec changes, noplaywright.config.tschanges — projects and servers are both derived from
APPS, and the specs resolve the app fromthe project name;
go-js/,nodejs-express-js/ornextjs/.Three things PHP does differently, and why
router.phpneverreturn falses. The app root is thephp -Sdocument root, so adelegated request would serve
.envor executepaynet.php.index.phpanswers every path andserves
public/from an allowlist — which is also why the nginx block has norootand notry_files. Verified:/.env,/router.php,/paynet.php,/views/checkout.htmland/tests/run.phpall answer404.is a
500with the detail in the log, and it is the same property that lets CI lint and testwith no credentials. Recorded in the root README beside Next's lazy check.
form_params(), not$_POST/$_GET, which rewrite.and a space ina parameter name and keep the last of a repeated one where Go and Node keep the first. The 3DS
return is verified and then forwarded, and those have to be the same value.
Two smaller ones, both in
php-js/CLAUDE.md: the signature encoder israwurlencodeand neverurlencode, and every sort isksort(…, SORT_STRING)because the default reads numeric-lookingkeys as numbers.
Checks
php -lover every file andphp tests/run.php— 13 tests, on the same OAuth base-string andcallback-checksum vectors
go-jsandnodejs-express-jspin, so a drifting example failsrather than agreeing with itself. Added as a CI job.
./scripts/sync-shared.sh && git diff --exit-codeclean.cd e2e-tests && npm test— 40 passed, ten per app: happy path, the SDK's field-stateclasses, pre-tokenize validation, no ticket, a refused Sale, a decline, the 3DS hop out and back
through the signed callback, and the forged-signature
403s. The emulator verifies the OAuthsignature against the generated public key and rejects a Sale whose amount is not the server's,
so the PHP signing and the server-owned amount are checked on the wire and not just rendered.
No key, no
MERCHANT_CONTROLand no realENDPOINT_IDanywhere:.env.exampleand the FPM pooltemplate carry placeholders only.