diff --git a/guides/react_checkout_guide/00_introduction.md b/guides/react_checkout_guide/00_introduction.md
new file mode 100644
index 00000000..88b7dd96
--- /dev/null
+++ b/guides/react_checkout_guide/00_introduction.md
@@ -0,0 +1,19 @@
+# Adding Checkout functionality to your React application
+
+```
+This guide is based on a demo that we ran a while back. Please take note that there might be issues for you when adding this functionality as your implementation probably looks different. This is also the first iteration of the guide and updates will be made in the future.
+If there are typos or issues in this guide, please notify a coach
+```
+
+In this section we are going to add checkout functionality to our react application.
+The prerequisite for this functionality is that we have products on our page that are ready to sell. Please remember as we implement we will omit explaining code that has already been covered. Remember to commit often in case you need to revert back and not lose large sections of the implementation.
+
+Start with making sure that you have the latest code pulled from GitHub on your development branch and create a new branch called `add_order_functionality` or something that you think is appropriate to the user story.
+
+As always we will work in a test-driven way. Making sure that we let our acceptance test guide our development. This will ensure us that we are not biting off more than we can chew. But also that we stay in scope.
+
+If you want to only learn about how to create a checkout functionality in react you can clone down the following repos:
+[API](https://github.com/CraftAcademy/slowfood_api_checkout_boilerplate) &
+[Client](https://github.com/CraftAcademy/slowfood_client_checkout_boilerplate)
+
+Let's gets started
\ No newline at end of file
diff --git a/guides/react_checkout_guide/01_getting_started.md b/guides/react_checkout_guide/01_getting_started.md
new file mode 100644
index 00000000..193f3833
--- /dev/null
+++ b/guides/react_checkout_guide/01_getting_started.md
@@ -0,0 +1,125 @@
+## Getting started
+
+Create a new feature file by typing
+`$ touch cypress/integration/userCanAddProductsToOrder.feature.js`
+
+Add the following tests to the file:
+
+```js
+describe("User can add a product to their order", () => {
+ before(() => {
+ cy.server();
+ cy.route({
+ method: "GET",
+ url: "http://localhost:3000/api/products",
+ response: "fixture:product_data.json",
+ });
+
+ cy.route({
+ method: "POST",
+ url: "http://localhost:3000/api/orders",
+ response: { message: "A product has been added to your order" },
+ });
+ });
+
+ it("user gests a confirmation messsage when adding a a producut to order", () => {
+ cy.visit("/");
+ cy.get("#product-1").within(() => {
+ cy.get("button").contains("Add to order").click();
+ });
+ cy.contains("A product has been added to your order");
+ });
+});
+```
+
+So what are we expecting to see in the implementation? Well, we need to make sure that we can add a product to an order. This means that we need to have a list of products since before. After that, we need to make sure that there is a button to make this happen. And it will all end with us getting a confirmation message that we have added a product to our order. Simple huh?
+
+When you run your test now it should all fail and to make it work we have to add the actual implementation code.
+
+In our case we already have a component that we use to display the products, all we have to do now is to add the button to the iteration and then make sure that we can see the message.
+
+Let's add the implementation code.
+
+We need to start with the first step which is to make sure that we have a button available to click on, let's add that where we are mapping over the products, for example `DisplayProductData`
+
+```jsx
+dataIndex = (
+
+);
+```
+
+So here we have added a `addToOrder` function that is not defined. Lets go ahead and add that functionality as well.
+
+Add the following code to the component:
+
+```js
+addToOrder() {
+ debugger
+}
+```
+
+So why are we adding a debugger here? Well, first we need to make sure that the `addToOrder` button gets invoked.
+
+Run your tests now. The debugger should kick in and halt the execution, if not then make sure that you have added React developer tools to your chrome browser.
+
+If we can see that then its time for us to add the real functionality.
+
+But first let's make sure that we are hitting the right button.
+
+Go to your chrome console where the execution of the code has stopped and run the following command in the console:
+
+`event.target.parentElement`
+
+You should probably see something like this in your console:
+
+```html
+
+ "Pizza" "Crusty and fluffy" "100 SEK"
+
+
+```
+
+If not then make sure that you are iterating over your products properly.
+So this was our first iteration. But we need to make sure that we are pulling the information of the product as a dataset rather than through an id, and the reason for that is that it will be easier for us to manage. But it will also help us use it because the values in the product are sent back to us in the form of an object.
+
+Add the dataset to the parent div:
+`data-id={item.id} data-price={item.price}`
+
+Like this:
+
+```js
+return (
+
+);
+```
+
+Now if you run your test, the debugger should kick in, run the following command in the console `event.target.parentElement.dataset`.
+You should see the same information as before, but the information about the product will be returned to us in the form of an object. This is good news for us because we can use this in our `addToOrder` function. Time to add the rest of the implementation code to the `addToOrder` function.
+
+```js
+async addToOrder(event) {
+ let id = event.target.parentElement.dataset.id
+ let result = await axios.post('http://localhost:3000/api/orders', { id: id } )
+}
+```
+
+So what did we do here? First, we made the method asynchronous because we need to wait for a response from our API, we passed in the event as an argument to the function and we also added `axios` to handle that call to the API. the last thing that we added was a post request that we save in the variable `result`
+
+Make sure that you add `axios` and also import it to the top of the file if you haven't already.
diff --git a/guides/react_checkout_guide/02_displaying_messages_and_the_backend.md b/guides/react_checkout_guide/02_displaying_messages_and_the_backend.md
new file mode 100644
index 00000000..b672f21e
--- /dev/null
+++ b/guides/react_checkout_guide/02_displaying_messages_and_the_backend.md
@@ -0,0 +1,96 @@
+## Displaying messages and the Backend
+
+The next thing we are going to focus on is to get the message to be displayed.
+Let's start with adding the message to the state:
+
+```js
+state = {
+ productData: [],
+ message: {},
+};
+```
+
+Next, we need to display the message below the button.
+
+```js
+[....]
+
+{parseInt(this.state.message.id) === item.id &&
+
{this.state.message.message}
}
+```
+
+Finally, we need to set the new state in the `addToOrder` function
+
+```js
+async addToOrder(event) {
+ let id = event.target.parentElement.dataset.id
+ let result = await axios.post('http://localhost:3000/api/orders', { id: id } )
+ this.setState({message: {id: id, message: result.data.message}})
+}
+```
+
+If we run our tests now everything should go green.
+
+Let's move over to the backend and work on our API. We will start with creating a spec. Make sure that you create a new branch to work on.
+
+```bash
+$ touch spec/requests/api/client_can_create_new_order_spec.rb
+```
+
+Add the following test to the spec file
+
+```ruby
+RSpec.describe Api::OrdersController, type: :request do
+ let!(:product_1) { create(:product, name: 'Pizza') }
+ let!(:product_2) { create(:product, name: 'Kebab') }
+
+ it 'responds with success message' do
+ post '/api/orders', params: {id: product_1.id }
+
+ expect(JSON.parse(response.body)['message']).to eq 'The product has been added to your order'
+ end
+end
+```
+
+Remember that the request spec acts like our feature test but for APIs. This will allow us to see what requests we are sending out.
+
+Run the test and make sure that you are getting the correct error messages.
+
+We will probably get an error message that states that we have an uninitialized constant `Api::OrdersController`.
+
+We will fix this by generating a new orders controller.
+
+`$ rails generate controller Api::Orders create`
+
+Run the test again and you will get a new error message.
+
+The error message probably has to do with our routes and that there are none present. Let's fix that.
+
+```ruby
+Rails.application.routes.draw do
+ namespace :api do
+ resources :products, only: [:index]
+ resources :orders, only: [:create]
+ end
+end
+```
+
+Our problem now is that we are not getting the right response. But this is expected.
+
+To fix this we need to add the order to the create action in our Orders controller.
+
+```ruby
+class Api::OrdersController < ApplicationController
+ def create
+ order = Order.create
+ order.order_items.create(product_id: params[:product_id])
+ render json: { message: 'The product has been added to your order', order_id: order.id }
+ end
+end
+```
+We are getting a new error:
+```bash
+NameError: uninitialized constant Api::OrdersController::Order
+```
+To fix this we need to create a new model
+Cool. let's move over to the model.
diff --git a/guides/react_checkout_guide/03_the_orders_model.md b/guides/react_checkout_guide/03_the_orders_model.md
new file mode 100644
index 00000000..70e61323
--- /dev/null
+++ b/guides/react_checkout_guide/03_the_orders_model.md
@@ -0,0 +1,58 @@
+## The orders model
+
+The next step we need to take is to generate an Order model to have something to pull out from the database.
+
+`$ rails generate model order`
+
+We also need to associate the `order` to the `order_items` that we have, and the `products`.
+
+`$ rails generate model OrderItems order:references product:references`
+
+Go ahead and go through the files that are generated and make sure that there are no problems.
+
+We will now add specs to test the associations we created between the models.
+In the newly generated `order_items_spec.rb` go ahead and add two specs.
+You will need more than that but for this guide, we will limit ourselves to these specs.
+
+```ruby
+require 'rails_helper'
+
+RSpec.describe OrderItem, type: :model do
+ it { is_expected.to belong_to :order }
+ it { is_expected.to belong_to :product }
+end
+```
+
+`order_spec.rb`
+
+```ruby
+require 'rails_helper'
+
+RSpec.describe Order, type: :model do
+ it {is_expected.to have_many :order_items}
+end
+```
+
+We need to make sure that the associations are present as well.
+
+`order_item.rb`
+
+```ruby
+class OrderItem < ApplicationRecord
+ belongs_to :order
+ belongs_to :product
+end
+```
+
+`order.rb`
+
+```ruby
+class Order < ApplicationRecord
+ has_many :order_items
+end
+```
+
+Run your migrations and then run your specs. Everything should be green like Bruce Banners alter ego.
+
+
+
diff --git a/guides/react_checkout_guide/04_some_updates.md b/guides/react_checkout_guide/04_some_updates.md
new file mode 100644
index 00000000..fd5d7567
--- /dev/null
+++ b/guides/react_checkout_guide/04_some_updates.md
@@ -0,0 +1,120 @@
+## Update the order on the Backend
+
+We have added a lot of functionality and e need to revisit our specs and make some updates to have it work on the client-side later on.
+
+First, we need to amend our request spec `client_can_create_new_order_spec`.
+
+We will amend our specs by adding a `before` block
+
+```ruby
+before do
+ post '/api/orders', params: { id: product_1.id }
+ @order = Order.last
+end
+```
+
+And adding a new spec
+
+```ruby
+it 'adds product to order if param "order_id" is present' do
+ post '/api/orders', params: {id: product_2.id, order_id: @order.id}
+ expect(@order.order_items.count).to eq 2
+end
+```
+
+And
+
+```ruby
+it 'adds another product to order if param "order_id" is present' do
+ put "/api/orders/#{order.id}", params: {product_id: product_2.id }
+ expect(@order.order_items.count).to eq 2
+end
+```
+
+
+
+
+You will probably get a routing error. We will fix that with adding update & create to the routes:
+
+`resources :orders, only: [:create, :update]`
+
+Run the test again and you will get a error stating that there is no action `update`.
+
+```ruby
+
+ def update
+ order = Order.find(params[:id])
+ product = Product.find(params[:product_id])
+ order.order_items.create(product: product)
+ render json: { message: 'The product has been added to your order'}
+ end
+```
+
+And the create action:
+
+```ruby
+ def create
+ order = Order.create
+ order.order_items.create(product_id: params[:product_id])
+ render json: { message: 'The product has been added to your order' }
+ end
+```
+
+We will have to refactor our test to better suit our implementation code, dy dividing both functionalities in out tests. The final shape of the test will look like this.
+
+```rb
+RSpec.describe Api::OrdersController, type: :request do
+ let!(:product_1) { create(:product, name: 'Pizza') }
+ let!(:product_2) { create(:product, name: 'Kebab') }
+
+ before do
+ post '/api/orders', params: { product_id: product_1.id }
+ @order_id = JSON.parse(response.body)['order_id']
+ end
+
+ describe 'POST /api/orders' do
+ it 'responds with success message' do
+ expect(JSON.parse(response.body)['message']).to eq 'The product has been added to your order'
+ end
+
+ it 'responds with order id' do
+ order = Order.find(@order_id)
+ expect(JSON.parse(response.body)['order_id']).to eq order.id
+ end
+ end
+
+ describe 'PUT /api/orders/:id' do
+ before do
+ put "/api/orders/#{@order_id}", params: { product_id: product_2.id }
+ @order = Order.find(@order_id)
+ end
+
+ it 'adds another product to order if request is a PUT and param id of the order is present' do
+ expect(@order.order_items.count).to eq 2
+ end
+
+ it 'responds with order id' do
+ expect(JSON.parse(response.body)['order_id']).to eq @order.id
+ end
+ end
+end
+```
+
+And in the controller:
+
+```rb
+class Api::OrdersController < ApplicationController
+ def create
+ order = Order.create
+ order.order_items.create(product_id: params[:product_id])
+ render json: { message: 'The product has been added to your order', order_id: order.id }
+ end
+
+ def update
+ order = Order.find(params[:id])
+ product = Product.find(params[:product_id])
+ order.order_items.create(product: product)
+ render json: { message: 'The product has been added to your order', order_id: order.id }
+ end
+end
+```
\ No newline at end of file
diff --git a/guides/react_checkout_guide/05_adding_multiple_products.md b/guides/react_checkout_guide/05_adding_multiple_products.md
new file mode 100644
index 00000000..cc6c20dd
--- /dev/null
+++ b/guides/react_checkout_guide/05_adding_multiple_products.md
@@ -0,0 +1,145 @@
+## Adding multiple products to the order
+
+Let's add the functionality to add multiple products to our order. As always we need to have a feature test for that. We will add this to the test that we already have.
+We need to do a couple of things:
+
+Instead of having a `before` function that only runs for one test, we will add a `beforeEach` that will run before all the upcoming feature test.
+
+We will also add a `PUT` request to this block in order to update the order that has already been created. And at last, we will add a test to check that we can add multiple products to an order.
+
+The final form of the feature will be the following:
+
+```js
+describe("User can add a product to his/her order", () => {
+ beforeEach(() => {
+ cy.server();
+ cy.route({
+ method: "GET",
+ url: "http://localhost:3000/api/products",
+ response: "fixture:product_data.json",
+ });
+
+ cy.route({
+ method: "POST",
+ url: "http://localhost:3000/api/orders",
+ response: {
+ message: "The product has been added to your order",
+ order_id: 1,
+ },
+ });
+
+ cy.route({
+ method: "PUT",
+ url: "http://localhost:3000/api/orders/1",
+ response: {
+ message: "The product has been added to your order",
+ order_id: 1,
+ },
+ });
+ cy.visit("http://localhost:3001");
+ });
+
+ it("user get a confirmation message when adding product to order", () => {
+ cy.get("#product-2").within(() => {
+ cy.get("button").contains("Add to order").click();
+ cy.get(".message").should(
+ "contain",
+ "The product has been added to your order"
+ );
+ });
+
+ cy.get("#product-3").within(() => {
+ cy.get("button").contains("Add to order").click();
+ cy.get(".message").should(
+ "contain",
+ "The product has been added to your order"
+ );
+ });
+ });
+});
+```
+
+Make sure that you have added the product_id to the messages in the `beforeEach` function.
+
+Let's continue with the implementation code to get all of this to work.
+
+Add the `orderId` to the state:
+
+```js
+state = {
+ productData: [],
+ message: {},
+ orderId: "",
+};
+```
+
+Now we need to update the `addToOrder` function with the new `orderId` state.
+
+```js
+async addToOrder(event) {
+ let id = event.target.parentElement.dataset.id
+ let result = await axios.post('http://localhost:3000/api/orders', { id: id } )
+ this.setState({message: {id: id, message: result.data.message}, orderId: results.data.order_id})
+}
+```
+
+The `order_id` is what is being returned to us from the backend.
+
+We also need to make sure that we are adding an order to an existing one, if one already exists so let's add a conditional for that.
+
+```js
+async addToOrder(event) {
+ let id = event.target.parentElement.dataset.id
+ let result
+ if (this.state.orderId !== "") {
+ result = await axios.put(`http://localhost:3000/api/orders/${this.state.orderId}`, { product_id: id })
+ } else {
+ result = await axios.post('http://localhost:3000/api/orders', { product_id: id } )
+ }
+ this.setState({message: {id: id, message: result.data.message}, orderId: result.data.order_id})
+}
+```
+
+So what does this conditional do? We are simply stating that if there is an `order` and that the `orderId` is not empty, then add the new product to that order with a `PUT` request. Otherwise, go ahead and create a new order.
+
+Run your feature test they should no go green. Go ahead and test this manually as well. Remember to fire up both the backend and frontend servers. You also need to have some products added to your backend.
+
+Now that we have the main functionality in place we need to start tweaking and making sure that the user experience is good.
+
+We want to make sure that we can view the products that we have added to our order.
+First, we want to make sure that there is a button where we can view the order.
+However, this button should only be visible when we have added products to our order.
+
+As a best practice, we will go back to our tests and refactor them a little. Refactor your code to look like this:
+
+```js
+ [....]
+
+ it('user can add multiple products to order and view its content', () => {
+ cy.get('button').contains('View order').should('not.exist') // Add this line
+ cy.get('#product-2').within(() => {
+ cy.get('button').contains('Add to order').click()
+ cy.get('.message').should('contain', "The product has been added to your order")
+ })
+
+ cy.get('button').contains('View order').should('exist') // Add this line
+ cy.get('#product-3').within(() => {
+ cy.get('button').contains('Add to order').click()
+ cy.get('.message').should('contain', "The product has been added to your order")
+ })
+ });
+});
+```
+
+And now for the implementation, let's add the button in the `DisplayProducts` component
+
+```js
+return (
+ <>
+ {this.state.orderId !== "" && }
+ {dataIndex}
+ >
+);
+```
+
+Run your tests now again and they should go green. Remember that we are only displaying the button, it has no functionality yet.
diff --git a/guides/react_checkout_guide/06_viewing_the_order.md b/guides/react_checkout_guide/06_viewing_the_order.md
new file mode 100644
index 00000000..289a5975
--- /dev/null
+++ b/guides/react_checkout_guide/06_viewing_the_order.md
@@ -0,0 +1,90 @@
+### Viewing the Order
+
+We are working in very small chunks at the time ensuring that we always are making progress as we move along.
+
+Now it's time for adding the view order functionality to the button.
+
+As always we start with the test.
+
+```js
+it("user can add multiple product to order and view its content", () => {
+ cy.get("button")
+ .contains("View order")
+ .should("not.exist");
+
+ cy.get("#product-2").within(() => {
+ cy.get("button")
+ .contains("Add to order")
+ .click();
+ cy.get(".message").should(
+ "contain",
+ "The product has been added to your order"
+ );
+ });
+
+ cy.get("button")
+ .contains("View order")
+ .should("exist");
+
+ cy.get("#product-3").within(() => {
+ cy.get("button")
+ .contains("Add to order")
+ .click();
+ cy.get(".message").should(
+ "contain",
+ "The product has been added to your order"
+ );
+ });
+
+ cy.get("button")
+ .contains("View order")
+ .click();
+ cy.get("#order-details").within(() => {
+ cy.get("li").should("have.length", 2);
+ });
+ cy.get("button")
+ .contains("View order")
+ .click();
+ cy.get("#order-details").should("not.exist");
+});
+```
+
+Run the test and it is failing....
+
+And now for the implementation code. First we update our state object
+
+```js
+state = {
+ productData: [],
+ message: {},
+ orderId: "",
+ showOrder: false
+};
+```
+
+Add the following code to the return block of your component
+
+```js
+return (
+ <>
+ {this.state.orderId !== "" && (
+
+ )}
+ {this.state.showOrder && (
+
+
Item 1
+
Item 2
+
+ )}
+ {dataIndex}
+ >
+);
+```
+
+Check if the tests are going green, The last one is probably not. However, we have hardcoded the values here. The best approach should be to pull in this information instead.
\ No newline at end of file
diff --git a/guides/react_checkout_guide/07_some_refactoring.md b/guides/react_checkout_guide/07_some_refactoring.md
new file mode 100644
index 00000000..904e2e84
--- /dev/null
+++ b/guides/react_checkout_guide/07_some_refactoring.md
@@ -0,0 +1,220 @@
+Some quick refactoring
+
+Let's go back to our test and refactor them a little.
+We want to use fixture files in order to mock the responses that we should be getting, instead of having the information our feature files.
+
+Start by creating to fixtures, one for the put request and one for the post request.
+
+```bash
+$ touch cypress/fixtures/put_response.json
+$ touch cypress/fixtures/post_response.json
+```
+
+Add the fixture files to the test
+
+`cypress/fixtures/post_response.json`
+
+```json
+{
+ "message": "The product has been added to your order",
+ "order_details": {
+ "order": {
+ "id": 1,
+ "products": [
+ {
+ "name": "Salad",
+ "price": 4.0
+ }
+ ]
+ }
+ }
+}
+```
+
+And the other in the `cypress/fixtures/put_response.json`
+
+```json
+{
+ "message": "The product has been added to your order",
+ "order_details": {
+ "order": {
+ "id": 1,
+ "products": [
+ {
+ "name": "Salad",
+ "price": 4.0
+ },
+ {
+ "name": "Ice Cream",
+ "price": 3.75
+ }
+ ]
+ }
+ }
+}
+```
+
+You also need to refactor your tests to look like this:
+
+```js
+describe("User can add a product to his/her order", () => {
+ beforeEach(() => {
+ cy.server();
+ cy.route({
+ method: "GET",
+ url: "http://localhost:3000/api/products",
+ response: "fixture:product_data.json",
+ });
+
+ cy.route({
+ method: "POST",
+ url: "http://localhost:3000/api/orders",
+ response: "fixture:post_response.json", // use the fixture here
+ });
+
+ cy.route({
+ method: "PUT",
+ url: "http://localhost:3000/api/orders/1",
+ response: "fixture:put_response.json", // use the fixture here
+ });
+ cy.visit("http://localhost:3001");
+ });
+
+ it("user can add multiple product to order and view its content", () => {
+ cy.get("button").contains("View order").should("not.exist");
+
+ cy.get("#product-2").within(() => {
+ cy.get("button").contains("Add to order").click();
+ cy.get(".message").should(
+ "contain",
+ "The product has been added to your order"
+ );
+ });
+
+ cy.get("button").contains("View order").should("exist");
+
+ cy.get("#product-3").within(() => {
+ cy.get("button").contains("Add to order").click();
+ cy.get(".message").should(
+ "contain",
+ "The product has been added to your order"
+ );
+ });
+
+ cy.get("button").contains("View order").click();
+
+ cy.get("#order-details").within(() => {
+ cy.get("li").should("have.length", 2);
+ });
+
+ cy.get("button").contains("View order").click();
+
+ cy.get("#order-details").should("not.exist");
+ });
+});
+```
+
+And finally our implementation code:
+It should look like this after you have refactored it.
+
+```js
+import React, { Component } from "react";
+import { getData } from "../modules/productData";
+import axios from "axios";
+
+class DisplayProductData extends Component {
+ state = {
+ productData: [],
+ message: {},
+ orderDetails: {},
+ showOrder: false,
+ };
+
+ componentDidMount() {
+ this.getProductData();
+ }
+
+ async getProductData() {
+ let result = await getData();
+ this.setState({ productData: result.data.products });
+ }
+
+ async addToOrder(event) {
+ let id = event.target.parentElement.dataset.id;
+ let result;
+ if (this.state.orderDetails.hasOwnProperty("id")) {
+ result = await axios.put(
+ `http://localhost:3000/api/orders/${this.state.orderDetails.id}`,
+ { product_id: id }
+ );
+ } else {
+ result = await axios.post("http://localhost:3000/api/orders", {
+ product_id: id,
+ });
+ }
+ this.setState({
+ message: { id: id, message: result.data.message },
+ orderDetails: result.data.order_details.order,
+ });
+ }
+
+ render() {
+ let dataIndex, orderDetailsDisplay;
+ if (
+ Array.isArray(this.state.productData) &&
+ this.state.productData.length
+ ) {
+ dataIndex = (
+
+ )}
+ {dataIndex}
+ >
+ );
+ }
+}
+export default DisplayProductData;
+```
+Your code might look different at this stage, if you are using different naming for the components. But this is how ours look. Take a moment to go over your code. Are there any further rooms for improvements? ask yourself if we can divide the code into more components or perhaps modules? Make sure that you note potential improvments that you and your team could make here.
+
+Run the test and move to the next section.
diff --git a/guides/react_checkout_guide/08_serializers.md b/guides/react_checkout_guide/08_serializers.md
new file mode 100644
index 00000000..00fda581
--- /dev/null
+++ b/guides/react_checkout_guide/08_serializers.md
@@ -0,0 +1,176 @@
+### Serializers
+
+Let's move to our backend to continue this functionality
+
+We want to add a gem called Active Model Serializer
+
+So what is a serializer? A serializer makes sure that our JSON responses has particular shape and content, rather than the content provided by the built-in JSON responses. We want to be in control of the information that is leaving the application, and to do that we are using this gem.
+
+Add the gem to your `Gemfile` and run bundle
+
+```rb
+gem 'active_model_serializers'
+```
+
+Create the file a configuration file where we are adding the settings.
+
+```bash
+$ touch config/initializers/active_model_serializers.rb
+```
+
+Add
+
+```rb
+ActiveModelSerializers.config.adapter = :json
+```
+
+Now let's generate a new serializer.
+In the future a new serializer will be generated by default if you generate a new model.
+But for our existing model, we will run the generator.
+
+
+```bash
+$ rails g serializer order
+```
+
+This generator creates a new file. But before we add code to this (and there is a lot of code to add) we need to refactor our test and implementation to match the serialization.
+Go over the following code carefully and take note of the changes we are making.
+
+`spec/requests/api/client_can_create_new_order_spec.rb`
+
+```rb
+RSpec.describe Api::OrdersController, type: :request do
+ let!(:product_1) { create(:product, name: "Pizza", price: 10) }
+ let!(:product_2) { create(:product, name: "Kebab", price: 20) }
+
+ before do
+ post "/api/orders", params: { product_id: product_1.id }
+ order_id = JSON.parse(response.body)["order"]["id"]
+ @order = Order.find(order_id)
+ end
+
+ describe "POST /api/orders" do
+ it "responds with success message" do
+ expect(JSON.parse(response.body)["message"]).to eq "The product has been added to your order"
+ end
+
+ it "responds with order id" do
+ expect(JSON.parse(response.body)["order"]["id"]).to eq @order.id
+ end
+
+ it "responds with right amount of products" do
+ expect(JSON.parse(response.body)["order"]["products"].count).to eq 1
+ end
+
+ it "responds with right order total" do
+ expect(JSON.parse(response.body)["order"]["total"]).to eq 10
+ end
+ end
+
+ describe "PUT /api/orders/:id" do
+ before do
+ put "/api/orders/#{@order.id}", params: { product_id: product_2.id }
+ put "/api/orders/#{@order.id}", params: { product_id: product_2.id }
+ end
+
+ it "adds another product to order if request is a PUT and param id of the order is present" do
+ expect(@order.order_items.count).to eq 3
+ end
+
+ it "responds with order id" do
+ expect(JSON.parse(response.body)["order"]["id"]).to eq @order.id
+ end
+
+ it "responds with right amount of unique products" do
+ expect(JSON.parse(response.body)["order"]["products"].count).to eq 2
+ end
+
+ it "responds with right order total" do
+ expect(JSON.parse(response.body)["order"]["total"]).to eq 50
+ end
+ end
+end
+```
+
+And now for the serializer. Go over the code here carefully and try to understand what is happening. I advise you to use binding.pry to stop the execution of the code and dive deeper into hoe these different methods work together.
+
+`app/serializers/order_serializer.rb`
+
+```rb
+class OrderSerializer < ActiveModel::Serializer
+ attributes :id, :products_1, :products_2, :products_3, :products, :total, :order_total
+
+ def products_1
+ products = []
+ object.order_items.group_by(&:product_id).each do |key, value|
+ product = Product.find(key)
+ hash = { amount: value.count, name: product.name, total: (product.price * value.count) }
+ products.push hash
+ end
+ products
+ end
+
+ def products_2
+ products = []
+ unique_items = object.order_items.uniq(&:product)
+ unique_items_count = object.order_items.group_by(&:product_id).map { |key, value| [key, value.size] }.to_h
+ unique_items.each do |item|
+ products.push(
+ amount: unique_items_count[item.product_id],
+ name: item.product.name,
+ total: (unique_items_count[item.product_id] * item.product.price)
+ )
+ end
+ products
+ end
+
+ def products_3
+ object.order_items.group_by(&:product_id).map do |_key, value|
+ product = value.uniq(&:product_id)[0].product
+ { amount: value.size, name: product.name, total: (value.size * product.price) }
+ end
+ end
+ alias_method :products, :products_3
+
+ def total
+ object.order_items.joins(:product).sum('products.price')
+ end
+end
+```
+
+And we need to update our controller with the changes.
+
+`app/controllers/api/orders_controller.rb`
+
+```rb
+class Api::OrdersController < ApplicationController
+ def create
+ order = Order.create
+ order.order_items.create(product_id: params[:product_id])
+ render json: create_json_response(order)
+ end
+
+ def update
+ order = Order.find(params[:id])
+ product = Product.find(params[:product_id])
+ order.order_items.create(product: product)
+ render json: create_json_response(order)
+ end
+
+private
+
+ def create_json_response(order)
+ json = { order: OrderSerializer.new(order) }
+ json.merge!(message: 'The product has been added to your order')
+ end
+end
+```
+
+And finally add this the method to order model.
+```rb
+def order_total
+ order_items.joins(:product).sum("products.price")
+end
+```
+
+Run your tests
\ No newline at end of file
diff --git a/guides/react_checkout_guide/09_back_to_the_frontend.md b/guides/react_checkout_guide/09_back_to_the_frontend.md
new file mode 100644
index 00000000..273139b6
--- /dev/null
+++ b/guides/react_checkout_guide/09_back_to_the_frontend.md
@@ -0,0 +1,121 @@
+## Go back to the frontend
+
+Now we want to go back to our frontend and add the changes there as well. We need to make sure that the order totals are showing up.
+
+As always we first start with our tests. Refactor your test by adding the following section to your it block in your test file.
+
+```js
+[....]
+cy.get('button').contains('View order').click()
+
+cy.get('#order-details').within(() => {
+ cy.get('li')
+ .should('have.length', 2)
+ .first().should('have.text', '1 x Salad')
+ .next().should('have.text', '1 x Ice Cream')
+})
+
+cy.get('button').contains('View order').click()
+cy.get('#order-details').should('not.exist')
+```
+
+We also need to import the `DisplayProductData` to the feature file.
+
+`import DisplayProductData from '../../src/components/DisplayProductData'`
+
+And of course, we need to update our fixture files to accommodate the new changes
+
+```json
+// put_response.json
+
+{
+ "message": "The product has been added to your order",
+ "order": {
+ "id": 1,
+ "products": [
+ {
+ "amount": 1,
+ "name": "Salad",
+ "price": 4.0
+ },
+ {
+ "amount": 1,
+ "name": "Ice Cream",
+ "price": 3.75
+ }
+ ],
+ "order_total": 7.75,
+ "finalized": false
+ }
+}
+```
+
+and
+
+```json
+// post_response.json
+{
+ "message": "The product has been added to your order",
+ "order": {
+ "id": 1,
+ "products": [
+ {
+ "amount": 1,
+ "name": "Salad",
+ "price": 4.0
+ }
+ ],
+ "order_total": 7.75,
+ "finalized": false
+ }
+}
+```
+
+And finally our implementation code.
+
+First in our `addToOrder` function
+`this.setState({ message: { id: id, message: result.data.message }, orderDetails: result.data.order })`
+
+So the function looks like this
+
+```js
+ async addToOrder(event) {
+ let id = event.target.parentElement.dataset.id
+ let result
+ if (this.state.orderDetails.hasOwnProperty('id')) {
+ result = await axios.put(`http://localhost:3000/api/orders/${this.state.orderDetails.id}`, { product_id: id })
+ } else {
+ result = await axios.post('http://localhost:3000/api/orders', { product_id: id })
+ }
+ this.setState({ message: { id: id, message: result.data.message }, orderDetails: result.data.order })
+```
+
+And in our if statement in the orderDetailDisplay
+
+```js
+orderDetailsDisplay = this.state.orderDetails.products.map((item) => {
+ return
+ >
+ }
+ {dataIndex}
+ >
+```
+
+Commit this and let's go back to the backend to finalize the order.
diff --git a/guides/react_checkout_guide/10_finalizing_the_order_on_the_backend.md b/guides/react_checkout_guide/10_finalizing_the_order_on_the_backend.md
new file mode 100644
index 00000000..70321b25
--- /dev/null
+++ b/guides/react_checkout_guide/10_finalizing_the_order_on_the_backend.md
@@ -0,0 +1,77 @@
+## Finalizing the order
+
+We will start by adding the functionality to the backend.
+First, we need to create a new request spec
+
+`$ touch spec/requests/api/user_can_finalize_order_spec.rb`
+
+Inside of this file add the following specs
+
+```rb
+RSpec.describe Api::OrdersController, type: :request do
+ let(:product_1) { create(:product, name: 'Pizza', price: 10) }
+ let(:product_2) { create(:product, name: 'Kebab', price: 20) }
+ let(:order) { create(:order) }
+
+ before do
+ order.order_items.create(product: product_1)
+ order.order_items.create(product: product_2)
+
+ put "/api/orders/#{order.id}", params: { activity: 'finalize' }
+ end
+
+ describe 'PUT /api/orders' do
+ it 'responds with success message' do
+ expect(JSON.parse(response.body)['message']).to eq 'Your order will be ready in 30 minutes!'
+ end
+
+ it 'sets the order attribute "finalized" to true' do
+ expect(order.reload.finalized).to eq true
+ end
+ end
+end
+```
+
+Run the specs you should and take notice of the error messages.
+We need to add a new column in our orders database table.
+
+`rails g migration AddFinalizedToOrders finalized:boolean`
+
+Before you migrate make sure that it is adding a column to the orders db:table and make sure that the default value is set to false.
+It should look like this:
+
+```rb
+class AddFinalizedToOrders < ActiveRecord::Migration[6.0]
+ def change
+ add_column :orders, :finalized, :boolean, default: false
+ end
+end
+```
+
+Run the migrations and make sure that the changes have been added to your schema file. The final step we need to take is to refactor our update action.
+
+`app/controllers/api/orders_controller.rb`
+
+```rb
+def update
+ order = Order.find(params[:id])
+ if params[:activity]
+ order.update_attribute(:finalized, true)
+ render json: { message: 'Your order will be ready in 30 minutes!' }
+ else
+ product = Product.find(params[:product_id])
+ order.order_items.create(product: product)
+ render json: create_json_response(order)
+ end
+end
+```
+
+Remember that need to update our order serializer as well.
+
+```rb
+attributes :id, :products_1, :products_2, :products_3, :products, :total, :order_total, :finalized
+```
+
+Run your spec they should be green by now.
+
+Let's head back over to the client.
\ No newline at end of file
diff --git a/guides/react_checkout_guide/11_finializing_on_the_client_side.md b/guides/react_checkout_guide/11_finializing_on_the_client_side.md
new file mode 100644
index 00000000..2445f9a6
--- /dev/null
+++ b/guides/react_checkout_guide/11_finializing_on_the_client_side.md
@@ -0,0 +1,132 @@
+## Finalizing and back to the client
+
+Okay...The final stretch
+As always let's start with a new test.
+
+Add the following code to your feature test:
+
+```js
+it("user can finalize the order", () => {
+ cy.get("#product-2").within(() => {
+ cy.get("button").contains("Add to order").click();
+ });
+ cy.get("#product-3").within(() => {
+ cy.get("button").contains("Add to order").click();
+ });
+ cy.get("button").contains("View order").click();
+ cy.route({
+ method: "PUT",
+ url: "http://localhost:3000/api/orders/1",
+ response: { message: "Your order will be ready in 30 minutes!" },
+ });
+ cy.get("button").contains("Confirm!").click();
+ cy.get(".message").should(
+ "contain",
+ "Your order will be ready in 30 minutes!"
+ );
+});
+```
+
+We also want to make sure that we have som configuration to use in the cypress tests. We are adding a delay depending on the different functions that cypress runs.
+
+```js
+// cypress/support/commands.js
+const COMMAND_DELAY = 150;
+
+for (const command of [
+ "get",
+ "visit",
+ "click",
+ "trigger",
+ "type",
+ "clear",
+ "reload",
+ "contains",
+]) {
+ Cypress.Commands.overwrite(command, (originalFn, ...args) => {
+ const origVal = originalFn(...args);
+
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(origVal);
+ }, COMMAND_DELAY);
+ });
+ });
+}
+```
+
+Let's work on the implementation code:
+
+Update the state object with orderTotal
+
+```js
+state = {
+ productData: [],
+ message: {},
+ orderDetails: {},
+ showOrder: false,
+ orderTotal: "",
+};
+```
+
+Add a new asynchronous function to handle the finalize order
+
+```js
+async finalizeOrder() {
+ let orderTotal = this.state.orderDetails.order_total
+ let result = await axios.put(`http://localhost:3000/api/orders/${this.state.orderDetails.id}`, { activity: 'finalize' })
+ this.setState({ message: { id: 0, message: result.data.message }, orderTotal: orderTotal, orderDetails: {}})
+}
+```
+
+You also need to update the `addToOrder` function
+
+```js
+if (this.state.orderDetails.hasOwnProperty('id') && this.state.orderDetails.finalized === false)
+```
+
+And update the return statement with the new changes.
+
+```js
+return (
+ <>
+ {this.state.message.id === 0 && (
+
;
+ });
+} else {
+ orderDetailsDisplay = "Nothing to see";
+}
+```
+
+Run your tests now and they should be green. We have now finalized the entire creating order functionality both in the front and the backend. Go over your code again and marvel at your creation. This is one approach we can take to create an ordering functionality in React from scratch.
+
+The last thing we need to do is to add our payment gateway.
diff --git a/guides/react_checkout_guide/12_intro_to_stripe.md b/guides/react_checkout_guide/12_intro_to_stripe.md
new file mode 100644
index 00000000..b7e21fa2
--- /dev/null
+++ b/guides/react_checkout_guide/12_intro_to_stripe.md
@@ -0,0 +1,70 @@
+
+## Intro to Stripe
+
+This guide needs to have the order functionality from the previous chapter in place. We will continue on the code both from the backend as well as from the frontend.
+
+We are going to use stripe to finalize the order by paying for the products.
+
+Stripe is a payment gateway that is developer-friendly.
+In this section, we are going to set up the functionality that we need in order to make the payments with debit or credit card.
+
+First and foremost you need to create an account on stripe. We will get back to this later. But make sure that you have an account ready. So let's set the stage, create a new branch and let's get started.
+
+As with everything that we develop here at Craft Academy we work in a test-driven way. This means that we always start with our feature test to stay in scope but also make sure that we are not breaking any functionality that has already been added to the previous features.
+
+Start by creating a new feature by running the following command in your terminal.
+
+`$ touch cypress/integration/userCanMakePayment.feature.js`
+
+After the file has been created we need to add the following test.
+
+```js
+describe("User can add a product to his/her order", () => {
+ beforeEach(() => {
+ cy.server();
+ cy.route({
+ method: "GET",
+ url: "http://localhost:3000/api/products",
+ response: "fixture:product_data.json"
+ });
+
+ cy.route({
+ method: "POST",
+ url: "http://localhost:3000/api/orders",
+ response: "fixture:post_response.json"
+ });
+
+ cy.route({
+ method: "PUT",
+ url: "http://localhost:3000/api/orders/1",
+ response: "fixture:put_response.json"
+ });
+
+ cy.visit("http://localhost:3001");
+ cy.get("#product-2").within(() => {
+ cy.get("button")
+ .contains("Add to order")
+ .click();
+ });
+ cy.get("#product-3").within(() => {
+ cy.get("button")
+ .contains("Add to order")
+ .click();
+ });
+ cy.get("button")
+ .contains("View order")
+ .click();
+ });
+
+ it("user can pay for his order", () => {
+ cy.get("button")
+ .contains("Confirm!")
+ .click();
+ cy.get("#payment-form").should("exist");
+ });
+});
+```
+So what is happening in this test?
+Well, we are setting the stage and making sure that we can display a payment form. Remember that we always work in small chunks and that we continually refactor both our test but also our implementation code as we add more and more functionality.
+
+Let's head over to the component that we want to display our payment form.
diff --git a/guides/react_checkout_guide/13_setting_the_stage.md b/guides/react_checkout_guide/13_setting_the_stage.md
new file mode 100644
index 00000000..4a3a9ce2
--- /dev/null
+++ b/guides/react_checkout_guide/13_setting_the_stage.md
@@ -0,0 +1,57 @@
+## Setting the stage
+
+Start by adding a new state, where we are adding the payment form. Why are we setting it to false?
+
+```js
+state = {
+ productData: [],
+ message: {},
+ orderDetails: {},
+ showOrder: false,
+ orderTotal: "",
+ showPaymentForm: false
+};
+```
+
+After that, we need to refactor the button to show the form when the user clicks on it.
+
+
+```js
+;
+{
+ this.state.showPaymentForm && (
+
+
+
+ );
+}
+```
+Remember to also import the on the top of the file.
+But we don't have a PaymentForm component you are thinking. Well, that's right. We don't so...
+Let's create one.
+
+```bash
+$ touch src/components/PaymentForm.jsx
+```
+
+```js
+import React, { Component } from "react";
+
+class PaymentForm extends Component {
+ render() {
+ return (
+
+
Here we will show a payment form
+
+ );
+ }
+}
+
+export default PaymentForm;
+```
+Run your test, commit and all that fun stuff.
+Just to check, you did remember to create a new branch on git before we started right? Of course you did. silly me....
+
+
diff --git a/guides/react_checkout_guide/14_back_to_the_tests.md b/guides/react_checkout_guide/14_back_to_the_tests.md
new file mode 100644
index 00000000..9915d23b
--- /dev/null
+++ b/guides/react_checkout_guide/14_back_to_the_tests.md
@@ -0,0 +1,90 @@
+## Back to our tests
+
+Let's revisit our test for one moment. When we are dealing with stripe they provide us with a lot of functionality in terms of security and checks. This is good for development but could be an issue when we are trying to test the payment functionality. We need to test that we can see an iframe where we will be displaying the payment form.
+
+Refactor your test to look like this.
+
+```js
+it("user can pay for his order", () => {
+ cy.get("button")
+ .contains("Confirm!")
+ .click();
+ cy.get("#payment-form").should("exist");
+ cy.wait(1000);
+ cy.get('iframe[name^="__privateStripeFrame5"]').then($iframe => {
+ const $body = $iframe.contents().find("body");
+ cy.wrap($body)
+ .find('input[name="cardnumber"]')
+ .type("4242424242424242", { delay: 50 });
+ });
+});
+```
+
+We are finding an input field and filling it out with the card number. Small steps remember. Run your test and behold the error messages.
+
+Time for the implementation
+
+Add the following package.
+
+```bash
+$ yarn add react-stripe-elements
+```
+
+To use stripe we also need to add a script provided by stripe.
+
+Add a script to the head in index.html
+
+```html
+
+```
+
+And we also need to turn off some of the built-in settings on cypress just to make sure that we are not getting errors because of our browser.
+
+Setting chromeWebSecurity to false in Chrome-based browsers allows you to do the following:
+
+- Display insecure content
+- Navigate to any superdomain without cross-origin errors
+- Access cross-origin iframes that are embedded in your application
+
+Add `chromeWebSecurity` and set it to `false`
+
+`cypress.json`
+
+```json
+{
+ "baseUrl": "http://localhost:3001",
+ "chromeWebSecurity": false
+}
+```
+Head over to your `index.js` and import the StripeProvider.
+
+```js
+import { StripeProvider } from "react-stripe-elements";
+```
+
+And wrap component with the StripeProvider
+
+```js
+ReactDOM.render(
+
+
+ ,
+ document.getElementById("root")
+);
+```
+
+The StripeProvider gives us access to the Stripe object. The Stripe object will contain the secret, and publishable key which will allow us to access the Stripe API.
+
+Get your API key from the stripe dashboard, make sure that you select "Show test data" and go to the developer's tab. Copy your Publishable key and add it to the stripe provider in your App component.
+
+```js
+ReactDOM.render(
+
+
+ ,
+ document.getElementById("root")
+);
+```
+
+Time to start working on the payment form itself.
+
diff --git a/guides/react_checkout_guide/15_the_payment_form.md b/guides/react_checkout_guide/15_the_payment_form.md
new file mode 100644
index 00000000..c21b1307
--- /dev/null
+++ b/guides/react_checkout_guide/15_the_payment_form.md
@@ -0,0 +1,40 @@
+## The payment form
+
+Wrap the PaymentForm component with the Elements, and make sure that you import Elements from `"react-stripe-elements"`
+
+```js
+{
+ this.state.showPaymentForm && (
+
+
+
+
+
+ );
+}
+```
+
+Move over to your PaymentForm and update it with the following code.
+
+```js
+import React, { Component } from "react";
+import { injectStripe, CardNumberElement } from "react-stripe-elements";
+
+class PaymentForm extends Component {
+ render() {
+ return (
+ <>
+
+
+ >
+ );
+ }
+}
+
+export default injectStripe(PaymentForm);
+```
+
+So what are we doing here? We are adding an input field where the users can add their card number but to use that we need to use the injectStripe component to make use of their form elements.
+
+So we have added a rudimentary payment form and we have also added the package that we need to use stripe in our react app. But we need multiple input fields for the rest of our card information. So let's add that now.
+
diff --git a/guides/react_checkout_guide/16_adding_multiple_input_field.md b/guides/react_checkout_guide/16_adding_multiple_input_field.md
new file mode 100644
index 00000000..4a48bf7c
--- /dev/null
+++ b/guides/react_checkout_guide/16_adding_multiple_input_field.md
@@ -0,0 +1,131 @@
+## Adding multiple input fields in the payment form
+
+We need to update our test again for the remaining input fields, as well as with the assertion that the payment has gone through.
+
+```js
+it("user can pay for his order", () => {
+ cy.route({
+ method: "PUT",
+ url: "http://localhost:3000/api/orders/1",
+ body: { activity: "finalize" },
+ response: {
+ paid: true,
+ message: "Your order will be ready in 30 minutes!",
+ },
+ });
+ cy.get("button").contains("Confirm!").click();
+
+ cy.get("#payment-form").should("exist");
+ cy.wait(1000);
+ cy.get('iframe[name^="__privateStripeFrame5"]').then(($iframe) => {
+ const $body = $iframe.contents().find("body");
+ cy.wrap($body)
+ .find('input[name="cardnumber"]')
+ .type("4242424242424242", { delay: 50 });
+ });
+
+ cy.get('iframe[name^="__privateStripeFrame6"]').then(($iframe) => {
+ const $body = $iframe.contents().find("body");
+ cy.wrap($body).find('input[name="exp-date"]').type("1222", { delay: 10 });
+ });
+ cy.get('iframe[name^="__privateStripeFrame7"]').then(($iframe) => {
+ const $body = $iframe.contents().find("body");
+ cy.wrap($body).find('input[name="cvc"]').type("999", { delay: 10 });
+ });
+
+ cy.get("button").contains("Submit").click();
+
+ cy.get("#payment-form").should("not.exist");
+ cy.get(".message").should(
+ "contain",
+ "Your order will be ready in 30 minutes!"
+ );
+});
+```
+
+We are adding multiple tests for the different iframes that stripe provides us with, the "credit card numbers" we are passing are given to us by stripe for testing the functionality.
+
+Let us go back to your PaymentForm component and update the return with the rest of the payment fields. Make sure that you import all the stripe components
+
+```js
+return (
+ <>
+
+
+
+
+
+
+
+ >
+```
+
+Next we want to add a function called `payWithStripe` in the component.
+
+```js
+async payWithStripe() {
+ await this.props.stripe.createToken().then(response => {
+ if (response.token) {
+ try {
+ this.performPayment(response.token)
+ }
+ catch {
+
+ }
+ }
+
+ })
+ }
+```
+
+We also want to add a function called performPayment. This function will make the payment.
+
+```js
+async performPayment(token) {
+ let orderResponse = await axios.put(
+ `http://localhost:3000/api/orders/${this.props.orderDetails.id}`,
+ {
+ activity: 'finalize',
+ stripeToken: token
+ }
+ )
+ if (orderResponse.data.paid === true) {
+ this.props.finalizeOrder(orderResponse.data.message)
+
+ } else {
+ debugger
+ }
+}
+```
+
+And finally, we need to update the button
+
+```js
+
+```
+
+Make sure that you have made adequate imports to the component.
+
+Move over to the DisplayProduct component or your main component where you are adding the products to the order.
+
+Update the Props that you are sending through
+
+```js
+
+```
+
+And update the finalizeOrder function we are passing in the finializeOrder function as a prop to the component.
+
+```js
+async finalizeOrder(message) {
+ let orderTotal = this.state.orderDetails.order_total
+ this.setState({ message: { id: 0, message: message }, orderTotal: orderTotal, orderDetails: {}, showPaymentForm: false })
+}
+```
+
+Run your tests and they should go green.
+
+Let's move over to the backend
diff --git a/guides/react_checkout_guide/17_stripe_in_the_backend.md b/guides/react_checkout_guide/17_stripe_in_the_backend.md
new file mode 100644
index 00000000..e87d08ac
--- /dev/null
+++ b/guides/react_checkout_guide/17_stripe_in_the_backend.md
@@ -0,0 +1,96 @@
+## Stripe in the Backend
+
+Time to work on the backend. Make sure that you create a new branch here as well before you get started.
+
+Start with creating a request spec.
+
+```bash
+$ touch spec/requests/api/client_can_send_payment_request_spec.rb
+```
+
+Add the following test
+
+```rb
+RSpec.describe Api::OrdersController, type: :request do
+ let(:product_1) { create(:product, price: 50) }
+ let(:order) { create(:order) }
+ let!(:order_item) { create(:order_item, product: product_1, order: order) }
+
+ before do
+ put "/api/orders/#{order.id}",
+ params: { activity: "finalize",
+ stripetoken: "12345",
+ email: 'testing@test.com'
+ }
+ end
+
+ it 'user can pay for the order' do
+ expect(JSON.parse(response.body)).to eq JSON.parse('{"paid": true, "message": "Your order will be ready in 30 minutes!"}')
+ end
+end
+```
+
+Run your tests and the should be failing. That is a good thing because now we know what to do next.
+
+Head over to your orders controller and add the following functionality to the update action
+
+```rb
+def update
+ order = Order.find(params[:id])
+ if params[:activity]
+ payment_status = perform_stripe_payment
+
+ if payment_status == true
+ # successful payment
+ order.update_attribute(:finalized, true)
+ render json: { paid: true, message: 'Your order will be ready in 30 minutes!' }
+ else
+ # unsuccessful payment
+ end
+
+ else
+ product = Product.find(params[:product_id])
+ order.order_items.create(product: product)
+ render json: create_json_response(order)
+ end
+end
+```
+
+We are not dealing with the unsuccessful payment. Make sure that you add and test for an error message once we are done with the Happy path.
+
+You will get a Name error stating that there is no `perform_stripe_payment`
+
+Let's fix that. In the private section of the `orders` controller add the following method
+
+```rb
+def perform_stripe_payment
+ order = Order.find(params[:id])
+ customer = Stripe::Customer.create(
+ email: params[:email],
+ source: params[:stripeToken],
+ description: 'slowfood client'
+ )
+
+charge = Stripe::Charge.create(
+ customer: customer.id,
+ amount: order.order_total,
+ currency: 'sek'
+)
+ charge
+end
+```
+
+We are calling the Stripe gem to create a customer but also to make charge the customer.
+
+You will be getting errors now, we need to fix that by adding the stripe gem to our Gemfile.
+
+`gem 'stripe-rails'`
+
+Run the tests again and your new error message should something in the lines of
+
+```bash
+Stripe::AuthenticationError:
+ No API key provided. Set your API key using "Stripe.api_key = ". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions.
+```
+
+This means that we need to use our API key that Stripe has provided for us.
diff --git a/guides/react_checkout_guide/18_adding_credentials.md b/guides/react_checkout_guide/18_adding_credentials.md
new file mode 100644
index 00000000..b7cf610d
--- /dev/null
+++ b/guides/react_checkout_guide/18_adding_credentials.md
@@ -0,0 +1,76 @@
+## Adding credentials
+
+Grab the "secret key" from your Stripe dashboard.
+
+Do not reveal this key. To use this securely we will add the key to our credentials file. Credentials is an encrypted file in rails. We need the master.key file in our config folder to open this file. The master.key is added to your gitignore by default so that you don\t accidentally push it to version control.
+
+Use the following steps to modify your credentials.
+
+Head over to your terminal and run the following command.
+
+```bash
+$ EDITOR='code --wait' rails credentials:edit
+```
+Notice that your terminal has been locked and the credentials file has been decrypted and opened in your code editor. The terminal will be locked until you save and close the credentials file in your code editor. But before we do that we need to add the secret key and publishable key in this file.
+
+
+```yml
+secret_key_base: 50fba2642c2978bcf7536a53606328149f18c7382a070822bf86f0d141095d9ed2c2c472b77577df08c34c61e36f3a27aaba26fb746cfb6a79ce3456edbcb04b
+stripe:
+ pk_key: your_publishable_key_here
+ secret_key: your_secret_key_here
+```
+
+Save and close the file and you should be getting the following message in the terminal.
+
+```bash
+16:16 $ EDITOR='code --wait' rails credentials:edit
+File encrypted and saved.
+```
+
+To make use of the stripe credentials we need to add some configurations.
+
+Head over to the `application.rb` file where we turn off the generators etc and add the following configurations.
+
+```rb
+ config.generators do |generate|
+ generate.helper false
+ generate.assets false
+ generate.view_specs false
+ generate.helper_specs false
+ generate.routing_specs false
+ generate.controller_specs false
+ end
+ config.stripe.secret_key = Rails.application.credentials.stripe[:secret_key]
+ config.stripe.publishable_key = Rails.application.credentials.stripe[:pk_key]
+ end
+```
+
+We do not want to make actual network calls to stripe when we are in development mode. For that we will use a gem called `stripe-ruby-mock`. Make sure that you use the version stated below.
+
+Add the following gem to the Gemfile
+
+`gem 'stripe-ruby-mock', '~> 2.5.6', require: 'stripe_mock'`
+
+Then we want to update the `rails_helper` to add the mock functionality
+
+Add `require 'stripe_mock'` to the rails_helper and add the configuration to RSpec
+
+```rb
+RSpec.configure do |config|
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
+ config.use_transactional_fixtures = true
+ config.infer_spec_type_from_file_location!
+ config.filter_rails_from_backtrace!
+ config.include FactoryBot::Syntax::Methods
+ config.include(Shoulda::Matchers::ActiveRecord, type: :model)
+ config.before(:each) do
+ @stripe_test_helper = StripeMock.create_test_helper
+ StripeMock.start
+ end
+ config.after(:each) do
+ StripeMock.stop
+ end
+end
+```
+
diff --git a/guides/react_checkout_guide/19_finalizing_the_payment.md b/guides/react_checkout_guide/19_finalizing_the_payment.md
new file mode 100644
index 00000000..f143148c
--- /dev/null
+++ b/guides/react_checkout_guide/19_finalizing_the_payment.md
@@ -0,0 +1,35 @@
+## Finalizing the payment
+
+Next up we want to modify the test with the mocked data.
+
+```rb
+before do
+ put "/api/orders/#{order.id}",
+ params: { activity: "finalize",
+ stripeToken: StripeMock.create_test_helper.generate_card_token,
+ email:'user@mail.com'}
+end
+```
+
+And finally update the perform_stripe_payment method in our controller.
+
+```rb
+def perform_stripe_payment
+ order = Order.find(params[:id])
+ customer = Stripe::Customer.create(
+ email: params[:email],
+ source: params[:stripeToken],
+ description: 'slowfood client'
+ )
+
+charge = Stripe::Charge.create(
+ customer: customer.id,
+ amount: order.order_total.to_i * 100,
+ currency: 'sek'
+)
+ charge.paid
+end
+```
+
+Run your tests now and they should be going green. So Stripe added.
+Let's celebrate by making a commit!