spree_doordash 0.1.0 → 0.1.2

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: ba867fc23a044f6166e7fcb61f1641a9f3bd79ddc7c08cf1808abf20664defee
4
- data.tar.gz: c3dba97527799999bcbd5cc9881b936c368a1ff819f89e4bc8cf568e7513f568
3
+ metadata.gz: 4386dd5c8b44b1fa483213fcfae97c5c21ad8143f92650714fb7cc0f1966e059
4
+ data.tar.gz: 75d2fbda1b0870f14397a77a042b518c6b5972f1e7d8c3fb7f4c38ca182395ea
5
5
  SHA512:
6
- metadata.gz: 9eccedd59afafb3bc174d769deb3cd47aa6511ca0ec4f6aec3989adab82c148928c74ba0c4416096996d1fa032f49534f1a22736292e670bcacf7ee33a1c090a
7
- data.tar.gz: f5bb35207b1cfba4544b47c459c56936eb69b163c3deb3f9fd4f5322ad043b6f36b5b7a220d306037dcffa5d68967b39fac1b2bd0bc558715a41ed78aa1da034
6
+ metadata.gz: b9c8465fcded4299713522d77ddf130c6ad9c8479233644fe9efe41817b90b08f7d6e8dd4edd99d88ee9c2b5b408668315d8f3bb58f2d8be47be708eeb7e34ef
7
+ data.tar.gz: 2ad0971dfd7551ebcb0e6ebac5c0fbe94a4ef7d5c8f6022469ee8c937cd31ded0d17c7c0ea2ed6e6fb94c1426d906a0ccd72b1f3de5b7d38513207a0780f6d64
data/CHANGELOG.md CHANGED
@@ -2,7 +2,55 @@
2
2
 
3
3
  All notable changes to this project are documented here.
4
4
 
5
- ## 0.1.0 (unreleased)
5
+ ## 0.1.2
6
+
7
+ A real bug found live during checkout-validation work: a blank phone on the dropoff address used
8
+ to fall back to a fake placeholder number (`+10000000000`) that DoorDash's API rejected anyway —
9
+ the request was doomed before it was even built, and (same as the E.164 issue fixed in 0.1.1) the
10
+ DoorDash Delivery rate just silently vanished with no error anywhere in the UI.
11
+
12
+ - `SpreeDoordash::Quote#call` now returns early (no API call at all) when the ship address has no
13
+ phone, instead of building a request known to fail. `format_phone`'s placeholder fallback is
14
+ removed entirely.
15
+ - `Spree::Calculator::Shipping::DoordashQuote#compute_package` now pushes a
16
+ `doordash_quote_unavailable` warning onto `Spree::Order#warnings` whenever `SpreeDoordash::Quote`
17
+ returns `nil` — generic to *why* the quote failed (bad/missing phone, an address DoorDash
18
+ genuinely can't serve, DoorDash API down), confirmed live for two different real causes.
19
+ `Spree::Order#warnings` is the same transient mechanism core's own
20
+ `ensure_available_shipping_rates` already uses for the identical class of problem, and flows
21
+ through to the Store API's `cart.warnings` with no new API surface. A storefront reference
22
+ implementation now renders this as a real, non-blocking banner instead of silence — see
23
+ `spree_storefront_web`'s `DeliveryMethodSection.tsx`.
24
+
25
+ Full suite: 90 examples, 0 failures (2 new). Coverage: 94.14% (241/256 lines). Brakeman clean.
26
+
27
+ ## 0.1.1
28
+
29
+ Two real production bugs, both invisible to the full spec suite and to every prior "live
30
+ verification" in this project, both found only by driving a genuine storefront checkout end to
31
+ end instead of calling the services directly:
32
+
33
+ - **DoorDash rejects any phone number that isn't strict E.164.** Every earlier live test in this
34
+ project — including the M1–M5 verifications documented below — happened to type phone numbers
35
+ directly in `+1XXXXXXXXXX` form. The first real checkout, with a number typed the way an address
36
+ form actually produces one (`(202) 555-0199`), got a real `400 Unknown phone number format` from
37
+ DoorDash's `/drive/v2/quotes` endpoint — which silently dropped the DoorDash Delivery shipping
38
+ rate with no error surfaced anywhere in checkout. Fixed by normalizing both `pickup_phone_number`
39
+ and `dropoff_phone_number` to E.164 before every quote request.
40
+
41
+ - **The order-completed subscriber was never actually registered with Spree's event system.**
42
+ `Spree::Subscriber`'s own docstring claims subscribers are "automatically registered during Rails
43
+ initialization" — that's not what `spree_core` 5.6.1 actually does. `Spree::Events.register_subscribers!`
44
+ only iterates an explicit `Spree.subscribers` array, populated via `Spree.subscribers << YourClass`
45
+ in an initializer; there's no automatic scan. This gem never had that initializer line. Every spec
46
+ for `OrderCompletedSubscriber` called `.call` on it directly, which bypasses the registry entirely
47
+ and kept passing regardless — so a real storefront order, placed with DoorDash Delivery selected,
48
+ completed successfully and was never dispatched, with nothing anywhere indicating a problem. Fixed
49
+ by adding the missing `config/initializers/spree.rb`, plus a new spec that asserts
50
+ `Spree.subscribers` actually includes the class — the one test that would have caught this from
51
+ the start.
52
+
53
+ ## 0.1.0
6
54
 
7
55
  Initial development. M1 (foundation) — complete and verified against a real DoorDash Sandbox
8
56
  endpoint (`bin/rails spree_doordash:verify_connection`, a real accepted quote):
@@ -20,7 +20,26 @@ module Spree
20
20
 
21
21
  def compute_package(package)
22
22
  result = SpreeDoordash::Quote.call(package.order)
23
- return nil unless result
23
+ if result.nil?
24
+ # A real bug found live: a nil Result here (bad/missing phone, an
25
+ # address DoorDash genuinely can't serve, DoorDash API down —
26
+ # SpreeDoordash::Quote#call collapses all of these to the same
27
+ # "unavailable" nil, deliberately, so this stays generic to *why*)
28
+ # used to just make the rate silently vanish with zero signal
29
+ # anywhere in the UI. Spree::Order#warnings is the same
30
+ # transient, request-scoped mechanism core's own
31
+ # `ensure_available_shipping_rates` already uses for the
32
+ # identical class of problem (a line item that can't ship at
33
+ # all) — it flows through to the Store API's `cart.warnings`
34
+ # with no new API surface needed. The storefront renders its own
35
+ # localized copy for this code; `message` here is English-only,
36
+ # a fallback for any other API consumer, not the UI text itself.
37
+ package.order.warnings |= [{
38
+ code: 'doordash_quote_unavailable',
39
+ message: 'We could not get a DoorDash delivery quote for this address.'
40
+ }]
41
+ return nil
42
+ end
24
43
 
25
44
  result.fee_cents / 100.0
26
45
  end
@@ -17,6 +17,16 @@ module SpreeDoordash
17
17
  def call(order)
18
18
  location_mapping = location_mapping_for(order)
19
19
  return nil unless location_mapping && order.ship_address
20
+ # A real bug found live: with no phone on the ship address,
21
+ # `format_phone` used to fall back to a fake placeholder number that
22
+ # DoorDash's API correctly rejects with a 400 — the request was
23
+ # doomed before it was even built. `Spree::Config[:address_requires_phone]`
24
+ # (spree_host) should make this unreachable in the normal storefront
25
+ # checkout flow, but this guard stays as defense in depth for any
26
+ # other path that can create an order (admin, API, migrated data)
27
+ # — same "unavailable, not exceptional" nil-return shape as every
28
+ # other unserviceable case below.
29
+ return nil if order.ship_address.phone.blank?
20
30
 
21
31
  client = SpreeDoordash::Client.for_store(order.store || Spree::Store.default)
22
32
  # A random suffix per attempt, not just order.number — confirmed live
@@ -73,14 +83,40 @@ module SpreeDoordash
73
83
  external_delivery_id: external_delivery_id,
74
84
  pickup_address: format_address(location_mapping.stock_location),
75
85
  pickup_business_name: location_mapping.stock_location.name,
76
- pickup_phone_number: location_mapping.stock_location.phone.presence || '+10000000000',
86
+ pickup_phone_number: format_phone(location_mapping.stock_location.phone),
77
87
  pickup_instructions: location_mapping.stock_location.pickup_instructions,
78
88
  dropoff_address: format_address(order.ship_address),
79
- dropoff_phone_number: order.ship_address.phone.presence || '+10000000000',
89
+ dropoff_phone_number: format_phone(order.ship_address.phone),
80
90
  order_value: (order.total * 100).to_i
81
91
  }
82
92
  end
83
93
 
94
+ # DoorDash wants E.164 (+1XXXXXXXXXX) and rejects anything else outright
95
+ # — confirmed live: a real checkout with a normally-typed US phone number
96
+ # ("(202) 555-0199", exactly what an address form produces) got a real
97
+ # 400 "Unknown phone number format" from DoorDash, silently dropping the
98
+ # DoorDash Delivery rate with no visible error anywhere in the UI. Every
99
+ # earlier live verification in this project used numbers already typed
100
+ # in +1XXXXXXXXXX form by hand, which is why this never surfaced until
101
+ # an actual storefront checkout was driven through normally.
102
+ #
103
+ # US-only, matching the rest of this demo (every address here is a US
104
+ # address) — strips everything but digits, assumes a bare 10-digit
105
+ # number is domestic and prepends the country code.
106
+ #
107
+ # No blank-phone fallback here (a prior version faked one as
108
+ # "+10000000000" — DoorDash rejected it too, just as uninformatively as
109
+ # a missing one; better to let a genuinely blank pickup phone fail
110
+ # DoorDash's own validation loudly than paper over it with fake data).
111
+ # `call` above already guards the dropoff phone specifically; this
112
+ # method is also reused for the pickup (StockLocation) phone, which is
113
+ # real business data expected to always be set.
114
+ def format_phone(raw)
115
+ digits = raw.to_s.gsub(/\D/, '')
116
+ digits = "1#{digits}" if digits.length == 10
117
+ "+#{digits}"
118
+ end
119
+
84
120
  # Spree::StockLocation and Spree::Address share the same field shape
85
121
  # (address1/address2/city/state/zipcode/country) even though they're
86
122
  # unrelated classes — DoorDash wants one flat string, not structured
@@ -0,0 +1,17 @@
1
+ # Registers this extension's event subscriber with Spree's event system.
2
+ #
3
+ # Spree::Subscriber's own docstring says subscribers are "automatically
4
+ # registered during Rails initialization" — that's not what actually
5
+ # happens in spree_core 5.6.1: Spree::Events.register_subscribers! only
6
+ # ever iterates the explicit Spree.subscribers array (see
7
+ # spree_core/lib/spree/events.rb) — there is no Zeitwerk-descendant scan.
8
+ # Without this file, SpreeDoordash::OrderCompletedSubscriber is a real,
9
+ # loadable class (so specs calling it directly always passed) but is never
10
+ # actually wired to the 'order.completed' event in the running app — found
11
+ # live: a real storefront order completed with DoorDash Delivery selected
12
+ # and never got dispatched, because nothing ever appended this subscriber
13
+ # to Spree.subscribers. Mirrors spree_square's own identical registration
14
+ # in its own config/initializers/spree.rb.
15
+ Rails.application.config.after_initialize do
16
+ Spree.subscribers << SpreeDoordash::OrderCompletedSubscriber
17
+ end
@@ -1,5 +1,5 @@
1
1
  module SpreeDoordash
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.1.2'.freeze
3
3
 
4
4
  def gem_version
5
5
  Gem::Version.new(VERSION)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_doordash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amit Solanki
@@ -136,6 +136,7 @@ files:
136
136
  - app/views/spree/admin/doordash_delivery_mappings/index.html.erb
137
137
  - app/views/spree/admin/doordash_webhook_events/index.html.erb
138
138
  - config/brakeman.ignore
139
+ - config/initializers/spree.rb
139
140
  - config/initializers/spree_admin_doordash_navigation.rb
140
141
  - config/initializers/spree_admin_doordash_tables.rb
141
142
  - config/initializers/spree_doordash_calculators.rb