shopify_api 4.9.0 → 6.0.0

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.
Files changed (95) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/probots.yml +2 -0
  4. data/.gitignore +3 -0
  5. data/.rubocop.yml +8 -0
  6. data/.travis.yml +0 -4
  7. data/CHANGELOG +67 -0
  8. data/README.md +48 -26
  9. data/lib/active_resource/detailed_log_subscriber.rb +25 -2
  10. data/lib/shopify_api/connection.rb +5 -3
  11. data/lib/shopify_api/limits.rb +1 -2
  12. data/lib/shopify_api/resources/abandoned_checkout.rb +7 -0
  13. data/lib/shopify_api/resources/access_scope.rb +5 -0
  14. data/lib/shopify_api/resources/api_permission.rb +9 -0
  15. data/lib/shopify_api/resources/asset.rb +8 -8
  16. data/lib/shopify_api/resources/billing_address.rb +1 -1
  17. data/lib/shopify_api/resources/checkout.rb +27 -1
  18. data/lib/shopify_api/resources/collection_publication.rb +10 -0
  19. data/lib/shopify_api/resources/currency.rb +6 -0
  20. data/lib/shopify_api/resources/custom_collection.rb +3 -3
  21. data/lib/shopify_api/resources/{customer_invite_message.rb → customer_invite.rb} +0 -0
  22. data/lib/shopify_api/resources/graphql.rb +22 -0
  23. data/lib/shopify_api/resources/image.rb +2 -2
  24. data/lib/shopify_api/resources/inventory_item.rb +6 -0
  25. data/lib/shopify_api/resources/inventory_level.rb +55 -0
  26. data/lib/shopify_api/resources/line_item.rb +9 -1
  27. data/lib/shopify_api/resources/location.rb +4 -0
  28. data/lib/shopify_api/resources/o_auth.rb +8 -0
  29. data/lib/shopify_api/resources/order.rb +16 -4
  30. data/lib/shopify_api/resources/payment.rb +7 -0
  31. data/lib/shopify_api/resources/ping.rb +3 -0
  32. data/lib/shopify_api/resources/ping/conversation.rb +42 -0
  33. data/lib/shopify_api/resources/ping/delivery_confirmation_details.rb +10 -0
  34. data/lib/shopify_api/resources/ping/message.rb +8 -0
  35. data/lib/shopify_api/resources/product.rb +4 -4
  36. data/lib/shopify_api/resources/product_publication.rb +10 -0
  37. data/lib/shopify_api/resources/publication.rb +5 -0
  38. data/lib/shopify_api/resources/shipping_line.rb +1 -1
  39. data/lib/shopify_api/resources/shipping_rate.rb +7 -0
  40. data/lib/shopify_api/resources/shop.rb +4 -4
  41. data/lib/shopify_api/resources/smart_collection.rb +6 -2
  42. data/lib/shopify_api/resources/tender_transaction.rb +6 -0
  43. data/lib/shopify_api/session.rb +16 -17
  44. data/lib/shopify_api/version.rb +1 -1
  45. data/service.yml +8 -0
  46. data/shopify_api.gemspec +4 -1
  47. data/test/abandoned_checkouts_test.rb +29 -0
  48. data/test/api_permission_test.rb +9 -0
  49. data/test/checkouts_test.rb +72 -4
  50. data/test/collection_publication_test.rb +40 -0
  51. data/test/currency_test.rb +21 -0
  52. data/test/detailed_log_subscriber_test.rb +19 -1
  53. data/test/fixtures/abandoned_checkout.json +184 -0
  54. data/test/fixtures/abandoned_checkouts.json +186 -0
  55. data/test/fixtures/checkout.json +160 -0
  56. data/test/fixtures/checkouts.json +25 -49
  57. data/test/fixtures/collection_publication.json +11 -0
  58. data/test/fixtures/collection_publications.json +13 -0
  59. data/test/fixtures/currencies.json +25 -0
  60. data/test/fixtures/inventory_level.json +7 -0
  61. data/test/fixtures/inventory_levels.json +24 -0
  62. data/test/fixtures/order_with_properties.json +373 -0
  63. data/test/fixtures/payment.json +7 -0
  64. data/test/fixtures/payments.json +9 -0
  65. data/test/fixtures/ping/conversation.json +1 -0
  66. data/test/fixtures/ping/failed_delivery_confirmation.json +1 -0
  67. data/test/fixtures/ping/message.json +1 -0
  68. data/test/fixtures/ping/successful_delivery_confirmation.json +1 -0
  69. data/test/fixtures/product_publication.json +11 -0
  70. data/test/fixtures/product_publications.json +13 -0
  71. data/test/fixtures/publications.json +9 -0
  72. data/test/fixtures/shipping_rates.json +12 -0
  73. data/test/fixtures/smart_collection_products.json +155 -0
  74. data/test/fixtures/tender_transactions.json +52 -0
  75. data/test/inventory_level_test.rb +59 -0
  76. data/test/location_test.rb +14 -0
  77. data/test/order_test.rb +29 -1
  78. data/test/payment_test.rb +19 -0
  79. data/test/ping/conversation_test.rb +71 -0
  80. data/test/ping/message_test.rb +23 -0
  81. data/test/product_publication_test.rb +40 -0
  82. data/test/product_test.rb +10 -0
  83. data/test/publication_test.rb +12 -0
  84. data/test/session_test.rb +11 -37
  85. data/test/shipping_rate_test.rb +17 -0
  86. data/test/smart_collection_test.rb +25 -0
  87. data/test/tender_transaction_test.rb +18 -0
  88. data/test/test_helper.rb +8 -5
  89. data/test/variant_test.rb +4 -1
  90. metadata +102 -10
  91. data/lib/shopify_api/resources/discount.rb +0 -11
  92. data/test/discount_test.rb +0 -52
  93. data/test/fixtures/discount.json +0 -17
  94. data/test/fixtures/discount_disabled.json +0 -17
  95. data/test/fixtures/discounts.json +0 -34
@@ -7,8 +7,7 @@ module ShopifyAPI
7
7
  end
8
8
 
9
9
  class Session
10
- cattr_accessor :api_key, :secret, :protocol, :myshopify_domain, :port
11
- self.protocol = 'https'
10
+ cattr_accessor :api_key, :secret, :myshopify_domain
12
11
  self.myshopify_domain = 'myshopify.com'
13
12
 
14
13
  attr_accessor :url, :token, :name, :extra
@@ -44,14 +43,13 @@ module ShopifyAPI
44
43
  shop = shop.slice(0, idx)
45
44
  end
46
45
  return nil if shop.empty?
47
- shop = "#{shop}.#{myshopify_domain}"
48
- port ? "#{shop}:#{port}" : shop
46
+ "#{shop}.#{myshopify_domain}"
49
47
  rescue URI::InvalidURIError
50
48
  nil
51
49
  end
52
50
 
53
51
  def validate_signature(params)
54
- params = params.with_indifferent_access
52
+ params = (params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params).with_indifferent_access
55
53
  return false unless signature = params[:hmac]
56
54
 
57
55
  calculated_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new(), secret, encoded_params_for_signature(params))
@@ -105,7 +103,7 @@ module ShopifyAPI
105
103
  end
106
104
 
107
105
  def site
108
- "#{protocol}://#{url}/admin"
106
+ "https://#{url}/admin"
109
107
  end
110
108
 
111
109
  def valid?
@@ -128,17 +126,18 @@ module ShopifyAPI
128
126
  end
129
127
 
130
128
  private
131
- def parameterize(params)
132
- URI.escape(params.collect{|k,v| "#{k}=#{v}"}.join('&'))
133
- end
134
129
 
135
- def access_token_request(code)
136
- uri = URI.parse("#{protocol}://#{url}/admin/oauth/access_token")
137
- https = Net::HTTP.new(uri.host, uri.port)
138
- https.use_ssl = true
139
- request = Net::HTTP::Post.new(uri.request_uri)
140
- request.set_form_data({"client_id" => api_key, "client_secret" => secret, "code" => code})
141
- https.request(request)
142
- end
130
+ def parameterize(params)
131
+ URI.escape(params.collect { |k, v| "#{k}=#{v}" }.join('&'))
132
+ end
133
+
134
+ def access_token_request(code)
135
+ uri = URI.parse("https://#{url}/admin/oauth/access_token")
136
+ https = Net::HTTP.new(uri.host, uri.port)
137
+ https.use_ssl = true
138
+ request = Net::HTTP::Post.new(uri.request_uri)
139
+ request.set_form_data('client_id' => api_key, 'client_secret' => secret, 'code' => code)
140
+ https.request(request)
141
+ end
143
142
  end
144
143
  end
@@ -1,3 +1,3 @@
1
1
  module ShopifyAPI
2
- VERSION = "4.9.0"
2
+ VERSION = "6.0.0"
3
3
  end
data/service.yml ADDED
@@ -0,0 +1,8 @@
1
+ audience: partner
2
+ classification: library
3
+ org_line: App & Partner Platform
4
+ owners:
5
+ - Shopify/app-partner-dev-tools-education
6
+ slack_channels:
7
+ - dev-tools-education
8
+ - api-patterns-team
data/shopify_api.gemspec CHANGED
@@ -23,14 +23,17 @@ Gem::Specification.new do |s|
23
23
  s.summary = %q{ShopifyAPI is a lightweight gem for accessing the Shopify admin REST web services}
24
24
  s.license = "MIT"
25
25
 
26
- s.required_ruby_version = ">= 2.0"
26
+ s.required_ruby_version = ">= 2.1"
27
27
 
28
28
  s.add_runtime_dependency("activeresource", ">= 3.0.0")
29
29
  s.add_runtime_dependency("rack")
30
+ s.add_runtime_dependency("graphql-client")
30
31
 
31
32
  s.add_development_dependency("mocha", ">= 0.9.8")
32
33
  s.add_development_dependency("fakeweb")
33
34
  s.add_development_dependency("minitest", ">= 4.0")
34
35
  s.add_development_dependency("rake")
35
36
  s.add_development_dependency("timecop")
37
+ s.add_development_dependency("pry")
38
+ s.add_development_dependency("pry-byebug")
36
39
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class AbandonedCheckoutsTest < Test::Unit::TestCase
5
+ def setup
6
+ super
7
+
8
+ @expected_checkouts = JSON.parse(load_fixture('abandoned_checkouts'))['checkouts']
9
+ @expected_checkout_id = JSON.parse(load_fixture('abandoned_checkout'))['checkout']['id']
10
+ end
11
+
12
+ test ":create creates a checkout" do
13
+ fake 'checkouts', method: :post, status: 201, body: load_fixture('abandoned_checkout')
14
+
15
+ checkout = ShopifyAPI::AbandonedCheckout.create
16
+
17
+ assert_equal @expected_checkout_id, checkout.id
18
+ assert_equal true, checkout.attributes.include?(:abandoned_checkout_url)
19
+ end
20
+
21
+ test "get all checkouts indexed by token" do
22
+ fake 'checkouts', method: :get, status: 200, body: load_fixture('abandoned_checkouts')
23
+
24
+ checkouts = ShopifyAPI::AbandonedCheckout.all
25
+
26
+ assert_equal @expected_checkout_id, checkouts.first.id
27
+ assert_equal @expected_checkouts.size, checkouts.size
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class ApiPermissionTest < Test::Unit::TestCase
5
+ test "revoke access token" do
6
+ fake "api_permissions/current", method: :delete, status: 200, body: "{}"
7
+ assert ShopifyAPI::ApiPermission.destroy
8
+ end
9
+ end
@@ -1,9 +1,77 @@
1
+ # frozen_string_literal: true
1
2
  require 'test_helper'
2
3
 
3
4
  class CheckoutsTest < Test::Unit::TestCase
4
- test "get all should get all orders" do
5
- fake 'checkouts', :method => :get, :status => 200, :body => load_fixture('checkouts')
6
- checkout = ShopifyAPI::Checkout.all
7
- assert_equal 450789469, checkout.first.id
5
+ def setup
6
+ super
7
+
8
+ @expected_checkouts = JSON.parse(load_fixture('checkouts'))['checkouts']
9
+ @expected_checkout_id = JSON.parse(load_fixture('checkout'))['checkout']['token']
10
+ end
11
+
12
+ test ".headers includes a version" do
13
+ assert_equal "2016-09-06", ShopifyAPI::Checkout.headers["X-Shopify-Checkout-Version"]
14
+ assert_nil ShopifyAPI::Base.headers["X-Shopify-Checkout-Version"]
15
+ end
16
+
17
+ test ":create creates a checkout" do
18
+ fake 'checkouts', method: :post, status: 201, body: load_fixture('checkout')
19
+
20
+ checkout = ShopifyAPI::Checkout.create
21
+
22
+ assert_equal @expected_checkout_id, checkout.id
23
+ end
24
+
25
+ test "get all checkouts indexed by token" do
26
+ fake 'checkouts', method: :get, status: 200, body: load_fixture('checkouts')
27
+
28
+ checkouts = ShopifyAPI::Checkout.all
29
+
30
+ assert_equal @expected_checkout_id, checkouts.first.id
31
+ assert_equal @expected_checkouts.size, checkouts.size
32
+ end
33
+
34
+ test ":complete completes a checkout" do
35
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 200, body: load_fixture('checkout')
36
+
37
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
38
+
39
+ fake "checkouts/#{@expected_checkout_id}/complete", method: :post, status: 200, body: load_fixture('checkouts')
40
+
41
+ checkout.complete
42
+ end
43
+
44
+ test ":ready? returns true when status is 201" do
45
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 201, body: load_fixture('checkout')
46
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
47
+
48
+ assert_predicate checkout, :ready?
49
+ end
50
+
51
+ test ":ready? returns false when status is 202" do
52
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 202, body: load_fixture('checkout')
53
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
54
+
55
+ refute_predicate checkout, :ready?
56
+ end
57
+
58
+ test ":payments returns payments for a checkout" do
59
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 200, body: load_fixture('checkout')
60
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
61
+
62
+ fake "checkouts/#{@expected_checkout_id}/payments", method: :get, status: 202, body: load_fixture('payments')
63
+
64
+ assert_equal 10.00, checkout.payments.first.amount
65
+ end
66
+
67
+ test ":shipping_rates returns shipping rates for a checkout" do
68
+ fake "checkouts/#{@expected_checkout_id}", method: :get, status: 200, body: load_fixture('checkout')
69
+ checkout = ShopifyAPI::Checkout.find(@expected_checkout_id)
70
+
71
+ fake("checkouts/#{@expected_checkout_id}/shipping_rates",
72
+ method: :get,
73
+ status: 202,
74
+ body: load_fixture('shipping_rates'))
75
+ assert_equal "canada_post-INT.TP.BOGUS-4.00", checkout.shipping_rates.first.id
8
76
  end
9
77
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class CollectionPublicationTest < Test::Unit::TestCase
5
+ def test_get_all_collection_publications
6
+ fake 'publications/55650051/collection_publications', body: load_fixture('collection_publications')
7
+ collection_publications = ShopifyAPI::CollectionPublication.find(:all, params: { publication_id: 55650051 })
8
+
9
+ assert_equal 96062799894, collection_publications.first.id
10
+ assert_equal 55650051, collection_publications.first.publication_id
11
+ end
12
+
13
+ def test_get_collection_publication
14
+ fake 'publications/55650051/collection_publications/647162527768', body: load_fixture('collection_publication')
15
+ collection_publication = ShopifyAPI::CollectionPublication.find(647162527768, params: { publication_id: 55650051 })
16
+
17
+ assert_equal 96062799894, collection_publication.id
18
+ assert_equal 55650051, collection_publication.publication_id
19
+ end
20
+
21
+ def test_create_collection_publication
22
+ fake 'publications/55650051/collection_publications', method: :post, body: load_fixture('collection_publication')
23
+ ShopifyAPI::CollectionPublication.create(
24
+ publication_id: 55650051,
25
+ published_at: "2018-01-29T14:06:08-05:00",
26
+ published: true,
27
+ collection_id: 8267093571
28
+ )
29
+
30
+ expected_body = {
31
+ collection_publication: {
32
+ published_at: "2018-01-29T14:06:08-05:00",
33
+ published: true,
34
+ collection_id: 8267093571,
35
+ },
36
+ }.to_json
37
+
38
+ assert_equal expected_body, FakeWeb.last_request.body
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+
2
+ # frozen_string_literal: true
3
+ require 'test_helper'
4
+ class CurrencyTest < Test::Unit::TestCase
5
+ def setup
6
+ super
7
+ fake "currencies", method: :get, body: load_fixture('currencies')
8
+ end
9
+
10
+ context "Currency" do
11
+ should 'return a list of enabled currencies' do
12
+ currencies = ShopifyAPI::Currency.all
13
+ assert_equal 4, currencies.count
14
+ assert_equal %w(AUD EUR GBP HKD), currencies.map(&:currency)
15
+ assert_equal [true, true, true, false], currencies.map(&:enabled)
16
+ currencies.each do |currency|
17
+ assert_equal "2018-10-03T14:44:08-04:00", currency.rate_updated_at
18
+ end
19
+ end
20
+ end
21
+ end
@@ -30,7 +30,6 @@ class LogSubscriberTest < Test::Unit::TestCase
30
30
  assert_match /\-\-\> 200/, @logger.logged(:info)[1]
31
31
  assert_equal "Headers: {\"Accept\"=>\"application/json\", #{@ua_header}}", @logger.logged(:info)[2]
32
32
  assert_match /Response:\n\{\"page\"\:\{((\"id\"\:1)|(\"title\"\:\"Shopify API\")),((\"id\"\:1)|(\"title\"\:\"Shopify API\"))\}\}/, @logger.logged(:info)[3]
33
-
34
33
  end
35
34
 
36
35
  test "logging on #find with an error" do
@@ -46,4 +45,23 @@ class LogSubscriberTest < Test::Unit::TestCase
46
45
  assert_equal "Headers: {\"Accept\"=>\"application/json\", #{@ua_header}}", @logger.logged(:info)[2]
47
46
  assert_equal "Response:", @logger.logged(:info)[3]
48
47
  end
48
+
49
+ test "warns when the server responds with a x-shopify-api-deprecated-reason header" do
50
+ fake(
51
+ "pages/1",
52
+ method: :get,
53
+ body: @page,
54
+ x_shopify_api_deprecated_reason: 'https://help.shopify.com/en/api/getting-started/api-deprecations'
55
+ )
56
+
57
+ ShopifyAPI::Page.find(1)
58
+
59
+ assert_equal 1, @logger.logged(:warn).size
60
+
61
+ assert_match %r{\[DEPRECATED\] ShopifyAPI made a call to GET \/admin\/pages\/1.json}, @logger.logged(:warn).first
62
+ assert_match(
63
+ %r{See https:\/\/help.shopify.com\/en\/api\/getting-started\/api-deprecations for more details.},
64
+ @logger.logged(:warn).first
65
+ )
66
+ end
49
67
  end
@@ -0,0 +1,184 @@
1
+ {
2
+ "checkout": {
3
+ "buyer_accepts_marketing": false,
4
+ "cart_token": "68778783ad298f1c80c3bafcddeea02f",
5
+ "closed_at": null,
6
+ "completed_at": null,
7
+ "created_at": "2012-10-12T07:05:27-04:00",
8
+ "currency": "USD",
9
+ "email": "bob.norman@hostmail.com",
10
+ "gateway": null,
11
+ "id": 450789469,
12
+ "landing_site": null,
13
+ "note": null,
14
+ "referring_site": null,
15
+ "shipping_lines": [
16
+ {
17
+ "title": "Free Shipping",
18
+ "price": "0.00",
19
+ "code": "Free Shipping",
20
+ "source": "shopify"
21
+ }
22
+ ],
23
+ "source": null,
24
+ "source_identifier": null,
25
+ "source_name": "web",
26
+ "source_url": null,
27
+ "subtotal_price": "398.00",
28
+ "taxes_included": false,
29
+ "token": "2a1ace52255252df566af0faaedfbfa7",
30
+ "total_discounts": "0.00",
31
+ "total_line_items_price": "398.00",
32
+ "total_price": "409.94",
33
+ "total_tax": "11.94",
34
+ "total_weight": 400,
35
+ "updated_at": "2012-10-12T07:05:27-04:00",
36
+ "line_items": [
37
+ {
38
+ "applied_discounts": [
39
+
40
+ ],
41
+ "compare_at_price": null,
42
+ "fulfillment_service": "manual",
43
+ "gift_card": false,
44
+ "grams": 200,
45
+ "id": 49148385,
46
+ "line_price": "199.00",
47
+ "price": "199.00",
48
+ "product_id": 632910392,
49
+ "properties": null,
50
+ "quantity": 1,
51
+ "requires_shipping": true,
52
+ "sku": "IPOD2008RED",
53
+ "tax_lines": [
54
+
55
+ ],
56
+ "taxable": true,
57
+ "title": "IPod Nano - 8GB",
58
+ "variant_id": 49148385,
59
+ "variant_title": "Red",
60
+ "vendor": "Apple"
61
+ },
62
+ {
63
+ "applied_discounts": [
64
+
65
+ ],
66
+ "compare_at_price": null,
67
+ "fulfillment_service": "manual",
68
+ "gift_card": false,
69
+ "grams": 200,
70
+ "id": 808950810,
71
+ "line_price": "199.00",
72
+ "price": "199.00",
73
+ "product_id": 632910392,
74
+ "properties": null,
75
+ "quantity": 1,
76
+ "requires_shipping": true,
77
+ "sku": "IPOD2008PINK",
78
+ "tax_lines": [
79
+
80
+ ],
81
+ "taxable": true,
82
+ "title": "IPod Nano - 8GB",
83
+ "variant_id": 808950810,
84
+ "variant_title": "Pink",
85
+ "vendor": "Apple"
86
+ }
87
+ ],
88
+ "name": "#450789469",
89
+ "note_attributes": [
90
+ {
91
+ "name": "custom engraving",
92
+ "value": "Happy Birthday"
93
+ },
94
+ {
95
+ "name": "colour",
96
+ "value": "green"
97
+ }
98
+ ],
99
+ "discount_codes": [
100
+ {
101
+ "code": "TENOFF",
102
+ "amount": "10.00"
103
+ }
104
+ ],
105
+ "abandoned_checkout_url": "https://checkout.local/orders/690933842/2a1ace52255252df566af0faaedfbfa7?recovered=1",
106
+ "tax_lines": [
107
+ {
108
+ "price": "11.94",
109
+ "rate": 0.06,
110
+ "title": "State Tax"
111
+ }
112
+ ],
113
+ "billing_address": {
114
+ "address1": "Chestnut Street 92",
115
+ "address2": "",
116
+ "city": "Louisville",
117
+ "company": null,
118
+ "country": "United States",
119
+ "first_name": "Bob",
120
+ "last_name": "Norman",
121
+ "latitude": "45.41634",
122
+ "longitude": "-75.6868",
123
+ "phone": "555-625-1199",
124
+ "province": "Kentucky",
125
+ "zip": "40202",
126
+ "name": "Bob Norman",
127
+ "country_code": "US",
128
+ "province_code": "KY"
129
+ },
130
+ "shipping_address": {
131
+ "address1": "Chestnut Street 92",
132
+ "address2": "",
133
+ "city": "Louisville",
134
+ "company": null,
135
+ "country": "United States",
136
+ "first_name": "Bob",
137
+ "last_name": "Norman",
138
+ "latitude": "45.41634",
139
+ "longitude": "-75.6868",
140
+ "phone": "555-625-1199",
141
+ "province": "Kentucky",
142
+ "zip": "40202",
143
+ "name": "Bob Norman",
144
+ "country_code": "US",
145
+ "province_code": "KY"
146
+ },
147
+ "customer": {
148
+ "accepts_marketing": false,
149
+ "created_at": "2014-03-07T16:12:37-05:00",
150
+ "email": "bob.norman@hostmail.com",
151
+ "first_name": "Bob",
152
+ "id": 207119551,
153
+ "last_name": "Norman",
154
+ "last_order_id": null,
155
+ "multipass_identifier": null,
156
+ "note": null,
157
+ "orders_count": 0,
158
+ "state": "disabled",
159
+ "total_spent": "0.00",
160
+ "updated_at": "2014-03-07T16:12:37-05:00",
161
+ "verified_email": true,
162
+ "tags": "",
163
+ "last_order_name": null,
164
+ "default_address": {
165
+ "address1": "Chestnut Street 92",
166
+ "address2": "",
167
+ "city": "Louisville",
168
+ "company": null,
169
+ "country": "United States",
170
+ "first_name": null,
171
+ "id": 207119551,
172
+ "last_name": null,
173
+ "phone": "555-625-1199",
174
+ "province": "Kentucky",
175
+ "zip": "40202",
176
+ "name": null,
177
+ "province_code": "KY",
178
+ "country_code": "US",
179
+ "country_name": "United States",
180
+ "default": true
181
+ }
182
+ }
183
+ }
184
+ }