spree_webpay 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: 545cf9cbe5bfe17aa2ba896a281a037c779e2a3b
4
+ data.tar.gz: 024e55c71adf2646a6d6d7a103fc7ed2241047d2
5
+ SHA512:
6
+ metadata.gz: 4a35b411b44382d8e6f02a3d9aa29e03955aec05dce56554c8de40aba1eb3e0b85212af457d34b8d821564cb7bfcd85dfbee92d03628462825991bf8a6fd4831
7
+ data.tar.gz: c625c0cd92d67ac8b7352ffd44d00bd07a08da3758d403f6f9f046725e632ed7882b2c935bf9870220110b9b54dd7f826e0c49eb6748f02b583b5f5df90fef5d
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ \#*
2
+ *~
3
+ .#*
4
+ .DS_Store
5
+ .idea
6
+ .project
7
+ .sass-cache
8
+ coverage
9
+ Gemfile.lock
10
+ tmp
11
+ nbproject
12
+ pkg
13
+ *.swp
14
+ spec/dummy
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.1.2
5
+ script: bundle exec rake
6
+ notifications:
7
+ webhooks:
8
+ secure: M6KmG9KM1DExfjf8uIjTMflLvwe7HaZa/I3r5g6Yg0lI1VySt3cUXKrbQgaRR2uU+wpjazdL0txzKzgH/Md/Hddbpkg1qSQXFDBzUe23xx+Hm0v0W/0CyQWeLj4pKf1bQcK4UaxjKK+VW7i4cwdKN/FYT7spNzRPwE8tHpxJhmI=
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'spree', github: 'spree/spree', branch: '2-3-stable'
4
+ # Provides basic authentication functionality for testing parts of your engine
5
+ gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '2-3-stable'
6
+
7
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) WebPay, released under the New BSD License
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,53 @@
1
+ SpreeWebpay
2
+ ===========
3
+
4
+ This is a spree extension to use [WebPay](https://webpay.jp/) as a payment method.
5
+
6
+ WebPayを決済に利用するためのSpree Extensionです。
7
+
8
+ Installation
9
+ ------------
10
+
11
+ Add spree_webpay to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'spree_webpay'
15
+ ```
16
+
17
+ Bundle your dependencies and run the installation generator:
18
+
19
+ ```shell
20
+ bundle
21
+ bundle exec rails g spree_webpay:install
22
+ ```
23
+
24
+ Open payment method configuration (http://localhost:3000/admin/payment_methods) and add a new payment method.
25
+
26
+ Select `Spree::PaymentMethod::Webpay`, and put secret and publishable keys shown in [your webpay setting page](https://webpay.jp/settings).
27
+
28
+ Testing
29
+ -------
30
+
31
+ 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. The dummy app can be regenerated by using `rake test_app`.
32
+
33
+ ```shell
34
+ bundle
35
+ bundle exec rake
36
+ ```
37
+
38
+ When testing your applications integration with this extension you may use it's factories.
39
+ Simply add this require statement to your spec_helper:
40
+
41
+ ```ruby
42
+ require 'spree_webpay/factories'
43
+ ```
44
+
45
+ Hacking
46
+ -------
47
+
48
+ Due to spree's problem, **card data cannot be saved**.
49
+ Applying [this patch](https://github.com/spree/spree/pull/5292) enables saving cards per user.
50
+ However, this change is known to break other payment methods.
51
+ Neither we nor the Spree team take responsibility about this hack.
52
+
53
+ Copyright (c) WebPay, released under the New BSD License
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'spree/testing_support/extension_rake'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default do
10
+ if Dir["spec/dummy"].empty?
11
+ Rake::Task[:test_app].invoke
12
+ Dir.chdir("../../")
13
+ end
14
+ Rake::Task[:spec].invoke
15
+ end
16
+
17
+ desc 'Generates a dummy app for testing'
18
+ task :test_app do
19
+ ENV['LIB_NAME'] = 'spree_webpay'
20
+ Rake::Task['extension:test_app'].invoke
21
+ 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,53 @@
1
+ # Inspired by spree_gateway's stripe.js.coffee
2
+ # Map cc types from webpay to spree
3
+ mapCC = (ccType) ->
4
+ if (ccType == 'MasterCard')
5
+ 'master'
6
+ else if (ccType == 'Visa')
7
+ 'visa'
8
+ else if (ccType == 'American Express')
9
+ 'american_express'
10
+ else if (ccType == 'Diners Club')
11
+ 'diners_club'
12
+ else if (ccType == 'JCB')
13
+ 'jcb'
14
+ else
15
+ ''
16
+
17
+ $(document).ready ->
18
+ # For errors that happen later.
19
+ Spree.webpayPaymentMethod.prepend("<div id='webpayError' class='errorExplanation' style='display:none'></div>")
20
+
21
+ $('[type="submit"]').click ->
22
+ $('#webpayError').hide()
23
+ if Spree.webpayPaymentMethod.is(':visible')
24
+ paymentMethodId = Spree.webpayPaymentMethod.prop('id').split("_")[2]
25
+ expiration = $('.cardExpiry:visible').payment('cardExpiryVal')
26
+ params =
27
+ name: $("[name=\"payment_source[#{paymentMethodId}][name]\"]").val()
28
+ number: $('.cardNumber:visible').val().replace(/\s/g, '')
29
+ cvc: $('.cardCode:visible').val()
30
+ exp_month: expiration.month || 0
31
+ exp_year: expiration.year || 0
32
+
33
+ WebPay.createToken(params, webpayResponseHandler)
34
+ return false
35
+
36
+ webpayResponseHandler = (status, response) ->
37
+ if response.error
38
+ $('#webpayError').html(response.error.message).show()
39
+ else
40
+ Spree.webpayPaymentMethod.find('.cardNumber, .cardCode, .cardExpiry').prop("disabled" , true)
41
+ Spree.webpayPaymentMethod.find(".ccType").prop("disabled", false)
42
+ Spree.webpayPaymentMethod.find(".ccType").val(mapCC(response.card.type))
43
+
44
+ # insert the token into the form so it gets submitted to the server
45
+ paymentMethodId = Spree.webpayPaymentMethod.prop('id').split("_")[2]
46
+ params =
47
+ gateway_payment_profile_id: response.id
48
+ last_digits: response.card.last4
49
+ month: response.card.exp_month
50
+ year: response.card.exp_year
51
+ for k, v of params
52
+ Spree.webpayPaymentMethod.append("<input type='hidden' class='webpayToken' name='payment_source[#{paymentMethodId}][#{k}]' value='#{v}'/>");
53
+ Spree.webpayPaymentMethod.parents("form").get(0).submit();
@@ -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,188 @@
1
+ require 'webpay'
2
+ module Spree
3
+
4
+ # not precise notation, but required for Rails convention
5
+ class PaymentMethod::Webpay < PaymentMethod
6
+ preference :secret_key, :string
7
+ preference :publishable_key, :string
8
+
9
+ # Meta ================
10
+ def payment_source_class
11
+ CreditCard
12
+ end
13
+
14
+ def method_type
15
+ 'webpay'
16
+ end
17
+
18
+ def payment_profiles_supported?
19
+ true
20
+ end
21
+
22
+ def source_required?
23
+ true
24
+ end
25
+
26
+ # Copied from spree-core gateway.rb
27
+ def reusable_sources(order)
28
+ if order.completed?
29
+ sources_by_order order
30
+ else
31
+ if order.user_id
32
+ self.credit_cards.where(user_id: order.user_id).with_payment_profile
33
+ else
34
+ []
35
+ end
36
+ end
37
+ end
38
+
39
+ def supports?(source)
40
+ @account_info ||= client.account.retrieve
41
+ brand_name =
42
+ case source.brand
43
+ when 'visa' then 'Visa'
44
+ when 'master' then 'MasterCard'
45
+ when 'diners_club' then 'Diners Club'
46
+ when 'american_express' then 'American Express'
47
+ when 'discover' then 'Discover'
48
+ when 'jcb' then 'JCB'
49
+ end
50
+ @account_info.card_types_supported.include?(brand_name)
51
+ end
52
+
53
+ # Payment related methods ================
54
+ # Options are ignored unless they are mentioned in parameters list.
55
+ CVC_CODE_TRANSLATOR = {
56
+ 'pass' => 'M',
57
+ 'fail' => 'N',
58
+ 'unchecked' => 'P'
59
+ }
60
+
61
+ # Performs an authorization, which reserves the funds on the customer's credit card, but does not
62
+ # charge the card.
63
+ #
64
+ # ==== Parameters
65
+ #
66
+ # * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
67
+ # * <tt>paysource</tt> -- The CreditCard or Check details for the transaction.
68
+ def authorize(money, paysource, options = {})
69
+ create_charge(money, paysource, false)
70
+ end
71
+
72
+ # Perform a purchase, which is essentially an authorization and capture in a single operation.
73
+ #
74
+ # ==== Parameters
75
+ #
76
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
77
+ # * <tt>paysource</tt> -- The CreditCard or Check details for the transaction.
78
+ def purchase(money, paysource, options = {})
79
+ create_charge(money, paysource, true)
80
+ end
81
+
82
+ # Captures the funds from an authorized transaction.
83
+ #
84
+ # ==== Parameters
85
+ #
86
+ # * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
87
+ # * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
88
+ def capture(money, authorization, options = {})
89
+ wrap_in_active_merchant_response { client.charge.capture(id: authorization, amount: money) }
90
+ end
91
+
92
+ # Void a previous transaction
93
+ #
94
+ # ==== Parameters
95
+ #
96
+ # * <tt>authorization</tt> - The authorization returned from the previous authorize request.
97
+ def void(authorization, _source, options = {})
98
+ wrap_in_active_merchant_response { client.charge.refund(authorization) }
99
+ end
100
+
101
+ # Refund a transaction.
102
+ #
103
+ # This transaction indicates to the gateway that
104
+ # money should flow from the merchant to the customer.
105
+ #
106
+ # ==== Parameters
107
+ #
108
+ # * <tt>money</tt> -- The amount to be credited to the customer as an Integer value.
109
+ # * <tt>identification</tt> -- The ID of the original transaction against which the refund is being issued.
110
+ def refund(money, _source, identification, options = {})
111
+ wrap_in_active_merchant_response do
112
+ charge = client.charge.retrieve(identification)
113
+ client.charge.refund(id: identification, amount: charge.amount.to_i - charge.amount_refunded.to_i - money)
114
+ end
115
+ end
116
+
117
+ def credit(money, source, identification, options = {})
118
+ refund(money, source, identification, options)
119
+ end
120
+
121
+ def create_profile(payment)
122
+ return if payment.source.gateway_customer_profile_id.present?
123
+
124
+ begin
125
+ customer = client.customer.create(
126
+ email: payment.order.email,
127
+ description: payment.order.name,
128
+ card: payment.source.gateway_payment_profile_id,
129
+ )
130
+ payment.source.update_attributes!({
131
+ gateway_customer_profile_id: customer.id,
132
+ gateway_payment_profile_id: nil
133
+ })
134
+ rescue WebPay::ApiError => e
135
+ payment.send(:gateway_error, e.respond_to?(:data) ? e.data.error.message : e.message)
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def sources_by_order(order)
142
+ source_ids = order.payments.where(source_type: payment_source_class.to_s, payment_method_id: self.id).pluck(:source_id).uniq
143
+ payment_source_class.where(id: source_ids).with_payment_profile
144
+ end
145
+
146
+ # In this gateway, what we call 'secret_key' is the 'login'
147
+ def client
148
+ @client ||= WebPay.new(preferred_secret_key)
149
+ end
150
+
151
+ def create_charge(money, paysource, capture)
152
+ params = {
153
+ amount: money,
154
+ currency: 'jpy',
155
+ capture: capture,
156
+ }
157
+ if payment_id = paysource.gateway_payment_profile_id.presence
158
+ params[:card] = payment_id
159
+ else
160
+ params[:customer] = paysource.gateway_customer_profile_id.presence
161
+ end
162
+ wrap_in_active_merchant_response { client.charge.create(params) }
163
+ end
164
+
165
+ def wrap_in_active_merchant_response(&block)
166
+ begin
167
+ response = block.call
168
+ ActiveMerchant::Billing::Response.new(!response.failure_message,
169
+ response.failure_message || "Transaction approved",
170
+ response.to_h,
171
+ test: !response.livemode,
172
+ authorization: response.id,
173
+ avs_result: nil, # WebPay does not check avs
174
+ cvv_result: CVC_CODE_TRANSLATOR[response.card.cvc_check]
175
+ )
176
+ rescue WebPay::ApiError => e
177
+ ActiveMerchant::Billing::Response.new(false,
178
+ e.respond_to?(:data) ? e.data.error.message : e.message,
179
+ {},
180
+ test: false,
181
+ authorization: e.respond_to?(:data) ? e.data.error.charge : nil,
182
+ avs_result: nil,
183
+ cvv_result: nil
184
+ )
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,7 @@
1
+ <%= render "spree/admin/payments/source_forms/gateway", payment_method: payment_method, previous_cards: payment_method.reusable_sources(@order) %>
2
+ <script type="text/javascript" src="https://js.webpay.jp/v1"></script>
3
+ <script type="text/javascript">
4
+ WebPay.setPublishableKey("<%= payment_method.preferred_publishable_key %>");
5
+ Spree.webpayPaymentMethod = $('#payment_method_' + <%= payment_method.id %>)
6
+ </script>
7
+ <%= javascript_include_tag "store/gateway/webpay" %>
@@ -0,0 +1 @@
1
+ <%= render "spree/admin/payments/source_views/gateway", payment: payment %>
@@ -0,0 +1,7 @@
1
+ <%= render "spree/checkout/payment/gateway", payment_method: payment_method %>
2
+ <script type="text/javascript" src="https://js.webpay.jp/v1"></script>
3
+ <script type="text/javascript">
4
+ WebPay.setPublishableKey("<%= payment_method.preferred_publishable_key %>");
5
+ Spree.webpayPaymentMethod = $('#payment_method_' + <%= payment_method.id %>)
6
+ </script>
7
+ <%= javascript_include_tag "store/gateway/webpay" %>
data/bin/rails ADDED
@@ -0,0 +1,7 @@
1
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
2
+
3
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
4
+ ENGINE_PATH = File.expand_path('../../lib/spree_webpay/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
@@ -0,0 +1,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ # Add your extension routes here
3
+ end
@@ -0,0 +1,31 @@
1
+ module SpreeWebpay
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ class_option :auto_run_migrations, :type => :boolean, :default => false
6
+
7
+ def add_javascripts
8
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/spree_webpay\n"
9
+ append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/spree_webpay\n"
10
+ end
11
+
12
+ def add_stylesheets
13
+ inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/spree_webpay\n", :before => /\*\//, :verbose => true
14
+ inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/spree_webpay\n", :before => /\*\//, :verbose => true
15
+ end
16
+
17
+ def add_migrations
18
+ run 'bundle exec rake railties:install:migrations FROM=spree_webpay'
19
+ end
20
+
21
+ def run_migrations
22
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
23
+ if run_migrations
24
+ run 'bundle exec rake db:migrate'
25
+ else
26
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ module SpreeWebpay
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_webpay'
6
+
7
+ # use rspec for tests
8
+ config.generators do |g|
9
+ g.test_framework :rspec
10
+ end
11
+
12
+ def self.activate
13
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
14
+ Rails.configuration.cache_classes ? require(c) : load(c)
15
+ end
16
+ end
17
+
18
+ config.to_prepare &method(:activate).to_proc
19
+
20
+ initializer "spree.webpay.payment_methods", after: 'spree.register.payment_methods' do |app|
21
+ app.config.spree.payment_methods << Spree::PaymentMethod::Webpay
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ # Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
3
+ #
4
+ # Example adding this to your spec_helper will load these Factories for use:
5
+ # require 'spree_webpay/factories'
6
+ end
@@ -0,0 +1,2 @@
1
+ require 'spree_core'
2
+ require 'spree_webpay/engine'
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+ require 'webpay/mock'
3
+
4
+ describe Spree::PaymentMethod::Webpay do
5
+ subject(:payment_method) { described_class.new() }
6
+ before do
7
+ payment_method.preferences = { secret_key: 'test_secret_xxx' }
8
+ end
9
+ let(:empty_avs_result) { {"code" => nil, "message" => nil, "street_match" => nil, "postal_match" => nil} }
10
+ let(:match_cvv_result) { {"code" => "M", "message" => "CVV matches"} }
11
+ let(:empty_cvv_result) { {"code" => nil, "message" => nil} }
12
+
13
+ describe '#supports?' do
14
+ before do
15
+ webpay_stub(:account, :retrieve, overrides: { card_types_supported: ['Visa', 'MasterCard'] })
16
+ end
17
+
18
+ def stub_source(brand)
19
+ double('Source', brand: brand)
20
+ end
21
+
22
+ it 'should be true when the source brand is visa' do
23
+ expect(payment_method.supports?(stub_source('visa'))).to eq true
24
+ end
25
+
26
+ it 'should be true when the source brand is jcb' do
27
+ expect(payment_method.supports?(stub_source('jcb'))).to eq false
28
+ end
29
+ end
30
+
31
+ describe '#authorize' do
32
+ let(:mock_card) { double('CreditCard', gateway_payment_profile_id: 'tok_fromspreeform') }
33
+ let(:params) { {
34
+ amount: 1500,
35
+ currency: 'jpy',
36
+ card: mock_card.gateway_payment_profile_id,
37
+ capture: false,
38
+ }}
39
+
40
+ it 'should request as expected' do
41
+ mock_response = webpay_stub(:charges, :create, params: params)
42
+ payment_method.authorize(1500, mock_card)
43
+ assert_requested(:post, "https://api.webpay.jp/v1/charges", body: JSON.dump(params))
44
+ end
45
+
46
+ it 'should return succeeded ActiveMerchant::Billing::Response for correct transaction' do
47
+ mock_response = webpay_stub(:charges, :create, params: params)
48
+ response = payment_method.authorize(1500, mock_card)
49
+ expect(response).to be_success
50
+ expect(response.message).to eq 'Transaction approved'
51
+ expect(response.test).to eq true
52
+ expect(response.authorization).to eq mock_response['id']
53
+ expect(response.avs_result).to eq empty_avs_result
54
+ expect(response.cvv_result).to eq match_cvv_result
55
+ end
56
+
57
+ it 'should return failed ActiveMerchant::Billing::Response for errors' do
58
+ mock_response = webpay_stub(:charges, :create, params: params, error: :card_error)
59
+ response = payment_method.authorize(1500, mock_card)
60
+ expect(response).not_to be_success
61
+ expect(response.message).to eq 'This card cannot be used.'
62
+ expect(response.test).to eq false
63
+ expect(response.authorization).to eq nil
64
+ expect(response.avs_result).to eq empty_avs_result
65
+ expect(response.cvv_result).to eq empty_cvv_result
66
+ end
67
+
68
+ it 'should return failed ActiveMerchant::Billing::Response for response with failure_message' do
69
+ mock_response = webpay_stub(:charges, :create, params: params, overrides: { failure_message: 'Service unavailable' })
70
+ response = payment_method.authorize(1500, mock_card)
71
+ expect(response).not_to be_success
72
+ expect(response.message).to eq 'Service unavailable'
73
+ expect(response.test).to eq true
74
+ expect(response.authorization).to eq mock_response['id']
75
+ expect(response.avs_result).to eq empty_avs_result
76
+ expect(response.cvv_result).to eq match_cvv_result
77
+ end
78
+
79
+ context 'with customer_profile_id' do
80
+ let(:mock_card) { double('CreditCard', gateway_customer_profile_id: 'cus_savedcustomer', gateway_payment_profile_id: nil) }
81
+ let(:params) { {
82
+ amount: 1500,
83
+ currency: 'jpy',
84
+ customer: 'cus_savedcustomer',
85
+ capture: false,
86
+ }}
87
+
88
+ it 'should request as expected' do
89
+ mock_response = webpay_stub(:charges, :create, params: params)
90
+ payment_method.authorize(1500, mock_card)
91
+ assert_requested(:post, "https://api.webpay.jp/v1/charges", body: JSON.dump(params))
92
+ end
93
+ end
94
+
95
+ context 'connection error' do
96
+ it 'should return failed ActiveMerchant::Billing::Response' do
97
+ stub_request(:any, 'https://api.webpay.jp/v1/charges').to_timeout
98
+ response = payment_method.authorize(1500, mock_card)
99
+ expect(response).not_to be_success
100
+ expect(response.message).to eq 'API request failed with execution expired'
101
+ end
102
+ end
103
+ end
104
+
105
+ describe '#purchase' do
106
+ let(:mock_card) { double('CreditCard', gateway_payment_profile_id: 'tok_fromspreeform') }
107
+ let(:params) { {
108
+ amount: 1500,
109
+ currency: 'jpy',
110
+ card: mock_card.gateway_payment_profile_id,
111
+ capture: true,
112
+ }}
113
+
114
+ it 'should request as expected' do
115
+ mock_response = webpay_stub(:charges, :create, params: params)
116
+ payment_method.purchase(1500, mock_card)
117
+ assert_requested(:post, "https://api.webpay.jp/v1/charges", body: JSON.dump(params))
118
+ end
119
+
120
+ it 'should return succeeded ActiveMerchant::Billing::Response for correct transaction' do
121
+ mock_response = webpay_stub(:charges, :create, params: params)
122
+ response = payment_method.purchase(1500, mock_card)
123
+ expect(response).to be_success
124
+ expect(response.message).to eq 'Transaction approved'
125
+ expect(response.test).to eq true
126
+ expect(response.authorization).to eq mock_response['id']
127
+ expect(response.avs_result).to eq empty_avs_result
128
+ expect(response.cvv_result).to eq match_cvv_result
129
+ end
130
+
131
+ # other cases are covered by #authorize
132
+ end
133
+
134
+ describe '#capture' do
135
+ let(:charge_id) { 'ch_authorizedcharge' }
136
+ let(:params) { { id: charge_id, amount: 1300 } }
137
+
138
+ it 'should capture existing charge with given amount' do
139
+ webpay_stub(:charges, :capture, params: params)
140
+ payment_method.capture(1300, charge_id)
141
+ assert_requested(:post, "https://api.webpay.jp/v1/charges/#{charge_id}/capture", body: JSON.dump(amount: 1300))
142
+ end
143
+
144
+ it 'should return success ActiveMerchant::Billing::Response' do
145
+ webpay_stub(:charges, :capture, params: params)
146
+ response = payment_method.capture(1300, charge_id)
147
+ expect(response).to be_success
148
+ end
149
+
150
+ it 'should return failed ActiveMerchant::Billing::Response for errors' do
151
+ webpay_stub(:charges, :capture, params: params, error: :bad_request)
152
+ response = payment_method.capture(1300, charge_id)
153
+ expect(response).not_to be_success
154
+ end
155
+ end
156
+
157
+ describe '#void' do
158
+ let(:charge_id) { 'ch_authorizedcharge' }
159
+ let(:params) { { id: charge_id } }
160
+
161
+ it 'should capture existing charge with given amount' do
162
+ webpay_stub(:charges, :refund, params: params)
163
+ payment_method.void(charge_id, nil)
164
+ assert_requested(:post, "https://api.webpay.jp/v1/charges/#{charge_id}/refund", body: '{}')
165
+ end
166
+
167
+ it 'should return success ActiveMerchant::Billing::Response' do
168
+ webpay_stub(:charges, :refund, params: params)
169
+ response = payment_method.void(charge_id, nil)
170
+ expect(response).to be_success
171
+ end
172
+
173
+ it 'should return failed ActiveMerchant::Billing::Response for errors' do
174
+ webpay_stub(:charges, :refund, params: params, error: :bad_request)
175
+ response = payment_method.void(charge_id, nil)
176
+ expect(response).not_to be_success
177
+ end
178
+ end
179
+
180
+ describe '#refund' do
181
+ let(:charge_id) { 'ch_captured' }
182
+ let!(:charge) { webpay_stub(:charges, :retrieve, params: { id: charge_id }, overrides: { amount: 1500, refunded: false, amount_refunded: 100 }) }
183
+ let(:params) { { id: charge_id, amount: 400 } }
184
+
185
+ it 'should capture existing charge with given amount' do
186
+ webpay_stub(:charges, :refund, params: params)
187
+ payment_method.refund(1000, nil, charge_id)
188
+ assert_requested(:post, "https://api.webpay.jp/v1/charges/#{charge_id}/refund", body: JSON.dump(amount: 400))
189
+ end
190
+
191
+ it 'should return success ActiveMerchant::Billing::Response' do
192
+ webpay_stub(:charges, :refund, params: params)
193
+ response = payment_method.refund(1000, nil, charge_id)
194
+ expect(response).to be_success
195
+ end
196
+
197
+ it 'should return failed ActiveMerchant::Billing::Response for errors in retrieve' do
198
+ webpay_stub(:charges, :retrieve, params: { id: charge_id }, error: :not_found)
199
+ response = payment_method.refund(1000, nil, charge_id)
200
+ expect(response).not_to be_success
201
+ expect(response.message).to eq 'No such charge: ch_bBM4IJ0XF2VIch8'
202
+ end
203
+
204
+ it 'should return failed ActiveMerchant::Billing::Response for errors in refund' do
205
+ webpay_stub(:charges, :refund, params: params, error: :bad_request)
206
+ response = payment_method.refund(1000, nil, charge_id)
207
+ expect(response).not_to be_success
208
+ expect(response.message).to eq "can't save charge: Amount can't be blank" # test error message
209
+ end
210
+ end
211
+
212
+ describe '#create_profile' do
213
+ let(:source) { double("Source", gateway_customer_profile_id: nil, gateway_payment_profile_id: 'tok_sourceprofileid') }
214
+ let(:order) { double("Source", email: 'customer@example.com', name: 'John Doe') }
215
+ let(:payment) { double("Payment", source: source, order: order) }
216
+ let(:params) { {
217
+ card: 'tok_sourceprofileid',
218
+ description: 'John Doe',
219
+ email: 'customer@example.com',
220
+ } }
221
+
222
+ it 'should do nothing if source already has customer_profile_id' do
223
+ expect(source).to receive(:gateway_customer_profile_id).and_return('cus_alreadyassigned')
224
+ expect(source).not_to receive(:update_attributes!)
225
+ payment_method.create_profile(payment)
226
+ end
227
+
228
+ it 'should create customer' do
229
+ customer = webpay_stub(:customers, :create, params: params)
230
+ expect(source).to receive(:update_attributes!).with(gateway_customer_profile_id: customer['id'], gateway_payment_profile_id: nil)
231
+ payment_method.create_profile(payment)
232
+
233
+ assert_requested(:post, "https://api.webpay.jp/v1/customers", body: JSON.dump(params))
234
+ end
235
+
236
+ it 'should call gateway_error on error response' do
237
+ webpay_stub(:customers, :create, params: params, error: :bad_request)
238
+ expect(source).not_to receive(:update_attributes!)
239
+ expect(payment).to receive(:send).with(:gateway_error, "can't save charge: Amount can't be blank")
240
+ payment_method.create_profile(payment)
241
+
242
+ assert_requested(:post, "https://api.webpay.jp/v1/customers", body: JSON.dump(params))
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,91 @@
1
+ # Run Coverage report
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter 'spec/dummy'
5
+ add_group 'Controllers', 'app/controllers'
6
+ add_group 'Helpers', 'app/helpers'
7
+ add_group 'Mailers', 'app/mailers'
8
+ add_group 'Models', 'app/models'
9
+ add_group 'Views', 'app/views'
10
+ add_group 'Libraries', 'lib'
11
+ end
12
+
13
+ # Configure Rails Environment
14
+ ENV['RAILS_ENV'] = 'test'
15
+
16
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
17
+
18
+ require 'rspec/rails'
19
+ require 'database_cleaner'
20
+ require 'ffaker'
21
+ require 'webmock/rspec'
22
+ require 'webpay/mock'
23
+
24
+ # Requires supporting ruby files with custom matchers and macros, etc,
25
+ # in spec/support/ and its subdirectories.
26
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f }
27
+
28
+ # Requires factories and other useful helpers defined in spree_core.
29
+ require 'spree/testing_support/authorization_helpers'
30
+ require 'spree/testing_support/capybara_ext'
31
+ require 'spree/testing_support/controller_requests'
32
+ require 'spree/testing_support/factories'
33
+ require 'spree/testing_support/url_helpers'
34
+
35
+ # Requires factories defined in lib/spree_webpay/factories.rb
36
+ require 'spree_webpay/factories'
37
+
38
+ RSpec.configure do |config|
39
+ config.include FactoryGirl::Syntax::Methods
40
+
41
+ # == URL Helpers
42
+ #
43
+ # Allows access to Spree's routes in specs:
44
+ #
45
+ # visit spree.admin_path
46
+ # current_path.should eql(spree.products_path)
47
+ config.include Spree::TestingSupport::UrlHelpers
48
+
49
+ # == Mock Framework
50
+ #
51
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
52
+ #
53
+ # config.mock_with :mocha
54
+ # config.mock_with :flexmock
55
+ # config.mock_with :rr
56
+ config.mock_with :rspec
57
+ config.color = true
58
+
59
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
60
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
61
+
62
+ # Capybara javascript drivers require transactional fixtures set to false, and we use DatabaseCleaner
63
+ # to cleanup after each test instead. Without transactional fixtures set to false the records created
64
+ # to setup a test will be unavailable to the browser, which runs under a separate server instance.
65
+ config.use_transactional_fixtures = false
66
+
67
+ # Ensure Suite is set to use transactions for speed.
68
+ config.before :suite do
69
+ DatabaseCleaner.strategy = :transaction
70
+ DatabaseCleaner.clean_with :truncation
71
+ end
72
+
73
+ # Before each spec check if it is a Javascript test and switch between using database transactions or not where necessary.
74
+ config.expose_current_running_example_as :example
75
+ config.before :each do
76
+ DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
77
+ DatabaseCleaner.start
78
+ end
79
+
80
+ # After each spec clean the database.
81
+ config.after :each do
82
+ DatabaseCleaner.clean
83
+ end
84
+
85
+ config.fail_fast = ENV['FAIL_FAST'] || false
86
+ config.order = "random"
87
+
88
+ config.include WebPay::Mock::WebMockWrapper
89
+ config.infer_spec_type_from_file_location!
90
+
91
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+ Gem::Specification.new do |s|
3
+ s.platform = Gem::Platform::RUBY
4
+ s.name = 'spree_webpay'
5
+ s.version = '0.0.1'
6
+ s.description = 'A spree extension to use WebPay as a payment method'
7
+ s.summary = 'Spree::PaymentMethod::Webpay add as a new payment method for using WebPay'
8
+ s.authors = ['webpay', 'tomykaira']
9
+ s.email = ['administrators@webpay.jp', 'tomykaira@webpay.jp']
10
+ s.homepage = 'https://webpay.jp'
11
+ s.license = 'New-BSD'
12
+ s.files = `git ls-files`.split($/)
13
+ s.test_files = s.files.grep(%r{^spec/})
14
+ s.require_path = ['lib']
15
+
16
+ s.required_ruby_version = '>= 1.9.3'
17
+
18
+ s.add_dependency 'spree_core', '>= 2.3.1', '< 2.5.0'
19
+ s.add_dependency 'webpay', '~> 3.1'
20
+
21
+ s.add_development_dependency 'capybara', '~> 2.1'
22
+ s.add_development_dependency 'coffee-rails'
23
+ s.add_development_dependency 'database_cleaner'
24
+ s.add_development_dependency 'factory_girl', '~> 4.4'
25
+ s.add_development_dependency 'ffaker'
26
+ s.add_development_dependency 'rspec-rails', '~> 2.13'
27
+ s.add_development_dependency 'sass-rails', '~> 4.0.2'
28
+ s.add_development_dependency 'selenium-webdriver'
29
+ s.add_development_dependency 'simplecov'
30
+ s.add_development_dependency 'sqlite3'
31
+ s.add_development_dependency 'webpay-mock'
32
+ end
metadata ADDED
@@ -0,0 +1,263 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spree_webpay
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - webpay
8
+ - tomykaira
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-10-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: spree_core
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 2.3.1
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: 2.5.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: 2.3.1
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.5.0
34
+ - !ruby/object:Gem::Dependency
35
+ name: webpay
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.1'
41
+ type: :runtime
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ - !ruby/object:Gem::Dependency
49
+ name: capybara
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.1'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.1'
62
+ - !ruby/object:Gem::Dependency
63
+ name: coffee-rails
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ - !ruby/object:Gem::Dependency
77
+ name: database_cleaner
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: factory_girl
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '4.4'
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.4'
104
+ - !ruby/object:Gem::Dependency
105
+ name: ffaker
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ type: :development
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ - !ruby/object:Gem::Dependency
119
+ name: rspec-rails
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.13'
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.13'
132
+ - !ruby/object:Gem::Dependency
133
+ name: sass-rails
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 4.0.2
139
+ type: :development
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 4.0.2
146
+ - !ruby/object:Gem::Dependency
147
+ name: selenium-webdriver
148
+ requirement: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ type: :development
154
+ prerelease: false
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ - !ruby/object:Gem::Dependency
161
+ name: simplecov
162
+ requirement: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: sqlite3
176
+ requirement: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ type: :development
182
+ prerelease: false
183
+ version_requirements: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ - !ruby/object:Gem::Dependency
189
+ name: webpay-mock
190
+ requirement: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ type: :development
196
+ prerelease: false
197
+ version_requirements: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ description: A spree extension to use WebPay as a payment method
203
+ email:
204
+ - administrators@webpay.jp
205
+ - tomykaira@webpay.jp
206
+ executables: []
207
+ extensions: []
208
+ extra_rdoc_files: []
209
+ files:
210
+ - ".gitignore"
211
+ - ".rspec"
212
+ - ".travis.yml"
213
+ - Gemfile
214
+ - LICENSE
215
+ - README.md
216
+ - Rakefile
217
+ - app/assets/javascripts/spree/backend/spree_webpay.js
218
+ - app/assets/javascripts/spree/frontend/spree_webpay.js
219
+ - app/assets/javascripts/store/gateway/webpay.js.coffee
220
+ - app/assets/stylesheets/spree/backend/spree_webpay.css
221
+ - app/assets/stylesheets/spree/frontend/spree_webpay.css
222
+ - app/models/spree/payment_method/webpay.rb
223
+ - app/views/spree/admin/payments/source_forms/_webpay.html.erb
224
+ - app/views/spree/admin/payments/source_views/_webpay.html.erb
225
+ - app/views/spree/checkout/payment/_webpay.html.erb
226
+ - bin/rails
227
+ - config/locales/en.yml
228
+ - config/routes.rb
229
+ - lib/generators/spree_webpay/install/install_generator.rb
230
+ - lib/spree_webpay.rb
231
+ - lib/spree_webpay/engine.rb
232
+ - lib/spree_webpay/factories.rb
233
+ - spec/models/payment_method/webpay_spec.rb
234
+ - spec/spec_helper.rb
235
+ - spree_webpay.gemspec
236
+ homepage: https://webpay.jp
237
+ licenses:
238
+ - New-BSD
239
+ metadata: {}
240
+ post_install_message:
241
+ rdoc_options: []
242
+ require_paths:
243
+ - - lib
244
+ required_ruby_version: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ version: 1.9.3
249
+ required_rubygems_version: !ruby/object:Gem::Requirement
250
+ requirements:
251
+ - - ">="
252
+ - !ruby/object:Gem::Version
253
+ version: '0'
254
+ requirements: []
255
+ rubyforge_project:
256
+ rubygems_version: 2.2.0
257
+ signing_key:
258
+ specification_version: 4
259
+ summary: Spree::PaymentMethod::Webpay add as a new payment method for using WebPay
260
+ test_files:
261
+ - spec/models/payment_method/webpay_spec.rb
262
+ - spec/spec_helper.rb
263
+ has_rdoc: