├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── clients ├── base.rb ├── guest.rb └── json.rb ├── examples ├── base.rb ├── checkout │ ├── braintree.yml.example │ ├── creating_with_line_items.rb │ ├── switching_address_recalculates_taxes.rb │ ├── walkthrough.rb │ ├── walkthrough_as_guest.rb │ └── walkthrough_with_existing_credit_card.rb ├── guest.rb ├── images │ ├── image.jpg │ ├── product_image_creation.rb │ ├── thinking-cat.jpg │ └── variant_image_creation.rb ├── orders │ └── importing.rb └── products │ ├── crud.rb │ ├── listing.rb │ ├── searching.rb │ ├── update_taxons.rb │ └── variants │ └── crud.rb ├── setup.rb └── test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | examples/checkout/braintree.yml 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'pry' 4 | gem 'faraday' 5 | gem 'httpclient' 6 | gem 'json' 7 | gem 'colorize' 8 | gem 'braintree' 9 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | braintree (2.33.1) 5 | builder (>= 2.0.0) 6 | builder (3.2.2) 7 | coderay (1.1.0) 8 | colorize (0.7.3) 9 | faraday (0.9.0) 10 | multipart-post (>= 1.2, < 3) 11 | httpclient (2.4.0) 12 | json (1.8.1) 13 | method_source (0.8.2) 14 | multipart-post (2.0.0) 15 | pry (0.10.0) 16 | coderay (~> 1.1.0) 17 | method_source (~> 0.8.1) 18 | slop (~> 3.4) 19 | slop (3.6.0) 20 | 21 | PLATFORMS 22 | ruby 23 | 24 | DEPENDENCIES 25 | braintree 26 | colorize 27 | faraday 28 | httpclient 29 | json 30 | pry 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spree API Examples 2 | 3 | In this repo, you can expect to find some examples of how to interact with Spree's API. 4 | 5 | All examples use the Faraday and httpclient gems, as these provide (imo) the best Ruby HTTP API available. Your mileage may vary if you choose to use other gems. 6 | 7 | There are not that many examples at the moment. This will change, over time. You can help it change by submitting pull requests to this repo. 8 | 9 | ## Do these examples work? 10 | 11 | Sure they do! 12 | 13 | There's a catch, though: you have to run them against a sample store setup using Spree's sample data. You can set one up by installing Spree and then running these commands: 14 | 15 | bundle exec rake db:reset spree_sample:load AUTO_ACCEPT=1 16 | 17 | Which is the equivalant of running: 18 | 19 | bundle exec rake db:drop 20 | bundle exec rake db:create 21 | bundle exec rake db:migrate 22 | bundle exec rake db:seed AUTO_ACCEPT=1 23 | bundle exec rake spree_sample:load --trace 24 | 25 | These commands (of course) will kill everything you know and love in your database. If you don't care about the data in your database, run them. 26 | 27 | These commands will give you *most* of what you need. One final thing: 28 | 29 | rails console 30 | Spree::User.first.update_column(:spree_api_key, 'fake') 31 | 32 | **Now you're ready to run the tests and be MEGA SUCCESSFUL**. 33 | 34 | You can run them in two ways: 35 | 36 | ruby test.rb # this will run them all 37 | ruby examples/checkout/walkthrough.rb # this will run just the one 38 | 39 | ## Guest Examples 40 | 41 | In order to run guest examples that don't require an API key you must disable API authentication in `config/initializers/spree.rb`: 42 | 43 | ``` 44 | Spree::Api::Config[:requires_authentication] = false 45 | ``` 46 | 47 | ## License 48 | 49 | Do whatever you want with the source code. 50 | 51 | Please tell me if you find it useful. I'm reachable through the usual means. 52 | 53 | No implied warranty. 54 | 55 | Batteries not included. May dissolve in water. Keep out of reach of children. 56 | -------------------------------------------------------------------------------- /clients/base.rb: -------------------------------------------------------------------------------- 1 | require_relative '../setup' 2 | 3 | module Clients 4 | class Base 5 | attr_accessor :conn, :last_response 6 | 7 | def initialize 8 | options = { 9 | url: 'http://localhost:3000', 10 | params: { token: 'fake' } 11 | } 12 | @conn = Faraday.new(options) do |f| 13 | f.request :multipart 14 | f.request :url_encoded 15 | f.adapter :httpclient 16 | end 17 | end 18 | 19 | def get(url, params={}) 20 | @last_response = conn.get(url, params) 21 | end 22 | 23 | def post(url, params={}) 24 | @last_response = conn.post(url, params) 25 | end 26 | 27 | def put(url, params={}) 28 | @last_response = conn.put(url, params) 29 | end 30 | 31 | def delete(url, params={}) 32 | @last_response = conn.delete(url, params) 33 | end 34 | 35 | def succeeded(message) 36 | puts "[SUCCESS] #{message}".green 37 | end 38 | 39 | def failed(message) 40 | puts "[FAILURE] #{message} (#{last_response.status})".red 41 | errors = ::JSON.parse(last_response.body) 42 | if errors['error'] 43 | puts errors['error'] 44 | puts errors['errors'] 45 | end 46 | puts "Error occurred at: " + caller[0] 47 | exit(1) 48 | end 49 | 50 | def pending(message) 51 | puts "[PENDING] #{message}".yellow 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /clients/guest.rb: -------------------------------------------------------------------------------- 1 | require_relative '../setup' 2 | 3 | module Clients 4 | class Guest 5 | attr_accessor :conn, :last_response 6 | 7 | def initialize 8 | options = { 9 | url: 'http://localhost:3000' 10 | } 11 | @conn = Faraday.new(options) do |f| 12 | f.request :multipart 13 | f.request :url_encoded 14 | f.adapter :httpclient 15 | end 16 | end 17 | 18 | def get(url, params={}) 19 | @last_response = conn.get(url, params) 20 | end 21 | 22 | def post(url, params={}) 23 | @last_response = conn.post(url, params) 24 | end 25 | 26 | def put(url, params={}) 27 | @last_response = conn.put(url, params) 28 | end 29 | 30 | def delete(url, params={}) 31 | @last_response = conn.delete(url, params) 32 | end 33 | 34 | def succeeded(message) 35 | puts "[SUCCESS] #{message}".green 36 | end 37 | 38 | def failed(message) 39 | puts "[FAILURE] #{message} (#{last_response.status})".red 40 | errors = ::JSON.parse(last_response.body) 41 | if errors['error'] 42 | puts errors['error'] 43 | puts errors['errors'] 44 | end 45 | puts "Error occurred at: " + caller[0] 46 | exit(1) 47 | end 48 | 49 | def pending(message) 50 | puts "[PENDING] #{message}".yellow 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /clients/json.rb: -------------------------------------------------------------------------------- 1 | require_relative 'base' 2 | 3 | module Clients 4 | class JSON < Base 5 | 6 | def post(url, params={}) 7 | @last_response = conn.post do |req| 8 | req.url url 9 | req.headers['Content-Type'] = 'application/json' 10 | req.body = params.to_json 11 | end 12 | end 13 | 14 | def put(url, params={}) 15 | @last_response = conn.put do |req| 16 | req.url url 17 | req.headers['Content-Type'] = 'application/json' 18 | req.body = params.to_json 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /examples/base.rb: -------------------------------------------------------------------------------- 1 | require_relative '../clients/base' 2 | require_relative '../clients/json' 3 | 4 | module Examples 5 | def self.run(example) 6 | clients = [] 7 | clients << Clients::Base.new 8 | clients << Clients::JSON.new 9 | 10 | clients.each do |client| 11 | message = "Running #{example.name} with #{client.class.name}" 12 | puts message 13 | puts "-" * message.length 14 | 15 | example.run(client) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /examples/checkout/braintree.yml.example: -------------------------------------------------------------------------------- 1 | environment: sandbox 2 | merchant_id: 3 | public_key: 4 | private_key: -------------------------------------------------------------------------------- /examples/checkout/creating_with_line_items.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Checkout 5 | class CreatingWithLineItems 6 | def self.run(client) 7 | response = client.post('/api/orders', { 8 | order: { 9 | # Repeat the elements in this hash for as many line items as you please. 10 | line_items: { 11 | "0" => { 12 | variant_id: 1, 13 | quantity: 5 14 | } 15 | } 16 | } 17 | }) 18 | 19 | if response.status == 201 20 | order = JSON.parse(response.body) 21 | if order['line_items'].count == 1 22 | client.succeeded 'Order with line items created successfully.' 23 | else 24 | client.failed 'Order was created, but no line items were present.' 25 | end 26 | else 27 | client.failed 'Order could not be created.' 28 | end 29 | end 30 | end 31 | end 32 | end 33 | 34 | Examples.run(Examples::Checkout::CreatingWithLineItems) 35 | -------------------------------------------------------------------------------- /examples/checkout/switching_address_recalculates_taxes.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Checkout 5 | class SwitchingAddressRecalculatesTaxes 6 | def self.run(client) 7 | 8 | if Clients::JSON === client 9 | client.pending "SwitchingAddressRecalculatesTaxes does not work with JSON client." 10 | return 11 | end 12 | 13 | # Create the order step by step: 14 | # You may also choose to start it off with some line items 15 | # See checkout/creating_with_line_items.rb 16 | 17 | response = client.post('/api/orders', {}) 18 | 19 | if response.status == 201 20 | client.succeeded "Created new checkout." 21 | order = JSON.parse(response.body) 22 | if order['email'] == 'spree@example.com' 23 | # Email addresses are necessary for orders to transition to address. 24 | # This just makes really sure that the email is already set. 25 | # You will not have to do this in your own API unless you've customized it. 26 | client.succeeded 'Email set automatically on order successfully.' 27 | else 28 | client.failed %Q{ 29 | Email address was not automatically set on order.' 30 | -> This may lead to problems transitioning to the address step. 31 | } 32 | end 33 | else 34 | client.failed 'Failed to create a new blank checkout.' 35 | end 36 | 37 | # Assign a line item to the order we just created. 38 | 39 | response = client.post("/api/orders/#{order['number']}/line_items", 40 | { 41 | line_item: { 42 | variant_id: 1, 43 | quantity: 1 44 | } 45 | } 46 | ) 47 | 48 | 49 | if response.status == 201 50 | client.succeeded "Added a line item." 51 | else 52 | client.failed "Failed to add a line item." 53 | end 54 | 55 | # Transition the order to the 'address' state 56 | response = client.put("/api/checkouts/#{order['number']}/next") 57 | if response.status == 200 58 | order = JSON.parse(response.body) 59 | client.succeeded "Transitioned order into address state." 60 | else 61 | client.failed "Could not transition order to address state." 62 | end 63 | 64 | # Add address information to the order 65 | # Before you make this request, you may need to make a request to one or both of: 66 | # - /api/countries 67 | # - /api/states 68 | # This will give you the correct country_id and state_id params to use for address information. 69 | 70 | # First, get the country: 71 | response = client.get('/api/countries?q[name_cont]=United') 72 | if response.status == 200 73 | client.succeeded "Retrieved a list of countries." 74 | countries = JSON.parse(response.body)['countries'] 75 | 76 | usa = countries.detect { |c| c['name'] == 'United States' } 77 | if usa.nil? 78 | client.failed "Expected 'United States' be returned when querying countries by 'United'." 79 | end 80 | 81 | uk = countries.detect { |c| c['name'] == 'United Kingdom' } 82 | if uk.nil? 83 | client.failed "Expected 'United Kingdom' be returned when querying countries by 'United'." 84 | end 85 | else 86 | client.failed "Failed to retrieve a list of countries." 87 | end 88 | 89 | # Then, get the state we want from the states of that country: 90 | 91 | response = client.get("/api/countries/#{usa['id']}/states") 92 | if response.status == 200 93 | client.succeeded "Retrieved a list of states." 94 | states = JSON.parse(response.body)['states'] 95 | taxed_state_id = states.detect { |s| s["name"] == "Minnesota" }["id"] 96 | untaxed_state_id = states.detect { |s| s["name"] == "New Hampshire" }["id"] 97 | else 98 | client.failed "Failed to retrieve a list of states." 99 | end 100 | 101 | # We can finally submit some address information now that we have it all: 102 | taxed_address = { 103 | first_name: 'Test', 104 | last_name: 'User', 105 | address1: '15 Kellogg Blvd West', 106 | address2: '', 107 | country_id: usa['id'], 108 | state_id: taxed_state_id, 109 | city: 'St Paul', 110 | zipcode: '55102', 111 | phone: '(555) 555-5555' 112 | } 113 | 114 | untaxed_address = { 115 | first_name: 'Test', 116 | last_name: 'User', 117 | address1: '1500 South Willow Street', 118 | address2: '', 119 | country_id: uk['id'], 120 | state_id: nil, 121 | state_name: 'London', 122 | city: 'London', 123 | zipcode: '03103', 124 | phone: '(555) 555-5555' 125 | } 126 | 127 | response = client.put("/api/checkouts/#{order['number']}", 128 | { 129 | order: { 130 | bill_address_attributes: taxed_address, 131 | ship_address_attributes: taxed_address 132 | } 133 | }) 134 | 135 | order = JSON.parse(response.body) 136 | puts "Taxes calculated for taxable state: " + order['display_tax_total'] 137 | # if (order['tax_total'].to_f == 0) 138 | # client.failed "Taxes should not be zero." 139 | # end 140 | 141 | response = client.put("/api/checkouts/#{order['number']}", 142 | { 143 | order: { 144 | bill_address_attributes: untaxed_address, 145 | ship_address_attributes: untaxed_address 146 | } 147 | }) 148 | 149 | order = JSON.parse(response.body) 150 | puts "Taxes calculated for untaxable state: #{order['display_tax_total']}" 151 | if (order['tax_total'].to_f != 0) 152 | client.failed "Taxes should be zero." 153 | end 154 | end 155 | end 156 | end 157 | end 158 | 159 | Examples.run(Examples::Checkout::SwitchingAddressRecalculatesTaxes) 160 | -------------------------------------------------------------------------------- /examples/checkout/walkthrough.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Checkout 5 | class Walkthrough 6 | def self.run(client) 7 | if Clients::JSON === client 8 | client.pending "Checkout::Walkthrough does not work with JSON client." 9 | return 10 | end 11 | 12 | # Create the order step by step: 13 | # You may also choose to start it off with some line items 14 | # See checkout/creating_with_line_items.rb 15 | response = client.post('/api/v1/orders') 16 | 17 | if response.status == 201 18 | client.succeeded "Created new checkout." 19 | order = JSON.parse(response.body) 20 | if order['email'] == 'spree@example.com' 21 | # Email addresses are necessary for orders to transition to address. 22 | # This just makes really sure that the email is already set. 23 | # You will not have to do this in your own API unless you've customized it. 24 | client.succeeded 'Email set automatically on order successfully.' 25 | else 26 | client.failed %Q{ 27 | Email address was not automatically set on order.' 28 | -> This may lead to problems transitioning to the address step. 29 | } 30 | end 31 | else 32 | client.failed 'Failed to create a new blank checkout.' 33 | end 34 | 35 | # Assign a line item to the order we just created. 36 | 37 | response = client.post("/api/v1/orders/#{order['number']}/line_items", 38 | { 39 | line_item: { 40 | variant_id: 1, 41 | quantity: 1 42 | } 43 | } 44 | ) 45 | 46 | 47 | if response.status == 201 48 | client.succeeded "Added a line item." 49 | else 50 | client.failed "Failed to add a line item." 51 | end 52 | 53 | # Transition the order to the 'address' state 54 | response = client.put("/api/v1/checkouts/#{order['number']}/next") 55 | if response.status == 200 56 | order = JSON.parse(response.body) 57 | client.succeeded "Transitioned order into address state." 58 | else 59 | client.failed "Could not transition order to address state." 60 | end 61 | 62 | # Add address information to the order 63 | # Before you make this request, you may need to make a request to one or both of: 64 | # - /api/countries 65 | # - /api/states 66 | # This will give you the correct country_id and state_id params to use for address information. 67 | 68 | # First, get the country: 69 | response = client.get('/api/v1/countries?q[name_cont]=United States') 70 | if response.status == 200 71 | client.succeeded "Retrieved a list of countries." 72 | countries = JSON.parse(response.body)['countries'] 73 | usa = countries.first 74 | if usa['name'] != 'United States' 75 | client.failed "Expected first country to be 'United States', but it wasn't." 76 | end 77 | else 78 | client.failed "Failed to retrieve a list of countries." 79 | end 80 | 81 | # Then, get the state we want from the states of that country: 82 | 83 | response = client.get("/api/v1/countries/#{usa['id']}/states?q[name_cont]=Maryland") 84 | if response.status == 200 85 | client.succeeded "Retrieved a list of states." 86 | states = JSON.parse(response.body)['states'] 87 | maryland = states.first 88 | if maryland['name'] != 'Maryland' 89 | client.failed "Expected first state to be 'Maryland', but it wasn't." 90 | end 91 | else 92 | client.failed "Failed to retrieve a list of states." 93 | end 94 | 95 | # We can finally submit some address information now that we have it all: 96 | 97 | address = { 98 | first_name: 'Test', 99 | last_name: 'User', 100 | address1: 'Unit 1', 101 | address2: '1 Test Lane', 102 | country_id: usa['id'], 103 | state_id: maryland['id'], 104 | city: 'Bethesda', 105 | zipcode: '20814', 106 | phone: '(555) 555-5555' 107 | } 108 | 109 | response = client.put("/api/v1/checkouts/#{order['number']}", 110 | { 111 | order: { 112 | bill_address_attributes: address, 113 | ship_address_attributes: address 114 | } 115 | } 116 | ) 117 | 118 | if response.status == 200 119 | client.succeeded "Address details added." 120 | order = JSON.parse(response.body) 121 | if order['state'] == 'delivery' 122 | client.succeeded "Order automatically transitioned to 'delivery'." 123 | else 124 | client.failed "Order failed to automatically transition to 'delivery'." 125 | end 126 | else 127 | client.failed "Could not add address details to order." 128 | end 129 | 130 | # Next step: delivery! 131 | 132 | first_shipment = order['shipments'].first 133 | response = client.put("/api/v1/checkouts/#{order['number']}", 134 | { 135 | order: { 136 | shipments_attributes: [ 137 | id: first_shipment['id'], 138 | selected_shipping_rate_id: first_shipment['shipping_rates'].first['id'] 139 | ] 140 | } 141 | } 142 | ) 143 | 144 | if response.status == 200 145 | client.succeeded "Delivery options selected." 146 | order = JSON.parse(response.body) 147 | if order['state'] == 'payment' 148 | client.succeeded "Order automatically transitioned to 'payment'." 149 | else 150 | client.failed "Order failed to automatically transition to 'payment'." 151 | end 152 | else 153 | client.failed "The store was not happy with the selected delivery options." 154 | end 155 | 156 | # Next step: payment! 157 | 158 | # First up: a credit card payment 159 | credit_card_payment_method = order['payment_methods'].detect { |pm| pm['name'] == "Credit Card" } 160 | 161 | response = client.put("/api/v1/checkouts/#{order['number']}", 162 | { 163 | order: { 164 | payments_attributes: [{ 165 | payment_method_id: credit_card_payment_method['id'] 166 | }], 167 | }, 168 | payment_source: { 169 | credit_card_payment_method['id'] => { 170 | number: '1', # just a nonsense one. Will work with dummy CC gateway 171 | month: '1', 172 | year: '2017', 173 | verification_value: '123', 174 | name: 'John Smith', 175 | } 176 | } 177 | }) 178 | 179 | if response.status == 200 180 | order = JSON.parse(response.body) 181 | client.succeeded "Payment details provided for the order." 182 | # Order will transition to the confirm state only if the selected payment 183 | # method allows for payment profiles. 184 | # The dummy Credit Card gateway in Spree does, so confirm is shown for this order. 185 | if order['state'] == 'confirm' 186 | client.succeeded "Order automatically transitioned to 'confirm'." 187 | else 188 | client.failed "Order did not transition automatically to 'confirm'." 189 | end 190 | else 191 | client.failed "Payment details were not accepted for the order." 192 | end 193 | 194 | # Test for #4927 195 | response = client.put("/api/v1/checkouts/#{order['number']}", 196 | { 197 | order: { 198 | payments_attributes: [{ 199 | payment_method_id: credit_card_payment_method['id'] 200 | }], 201 | }, 202 | payment_source: { 203 | credit_card_payment_method['id'] => { 204 | number: '1', # just a nonsense one. Will work with dummy CC gateway 205 | month: '1', 206 | year: '2017', 207 | verification_value: '123', 208 | name: 'John Smith', 209 | } 210 | }, 211 | state: 'payment' 212 | }) 213 | 214 | if response.status == 200 215 | order = JSON.parse(response.body) 216 | client.succeeded "Second payment added to order." 217 | if order['state'] == 'confirm' 218 | client.succeeded "Order automatically transitioned to 'confirm' again." 219 | else 220 | client.failed "Order did not transition automatically to 'confirm'." 221 | end 222 | 223 | payments = order['payments'].sort { |p1, p2| p1["id"] <=> p2["id"] } 224 | if payments.first["state"] == "invalid" 225 | client.succeeded "First payment has been marked as invalid." 226 | else 227 | client.failed "First payment is not invalid, is #{payments.first["state"]} instead." 228 | end 229 | 230 | if payments.last["state"] == "checkout" 231 | client.succeeded "Second payment is in checkout state." 232 | else 233 | client.failed "Second payment is not in checkout, is #{payments.last["state"]} instead." 234 | end 235 | else 236 | client.failed "Payment details were not accepted for the order." 237 | end 238 | 239 | # This is the final point where the user gets to view their order's final information. 240 | # All that's required at this point is that we complete the order, which is as easy as: 241 | 242 | response = client.put("/api/v1/checkouts/#{order['number']}/next") 243 | if response.status == 200 244 | order = JSON.parse(response.body) 245 | if order['state'] == 'complete' 246 | client.succeeded "Order complete!" 247 | else 248 | client.failed "Order did not complete." 249 | end 250 | else 251 | client.failed "Order could not transition to 'complete'." 252 | end 253 | end 254 | end 255 | end 256 | end 257 | 258 | Examples.run(Examples::Checkout::Walkthrough) 259 | -------------------------------------------------------------------------------- /examples/checkout/walkthrough_as_guest.rb: -------------------------------------------------------------------------------- 1 | require_relative '../guest' 2 | 3 | module Examples 4 | module Checkout 5 | class WalkthroughAsGuest 6 | def self.run(client) 7 | # Create the order step by step: 8 | # You may also choose to start it off with some line items 9 | # See checkout/creating_with_line_items.rb 10 | response = client.post('/api/v1/orders?order[email]=test@example.com') 11 | 12 | if response.status == 201 13 | client.succeeded "Created new checkout." 14 | order = JSON.parse(response.body) 15 | if order['email'] == 'test@example.com' 16 | # Email addresses are necessary for orders to transition to address. 17 | # This just makes really sure that the email is already set. 18 | # You will not have to do this in your own API unless you've customized it. 19 | client.succeeded 'Email set automatically on order successfully.' 20 | else 21 | client.failed %Q{ 22 | Email address was not automatically set on order.' 23 | -> This may lead to problems transitioning to the address step. 24 | } 25 | end 26 | else 27 | client.failed 'Failed to create a new blank checkout.' 28 | end 29 | 30 | # Assign a line item to the order we just created. 31 | 32 | response = client.post("/api/v1/orders/#{order['number']}/line_items", 33 | { 34 | line_item: { 35 | variant_id: 1, 36 | quantity: 1 37 | }, 38 | order_token: order['token'] 39 | } 40 | ) 41 | 42 | 43 | if response.status == 201 44 | client.succeeded "Added a line item." 45 | else 46 | client.failed "Failed to add a line item." 47 | end 48 | 49 | # Transition the order to the 'address' state 50 | response = client.put("/api/v1/checkouts/#{order['number']}/next?order_token=#{order['token']}") 51 | if response.status == 200 52 | order = JSON.parse(response.body) 53 | client.succeeded "Transitioned order into address state." 54 | else 55 | client.failed "Could not transition order to address state." 56 | end 57 | 58 | # Add address information to the order 59 | # Before you make this request, you may need to make a request to one or both of: 60 | # - /api/countries 61 | # - /api/states 62 | # This will give you the correct country_id and state_id params to use for address information. 63 | 64 | # First, get the country: 65 | response = client.get('/api/v1/countries?q[name_cont]=United States') 66 | if response.status == 200 67 | client.succeeded "Retrieved a list of countries." 68 | countries = JSON.parse(response.body)['countries'] 69 | usa = countries.first 70 | if usa['name'] != 'United States' 71 | client.failed "Expected first country to be 'United States', but it wasn't." 72 | end 73 | else 74 | client.failed "Failed to retrieve a list of countries." 75 | end 76 | 77 | # Then, get the state we want from the states of that country: 78 | 79 | response = client.get("/api/v1/countries/#{usa['id']}/states?q[name_cont]=Maryland") 80 | if response.status == 200 81 | client.succeeded "Retrieved a list of states." 82 | states = JSON.parse(response.body)['states'] 83 | maryland = states.first 84 | if maryland['name'] != 'Maryland' 85 | client.failed "Expected first state to be 'Maryland', but it wasn't." 86 | end 87 | else 88 | client.failed "Failed to retrieve a list of states." 89 | end 90 | 91 | # We can finally submit some address information now that we have it all: 92 | 93 | address = { 94 | first_name: 'Test', 95 | last_name: 'User', 96 | address1: 'Unit 1', 97 | address2: '1 Test Lane', 98 | country_id: usa['id'], 99 | state_id: maryland['id'], 100 | city: 'Bethesda', 101 | zipcode: '20814', 102 | phone: '(555) 555-5555' 103 | } 104 | 105 | response = client.put("/api/v1/checkouts/#{order['number']}", 106 | { 107 | order: { 108 | bill_address_attributes: address, 109 | ship_address_attributes: address 110 | }, 111 | order_token: order['token'] 112 | } 113 | ) 114 | 115 | if response.status == 200 116 | client.succeeded "Address details added." 117 | order = JSON.parse(response.body) 118 | if order['state'] == 'delivery' 119 | client.succeeded "Order automatically transitioned to 'delivery'." 120 | else 121 | client.failed "Order failed to automatically transition to 'delivery'." 122 | end 123 | else 124 | client.failed "Could not add address details to order." 125 | end 126 | 127 | # Next step: delivery! 128 | 129 | first_shipment = order['shipments'].first 130 | response = client.put("/api/v1/checkouts/#{order['number']}", 131 | { 132 | order: { 133 | shipments_attributes: [ 134 | id: first_shipment['id'], 135 | selected_shipping_rate_id: first_shipment['shipping_rates'].first['id'] 136 | ] 137 | }, 138 | order_token: order['token'] 139 | } 140 | ) 141 | 142 | if response.status == 200 143 | client.succeeded "Delivery options selected." 144 | order = JSON.parse(response.body) 145 | if order['state'] == 'payment' 146 | client.succeeded "Order automatically transitioned to 'payment'." 147 | else 148 | client.failed "Order failed to automatically transition to 'payment'." 149 | end 150 | else 151 | client.failed "The store was not happy with the selected delivery options." 152 | end 153 | 154 | # Next step: payment! 155 | 156 | # First up: a credit card payment 157 | credit_card_payment_method = order['payment_methods'].detect { |pm| pm['name'] == "Credit Card" } 158 | 159 | response = client.put("/api/v1/checkouts/#{order['number']}", 160 | { 161 | order: { 162 | payments_attributes: [{ 163 | payment_method_id: credit_card_payment_method['id'] 164 | }], 165 | }, 166 | order_token: order['token'], 167 | payment_source: { 168 | credit_card_payment_method['id'] => { 169 | number: '1', # just a nonsense one. Will work with dummy CC gateway 170 | month: '1', 171 | year: '2017', 172 | verification_value: '123', 173 | name: 'John Smith', 174 | } 175 | } 176 | }) 177 | 178 | if response.status == 200 179 | order = JSON.parse(response.body) 180 | client.succeeded "Payment details provided for the order." 181 | # Order will transition to the confirm state only if the selected payment 182 | # method allows for payment profiles. 183 | # The dummy Credit Card gateway in Spree does, so confirm is shown for this order. 184 | if order['state'] == 'confirm' 185 | client.succeeded "Order automatically transitioned to 'confirm'." 186 | else 187 | client.failed "Order did not transition automatically to 'confirm'." 188 | end 189 | else 190 | client.failed "Payment details were not accepted for the order." 191 | end 192 | 193 | # Test for #4927 194 | response = client.put("/api/v1/checkouts/#{order['number']}", 195 | { 196 | order: { 197 | payments_attributes: [{ 198 | payment_method_id: credit_card_payment_method['id'] 199 | }], 200 | }, 201 | order_token: order['token'], 202 | payment_source: { 203 | credit_card_payment_method['id'] => { 204 | number: '1', # just a nonsense one. Will work with dummy CC gateway 205 | month: '1', 206 | year: '2017', 207 | verification_value: '123', 208 | name: 'John Smith', 209 | } 210 | }, 211 | state: 'payment' 212 | }) 213 | 214 | if response.status == 200 215 | order = JSON.parse(response.body) 216 | client.succeeded "Second payment added to order." 217 | if order['state'] == 'confirm' 218 | client.succeeded "Order automatically transitioned to 'confirm' again." 219 | else 220 | client.failed "Order did not transition automatically to 'confirm'." 221 | end 222 | 223 | payments = order['payments'].sort { |p1, p2| p1["id"] <=> p2["id"] } 224 | if payments.first["state"] == "invalid" 225 | client.succeeded "First payment has been marked as invalid." 226 | else 227 | client.failed "First payment is not invalid, is #{payments.first["state"]} instead." 228 | end 229 | 230 | if payments.last["state"] == "checkout" 231 | client.succeeded "Second payment is in checkout state." 232 | else 233 | client.failed "Second payment is not in checkout, is #{payments.last["state"]} instead." 234 | end 235 | else 236 | client.failed "Payment details were not accepted for the order." 237 | end 238 | 239 | # This is the final point where the user gets to view their order's final information. 240 | # All that's required at this point is that we complete the order, which is as easy as: 241 | 242 | response = client.put("/api/v1/checkouts/#{order['number']}/next?order_token=#{order['token']}") 243 | if response.status == 200 244 | order = JSON.parse(response.body) 245 | if order['state'] == 'complete' 246 | client.succeeded "Order complete!" 247 | else 248 | client.failed "Order did not complete." 249 | end 250 | else 251 | client.failed "Order could not transition to 'complete'." 252 | end 253 | end 254 | end 255 | end 256 | end 257 | 258 | Examples.run(Examples::Checkout::WalkthroughAsGuest) 259 | -------------------------------------------------------------------------------- /examples/checkout/walkthrough_with_existing_credit_card.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | require 'braintree' 3 | require 'yaml' 4 | 5 | module Examples 6 | module Checkout 7 | class WalkthroughWithExistingCreditCard 8 | def self.run(client) 9 | if Clients::JSON === client 10 | client.pending "Checkout::WalkthroughWithExistingCreditCard does not work with JSON client." 11 | return 12 | end 13 | 14 | # Create the order step by step: 15 | # You may also choose to start it off with some line items 16 | # See checkout/creating_with_line_items.rb 17 | response = client.post('/api/orders') 18 | 19 | if response.status == 201 20 | client.succeeded "Created new checkout." 21 | order = JSON.parse(response.body) 22 | if order['email'] == 'spree@example.com' 23 | # Email addresses are necessary for orders to transition to address. 24 | # This just makes really sure that the email is already set. 25 | # You will not have to do this in your own API unless you've customized it. 26 | client.succeeded 'Email set automatically on order successfully.' 27 | else 28 | client.failed %Q{ 29 | Email address was not automatically set on order.' 30 | -> This may lead to problems transitioning to the address step. 31 | } 32 | end 33 | else 34 | client.failed 'Failed to create a new blank checkout.' 35 | end 36 | 37 | # Assign a line item to the order we just created. 38 | 39 | response = client.post("/api/orders/#{order['number']}/line_items", 40 | { 41 | line_item: { 42 | variant_id: 1, 43 | quantity: 1 44 | } 45 | } 46 | ) 47 | 48 | 49 | if response.status == 201 50 | client.succeeded "Added a line item." 51 | else 52 | client.failed "Failed to add a line item." 53 | end 54 | 55 | # Transition the order to the 'address' state 56 | response = client.put("/api/checkouts/#{order['number']}/next") 57 | if response.status == 200 58 | order = JSON.parse(response.body) 59 | client.succeeded "Transitioned order into address state." 60 | else 61 | client.failed "Could not transition order to address state." 62 | end 63 | 64 | # Add address information to the order 65 | # Before you make this request, you may need to make a request to one or both of: 66 | # - /api/countries 67 | # - /api/states 68 | # This will give you the correct country_id and state_id params to use for address information. 69 | 70 | # First, get the country: 71 | response = client.get('/api/countries?q[name_cont]=United States') 72 | if response.status == 200 73 | client.succeeded "Retrieved a list of countries." 74 | countries = JSON.parse(response.body)['countries'] 75 | usa = countries.first 76 | if usa['name'] != 'United States' 77 | client.failed "Expected first country to be 'United States', but it wasn't." 78 | end 79 | else 80 | client.failed "Failed to retrieve a list of countries." 81 | end 82 | 83 | # Then, get the state we want from the states of that country: 84 | 85 | response = client.get("/api/countries/#{usa['id']}/states?q[name_cont]=Maryland") 86 | if response.status == 200 87 | client.succeeded "Retrieved a list of states." 88 | states = JSON.parse(response.body)['states'] 89 | maryland = states.first 90 | if maryland['name'] != 'Maryland' 91 | client.failed "Expected first state to be 'Maryland', but it wasn't." 92 | end 93 | else 94 | client.failed "Failed to retrieve a list of states." 95 | end 96 | 97 | # We can finally submit some address information now that we have it all: 98 | 99 | address = { 100 | first_name: 'Test', 101 | last_name: 'User', 102 | address1: 'Unit 1', 103 | address2: '1 Test Lane', 104 | country_id: usa['id'], 105 | state_id: maryland['id'], 106 | city: 'Bethesda', 107 | zipcode: '20814', 108 | phone: '(555) 555-5555' 109 | } 110 | 111 | response = client.put("/api/checkouts/#{order['number']}", 112 | { 113 | order: { 114 | bill_address_attributes: address, 115 | ship_address_attributes: address 116 | } 117 | } 118 | ) 119 | 120 | if response.status == 200 121 | client.succeeded "Address details added." 122 | order = JSON.parse(response.body) 123 | if order['state'] == 'delivery' 124 | client.succeeded "Order automatically transitioned to 'delivery'." 125 | else 126 | client.failed "Order failed to automatically transition to 'delivery'." 127 | end 128 | else 129 | client.failed "Could not add address details to order." 130 | end 131 | 132 | # Next step: delivery! 133 | 134 | first_shipment = order['shipments'].first 135 | response = client.put("/api/checkouts/#{order['number']}", 136 | { 137 | order: { 138 | shipments_attributes: [ 139 | id: first_shipment['id'], 140 | selected_shipping_rate_id: first_shipment['shipping_rates'].first['id'] 141 | ] 142 | } 143 | } 144 | ) 145 | 146 | if response.status == 200 147 | client.succeeded "Delivery options selected." 148 | order = JSON.parse(response.body) 149 | if order['state'] == 'payment' 150 | client.succeeded "Order automatically transitioned to 'payment'." 151 | else 152 | client.failed "Order failed to automatically transition to 'payment'." 153 | end 154 | else 155 | client.failed "The store was not happy with the selected delivery options." 156 | end 157 | 158 | # Next step: payment! 159 | 160 | # First up: a credit card payment 161 | credit_card_payment_method = order['payment_methods'].detect { |pm| pm['name'] == "Credit Card" } 162 | 163 | response = client.put("/api/checkouts/#{order['number']}", 164 | { 165 | order: { 166 | payments_attributes: [{ 167 | payment_method_id: credit_card_payment_method['id'] 168 | }], 169 | }, 170 | payment_source: { 171 | credit_card_payment_method['id'] => { 172 | number: '1', # just a nonsense one. Will work with dummy CC gateway 173 | month: '1', 174 | year: '2017', 175 | verification_value: '123', 176 | name: 'John Smith', 177 | } 178 | } 179 | }) 180 | 181 | # This is the final point where the user gets to view their order's final information. 182 | # All that's required at this point is that we complete the order, which is as easy as: 183 | response = client.put("/api/checkouts/#{order['number']}/next") 184 | if response.status == 200 185 | order = JSON.parse(response.body) 186 | if order['state'] == 'complete' 187 | client.succeeded "Order complete!" 188 | else 189 | client.failed "Order did not complete." 190 | end 191 | else 192 | client.failed "Order could not transition to 'complete'." 193 | end 194 | 195 | # Now that there is an existing card we can checkout with it. 196 | 197 | # Create the order step by step: 198 | # You may also choose to start it off with some line items 199 | # See checkout/creating_with_line_items.rb 200 | response = client.post('/api/orders') 201 | 202 | if response.status == 201 203 | client.succeeded "Created new checkout." 204 | order = JSON.parse(response.body) 205 | if order['email'] == 'spree@example.com' 206 | # Email addresses are necessary for orders to transition to address. 207 | # This just makes really sure that the email is already set. 208 | # You will not have to do this in your own API unless you've customized it. 209 | client.succeeded 'Email set automatically on order successfully.' 210 | else 211 | client.failed %Q{ 212 | Email address was not automatically set on order.' 213 | -> This may lead to problems transitioning to the address step. 214 | } 215 | end 216 | else 217 | client.failed 'Failed to create a new blank checkout.' 218 | end 219 | 220 | # Assign a line item to the order we just created. 221 | 222 | response = client.post("/api/orders/#{order['number']}/line_items", 223 | { 224 | line_item: { 225 | variant_id: 1, 226 | quantity: 1 227 | } 228 | } 229 | ) 230 | 231 | 232 | if response.status == 201 233 | client.succeeded "Added a line item." 234 | else 235 | client.failed "Failed to add a line item." 236 | end 237 | 238 | # Transition the order to the 'address' state 239 | response = client.put("/api/checkouts/#{order['number']}/next") 240 | if response.status == 200 241 | order = JSON.parse(response.body) 242 | client.succeeded "Transitioned order into address state." 243 | else 244 | client.failed "Could not transition order to address state." 245 | end 246 | 247 | # Add address information to the order 248 | # Before you make this request, you may need to make a request to one or both of: 249 | # - /api/countries 250 | # - /api/states 251 | # This will give you the correct country_id and state_id params to use for address information. 252 | 253 | # First, get the country: 254 | response = client.get('/api/countries?q[name_cont]=United States') 255 | if response.status == 200 256 | client.succeeded "Retrieved a list of countries." 257 | countries = JSON.parse(response.body)['countries'] 258 | usa = countries.first 259 | if usa['name'] != 'United States' 260 | client.failed "Expected first country to be 'United States', but it wasn't." 261 | end 262 | else 263 | client.failed "Failed to retrieve a list of countries." 264 | end 265 | 266 | # Then, get the state we want from the states of that country: 267 | 268 | response = client.get("/api/countries/#{usa['id']}/states?q[name_cont]=Maryland") 269 | if response.status == 200 270 | client.succeeded "Retrieved a list of states." 271 | states = JSON.parse(response.body)['states'] 272 | maryland = states.first 273 | if maryland['name'] != 'Maryland' 274 | client.failed "Expected first state to be 'Maryland', but it wasn't." 275 | end 276 | else 277 | client.failed "Failed to retrieve a list of states." 278 | end 279 | 280 | # We can finally submit some address information now that we have it all: 281 | response = client.put("/api/checkouts/#{order['number']}", 282 | { 283 | order: { 284 | bill_address_attributes: address, 285 | ship_address_attributes: address 286 | } 287 | } 288 | ) 289 | 290 | if response.status == 200 291 | client.succeeded "Address details added." 292 | order = JSON.parse(response.body) 293 | if order['state'] == 'delivery' 294 | client.succeeded "Order automatically transitioned to 'delivery'." 295 | else 296 | client.failed "Order failed to automatically transition to 'delivery'." 297 | end 298 | else 299 | client.failed "Could not add address details to order." 300 | end 301 | 302 | # Next step: delivery! 303 | 304 | first_shipment = order['shipments'].first 305 | response = client.put("/api/checkouts/#{order['number']}", 306 | { 307 | order: { 308 | shipments_attributes: [ 309 | id: first_shipment['id'], 310 | selected_shipping_rate_id: first_shipment['shipping_rates'].first['id'] 311 | ] 312 | } 313 | } 314 | ) 315 | 316 | if response.status == 200 317 | client.succeeded "Delivery options selected." 318 | order = JSON.parse(response.body) 319 | if order['state'] == 'payment' 320 | client.succeeded "Order automatically transitioned to 'payment'." 321 | else 322 | client.failed "Order failed to automatically transition to 'payment'." 323 | end 324 | else 325 | client.failed "The store was not happy with the selected delivery options." 326 | end 327 | 328 | # Next step: payment! 329 | response = client.get("/api/users/#{order['user_id']}/credit_cards") 330 | cards = JSON.parse(response.body) 331 | 332 | # First up: Retrieve existing card for a credit card payment 333 | response = client.put("/api/checkouts/#{order['number']}", 334 | { 335 | order: { 336 | existing_card: cards['credit_cards'][1]['id'], 337 | } 338 | }) 339 | 340 | if response.status == 200 341 | order = JSON.parse(response.body) 342 | client.succeeded "Payment details provided for the order." 343 | # Order will transition to the confirm state only if the selected payment 344 | # method allows for payment profiles. 345 | # The dummy Credit Card gateway in Spree does, so confirm is shown for this order. 346 | if order['state'] == 'confirm' 347 | client.succeeded "Order automatically transitioned to 'confirm'." 348 | else 349 | client.failed "Order did not transition automatically to 'confirm'." 350 | end 351 | else 352 | client.failed "Payment details were not accepted for the order." 353 | end 354 | 355 | # This is the final point where the user gets to view their order's final information. 356 | # All that's required at this point is that we complete the order, which is as easy as: 357 | response = client.put("/api/checkouts/#{order['number']}/next") 358 | if response.status == 200 359 | order = JSON.parse(response.body) 360 | if order['state'] == 'complete' 361 | client.succeeded "Order complete!" 362 | else 363 | client.failed "Order did not complete." 364 | end 365 | else 366 | client.failed "Order could not transition to 'complete'." 367 | end 368 | 369 | end 370 | end 371 | end 372 | end 373 | 374 | Examples.run(Examples::Checkout::WalkthroughWithExistingCreditCard) 375 | -------------------------------------------------------------------------------- /examples/guest.rb: -------------------------------------------------------------------------------- 1 | require_relative '../clients/guest' 2 | 3 | module Examples 4 | def self.run(example) 5 | clients = [] 6 | clients << Clients::Guest.new 7 | 8 | clients.each do |client| 9 | message = "Running #{example.name} with #{client.class.name}" 10 | puts message 11 | puts "-" * message.length 12 | 13 | example.run(client) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /examples/images/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spree-contrib/spree_api_examples/f6934a735867bc6af16bcb69ebeff22dcd05669c/examples/images/image.jpg -------------------------------------------------------------------------------- /examples/images/product_image_creation.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Images 5 | class ProductImageCreation 6 | def self.run(client) 7 | if Clients::JSON === client 8 | client.pending "ProductImageCreation does not work with JSON client." 9 | return 10 | end 11 | 12 | image = File.dirname(__FILE__) + '/thinking-cat.jpg' 13 | attachment = Faraday::UploadIO.new(image, 'image/jpeg') 14 | 15 | # Adding an image to a product's master variant 16 | response = client.post('/api/products/1/images', 17 | { 18 | image: { 19 | attachment: attachment 20 | } 21 | } 22 | ) 23 | 24 | if response.status == 201 25 | client.succeeded "Created an image for a product." 26 | image = JSON.parse(response.body) 27 | # Undo! Undo! Undo! 28 | delete_response = client.delete("/api/products/1/images/#{image["id"]}") 29 | if delete_response.status == 204 30 | client.succeeded "Deleted the image we just created." 31 | else 32 | client.failed "Could not delete the image we just created (#{delete_response.status})" 33 | exit(1) 34 | end 35 | else 36 | client.failed "Could not create an image for a product (#{response.status})" 37 | exit(1) 38 | end 39 | end 40 | end 41 | end 42 | end 43 | 44 | Examples.run(Examples::Images::ProductImageCreation) 45 | -------------------------------------------------------------------------------- /examples/images/thinking-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spree-contrib/spree_api_examples/f6934a735867bc6af16bcb69ebeff22dcd05669c/examples/images/thinking-cat.jpg -------------------------------------------------------------------------------- /examples/images/variant_image_creation.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Images 5 | class VariantImageCreation 6 | def self.run(client) 7 | if Clients::JSON === client 8 | client.pending "VariantImageCreation does not work with JSON client." 9 | return 10 | end 11 | 12 | image = File.dirname(__FILE__) + '/thinking-cat.jpg' 13 | attachment = Faraday::UploadIO.new(image, 'image/jpeg') 14 | 15 | # Adding an image to a variant 16 | response = client.post('/api/variants/1/images', 17 | { 18 | image: { 19 | attachment: attachment 20 | } 21 | } 22 | ) 23 | 24 | if response.status == 201 25 | client.succeeded "Created an image for a variant" 26 | image = JSON.parse(response.body) 27 | # Undo! Undo! Undo! 28 | delete_response = client.delete("/api/variants/1/images/#{image["id"]}") 29 | if delete_response.status == 204 30 | client.succeeded "Deleted the image we just created." 31 | else 32 | client.failed "Could not delete the image we just created (#{delete_response.status})" 33 | end 34 | else 35 | client.failed "Could not create an image for a variant (#{response.status})" 36 | end 37 | end 38 | end 39 | end 40 | end 41 | 42 | Examples.run(Examples::Images::VariantImageCreation) -------------------------------------------------------------------------------- /examples/orders/importing.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | # A little known feature of Spree: you can import entire Order objects. 4 | # This is useful if you want to copy over your orders from another ecommerce platform. 5 | # This is assuming that you know EXACTLY what you want to import. 6 | 7 | ########### DANGER, DANGER, DANGER ######### 8 | ## ## 9 | ## ## 10 | ## You MUST be an admin to import orders. ## 11 | ## ## 12 | ## ## 13 | ########### DANGER, DANGER, DANGER ######### 14 | 15 | # If you attempt to do this as a normal user, you will only be able to: 16 | # - add line items 17 | # - add address information 18 | # 19 | # For users, payments and shipping data are automatically generated. 20 | # Users MUST walkthrough the complete checkout without skipping a step. 21 | 22 | module Examples 23 | module Orders 24 | class Importing 25 | def self.run(client) 26 | # First thing you might want to do is to create a user for this order. 27 | # The parameters below assume spree_auth_devise 28 | # These might be different if you're using a different authentication method. 29 | response = client.post('/api/users', 30 | { 31 | user: { 32 | email: "test#{rand(9999999)}@example.com", 33 | password: "password", 34 | password_confirmation: "password" 35 | } 36 | } 37 | ) 38 | 39 | if response.status == 201 40 | user = JSON.parse(response.body) 41 | client.succeeded "User was created successfully." 42 | else 43 | client.failed "User could not be created" 44 | end 45 | 46 | # The next step is to add address information the order 47 | # This will be automatically linked to the user. 48 | # Before you make this request, you may need to make a request to one or both of: 49 | # - /api/countries 50 | # - /api/states 51 | # This will give you the correct country_id and state_id params to use for address information. 52 | 53 | # First, get the country: 54 | response = client.get('/api/countries?q[name_cont]=United States') 55 | if response.status == 200 56 | client.succeeded "Retrieved a list of countries." 57 | countries = JSON.parse(response.body)['countries'] 58 | usa = countries.first 59 | if usa['name'] != 'United States' 60 | client.failed "Expected first country to be 'United States', but it wasn't." 61 | end 62 | else 63 | client.failed "Failed to retrieve a list of countries." 64 | end 65 | 66 | # Then, get the state we want from the states of that country: 67 | 68 | response = client.get("/api/countries/#{usa['id']}/states?q[name_cont]=Maryland") 69 | if response.status == 200 70 | client.succeeded "Retrieved a list of states." 71 | states = JSON.parse(response.body)['states'] 72 | maryland = states.first 73 | if maryland['name'] != 'Maryland' 74 | client.failed "Expected first state to be 'Maryland', but it wasn't." 75 | end 76 | else 77 | client.failed "Failed to retrieve a list of states." 78 | end 79 | 80 | # We can finally submit some address information now that we have it all: 81 | 82 | address = { 83 | firstname: 'Test', 84 | lastname: 'User', 85 | address1: 'Unit 1', 86 | address2: '1 Test Lane', 87 | country_id: usa['id'], 88 | state_id: maryland['id'], 89 | city: 'Bethesda', 90 | zipcode: '20814', 91 | phone: '(555) 555-5555' 92 | } 93 | 94 | response = client.post('/api/orders', { 95 | order: { 96 | user_id: user["id"], 97 | completed_at: Date.today.to_s, 98 | line_items: { 99 | "0" => { 100 | variant_id: 1, 101 | quantity: 5 102 | } 103 | }, 104 | bill_address_attributes: address, 105 | ship_address_attributes: address, 106 | shipments: [ 107 | { 108 | # The tracking number for this shipment (if there is one) 109 | tracking: 'H12345', 110 | stock_location: 'default', # Actual stock location name as per the database 111 | shipping_method: 'UPS Ground (USD)', # Actual shipping method as per the database 112 | cost: 5, 113 | inventory_units: [ 114 | variant_id: 1 115 | ] 116 | } 117 | ], 118 | payments: [ 119 | amount: 19.99, 120 | payment_method: 'Credit Card' 121 | ], 122 | # Apply a negative adjustment for the cost of the line item: 123 | adjustments_attributes: [ 124 | { 125 | label: 'Some sort of discount', 126 | amount: -10 127 | } 128 | ] 129 | # 130 | # These adjustments are not assignable to specific items within the order through this API. 131 | # That's even though we support this feature in Spree 2.2. 132 | } 133 | }) 134 | 135 | if response.status == 201 136 | order = JSON.parse(response.body) 137 | client.succeeded 'Order has been successfully imported.' 138 | if order["user_id"] == user["id"] 139 | client.succeeded 'User was correctly associated with this order.' 140 | else 141 | client.failed 'Expected order to be associated with a user, but it was not.' 142 | end 143 | else 144 | client.failed 'Order failed to import.' 145 | end 146 | end 147 | end 148 | end 149 | end 150 | 151 | Examples.run(Examples::Orders::Importing) 152 | -------------------------------------------------------------------------------- /examples/products/crud.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Products 5 | class CRUD 6 | def self.run(client) 7 | 8 | # create a new product 9 | response = client.post("/api/products", 10 | { 11 | product: { 12 | name: 'Brians Product', 13 | price: 9.99, 14 | shipping_category_id: 1 15 | } 16 | } 17 | ) 18 | 19 | if response.status == 201 20 | client.succeeded "Created a product." 21 | product = JSON.parse(response.body) 22 | id = product['id'] 23 | 24 | if product['name'] != 'Brians Product' 25 | client.failed "Product name does not match expected value. #{product.inspect}" 26 | end 27 | else 28 | client.failed "Could not create product (#{response.status})" 29 | exit(1) 30 | end 31 | 32 | 33 | # fetch newly created product 34 | response = client.get("/api/products/#{id}") 35 | 36 | if response.status == 200 37 | client.succeeded "Fetched newly created product." 38 | else 39 | client.failed "Failed to fetch newly created product (#{response.status})" 40 | exit(1) 41 | end 42 | 43 | #update newly created product 44 | response = client.put("/api/products/#{id}", 45 | { 46 | product: { 47 | sku: 'BDQ-1234' 48 | } 49 | } 50 | ) 51 | 52 | if response.status == 200 53 | client.succeeded "Updated product." 54 | 55 | product = JSON.parse(response.body) 56 | 57 | if product['master']['sku'] != 'BDQ-1234' 58 | client.failed "Product SKU does not match expected value. #{product.inspect}" 59 | end 60 | else 61 | client.failed "Failed to update product (#{response.status})" 62 | exit(1) 63 | end 64 | 65 | # delete our lovely product 66 | response = client.delete("/api/products/#{id}") 67 | if response.status == 204 68 | client.succeeded "Deleted product." 69 | else 70 | client.failed "Failed to delete product (#{response.status})" 71 | exit(1) 72 | end 73 | end 74 | end 75 | end 76 | end 77 | 78 | Examples.run(Examples::Products::CRUD) 79 | -------------------------------------------------------------------------------- /examples/products/listing.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Products 5 | class Listing 6 | def self.run(client) 7 | response = client.get('/api/products') 8 | if response.status == 200 9 | client.succeeded "Retrieved a list of products" 10 | else 11 | client.failed "Failed to retrieve a list a products (#{response.status})" 12 | end 13 | end 14 | end 15 | end 16 | end 17 | 18 | Examples.run(Examples::Products::Listing) 19 | -------------------------------------------------------------------------------- /examples/products/searching.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Products 5 | class Searching 6 | def self.run(client) 7 | response = client.get('/api/products?q[variants_including_master_sku_cont]=RUB') 8 | products = JSON.parse(response.body) 9 | 10 | if response.status == 200 && products['products'].first 11 | client.succeeded "Retrieved a list of products with sku containing RUB" 12 | else 13 | client.failed "Failed to retrieve a list a products (#{response.status})" 14 | end 15 | end 16 | end 17 | end 18 | end 19 | 20 | Examples.run(Examples::Products::Searching) 21 | -------------------------------------------------------------------------------- /examples/products/update_taxons.rb: -------------------------------------------------------------------------------- 1 | require_relative '../base' 2 | 3 | module Examples 4 | module Products 5 | class UpdateTaxons 6 | def self.run(client) 7 | 8 | # create a new product 9 | response = client.post("/api/products", 10 | { 11 | product: { 12 | name: 'Taxon Product', 13 | price: 9.99, 14 | shipping_category_id: 1 15 | } 16 | } 17 | ) 18 | 19 | if response.status == 201 20 | client.succeeded "Created a product." 21 | product = JSON.parse(response.body) 22 | id = product['id'] 23 | 24 | if product['name'] != 'Taxon Product' 25 | client.failed "Product name does not match expected value. #{product.inspect}" 26 | end 27 | else 28 | client.failed "Could not create product (#{response.status})" 29 | exit(1) 30 | end 31 | 32 | 33 | # fetch newly created product 34 | response = client.get("/api/products/#{id}") 35 | 36 | if response.status == 200 37 | client.succeeded "Fetched newly created product." 38 | else 39 | client.failed "Failed to fetch newly created product (#{response.status})" 40 | exit(1) 41 | end 42 | 43 | #update newly created product 44 | response = client.put("/api/products/#{id}", 45 | { 46 | product: { 47 | name: 'Taxon Prod', 48 | taxon_ids: [9] 49 | } 50 | } 51 | ) 52 | 53 | if response.status == 200 54 | client.succeeded "Updated product." 55 | 56 | product = JSON.parse(response.body) 57 | if product['taxon_ids'] != [9] 58 | client.failed "Product Taxons does not match expected value. #{product.inspect}" 59 | end 60 | else 61 | client.failed "Failed to update product (#{response.status})" 62 | exit(1) 63 | end 64 | end 65 | end 66 | end 67 | end 68 | 69 | Examples.run(Examples::Products::UpdateTaxons) 70 | -------------------------------------------------------------------------------- /examples/products/variants/crud.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../base' 2 | 3 | module Examples 4 | module Products 5 | module Variants 6 | class CRUD 7 | def self.run(client) 8 | 9 | ov_id = nil 10 | 11 | # create a new product 12 | response = client.post("/api/v1/products", 13 | { 14 | product: { 15 | name: 'Brians Product', 16 | price: 9.99, 17 | shipping_category_id: 1 18 | } 19 | } 20 | ) 21 | 22 | if response.status == 201 23 | client.succeeded "Created a product." 24 | product = JSON.parse(response.body) 25 | id = product['id'] 26 | else 27 | client.failed "Could not create product (#{response.status})" 28 | exit(1) 29 | end 30 | 31 | # create option type 32 | response = client.post("/api/v1/option_types", 33 | { 34 | option_type: { 35 | name: 'level', 36 | presentation: 'Level' 37 | } 38 | } 39 | ) 40 | 41 | if response.status == 201 42 | client.succeeded "Created an option type." 43 | ot = JSON.parse(response.body) 44 | ot_id = ot['id'] 45 | else 46 | client.failed "Could not create option type (#{response.status})" 47 | exit(1) 48 | end 49 | 50 | %w{one two}.each do |level| 51 | # create option type 52 | response = client.post("/api/v1/option_types/#{ot_id}/option_values", 53 | { 54 | option_value: { 55 | name: level, 56 | presentation: level.capitalize 57 | } 58 | } 59 | ) 60 | 61 | if response.status == 201 62 | client.succeeded "Created an option value." 63 | ov = JSON.parse(response.body) 64 | ov_id = ov['id'] 65 | else 66 | client.failed "Could not create option value (#{response.status})" 67 | exit(1) 68 | end 69 | end 70 | 71 | # Update product with option types 72 | response = client.put("/api/v1/products/#{id}", 73 | { 74 | product: { 75 | option_types: [ot_id] 76 | } 77 | } 78 | ) 79 | 80 | if response.status == 200 81 | client.succeeded "Updated product." 82 | 83 | product = JSON.parse(response.body) 84 | else 85 | client.failed "Failed to update product (#{response.status})" 86 | exit(1) 87 | end 88 | 89 | # Create variant 90 | response = client.post("/api/v1/products/#{id}/variants", 91 | { 92 | variant: { 93 | option_value_ids: [ov_id] 94 | } 95 | } 96 | ) 97 | 98 | if response.status == 201 99 | variant = JSON.parse(response.body) 100 | if variant['option_values'].empty? 101 | client.failed "Failed to assign option value (#{response.status})" 102 | exit(1) 103 | else 104 | client.succeeded "Created Variant." 105 | end 106 | else 107 | client.failed "Failed to create variant (#{response.status})" 108 | exit(1) 109 | end 110 | 111 | end 112 | end 113 | end 114 | end 115 | end 116 | 117 | Examples.run(Examples::Products::Variants::CRUD) 118 | -------------------------------------------------------------------------------- /setup.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'faraday' 3 | require 'json' 4 | require 'colorize' 5 | require 'pry' -------------------------------------------------------------------------------- /test.rb: -------------------------------------------------------------------------------- 1 | Dir['examples/**/*.rb'].each do |f| 2 | require File.expand_path(f) 3 | end 4 | 5 | puts "\n" 6 | puts ("*" * 50).green 7 | puts "[MEGA SUCCESS] All tests complete!".green 8 | puts ("*" * 50).green 9 | --------------------------------------------------------------------------------