spree_gateway 2.2.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -17
- data/Gemfile +1 -1
- data/README.md +9 -3
- data/app/models/spree/billing_integration.rb +21 -0
- data/app/models/spree/gateway/authorize_net.rb +31 -1
- data/app/models/spree/gateway/authorize_net_cim.rb +128 -47
- data/app/models/spree/gateway/balanced_gateway.rb +3 -5
- data/app/models/spree/gateway/braintree_gateway.rb +55 -18
- data/app/models/spree/gateway/cyber_source.rb +10 -0
- data/app/models/spree/gateway/migs.rb +11 -0
- data/app/models/spree/gateway/payflow_pro.rb +1 -0
- data/app/models/spree/gateway/pin_gateway.rb +45 -0
- data/app/models/spree/gateway/spreedly_core_gateway.rb +10 -0
- data/app/models/spree/gateway/stripe_gateway.rb +29 -6
- data/app/models/spree/gateway/usa_epay_transaction.rb +9 -0
- data/config/routes.rb +1 -1
- data/db/migrate/20111118164631_create_skrill_transactions.rb +1 -2
- data/lib/assets/javascripts/spree/frontend/spree_gateway.js +1 -0
- data/{app → lib}/assets/stylesheets/spree/frontend/spree_gateway.css +0 -0
- data/{app/controllers → lib/controllers/frontend}/spree/checkout_controller_decorator.rb +1 -1
- data/{app/controllers → lib/controllers/frontend}/spree/skrill_status_controller.rb +0 -0
- data/lib/spree_gateway.rb +1 -1
- data/lib/spree_gateway/engine.rb +56 -25
- data/{app/views → lib/views/backend}/spree/admin/log_entries/_braintree.html.erb +0 -0
- data/{app/views → lib/views/backend}/spree/admin/log_entries/_stripe.html.erb +0 -0
- data/{app/views → lib/views/backend}/spree/admin/payments/source_forms/_quickcheckout.html.erb +0 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_stripe.html.erb +1 -0
- data/{app/views → lib/views/backend}/spree/admin/payments/source_views/_quickcheckout.html.erb +0 -0
- data/{app/views → lib/views/backend}/spree/admin/payments/source_views/_stripe.html.erb +0 -0
- data/{app/views → lib/views/frontend}/spree/checkout/payment/_quickcheckout.html.erb +2 -2
- data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +90 -0
- data/spec/factories/payment_method_factory.rb +0 -1
- data/spec/features/stripe_checkout_spec.rb +15 -4
- data/spec/models/gateway/authorize_net_cim_spec.rb +6 -6
- data/spec/models/gateway/authorize_net_spec.rb +6 -6
- data/spec/models/gateway/balanced_gateway_spec.rb +1 -1
- data/spec/models/gateway/beanstream_spec.rb +1 -1
- data/spec/models/gateway/braintree_gateway_spec.rb +147 -31
- data/spec/models/gateway/cyber_source_spec.rb +11 -0
- data/spec/models/gateway/eway_spec.rb +3 -3
- data/spec/models/gateway/maxipago_spec.rb +1 -1
- data/spec/models/gateway/pay_junction_spec.rb +2 -2
- data/spec/models/gateway/payflow_pro_spec.rb +2 -2
- data/spec/models/gateway/pin_gateway_spec.rb +4 -6
- data/spec/models/gateway/stripe_gateway_spec.rb +90 -9
- data/spec/models/gateway/{usa_epay_spec.rb → usa_epay_transaction_spec.rb} +9 -9
- data/spec/spec_helper.rb +5 -2
- data/spree_gateway.gemspec +15 -16
- metadata +89 -98
- data/Versionfile +0 -7
- data/app/assets/javascripts/spree/frontend/spree_gateway.js +0 -3
- data/app/assets/javascripts/store/gateway/stripe.js.coffee +0 -58
- data/app/assets/javascripts/store/spree_gateway.js +0 -2
- data/app/models/spree/gateway/fatzebra.rb +0 -15
- data/app/models/spree/gateway/linkpoint.rb +0 -28
- data/app/models/spree/gateway/samurai.rb +0 -63
- data/app/models/spree/gateway/usa_epay.rb +0 -9
- data/app/views/spree/admin/payments/source_forms/_stripe.html.erb +0 -1
- data/app/views/spree/checkout/payment/_stripe.html.erb +0 -21
- data/config/initializers/savon.rb +0 -3
- data/spec/models/gateway/fatzebra_spec.rb +0 -51
- data/spec/models/gateway/linkpoint_spec.rb +0 -62
- data/spec/models/gateway/samurai_spec.rb +0 -17
- data/spec/models/savon_spec.rb +0 -9
@@ -6,10 +6,55 @@ module Spree
|
|
6
6
|
def provider_class
|
7
7
|
ActiveMerchant::Billing::PinGateway
|
8
8
|
end
|
9
|
+
|
10
|
+
def purchase(money, creditcard, options = {})
|
11
|
+
super(money, creditcard.try(:gateway_customer_profile_id) || creditcard.try(:gateway_payment_profile_id) || creditcard, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_profile(payment)
|
15
|
+
if payment.source.gateway_customer_profile_id.nil?
|
16
|
+
response = provider.store(payment.source, options_for_payment(payment))
|
17
|
+
|
18
|
+
if response.success?
|
19
|
+
payment.source.update_attributes!(:gateway_customer_profile_id => response.authorization)
|
20
|
+
|
21
|
+
cc = response.params['response']['card']
|
22
|
+
payment.source.update_attributes!(:gateway_payment_profile_id => cc['token']) if cc
|
23
|
+
else
|
24
|
+
payment.send(:gateway_error, response.message)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
9
28
|
|
10
29
|
# Pin does not appear to support authorizing transactions yet
|
11
30
|
def auto_capture?
|
12
31
|
true
|
13
32
|
end
|
33
|
+
|
34
|
+
def payment_profiles_supported?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def options_for_payment(p)
|
41
|
+
o = Hash.new
|
42
|
+
o[:email] = p.order.email
|
43
|
+
|
44
|
+
if p.order.bill_address
|
45
|
+
bill_addr = p.order.bill_address
|
46
|
+
|
47
|
+
o[:billing_address] = {
|
48
|
+
address1: bill_addr.address1,
|
49
|
+
city: bill_addr.city,
|
50
|
+
state: bill_addr.state ? bill_addr.state.name : bill_addr.state_name,
|
51
|
+
country: bill_addr.country.iso3,
|
52
|
+
zip: bill_addr.zipcode
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
return o
|
57
|
+
end
|
58
|
+
|
14
59
|
end
|
15
60
|
end
|
@@ -3,6 +3,12 @@ module Spree
|
|
3
3
|
preference :secret_key, :string
|
4
4
|
preference :publishable_key, :string
|
5
5
|
|
6
|
+
CARD_TYPE_MAPPING = {
|
7
|
+
'American Express' => 'american_express',
|
8
|
+
'Diners Club' => 'diners_club',
|
9
|
+
'Visa' => 'visa'
|
10
|
+
}
|
11
|
+
|
6
12
|
def method_type
|
7
13
|
'stripe'
|
8
14
|
end
|
@@ -23,8 +29,8 @@ module Spree
|
|
23
29
|
provider.authorize(*options_for_purchase_or_auth(money, creditcard, gateway_options))
|
24
30
|
end
|
25
31
|
|
26
|
-
def capture(
|
27
|
-
provider.capture(
|
32
|
+
def capture(money, response_code, gateway_options)
|
33
|
+
provider.capture(money, response_code, gateway_options)
|
28
34
|
end
|
29
35
|
|
30
36
|
def credit(money, creditcard, response_code, gateway_options)
|
@@ -35,6 +41,10 @@ module Spree
|
|
35
41
|
provider.void(response_code, {})
|
36
42
|
end
|
37
43
|
|
44
|
+
def cancel(response_code)
|
45
|
+
provider.void(response_code, {})
|
46
|
+
end
|
47
|
+
|
38
48
|
def create_profile(payment)
|
39
49
|
return unless payment.source.gateway_customer_profile_id.nil?
|
40
50
|
options = {
|
@@ -42,12 +52,21 @@ module Spree
|
|
42
52
|
login: preferred_secret_key,
|
43
53
|
}.merge! address_for(payment)
|
44
54
|
|
45
|
-
|
55
|
+
source = update_source!(payment.source)
|
56
|
+
if source.number.blank? && source.gateway_payment_profile_id.present?
|
57
|
+
creditcard = source.gateway_payment_profile_id
|
58
|
+
else
|
59
|
+
creditcard = source
|
60
|
+
end
|
61
|
+
|
62
|
+
response = provider.store(creditcard, options)
|
46
63
|
if response.success?
|
47
64
|
payment.source.update_attributes!({
|
48
|
-
:
|
49
|
-
:
|
65
|
+
cc_type: payment.source.cc_type, # side-effect of update_source!
|
66
|
+
gateway_customer_profile_id: response.params['id'],
|
67
|
+
gateway_payment_profile_id: response.params['default_source'] || response.params['default_card']
|
50
68
|
})
|
69
|
+
|
51
70
|
else
|
52
71
|
payment.send(:gateway_error, response.message)
|
53
72
|
end
|
@@ -61,7 +80,6 @@ module Spree
|
|
61
80
|
options.merge(:login => preferred_secret_key)
|
62
81
|
end
|
63
82
|
|
64
|
-
|
65
83
|
def options_for_purchase_or_auth(money, creditcard, gateway_options)
|
66
84
|
options = {}
|
67
85
|
options[:description] = "Spree Order ID: #{gateway_options[:order_id]}"
|
@@ -99,5 +117,10 @@ module Spree
|
|
99
117
|
end
|
100
118
|
end
|
101
119
|
end
|
120
|
+
|
121
|
+
def update_source!(source)
|
122
|
+
source.cc_type = CARD_TYPE_MAPPING[source.cc_type] if CARD_TYPE_MAPPING.include?(source.cc_type)
|
123
|
+
source
|
124
|
+
end
|
102
125
|
end
|
103
126
|
end
|
data/config/routes.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
//= require spree/frontend
|
File without changes
|
@@ -38,7 +38,7 @@ module Spree
|
|
38
38
|
|
39
39
|
private
|
40
40
|
def confirm_skrill
|
41
|
-
return unless (params[:state] == "payment") && params[:order][:payments_attributes]
|
41
|
+
return unless (params[:state] == "payment") && params[:order] && params[:order][:payments_attributes]
|
42
42
|
|
43
43
|
payment_method = PaymentMethod.find(params[:order][:payments_attributes].first[:payment_method_id])
|
44
44
|
if payment_method.kind_of?(BillingIntegration::Skrill::QuickCheckout)
|
File without changes
|
data/lib/spree_gateway.rb
CHANGED
data/lib/spree_gateway/engine.rb
CHANGED
@@ -5,31 +5,62 @@ module SpreeGateway
|
|
5
5
|
config.autoload_paths += %W(#{config.root}/lib)
|
6
6
|
|
7
7
|
initializer "spree.gateway.payment_methods", :after => "spree.register.payment_methods" do |app|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
8
|
+
app.config.spree.payment_methods << Spree::BillingIntegration::Skrill::QuickCheckout
|
9
|
+
app.config.spree.payment_methods << Spree::Gateway::AuthorizeNet
|
10
|
+
app.config.spree.payment_methods << Spree::Gateway::AuthorizeNetCim
|
11
|
+
app.config.spree.payment_methods << Spree::Gateway::BalancedGateway
|
12
|
+
app.config.spree.payment_methods << Spree::Gateway::Banwire
|
13
|
+
app.config.spree.payment_methods << Spree::Gateway::Beanstream
|
14
|
+
app.config.spree.payment_methods << Spree::Gateway::BraintreeGateway
|
15
|
+
app.config.spree.payment_methods << Spree::Gateway::CardSave
|
16
|
+
app.config.spree.payment_methods << Spree::Gateway::CyberSource
|
17
|
+
app.config.spree.payment_methods << Spree::Gateway::DataCash
|
18
|
+
app.config.spree.payment_methods << Spree::Gateway::Eway
|
19
|
+
app.config.spree.payment_methods << Spree::Gateway::Maxipago
|
20
|
+
app.config.spree.payment_methods << Spree::Gateway::Migs
|
21
|
+
app.config.spree.payment_methods << Spree::Gateway::Moneris
|
22
|
+
app.config.spree.payment_methods << Spree::Gateway::PayJunction
|
23
|
+
app.config.spree.payment_methods << Spree::Gateway::PayPalGateway
|
24
|
+
app.config.spree.payment_methods << Spree::Gateway::PayflowPro
|
25
|
+
app.config.spree.payment_methods << Spree::Gateway::Paymill
|
26
|
+
app.config.spree.payment_methods << Spree::Gateway::PinGateway
|
27
|
+
app.config.spree.payment_methods << Spree::Gateway::SagePay
|
28
|
+
app.config.spree.payment_methods << Spree::Gateway::SecurePayAU
|
29
|
+
app.config.spree.payment_methods << Spree::Gateway::SpreedlyCoreGateway
|
30
|
+
app.config.spree.payment_methods << Spree::Gateway::StripeGateway
|
31
|
+
app.config.spree.payment_methods << Spree::Gateway::UsaEpayTransaction
|
32
|
+
app.config.spree.payment_methods << Spree::Gateway::Worldpay
|
33
33
|
end
|
34
|
+
|
35
|
+
def self.activate
|
36
|
+
if SpreeGateway::Engine.frontend_available?
|
37
|
+
Rails.application.config.assets.precompile += [
|
38
|
+
'lib/assets/javascripts/spree/frontend/spree_gateway.js',
|
39
|
+
'lib/assets/javascripts/spree/frontend/spree_gateway.css',
|
40
|
+
]
|
41
|
+
Dir.glob(File.join(File.dirname(__FILE__), "../../controllers/frontend/*/*_decorator*.rb")) do |c|
|
42
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.backend_available?
|
48
|
+
@@backend_available ||= ::Rails::Engine.subclasses.map(&:instance).map{ |e| e.class.to_s }.include?('Spree::Backend::Engine')
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.frontend_available?
|
52
|
+
@@frontend_available ||= ::Rails::Engine.subclasses.map(&:instance).map{ |e| e.class.to_s }.include?('Spree::Frontend::Engine')
|
53
|
+
end
|
54
|
+
|
55
|
+
if self.backend_available?
|
56
|
+
paths["app/views"] << "lib/views/backend"
|
57
|
+
end
|
58
|
+
|
59
|
+
if self.frontend_available?
|
60
|
+
paths["app/controllers"] << "lib/controllers/frontend"
|
61
|
+
paths["app/views"] << "lib/views/frontend"
|
62
|
+
end
|
63
|
+
|
64
|
+
config.to_prepare &method(:activate).to_proc
|
34
65
|
end
|
35
66
|
end
|
File without changes
|
File without changes
|
data/{app/views → lib/views/backend}/spree/admin/payments/source_forms/_quickcheckout.html.erb
RENAMED
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render "spree/admin/payments/source_forms/gateway", payment_method: payment_method, previous_cards: payment_method.reusable_sources(@order) %>
|
data/{app/views → lib/views/backend}/spree/admin/payments/source_views/_quickcheckout.html.erb
RENAMED
File without changes
|
File without changes
|
@@ -4,9 +4,9 @@
|
|
4
4
|
opts = {}
|
5
5
|
opts[:transaction_id] = "#{@order.number}"
|
6
6
|
opts[:amount] = @order.total
|
7
|
-
opts[:return_url] = skrill_return_order_checkout_url(@order, :token => @order.
|
7
|
+
opts[:return_url] = skrill_return_order_checkout_url(@order, :token => @order.guest_token,
|
8
8
|
:payment_method_id => payment_method.id)
|
9
|
-
opts[:cancel_url] = skrill_cancel_order_checkout_url(@order, :token => @order.
|
9
|
+
opts[:cancel_url] = skrill_cancel_order_checkout_url(@order, :token => @order.guest_token)
|
10
10
|
opts[:status_url] = skrill_url
|
11
11
|
opts[:payment_method_id] = payment_method.id
|
12
12
|
%>
|
@@ -0,0 +1,90 @@
|
|
1
|
+
<%= render "spree/checkout/payment/gateway", payment_method: payment_method %>
|
2
|
+
|
3
|
+
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
|
4
|
+
<script type="text/javascript">
|
5
|
+
Stripe.setPublishableKey("<%= payment_method.preferred_publishable_key %>");
|
6
|
+
</script>
|
7
|
+
|
8
|
+
<script>
|
9
|
+
Spree.stripePaymentMethod = $('#payment_method_' + <%= payment_method.id %>);
|
10
|
+
|
11
|
+
var mapCC, stripeResponseHandler;
|
12
|
+
|
13
|
+
mapCC = function(ccType) {
|
14
|
+
if (ccType === 'MasterCard') {
|
15
|
+
return 'mastercard';
|
16
|
+
} else if (ccType === 'Visa') {
|
17
|
+
return 'visa';
|
18
|
+
} else if (ccType === 'American Express') {
|
19
|
+
return 'amex';
|
20
|
+
} else if (ccType === 'Discover') {
|
21
|
+
return 'discover';
|
22
|
+
} else if (ccType === 'Diners Club') {
|
23
|
+
return 'dinersclub';
|
24
|
+
} else if (ccType === 'JCB') {
|
25
|
+
return 'jcb';
|
26
|
+
}
|
27
|
+
};
|
28
|
+
|
29
|
+
stripeResponseHandler = function(status, response) {
|
30
|
+
var paymentMethodId, token;
|
31
|
+
if (response.error) {
|
32
|
+
$('#stripeError').html(response.error.message);
|
33
|
+
var param_map = {
|
34
|
+
number: '#card_number',
|
35
|
+
exp_month: '#card_expiry',
|
36
|
+
exp_year: '#card_expiry',
|
37
|
+
cvc: '#card_code'
|
38
|
+
}
|
39
|
+
if (response.error.param)
|
40
|
+
Spree.stripePaymentMethod.find(param_map[response.error.param]).addClass('error');
|
41
|
+
|
42
|
+
return $('#stripeError').show();
|
43
|
+
} else {
|
44
|
+
Spree.stripePaymentMethod.find('#card_number, #card_expiry, #card_code').prop("disabled", true);
|
45
|
+
Spree.stripePaymentMethod.find(".ccType").prop("disabled", false);
|
46
|
+
Spree.stripePaymentMethod.find(".ccType").val(mapCC(response.card.brand));
|
47
|
+
token = response['id'];
|
48
|
+
paymentMethodId = Spree.stripePaymentMethod.prop('id').split("_")[2];
|
49
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][gateway_payment_profile_id]' value='" + token + "'/>");
|
50
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][last_digits]' value='" + response.card.last4 + "'/>");
|
51
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][month]' value='" + response.card.exp_month + "'/>");
|
52
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][year]' value='" + response.card.exp_year + "'/>");
|
53
|
+
return Spree.stripePaymentMethod.parents("form").trigger('submit');
|
54
|
+
}
|
55
|
+
};
|
56
|
+
|
57
|
+
$(document).ready(function() {
|
58
|
+
Spree.stripePaymentMethod.prepend("<div id='stripeError' class='errorExplanation' style='display:none'></div>");
|
59
|
+
return $('#checkout_form_payment [data-hook=buttons]').click(function() {
|
60
|
+
var expiration, params;
|
61
|
+
$('#stripeError').hide();
|
62
|
+
Spree.stripePaymentMethod.find('#card_number, #card_expiry, #card_code').removeClass('error');
|
63
|
+
if (Spree.stripePaymentMethod.is(':visible')) {
|
64
|
+
expiration = $('.cardExpiry:visible').payment('cardExpiryVal');
|
65
|
+
params = $.extend({
|
66
|
+
number: $('.cardNumber:visible').val(),
|
67
|
+
cvc: $('.cardCode:visible').val(),
|
68
|
+
exp_month: expiration.month || 0,
|
69
|
+
exp_year: expiration.year || 0
|
70
|
+
}, Spree.stripeAdditionalInfo);
|
71
|
+
Stripe.card.createToken(params, stripeResponseHandler);
|
72
|
+
return false;
|
73
|
+
}
|
74
|
+
});
|
75
|
+
});
|
76
|
+
</script>
|
77
|
+
|
78
|
+
<%- if @order.has_checkout_step?('address') -%>
|
79
|
+
<script>
|
80
|
+
Spree.stripeAdditionalInfo = {
|
81
|
+
name: "<%= @order.bill_address.full_name %>",
|
82
|
+
address_line1: "<%= @order.bill_address.address1 %>",
|
83
|
+
address_line2: "<%= @order.bill_address.address2 %>",
|
84
|
+
address_city: "<%= @order.bill_address.city %>",
|
85
|
+
address_state: "<%= @order.bill_address.state_text %>",
|
86
|
+
address_zip: "<%= @order.bill_address.zipcode %>",
|
87
|
+
address_country: "<%= @order.bill_address.country %>"
|
88
|
+
}
|
89
|
+
</script>
|
90
|
+
<%- end -%>
|
@@ -10,8 +10,7 @@ describe "Stripe checkout" do
|
|
10
10
|
Spree::Gateway::StripeGateway.create!(
|
11
11
|
:name => "Stripe",
|
12
12
|
:preferred_secret_key => "sk_test_VCZnDv3GLU15TRvn8i2EsaAN",
|
13
|
-
:preferred_publishable_key => "pk_test_Cuf0PNtiAkkMpTVC2gwYDMIg"
|
14
|
-
:environment => "test"
|
13
|
+
:preferred_publishable_key => "pk_test_Cuf0PNtiAkkMpTVC2gwYDMIg"
|
15
14
|
)
|
16
15
|
end
|
17
16
|
|
@@ -63,6 +62,7 @@ describe "Stripe checkout" do
|
|
63
62
|
it "shows an error with an invalid credit card number", :js => true do
|
64
63
|
click_button "Save and Continue"
|
65
64
|
page.should have_content("This card number looks invalid")
|
65
|
+
page.should have_css('#card_number.error')
|
66
66
|
end
|
67
67
|
|
68
68
|
it "shows an error with invalid security fields", :js => true do
|
@@ -70,13 +70,24 @@ describe "Stripe checkout" do
|
|
70
70
|
fill_in "Expiration", :with => "01 / #{Time.now.year + 1}"
|
71
71
|
click_button "Save and Continue"
|
72
72
|
page.should have_content("Your card's security code is invalid.")
|
73
|
+
page.should have_css('#card_code.error')
|
73
74
|
end
|
74
75
|
|
75
|
-
it "shows an error with invalid expiry
|
76
|
+
it "shows an error with invalid expiry month field", :js => true do
|
76
77
|
fill_in "Card Number", :with => "4242 4242 4242 4242"
|
77
78
|
fill_in "Expiration", :with => "00 / #{Time.now.year + 1}"
|
78
79
|
fill_in "Card Code", :with => "123"
|
79
80
|
click_button "Save and Continue"
|
80
81
|
page.should have_content("Your card's expiration month is invalid.")
|
82
|
+
page.should have_css('#card_expiry.error')
|
81
83
|
end
|
82
|
-
|
84
|
+
|
85
|
+
it "shows an error with invalid expiry year field", :js => true do
|
86
|
+
fill_in "Card Number", :with => "4242 4242 4242 4242"
|
87
|
+
fill_in "Expiration", :with => "12 / "
|
88
|
+
fill_in "Card Code", :with => "123"
|
89
|
+
click_button "Save and Continue"
|
90
|
+
page.should have_content("Your card's expiration year is invalid.")
|
91
|
+
page.should have_css('#card_expiry.error')
|
92
|
+
end
|
93
|
+
end
|