spree_webpay 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: 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: