Skip to content

Commit

Permalink
Add PayPal buttons to frontend checkout flow
Browse files Browse the repository at this point in the history
Adds paypal buttons and associated javascripts to allow customers to
checkout using PayPal.
  • Loading branch information
seand7565 committed May 25, 2020
1 parent 26ae4d8 commit 4d787e3
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module SolidusPaypalCommercePlatform
class OrdersController < ::Spree::Api::BaseController
before_action :load_order
before_action :load_payment_method
skip_before_action :authenticate_user

def create
authorize! :update, @order, order_token
request = SolidusPaypalCommercePlatform::Requests.new(
@payment_method.client_id, @payment_method.client_secret
).create_order(@order, @payment_method.auto_capture)
render json: request, status: :ok
end

private

def load_order
@order = Spree::Order.find_by!(number: params[:order_id])
end

def load_payment_method
@payment_method = Spree::PaymentMethod.find(params[:payment_method_id])
end

end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module SolidusPaypalCommercePlatform
class PaymentsController < ::Spree::Api::BaseController
before_action :load_order
skip_before_action :authenticate_user

def create
authorize! :update, @order, order_token
paypal_order_id = paypal_params[:paypal_order_id]

if !paypal_order_id
return redirect_to checkout_state_path(@order.state), notice: "Invalid order confirmation data passed in"
end

if @order.complete?
return redirect_to spree.order_path(@order), notice: "Order is already in complete state"
end

source = SolidusPaypalCommercePlatform::Source.new(paypal_order_id: paypal_order_id)

source.transaction do
if source.save!
payment = @order.payments.create!({
payment_method_id: paypal_params[:payment_method_id],
source: source
})

render json: {}, status: :ok
end
end
end

private

def paypal_params
params.permit(:paypal_order_id, :order_id, :order_token, :payment_method_id)
end

def load_order
@order = Spree::Order.find_by!(number: params[:order_id])
end

end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
module SolidusPaypalCommercePlatform
module OrderFormatHelper

def order_json(order, intent)
@order = order
return {
intent: intent,
purchase_units: purchase_units,
payer: payer
}
end

private

def address(type)
{
address_line_1: @order.send(type.to_sym).address1,
address_line_2: @order.send(type.to_sym).address2,
admin_area_1: @order.send(type.to_sym).state.try(:abbr) || @order.send(type.to_sym).state_name,
admin_area_2: @order.send(type.to_sym).city,
postal_code: @order.send(type.to_sym).zipcode,
country_code: @order.send(type.to_sym).country.iso,
}
end

def payer
{
name: name(@order.bill_address),
email_address: @order.email,
address: address("bill_address")
}
end

def name(address)
{
given_name: address.firstname,
surname: address.lastname
}
end

def purchase_units
[
{
amount: amount,
items: line_items,
shipping: shipping
}
]
end

def shipping
{
name: {
full_name: @order.ship_address.name
},
email_address: @order.email,
address: address("ship_address")
}
end

def line_items
@order.line_items.map{ |line_item|
{
name: line_item.product.name,
unit_amount: price(line_item.price),
tax: price(line_item.included_tax_total / line_item.quantity),
quantity: line_item.quantity
}
}
end

def amount
{
currency_code: @order.currency,
value: @order.total,
breakdown: breakdown
}
end

def breakdown
{
item_total: price(@order.item_total),
shipping: price(@order.shipment_total),
tax_total: price(@order.tax_total),
discount: price(@order.adjustments.sum(&:amount))
}
end

def price(amount)
{
currency_code: @order.currency,
value: amount
}
end

end
end
18 changes: 18 additions & 0 deletions app/models/solidus_paypal_commerce_platform/gateway.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
module SolidusPaypalCommercePlatform
class Gateway < SolidusSupport.payment_method_parent_class
preference :client_id, :string
preference :client_secret, :string

def partial_name
"paypal_commerce_platform"
end

def client_id
preferences[:client_id]
end

def client_secret
preferences[:client_secret]
end

def payment_source_class
Source
end

end
end
21 changes: 21 additions & 0 deletions app/models/solidus_paypal_commerce_platform/requests.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module SolidusPaypalCommercePlatform
class Requests
include SolidusPaypalCommercePlatform::OrderFormatHelper
require 'paypal-checkout-sdk'

class Request
Expand All @@ -23,8 +24,28 @@ def trade_tokens(credentials)
get_api_credentials({accessToken:access_token}).result
end

def create_order(order, auto_capture)
intent = auto_capture ? "CAPTURE" : "AUTHORIZE"
post_order(order, intent).result
end

private

def post_order(order, intent)
@client.execute(
jack = Request.new({
path: "/v2/checkout/orders",
body: order_json(order, intent),
headers: {
"Content-Type" => "application/json",
"Authoriation" => @env.authorizationString(),
"PayPal-Partner-Attribution-Id" => "Solidus_PCP_SP",
},
verb: "POST"
})
)
end

def get_access_token(credentials)
@client.execute(
Request.new({
Expand Down
23 changes: 23 additions & 0 deletions app/models/solidus_paypal_commerce_platform/source.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module SolidusPaypalCommercePlatform
class Source < SolidusSupport.payment_source_parent_class
self.table_name = "paypal_commerce_platform_sources"
validates_presence_of :paypal_order_id, :payment_method_id

def actions
%w(capture void credit)
end

def can_capture?(payment)
payment.pending? || payment.checkout?
end

def can_void?(payment)
!payment.failed? && !payment.void?
end

def can_credit?(payment)
false
end

end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script
src="https://www.paypal.com/sdk/js?client-id=ATDpQjHzjCz_C_qbbJ76Ca0IjcmwlS4FztD6YfuRFZXDCmcWWw8-8QWcF3YIkbC85ixTUuuSEvrBMVSX">
</script>

<div id="paypal-button-container"></div>
<input type="hidden" name="payment_source[<%= payment_method.id %>][paypal_order_id]" id="order_payments_attributes__source_attributes__paypal_order_id">

<script>
$( document ).ready(function() {
paypal.Buttons({
createOrder: function (data, actions) {
return Spree.ajax({
url: '/solidus_paypal_commerce_platform/orders',
method: 'POST',
data: {
payment_method_id: <%= payment_method.id %>,
order_id: Spree.current_order_id,
order_token: Spree.current_order_token
}
}).then(function(res) {
return res.table.id;
})
},
onApprove: function (data, actions) {
// return Spree.ajax({
// url: '/solidus_paypal_commerce_platform/payments',
// method: 'POST',
// data: {
// payment_method_id: payment_method.id,
// order_id: Spree.current_order_id,
// order_token: Spree.current_order_token,
// paypal_order_id: data.orderID
// }
// }).then(function(res) {
// console.log(res)
// if (!res.ok) {
// alert('Something went wrong');
// }
// else {
// $("#checkout_form_payment").submit()
// }
// });
$("#order_payments_attributes__source_attributes__paypal_order_id").val(data.orderId)
}
}).render('#paypal-button-container');
})
</script>
3 changes: 2 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
SolidusPaypalCommercePlatform::Engine.routes.draw do
# Add your extension routes here
resources :wizard, only: [:create]
# post :paypal_wizard, to: "wizard#create"
resources :orders, only: [:create]
resources :payments, only: [:create]
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class AddPaypalCommercePlatformSources < ActiveRecord::Migration[6.0]
def change
create_table :paypal_commerce_platform_sources do |t|
t.string :paypal_order_id
t.integer :payment_method_id
t.timestamps
end
end
end
3 changes: 2 additions & 1 deletion lib/solidus_paypal_commerce_platform/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ class Engine < Rails::Engine

initializer "register_solidus_paypal_commerce_platform_gateway", after: "spree.register.payment_methods" do |app|
app.config.spree.payment_methods << SolidusPaypalCommercePlatform::Gateway
Spree::PermittedAttributes.source_attributes.concat [:paypal_order_id]
end

initializer "register_solidus_paypal_commerce_platform_wizard", after: "spree.register.payment_methods" do |app|
# Adding the class set below - if this is ported to core, we can remove this line.
Spree::Core::Environment.add_class_set("payment_setup_wizards")
Expand Down

0 comments on commit 4d787e3

Please sign in to comment.