diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 5cb8af0ebe..292b5e21ef 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -36,6 +36,7 @@ //= require ./orders/orders //= require ./order_cycles/order_cycles //= require ./payment_methods/payment_methods +//= require ./payments/payments //= require ./product_import/product_import //= require ./products/products //= require ./resources/resources diff --git a/app/assets/javascripts/admin/payments/controllers/payment.js.coffee b/app/assets/javascripts/admin/payments/controllers/payment.js.coffee new file mode 100644 index 0000000000..549ec365a1 --- /dev/null +++ b/app/assets/javascripts/admin/payments/controllers/payment.js.coffee @@ -0,0 +1,10 @@ +angular.module("admin.payments").controller "PaymentCtrl", ($scope, Payment, StatusMessage) -> + $scope.form_data = Payment.form_data + $scope.submitted = false + $scope.StatusMessage = StatusMessage + + $scope.submitPayment = () -> + return false if $scope.submitted + $scope.submitted = true + StatusMessage.display 'progress', t("spree.admin.payments.source_forms.stripe.submitting_payment") + Payment.purchase() diff --git a/app/assets/javascripts/admin/payments/directives/stripe_elements.js.coffee b/app/assets/javascripts/admin/payments/directives/stripe_elements.js.coffee new file mode 100644 index 0000000000..d9de8a2d54 --- /dev/null +++ b/app/assets/javascripts/admin/payments/directives/stripe_elements.js.coffee @@ -0,0 +1,36 @@ +angular.module('admin.payments').directive "stripeElements", ($injector, AdminStripeElements) -> + restrict: 'E' + template: "" + + 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 + + AdminStripeElements.stripe = stripe + AdminStripeElements.card = card diff --git a/app/assets/javascripts/admin/payments/payments.js.coffee b/app/assets/javascripts/admin/payments/payments.js.coffee new file mode 100644 index 0000000000..a6828adb70 --- /dev/null +++ b/app/assets/javascripts/admin/payments/payments.js.coffee @@ -0,0 +1 @@ +angular.module("admin.payments", ['ofn.admin']) diff --git a/app/assets/javascripts/admin/payments/services/payment.js.coffee b/app/assets/javascripts/admin/payments/services/payment.js.coffee new file mode 100644 index 0000000000..f079137818 --- /dev/null +++ b/app/assets/javascripts/admin/payments/services/payment.js.coffee @@ -0,0 +1,47 @@ +angular.module('admin.payments').factory 'Payment', (AdminStripeElements, currentOrderNumber, paymentMethods, PaymentMethods, PaymentResource, StatusMessage, $window)-> + 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' + AdminStripeElements.requestToken(@form_data, @submit) + else + @submit() + + submit: => + munged = @preprocess() + PaymentResource.create({order_id: munged.order_id}, munged, (response, headers, status)=> + $window.location.pathname = "/admin/orders/" + munged.order_id + "/payments" + , (response) -> + StatusMessage.display 'error', t("spree.admin.payments.source_forms.stripe.error_saving_payment") + ) diff --git a/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee b/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee new file mode 100644 index 0000000000..03971be228 --- /dev/null +++ b/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee @@ -0,0 +1,38 @@ +angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, StatusMessage) -> + new class AdminStripeElements + + # These are both set from the AdminStripeElements directive + stripe: null + card: null + + # New Stripe Elements method + requestToken: (secrets, submit) -> + return unless @stripe? && @card? + + cardData = @makeCardData(secrets) + + @stripe.createToken(@card, cardData).then (response) => + if(response.error) + StatusMessage.display '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) -> + switch ccType + when 'MasterCard' then return 'master' + when 'Visa' then return 'visa' + when 'American Express' then return 'american_express' + when 'Discover' then return 'discover' + when 'JCB' then return 'jcb' + when 'Diners Club' then return 'diners_club' + + # 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} diff --git a/app/assets/javascripts/admin/resources/resources/payment_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/payment_resource.js.coffee new file mode 100644 index 0000000000..c3dba12b64 --- /dev/null +++ b/app/assets/javascripts/admin/resources/resources/payment_resource.js.coffee @@ -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' + }) diff --git a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee index 1b0db2dcc9..32b0535251 100644 --- a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee +++ b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee @@ -25,19 +25,13 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> # 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 + switch ccType + when 'MasterCard' then return 'master' + when 'Visa' then return 'visa' + when 'American Express' then return 'american_express' + when 'Discover' then return 'discover' + when 'JCB' then return 'jcb' + when 'Diners Club' then return 'diners_club' # It doesn't matter if any of these are nil, all are optional. makeCardData: (secrets) -> diff --git a/app/overrides/spree/admin/payments/new/add_angular_to_form.html.haml.deface b/app/overrides/spree/admin/payments/new/add_angular_to_form.html.haml.deface new file mode 100644 index 0000000000..b2c55d6a8b --- /dev/null +++ b/app/overrides/spree/admin/payments/new/add_angular_to_form.html.haml.deface @@ -0,0 +1,2 @@ +/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| diff --git a/app/overrides/spree/admin/payments/new/add_save_bar.html.haml.deface b/app/overrides/spree/admin/payments/new/add_save_bar.html.haml.deface new file mode 100644 index 0000000000..209c4c820b --- /dev/null +++ b/app/overrides/spree/admin/payments/new/add_save_bar.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_before "fieldset.no-border-top" + +%save-bar{ persist: "true" } diff --git a/app/overrides/spree/admin/payments/new/override_submit_for_button.html.haml.deface b/app/overrides/spree/admin/payments/new/override_submit_for_button.html.haml.deface new file mode 100644 index 0000000000..4fb4d91ad5 --- /dev/null +++ b/app/overrides/spree/admin/payments/new/override_submit_for_button.html.haml.deface @@ -0,0 +1,2 @@ +/replace 'code[erb-loud]:contains("button @order.cart?")' += button_tag t(:update), type: 'button', "ng-click" => "submitPayment()" diff --git a/app/views/spree/admin/payments/_form.html.erb b/app/views/spree/admin/payments/_form.html.erb index fe019007b1..e46b2febcd 100644 --- a/app/views/spree/admin/payments/_form.html.erb +++ b/app/views/spree/admin/payments/_form.html.erb @@ -1,8 +1,11 @@ +<%= admin_inject_json "admin.payments", "currentOrderNumber", @order.number %> +<%= admin_inject_json_ams_array "admin.payments", "paymentMethods", @payment_methods, Api::PaymentMethodSerializer %> +