Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Commit

Permalink
Merge pull request #16 from solidusio/end_to_end_transaction
Browse files Browse the repository at this point in the history
End to end transaction
  • Loading branch information
cbrunsdon authored Sep 13, 2016
2 parents d162c04 + 983e818 commit dcf35a4
Show file tree
Hide file tree
Showing 12 changed files with 1,125 additions and 5 deletions.
107 changes: 106 additions & 1 deletion app/assets/javascripts/spree/frontend/solidus_paypal_braintree.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,107 @@
// Placeholder manifest file.
// the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js'
// the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js'

window.SolidusPaypalBraintree = {
APPLE_PAY_API_VERSION: 1,

initialize: function(authToken, clientReadyCallback) {
braintree.client.create({
authorization: authToken
}, function (clientErr, clientInstance) {
if (clientErr) {
console.error('Error creating client:', clientErr);
return;
}
clientReadyCallback(clientInstance);
});
},

setupApplePay: function(braintreeClient, merchantId, readyCallback) {
if(window.ApplePaySession) {
var promise = ApplePaySession.canMakePaymentsWithActiveCard(merchantId);
promise.then(function (canMakePayments) {
if (canMakePayments) {
braintree.applePay.create({
client: braintreeClient
}, function (applePayErr, applePayInstance) {
if (applePayErr) {
console.error("Error creating ApplePay:", applePayErr);
return;
}
readyCallback(applePayInstance);
});
}
});
};
},

initializeApplePaySession: function(applePayInstance, storeName, paymentRequest, sessionCallback) {

paymentRequest['requiredShippingContactFields'] = ['postalAddress', 'phone', 'email']
var paymentRequest = applePayInstance.createPaymentRequest(paymentRequest);

var session = new ApplePaySession(SolidusPaypalBraintree.APPLE_PAY_API_VERSION, paymentRequest);
session.onvalidatemerchant = function (event) {
applePayInstance.performValidation({
validationURL: event.validationURL,
displayName: storeName,
}, function (validationErr, merchantSession) {
if (validationErr) {
console.error('Error validating Apple Pay:', validationErr);
session.abort();
return;
};
session.completeMerchantValidation(merchantSession);
});
};

session.onpaymentauthorized = function (event) {
applePayInstance.tokenize({
token: event.payment.token
}, function (tokenizeErr, payload) {
if (tokenizeErr) {
console.error('Error tokenizing Apple Pay:', tokenizeErr);
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
session.completePayment(ApplePaySession.STATUS_SUCCESS);

SolidusPaypalBraintree.setBraintreeApplePayContact(event.payment.shippingContact);
SolidusPaypalBraintree.submitBraintreePayload(payload);
});
};

sessionCallback(session);

session.begin();
},

setBraintreeApplePayContact: function(appleContact) {
var apple_map = {
locality: 'city',
countryCode: 'country_code',
familyName: 'last_name',
givenName: 'first_name',
postalCode: 'zip',
administrativeArea: 'state_code',
}
for (var key in apple_map) {
document.querySelector("#transaction_address_attributes_" + apple_map[key]).value = appleContact[key];
}

window.addressCon = appleContact;
document.querySelector("#transaction_address_attributes_address_line_1").value = appleContact.addressLines[0];

if(appleContact.addressLines.length > 1) {
document.querySelector("#transaction_address_attributes_address_line_2").value = appleContact.addressLines[1];
}

document.querySelector("#transaction_phone").value = appleContact.phoneNumber;
document.querySelector("#transaction_email").value = appleContact.emailAddress;
},

submitBraintreePayload: function(payload) {
document.querySelector("#transaction_nonce").value = payload.nonce;
document.querySelector("#transaction_payment_type").value = payload.type;
document.querySelector('#new_transaction').submit();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class SolidusPaypalBraintree::TransactionsController < Spree::StoreController
PERMITTED_BRAINTREE_TRANSACTION_PARAMS = [
:nonce,
:payment_type,
:phone,
:email,
address_attributes: [
:country_code, :last_name, :first_name,
:city, :zip, :state_code, :address_line_1, :address_line_2
]
]

def create
transaction = SolidusPaypalBraintree::Transaction.new transaction_params

if transaction.valid?
import = SolidusPaypalBraintree::TransactionImport.new(current_order, transaction)
import.import!

if import.order.complete?
return redirect_to spree.order_path(import.order)
else
return redirect_to spree.checkout_state_path(import.order.state)
end
else
render text: transaction.errors
end
end

private

def transaction_params
params.require(:transaction)
.permit(PERMITTED_BRAINTREE_TRANSACTION_PARAMS)
.merge({ payment_method: payment_method })
end

def payment_method
SolidusPaypalBraintree::Gateway.find(params[:payment_method_id])
end
end
26 changes: 26 additions & 0 deletions app/models/solidus_paypal_braintree/transaction.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'active_model'
require 'solidus_paypal_braintree/transaction_address'

module SolidusPaypalBraintree
class Transaction
include ActiveModel::Model

attr_accessor :nonce, :payment_method, :payment_type, :address, :email, :phone

validates :nonce, presence: true
validates :payment_method, presence: true
validates :payment_type, presence: true
validates :phone, presence: true
validates :email, presence: true

validate do
unless payment_method.is_a? SolidusPaypalBraintree::Gateway
errors.add(:payment_method, 'Must be braintree')
end
end

def address_attributes=(attributes)
self.address = TransactionAddress.new attributes
end
end
end
10 changes: 10 additions & 0 deletions app/models/solidus_paypal_braintree/transaction_address.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'active_model'

module SolidusPaypalBraintree
class TransactionAddress
include ActiveModel::Model

attr_accessor :country_code, :last_name, :first_name,
:city, :zip, :state_code, :address_line_1, :address_line_2
end
end
63 changes: 63 additions & 0 deletions app/models/solidus_paypal_braintree/transaction_import.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require 'active_model'

module SolidusPaypalBraintree
class TransactionImport
attr_reader :transaction, :order

def initialize(order, transaction)
@order = order
@transaction = transaction
end

def source
SolidusPaypalBraintree::Source.new nonce: transaction.nonce,
payment_type: transaction.payment_type,
user: user
end

def user
order.user
end

def import!
order.email = user.try!(:email) || transaction.email

if address
order.shipping_address = order.billing_address = address
# work around a bug in most solidus versions
# about tax zone cachine between address changes
order.instance_variable_set("@tax_zone", nil)
order.next
end

order.payments.new source: source,
payment_method: transaction.payment_method,
amount: order.total

order.save!

advance_order
end

def address
transaction.address.try do |ta|
country = Spree::Country.find_by(iso: ta.country_code.upcase)
Spree::Address.new first_name: ta.first_name,
last_name: ta.last_name,
city: ta.city,
country: country,
state_name: ta.state_code,
address1: ta.address_line_1,
address2: ta.address_line_2,
zipcode: ta.zip,
phone: transaction.phone
end
end

protected

def advance_order
order.next! until order.state == "confirm"
end
end
end
16 changes: 16 additions & 0 deletions app/views/solidus_paypal_braintree/_transaction_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<%= form_for [main_app, SolidusPaypalBraintree::Transaction.new], id: 'braintree_complete_form', as: :transaction do |f| %>
<%= f.hidden_field :nonce %>
<%= f.hidden_field :payment_type %>
<%= hidden_field_tag :payment_method_id, payment_method.id %>
<%= f.hidden_field :phone %>
<%= f.hidden_field :email %>

<%= f.fields_for :address, SolidusPaypalBraintree::TransactionAddress.new do |fa| %>
<% [:country_code, :last_name, :first_name,
:city, :zip, :state_code,
:address_line_1, :address_line_2].each do |address_field| %>
<%= fa.hidden_field address_field %>
<% end %>
<% end %>

<% end %>
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
Rails.application.routes.draw do
namespace :solidus_paypal_braintree do
resource :checkout, only: [:update, :edit]

resource :transactions, only: [:create]
end
end
2 changes: 2 additions & 0 deletions lib/solidus_paypal_braintree/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ def self.activate
end
end

config.assets.precompile += ['spree/frontend/solidus_paypal_braintree']

config.to_prepare(&method(:activate).to_proc)
end
end
Loading

0 comments on commit dcf35a4

Please sign in to comment.