workarea-zipco 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/.eslintrc.json +35 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  4. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.github/workflows/ci.yml +64 -0
  7. data/.gitignore +23 -0
  8. data/.rubocop.yml +2 -0
  9. data/.stylelintrc.json +8 -0
  10. data/CHANGELOG.md +17 -0
  11. data/Gemfile +17 -0
  12. data/README.md +51 -0
  13. data/Rakefile +59 -0
  14. data/app/assets/images/zipco/.keep +0 -0
  15. data/app/assets/stylesheets/workarea/storefront/zipco/components/_zipco_icon.scss +7 -0
  16. data/app/controllers/storefront/checkout/place_order_controller.decorator +11 -0
  17. data/app/controllers/storefront/zipco_controller.rb +96 -0
  18. data/app/controllers/storefront/zipco_landing_controller.rb +6 -0
  19. data/app/helpers/.keep +0 -0
  20. data/app/mailers/.keep +0 -0
  21. data/app/models/checkout.decorator +33 -0
  22. data/app/models/workarea/order.decorator +52 -0
  23. data/app/models/workarea/order/status/zip_referred.rb +13 -0
  24. data/app/models/workarea/payment.decorator +30 -0
  25. data/app/models/workarea/payment/authorize/zipco.rb +66 -0
  26. data/app/models/workarea/payment/capture/zipco.rb +58 -0
  27. data/app/models/workarea/payment/purchase/zipco.rb +66 -0
  28. data/app/models/workarea/payment/refund/zipco.rb +59 -0
  29. data/app/models/workarea/payment/tender/zipco.rb +13 -0
  30. data/app/models/workarea/search/admin/admin.decorator +7 -0
  31. data/app/services/workarea/zipco/checkout.rb +48 -0
  32. data/app/services/workarea/zipco/order.rb +188 -0
  33. data/app/services/workarea/zipco/setup.rb +37 -0
  34. data/app/view_models/workarea/storefront/checkout/payment_view_model.decorator +17 -0
  35. data/app/view_models/workarea/storefront/order_view_model.decorator +11 -0
  36. data/app/view_models/workarea/storefront/zipco_view_model.rb +18 -0
  37. data/app/views/workarea/admin/orders/tenders/_zipco.html.haml +4 -0
  38. data/app/views/workarea/storefront/checkouts/_zipco_payment.html.haml +10 -0
  39. data/app/views/workarea/storefront/order_mailer/tenders/_zipco.html.haml +4 -0
  40. data/app/views/workarea/storefront/order_mailer/tenders/_zipco.text.erb +1 -0
  41. data/app/views/workarea/storefront/orders/_zipco_order_message.html.haml +2 -0
  42. data/app/views/workarea/storefront/orders/tenders/_zipco.html.haml +6 -0
  43. data/app/views/workarea/storefront/zipco/_zipco_tagline.html.haml +4 -0
  44. data/app/views/workarea/storefront/zipco_landing/show.html.haml +4 -0
  45. data/bin/rails +25 -0
  46. data/config/initializers/appends.rb +24 -0
  47. data/config/initializers/workarea.rb +15 -0
  48. data/config/locales/en.yml +38 -0
  49. data/config/routes.rb +5 -0
  50. data/lib/workarea/zipco.rb +48 -0
  51. data/lib/workarea/zipco/bogus_gateway.rb +160 -0
  52. data/lib/workarea/zipco/engine.rb +10 -0
  53. data/lib/workarea/zipco/gateway.rb +113 -0
  54. data/lib/workarea/zipco/response.rb +29 -0
  55. data/lib/workarea/zipco/version.rb +5 -0
  56. data/test/dummy/.ruby-version +1 -0
  57. data/test/dummy/Rakefile +6 -0
  58. data/test/dummy/app/assets/config/manifest.js +3 -0
  59. data/test/dummy/app/assets/images/.keep +0 -0
  60. data/test/dummy/app/assets/javascripts/application.js +14 -0
  61. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  62. data/test/dummy/app/controllers/application_controller.rb +2 -0
  63. data/test/dummy/app/controllers/concerns/.keep +0 -0
  64. data/test/dummy/app/helpers/application_helper.rb +2 -0
  65. data/test/dummy/app/jobs/application_job.rb +2 -0
  66. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  67. data/test/dummy/app/models/concerns/.keep +0 -0
  68. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  69. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  70. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  71. data/test/dummy/bin/bundle +3 -0
  72. data/test/dummy/bin/rails +4 -0
  73. data/test/dummy/bin/rake +4 -0
  74. data/test/dummy/bin/setup +28 -0
  75. data/test/dummy/bin/update +28 -0
  76. data/test/dummy/bin/yarn +11 -0
  77. data/test/dummy/config.ru +5 -0
  78. data/test/dummy/config/application.rb +34 -0
  79. data/test/dummy/config/boot.rb +5 -0
  80. data/test/dummy/config/environment.rb +5 -0
  81. data/test/dummy/config/environments/development.rb +52 -0
  82. data/test/dummy/config/environments/production.rb +83 -0
  83. data/test/dummy/config/environments/test.rb +45 -0
  84. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  85. data/test/dummy/config/initializers/assets.rb +14 -0
  86. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  87. data/test/dummy/config/initializers/content_security_policy.rb +25 -0
  88. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  89. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  90. data/test/dummy/config/initializers/inflections.rb +16 -0
  91. data/test/dummy/config/initializers/mime_types.rb +4 -0
  92. data/test/dummy/config/initializers/workarea.rb +5 -0
  93. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  94. data/test/dummy/config/locales/en.yml +33 -0
  95. data/test/dummy/config/puma.rb +34 -0
  96. data/test/dummy/config/routes.rb +5 -0
  97. data/test/dummy/config/spring.rb +6 -0
  98. data/test/dummy/db/seeds.rb +2 -0
  99. data/test/dummy/lib/assets/.keep +0 -0
  100. data/test/dummy/log/.keep +0 -0
  101. data/test/integration/workarea/storefront/zipco_integration_test.rb +250 -0
  102. data/test/models/workarea/order/zipco_queries_test.rb +35 -0
  103. data/test/models/workarea/payment/zipco_payment_integration_test.rb +89 -0
  104. data/test/services/workarea/zipco/checkout_test.rb +43 -0
  105. data/test/services/workarea/zipco/order_test.rb +102 -0
  106. data/test/services/workarea/zipco/setup_test.rb +65 -0
  107. data/test/teaspoon_env.rb +6 -0
  108. data/test/test_helper.rb +10 -0
  109. data/test/view_models/workarea/storefront/zipco_view_model_test.rb +55 -0
  110. data/workarea-zipco.gemspec +19 -0
  111. metadata +167 -0
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do
2
+ mount Workarea::Core::Engine => '/'
3
+ mount Workarea::Admin::Engine => '/admin', as: 'admin'
4
+ mount Workarea::Storefront::Engine => '/', as: 'storefront'
5
+ end
@@ -0,0 +1,6 @@
1
+ %w[
2
+ .ruby-version
3
+ .rbenv-vars
4
+ tmp/restart.txt
5
+ tmp/caching-dev.txt
6
+ ].each { |path| Spring.watch(path) }
@@ -0,0 +1,2 @@
1
+ require 'workarea/seeds'
2
+ Workarea::Seeds.run
File without changes
File without changes
@@ -0,0 +1,250 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Storefront
5
+ class ZipcoIntengrationTest < Workarea::IntegrationTest
6
+ setup :set_order_data
7
+
8
+ def set_order_data
9
+ create_tax_category(
10
+ name: 'Sales Tax',
11
+ code: '001',
12
+ rates: [{ percentage: 0.07, country: 'US', region: 'PA' }]
13
+ )
14
+
15
+ product = create_product(
16
+ variants: [{ sku: 'SKU1', regular: 6.to_m, tax_code: '001' }]
17
+ )
18
+
19
+ create_shipping_service(
20
+ carrier: 'UPS',
21
+ name: 'Ground',
22
+ service_code: '03',
23
+ tax_code: '001',
24
+ rates: [{ price: 7.to_m }]
25
+ )
26
+
27
+ post storefront.cart_items_path,
28
+ params: {
29
+ product_id: product.id,
30
+ sku: product.skus.first,
31
+ quantity: 2
32
+ }
33
+
34
+ patch storefront.checkout_addresses_path,
35
+ params: {
36
+ email: 'bcrouse@workarea.com',
37
+ billing_address: {
38
+ first_name: 'Ben',
39
+ last_name: 'Crouse',
40
+ street: '12 N. 3rd St.',
41
+ city: 'Philadelphia',
42
+ region: 'PA',
43
+ postal_code: '19106',
44
+ country: 'US',
45
+ phone_number: '2159251800'
46
+ },
47
+ shipping_address: {
48
+ first_name: 'Ben',
49
+ last_name: 'Crouse',
50
+ street: '22 S. 3rd St.',
51
+ city: 'Philadelphia',
52
+ region: 'PA',
53
+ postal_code: '19106',
54
+ country: 'US',
55
+ phone_number: '2159251800'
56
+ }
57
+ }
58
+ patch storefront.checkout_shipping_path
59
+ end
60
+
61
+ def test_start_adds_a_token_to_payment
62
+ get storefront.start_zipco_path
63
+
64
+ payment = Payment.find(order.id)
65
+ payment.reload
66
+
67
+ assert(payment.zipco.present?)
68
+
69
+ order.reload
70
+
71
+ assert(order.zipco_order_id.present?)
72
+ end
73
+
74
+ def test_start_clears_credit_card
75
+ payment = Payment.find(order.id)
76
+ payment.set_credit_card({
77
+ month: "01",
78
+ year: Time.now.year + 1,
79
+ number: 1,
80
+ cvv: "123"
81
+ })
82
+
83
+ payment.reload
84
+
85
+ get storefront.start_zipco_path
86
+
87
+ payment.reload
88
+ order.reload
89
+
90
+ refute(payment.credit_card.present?)
91
+ assert(payment.zipco.present?)
92
+ assert(order.zipco_order_id.present?)
93
+ end
94
+
95
+ def test_cancel_removes_zipco
96
+ payment = Payment.find(order.id)
97
+
98
+ order.update_attributes(zipco_order_id: '1234')
99
+
100
+ payment.set_zipco(token: '1234')
101
+
102
+ assert(payment.zipco?)
103
+
104
+ get storefront.complete_zipco_path(checkoutId: '1234', result: "cancelled")
105
+ payment.reload
106
+ order.reload
107
+
108
+ refute(payment.zipco?)
109
+ refute(order.zipco_order_id.present?)
110
+ end
111
+
112
+ def test_decline_removes_zipco
113
+ payment = Payment.find(order.id)
114
+
115
+ order.update_attributes(zipco_order_id: '1234')
116
+
117
+ payment.set_zipco(token: '1234')
118
+
119
+ assert(payment.zipco?)
120
+
121
+ get storefront.complete_zipco_path(checkoutId: '1234', result: "declined")
122
+ payment.reload
123
+ order.reload
124
+
125
+ refute(payment.zipco?)
126
+ refute(order.zipco_order_id.present?)
127
+ end
128
+
129
+ def test_placing_order_from_zipco
130
+ payment = Payment.find(order.id)
131
+
132
+ get storefront.start_zipco_path
133
+
134
+ params = { checkoutId: 'co_P9GOgSVE9qMnL0VA6Jy8z6', result: 'approved' }
135
+
136
+ get storefront.complete_zipco_path(params)
137
+
138
+ payment.reload
139
+ order.reload
140
+
141
+
142
+ assert(order.placed?)
143
+
144
+ transactions = payment.tenders.first.transactions
145
+ assert_equal(1, transactions.size)
146
+ assert(transactions.first.success?)
147
+ assert_equal('authorize', transactions.first.action)
148
+ end
149
+
150
+ def test_referred_then_approved_order_from_zipco
151
+ payment = Payment.find(order.id)
152
+
153
+ get storefront.start_zipco_path
154
+
155
+ params = { checkoutId: 'co_P9GOgSVE9qMnL0VA6Jy8z6', result: 'referred' }
156
+
157
+ get storefront.complete_zipco_path(params)
158
+
159
+ payment.reload
160
+ order.reload
161
+
162
+ assert(order.zipco_order_id.present?)
163
+ assert(:zipco_referred, order.status)
164
+ refute(order.placed?)
165
+
166
+ transactions = payment.tenders.first.transactions
167
+ assert_equal(0, transactions.size)
168
+ assert(order.zipco_referred?)
169
+ assert_equal(:pending, payment.status)
170
+
171
+ assert_redirected_to(storefront.checkout_confirmation_path)
172
+
173
+ params = { checkoutId: 'co_P9GOgSVE9qMnL0VA6Jy8z6', result: 'approved' }
174
+
175
+ get storefront.complete_zipco_path(params)
176
+
177
+ payment.reload
178
+ order.reload
179
+ transactions = payment.tenders.first.transactions
180
+
181
+ assert_equal(1, transactions.size)
182
+ assert(order.zipco_order_id.present?)
183
+ assert(order.placed?)
184
+ assert(:authorized, payment.status)
185
+ refute(order.zipco_referred?)
186
+ assert_redirected_to(storefront.checkout_confirmation_path)
187
+ end
188
+
189
+ def test_referred_then_cancelled_order_from_zipco
190
+ payment = Payment.find(order.id)
191
+
192
+ get storefront.start_zipco_path
193
+
194
+ params = { checkoutId: 'co_P9GOgSVE9qMnL0VA6Jy8z6', result: 'referred' }
195
+
196
+ get storefront.complete_zipco_path(params)
197
+
198
+ order.reload
199
+ payment.reload
200
+
201
+ assert(order.zipco_order_id.present?)
202
+ refute(order.placed?)
203
+
204
+ assert(order.zipco_referred?)
205
+ assert_equal(:pending, payment.status)
206
+
207
+ params = { checkoutId: 'co_P9GOgSVE9qMnL0VA6Jy8z6', result: 'declined' }
208
+
209
+ get storefront.complete_zipco_path(params)
210
+
211
+ payment.reload
212
+ order.reload
213
+
214
+ assert_equal(:canceled, order.status)
215
+
216
+ transactions = payment.tenders.first.transactions
217
+ assert(:pending, payment.status)
218
+ assert_equal(0, transactions.size)
219
+ assert_redirected_to(storefront.cart_path)
220
+ end
221
+
222
+ def test_failed_zipco_capture
223
+ payment = Payment.find(order.id)
224
+
225
+ payment.set_zipco(token: 'error_token')
226
+
227
+ params = { token: 'error_token', status: 'SUCCESS' }
228
+
229
+ get storefront.complete_zipco_path(params)
230
+
231
+ payment.reload
232
+ order.reload
233
+
234
+ refute(order.placed?)
235
+ end
236
+
237
+ private
238
+
239
+ def order
240
+ @order ||= Order.first
241
+ end
242
+
243
+ def product
244
+ @product ||= create_product(
245
+ variants: [{ sku: 'SKU1', regular: 6.to_m, tax_code: '001' }]
246
+ )
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Zipco
5
+ class ZipcoQueriesTest < Workarea::TestCase
6
+ attr_reader :email, :user
7
+
8
+ setup :setup_user
9
+
10
+ def setup_user
11
+ @email = 'bcrouse@workarea.com'
12
+ @user = create_user(email: email)
13
+ end
14
+
15
+ def test_referred_orders_do_not_need_reminding
16
+ referred_order = Workarea::Order.create!(email: email, checkout_started_at: 2.hours.ago, items: [{ product_id: '1', sku: 2 }], zipco_referred_at: Time.now)
17
+ results = Workarea::Order.need_reminding.to_a
18
+
19
+ refute_includes(results, referred_order)
20
+ end
21
+
22
+ def test_find_current_by_id_excludes_orders_under_review
23
+ order = create_order(zipco_referred_at: Time.now)
24
+
25
+ result = Workarea::Order.find_current(id: order.id)
26
+ refute result.persisted?
27
+ end
28
+
29
+ def test_find_current_by_user_id_excludes_orders_under_review
30
+ result = Workarea::Order.find_current(user_id: 'foo')
31
+ refute result.persisted?
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,89 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ class ZipcoPaymentIntegrationTest < Workarea::TestCase
5
+ def test_auth_capture
6
+ transaction = tender.build_transaction(action: 'authorize')
7
+ Payment::Purchase::Zipco.new(tender, transaction).complete!
8
+
9
+ assert(transaction.success?)
10
+ transaction.save!
11
+
12
+ assert(tender.token.present?)
13
+
14
+ capture = Payment::Capture.new(payment: payment)
15
+ capture.allocate_amounts!(total: 5.to_m)
16
+ assert(capture.valid?)
17
+ capture.complete!
18
+
19
+ capture_transaction = payment.transactions.detect(&:captures)
20
+ assert(capture_transaction.valid?)
21
+ end
22
+
23
+ def test_auth
24
+ transaction = tender.build_transaction(action: 'authorize')
25
+ Payment::Authorize::Zipco.new(tender, transaction).complete!
26
+ assert(transaction.success?, 'expected transaction to be successful')
27
+ end
28
+
29
+ def test_purchase
30
+ transaction = tender.build_transaction(action: 'purchase')
31
+ Payment::Purchase::Zipco.new(tender, transaction).complete!
32
+ assert(transaction.success?)
33
+ end
34
+
35
+ def test_timeout_auth
36
+ transaction = timeout_tender.build_transaction(action: 'authorize')
37
+ operation = Payment::Authorize::Zipco.new(timeout_tender, transaction)
38
+ operation.complete!
39
+ refute(transaction.success?, 'expected transaction to be a failure')
40
+ end
41
+
42
+ private
43
+
44
+ def payment
45
+ @payment ||=
46
+ begin
47
+ profile = create_payment_profile
48
+ create_payment(
49
+ profile_id: profile.id,
50
+ address: {
51
+ first_name: 'Ben',
52
+ last_name: 'Crouse',
53
+ street: '22 s. 3rd st.',
54
+ city: 'Philadelphia',
55
+ region: 'PA',
56
+ postal_code: '19106',
57
+ country: Country['US']
58
+ }
59
+ )
60
+ end
61
+ end
62
+
63
+ def tender
64
+ @tender ||=
65
+ begin
66
+ payment.set_address(first_name: 'Ben', last_name: 'Crouse')
67
+
68
+ payment.build_zipco(
69
+ token: '12345'
70
+ )
71
+
72
+ payment.zipco
73
+ end
74
+ end
75
+
76
+ def timeout_tender
77
+ @tender ||=
78
+ begin
79
+ payment.set_address(first_name: 'Ben', last_name: 'Crouse')
80
+
81
+ payment.build_zipco(
82
+ token: 'timeout_token'
83
+ )
84
+
85
+ payment.zipco
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Zipco
5
+ class CheckoutTest < Workarea::TestCase
6
+ setup :set_models
7
+
8
+ def set_models
9
+ @user = create_user
10
+ @product = create_product(variants: [{ sku: 'SKU', regular: 5.to_m }])
11
+ @order = create_order(zipco_order_id: "1234", items: [{ product_id: @product.id, sku: 'SKU' }])
12
+ @checkout = Workarea::Checkout.new(@order, @user)
13
+ end
14
+
15
+ def test_complete_approved
16
+ @checkout.payment.set_zipco(token: "1234")
17
+
18
+ @checkout.steps.each do |step|
19
+ step.any_instance.expects(:complete?).returns(true).at_least_once
20
+ end
21
+ Workarea::Checkout::ShippingOptions.any_instance.expects(:valid?).returns(true)
22
+
23
+ zipco_checkout = Zipco::Checkout.new(@checkout, @user, { result: "approved", checkoutId: "1234" })
24
+ complete = zipco_checkout.complete
25
+
26
+ assert(complete)
27
+ end
28
+
29
+ def test_complete_referred
30
+ @checkout.payment.set_zipco(token: "1234")
31
+
32
+ @checkout.steps.each do |step|
33
+ step.any_instance.expects(:complete?).returns(true).at_least_once
34
+ end
35
+ Workarea::Checkout::ShippingOptions.any_instance.expects(:valid?).returns(true)
36
+
37
+ zipco_checkout = Zipco::Checkout.new(@checkout, @user, { result: "referred", checkoutId: "1234" })
38
+ complete = zipco_checkout.complete
39
+ assert(complete)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,102 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Zipco
5
+ class OrderTest < Workarea::TestCase
6
+ def test_to_h
7
+ create_order_total_discount
8
+ order = create_placed_order
9
+ payment = Workarea::Payment.find(order.id)
10
+
11
+ payment = Workarea::Payment.find(order.id)
12
+ payment.profile.update_attributes!(store_credit: 2.00)
13
+ payment.set_store_credit
14
+ payment.tenders.first.amount = 2.to_m
15
+ payment.save
16
+
17
+ order.reload
18
+ payment.reload
19
+
20
+ order_hash = Workarea::Zipco::Order.new(order).to_h
21
+ items = order_hash[:order][:items]
22
+ assert_equal(5, items.size)
23
+
24
+ assert_equal(8.00, order_hash[:order][:amount].to_f)
25
+
26
+ discount_items = items.select { |i| i[:type] == "discount" }
27
+
28
+ assert_equal(2, discount_items.size)
29
+ discount_prices = discount_items.map { |d| d[:amount] }
30
+ assert(discount_prices.include? -1.0) # order total discount
31
+ assert(discount_prices.include? -2.0) # store credit
32
+
33
+ zipco_order = order_hash[:order]
34
+ assert_equal(order.id, zipco_order[:reference])
35
+ assert_equal("USD", zipco_order[:currency])
36
+
37
+ zipco_shipping = zipco_order[:shipping]
38
+ assert(zipco_shipping.present?)
39
+ assert(zipco_shipping[:address].present?)
40
+
41
+ item = order.items.first
42
+ item.adjust_pricing(
43
+ price: 'item',
44
+ quantity: 1,
45
+ description: "Gift Wrapping",
46
+ calculator: "Workarea::Pricing::Calculators::GiftWrappingCalculator",
47
+ amount: 5.to_m,
48
+ data: {}
49
+ )
50
+
51
+ order_hash = Workarea::Zipco::Order.new(order).to_h
52
+
53
+ gift_wrap_item = order_hash[:order][:items].detect { |i| i[:name] == "Gift Wrapping" }
54
+ assert(gift_wrap_item.present?)
55
+ assert_equal(5.0, gift_wrap_item[:amount])
56
+ end
57
+
58
+ private
59
+ def create_order(overrides = {})
60
+ attributes = {
61
+ id: '1234',
62
+ email: 'bcrouse-new@workarea.com',
63
+ placed_at: Time.current
64
+ }.merge(overrides)
65
+
66
+ shipping_service = create_shipping_service
67
+ product = create_product(variants: [{ sku: 'SKU', regular: 5.to_m }])
68
+
69
+ order = Workarea::Order.new(attributes)
70
+ order.add_item(product_id: product.id, sku: 'SKU', quantity: 2)
71
+
72
+ checkout = Checkout.new(order)
73
+ checkout.update(
74
+ shipping_address: {
75
+ first_name: 'Ben',
76
+ last_name: 'Crouse',
77
+ street: '22 S. 3rd St.',
78
+ street_2: 'Second Floor',
79
+ city: 'Philadelphia',
80
+ region: 'PA',
81
+ postal_code: '19106',
82
+ country: 'US'
83
+ },
84
+ billing_address: {
85
+ first_name: 'Bob',
86
+ last_name: 'Clams',
87
+ street: '12 N. 3rd St.',
88
+ street_2: 'Second Floor',
89
+ city: 'Wilmington',
90
+ region: 'DE',
91
+ postal_code: '18083',
92
+ country: 'US'
93
+ },
94
+ shipping_service: shipping_service.name,
95
+ )
96
+
97
+
98
+ order
99
+ end
100
+ end
101
+ end
102
+ end