spree_paypal_api_checkout 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +5 -0
- data/README.md +55 -0
- data/Rakefile +21 -0
- data/app/assets/javascripts/spree/backend/spree_paypal_checkout.js +25 -0
- data/app/assets/javascripts/spree/frontend/spree_paypal_checkout.js +31 -0
- data/app/assets/stylesheets/spree/backend/spree_paypal_checkout.css +3 -0
- data/app/assets/stylesheets/spree/frontend/spree_paypal_checkout.css +3 -0
- data/app/controllers/spree/admin/payments_controller_decorator.rb +21 -0
- data/app/controllers/spree/admin/paypal_checkout_payments_controller.rb +15 -0
- data/app/controllers/spree/paypal_checkout_controller.rb +110 -0
- data/app/models/spree/gateway/pay_pal_checkout.rb +141 -0
- data/app/models/spree/paypal_api_checkout.rb +3 -0
- data/app/views/spree/admin/payments/_paypal_complete.html.erb +20 -0
- data/app/views/spree/admin/payments/paypal_refund.html.erb +38 -0
- data/app/views/spree/admin/payments/source_forms/_paypal.html.erb +6 -0
- data/app/views/spree/admin/payments/source_views/_paypal.html.erb +35 -0
- data/app/views/spree/checkout/payment/_paypal_checkout.html.erb +33 -0
- data/config/locales/de.yml +21 -0
- data/config/locales/en.yml +21 -0
- data/config/locales/es.yml +21 -0
- data/config/locales/it.yml +21 -0
- data/config/locales/pl.yml +21 -0
- data/config/locales/pt.yml +21 -0
- data/config/routes.rb +8 -0
- data/db/migrate/20220620042734_create_spree_paypal_api_checkouts.rb +15 -0
- data/lib/generators/spree_paypal_checkout/install/install_generator.rb +35 -0
- data/lib/spree_paypal_api_checkout.rb +5 -0
- data/lib/spree_paypal_checkout/engine.rb +26 -0
- data/lib/spree_paypal_checkout/version.rb +3 -0
- data/script/rails +7 -0
- data/spree_paypal_api_checkout.gemspec +74 -0
- metadata +408 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 86103567a8e43ddb81be86c41254d93b1c7b9e52f871d2280af2844ff89876c9
|
4
|
+
data.tar.gz: e653e47f93021041502c595868372205a34cb1be4d86fe97b2d4da3d1564ecdd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b86af98a3383d055f2ca6aacec4ce1f3686c2ab27449f20516c796131334f22d3d194c372525967640e09ba88bf8d1370a8a28c76967b668fc8b832fca34cdef
|
7
|
+
data.tar.gz: '0081b05f5b40bd2be3f729710d125d53b6ec42c35ad5f1b6bdaa037c4f168a56757b0f016b2074f30a8799d83da289cc28a738fa65437d0fbb898dcd3544f1ff'
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Spree PayPal checkout
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/spree-contrib/better_spree_paypal_checkout.svg?branch=master)](https://travis-ci.org/spree-contrib/better_spree_paypal_checkout)
|
4
|
+
|
5
|
+
This is the official Paypal checkout extension for Spree.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
1. Add this extension to your Gemfile with this line:
|
10
|
+
|
11
|
+
gem 'spree_paypal_checkout', github: 'spree-contrib/better_spree_paypal_checkout'
|
12
|
+
|
13
|
+
2. Install the gem using Bundler:
|
14
|
+
|
15
|
+
bundle install
|
16
|
+
|
17
|
+
3. Copy & run migrations
|
18
|
+
|
19
|
+
bundle exec rails g spree_paypal_api_checkout:install
|
20
|
+
|
21
|
+
4. Restart your server
|
22
|
+
|
23
|
+
If your server was running, restart it so that it can find the assets properly.
|
24
|
+
|
25
|
+
### Sandbox Setup
|
26
|
+
|
27
|
+
#### PayPal
|
28
|
+
|
29
|
+
Go to [PayPal's Developer Website](https://developer.paypal.com/), sign in with your PayPal account, click "Applications" then "Sandbox Accounts" and create a new "Business" account. Once the account is created, click on the triangle next to its email address, then "Profile". The "API Credentials" tab will provide your API credentials (probably). If this tab is blank, try refreshing the page.
|
30
|
+
|
31
|
+
You will also need a "Personal" account to test the transactions on your site. Create this in the same way, finding the account information under "Profile" as well. You may need to set a password in order to be able to log in to PayPal's sandbox for this user.
|
32
|
+
|
33
|
+
#### Spree Setup
|
34
|
+
|
35
|
+
In Spree, go to the admin backend, click "Configuration" and then "Payment Methods" and create a new payment method. Select "Spree::Gateway::PayPalcheckout" as the provider, and click "Create". Enter the email address, password and signature from the "API Credentials" tab for the **Business** account on PayPal.
|
36
|
+
|
37
|
+
### Production setup
|
38
|
+
|
39
|
+
#### PayPal
|
40
|
+
|
41
|
+
Sign in to PayPal, then click "Profile" and then (under "Account Information" on the left), click "API Access". On this page, select "Option 2" and click "View API Signature". The username, password and signature will be displayed on this screen.
|
42
|
+
|
43
|
+
If you are unable to find it, then follow [PayPal's own documentation](https://developer.paypal.com/webapps/developer/docs/classic/api/apiCredentials/).
|
44
|
+
|
45
|
+
#### Spree Setup
|
46
|
+
|
47
|
+
Same as sandbox setup, but change "Server" from "sandbox" to "live".
|
48
|
+
|
49
|
+
## Configuration
|
50
|
+
This Spree extension supports *some* of those. If your favourite is not here, then please submit an issue about it, or better still a patch to add it in.
|
51
|
+
|
52
|
+
|
53
|
+
**Must** be an absolute path to the image.
|
54
|
+
|
55
|
+
## Caveats
|
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_paypal_express'
|
20
|
+
Rake::Task['extension:test_app'].invoke
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
//= require spree/backend
|
2
|
+
|
3
|
+
SpreePaypalCheckout = {
|
4
|
+
hideSettings: function(paymentMethod) {
|
5
|
+
if (SpreePaypalCheckout.paymentMethodID && paymentMethod.val() == SpreePaypalCheckout.paymentMethodID) {
|
6
|
+
$('.payment-method-settings').children().hide();
|
7
|
+
$('#payment_amount').prop('disabled', 'disabled');
|
8
|
+
$('button[type="submit"]').prop('disabled', 'disabled');
|
9
|
+
$('#paypal-warning').show();
|
10
|
+
} else if (SpreePaypalCheckout.paymentMethodID) {
|
11
|
+
$('.payment-method-settings').children().show();
|
12
|
+
$('button[type=submit]').prop('disabled', '');
|
13
|
+
$('#payment_amount').prop('disabled', '')
|
14
|
+
$('#paypal-warning').hide();
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
$(document).ready(function() {
|
20
|
+
checkedPaymentMethod = $('[data-hook="payment_method_field"] input[type="radio"]:checked');
|
21
|
+
SpreePaypalCheckout.hideSettings(checkedPaymentMethod);
|
22
|
+
paymentMethods = $('[data-hook="payment_method_field"] input[type="radio"]').click(function (e) {
|
23
|
+
SpreePaypalCheckout.hideSettings($(e.target));
|
24
|
+
});
|
25
|
+
})
|
@@ -0,0 +1,31 @@
|
|
1
|
+
//= require spree/frontend
|
2
|
+
|
3
|
+
SpreePaypalCheckout = {
|
4
|
+
updateSaveAndContinueVisibility: function() {
|
5
|
+
if (this.isButtonHidden()) {
|
6
|
+
$(this).trigger('hideSaveAndContinue')
|
7
|
+
} else {
|
8
|
+
$(this).trigger('showSaveAndContinue')
|
9
|
+
}
|
10
|
+
},
|
11
|
+
isButtonHidden: function () {
|
12
|
+
paymentMethod = this.checkedPaymentMethod();
|
13
|
+
return (!$('#use_existing_card_yes:checked').length && SpreePaypalCheckout.paymentMethodID && paymentMethod.val() == SpreePaypalCheckout.paymentMethodID);
|
14
|
+
},
|
15
|
+
checkedPaymentMethod: function() {
|
16
|
+
return $('div[data-hook="checkout_payment_step"] input[type="radio"][name="order[payments_attributes][][payment_method_id]"]:checked');
|
17
|
+
},
|
18
|
+
hideSaveAndContinue: function() {
|
19
|
+
$("#checkout_form_payment [data-hook=buttons]").hide();
|
20
|
+
},
|
21
|
+
showSaveAndContinue: function() {
|
22
|
+
$("#checkout_form_payment [data-hook=buttons]").show();
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
$(document).ready(function() {
|
27
|
+
SpreePaypalCheckout.updateSaveAndContinueVisibility();
|
28
|
+
paymentMethods = $('div[data-hook="checkout_payment_step"] input[type="radio"]').click(function (e) {
|
29
|
+
SpreePaypalCheckout.updateSaveAndContinueVisibility();
|
30
|
+
});
|
31
|
+
})
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Spree::Admin::PaymentsControllerDecorator
|
2
|
+
def paypal_refund
|
3
|
+
if request.get?
|
4
|
+
if @payment.source.state == 'refunded'
|
5
|
+
flash[:error] = Spree.t(:already_refunded, :scope => 'paypal')
|
6
|
+
redirect_to admin_order_payment_path(@order, @payment)
|
7
|
+
end
|
8
|
+
elsif request.post?
|
9
|
+
response = @payment.payment_method.refund(@payment, params[:refund_amount])
|
10
|
+
if response.success?
|
11
|
+
flash[:success] = Spree.t(:refund_successful, :scope => 'paypal')
|
12
|
+
redirect_to admin_order_payments_path(@order)
|
13
|
+
else
|
14
|
+
flash.now[:error] = Spree.t(:refund_unsuccessful, :scope => 'paypal') + " (#{response.errors.first.long_message})"
|
15
|
+
render
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Spree::Admin::PaymentsController.prepend Spree::Admin::PaymentsControllerDecorator
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Spree
|
2
|
+
class Admin::PaypalCheckoutPaymentsController < Spree::Admin::BaseController
|
3
|
+
before_action :load_order
|
4
|
+
|
5
|
+
def index
|
6
|
+
@payments = @order.payments.includes(:payment_method).where(:spree_payment_methods => { :name => 'Spree::Gateway::PayPalCheckout' })
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def load_order
|
12
|
+
@order = Spree::Order.where(:number => params[:order_id]).first
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Spree
|
2
|
+
class PaypalCheckoutController < StoreController
|
3
|
+
skip_before_action :verify_authenticity_token
|
4
|
+
def express
|
5
|
+
order = current_order || raise(ActiveRecord::RecordNotFound)
|
6
|
+
items = order.line_items.map(&method(:line_item))
|
7
|
+
|
8
|
+
additional_adjustments = order.all_adjustments.additional
|
9
|
+
tax_adjustments = additional_adjustments.tax
|
10
|
+
shipping_adjustments = additional_adjustments.shipping
|
11
|
+
|
12
|
+
additional_adjustments.eligible.each do |adjustment|
|
13
|
+
# Because PayPal doesn't accept $0 items at all. See #10
|
14
|
+
# https://cms.paypal.com/uk/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECCustomizing
|
15
|
+
# "It can be a positive or negative value but not zero."
|
16
|
+
next if adjustment.amount.zero?
|
17
|
+
next if tax_adjustments.include?(adjustment) || shipping_adjustments.include?(adjustment)
|
18
|
+
|
19
|
+
items << {
|
20
|
+
name: adjustment.label,
|
21
|
+
quantity: 1,
|
22
|
+
amount: {
|
23
|
+
currency_code: order.currency,
|
24
|
+
value: adjustment.amount
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
pp_response = provider.create_order(order, express_checkout_request_details(order, items))
|
30
|
+
|
31
|
+
render json: pp_response
|
32
|
+
end
|
33
|
+
|
34
|
+
def confirm
|
35
|
+
order = current_order || raise(ActiveRecord::RecordNotFound)
|
36
|
+
response = provider.capture_payment(params[:number])
|
37
|
+
order.payments.create!({
|
38
|
+
source: Spree::PaypalApiCheckout.create({
|
39
|
+
token: response['id'],
|
40
|
+
payer_id: response['payer']['payer_id']
|
41
|
+
}),
|
42
|
+
amount: order.total,
|
43
|
+
payment_method: payment_method
|
44
|
+
})
|
45
|
+
render json: response
|
46
|
+
end
|
47
|
+
|
48
|
+
def proceed
|
49
|
+
order = current_order || raise(ActiveRecord::RecordNotFound)
|
50
|
+
order.payments.last.source.update(transaction_id: params[:transaction_id])
|
51
|
+
order.next
|
52
|
+
if order.complete?
|
53
|
+
flash.notice = Spree.t(:order_processed_successfully)
|
54
|
+
flash[:order_completed] = true
|
55
|
+
session[:order_id] = nil
|
56
|
+
redirect_to completion_route(order)
|
57
|
+
else
|
58
|
+
redirect_to checkout_state_path(order.state)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def cancel
|
63
|
+
flash[:notice] = Spree.t('flash.cancel', scope: 'paypal')
|
64
|
+
order = current_order || raise(ActiveRecord::RecordNotFound)
|
65
|
+
redirect_to checkout_state_path(order.state, paypal_cancel_token: params[:token])
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def line_item(item)
|
71
|
+
{
|
72
|
+
name: item.product.name,
|
73
|
+
number: item.variant.sku,
|
74
|
+
quantity: item.quantity,
|
75
|
+
amount: {
|
76
|
+
currency_code: item.order.currency,
|
77
|
+
value: item.price
|
78
|
+
},
|
79
|
+
ItemCategory: "Physical"
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def express_checkout_request_details order, items
|
84
|
+
{
|
85
|
+
intent: 'CAPTURE',
|
86
|
+
purchase_units: [
|
87
|
+
{
|
88
|
+
amount: {
|
89
|
+
currency_code: current_order.currency,
|
90
|
+
value: order.total
|
91
|
+
},
|
92
|
+
item: items
|
93
|
+
},
|
94
|
+
]
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
def payment_method
|
99
|
+
Spree::PaymentMethod.find(params[:payment_method_id])
|
100
|
+
end
|
101
|
+
|
102
|
+
def provider
|
103
|
+
payment_method
|
104
|
+
end
|
105
|
+
|
106
|
+
def completion_route(order)
|
107
|
+
order_path(order)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Spree
|
2
|
+
class Gateway::PayPalCheckout < Gateway
|
3
|
+
preference :api_key, :string
|
4
|
+
preference :secret_key, :string
|
5
|
+
preference :server, :string, default: 'https://api-m.sandbox.paypal.com'
|
6
|
+
|
7
|
+
def supports?(source)
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
def provider_class;end
|
12
|
+
|
13
|
+
def provider;end
|
14
|
+
|
15
|
+
def auto_capture?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_type
|
20
|
+
'paypal_checkout'
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_client_token
|
24
|
+
uri = URI.parse("https://#{preferred_server}/v1/identity/generate-token")
|
25
|
+
request = Net::HTTP::Post.new(uri)
|
26
|
+
request['Authorization'] = "Bearer #{generate_access_token}"
|
27
|
+
request.content_type = "application/json"
|
28
|
+
|
29
|
+
req_options = { use_ssl: uri.scheme == "https" }
|
30
|
+
|
31
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
32
|
+
response = http.request(request)
|
33
|
+
end
|
34
|
+
return response
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_access_token
|
38
|
+
uri = URI.parse("https://#{preferred_server}/v1/oauth2/token")
|
39
|
+
request = Net::HTTP::Post.new(uri)
|
40
|
+
request.basic_auth("#{preferred_api_key}", "#{preferred_secret_key}")
|
41
|
+
request.content_type = "application/json"
|
42
|
+
request.body = "grant_type=client_credentials"
|
43
|
+
|
44
|
+
req_options = { use_ssl: uri.scheme == "https" }
|
45
|
+
|
46
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
47
|
+
response = http.request(request)
|
48
|
+
end
|
49
|
+
return JSON.parse(response.read_body)['access_token']
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_order order, body
|
53
|
+
uri = URI.parse("https://#{preferred_server}/v2/checkout/orders")
|
54
|
+
request = Net::HTTP::Post.new(uri)
|
55
|
+
request['Authorization'] = "Bearer #{generate_access_token}"
|
56
|
+
request.content_type = "application/json"
|
57
|
+
|
58
|
+
request.body = body.to_json
|
59
|
+
req_options = { use_ssl: uri.scheme == "https" }
|
60
|
+
|
61
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
62
|
+
response = http.request(request)
|
63
|
+
end
|
64
|
+
return JSON.parse(response.read_body)
|
65
|
+
end
|
66
|
+
|
67
|
+
def capture_payment order_id
|
68
|
+
uri = URI.parse("https://#{preferred_server}/v2/checkout/orders/#{order_id}/capture")
|
69
|
+
request = Net::HTTP::Post.new(uri)
|
70
|
+
request['Authorization'] = "Bearer #{generate_access_token}"
|
71
|
+
request.content_type = "application/json"
|
72
|
+
req_options = { use_ssl: uri.scheme == "https" }
|
73
|
+
|
74
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
75
|
+
response = http.request(request)
|
76
|
+
end
|
77
|
+
return JSON.parse(response.read_body)
|
78
|
+
end
|
79
|
+
|
80
|
+
def refund_payment id, body
|
81
|
+
uri = URI.parse("https://#{preferred_server}/v2/payments/captures/#{id}/refund")
|
82
|
+
request = Net::HTTP::Post.new(uri)
|
83
|
+
request['Authorization'] = "Bearer #{generate_access_token}"
|
84
|
+
request.body = body.to_json
|
85
|
+
request.content_type = "application/json"
|
86
|
+
req_options = { use_ssl: uri.scheme == "https" }
|
87
|
+
|
88
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
89
|
+
response = http.request(request)
|
90
|
+
end
|
91
|
+
return JSON.parse(response.read_body)
|
92
|
+
end
|
93
|
+
|
94
|
+
def purchase(amount, express_checkout, gateway_options={})
|
95
|
+
if express_checkout.success?
|
96
|
+
Class.new do
|
97
|
+
def success?; true; end
|
98
|
+
def authorization; nil; end
|
99
|
+
end.new
|
100
|
+
else
|
101
|
+
'No transaction'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def refund(payment, amount)
|
106
|
+
refund_type = payment.amount == amount.to_f ? "Full" : "Partial"
|
107
|
+
refund_transaction = {
|
108
|
+
transaction_id: payment.source.transaction_id,
|
109
|
+
amount: {
|
110
|
+
value: payment.order.item_total,
|
111
|
+
currency_code: payment.currency
|
112
|
+
},
|
113
|
+
invoice_id: payment.order.number,
|
114
|
+
refund_type: refund_type,
|
115
|
+
refund_source: 'any'
|
116
|
+
}
|
117
|
+
refund_transaction_response = refund_payment(payment.source.transaction_id, refund_transaction)
|
118
|
+
if refund_transaction_response['status']=='COMPLETED'
|
119
|
+
payment.source.update({
|
120
|
+
:refunded_at => Time.now,
|
121
|
+
:refund_transaction_id => refund_transaction_response['id'],
|
122
|
+
:state => "refunded",
|
123
|
+
:refund_type => refund_type
|
124
|
+
})
|
125
|
+
|
126
|
+
payment.class.create!(
|
127
|
+
:order => payment.order,
|
128
|
+
:source => payment,
|
129
|
+
:payment_method => payment.payment_method,
|
130
|
+
:amount => amount.to_f.abs * -1,
|
131
|
+
:response_code => refund_transaction_response['id'],
|
132
|
+
:state => 'completed'
|
133
|
+
)
|
134
|
+
end
|
135
|
+
refund_transaction_response
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# payment.state = 'completed'
|
141
|
+
# current_order.state = 'complete'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%= form_tag paypal_refund_admin_order_payment_path(@order, @payment) do %>
|
2
|
+
<div class="label-block left five columns alpha">
|
3
|
+
<div>
|
4
|
+
<fieldset data-hook="admin_variant_new_form">
|
5
|
+
<legend><%= Spree.t('refund', :scope => :paypal) %></legend>
|
6
|
+
<div class='field'>
|
7
|
+
<%= label_tag 'refund_amount', Spree.t(:refund_amount, :scope => 'paypal') %>
|
8
|
+
<small><em><%= Spree.t(:original_amount, :scope => 'paypal', :amount => @payment.display_amount) %></em></small><br>
|
9
|
+
<% symbol = ::Money.new(1, Spree::Config[:currency]).symbol %>
|
10
|
+
<% if Spree::Config[:currency_symbol_position] == "before" %>
|
11
|
+
<%= symbol %><%= text_field_tag 'refund_amount', @payment.amount %>
|
12
|
+
<% else %>
|
13
|
+
<%= text_field_tag 'refund_amount', @payment.amount %><%= symbol %>
|
14
|
+
<% end %>
|
15
|
+
</div>
|
16
|
+
<%= button Spree.t(:refund, scope: 'paypal'), 'money' %>
|
17
|
+
</fieldset>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<%= render :partial => 'spree/admin/shared/order_tabs', :locals => { :current => 'Payments' } %>
|
2
|
+
|
3
|
+
<% content_for :page_title do %>
|
4
|
+
/ <%= link_to Spree.t(:payments), admin_order_payments_path(@order) %>
|
5
|
+
/ <%= payment_method_name(@payment) %>
|
6
|
+
/ <%= Spree.t('refund', :scope => :paypal) %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<%= form_tag paypal_refund_admin_order_payment_path(@order, @payment) do %>
|
10
|
+
<div class="label-block left five columns alpha">
|
11
|
+
<div>
|
12
|
+
<fieldset data-hook="admin_variant_new_form">
|
13
|
+
<legend><%= Spree.t('refund', :scope => :paypal) %></legend>
|
14
|
+
<div class='form-group'>
|
15
|
+
<%= label_tag 'refund_amount', Spree.t(:refund_amount, :scope => 'paypal') %>
|
16
|
+
<small><em><%= Spree.t(:original_amount, :scope => 'paypal', :amount => @payment.display_amount) %></em></small><br>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<% currency = ::Money::Currency.new(Spree::Config[:currency]) %>
|
20
|
+
<div class="form-group">
|
21
|
+
<div class="input-group">
|
22
|
+
<% if currency.symbol_first %>
|
23
|
+
<span class="input-group-addon" id="basic-addon1"><%= currency.symbol %></span>
|
24
|
+
<%= text_field_tag 'refund_amount', @payment.amount, class: 'form-control' %>
|
25
|
+
<% else %>
|
26
|
+
<%= text_field_tag 'refund_amount', @payment.amount, class: 'form-control' %>
|
27
|
+
<span class="input-group-addon" id="basic-addon1"><%= currency.symbol %></span>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<div class="form-group">
|
33
|
+
<%= button Spree.t(:refund, scope: 'paypal'), 'money', class: 'btn btn-primary' %>
|
34
|
+
</div>
|
35
|
+
</fieldset>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
<% end %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<fieldset data-hook="paypal">
|
2
|
+
<legend align="center"><%= Spree.t(:transaction, :scope => :paypal) %></legend>
|
3
|
+
|
4
|
+
<div class="row">
|
5
|
+
<div class="alpha six columns">
|
6
|
+
<dl>
|
7
|
+
<dt><%= Spree.t(:payer_id, :scope => :paypal) %>:</dt>
|
8
|
+
<dd><%= payment.source.payer_id %></dd>
|
9
|
+
|
10
|
+
<dt><%= Spree.t(:token, :scope => :paypal) %>:</dt>
|
11
|
+
<dd><%= payment.source.token %></dd>
|
12
|
+
|
13
|
+
<dt><%= Spree.t(:transaction_id) %>:</dt>
|
14
|
+
<dd><%= payment.source.transaction_id %></dd>
|
15
|
+
</dl>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<% if payment.source.state != 'refunded' %>
|
19
|
+
<%= button_link_to Spree.t('actions.refund', scope: :paypal), spree.paypal_refund_admin_order_payment_path(@order, payment), icon: 'money' %>
|
20
|
+
<% else %>
|
21
|
+
<div class="alpha six columns">
|
22
|
+
<dl>
|
23
|
+
<dt><%= Spree.t(:state, :scope => :paypal) %>:</dt>
|
24
|
+
<dd><%= payment.source.state.titleize %></dd>
|
25
|
+
|
26
|
+
<dt><%= Spree.t(:refunded_at, :scope => :paypal) %>:</dt>
|
27
|
+
<dd><%= pretty_time(payment.source.refunded_at) %></dd>
|
28
|
+
|
29
|
+
<dt><%= Spree.t(:refund_transaction_id, :scope => :paypal) %>:</dt>
|
30
|
+
<dd><%= payment.source.refund_transaction_id %></dd>
|
31
|
+
</dl>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
</fieldset>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<div id="paypal-button-container" class="paypal-button-container"></div>
|
2
|
+
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
3
|
+
<script type="text/javascript" src="https://www.paypal.com/sdk/js?client-id=<%= CONSTANTS[:BUSINESS_CLIENT_ID] %>¤cy=<%= @order.currency %>"></script>
|
4
|
+
<script type="text/javascript">
|
5
|
+
paypal.Buttons({
|
6
|
+
style: {
|
7
|
+
layout: 'horizontal',
|
8
|
+
color: 'gold',
|
9
|
+
shape: 'rect',
|
10
|
+
label: 'pay',
|
11
|
+
tagline: 'true'
|
12
|
+
},
|
13
|
+
createOrder: function(data, actions) {
|
14
|
+
return fetch(`/paypal_checkout?payment_method_id=<%= payment_method.id %>`, {
|
15
|
+
method: "post"
|
16
|
+
})
|
17
|
+
.then((response) => response.json())
|
18
|
+
.then((data) => data['id']);
|
19
|
+
},
|
20
|
+
onApprove: function(data, actions) {
|
21
|
+
return fetch(`/paypal_checkout/confirm?number=${data.orderID}&payment_method_id=<%= payment_method.id %>`, {
|
22
|
+
method: "post",
|
23
|
+
})
|
24
|
+
.then((response) => response.json())
|
25
|
+
.then((orderData) => {
|
26
|
+
var transaction = orderData.purchase_units[0].payments.captures[0];
|
27
|
+
fetch(`/paypal_checkout/proceed?number=${data.orderID}&status=${transaction.status}&transaction_id=${transaction.id}`, {
|
28
|
+
method: "get",
|
29
|
+
})
|
30
|
+
});
|
31
|
+
}
|
32
|
+
}).render('#paypal-button-container');
|
33
|
+
</script>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
de:
|
3
|
+
spree:
|
4
|
+
paypal:
|
5
|
+
already_refunded: "Diese Zahlung wurde zurück erstattet und kann nicht weiter bearbeitet werden."
|
6
|
+
no_payment_via_admin_backend: "Sie können nicht aufgeladen PayPal-Konten über das Admin-Backend zu diesem Zeitpunkt."
|
7
|
+
transaction: "PayPal Transaktion"
|
8
|
+
payer_id: "Zahler ID"
|
9
|
+
transaction_id: "Transaktions ID"
|
10
|
+
token: "Token"
|
11
|
+
refund: "Rückerstattung"
|
12
|
+
refund_amount: "Erstattungsbetrag"
|
13
|
+
original_amount: "Originalbetrag: %{amount}"
|
14
|
+
refund_successful: "PayPal Erstattung erfolgreich"
|
15
|
+
refund_unsuccessful: "PayPal Erstattung nicht erfolgreich"
|
16
|
+
actions:
|
17
|
+
refund: "Erstatten"
|
18
|
+
flash:
|
19
|
+
cancel: "Sie wollen PayPal doch nicht benutzen? Kein Problem."
|
20
|
+
connection_failed: "Verbindung zu Paypal nicht erfolgreich."
|
21
|
+
generic_error: "PayPal fehlgeschlagen. %{reasons}"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
spree:
|
4
|
+
paypal:
|
5
|
+
already_refunded: "This payment has been refunded and no further action can be taken on it."
|
6
|
+
no_payment_via_admin_backend: "You cannot charge PayPal accounts through the admin backend at this time."
|
7
|
+
transaction: "PayPal Transaction"
|
8
|
+
payer_id: "Payer ID"
|
9
|
+
transaction_id: "Transaction ID"
|
10
|
+
token: "Token"
|
11
|
+
refund: "Refund"
|
12
|
+
refund_amount: "Amount"
|
13
|
+
original_amount: "Original amount: %{amount}"
|
14
|
+
refund_successful: "PayPal refund successful"
|
15
|
+
refund_unsuccessful: "PayPal refund unsuccessful"
|
16
|
+
actions:
|
17
|
+
refund: "Refund"
|
18
|
+
flash:
|
19
|
+
cancel: "Don't want to use PayPal? No problems."
|
20
|
+
connection_failed: "Could not connect to PayPal."
|
21
|
+
generic_error: "PayPal failed. %{reasons}"
|