spree-shipstation 6.0.3 → 6.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddec4f0247a95634906e9524cfb5cea90e8d2196263fbb1eeca89d648d2635fe
4
- data.tar.gz: 110ab5a99182abba46d72cea236e15aa699d6ca93f36426553eb828ea04a53cc
3
+ metadata.gz: 7865e513b20d571959943b59b7747596eef99af0e0b2f0603f15e276ba95a0e1
4
+ data.tar.gz: 807dc1a6569d10d54d6c1980e8559a348f2c42fe517ac04c156b54a5f3a9d186
5
5
  SHA512:
6
- metadata.gz: 1585e912ace73bb1e8bf1011b770f60564ee13e98d36fb4760195b47cece397b51f2c3dda0fc471630c0187a10e361f5aa7488b00c26d370de0c83041fb47757
7
- data.tar.gz: 5434abcf2c333f1ffddf4e562fcb0ac0c2048277d6670da17cb9cc0ac7b6f89896c54081735977ed60139f9bace28f25e6be5cbdda537f63d522c9eb2bb58d5c
6
+ metadata.gz: 7c1c3b60842f0e7602e2134c5737198687ecffae71365e93172434820a99e83c8b7df9ce9b8b27c6991a0aa63082c8a9af30763110440dce5521ddc2484379cd
7
+ data.tar.gz: f82a70dedf894e663af683507af14575ba9ed45beb68127275c4715c35af64909e69f07939ea9e79c644fed9186d5bc2097d4218cbb6cbdb7688fbb3c696d4a2
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 6.0.4
6
+
7
+ ### Changed
8
+
9
+ - The dashboard's **Setup guide** link opens a setup page for store staff
10
+ (`docs/setup.md`) instead of the README.
11
+ - The README is now a short overview. Install, setup, configuration, how it
12
+ works, upgrading from 5.0 and development each have their own page under
13
+ `docs/`, which the gem's RubyGems page links as its documentation.
14
+
5
15
  ## 6.0.3
6
16
 
7
17
  ### Changed
@@ -46,8 +56,8 @@ polls.** Spree's upgrade tasks move 5.x shipment statuses to fulfillment statuse
46
56
  backfill the delivery-method providers the pickup rule reads, recompute the
47
57
  statuses of orders whose shipments were pending, ready or shipped, encrypt stored
48
58
  secrets, and carry `auto_capture_on_dispatch` over to the store's
49
- `capture_method`. Until then the export is empty. See "Upgrading
50
- from 5.0" in the README.
59
+ `capture_method`. Until then the export is empty. See
60
+ [Upgrading from 5.0](docs/upgrading-5-to-6.md).
51
61
 
52
62
  **Requirements:** Spree `~> 6.0`, Ruby 3.3 or newer, pagy 43.6 or newer. The 5.x
53
63
  releases do not install on Spree 6.
data/README.md CHANGED
@@ -3,215 +3,43 @@
3
3
  ![CI](https://github.com/aypex-io/spree-shipstation/workflows/CI/badge.svg)
4
4
  ![Standard Rb](https://github.com/aypex-io/spree-shipstation/workflows/Standard%20Rb/badge.svg)
5
5
 
6
- The spree-shipstation integration connects your Spree stores with [ShipStation](https://www.shipstation.com), allowing ShipStation to pull the fulfillments that are ready to ship from your store, and when one is sent, update the order with a tracking number and mark it as fulfilled.
6
+ Connects a Spree store to [ShipStation](https://www.shipstation.com) as a
7
+ **Custom Store**. ShipStation pulls the fulfillments that are ready to ship, and
8
+ when it creates a label, the gem records the tracking number and fulfils the
9
+ fulfillment in Spree.
7
10
 
8
- ## Requirements
11
+ | spree-shipstation | Spree | Ruby |
12
+ |---|---|---|
13
+ | 6.0.x | 6.x | 3.3 or newer |
14
+ | 5.0.x, on [`5-0-stable`](https://github.com/aypex-io/spree-shipstation/tree/5-0-stable) | 5.x | 3.1 or newer |
9
15
 
10
- - Spree 6 (`~> 6.0`)
11
- - Ruby 3.3 or newer
12
-
13
- Spree 5 stores stay on the 5.0.x releases, which live on the [`5-0-stable`](https://github.com/aypex-io/spree-shipstation/tree/5-0-stable) branch:
16
+ ## Install
14
17
 
15
18
  ```ruby
16
- gem "spree-shipstation", "~> 5.0"
19
+ gem "spree-shipstation", "~> 6.0"
17
20
  ```
18
21
 
19
- ## Installation
20
-
21
- 1. Add this extension to your Gemfile with this line:
22
-
23
- ```ruby
24
- gem "spree-shipstation", "~> 6.0"
25
- ```
26
-
27
- 2. Install the gem using Bundler
28
-
29
- ```shell
30
- bundle install
31
- ```
32
-
33
- The extension registers itself with Spree's integration framework automatically — there are no migrations or generators to run.
34
-
35
- ### Step 1: Configuring Spree
36
-
37
- Visit the **Integrations** section of your Spree dashboard and configure the ShipStation integration (listed with the shipping integrations) by creating a unique username and password. The username is 10 to 30 characters of letters, numbers and `. _ @ + -`; the password is 20 to 60 characters and needs an uppercase letter, a number and a special character, one of `! @ # $ % ^ & * ( ) , . ? " : { } | < >`. Other characters such as `-`, `_`, `+` and `=` may appear but do not count as the special character.
38
-
39
- ### Step 2: Configuring ShipStation
40
-
41
- Create a new ShipStation store by visiting: **Settings** -> **Selling Channels** -> **Stores** -> **Add Store**, then selecting the **Custom Store** option.
42
-
43
- Enter the following details:
44
-
45
- - **Username**: The username you created in Step 1.
46
- - **Password**: The password you created in Step 1.
47
- - **URL to custom page**: `https://shop.example.com/shipstation.xml`.
48
-
49
- That URL belongs to your **default store**. Any other store (a multi-store setup) must use the URL that names its ShipStation integration:
50
-
51
- ```
52
- https://shop.example.com/shipstation/int_abc123.xml
53
- ```
54
-
55
- `int_abc123` stands for the integration's prefixed ID, which starts with `int_`. In a Rails console, `Spree::Integrations::Shipstation.all.map(&:prefixed_id)` lists them. The credentials are always the ones set on that integration. The `.xml` is optional on both URLs.
56
-
57
- ### Order status
58
-
59
- Every order the gem sends has the status `ready`, so in ShipStation's Custom Store settings map `ready` to **Awaiting Shipment**. ShipStation's other statuses are never sent: the gem exports only fulfillments that can ship now, and ShipStation marks one as shipped itself once the label exists.
60
-
61
- ShipStation description | ShipStation status | Sent by the gem
62
- ------------------------|--------------------|----------------
63
- Awaiting Shipment | `paid` | `ready`
64
- Anything else | — | never; only `ready` is exported
65
-
66
- ## What ShipStation pulls
67
-
68
- Spree 6 splits an order into fulfillments (parcels), and the gem exports one ShipStation order per fulfillment. An order with several fulfillments therefore appears once for each, every copy carrying the order's totals.
69
-
70
- A fulfillment is exported when all of these hold:
71
-
72
- - it belongs to the store and is still **unfulfilled**;
73
- - its order is **placed**, so drafts and canceled orders are left out;
74
- - its order is **paid**: paid, overcharged or partially refunded; or it owes nothing (a total of zero); or, on a store whose capture method is `on_dispatch` or `manual`, it is only authorized, or partially paid with the rest authorized (mixed tender, such as store credit plus a card) (see [Payment capture](#payment-capture));
75
- - **none of its items is backordered**;
76
- - it is **not a pickup**: fulfillments collected in person (pickup or pickup point delivery methods) are not shipped.
77
-
78
- Apart from the pickup rule, these are most of the checks Spree makes again when the parcel is fulfilled, so ShipStation is rarely offered a parcel Spree would refuse. When it is fulfilled, Spree also checks that the stock location has the units on hand, and the limits under [Payment capture](#payment-capture) apply. A parcel refused then gets a `400` with Spree's reason.
79
-
80
- ShipStation's `start_date` and `end_date` (`MM/DD/YYYY HH:MM`, UTC) limit the export to fulfillments whose own or whose order's last update falls in that window. A date the gem cannot read is ignored. Results are ordered by last update, oldest first.
81
-
82
- `OrderID` is the fulfillment's id and `OrderNumber` its number. Fulfillments created on Spree 6 are numbered after their order, for example `R1001-F1`; fulfillments created before 6.0 keep the number they already had, so what ShipStation already holds does not change.
83
-
84
- ## What happens on a ship notice
85
-
86
- When a label is created, ShipStation posts to the same URL with `order_number` (the `OrderNumber` it was given), `tracking_number` and `carrier` in the query string, and the notice again as an XML body that carries the `OrderID` it was given. The gem finds that fulfillment in the store, by its `OrderID` when the body carries one and by its number otherwise, and:
87
-
88
- - **fulfils it** through Spree's own fulfil workflow, recording the tracking number, and answers `200`;
89
- - records the carrier when ShipStation's carrier name matches one of Spree's `tracking_carriers` (for example `UPS`, `USPS`, `FedEx` or `Royal Mail`); otherwise Spree detects it from the tracking number;
90
- - if the fulfillment is **already fulfilled or delivered**, only records or corrects the tracking number and answers `200`. ShipStation retries, and a retry does not fulfil the parcel or publish the event again. The one exception is a tracking number that is already on a different delivery of the same fulfillment, which is refused (below);
91
- - otherwise answers `400` with a plain-text reason and changes nothing. ShipStation shows the reason to the warehouse. The reasons include:
92
- - `Could not find shipment with number …`, for an unknown or blank number, or an `OrderID` whose fulfillment no longer exists;
93
- - `Shipment … does not match order id …`, when the `OrderID` belongs to a fulfillment of another order;
94
- - `Tracking number is required`;
95
- - `Shipment … is canceled`;
96
- - Spree's own message when it refuses to fulfil, for example `This order is not paid for yet`, or its message about backordered units;
97
- - `Could not capture payment: …`, when the gateway declines a capture on dispatch;
98
- - `Tracking number has already been taken`, when a retry for a fulfilled parcel names a tracking number that already belongs to another delivery of the same fulfillment.
99
-
100
- The gem never forces a fulfilment past Spree's paid and backordered checks, and never splits a fulfillment.
101
-
102
- ### Why the `OrderID` comes first
103
-
104
- A new fulfillment's number is its place in its order: `R1001-F2` is the second parcel. When every item is moved out of a parcel (with the dashboard's split or change-location actions, say), Spree deletes it, and the parcels after it move up a place and take new numbers. A fulfillment's id never changes, so the gem trusts the `OrderID` over the number. A notice for a renumbered parcel still fulfils the right one, and a notice for a parcel Spree deleted after ShipStation imported it is refused with a `400` rather than fulfilling a sibling that now has its number. Re-import the order from ShipStation to pick up its parcels as they now are.
105
-
106
- So, in an order with several parcels, avoid moving every item out of a parcel ShipStation has already imported: that parcel is no longer valid in ShipStation.
107
-
108
- ## Configuration
109
-
110
- ### Payment capture
111
-
112
- The gem has no capture setting of its own. Capture on dispatch follows Spree 6's `capture_method`, set on the store and optionally overridden by each payment method:
113
-
114
- - `checkout` (the default) charges as the order is placed. Authorized orders are not yet paid, so they are not exported.
115
- - `on_dispatch` authorizes at checkout and charges when the fulfillment is fulfilled. The gem exports authorized orders, and partially paid ones whose rest is authorized, and Spree captures the payment as the ship notice is applied. If the gateway declines, ShipStation gets a `400` ("Could not capture payment: …") and nothing is kept: the fulfillment stays unfulfilled, the payment stays pending and any tracking number written during the attempt is rolled back. The next retry, or the merchant's fix to the payment, fulfils it.
116
- - `manual` authorizes at checkout and leaves charging to your staff. The gem exports authorized and partially paid orders, and Spree lets them ship without charging them.
22
+ Run `bundle install` and restart. There is nothing else to run. Then connect
23
+ ShipStation from the dashboard under **Settings → Integrations** by following
24
+ [Setup](docs/setup.md).
117
25
 
118
- Two limits:
26
+ ## Documentation
119
27
 
120
- - Whether an authorized or partially paid order is exported depends on the **store's** capture method only. A store that captures at checkout but has a payment method that overrides it to `on_dispatch` never exports that method's authorized orders, and does so silently. To export them, set the store's capture method to `on_dispatch` or `manual`, bearing in mind that this changes it for every payment method that has no setting of its own. In the reverse case, a store set to `on_dispatch` with a payment method overridden to capture at checkout, an authorized order is exported and the ship notice is then refused with Spree's reason.
121
- - A partially refunded order is exported, because the rest of it may still ship, but Spree compares the net captured amount (after refunds) with what the order was due, so the ship notice may still be refused with Spree's reason. Likewise a partially paid order ships only while the rest is authorized on payment methods that defer capture.
122
-
123
- ### Fulfillment providers
124
-
125
- Give the delivery methods whose parcels ShipStation ships Spree's **Manual** fulfillment provider. On a ship notice, Spree's fulfil workflow also buys a label and tells the delivery method's provider that the parcel has shipped; for Manual, both do nothing. A provider that buys labels or hands parcels to a 3PL would act a second time for a parcel ShipStation has already shipped. A declined capture on dispatch does not undo those calls either, even though everything Spree wrote is rolled back.
126
-
127
- ### Telling the customer
128
-
129
- Spree core does not send a shipped email by itself (as of Spree 6.0.1). When a fulfillment is fulfilled, Spree publishes the `fulfillment.fulfilled` event, and the gem asks for the customer to be notified (`notify_customer: true`). Your application decides whether, and how, to tell the customer, by subscribing to `fulfillment.fulfilled`. Without a subscriber, no shipped email goes out.
130
-
131
- One caveat: when a capture on dispatch is declined, Spree publishes `fulfillment.fulfilled` synchronously inside the transaction that is then rolled back. A subscriber that notifies at once, rather than after the transaction commits, can tell a customer their order has shipped when the attempt was declined and nothing was kept. Spree's own fulfil endpoint behaves the same way, so if that matters to you, make your subscriber send only after the transaction has committed.
132
-
133
- ### Pagination
134
-
135
- The export endpoint returns up to **50 fulfillments per page**. ShipStation handles pagination automatically using the `page` query parameter; a `page` that is not a number is read as the first page.
136
-
137
- ## Upgrading from 5.0
138
-
139
- - Bump the gem to `~> 6.0` alongside your upgrade to Spree 6.
140
- - The gem has no data migration of its own. Spree 6's migrations rename shipments to fulfillments, keeping their ids and numbers, and copy the integration's password into `secret_preferences` as plain text. `bin/rake spree:upgrade` (next step) encrypts it when Active Record encryption keys are configured.
141
- - **Run `bin/rake spree:upgrade` right after `db:migrate`, before ShipStation next polls.** Spree's upgrade tasks move 5.x shipment statuses to fulfillment statuses (`ready` and `pending` become `unfulfilled`, `shipped` becomes `fulfilled`), backfill the delivery-method providers the pickup rule reads, recompute the payment and fulfillment statuses of orders whose shipments were pending, ready or shipped, encrypt stored secrets, and carry settings such as `auto_capture_on_dispatch` over to the store's `capture_method`. An order in another state, such as partly shipped or backordered, keeps its 5.x payment status until Spree next updates it. Until then the export is empty, since no fulfillment is `unfulfilled` yet. Meanwhile a parcel still marked `shipped` is treated as fulfilled: a ship notice only records its tracking.
142
- - **ShipStation needs no changes** for the default store: same URL, same credentials, same `OrderNumber` for parcels it has already imported, same `ready` status.
143
- - New fulfillments are numbered like `R1001-F1`. A store other than the default must use `/shipstation/<integration id>` (see Step 2): 5.x picked the store from the request's host, and Spree 6 does not, so the bare URL serves only the default store.
144
- - Capture on dispatch now follows Spree 6's `capture_method` setting (on the store or the payment method). Spree's upgrade task carries `auto_capture_on_dispatch` over to the store's `capture_method`.
145
- - As before, unpaid or backordered fulfillments do not appear in ShipStation until they can ship: 5.x exported only `ready` shipments.
146
- - A ship notice for something Spree will not ship is answered with `400` and Spree's reason.
147
- - Spree 6 core sends no shipped email on its own, so subscribe to `fulfillment.fulfilled` if your store relied on the 5.x email (see [Telling the customer](#telling-the-customer)).
148
-
149
- ## Security considerations
150
-
151
- - **Serve the endpoints over HTTPS.** Both `/shipstation` endpoints authenticate with HTTP Basic Auth, which transmits the configured username and password (base64-encoded) on every request. Always terminate these requests over TLS in production so the credentials are not exposed in transit. Credentials are compared in constant time to avoid timing attacks, and an integration whose stored username or password is blank refuses every request.
152
- - **Consider rate limiting.** The gem does not throttle authentication attempts. If you want brute-force protection, add it at the application or edge layer (for example, [`rack-attack`](https://github.com/rack/rack-attack) or Rails' `rate_limit`). The enforced credential length (10–30 character username, 20–60 character complex password) already makes guessing impractical.
153
- - **Response codes.** When no active ShipStation integration is configured, the endpoints respond with `404` before authentication is checked; a configured-but-unauthenticated request responds with `401`. This is intentional, but be aware it reveals whether the integration is configured. An integration that is switched off is the same as none.
154
-
155
- ## Performance
156
-
157
- The export query eager-loads its association graph to avoid N+1 queries. It selects unfulfilled fulfillments, joins their orders and filters by payment status, and orders and windows by `updated_at`. The gem ships no migrations of its own. Spree 6's schema indexes `spree_fulfillments.status` and `spree_orders.payment_status`, but not `spree_fulfillments.updated_at` or `spree_orders.updated_at`; on large stores, consider indexing those two in your application's database.
158
-
159
- ## Usage
160
-
161
- There's nothing you need to do. Once properly configured, the integration just works!
162
-
163
- ### Compatibility
164
-
165
- spree-shipstation | Spree | Ruby
166
- ------------------|-------|------
167
- 6.0.x | 6.x (`~> 6.0`) | 3.3 or newer
168
- 5.0.x, on [`5-0-stable`](https://github.com/aypex-io/spree-shipstation/tree/5-0-stable) | 5.x | 3.1 or newer
169
-
170
- ### Testing
171
-
172
- First bundle your dependencies:
173
-
174
- ```shell
175
- bundle
176
- ```
177
-
178
- To run the tests use:
179
-
180
- ```shell
181
- bundle exec rake
182
- ```
183
-
184
- CI runs the suite against Spree 6.0.0 and the newest 6.x release. To test against a specific Spree version locally:
185
-
186
- ```shell
187
- SPREE_VERSION=6.0.0 bundle update spree
188
- bundle exec rake
189
- ```
190
-
191
- ### Code Formatting
192
-
193
- To check your code formatting with [Standard Rb](https://github.com/testdouble/standard) run:
194
-
195
- ```shell
196
- bundle exec standardrb
197
- ```
198
-
199
- To fix basic code formatting issues run:
200
-
201
- ```shell
202
- bundle exec standardrb --fix
203
- ```
204
-
205
- ## Releasing
206
-
207
- Bump `Spree::Shipstation::VERSION` in `lib/spree/shipstation/version.rb`, add the release to the changelog, and merge. Pushing a `v*` tag publishes the gem to RubyGems through the Release workflow.
28
+ - [Install](docs/install.md): adding the gem, its routes, and a production
29
+ checklist
30
+ - [Setup](docs/setup.md): connecting ShipStation, for store staff
31
+ - [Configuration](docs/configuration.md): payment capture, fulfillment
32
+ providers, shipped emails
33
+ - [How it works](docs/how-it-works.md): what is exported, what a ship notice
34
+ does, error messages
35
+ - [Upgrading from 5.0](docs/upgrading-5-to-6.md)
36
+ - [Development](docs/development.md): tests, linting, releasing
37
+ - [Changelog](CHANGELOG.md)
208
38
 
209
39
  ## License
210
40
 
211
41
  Copyright (c) 2021-2026 Matthew Kennedy, released under the MIT License.
212
42
 
213
- ## Code of Conduct
214
-
215
- Everyone interacting in this project's codebases, issue trackers, and
216
- communication channels is expected to follow the [code of
217
- conduct](CODE_OF_CONDUCT.md).
43
+ Everyone interacting in this project's codebases, issue trackers and
44
+ communication channels is expected to follow the
45
+ [code of conduct](CODE_OF_CONDUCT.md).
@@ -72,12 +72,12 @@ module Spree
72
72
  end
73
73
 
74
74
  ##
75
- # Link to this extension's setup guide, shown on the dashboard's gallery card.
75
+ # Link to the setup guide for store staff, shown on the dashboard's gallery card.
76
76
  #
77
77
  # @return [String]
78
78
  #
79
79
  def self.docs_url
80
- "https://github.com/aypex-io/spree-shipstation#readme"
80
+ "https://github.com/aypex-io/spree-shipstation/blob/main/docs/setup.md"
81
81
  end
82
82
 
83
83
  private
@@ -4,7 +4,7 @@ module Spree
4
4
  module Shipstation
5
5
  # Major version tracks Spree's major version: 6.x supports Spree 6; 5.0.x
6
6
  # lives on +5-0-stable+.
7
- VERSION = "6.0.3"
7
+ VERSION = "6.0.4"
8
8
 
9
9
  ##
10
10
  # Gem version as a comparable +Gem::Version+.
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
  "source_code_uri" => s.homepage,
23
23
  "bug_tracker_uri" => "#{s.homepage}/issues",
24
24
  "changelog_uri" => "#{s.homepage}/blob/main/CHANGELOG.md",
25
+ "documentation_uri" => "#{s.homepage}/tree/main/docs",
25
26
  "rubygems_mfa_required" => "true"
26
27
  }
27
28
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree-shipstation
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.3
4
+ version: 6.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Kennedy
@@ -89,6 +89,7 @@ metadata:
89
89
  source_code_uri: https://github.com/aypex-io/spree-shipstation
90
90
  bug_tracker_uri: https://github.com/aypex-io/spree-shipstation/issues
91
91
  changelog_uri: https://github.com/aypex-io/spree-shipstation/blob/main/CHANGELOG.md
92
+ documentation_uri: https://github.com/aypex-io/spree-shipstation/tree/main/docs
92
93
  rubygems_mfa_required: 'true'
93
94
  rdoc_options: []
94
95
  require_paths: