summaryrefslogtreecommitdiffstats
path: root/README.md
blob: 2233555087438e6caab8a12e90d2562b91bc77ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# CEP Autofill for WooCommerce

Fills the WooCommerce checkout address from the CEP the shopper types. Street,
neighbourhood, city and state, from official Correios data (DNE) via the
[public API at cep.is](https://cep.is).

Works on **both** WooCommerce checkouts — the Cart/Checkout Blocks and the
classic `[woocommerce_checkout]` shortcode — see
[Supported checkouts](#supported-checkouts).

No Composer, no npm, no build step: the PHP client and the JavaScript widget
are bundled in the plugin.

## Install

Download the `.zip` from `dist/` (or run `make build`), then **Plugins → Add
New → Upload Plugin** in WordPress. Activate it; WooCommerce must be active
first.

By default the shopper's browser talks to cep.is directly, so there is nothing
to configure and no key to obtain.

Requires WordPress 6.9 or newer, PHP 8.1 or newer, and WooCommerce.

## Settings

**WooCommerce → Settings → CEP autofill.**

| setting                    | default  | what it does                                                                        |
| -------------------------- | -------- | ----------------------------------------------------------------------------------- |
| **Autofill**               | on       | look the address up while the shopper types                                          |
| **Lookups go through**     | browser  | the browser straight to cep.is, or this site's REST API                              |
| **Fields already filled**  | keep     | keep what the shopper typed / only fill empty fields / always overwrite              |
| **House number**           | on       | move the cursor to the house-number field after a hit (classic checkout)             |
| **API address**            | cep.is   | point at your own instance of the API                                                |

### Lookups go through

**The browser (default).** One cross-origin GET against a Cloudflare edge
cache. No PHP runs, nothing is billed to the store, and the answer arrives in
about twenty milliseconds.

**This site.** The browser calls
`/wp-json/cepis/v1/lookup/{token}/{cep}` instead, and PHP calls cep.is through
`wp_remote_get()`. Choose this when a Content-Security-Policy on the checkout
forbids external requests, or when you want the answers cached server-side.
Answers are kept in transients: a hit for a week, since the database behind the
API is regenerated fortnightly and a week is half a refresh cycle; a "does not
exist" for a day, since a CEP absent today can appear in the next release.

The route carries a short-lived token derived from the site's salts, and
answers `403` without one. It is registered only while this mode is on.

### Fields already filled in

`keep` — the default — fills empty fields, replaces values this plugin wrote
itself, and leaves anything the shopper typed alone, so correcting a street by
hand survives the next CEP.

The state is the exception and is always replaced: the CEP decides it, and
WooCommerce pre-selects the store's own state on a fresh checkout.

## Supported checkouts

Both, and both verified in a real browser against a real store.

| checkout                                       | supported | how                                                                         |
| ---------------------------------------------- | --------- | --------------------------------------------------------------------------- |
| **Cart/Checkout Blocks** (the default since WooCommerce 8.3) | yes | reads and writes the `wc/store/cart` data store; no DOM is touched   |
| **Classic** `[woocommerce_checkout]`           | yes       | the widget fills the fields directly and announces progress in an `aria-live` region |

On the block checkout the fields are React-controlled, so the plugin drives the
official `setShippingAddress` / `setBillingAddress` actions and reports failures
through the checkout's own notice area. On the classic checkout the status
message appears under the CEP field, styleable through
`.cepis-autofill-status[data-cepis-state="…"]`.

Both fill billing and shipping. The cart block's shipping calculator is not
covered.

## When the CEP does not exist

A CEP that fails at the checkout is rarely a random number. It is usually a
shopper who knows perfectly well which town they live in and got a digit wrong
— most often by trying the `-000`, which 957 municipalities never received.
Areal/RJ, for one, answers from 25845-001 upwards and never at 25845-000.

When the API says which municipality the number would have belonged to, so does
the message:

> CEP not found. This number would be in Areal/RJ, which has CEPs from
> 25845-001 to 25849-899. Check the number.

It is still an **error**. Nothing from it is ever written into a checkout field
— not the city, not the state, nothing at all. A CEP that does not exist has no
address, and filling one in from the municipality behind it means a parcel
delivered to the wrong town.

The two CEPs quoted are the lowest and the highest that really exist there, not
a list of valid ones and not a suggestion. The range is sparse — 118 real CEPs
across nearly five thousand numbers, in Areal's case — so a number picked out of
the middle of it would most likely not exist either.

When the API names no municipality, because the CEP falls in no range at all,
the message is the one it has always been.

Reading it costs one extra request, on the miss path only and once per CEP.
Both checkouts do it and both lookup modes support it: with **This site**
selected the municipality is passed through with the 404 and cached with it.

Verified on WordPress 7.0.2, WooCommerce 10.9.4, PHP 8.3. `Requires at least`
is declared as 6.9 because that is what WooCommerce 10.x itself requires; older
WordPress was not tested.

## Brazilian address fields

WooCommerce has no house-number and no neighbourhood field. When
[Brazilian Market on WooCommerce](https://wordpress.org/plugins/woocommerce-extra-checkout-fields-for-brazil/)
adds `billing_number` and `billing_neighborhood` to the classic checkout, this
plugin fills the neighbourhood and moves the cursor to the number. When they
are absent it fills what exists and does nothing else — the neighbourhood is
**not** written into `address_2`, which in WooCommerce means "apartment, suite,
unit" and would produce a wrong shipping label.

The block checkout has no such fields and no plugin currently adds them. A
store that registers its own with
`woocommerce_register_additional_checkout_field()` can map it:

```php
add_filter( 'cepis_woocommerce_blocks_fields', function ( $fields ) {
	$fields['bairro'] = 'my-plugin/neighborhood';

	return $fields;
} );
```

The classic equivalent is `cepis_woocommerce_classic_fields`, which takes CSS
selectors per address section, for themes and one-page-checkout plugins that
rename the fields. `cepis_woocommerce_messages` overrides what the shopper is
told.

## Development

```sh
make            # list the targets
make test       # stand a store up in Docker and drive the checkout in Chromium
make lint       # php -l, prettier --check, and phpcs when installed
make format     # apply prettier (writes)
make build      # produce dist/cepis-woocommerce-0.2.0.zip
make check      # lint, then test, then build
make clean      # remove build output and throw the test containers away
```

`make test` needs Docker and `bun`. It brings up WordPress, MySQL and WP-CLI
from `test/compose.yml`, installs and configures WooCommerce, and drives both
checkouts with Playwright's Chromium against a fake cep.is on the host, so no
test touches the network. Install Chromium with `bunx playwright install
chromium`, or point `CEPIS_CHROMIUM` at an existing binary.

`make phpcs` fetches PHP_CodeSniffer and the WordPress standard into `.phpcs/`;
`make lint` picks them up automatically once they are there.

### Refreshing the bundled dependencies

`lib/cepis/` is the [PHP client](https://cep.is) and
`assets/js/cepis-autofill.min.js` is the
[autofill widget](https://cep.is), both copied in rather than installed,
because a WordPress site is deployed by copying files and never runs
`composer install`.

```sh
tools/update-vendor.sh ../cepis-php ../cepis-autofill
```

That rebuilds the widget, copies both in, and leaves you a diff to review.

## Licence

ISC. See [LICENSE](LICENSE).