Repository navigation
feat(plugin): add the WooCommerce plugin for Nigerian postcodes - #1
Merged
Merged
Conversation
The WooCommerce plugin needs its own repo, because it follows the WordPress rules of the PHP standard and ships as a zip. Composer bundles the PHP SDK, and Strauss moves it under the plugin's own namespace, so another plugin's copy cannot clash with it. The tests run WordPress's own test suite with WooCommerce on SQLite, so they need no Docker and no database server. The plugin file declares support for order tables and the checkout blocks, and deleting the plugin removes its options. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A store needs to know two things about a Nigerian postcode: whether a customer typed it in the right form, and whether NIPOST knows it. The format check runs on the server with the bundled SDK and names the one problem it finds. The lookup uses the store's own live key through the WordPress HTTP API, waits at most three seconds and never retries, so a failure gives a status and an error code in place of a slow checkout. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A store owner needs one place to choose whether the postcode is required, what to do with old 6-digit codes and whether to look codes up. The section sits in the Advanced tab, and WooCommerce's settings API checks the nonce and the capability. The key setting keeps only a live secret key, because a test key cannot reach the production gateway and a publishable key must never sit on a server. The key field never prints the saved key. An empty field keeps it, and a checkbox removes it. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
WooCommerce 9.9 still checks the hidden block field, so a British order fails there. The plugin header now names 10.0 as the floor. The tests load vendor/ as well as vendor-prefixed/, so a plugin file that named an unprefixed bundled class passed every check and failed only on a real site, where the zip has no vendor/. A new check in composer check fails on any such name in the plugin code. The licence check now also needs NOTICE to name each shipped package, and tests run it on small fake lock files. The uninstall test proves that an unrelated option stays. The install script finds a missing sum before it downloads. Comments no longer name files that do not exist. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A reply with a malformed header made the PSR-7 messages throw InvalidArgumentException, which the lookup did not catch, so the order could stop. The transport now reports an unreadable reply as a failed request, and the checker gives the status error for any exception. The transport also leaves the Host header to WordPress, an empty list of statuses reads as unchecked, and the fake gateway answers a path it does not serve with the unknown-path error. Tests cover a timeout, a 5xx status, an unreadable body and a bad header. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A new valid key now has a test that it replaces the saved key. A new key wins over a ticked remove box in the same save, so the person does not lose it. The remove box shows only when a key is saved. The key note is one translatable string, and the input points to its help text. Tests now cover the rendered page, the hook wiring and a save by a person without the capability. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
Both checkouts need the same record of a postcode once the order exists. The plugin keeps the canonical code and the check status under its own meta keys, and copies the code into the order address so that shipping plugins see it. A postcode that needs a person gets an order note with the cause and the fix, and the note and the log show the code redacted, because an order holds personal data. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A failure on the plugin's own side told the store that the gateway gave an unreadable answer, so staff would look at the wrong system. The check now keeps the class of the exception, never its message, because the message can quote the URL that holds the postcode. Each error code has its own note, one lookup gives one log line, and recording the postcodes never throws, so it cannot stop an order. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
WooCommerce hides the postcode field for Nigeria, and its own check knows only the old 6-digit codes. The classic checkout now shows the plugin's field for a Nigerian address only, keeps the native field hidden, and refuses a code with the wrong form with a message that names the problem. WooCommerce saves the field as meta, so the plugin moves it to its own key and checks it after the order. A failure of the plugin itself never stops the order. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
Most new stores use the checkout block, which builds its address forms from registered fields. The plugin registers one address field that a rule hides unless the country is Nigeria, so it never asks a customer abroad for a postcode. The Store API checks every field of the address locale, so the classic field stays optional there, or a required setting would block every order of the block checkout. A failure of the plugin itself never stops an order. Only a wrong format does. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
The classic checkout deleted WooCommerce's copy of the typed postcode before the plugin stored its own, so a failure in between could drop it with no copy left. The plugin now removes WooCommerce's copy only for an address it stored. On any failure, in either checkout, the typed text stays as the native postcode, the order gets the status unchecked, and one warning names the class of the failure. A failed format check logs a warning too. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A classic checkout request can look like a Store API request, and WooCommerce then left the classic field optional, so a store that requires the postcode did not get it. The classic validation now checks the required setting itself, and keeps only one error when WooCommerce asks as well. One helper now holds the policy for a failure of the plugin, which logs the class name and keeps the typed postcode. The address rules and the block validation log a failure and fail open. The ADR for the floors now states the environment of the runs. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A store owner scans the orders list, not each order, to find the postcodes that need a call. The new column shows the shipping postcode, or else the billing one, with the check status in words. WooCommerce has one list for order tables and another for orders stored as posts, so the column joins both, and the tests cover both. A failure in the column leaves the cell empty and logs one warning, so it cannot break the list. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
The PHPUnit tests reach the plugin through WordPress's hooks, but a store meets it through two checkout pages, a settings screen and two orders lists. These tests drive each of them in Chromium against a shop that installs the release zip, with the gateway answered from the spec fixtures. The shop refuses a call to any other host, and a test fails when the browser or the shop makes one. The tests also check that the field passes axe and works by keyboard, that the settings keep, replace and remove the saved key, and that the zip works with the bundled SDK alone. The zip needs its WordPress.org readme, which names the one external service, what the plugin sends to it and when. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
WordPress.org reviews each plugin zip with Plugin Check, so CI runs the same tool on the same zip that a release attaches, and fails on any error. A version tag, or a manual run for an existing tag, builds that zip, attests it and attaches it to a draft release. Nothing publishes the draft or sends the plugin to WordPress.org. CI also runs the browser tests on each pull request. The readme now links NIPOST's privacy policy, and the first change file records the plugin's first version. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
The zip test now compares each file of the zip with the tracked plugin files and the bundled SDK, so a stray backup file in src/ fails it. The zip build copies only tracked files, which keeps such a file out. The licence check exits with code 2 when the npm report is missing, empty, not a JSON object or free of packages, and it counts Composer and npm packages apart. It accepts a package with an either-or licence when one alternative is permissive. The readme says that the plugin never sends an old 6-digit postcode and that NIPOST sees the IP address of the store's server. More browser tests cover the order note, the address postcode, an old postcode in the checkout block, a failed lookup in the classic checkout, a customer outside Nigeria and a block order in the orders list. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
The Plugin Check script ended its command with a fallback that hid every failure, so a crash left an empty report and the job passed. The script now fails on a stop it does not expect, on an empty or unknown report, and on any error row. A release tag must now be a plain version tag, and must equal the plugin version and the stable tag of the readme, or the job stops before it builds. The tag reaches the checks through an environment variable. The draft release step checks that the tag still points to the built commit. The CI Plugin Check job keeps the zip as an artifact again. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
WooCommerce reuses the order of a failed payment, so both checkout hooks fired again for the same order. Each retry sent the postcodes to NIPOST again and added a second note, which spends the rate limit of the key and let a card-testing run multiply the requests. The order now holds the result of a lookup: when every Nigerian address has the same stored postcode and the status is valid or invalid, the plugin sends nothing and only restores the address postcode. It looks up again after an error or an unchecked status. The classic checkout left two empty postcode rows on every order, also on a shop with no Nigerian customer, and a foreign address could keep a Nigerian postcode that a customer typed in the hidden field. The plugin now removes WooCommerce's copy when it is empty, when the address is not in Nigeria, or when it stored the postcode itself. The checkout block copy goes in the first two cases. A retry that moves an address out of Nigeria also clears the stored postcode and the status. Every request read the secret key, which is not autoloaded, so each page paid one query for a key that only an order needs. The block field and the address rules now read the two settings they use. The checker, and with it the key, exists only for an order with a Nigerian postcode. Activation adds the three small options, so a missing option costs no query either. Each lookup also sent the WordPress version and the address of the shop in the User-Agent header. The transport now sends the fixed text gatepost-postcode-for-woocommerce, and the readme says what is sent, when, and how often. Smaller points in the same code. A hook that another plugin fires with something other than an order no longer ends the request with a type error. The classic required error names the billing or shipping address. The log names an anonymous exception class without the path of its file. A kept typed postcode no longer hides an invalid or error status. The plugin file shows an admin notice instead of a fatal error when the bundled SDK is missing, as in GitHub's source archive. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A postcode of the new format names one building, so it is personal data. An erase request through WooCommerce left the full postcode in four meta rows and in the orders list, and an export left it out. The plugin now uses the privacy hooks that WooCommerce already pages through. The export lists the billing and shipping postcode of an order and of a customer's saved address. The erase removes every copy of the postcode and keeps the check status, which is not personal data. The plugin also adds a short text for the privacy policy page that names the lookup, what it sends and to whom. The postcode field of the classic checkout is also saved in the user meta of a customer, and the form of My Account had no format check. The form now refuses a wrong format for a Nigerian address. Uninstall keeps the saved postcodes, as it keeps the order meta, because both belong to the store's own records and the erase tools cover them. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
WordPress runs uninstall.php once, for the site that a person deletes the plugin from. On a network, every other site kept its four options, and one of them is a live secret key. The file now visits each site and deletes the options there. It still leaves the order data alone. The suite runs on one site, so the network test has its own composer script, which composer check runs. WordPress's test library installs a network when WP_MULTISITE is set, and that works on SQLite. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
A tag on a commit with a red CI still built a zip and attached it to a release. Only Plugin Check ran on that zip, and the PHP and browser tests ran on another build of the same commit. The claim that a store installs what CI tested rested on a person who checks by hand. A gate job now reads the check runs of the tagged commit and stops unless the check, e2e and plugin-check jobs and all three test legs succeeded. It counts only runs of GitHub Actions, takes the newest run of each job, and has no permission but read access to contents and checks. The zip job then runs the browser tests on the zip in dist/ without building a second one, and compares the sum of the file before and after, so the release attaches the file that the tests used. The zip job also stops when the tag moved after the gate. Every action keeps its pinned commit, and the new Node step turns off the package cache. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
The orders list showed one postcode with the status of the order. When the billing and shipping postcodes differ, the status can belong to either, and staff asked the customer about the wrong one. The cell now shows both postcodes with their address. When staff correct the postcode of the order after the check, the cell shows the new postcode and says that it changed after the check, because the stored status describes the old one. The cell and its hooks also ignore a call that does not hold an order. The block checkout shows the postcode twice in an order, once in the address and once as a field line. WooCommerce has no setting to hide one additional field from these displays, so a new decision record explains why the plugin accepts the repeat. Other changes. The notice file names the Unicode data inside the bundled SDK and its licence. The README links the contributing and security files in the organisation repo and sends questions to Issues. The multi-sentence texts say that a translation may join the sentences in another way. Every class file starts with the ABSPATH guard. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
The checkout block saves its postcode field in the user meta of a customer with an account. The export left that postcode out and the erase request left it in. Both customer hooks now handle the block keys next to the classic ones. One status for the whole order meant that a retry sent a known postcode again next to a failed one, never sent a failed postcode next to an unknown one, and sent an unchanged postcode again after a country change away and back. Each address now remembers its own answer, valid or invalid, with a keyed hash of its postcode. A retry sends only a postcode with no answer yet or a changed postcode. The hash holds no postcode, it survives the move of an address out of Nigeria, and an erase request removes it. The order status stays as the summary that the orders list reads. A retry that needs no lookup no longer reads the secret key. The release gate names its jobs and its number of test legs. A test now compares both with ci.yml, so a new job or leg fails the checks of the branch and does not pass unseen at release time. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
Follow the organisation's README look: a centred header block, the notice, a link row and a screenshot of the checkout with a synthetic postcode. Say plainly that no release exists and that the plugin is not on WordPress.org. Add a section that repeats what the plugin sends to NIPOST, in line with readme.txt. The image sits in assets/, which the zip script does not copy, and REUSE.toml covers it. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
The WordPress and WooCommerce floors are merged in the spec repo, so the submodule now points at that commit on its main branch. The glossary is copied from the spec again, as the check asks. Signed-off-by: MobileMage <aidelojejoshua@gmail.com>
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.
This adds the WooCommerce plugin for Nigeria's digital postcodes. It
puts a postcode field in the classic checkout and in the checkout
block for Nigerian addresses, checks the format on the server, stores
the postcode with the order, and shows it in the orders list.
With a secret key in the settings, the plugin looks each postcode up
once, after the order. A failed lookup never stops an order: the order
keeps the postcode that the customer typed, gets a status and a note
that names the true cause, and the log gets one line with the postcode
redacted. The settings page never prints the stored key. The plugin
answers export and erase requests for the postcodes it keeps.
The plugin bundles the PHP SDK under its own namespace, so another
plugin's copy cannot clash, and a check fails on any unprefixed name.
The floors are WordPress 6.7 and WooCommerce 10.0, because the checkout
block first skips a hidden address field on the server in 10.0.
Tests run on the floor versions and the newest ones, on SQLite and
MySQL. Browser tests run both checkouts on the zip that a store
installs, and Plugin Check runs on the same zip. The release workflow
makes a draft release only, for a tag that matches the version, after
CI passed for that commit. No test calls NIPOST.
This change is larger than the 400 lines that GIT-5 suggests, because
it starts the repo.
Signed-off-by: MobileMage aidelojejoshua@gmail.com