solidus_paybright 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 428533288de6df5ad9fa6ee5049fc880786f4f54
4
+ data.tar.gz: 263bad7f16a2dacaeac90c92c8787ae1f5e1cf54
5
+ SHA512:
6
+ metadata.gz: 082000ec87e0ea6d4db5751a827998775a57c05666ab05c882b73a14534d7971f138d28f726a3bbe51775be783f201a1d09fac54e7126eeaf2e1c69ec20d2d16
7
+ data.tar.gz: 6e24fbd70d6e193e23d37ce64009dde7471676a64e2d58fb8e6af9a2c2ca38ee2594fe036d9612839f80f0e3e971284bf1dc72d428c6beee007d44e3083c051b
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2017 Stembolt
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name Spree nor the names of its contributors may be used to
13
+ endorse or promote products derived from this software without specific
14
+ prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # SolidusPaybright
2
+
3
+ This extension provides the [Paybright](https://paybright.com/) payment option
4
+ for your Solidus storefront
5
+
6
+ ## Installation
7
+
8
+ Add solidus_paybright to your Gemfile:
9
+
10
+ ```ruby
11
+ gem 'solidus_paybright'
12
+ ```
13
+
14
+ Bundle your dependencies and run the installation generator:
15
+
16
+ ```shell
17
+ bundle
18
+ bundle exec rails g solidus_paybright:install
19
+ ```
20
+
21
+ ## Configuration
22
+
23
+ Configure the required credentials using static preferences, for example:
24
+
25
+ ```ruby
26
+ Spree.config do |config|
27
+ config.static_model_preferences.add(
28
+ Spree::PaymentMethod::Paybright,
29
+ "paybright_credentials",
30
+ api_key: ENV["PAYBRIGHT_API_KEY"],
31
+ api_token: ENV["PAYBRIGHT_API_TOKEN"],
32
+ test_mode: true,
33
+ shop_country_code: "CA",
34
+ shop_name: "My shop",
35
+ )
36
+ end
37
+ ```
38
+
39
+ Also make sure to set the `default_url_options` values for the Solidus engine,
40
+ on all the environments. This is required to generate absolute URLs:
41
+
42
+ ```ruby
43
+ Spree::Core::Engine.routes.default_url_options = {
44
+ host: "example.com",
45
+ protocol: "https"
46
+ }
47
+ ```
48
+
49
+ ## Testing
50
+
51
+ First bundle your dependencies, then run `rake`. `rake` will default to building the dummy app if it does not exist, then it will run specs, and [Rubocop](https://github.com/bbatsov/rubocop) static code analysis. The dummy app can be regenerated by using `rake test_app`.
52
+
53
+ ```shell
54
+ bundle
55
+ bundle exec rake
56
+ ```
57
+
58
+ When testing your applications integration with this extension you may use it's factories.
59
+ Simply add this require statement to your spec_helper:
60
+
61
+ ```ruby
62
+ require 'solidus_paybright/factories'
63
+ ```
64
+
65
+ Copyright (c) 2017 Stembolt, released under the New BSD License
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'bundler'
2
+
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ begin
6
+ require 'spree/testing_support/extension_rake'
7
+ require 'rubocop/rake_task'
8
+ require 'rspec/core/rake_task'
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ RuboCop::RakeTask.new
13
+
14
+ task default: %i(first_run rubocop spec)
15
+ rescue LoadError
16
+ # no rspec available
17
+ end
18
+
19
+ task :first_run do
20
+ if Dir['spec/dummy'].empty?
21
+ Rake::Task[:test_app].invoke
22
+ Dir.chdir('../../')
23
+ end
24
+ end
25
+
26
+ desc 'Generates a dummy app for testing'
27
+ task :test_app do
28
+ ENV['LIB_NAME'] = 'solidus_paybright'
29
+ Rake::Task['extension:test_app'].invoke
30
+ end
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/backend/all.js'
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js'
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/frontend/all.css'
4
+ */
@@ -0,0 +1,75 @@
1
+ module SolidusPaybright
2
+ module CheckoutControllerDecorator
3
+ def update
4
+ if SolidusSupport.solidus_gem_version >= Gem::Version.new("2.2.0")
5
+ update_v2_2
6
+ else
7
+ update_v1_2
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def update_v2_2
14
+ if update_order
15
+
16
+ assign_temp_address
17
+ return if follow_payment_redirect
18
+
19
+ unless transition_forward
20
+ redirect_on_failure
21
+ return
22
+ end
23
+
24
+ if @order.completed?
25
+ finalize_order
26
+ else
27
+ send_to_next_state
28
+ end
29
+
30
+ else
31
+ render :edit
32
+ end
33
+ end
34
+
35
+ def update_v1_2
36
+ if Spree::OrderUpdateAttributes.new(@order, update_params, request_env: request.headers.env).apply
37
+ @order.temporary_address = !params[:save_user_address]
38
+ return if follow_payment_redirect
39
+
40
+ success = if @order.state == 'confirm'
41
+ @order.complete
42
+ else
43
+ @order.next
44
+ end
45
+ if !success
46
+ flash[:error] = @order.errors.full_messages.join("\n")
47
+ redirect_to(checkout_state_path(@order.state)) && return
48
+ end
49
+
50
+ if @order.completed?
51
+ @current_order = nil
52
+ flash.notice = Spree.t(:order_processed_successfully)
53
+ flash['order_completed'] = true
54
+ redirect_to completion_route
55
+ else
56
+ redirect_to checkout_state_path(@order.state)
57
+ end
58
+ else
59
+ render :edit
60
+ end
61
+ end
62
+
63
+ def follow_payment_redirect
64
+ return unless params[:state] == "payment"
65
+
66
+ payment = @order.payments.valid.last
67
+ if redirect_url = payment.try(:redirect_url)
68
+ redirect_to redirect_url
69
+ true
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ Spree::CheckoutController.prepend SolidusPaybright::CheckoutControllerDecorator
@@ -0,0 +1,63 @@
1
+ module Spree
2
+ class PaybrightController < Spree::BaseController
3
+ # Server2server call that gets parameters about the results of the Paybright
4
+ # transaction.
5
+ def callback
6
+ result, message = handle_callback_params!
7
+ status = result ? :ok : :bad_request
8
+ render plain: message, status: status
9
+ end
10
+
11
+ # The user is redirected here after completing the full Paybright checkout.
12
+ # It also gets the same parameters of #callback.
13
+ def complete
14
+ handle_callback_params!
15
+ redirect_to redirect_path(@payment.try(:order))
16
+ end
17
+
18
+ # The user is redirected here after failing some intermediate step of the
19
+ # Paybright checkout.
20
+ def cancel
21
+ payment = Spree::Payment.find_by(id: params[:payment_id])
22
+ redirect_to redirect_path(payment.try(:order))
23
+ end
24
+
25
+ private
26
+
27
+ def paybright_params
28
+ params.permit(
29
+ :x_account_id, :x_reference, :x_currency, :x_test, :x_amount,
30
+ :x_gateway_reference, :x_timestamp, :x_result, :x_signature, :x_message
31
+ )
32
+ end
33
+
34
+ def redirect_path(order)
35
+ return cart_path unless order
36
+ order.complete? ? order_path(order) : checkout_state_path(order.state)
37
+ end
38
+
39
+ def handle_callback_params!
40
+ @payment = Spree::Payment.find_by(id: paybright_params[:x_reference])
41
+
42
+ valid, error = SolidusPaybright::CallbackValidator.new(paybright_params).call
43
+ unless valid
44
+ logger.debug "Paybright: #{error}"
45
+ return [false, error]
46
+ end
47
+
48
+ @payment.update_attributes!(
49
+ response_code: paybright_params[:x_gateway_reference],
50
+ amount: paybright_params[:x_amount]
51
+ )
52
+ @payment.complete!
53
+ advance_and_complete(@payment.order)
54
+
55
+ [true, ""]
56
+ end
57
+
58
+ def advance_and_complete(order)
59
+ order.next!
60
+ order.complete! if order.can_complete?
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ module SolidusPaybright
2
+ class Configuration < Spree::Preferences::Configuration
3
+ attr_writer :test_redirect_url
4
+ def test_redirect_url
5
+ @test_redirect_url ||= "https://dev.healthsmartfinancial.com/CheckOut/AppForm.aspx"
6
+ end
7
+
8
+ attr_writer :live_redirect_url
9
+ def live_redirect_url
10
+ @live_redirect_url ||= "https://app.healthsmartfinancial.com/checkout/appform.aspx"
11
+ end
12
+
13
+ attr_writer :test_api_endpoint
14
+ def test_api_endpoint
15
+ # no trailing slash
16
+ @test_api_endpoint ||= "https://devapi.paybright.com/api"
17
+ end
18
+
19
+ attr_writer :live_api_endpoint
20
+ def live_api_endpoint
21
+ # no trailing slash
22
+ @live_api_endpoint ||= "https://api.paybright.com/api"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ module SolidusPaybright
2
+ module PaymentDecorator
3
+ def redirect_url
4
+ payment_method.redirect_url(self)
5
+ end
6
+ end
7
+ end
8
+
9
+ Spree::Payment.include SolidusPaybright::PaymentDecorator
@@ -0,0 +1,92 @@
1
+ module Spree
2
+ class PaymentMethod::Paybright < Spree::PaymentMethod
3
+ preference :api_key, :string
4
+ preference :api_token, :string
5
+ preference :shop_country_code, :string
6
+ preference :shop_name, :string
7
+ preference :test_mode, :boolean, default: true
8
+
9
+ def payment_source_class
10
+ nil
11
+ end
12
+
13
+ def source_required?
14
+ false
15
+ end
16
+
17
+ def auto_capture
18
+ false
19
+ end
20
+
21
+ # @return [Array<String>] the actions available on this payment method
22
+ def actions
23
+ %w(void credit)
24
+ end
25
+
26
+ def redirect_url(payment)
27
+ uri = URI.parse(paybright_redirect_url)
28
+ params = SolidusPaybright::ParamsHelper.new(payment).build_redirect_params
29
+ uri.query = params.to_query
30
+
31
+ uri.to_s
32
+ end
33
+
34
+ def void(transaction_id, _gateway_options = {})
35
+ if api_client.void!(transaction_id)
36
+ response(
37
+ true,
38
+ Spree.t("paybright.successful_action", action: "void", id: transaction_id)
39
+ )
40
+ else
41
+ response(
42
+ false,
43
+ Spree.t("paybright.unsuccessful_action", action: "void", id: transaction_id)
44
+ )
45
+ end
46
+ end
47
+
48
+ def credit(amount_in_cents, transaction_id, _gateway_options = {})
49
+ if api_client.refund!(transaction_id, amount_in_cents / 100.0)
50
+ response(
51
+ true,
52
+ Spree.t("paybright.successful_action", action: "credit", id: transaction_id)
53
+ )
54
+ else
55
+ response(
56
+ false,
57
+ Spree.t("paybright.unsuccessful_action", action: "credit", id: transaction_id)
58
+ )
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def paybright_redirect_url
65
+ if preferred_test_mode
66
+ SolidusPaybright::Config.test_redirect_url
67
+ else
68
+ SolidusPaybright::Config.live_redirect_url
69
+ end
70
+ end
71
+
72
+ def api_url
73
+ if preferred_test_mode
74
+ SolidusPaybright::Config.test_api_endpoint
75
+ else
76
+ SolidusPaybright::Config.live_api_endpoint
77
+ end
78
+ end
79
+
80
+ def api_client
81
+ SolidusPaybright::ApiClient.new(
82
+ preferred_api_key,
83
+ preferred_api_token,
84
+ api_url
85
+ )
86
+ end
87
+
88
+ def response(success, message)
89
+ ActiveMerchant::Billing::Response.new(success, message, {}, {})
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,9 @@
1
+ module SolidusPaybright
2
+ module PaymentMethodDecorator
3
+ def redirect_url(_payment)
4
+ nil
5
+ end
6
+ end
7
+ end
8
+
9
+ Spree::PaymentMethod.include SolidusPaybright::PaymentMethodDecorator
@@ -0,0 +1,9 @@
1
+ en:
2
+ spree:
3
+ api_key: API key
4
+ api_token: API token
5
+ shop_country_code: Shop country code
6
+ shop_name: Shop name
7
+ paybright:
8
+ successful_action: Successful Paybright %{action} (transaction_id %{id})
9
+ unsuccessful_action: Unsuccessful Paybright %{action} (transaction_id %{id})
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ post "/paybright/callback", to: "paybright#callback", as: :paybright_callback
3
+ get "/paybright/cancel/:payment_id", to: "paybright#cancel", as: :paybright_cancel
4
+ get "/paybright/complete", to: "paybright#complete", as: :paybright_complete
5
+ end
@@ -0,0 +1,30 @@
1
+ module SolidusPaybright
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ class_option :auto_run_migrations, type: :boolean, default: false
5
+
6
+ def add_javascripts
7
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_paybright\n"
8
+ append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_paybright\n"
9
+ end
10
+
11
+ def add_stylesheets
12
+ inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/solidus_paybright\n", before: /\*\//, verbose: true
13
+ inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/solidus_paybright\n", before: /\*\//, verbose: true
14
+ end
15
+
16
+ def add_migrations
17
+ run 'bundle exec rake railties:install:migrations FROM=solidus_paybright'
18
+ end
19
+
20
+ def run_migrations
21
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
22
+ if run_migrations
23
+ run 'bundle exec rake db:migrate'
24
+ else
25
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,85 @@
1
+ require "typhoeus"
2
+ require "securerandom"
3
+ require "digest"
4
+ require "cgi"
5
+
6
+ module SolidusPaybright
7
+ class ApiClient
8
+ # @param api_key [String] The account Api key
9
+ # @param api_token [String] The account secret Api token
10
+ # @param base_url [String] The API base endpoint
11
+ def initialize(api_key, api_token, base_url)
12
+ @api_key = api_key
13
+ @api_token = api_token
14
+ @base_url = base_url
15
+ end
16
+
17
+ # This cancels an authorized order. For example, when a user decides to
18
+ # cancel their order before it's fulfilled. Once a loan is voided, it is
19
+ # permanently canceled and cannot be reauthorized.
20
+ #
21
+ # @param paybright_order_id [String] The Paybright order reference
22
+ # @return [Boolean] The successfulness of the void operation
23
+ def void!(paybright_order_id)
24
+ uri = "#{@base_url}/orders/#{paybright_order_id}/void/"
25
+ body = ""
26
+ nonce = SecureRandom.hex
27
+
28
+ response = Typhoeus.post(
29
+ uri,
30
+ body: body,
31
+ headers: {
32
+ "Authorization" => auth_header_for(nonce, body, uri)
33
+ }
34
+ )
35
+
36
+ handle_response(response)
37
+ end
38
+
39
+ # Refund some amount of refunds on an Paybright order.
40
+ # Once a loan is fully refunded it cannot be reinstated.
41
+ #
42
+ # @param paybright_order_id [String] The Paybright order reference
43
+ # @param amount [String] The amount to refund
44
+ # @return [Boolean] The successfulness of the refund operation
45
+ def refund!(paybright_order_id, amount)
46
+ uri = "#{@base_url}/orders/#{paybright_order_id}/refund/"
47
+ body = "{\"amount\": #{amount}}"
48
+ nonce = SecureRandom.hex
49
+
50
+ response = Typhoeus.post(
51
+ uri,
52
+ body: body,
53
+ headers: {
54
+ "Authorization" => auth_header_for(nonce, body, uri),
55
+ "Content-Type" => "application/json"
56
+ }
57
+ )
58
+
59
+ handle_response(response)
60
+ end
61
+
62
+ private
63
+
64
+ def auth_header_for(nonce, body, uri)
65
+ params = {
66
+ "x_apikey" => @api_key,
67
+ "x_nonce" => nonce,
68
+ "x_requestbodyhash" => Digest::MD5.base64digest(body),
69
+ "x_requesttype" => "POST",
70
+ "x_requesturi" => CGI.escape(uri).downcase
71
+ }
72
+
73
+ signature = SolidusPaybright::SigningHelper.new(@api_token).params_signature(params)
74
+
75
+ "amx #{@api_key}:#{signature}:#{nonce}"
76
+ end
77
+
78
+ def handle_response(response)
79
+ return false if response.code != 200
80
+
81
+ json = JSON.parse(response.body)
82
+ json["message"].to_s.casecmp("success").zero?
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,42 @@
1
+ module SolidusPaybright
2
+ class CallbackValidator
3
+ # @param params [Hash] The Paybright callback params
4
+ def initialize(params)
5
+ @params = params
6
+ end
7
+
8
+ # @return [Array] The validation result and the error message
9
+ def call
10
+ payment = Spree::Payment.find_by(id: @params[:x_reference])
11
+
12
+ unless payment
13
+ return [false, "Payment #{@params[:x_reference]} not found"]
14
+ end
15
+
16
+ if payment.completed?
17
+ return [false, "Payment #{payment.id} is already in completed state"]
18
+ end
19
+
20
+ if payment.order.complete?
21
+ return [false, "Order is already in complete state"]
22
+ end
23
+
24
+ if !signing_helper(payment).valid_params?(@params.to_hash)
25
+ return [false, "Invalid parameters signature"]
26
+ end
27
+
28
+ if @params[:x_result].casecmp("completed") != 0
29
+ return [false, "The contract was not completed (#{@params[:x_result]})"]
30
+ end
31
+
32
+ [true, ""]
33
+ end
34
+
35
+ private
36
+
37
+ def signing_helper(payment)
38
+ api_token = payment.payment_method.preferences.fetch(:api_token)
39
+ SolidusPaybright::SigningHelper.new(api_token)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,28 @@
1
+ module SolidusPaybright
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'solidus_paybright'
6
+
7
+ # use rspec for tests
8
+ config.generators do |g|
9
+ g.test_framework :rspec
10
+ end
11
+
12
+ initializer "spree.solidus_paybright.environment", before: :load_config_initializers do |_app|
13
+ SolidusPaybright::Config = SolidusPaybright::Configuration.new
14
+ end
15
+
16
+ initializer "spree.solidus_paybright.payment_methods", after: "spree.register.payment_methods" do |app|
17
+ app.config.spree.payment_methods << Spree::PaymentMethod::Paybright
18
+ end
19
+
20
+ def self.activate
21
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
22
+ Rails.configuration.cache_classes ? require(c) : load(c)
23
+ end
24
+ end
25
+
26
+ config.to_prepare(&method(:activate).to_proc)
27
+ end
28
+ end
@@ -0,0 +1,10 @@
1
+ FactoryGirl.define do
2
+ factory :paybright_payment_method, class: Spree::PaymentMethod::Paybright do
3
+ name "Paybright"
4
+ end
5
+
6
+ factory :paybright_payment, class: Spree::Payment do
7
+ association(:payment_method, factory: :paybright_payment_method)
8
+ order
9
+ end
10
+ end
@@ -0,0 +1,70 @@
1
+ module SolidusPaybright
2
+ class ParamsHelper
3
+ include Spree::Core::Engine.routes.url_helpers
4
+
5
+ # @param payment [Spree::Payment] The payment in question
6
+ def initialize(payment)
7
+ @payment = payment
8
+ end
9
+
10
+ # @return [Hash] The parameters to be used on the Paybright redirect call
11
+ def build_redirect_params
12
+ bill_address = order.bill_address
13
+ ship_address = order.ship_address
14
+
15
+ params = {
16
+ # mandatory parameters
17
+ "x_account_id" => credentials[:api_key],
18
+ "x_amount" => order.total.to_money.to_s,
19
+ "x_currency" => order.currency,
20
+ "x_reference" => @payment.id,
21
+ "x_shop_country" => credentials[:shop_country_code],
22
+ "x_shop_name" => credentials[:shop_name],
23
+ "x_test" => credentials[:test_mode].to_s,
24
+ "x_url_callback" => paybright_callback_url,
25
+ "x_url_cancel" => paybright_cancel_url(@payment),
26
+ "x_url_complete" => paybright_complete_url,
27
+ # optional parameters
28
+ "x_description" => "Order ##{order.number}",
29
+ "x_customer_email" => order.email,
30
+ "x_customer_first_name" => bill_address.firstname,
31
+ "x_customer_last_name" => bill_address.lastname,
32
+ "x_customer_billing_address1" => bill_address.address1,
33
+ "x_customer_billing_address2" => bill_address.address2,
34
+ "x_customer_billing_city" => bill_address.city,
35
+ "x_customer_billing_company" => bill_address.company,
36
+ "x_customer_billing_country" => bill_address.country.iso,
37
+ "x_customer_billing_phone" => bill_address.phone,
38
+ "x_customer_billing_state" => bill_address.state.try(:name),
39
+ "x_customer_billing_zip" => bill_address.zipcode,
40
+ "x_customer_shipping_address1" => ship_address.address1,
41
+ "x_customer_shipping_address2" => ship_address.address2,
42
+ "x_customer_shipping_city" => ship_address.city,
43
+ "x_customer_shipping_company" => ship_address.company,
44
+ "x_customer_shipping_country" => ship_address.country.iso,
45
+ "x_customer_shipping_first_name" => ship_address.firstname,
46
+ "x_customer_shipping_last_name" => ship_address.lastname,
47
+ "x_customer_shipping_phone" => ship_address.phone,
48
+ "x_customer_shipping_state" => ship_address.state.try(:name),
49
+ "x_customer_shipping_zip" => ship_address.zipcode
50
+ }
51
+
52
+ params.keep_if { |_, value| value.present? }
53
+ params.merge("x_signature" => signing_helper.params_signature(params))
54
+ end
55
+
56
+ private
57
+
58
+ def order
59
+ @order ||= @payment.order
60
+ end
61
+
62
+ def credentials
63
+ @credentials ||= @payment.payment_method.preferences.to_hash
64
+ end
65
+
66
+ def signing_helper
67
+ SolidusPaybright::SigningHelper.new(credentials[:api_token])
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,31 @@
1
+ require "openssl"
2
+
3
+ module SolidusPaybright
4
+ class SigningHelper
5
+ SIGNATURE_PARAM_KEY = "x_signature"
6
+
7
+ # @param hmac_key [String] The HMAC secret key used for signing
8
+ def initialize(hmac_key)
9
+ @hmac_key = hmac_key
10
+ end
11
+
12
+ # @param params [Hash] The parameters to be signed
13
+ # @return [String] The signature of the parameters
14
+ def params_signature(params)
15
+ message = params.select do |key, _|
16
+ key != SIGNATURE_PARAM_KEY && key.start_with?("x_")
17
+ end.sort.join
18
+
19
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), @hmac_key, message)
20
+ end
21
+
22
+ # @param params [Hash] The parameters to be validated, including the "x_signature" key
23
+ # @return [Boolean] The validation result
24
+ def valid_params?(params)
25
+ expected_signature = params[SIGNATURE_PARAM_KEY].to_s
26
+ signature = params_signature(params)
27
+
28
+ expected_signature.casecmp(signature.downcase).zero?
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module SolidusPaybright
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,7 @@
1
+ require 'solidus_core'
2
+ require 'solidus_support'
3
+ require 'solidus_paybright/engine'
4
+ require 'solidus_paybright/signing_helper'
5
+ require 'solidus_paybright/params_helper'
6
+ require 'solidus_paybright/callback_validator'
7
+ require 'solidus_paybright/api_client'
metadata ADDED
@@ -0,0 +1,266 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solidus_paybright
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alessandro Lepore
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: solidus_frontend
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: solidus_support
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: typhoeus
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: capybara
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: poltergeist
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: coffee-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sass-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: database_cleaner
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: factory_girl
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rspec-rails
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0.38'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0.38'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop-rspec
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '='
172
+ - !ruby/object:Gem::Version
173
+ version: 1.4.0
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '='
179
+ - !ruby/object:Gem::Version
180
+ version: 1.4.0
181
+ - !ruby/object:Gem::Dependency
182
+ name: simplecov
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: sqlite3
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ description: This extension provides the Paybright payment option for your Solidus
210
+ storefront
211
+ email: alessandro@stembolt.com
212
+ executables: []
213
+ extensions: []
214
+ extra_rdoc_files: []
215
+ files:
216
+ - LICENSE
217
+ - README.md
218
+ - Rakefile
219
+ - app/assets/javascripts/spree/backend/solidus_paybright.js
220
+ - app/assets/javascripts/spree/frontend/solidus_paybright.js
221
+ - app/assets/stylesheets/spree/backend/solidus_paybright.css
222
+ - app/assets/stylesheets/spree/frontend/solidus_paybright.css
223
+ - app/controllers/spree/checkout_controller_decorator.rb
224
+ - app/controllers/spree/paybright_controller.rb
225
+ - app/models/solidus_paybright/configuration.rb
226
+ - app/models/spree/payment_decorator.rb
227
+ - app/models/spree/payment_method/paybright.rb
228
+ - app/models/spree/payment_method_decorator.rb
229
+ - app/views/spree/admin/payments/source_views/_paybright.html.erb
230
+ - app/views/spree/checkout/payment/_paybright.html.erb
231
+ - config/locales/en.yml
232
+ - config/routes.rb
233
+ - lib/generators/solidus_paybright/install/install_generator.rb
234
+ - lib/solidus_paybright.rb
235
+ - lib/solidus_paybright/api_client.rb
236
+ - lib/solidus_paybright/callback_validator.rb
237
+ - lib/solidus_paybright/engine.rb
238
+ - lib/solidus_paybright/factories.rb
239
+ - lib/solidus_paybright/params_helper.rb
240
+ - lib/solidus_paybright/signing_helper.rb
241
+ - lib/solidus_paybright/version.rb
242
+ homepage: https://stembolt.com/
243
+ licenses:
244
+ - BSD-3-Clause
245
+ metadata: {}
246
+ post_install_message:
247
+ rdoc_options: []
248
+ require_paths:
249
+ - lib
250
+ required_ruby_version: !ruby/object:Gem::Requirement
251
+ requirements:
252
+ - - ">="
253
+ - !ruby/object:Gem::Version
254
+ version: '0'
255
+ required_rubygems_version: !ruby/object:Gem::Requirement
256
+ requirements:
257
+ - - ">="
258
+ - !ruby/object:Gem::Version
259
+ version: '0'
260
+ requirements: []
261
+ rubyforge_project:
262
+ rubygems_version: 2.5.1
263
+ signing_key:
264
+ specification_version: 4
265
+ summary: Solidus extension for the Paybright payment method
266
+ test_files: []