-
Notifications
You must be signed in to change notification settings - Fork 439
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #305 from spark-solutions/use-stripe-elements
Add support for Stripe Elements and Apple Pay (via Payment Request API)
- Loading branch information
Showing
14 changed files
with
405 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module Spree | ||
class Gateway::StripeApplePayGateway < Gateway::StripeGateway | ||
preference :country_code, :string, default: 'US' | ||
preference :domain_verification_certificate, :text | ||
|
||
def method_type | ||
'stripe_apple_pay' | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module Spree | ||
class Gateway::StripeElementsGateway < Gateway::StripeGateway | ||
def method_type | ||
'stripe_elements' | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
module SpreeGateway | ||
module ApplePayOrderDecorator | ||
def confirmation_required? | ||
return false if paid_with_apple_pay? | ||
|
||
super | ||
end | ||
|
||
def paid_with_apple_pay? | ||
payments.valid.any?(&:apple_pay?) | ||
end | ||
end | ||
end | ||
|
||
Spree::Order.prepend SpreeGateway::ApplePayOrderDecorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module SpreeGateway | ||
module ApplePayPaymentDecorator | ||
def apple_pay? | ||
payment_method.is_a? Spree::Gateway::StripeApplePayGateway | ||
end | ||
end | ||
end | ||
|
||
Spree::Payment.prepend SpreeGateway::ApplePayPaymentDecorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# we need to add this route to the root Rails application because Spree can | ||
# be mounted to some path eg. /shop and Apple Pay expects to access this file | ||
# via https://example.com/.well-known/apple-developer-merchantid-domain-association | ||
Rails.application.routes.draw do | ||
get '/.well-known/apple-developer-merchantid-domain-association' => 'spree/apple_pay_domain_verification#show' | ||
end |
11 changes: 11 additions & 0 deletions
11
lib/controllers/spree/apple_pay_domain_verification_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module Spree | ||
class ApplePayDomainVerificationController < Spree::BaseController | ||
def show | ||
gateway = Spree::Gateway::StripeApplePayGateway.active.last | ||
|
||
raise ActiveRecord::RecordNotFound unless gateway | ||
|
||
render plain: gateway.preferred_domain_verification_certificate | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
lib/views/frontend/spree/checkout/payment/_stripe_additional_info.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<% bill_address ||= @order.bill_address %> | ||
<%- if @order.has_checkout_step?('address') -%> | ||
<script> | ||
Spree.stripeAdditionalInfo = { | ||
name: "<%= bill_address.full_name %>", | ||
address_line1: "<%= bill_address.address1 %>", | ||
address_line2: "<%= bill_address.address2 %>", | ||
address_city: "<%= bill_address.city %>", | ||
address_state: "<%= bill_address.state_text %>", | ||
address_zip: "<%= bill_address.zipcode %>", | ||
address_country: "<%= bill_address.country %>" | ||
} | ||
</script> | ||
<%- end -%> |
97 changes: 97 additions & 0 deletions
97
lib/views/frontend/spree/checkout/payment/_stripe_apple_pay.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<% param_prefix = "payment_source[#{payment_method.id}]" %> | ||
|
||
<div id="payment-request-button"> | ||
<!-- A Stripe Element will be inserted here. --> | ||
</div> | ||
|
||
<script type="text/javascript" src="https://js.stripe.com/v3/"></script> | ||
|
||
<script> | ||
Spree.stripeApplePayPaymentMethod = $('#payment_method_' + <%= payment_method.id %>); | ||
var stripeApplePay = Stripe("<%= payment_method.preferred_publishable_key %>"); | ||
var elements = stripeApplePay.elements(); | ||
|
||
var paymentRequest = stripeApplePay.paymentRequest({ | ||
country: '<%= payment_method.preferred_country_code.try(:upcase) %>', | ||
currency: '<%= @order.currency.downcase %>', | ||
displayItems: [ | ||
<% @order.line_items.each do |line_item| %> | ||
{ | ||
label: '<%= line_item.name %> x <%= line_item.quantity %>', | ||
amount: <%= Spree::Money.new(line_item.total, currency: line_item.currency).amount_in_cents %> | ||
}, | ||
<% end %> | ||
<% if @order.tax_total != 0 %> | ||
{ | ||
label: '<%= Spree.t(:tax) %>', | ||
amount: <%= Spree::Money.new(@order.tax_total, currency: @order.currency).amount_in_cents %> | ||
}, | ||
<% end %> | ||
<% if @order.shipment_total != 0 %> | ||
{ | ||
label: '<%= Spree.t(:shipment) %>', | ||
amount: <%= Spree::Money.new(@order.shipment_total, currency: @order.currency).amount_in_cents %> | ||
} | ||
<% end %> | ||
], | ||
total: { | ||
label: '<%= Spree.t(:total) %>', | ||
amount: <%= Spree::Money.new(@order.total, currency: @order.currency).amount_in_cents %> | ||
}, | ||
requestPayerName: false, | ||
requestPayerEmail: false, | ||
requestPayerPhone: false | ||
}); | ||
|
||
var prButton = elements.create('paymentRequestButton', { | ||
paymentRequest: paymentRequest, | ||
}); | ||
|
||
// Check the availability of the Payment Request API first. | ||
paymentRequest.canMakePayment().then(function(result) { | ||
if (result) { | ||
prButton.mount('#payment-request-button'); | ||
} else { | ||
document.getElementById('payment-request-button').style.display = 'none'; | ||
} | ||
}); | ||
|
||
function addCreditCardFieldToForm(form, name, value) { | ||
var hiddenInput = document.createElement('input'); | ||
|
||
hiddenInput.setAttribute('type', 'hidden'); | ||
hiddenInput.setAttribute('name', name); | ||
hiddenInput.setAttribute('value', value); | ||
form.appendChild(hiddenInput); | ||
}; | ||
|
||
paymentRequest.on('token', function(ev) { | ||
var form = document.getElementById('checkout_form_payment'); | ||
var token = ev.token; | ||
if (ev.payerName) { | ||
var payerName = ev.payerName | ||
} else if (Spree.stripeAdditionalInfo) { | ||
var payerName = Spree.stripeAdditionalInfo.name | ||
} | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[gateway_payment_profile_id]', token.id); | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[number]', token.card.last4); | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[month]', token.card.exp_month); | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[year]', token.card.exp_year); | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[name]', payerName); | ||
ev.complete('success'); | ||
form.submit(); | ||
}); | ||
|
||
Spree.ready(function() { | ||
Spree.stripeApplePayPaymentMethod.prepend("<div id='stripeApplePayError' class='errorExplanation alert alert-danger' style='display:none'></div>"); | ||
var form = document.getElementById('checkout_form_payment'); | ||
form.addEventListener('submit', function(e) { | ||
if (Spree.stripeApplePayPaymentMethod.is(':visible')) { | ||
$('#stripeApplePayError').hide(); | ||
e.preventDefault(); | ||
} | ||
}); | ||
}); | ||
</script> | ||
|
||
<%= render 'spree/checkout/payment/stripe_additional_info' %> |
104 changes: 104 additions & 0 deletions
104
lib/views/frontend/spree/checkout/payment/_stripe_elements.html.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<% param_prefix = "payment_source[#{payment_method.id}]" %> | ||
<div class="well clearfix"> | ||
<p class="field"> | ||
<%= label_tag "name_on_card_#{payment_method.id}" do %> | ||
<%= Spree.t(:name_on_card) %><abbr class="required" title="required">*</abbr> | ||
<% end %> | ||
<%= text_field_tag "#{param_prefix}[name]", "#{@order.bill_address_firstname} #{@order.bill_address_lastname}", { id: "name_on_card_#{payment_method.id}", class: 'form-control required'} %> | ||
</p> | ||
<div class="form-row"> | ||
<%= label_tag "card_number" do %> | ||
<%= Spree.t(:card_number) %><abbr class="required" title="required">*</abbr> | ||
<% end %> | ||
<div class="form-control required cardNumber"> | ||
<div id="card-element"> | ||
<!-- a Stripe Element will be inserted here. --> | ||
</div> | ||
</div> | ||
<!-- Used to display form errors --> | ||
<div id="card-errors" role="alert"></div> | ||
</div> | ||
</p> | ||
</div> | ||
|
||
<script type="text/javascript" src="https://js.stripe.com/v3/"></script> | ||
|
||
<script> | ||
Spree.stripeElementsPaymentMethod = $('#payment_method_' + <%= payment_method.id %>); | ||
var stripeElements = Stripe("<%= payment_method.preferred_publishable_key %>"); | ||
var elements = stripeElements.elements(); | ||
|
||
var card = elements.create('card', { | ||
iconStyle: 'solid', | ||
hidePostalCode: true, | ||
style: { | ||
base: { | ||
iconColor: '#555555', | ||
lineHeight: '24px', | ||
fontWeight: 300, | ||
fontFamily: '"Helvetica Neue", Helvetica, sans-serif', | ||
fontSize: '14px', | ||
|
||
'::placeholder': { | ||
color: '#555555', | ||
}, | ||
}, | ||
invalid: { | ||
iconColor: '#e85746', | ||
color: '#e85746', | ||
} | ||
}, | ||
classes: { | ||
focus: 'is-focused', | ||
empty: 'is-empty', | ||
}, | ||
}); | ||
card.mount('#card-element'); | ||
|
||
function addCreditCardFieldToForm(form, name, value) { | ||
var hiddenInput = document.createElement('input'); | ||
|
||
hiddenInput.setAttribute('type', 'hidden'); | ||
hiddenInput.setAttribute('name', name); | ||
hiddenInput.setAttribute('value', value); | ||
form.appendChild(hiddenInput); | ||
}; | ||
|
||
function stripeElementsTokenHandler(token) { | ||
var form = document.getElementById('checkout_form_payment'); | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[gateway_payment_profile_id]', token.id) | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[number]', token.card.last4) | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[month]', token.card.exp_month) | ||
addCreditCardFieldToForm(form, '<%= param_prefix %>[year]', token.card.exp_year) | ||
form.submit(); | ||
}; | ||
|
||
function createStripeElementsToken() { | ||
stripeElements.createToken(card, Spree.stripeAdditionalInfo).then(function (result) { | ||
if (result.error) { | ||
// Inform the user if there was an error | ||
var errorElement = document.getElementById('card-errors'); | ||
|
||
$('#stripeElementsError').html(result.error.message); | ||
$('#stripeElementsError').show() | ||
Spree.enableSave(); | ||
} else { | ||
stripeElementsTokenHandler(result.token); | ||
} | ||
}); | ||
}; | ||
|
||
Spree.ready(function() { | ||
Spree.stripeElementsPaymentMethod.prepend("<div id='stripeElementsError' class='errorExplanation alert alert-danger' style='display:none'></div>"); | ||
var form = document.getElementById('checkout_form_payment'); | ||
form.addEventListener('submit', function(e) { | ||
if (Spree.stripeElementsPaymentMethod.is(':visible')) { | ||
$('#stripeElementsError').hide(); | ||
e.preventDefault(); | ||
createStripeElementsToken(); | ||
} | ||
}); | ||
}); | ||
</script> | ||
|
||
<%= render 'spree/checkout/payment/stripe_additional_info' %> |
Oops, something went wrong.