solidus_paybright 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []