Add Stripe form and duplicate Stripe Elements in backend Angular app

This commit is contained in:
stveep
2018-06-17 18:15:41 +01:00
parent e0d13010bb
commit 8272aebe29
10 changed files with 166 additions and 4 deletions

View File

@@ -0,0 +1,9 @@
angular.module("admin.payments").controller "PaymentCtrl", ($scope, Payment, Loading) ->
$scope.form_data = Payment.form_data
$scope.submitted = false
$scope.submitPayment = () ->
return false if $scope.submitted == true
$scope.submitted = true
Loading.message = t("submitting_payment")
Payment.purchase()

View File

@@ -0,0 +1,35 @@
angular.module("admin.payments").directive "stripeElements", ($injector, StripeElements) ->
restrict: 'E'
template: "<label for='card-element'>\
<div id='card-element'></div>\
<div id='card-errors' class='error'></div>\
</label>"
link: (scope, elem, attr)->
if $injector.has('stripeObject')
stripe = $injector.get('stripeObject')
card = stripe.elements().create 'card',
hidePostalCode: false
style:
base:
fontFamily: "Roboto, Arial, sans-serif"
fontSize: '16px'
color: '#5c5c5c'
'::placeholder':
color: '#6c6c6c'
card.mount('#card-element')
# Elements validates user input as it is typed. To help your customers
# catch mistakes, you should listen to change events on the card Element
# and display any errors:
card.addEventListener 'change', (event) ->
displayError = document.getElementById('card-errors')
if event.error
displayError.textContent = event.error.message
else
displayError.textContent = ''
return
StripeElements.stripe = stripe
StripeElements.card = card

View File

@@ -0,0 +1 @@
angular.module("admin.payments", ['OfnStripe','Loading','RailsFlashLoader','ngResource','admin.resources', 'ofn.admin','Navigation'])

View File

@@ -0,0 +1,47 @@
angular.module('admin.payments').factory 'Payment', (StripeElements, currentOrderNumber, paymentMethods, PaymentMethods, PaymentResource, Navigation, RailsFlashLoader)->
new class Payment
order: currentOrderNumber
form_data: {}
paymentMethodType: ->
PaymentMethods.byID[@form_data.payment_method].method_type
preprocess: ->
munged_payment = {}
munged_payment["payment"] = {payment_method_id: @form_data.payment_method, amount: @form_data.amount}
munged_payment["order_id"] = @order
# Not tested with Gateway other than Stripe. Could fall back to Rails for this?
# Works ok without extra source_attrs for Cash, Bank Transfer etc.
switch @paymentMethodType()
when 'gateway'
angular.extend munged_payment.payment, {
source_attributes:
number: @form_data.card_number
month: @form_data.card_month
year: @form_data.card_year
verification_value: @form_data.card_verification_value
}
when 'stripe'
angular.extend munged_payment.payment, {
source_attributes:
gateway_payment_profile_id: @form_data.token
cc_type: @form_data.cc_type
last_digits: @form_data.card.last4
month: @form_data.card.exp_month
year: @form_data.card.exp_year
}
munged_payment
purchase: ->
if @paymentMethodType() == 'stripe'
StripeElements.requestToken(@form_data, @submit)
else
@submit()
submit: =>
munged = @preprocess()
PaymentResource.create({order_id: munged.order_id}, munged, (response, headers, status)=>
Navigation.go "/admin/orders/" + munged.order_id + "/payments"
, (response) ->
RailsFlashLoader.loadFlash({error: t("error saving payment")})
)

View File

@@ -0,0 +1,47 @@
angular.module("admin.payments").factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
new class StripeElements
# TODO: add locale here for translations of error messages etc. from Stripe
# These are both set from the StripeElements directive
stripe: null
card: null
# New Stripe Elements method
requestToken: (secrets, submit, loading_message = t("processing_payment")) ->
return unless @stripe? && @card?
Loading.message = loading_message
cardData = @makeCardData(secrets)
@stripe.createToken(@card, cardData).then (response) =>
if(response.error)
Loading.clear()
RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"})
else
secrets.token = response.token.id
secrets.cc_type = @mapCC(response.token.card.brand)
secrets.card = response.token.card
submit()
# Maps the brand returned by Stripe to that required by activemerchant
mapCC: (ccType) ->
if ccType == 'MasterCard'
return 'master'
else if ccType == 'Visa'
return 'visa'
else if ccType == 'American Express'
return 'american_express'
else if ccType == 'Discover'
return 'discover'
else if ccType == 'JCB'
return 'jcb'
else if ccType == 'Diners Club'
return 'diners_club'
return
# It doesn't matter if any of these are nil, all are optional.
makeCardData: (secrets) ->
{'name': secrets.name,
'address1': secrets.address1,
'city': secrets.city,
'zipcode': secrets.zipcode}

View File

@@ -0,0 +1,5 @@
angular.module("admin.resources").factory 'PaymentResource', ($resource) ->
$resource('/admin/orders/:order_id/payments.json', {order_id: "@order_id"}, {
'create':
method: 'POST'
})

View File

@@ -0,0 +1,4 @@
<!-- replace 'code[erb-loud]:contains("form_for @payment")' -->
<%= form_for @payment, :url => admin_order_payments_path(@order),
:html => {"ng-app" => "admin.payments",
"ng-controller" => "PaymentCtrl"} do |f| %>

View File

@@ -0,0 +1,2 @@
/replace 'code[erb-loud]:contains("button @order.cart?")'
= button_tag t(:update), type: 'button', "ng-click" => "submitPayment()"

View File

@@ -2,7 +2,7 @@
<div class="alpha three columns">
<div class="field">
<%= f.label :amount, t(:amount) %>
<%= f.text_field :amount, :value => @order.outstanding_balance, :class => 'fullwidth' %>
<%= f.text_field :amount, :value => @order.outstanding_balance, :class => 'fullwidth', "watch-value-as" => 'form_data.amount', "ng-init" => "form_data.amount=#{@order.outstanding_balance}" %>
</div>
</div>
<div class="omega nine columns">
@@ -12,7 +12,7 @@
<% @payment_methods.each do |method| %>
<li>
<label data-hook="payment_method_field">
<%= radio_button_tag 'payment[payment_method_id]', method.id, method == @payment_method, { class: "payment_methods_radios" } %>
<%= radio_button_tag 'payment[payment_method_id]', method.id, method == @payment_method, { class: "payment_methods_radios", "ng-model" => 'form_data.payment_method' } %>
<%= t(method.name, :scope => :payment_methods, :default => method.name) %>
</label>
</li>

View File

@@ -1,4 +1,16 @@
-# = render "spree/admin/payments/source_forms/gateway", payment_method: payment_method
.stripe
%script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"}
- if Stripe.publishable_key
:javascript
angular.module('admin.payments').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
%strong
= t('.no_payment_via_admin_backend')
= admin_inject_json "admin.payments", "railsFlash", "flash"
= admin_inject_json "admin.payments", "currentOrderNumber", @order.number
= admin_inject_json_ams_array "admin.payments", "paymentMethods", @payment_methods, Api::PaymentMethodSerializer
.row
.three.columns
= label_tag :cardholder_name, t(:cardholder_name)
.six.columns
= text_field_tag :cardholder_name, nil, {size: 40, "ng-model" => 'form_data.name'}
%stripe-elements