@spree/docs 0.1.182 → 0.1.183
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/developer/core-concepts/addresses.md +106 -198
- package/dist/developer/core-concepts/architecture.md +97 -126
- package/dist/developer/core-concepts/calculators.md +75 -252
- package/dist/developer/core-concepts/carts.md +1 -1
- package/dist/developer/core-concepts/channels.md +0 -4
- package/dist/developer/core-concepts/companies-and-catalogs.md +1 -1
- package/dist/developer/core-concepts/customers.md +0 -3
- package/dist/developer/core-concepts/discounts.md +133 -0
- package/dist/developer/core-concepts/events.md +83 -576
- package/dist/developer/core-concepts/fees.md +144 -0
- package/dist/developer/core-concepts/imports-exports.md +105 -679
- package/dist/developer/core-concepts/inventory.md +114 -248
- package/dist/developer/core-concepts/markets.md +9 -12
- package/dist/developer/core-concepts/media.md +9 -11
- package/dist/developer/core-concepts/metafields.md +123 -200
- package/dist/developer/core-concepts/order-totals.md +110 -0
- package/dist/developer/core-concepts/orders.md +1 -1
- package/dist/developer/core-concepts/payments.md +11 -14
- package/dist/developer/core-concepts/pricing.md +11 -13
- package/dist/developer/core-concepts/products.md +35 -19
- package/dist/developer/core-concepts/promotions.md +12 -11
- package/dist/developer/core-concepts/search-filtering.md +2 -4
- package/dist/developer/core-concepts/store-credits-gift-cards.md +0 -3
- package/dist/developer/core-concepts/taxes.md +125 -113
- package/dist/developer/core-concepts/translations.md +61 -68
- package/dist/developer/core-concepts/webhooks.md +25 -59
- package/dist/developer/how-to/custom-promotion.md +3 -3
- package/package.json +1 -1
- package/dist/developer/core-concepts/taxes-discounts-fees.md +0 -199
|
@@ -1,281 +1,189 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Addresses
|
|
3
|
-
description: How Spree
|
|
3
|
+
description: How Spree handles addresses, country and state data, and building address forms that adapt to the country a customer picks.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Overview
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
An address is where something goes or who gets billed. Every cart collects a shipping and a billing address, and signed-in customers can keep an address book.
|
|
9
|
+
|
|
10
|
+
Addresses matter beyond the label on a parcel: the shipping address decides which delivery options a customer sees and how much tax they pay.
|
|
11
|
+
|
|
12
|
+
```mermaid
|
|
13
|
+
erDiagram
|
|
14
|
+
Customer ||--o{ Address : "address book"
|
|
15
|
+
Cart }o--|| Address : "shipping"
|
|
16
|
+
Cart }o--|| Address : "billing"
|
|
17
|
+
Order }o--|| Address : "shipping (copied)"
|
|
18
|
+
Order }o--|| Address : "billing (copied)"
|
|
19
|
+
|
|
20
|
+
Address {
|
|
21
|
+
string first_name
|
|
22
|
+
string last_name
|
|
23
|
+
string address1
|
|
24
|
+
string city
|
|
25
|
+
string postal_code
|
|
26
|
+
string country_code
|
|
27
|
+
string state_code
|
|
28
|
+
}
|
|
14
29
|
```
|
|
15
30
|
|
|
16
|
-
|
|
17
|
-
- **Countries** and **States** provide the geographic data for addresses
|
|
18
|
-
- **Zones** group countries or states together to define where tax rates and shipping methods apply
|
|
19
|
-
- **Addresses** tie a customer to a specific location, determining which zones — and therefore which taxes and shipping options — apply to their order
|
|
20
|
-
|
|
21
|
-
## Addresses
|
|
22
|
-
|
|
23
|
-
An address represents a shipping or billing location. Every order has a shipping address and a billing address, and customers can save multiple addresses in their address book.
|
|
31
|
+
## Address fields
|
|
24
32
|
|
|
25
33
|
| Field | Description |
|
|
26
|
-
|
|
34
|
+
|---|---|
|
|
27
35
|
| `first_name`, `last_name` | Contact name |
|
|
28
36
|
| `address1`, `address2` | Street address |
|
|
29
37
|
| `city` | City |
|
|
30
|
-
| `postal_code` | Postal code
|
|
38
|
+
| `postal_code` | Postal code — not used by every country |
|
|
31
39
|
| `phone` | Phone number |
|
|
32
|
-
| `company` | Company name
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
40
|
+
| `company` | Company name, optional |
|
|
41
|
+
| `country_code` | Country, as an ISO code such as `US` |
|
|
42
|
+
| `state_code` | State or province code such as `CA`, where the country has them |
|
|
43
|
+
| `state_name` | Free text, for countries with no official list |
|
|
44
|
+
| `label` | What the customer filed it under — "Home", "Office" |
|
|
45
|
+
| `is_default_shipping`, `is_default_billing` | Whether it is the default in the address book |
|
|
46
|
+
|
|
47
|
+
> **NOTE:** Countries and states are named by their **ISO codes** — `country_code: "US"`, `state_code: "CA"` — never by an ID. A code means the same thing everywhere, so you can send one without looking anything up first.
|
|
48
|
+
>
|
|
49
|
+
> The older names `country_iso` and `state_abbr` still work but are going away. Use `country_code` and `state_code` in new code.
|
|
35
50
|
|
|
36
|
-
|
|
51
|
+
## Address forms that adapt
|
|
37
52
|
|
|
38
|
-
|
|
53
|
+
Address formats are not universal. The United States wants a state and a ZIP code; Hong Kong wants neither. A form that demands both everywhere will block real customers from checking out.
|
|
39
54
|
|
|
40
|
-
|
|
55
|
+
Every country tells you what it needs:
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
```typescript Store SDK
|
|
59
|
+
const { data: countries } = await client.countries.list()
|
|
60
|
+
|
|
61
|
+
// One country, with its states for a picker
|
|
62
|
+
const usa = await client.countries.get('US', { expand: ['states'] })
|
|
63
|
+
|
|
64
|
+
usa.states_required // true → show a state picker
|
|
65
|
+
usa.zipcode_required // true → require a postal code
|
|
66
|
+
usa.states // [{ abbr: "CA", name: "California" }, ...]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```bash cURL
|
|
70
|
+
curl 'https://api.mystore.com/api/v3/store/countries' \
|
|
71
|
+
-H 'X-Spree-API-Key: pk_xxx'
|
|
72
|
+
|
|
73
|
+
curl 'https://api.mystore.com/api/v3/store/countries/US?expand=states' \
|
|
74
|
+
-H 'X-Spree-API-Key: pk_xxx'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
Drive the form from those two flags rather than hardcoding a list of countries. When `states_required` is `false`, let the customer type a `state_name` instead of picking a `state_code`.
|
|
79
|
+
|
|
80
|
+
Only countries you actually sell to are listed — that's decided by your [Markets](markets.md).
|
|
81
|
+
|
|
82
|
+
## The customer address book
|
|
83
|
+
|
|
84
|
+
A signed-in customer can save addresses and mark defaults, so checkout is one tap next time.
|
|
41
85
|
|
|
42
86
|
|
|
43
87
|
```typescript Store SDK
|
|
44
|
-
// List addresses
|
|
45
88
|
const { data: addresses } = await client.customer.addresses.list()
|
|
46
89
|
|
|
47
|
-
// Create an address
|
|
48
90
|
const address = await client.customer.addresses.create({
|
|
91
|
+
label: 'Home',
|
|
49
92
|
first_name: 'John',
|
|
50
93
|
last_name: 'Doe',
|
|
51
94
|
address1: '123 Main St',
|
|
52
95
|
city: 'Los Angeles',
|
|
53
|
-
|
|
54
|
-
|
|
96
|
+
country_code: 'US',
|
|
97
|
+
state_code: 'CA',
|
|
55
98
|
postal_code: '90001',
|
|
56
99
|
phone: '555-0100',
|
|
100
|
+
is_default_shipping: true,
|
|
57
101
|
})
|
|
58
102
|
|
|
59
|
-
|
|
60
|
-
await client.customer.addresses.
|
|
61
|
-
|
|
62
|
-
// Delete an address
|
|
63
|
-
await client.customer.addresses.delete('addr_xxx')
|
|
64
|
-
|
|
65
|
-
// Set as default shipping or billing address — pass the boolean flags to create or update
|
|
66
|
-
await client.customer.addresses.update('addr_xxx', { is_default_shipping: true })
|
|
67
|
-
await client.customer.addresses.update('addr_xxx', { is_default_billing: true })
|
|
103
|
+
await client.customer.addresses.update(address.id, { city: 'Brooklyn' })
|
|
104
|
+
await client.customer.addresses.delete(address.id)
|
|
68
105
|
```
|
|
69
106
|
|
|
70
107
|
```bash cURL
|
|
71
|
-
# List addresses
|
|
72
|
-
curl 'https://api.mystore.com/api/v3/store/customers/me/addresses' \
|
|
73
|
-
-H 'X-Spree-API-Key: pk_xxx' \
|
|
74
|
-
-H 'Authorization: Bearer <jwt_token>'
|
|
75
|
-
|
|
76
|
-
# Create an address
|
|
77
108
|
curl -X POST 'https://api.mystore.com/api/v3/store/customers/me/addresses' \
|
|
78
109
|
-H 'X-Spree-API-Key: pk_xxx' \
|
|
79
|
-
-H 'Authorization: Bearer <
|
|
110
|
+
-H 'Authorization: Bearer <customer_token>' \
|
|
80
111
|
-H 'Content-Type: application/json' \
|
|
81
112
|
-d '{
|
|
113
|
+
"label": "Home",
|
|
82
114
|
"first_name": "John",
|
|
83
115
|
"last_name": "Doe",
|
|
84
116
|
"address1": "123 Main St",
|
|
85
117
|
"city": "Los Angeles",
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"postal_code": "90001"
|
|
89
|
-
"phone": "555-0100"
|
|
118
|
+
"country_code": "US",
|
|
119
|
+
"state_code": "CA",
|
|
120
|
+
"postal_code": "90001"
|
|
90
121
|
}'
|
|
91
|
-
|
|
92
|
-
# Update an address
|
|
93
|
-
curl -X PATCH 'https://api.mystore.com/api/v3/store/customers/me/addresses/addr_xxx' \
|
|
94
|
-
-H 'X-Spree-API-Key: pk_xxx' \
|
|
95
|
-
-H 'Authorization: Bearer <jwt_token>' \
|
|
96
|
-
-H 'Content-Type: application/json' \
|
|
97
|
-
-d '{ "city": "Brooklyn" }'
|
|
98
|
-
|
|
99
|
-
# Delete an address
|
|
100
|
-
curl -X DELETE 'https://api.mystore.com/api/v3/store/customers/me/addresses/addr_xxx' \
|
|
101
|
-
-H 'X-Spree-API-Key: pk_xxx' \
|
|
102
|
-
-H 'Authorization: Bearer <jwt_token>'
|
|
103
122
|
```
|
|
104
123
|
|
|
105
124
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
During checkout, you can either reference a saved address by ID or pass a new address inline. See the [cart and checkout SDK guide](../sdk/store/cart-checkout.md) for the full flow of passing addresses during checkout:
|
|
125
|
+
## Addresses at checkout
|
|
109
126
|
|
|
127
|
+
On a cart you can point at a saved address or send a new one inline:
|
|
110
128
|
|
|
111
129
|
```typescript Store SDK
|
|
112
|
-
//
|
|
130
|
+
// Reuse something from the address book
|
|
113
131
|
await client.carts.update(cartId, {
|
|
114
132
|
shipping_address_id: 'addr_xxx',
|
|
115
133
|
billing_address_id: 'addr_yyy',
|
|
116
134
|
})
|
|
117
135
|
|
|
118
|
-
// Or
|
|
136
|
+
// Or send one directly — nothing needs saving first
|
|
119
137
|
await client.carts.update(cartId, {
|
|
138
|
+
email: 'john@example.com',
|
|
120
139
|
shipping_address: {
|
|
121
140
|
first_name: 'John',
|
|
122
141
|
last_name: 'Doe',
|
|
123
142
|
address1: '123 Main St',
|
|
124
143
|
city: 'Los Angeles',
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
country_code: 'US',
|
|
145
|
+
state_code: 'CA',
|
|
127
146
|
postal_code: '90001',
|
|
128
|
-
phone: '555-0100',
|
|
129
147
|
},
|
|
130
148
|
})
|
|
131
149
|
```
|
|
132
150
|
|
|
133
|
-
|
|
134
|
-
# Use a saved address
|
|
135
|
-
curl -X PATCH 'https://api.mystore.com/api/v3/store/carts/cart_xxx' \
|
|
136
|
-
-H 'X-Spree-API-Key: pk_xxx' \
|
|
137
|
-
-H 'X-Spree-Token: order_token' \
|
|
138
|
-
-H 'Content-Type: application/json' \
|
|
139
|
-
-d '{ "shipping_address_id": "addr_xxx", "billing_address_id": "addr_yyy" }'
|
|
140
|
-
|
|
141
|
-
# Or pass a new address inline
|
|
142
|
-
curl -X PATCH 'https://api.mystore.com/api/v3/store/carts/cart_xxx' \
|
|
143
|
-
-H 'X-Spree-API-Key: pk_xxx' \
|
|
144
|
-
-H 'X-Spree-Token: order_token' \
|
|
145
|
-
-H 'Content-Type: application/json' \
|
|
146
|
-
-d '{
|
|
147
|
-
"shipping_address": {
|
|
148
|
-
"first_name": "John",
|
|
149
|
-
"last_name": "Doe",
|
|
150
|
-
"address1": "123 Main St",
|
|
151
|
-
"city": "Los Angeles",
|
|
152
|
-
"country_iso": "US",
|
|
153
|
-
"state_abbr": "CA",
|
|
154
|
-
"postal_code": "90001",
|
|
155
|
-
"phone": "555-0100"
|
|
156
|
-
}
|
|
157
|
-
}'
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
## Countries
|
|
162
|
-
|
|
163
|
-
Countries are the foundation of Spree's geographic system. They connect to [Markets](markets.md), contain states, and belong to zones.
|
|
164
|
-
|
|
165
|
-
Each country includes metadata that drives address form behavior:
|
|
151
|
+
Setting the shipping address is what makes delivery options and tax appear, so do it before asking the customer to choose delivery. The updated cart comes back with new totals — you don't need a second request.
|
|
166
152
|
|
|
167
|
-
|
|
168
|
-
|-------|-------------|---------|
|
|
169
|
-
| `iso` | ISO 3166-1 alpha-2 code | `US` |
|
|
170
|
-
| `iso3` | ISO 3166-1 alpha-3 code | `USA` |
|
|
171
|
-
| `name` | Country name | `United States` |
|
|
172
|
-
| `states_required` | Whether the address form should show a state/province picker | `true` |
|
|
173
|
-
| `zipcode_required` | Whether the address form should require a postal code | `true` |
|
|
153
|
+
> **WARNING:** An address is **copied onto the order** when the cart completes, not linked to. Editing a saved address next year does not rewrite last year's invoice — which is exactly what you want when a customer moves house.
|
|
174
154
|
|
|
175
|
-
|
|
155
|
+
## Validating addresses
|
|
176
156
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
```typescript Store SDK
|
|
181
|
-
// List all countries available in this store
|
|
182
|
-
const { data: countries } = await client.countries.list()
|
|
183
|
-
|
|
184
|
-
// Get a country with its states (for address form dropdowns)
|
|
185
|
-
const usa = await client.countries.get('US', { expand: ['states'] })
|
|
186
|
-
// usa.states => [{ name: "Alabama", abbr: "AL" }, { name: "Alaska", abbr: "AK" }, ...]
|
|
157
|
+
Spree checks that required fields are present and that the state and postal code make sense for the country. Anything it rejects comes back as a `422` naming the field, so you can put the message next to the input.
|
|
187
158
|
|
|
188
|
-
|
|
189
|
-
const germany = await client.countries.get('DE', { expand: ['market'] })
|
|
190
|
-
// germany.market => { currency: "EUR", default_locale: "de", tax_inclusive: true }
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
```typescript Admin SDK
|
|
194
|
-
const { data: countries } = await adminClient.countries.list()
|
|
195
|
-
|
|
196
|
-
const usa = await adminClient.countries.get('US')
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
```bash cURL
|
|
200
|
-
# List all countries
|
|
201
|
-
curl 'https://api.mystore.com/api/v3/store/countries' \
|
|
202
|
-
-H 'X-Spree-API-Key: pk_xxx'
|
|
203
|
-
|
|
204
|
-
# Get a country with states
|
|
205
|
-
curl 'https://api.mystore.com/api/v3/store/countries/US?expand=states' \
|
|
206
|
-
-H 'X-Spree-API-Key: pk_xxx'
|
|
207
|
-
|
|
208
|
-
# Get a country with its market
|
|
209
|
-
curl 'https://api.mystore.com/api/v3/store/countries/DE?expand=market' \
|
|
210
|
-
-H 'X-Spree-API-Key: pk_xxx'
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
Use the `states_required` and `zipcode_required` fields to build adaptive address forms — show a state picker only when needed, and skip the zipcode field for countries that don't use them.
|
|
215
|
-
|
|
216
|
-
## States
|
|
217
|
-
|
|
218
|
-
States (provinces, regions) belong to a country and are used for address validation and zone matching. Countries like the US, Canada, Australia, and India have predefined states — for these countries, customers must select a state from the list rather than typing a name.
|
|
219
|
-
|
|
220
|
-
States are fetched via the country endpoint using [`?expand=states`](../../api-reference/store-api/relations.md):
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
```typescript Store SDK
|
|
224
|
-
const usa = await client.countries.get('US', { expand: ['states'] })
|
|
225
|
-
|
|
226
|
-
// Build a state picker from the response
|
|
227
|
-
usa.states.forEach(state => {
|
|
228
|
-
console.log(state.abbr, state.name) // "AL", "Alabama"
|
|
229
|
-
})
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
```typescript Admin SDK
|
|
233
|
-
const usa = await adminClient.countries.get('US', { expand: ['states'] })
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
```bash cURL
|
|
237
|
-
curl 'https://api.mystore.com/api/v3/store/countries/US?expand=states' \
|
|
238
|
-
-H 'X-Spree-API-Key: pk_xxx'
|
|
239
|
-
```
|
|
159
|
+
For real-world verification — that a street exists, that a unit number is missing — connect an address validation service. See [Providers](../providers/overview.md).
|
|
240
160
|
|
|
161
|
+
## How geography flows through a purchase
|
|
241
162
|
|
|
242
|
-
|
|
163
|
+
**Step 1: A customer arrives**
|
|
243
164
|
|
|
244
|
-
|
|
165
|
+
Their country resolves to a [Market](markets.md), which sets the currency and locale, and whether prices are shown with tax included.
|
|
245
166
|
|
|
246
|
-
|
|
167
|
+
**Step 2: They fill in an address**
|
|
247
168
|
|
|
248
|
-
|
|
249
|
-
- **EU VAT** (country zone) — Germany, France, Italy, Spain, ... → applies EU VAT rates
|
|
250
|
-
- **California** (state zone) — CA → applies California sales tax
|
|
251
|
-
- **Domestic Shipping** (country zone) — US, CA → enables domestic shipping methods
|
|
169
|
+
The form adapts to the country using `states_required` and `zipcode_required`.
|
|
252
170
|
|
|
253
|
-
|
|
254
|
-
1. Which **tax rates** apply (see [Taxes](taxes.md))
|
|
255
|
-
2. Which **shipping methods** are available (see [Shipments](fulfillments.md))
|
|
171
|
+
**Step 3: Delivery options appear**
|
|
256
172
|
|
|
257
|
-
|
|
173
|
+
The shipping address decides which [delivery methods](fulfillments.md) can reach them, and what each costs.
|
|
258
174
|
|
|
259
|
-
|
|
175
|
+
**Step 4: Tax is worked out**
|
|
260
176
|
|
|
261
|
-
|
|
177
|
+
The same address decides the [tax](taxes.md) charged.
|
|
262
178
|
|
|
263
|
-
|
|
179
|
+
**Step 5: The order is placed**
|
|
264
180
|
|
|
265
|
-
|
|
181
|
+
Both addresses are copied onto the order as a permanent record.
|
|
266
182
|
|
|
267
|
-
1. **Customer visits the store** — their country is detected (from URL, geolocation, or selection)
|
|
268
|
-
2. **Market resolved** — the country maps to a market, which sets the currency and locale
|
|
269
|
-
3. **Products displayed** — prices use the market's currency and tax zone for correct formatting
|
|
270
|
-
4. **Customer enters address** — the address form adapts based on `states_required` and `zipcode_required`
|
|
271
|
-
5. **Zones matched** — the shipping address determines which tax rates and shipping methods apply
|
|
272
|
-
6. **Order completed** — the address is cloned onto the order for permanent record
|
|
273
183
|
|
|
274
|
-
## Related
|
|
184
|
+
## Related
|
|
275
185
|
|
|
276
|
-
- [Markets](markets.md) —
|
|
277
|
-
- [
|
|
278
|
-
- [
|
|
279
|
-
- [
|
|
280
|
-
- [Account SDK guide](../sdk/store/account.md) — Managing the logged-in customer's address book with `client.customer.addresses`
|
|
281
|
-
- [Store API authentication](../../api-reference/store-api/authentication.md) — How to obtain and send the customer JWT (`Authorization: Bearer` header)
|
|
186
|
+
- [Markets](markets.md) — where you sell, in which currency
|
|
187
|
+
- [Fulfillments](fulfillments.md) — how the address shapes delivery options
|
|
188
|
+
- [Taxes](taxes.md) — how the address shapes tax
|
|
189
|
+
- [Carts](carts.md) — the checkout flow end to end
|